summaryrefslogtreecommitdiff
path: root/spec/ruby/core/array/bsearch_index_spec.rb
blob: df2c7c098e282f08dd91a3f00f9550a5cb3de2b5 (plain)
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
require_relative '../../spec_helper'
require_relative '../enumerable/shared/enumeratorized'

describe "Array#bsearch_index" do
  context "when not passed a block" do
    before :each do
      @enum = [1, 2, 42, 100, 666].bsearch_index
    end

    it "returns an Enumerator" do
      @enum.should be_an_instance_of(Enumerator)
    end

    it "returns an Enumerator with unknown size" do
      @enum.size.should be_nil
    end

    it "returns index of element when block condition is satisfied" do
      @enum.each { |x| x >= 33 }.should == 2
    end
  end

  it "raises a TypeError when block returns a String" do
    -> { [1, 2, 3].bsearch_index { "not ok" } }.should raise_error(TypeError)
  end

  it "returns nil when block is empty" do
    [1, 2, 3].bsearch_index {}.should be_nil
  end

  context "minimum mode" do
    before :each do
      @array = [0, 4, 7, 10, 12]
    end

    it "returns index of first element which satisfies the block" do
      @array.bsearch_index { |x| x >= 4 }.should == 1
      @array.bsearch_index { |x| x >= 6 }.should == 2
      @array.bsearch_index { |x| x >= -1 }.should == 0
    end

    it "returns nil when block condition is never satisfied" do
      @array.bsearch_index { false }.should be_nil
      @array.bsearch_index { |x| x >= 100 }.should be_nil
    end
  end

  context "find any mode" do
    before :each do
      @array = [0, 4, 7, 10, 12]
    end

    it "returns the index of any matched elements where element is between 4 <= x < 8" do
      [1, 2].should include(@array.bsearch_index { |x| 1 - x / 4 })
    end

    it "returns the index of any matched elements where element is between 8 <= x < 10" do
      @array.bsearch_index { |x| 4 - x / 2 }.should be_nil
    end

    it "returns nil when block never returns 0" do
      @array.bsearch_index { |x| 1 }.should be_nil
      @array.bsearch_index { |x| -1 }.should be_nil
    end

    it "returns the middle element when block always returns zero" do
      @array.bsearch_index { |x| 0 }.should == 2
    end

    context "magnitude does not effect the result" do
      it "returns the index of any matched elements where element is between 4n <= xn < 8n" do
        [1, 2].should include(@array.bsearch_index { |x| (1 - x / 4) * (2**100) })
      end

      it "returns nil when block never returns 0" do
        @array.bsearch_index { |x| 1 * (2**100) }.should be_nil
        @array.bsearch_index { |x| (-1) * (2**100) }.should be_nil
      end

      it "handles values from Integer#coerce" do
        [1, 2].should include(@array.bsearch_index { |x| (2**100).coerce((1 - x / 4) * (2**100)).first })
      end
    end
  end
end
d class='graph'>
-rw-r--r--.github/workflows/compilers.yml76
-rw-r--r--.github/workflows/macos.yml36
-rw-r--r--.github/workflows/mingw.yml38
-rw-r--r--.github/workflows/mjit-bindgen.yml104
-rw-r--r--.github/workflows/mjit.yml48
-rw-r--r--.github/workflows/publish.yml18
-rw-r--r--.github/workflows/scorecards.yml72
-rw-r--r--.github/workflows/spec_guards.yml50
-rw-r--r--.github/workflows/ubuntu.yml55
-rw-r--r--.github/workflows/wasm.yml47
-rw-r--r--.github/workflows/windows.yml87
-rw-r--r--.github/workflows/yjit-ubuntu.yml70
-rw-r--r--.gitignore16
-rw-r--r--.indent.pro32
-rw-r--r--.travis.yml234
-rw-r--r--LEGAL10
-rw-r--r--NEWS.md746
-rw-r--r--README.ja.md1
-rw-r--r--README.md5
-rw-r--r--addr2line.c430
-rw-r--r--array.c2083
-rw-r--r--ast.c60
-rw-r--r--ast.rb110
-rw-r--r--benchmark/array_sort_int.yml15
-rw-r--r--benchmark/buffer_each.yml27
-rw-r--r--benchmark/buffer_get.yml32
-rw-r--r--benchmark/cgi_escape_html.yml37
-rw-r--r--benchmark/enum_minmax.yml25
-rw-r--r--benchmark/enum_sort.yml15
-rw-r--r--benchmark/erb_escape_html.yml31
-rw-r--r--benchmark/lib/benchmark_driver/runner/mjit.rb2
-rw-r--r--benchmark/lib/benchmark_driver/runner/mjit_exec.rb237
-rw-r--r--benchmark/marshal_dump_load_integer.yml22
-rw-r--r--benchmark/masgn.yml26
-rw-r--r--benchmark/mjit_exec_jt2jt.yml6
-rw-r--r--benchmark/mjit_exec_vm2jt.yml6
-rw-r--r--benchmark/mjit_exec_vm2vm.yml6
-rw-r--r--benchmark/numeric_methods.yml16
-rw-r--r--benchmark/range_min.yml2
-rw-r--r--benchmark/so_nbody.rb58
-rw-r--r--benchmark/string_concat.yml41
-rw-r--r--benchmark/time_parse.yml2
-rw-r--r--benchmark/vm_const.yml6
-rw-r--r--benchmark/vm_freezeobj.yml6
-rw-r--r--benchmark/vm_ivar_get.yml37
-rw-r--r--benchmark/vm_ivar_get_unintialized.yml12
-rw-r--r--benchmark/vm_ivar_lazy_set.yml12
-rw-r--r--benchmark/vm_ivar_set_on_instance.yml35
-rw-r--r--benchmark/vm_lvar_cond_set.yml8
-rw-r--r--bignum.c1118
-rwxr-xr-xbin/gem19
-rwxr-xr-xbootstraptest/runner.rb94
-rw-r--r--bootstraptest/test_attr.rb16
-rw-r--r--bootstraptest/test_io.rb2
-rw-r--r--bootstraptest/test_ractor.rb56
-rw-r--r--bootstraptest/test_yjit.rb479
-rw-r--r--builtin.h4
-rw-r--r--ccan/check_type/check_type.h2
-rw-r--r--ccan/container_of/container_of.h4
-rw-r--r--ccan/list/list.h2
-rw-r--r--class.c671
-rw-r--r--common.mk1302
-rw-r--r--compar.c16
-rw-r--r--compile.c7074
-rw-r--r--complex.c793
-rw-r--r--configure.ac475
-rw-r--r--cont.c905
-rw-r--r--coroutine/asyncify/Context.h8
-rw-r--r--coroutine/ppc/Context.S117
-rw-r--r--coroutine/ppc/Context.h1
-rw-r--r--coroutine/ppc64/Context.S121
-rw-r--r--coroutine/ppc64/Context.h4
-rw-r--r--cygwin/GNUmakefile.in34
-rw-r--r--debug.c122
-rw-r--r--debug_counter.c28
-rw-r--r--debug_counter.h45
-rw-r--r--defs/gmake.mk192
-rw-r--r--defs/id.def6
-rw-r--r--defs/keywords2
-rw-r--r--defs/lex.c.src2
-rw-r--r--dir.c1750
-rw-r--r--dln.c168
-rw-r--r--dln_find.c246
-rw-r--r--dmyenc.c2
-rw-r--r--doc/.document2
-rw-r--r--doc/ChangeLog-2.3.010
-rw-r--r--doc/NEWS/NEWS-3.0.0.md12
-rw-r--r--doc/command_injection.rdoc2
-rw-r--r--doc/contributing/building_ruby.md50
-rw-r--r--doc/contributing/documentation_guide.md57
-rw-r--r--doc/contributing/testing_ruby.md7
-rw-r--r--doc/date/calendars.rdoc62
-rw-r--r--doc/encodings.rdoc11
-rw-r--r--doc/examples/files.rdoc26
-rw-r--r--doc/mjit/mjit.md39
-rw-r--r--doc/net-http/examples.rdoc31
-rw-r--r--doc/net-http/included_getters.rdoc3
-rw-r--r--doc/packed_data.rdoc590
-rw-r--r--doc/rdoc/markup_reference.rb1257
-rw-r--r--doc/regexp.rdoc20
-rw-r--r--doc/strftime_formatting.rdoc172
-rw-r--r--doc/syntax/literals.rdoc6
-rw-r--r--doc/time/in.rdoc7
-rw-r--r--doc/time/mon-min.rdoc8
-rw-r--r--doc/time/msec.rdoc2
-rw-r--r--doc/time/nsec.rdoc2
-rw-r--r--doc/time/sec.rdoc2
-rw-r--r--doc/time/sec_i.rdoc1
-rw-r--r--doc/time/usec.rdoc2
-rw-r--r--doc/time/year.rdoc1
-rw-r--r--doc/time/zone_and_in.rdoc8
-rw-r--r--doc/timezones.rdoc108
-rw-r--r--doc/yjit/yjit.md237
-rw-r--r--enc/Makefile.in4
-rw-r--r--enc/cesu_8.c23
-rw-r--r--enc/depend1
-rw-r--r--enc/encdb.c2
-rw-r--r--enc/jis/props.h.blt4
-rw-r--r--enc/jis/props.kwd2
-rw-r--r--enc/jis/props.src2
-rwxr-xr-xenc/make_encmake.rb2
-rw-r--r--enc/trans/newline.trans20
-rw-r--r--enc/unicode/15.0.0/casefold.h (renamed from enc/unicode/14.0.0/casefold.h)8
-rw-r--r--enc/unicode/15.0.0/name2ctype.h (renamed from enc/unicode/14.0.0/name2ctype.h)3097
-rw-r--r--enc/utf_16_32.h2
-rw-r--r--encoding.c532
-rw-r--r--enum.c713
-rw-r--r--enumerator.c983
-rw-r--r--error.c833
-rw-r--r--eval.c807
-rw-r--r--eval_error.c442
-rw-r--r--eval_intern.h6
-rw-r--r--eval_jump.c38
-rw-r--r--ext/-test-/abi/extconf.rb1
-rw-r--r--ext/-test-/arith_seq/beg_len_step/beg_len_step.c19
-rw-r--r--ext/-test-/arith_seq/beg_len_step/depend161
-rw-r--r--ext/-test-/arith_seq/beg_len_step/extconf.rb2
-rw-r--r--ext/-test-/debug/inspector.c14
-rw-r--r--ext/-test-/debug/profile_frames.c27
-rw-r--r--ext/-test-/econv/append.c15
-rw-r--r--ext/-test-/econv/extconf.rb3
-rw-r--r--ext/-test-/econv/init.c11
-rw-r--r--ext/-test-/enumerator_kw/enumerator_kw.c3
-rw-r--r--ext/-test-/eval/eval.c13
-rw-r--r--ext/-test-/eval/extconf.rb2
-rw-r--r--ext/-test-/file/fs.c14
-rw-r--r--ext/-test-/funcall/funcall.c12
-rw-r--r--ext/-test-/gvl/call_without_gvl/call_without_gvl.c2
-rw-r--r--ext/-test-/marshal/internal_ivar/internal_ivar.c5
-rw-r--r--ext/-test-/memory_status/memory_status.c12
-rw-r--r--ext/-test-/num2int/num2int.c26
-rw-r--r--ext/-test-/printf/printf.c64
-rw-r--r--ext/-test-/proc/super.c2
-rw-r--r--ext/-test-/random/bad_version.c135
-rw-r--r--ext/-test-/random/depend160
-rw-r--r--ext/-test-/random/loop.c1
-rw-r--r--ext/-test-/rational/depend1
-rw-r--r--ext/-test-/rb_call_super_kw/rb_call_super_kw.c3
-rw-r--r--ext/-test-/st/foreach/foreach.c100
-rw-r--r--ext/-test-/st/numhash/numhash.c16
-rw-r--r--ext/-test-/st/update/update.c12
-rw-r--r--ext/-test-/string/coderange.c8
-rw-r--r--ext/-test-/string/cstr.c16
-rw-r--r--ext/-test-/string/fstring.c4
-rw-r--r--ext/-test-/string/qsort.c14
-rw-r--r--ext/-test-/string/set_len.c10
-rw-r--r--ext/-test-/struct/member.c2
-rw-r--r--ext/-test-/symbol/type.c6
-rw-r--r--ext/-test-/tracepoint/gc_hook.c26
-rw-r--r--ext/-test-/tracepoint/tracepoint.c54
-rw-r--r--ext/-test-/typeddata/typeddata.c2
-rw-r--r--ext/-test-/vm/at_exit.c12
-rw-r--r--ext/-test-/win32/console/attribute.c18
-rw-r--r--ext/-test-/win32/fd_setsize/fd_setsize.c10
-rw-r--r--ext/bigdecimal/bigdecimal.c1366
-rw-r--r--ext/bigdecimal/bigdecimal.gemspec4
-rw-r--r--ext/bigdecimal/bigdecimal.h48
-rw-r--r--ext/bigdecimal/extconf.rb2
-rw-r--r--ext/bigdecimal/lib/bigdecimal/util.rb6
-rw-r--r--ext/bigdecimal/missing.h43
-rw-r--r--ext/cgi/escape/escape.c353
-rw-r--r--ext/coverage/coverage.c216
-rw-r--r--ext/coverage/depend5
-rw-r--r--ext/date/date.gemspec24
-rw-r--r--ext/date/date_core.c1000
-rw-r--r--ext/date/date_parse.c72
-rw-r--r--ext/date/date_strptime.c105
-rw-r--r--ext/date/lib/date.rb6
-rw-r--r--ext/date/zonetab.h2
-rw-r--r--ext/date/zonetab.list2
-rw-r--r--ext/digest/bubblebabble/bubblebabble.c2
-rw-r--r--ext/digest/digest.c12
-rw-r--r--ext/digest/digest.h2
-rw-r--r--ext/digest/digest_conf.rb2
-rw-r--r--ext/digest/lib/digest/version.rb2
-rw-r--r--ext/digest/md5/depend1
-rw-r--r--ext/digest/sha1/depend1
-rw-r--r--ext/digest/sha2/depend1
-rw-r--r--ext/digest/sha2/sha2init.c2
-rw-r--r--ext/erb/escape/escape.c95
-rw-r--r--ext/erb/escape/extconf.rb7
-rw-r--r--ext/etc/etc.c10
-rwxr-xr-xext/extmk.rb69
-rw-r--r--ext/fcntl/fcntl.gemspec2
-rw-r--r--ext/fiddle/closure.c135
-rw-r--r--ext/fiddle/conversions.c20
-rw-r--r--ext/fiddle/extconf.rb30
-rw-r--r--ext/fiddle/extlibs13
-rw-r--r--ext/fiddle/fiddle.c214
-rw-r--r--ext/fiddle/fiddle.gemspec3
-rw-r--r--ext/fiddle/fiddle.h21
-rw-r--r--ext/fiddle/handle.c59
-rw-r--r--ext/fiddle/lib/fiddle.rb35
-rw-r--r--ext/fiddle/lib/fiddle/closure.rb25
-rw-r--r--ext/fiddle/lib/fiddle/cparser.rb18
-rw-r--r--ext/fiddle/lib/fiddle/pack.rb35
-rw-r--r--ext/fiddle/lib/fiddle/version.rb2
-rw-r--r--ext/io/console/console.c40
-rw-r--r--ext/io/console/depend2
-rw-r--r--ext/io/console/io-console.gemspec2
-rw-r--r--ext/io/console/win32_vk.inc327
-rw-r--r--ext/io/console/win32_vk.list2
-rw-r--r--ext/io/nonblock/io-nonblock.gemspec2
-rw-r--r--ext/io/wait/extconf.rb2
-rw-r--r--ext/io/wait/io-wait.gemspec2
-rw-r--r--ext/io/wait/wait.c46
-rw-r--r--ext/json/VERSION2
-rw-r--r--ext/json/generator/generator.c4
-rw-r--r--ext/json/lib/json/version.rb2
-rw-r--r--ext/json/parser/extconf.rb4
-rw-r--r--ext/json/parser/parser.c14
-rw-r--r--ext/json/parser/parser.rl14
-rw-r--r--ext/nkf/nkf.c78
-rw-r--r--ext/nkf/nkf.gemspec2
-rw-r--r--ext/objspace/depend23
-rw-r--r--ext/objspace/lib/objspace.rb97
-rw-r--r--ext/objspace/object_tracing.c167
-rw-r--r--ext/objspace/objspace.c347
-rw-r--r--ext/objspace/objspace_dump.c221
-rw-r--r--ext/openssl/History.md105
-rw-r--r--ext/openssl/extconf.rb108
-rw-r--r--ext/openssl/lib/openssl/pkey.rb20
-rw-r--r--ext/openssl/lib/openssl/ssl.rb5
-rw-r--r--ext/openssl/lib/openssl/version.rb2
-rw-r--r--ext/openssl/openssl.gemspec2
-rw-r--r--ext/openssl/ossl.h6
-rw-r--r--ext/openssl/ossl_asn1.c19
-rw-r--r--ext/openssl/ossl_bn.c36
-rw-r--r--ext/openssl/ossl_cipher.c3
-rw-r--r--ext/openssl/ossl_hmac.c8
-rw-r--r--ext/openssl/ossl_kdf.c6
-rw-r--r--ext/openssl/ossl_pkey.c50
-rw-r--r--ext/openssl/ossl_pkey.h2
-rw-r--r--ext/openssl/ossl_pkey_dh.c12
-rw-r--r--ext/openssl/ossl_pkey_dsa.c14
-rw-r--r--ext/openssl/ossl_pkey_ec.c68
-rw-r--r--ext/openssl/ossl_pkey_rsa.c12
-rw-r--r--ext/openssl/ossl_ssl.c183
-rw-r--r--ext/openssl/ossl_ssl_session.c4
-rw-r--r--ext/openssl/ossl_x509cert.c6
-rw-r--r--ext/openssl/ossl_x509crl.c6
-rw-r--r--ext/openssl/ossl_x509req.c6
-rw-r--r--ext/openssl/ossl_x509revoked.c6
-rw-r--r--ext/pathname/lib/pathname.rb8
-rw-r--r--ext/pathname/pathname.c2
-rw-r--r--ext/pathname/pathname.gemspec2
-rw-r--r--ext/psych/extconf.rb39
-rw-r--r--ext/psych/extlibs11
-rw-r--r--ext/psych/lib/psych.rb2
-rw-r--r--ext/psych/lib/psych/exception.rb14
-rw-r--r--ext/psych/lib/psych/parser.rb13
-rw-r--r--ext/psych/lib/psych/scalar_scanner.rb2
-rw-r--r--ext/psych/lib/psych/versions.rb4
-rw-r--r--ext/psych/lib/psych/visitors/to_ruby.rb8
-rw-r--r--ext/psych/lib/psych/visitors/yaml_tree.rb16
-rw-r--r--ext/psych/psych_parser.c22
-rw-r--r--ext/pty/depend1
-rw-r--r--ext/pty/extconf.rb6
-rw-r--r--ext/pty/lib/expect.rb16
-rw-r--r--ext/pty/pty.c136
-rw-r--r--ext/racc/cparse/cparse.c4
-rw-r--r--ext/readline/readline-ext.gemspec2
-rw-r--r--ext/readline/readline.c4
-rw-r--r--ext/ripper/depend9
-rw-r--r--ext/ripper/eventids2.c19
-rw-r--r--ext/ripper/lib/ripper/lexer.rb9
-rw-r--r--ext/ripper/tools/preproc.rb2
-rw-r--r--ext/socket/addrinfo.h36
-rw-r--r--ext/socket/ancdata.c262
-rw-r--r--ext/socket/basicsocket.c76
-rw-r--r--ext/socket/constants.c6
-rw-r--r--ext/socket/depend15
-rw-r--r--ext/socket/extconf.rb23
-rw-r--r--ext/socket/getaddrinfo.c898
-rw-r--r--ext/socket/getnameinfo.c226
-rw-r--r--ext/socket/ifaddr.c8
-rw-r--r--ext/socket/init.c156
-rw-r--r--ext/socket/ipsocket.c190
-rw-r--r--ext/socket/lib/socket.rb9
-rw-r--r--ext/socket/mkconstants.rb7
-rw-r--r--ext/socket/option.c98
-rw-r--r--ext/socket/raddrinfo.c264
-rw-r--r--ext/socket/rubysocket.h24
-rw-r--r--ext/socket/socket.c456
-rw-r--r--ext/socket/sockssocket.c4
-rw-r--r--ext/socket/tcpserver.c2
-rw-r--r--ext/socket/tcpsocket.c18
-rw-r--r--ext/socket/udpsocket.c30
-rw-r--r--ext/socket/unixserver.c8
-rw-r--r--ext/socket/unixsocket.c148
-rw-r--r--ext/stringio/stringio.c318
-rw-r--r--ext/strscan/extconf.rb11
-rw-r--r--ext/strscan/strscan.c178
-rw-r--r--ext/strscan/strscan.gemspec23
-rw-r--r--ext/syslog/syslog.c22
-rw-r--r--ext/syslog/syslog.gemspec2
-rw-r--r--ext/win32/lib/win32/registry.rb13
-rw-r--r--ext/win32/resolv/resolv.c18
-rw-r--r--ext/win32ole/win32ole.c168
-rw-r--r--ext/win32ole/win32ole.gemspec2
-rw-r--r--ext/win32ole/win32ole_event.c18
-rw-r--r--ext/win32ole/win32ole_typelib.c16
-rw-r--r--ext/zlib/zlib.c2
-rw-r--r--file.c2913
-rw-r--r--gc.c4266
-rw-r--r--gc.h26
-rw-r--r--gc.rb60
-rw-r--r--gem_prelude.rb7
-rw-r--r--gems/bundled_gems30
-rw-r--r--gems/lib/core_assertions.rb1
-rw-r--r--gems/lib/envutil.rb1
-rw-r--r--gems/lib/rake/extensiontask.rb (renamed from tool/dummy-rake-compiler/rake/extensiontask.rb)5
-rw-r--r--goruby.c32
-rw-r--r--hash.c841
-rw-r--r--hrtime.h11
-rw-r--r--id_table.c138
-rw-r--r--id_table.h5
-rw-r--r--include/ruby/assert.h2
-rw-r--r--include/ruby/debug.h35
-rw-r--r--include/ruby/fiber/scheduler.h90
-rw-r--r--include/ruby/internal/abi.h11
-rw-r--r--include/ruby/internal/anyargs.h37
-rw-r--r--include/ruby/internal/arithmetic.h3
-rw-r--r--include/ruby/internal/attr/nodiscard.h2
-rw-r--r--include/ruby/internal/attr/nonstring.h32
-rw-r--r--include/ruby/internal/config.h2
-rw-r--r--include/ruby/internal/core/robject.h56
-rw-r--r--include/ruby/internal/encoding/ctype.h101
-rw-r--r--include/ruby/internal/encoding/encoding.h10
-rw-r--r--include/ruby/internal/encoding/transcode.h18
-rw-r--r--include/ruby/internal/eval.h33
-rw-r--r--include/ruby/internal/fl_type.h19
-rw-r--r--include/ruby/internal/gc.h13
-rw-r--r--include/ruby/internal/intern/array.h6
-rw-r--r--include/ruby/internal/intern/class.h12
-rw-r--r--include/ruby/internal/intern/cont.h22
-rw-r--r--include/ruby/internal/intern/file.h2
-rw-r--r--include/ruby/internal/intern/gc.h4
-rw-r--r--include/ruby/internal/intern/object.h4
-rw-r--r--include/ruby/internal/intern/select/posix.h2
-rw-r--r--include/ruby/internal/memory.h6
-rw-r--r--include/ruby/internal/scan_args.h2
-rw-r--r--include/ruby/internal/special_consts.h87
-rw-r--r--include/ruby/io.h114
-rw-r--r--include/ruby/io/buffer.h29
-rw-r--r--include/ruby/memory_view.h2
-rw-r--r--include/ruby/onigmo.h8
-rw-r--r--include/ruby/random.h63
-rw-r--r--include/ruby/ruby.h38
-rw-r--r--include/ruby/st.h2
-rw-r--r--include/ruby/thread.h4
-rw-r--r--include/ruby/util.h4
-rw-r--r--include/ruby/win32.h33
-rw-r--r--inits.c5
-rw-r--r--insns.def192
-rw-r--r--internal.h6
-rw-r--r--internal/array.h12
-rw-r--r--internal/basic_operators.h64
-rw-r--r--internal/class.h59
-rw-r--r--internal/cmdlineopt.h3
-rw-r--r--internal/compar.h32
-rw-r--r--internal/cont.h8
-rw-r--r--internal/encoding.h4
-rw-r--r--internal/eval.h1
-rw-r--r--internal/gc.h6
-rw-r--r--internal/hash.h1
-rw-r--r--internal/imemo.h6
-rw-r--r--internal/numeric.h8
-rw-r--r--internal/object.h22
-rw-r--r--internal/parse.h2
-rw-r--r--internal/string.h4
-rw-r--r--internal/thread.h2
-rw-r--r--internal/time.h5
-rw-r--r--internal/variable.h15
-rw-r--r--internal/vm.h2
-rw-r--r--io.c5222
-rw-r--r--io_buffer.c2183
-rw-r--r--iseq.c1868
-rw-r--r--iseq.h76
-rw-r--r--lex.c.blt2
-rw-r--r--lib/English.gemspec2
-rw-r--r--lib/abbrev.gemspec2
-rw-r--r--lib/benchmark/version.rb2
-rw-r--r--lib/bundler.rb100
-rw-r--r--lib/bundler/bundler.gemspec18
-rw-r--r--lib/bundler/cli.rb60
-rw-r--r--lib/bundler/cli/add.rb2
-rw-r--r--lib/bundler/cli/binstubs.rb8
-rw-r--r--lib/bundler/cli/check.rb2
-rw-r--r--lib/bundler/cli/common.rb2
-rw-r--r--lib/bundler/cli/console.rb4
-rw-r--r--lib/bundler/cli/doctor.rb10
-rw-r--r--lib/bundler/cli/gem.rb102
-rw-r--r--lib/bundler/cli/info.rb2
-rw-r--r--lib/bundler/cli/init.rb8
-rw-r--r--lib/bundler/cli/install.rb14
-rw-r--r--lib/bundler/cli/lock.rb13
-rw-r--r--lib/bundler/cli/open.rb10
-rw-r--r--lib/bundler/cli/outdated.rb21
-rw-r--r--lib/bundler/cli/platform.rb12
-rw-r--r--lib/bundler/cli/viz.rb2
-rw-r--r--lib/bundler/compact_index_client/cache.rb2
-rw-r--r--lib/bundler/compact_index_client/updater.rb79
-rw-r--r--lib/bundler/constants.rb2
-rw-r--r--lib/bundler/current_ruby.rb21
-rw-r--r--lib/bundler/definition.rb389
-rw-r--r--lib/bundler/dep_proxy.rb55
-rw-r--r--lib/bundler/dependency.rb109
-rw-r--r--lib/bundler/dsl.rb9
-rw-r--r--lib/bundler/endpoint_specification.rb15
-rw-r--r--lib/bundler/env.rb4
-rw-r--r--lib/bundler/environment_preserver.rb5
-rw-r--r--lib/bundler/errors.rb12
-rw-r--r--lib/bundler/feature_flag.rb2
-rw-r--r--lib/bundler/fetcher.rb36
-rw-r--r--lib/bundler/fetcher/compact_index.rb24
-rw-r--r--lib/bundler/fetcher/dependency.rb8
-rw-r--r--lib/bundler/fetcher/downloader.rb9
-rw-r--r--lib/bundler/fetcher/index.rb3
-rw-r--r--lib/bundler/force_platform.rb2
-rw-r--r--lib/bundler/friendly_errors.rb5
-rw-r--r--lib/bundler/gem_helper.rb7
-rw-r--r--lib/bundler/gem_helpers.rb9
-rw-r--r--lib/bundler/gem_version_promoter.rb151
-rw-r--r--lib/bundler/graph.rb6
-rw-r--r--lib/bundler/index.rb60
-rw-r--r--lib/bundler/injector.rb9
-rw-r--r--lib/bundler/inline.rb20
-rw-r--r--lib/bundler/installer.rb46
-rw-r--r--lib/bundler/installer/parallel_installer.rb38
-rw-r--r--lib/bundler/installer/standalone.rb22
-rw-r--r--lib/bundler/lazy_specification.rb117
-rw-r--r--lib/bundler/lockfile_generator.rb6
-rw-r--r--lib/bundler/lockfile_parser.rb33
-rw-r--r--lib/bundler/man/bundle-add.18
-rw-r--r--lib/bundler/man/bundle-add.1.ronn5
-rw-r--r--lib/bundler/man/bundle-binstubs.12
-rw-r--r--lib/bundler/man/bundle-cache.112
-rw-r--r--lib/bundler/man/bundle-cache.1.ronn11
-rw-r--r--lib/bundler/man/bundle-check.12
-rw-r--r--lib/bundler/man/bundle-clean.14
-rw-r--r--lib/bundler/man/bundle-clean.1.ronn2
-rw-r--r--lib/bundler/man/bundle-config.132
-rw-r--r--lib/bundler/man/bundle-config.1.ronn23
-rw-r--r--lib/bundler/man/bundle-console.153
-rw-r--r--lib/bundler/man/bundle-console.1.ronn44
-rw-r--r--lib/bundler/man/bundle-doctor.12
-rw-r--r--lib/bundler/man/bundle-exec.112
-rw-r--r--lib/bundler/man/bundle-exec.1.ronn12
-rw-r--r--lib/bundler/man/bundle-gem.164
-rw-r--r--lib/bundler/man/bundle-gem.1.ronn10
-rw-r--r--lib/bundler/man/bundle-help.113
-rw-r--r--lib/bundler/man/bundle-help.1.ronn12
-rw-r--r--lib/bundler/man/bundle-info.16
-rw-r--r--lib/bundler/man/bundle-info.1.ronn6
-rw-r--r--lib/bundler/man/bundle-init.16
-rw-r--r--lib/bundler/man/bundle-init.1.ronn2
-rw-r--r--lib/bundler/man/bundle-inject.17
-rw-r--r--lib/bundler/man/bundle-inject.1.ronn4
-rw-r--r--lib/bundler/man/bundle-install.135
-rw-r--r--lib/bundler/man/bundle-install.1.ronn35
-rw-r--r--lib/bundler/man/bundle-list.12
-rw-r--r--lib/bundler/man/bundle-lock.12
-rw-r--r--lib/bundler/man/bundle-open.124
-rw-r--r--lib/bundler/man/bundle-open.1.ronn10
-rw-r--r--lib/bundler/man/bundle-outdated.122
-rw-r--r--lib/bundler/man/bundle-outdated.1.ronn21
-rw-r--r--lib/bundler/man/bundle-platform.122
-rw-r--r--lib/bundler/man/bundle-platform.1.ronn21
-rw-r--r--lib/bundler/man/bundle-plugin.181
-rw-r--r--lib/bundler/man/bundle-plugin.1.ronn59
-rw-r--r--lib/bundler/man/bundle-pristine.12
-rw-r--r--lib/bundler/man/bundle-remove.12
-rw-r--r--lib/bundler/man/bundle-show.12
-rw-r--r--lib/bundler/man/bundle-update.12
-rw-r--r--lib/bundler/man/bundle-version.135
-rw-r--r--lib/bundler/man/bundle-version.1.ronn24
-rw-r--r--lib/bundler/man/bundle-viz.15
-rw-r--r--lib/bundler/man/bundle-viz.1.ronn2
-rw-r--r--lib/bundler/man/bundle.125
-rw-r--r--lib/bundler/man/bundle.1.ronn19
-rw-r--r--lib/bundler/man/gemfile.586
-rw-r--r--lib/bundler/man/gemfile.5.ronn71
-rw-r--r--lib/bundler/man/index.txt4
-rw-r--r--lib/bundler/match_metadata.rb13
-rw-r--r--lib/bundler/match_remote_metadata.rb29
-rw-r--r--lib/bundler/mirror.rb12
-rw-r--r--lib/bundler/plugin.rb4
-rw-r--r--lib/bundler/plugin/index.rb10
-rw-r--r--lib/bundler/plugin/installer.rb7
-rw-r--r--lib/bundler/plugin/installer/rubygems.rb4
-rw-r--r--lib/bundler/remote_specification.rb19
-rw-r--r--lib/bundler/resolver.rb663
-rw-r--r--lib/bundler/resolver/base.rb107
-rw-r--r--lib/bundler/resolver/candidate.rb94
-rw-r--r--lib/bundler/resolver/incompatibility.rb15
-rw-r--r--lib/bundler/resolver/package.rb72
-rw-r--r--lib/bundler/resolver/root.rb25
-rw-r--r--lib/bundler/resolver/spec_group.rb113
-rw-r--r--lib/bundler/ruby_dsl.rb8
-rw-r--r--lib/bundler/ruby_version.rb16
-rw-r--r--lib/bundler/rubygems_ext.rb127
-rw-r--r--lib/bundler/rubygems_gem_installer.rb11
-rw-r--r--lib/bundler/rubygems_integration.rb20
-rw-r--r--lib/bundler/runtime.rb2
-rw-r--r--lib/bundler/safe_marshal.rb31
-rw-r--r--lib/bundler/settings.rb16
-rw-r--r--lib/bundler/setup.rb5
-rw-r--r--lib/bundler/shared_helpers.rb6
-rw-r--r--lib/bundler/source.rb2
-rw-r--r--lib/bundler/source/git.rb85
-rw-r--r--lib/bundler/source/git/git_proxy.rb316
-rw-r--r--lib/bundler/source/metadata.rb3
-rw-r--r--lib/bundler/source/path.rb12
-rw-r--r--lib/bundler/source/path/installer.rb23
-rw-r--r--lib/bundler/source/rubygems.rb107
-rw-r--r--lib/bundler/source_list.rb10
-rw-r--r--lib/bundler/spec_set.rb97
-rw-r--r--lib/bundler/templates/Executable2
-rw-r--r--lib/bundler/templates/Executable.bundler15
-rw-r--r--lib/bundler/templates/Executable.standalone2
-rw-r--r--lib/bundler/templates/gems.rb5
-rw-r--r--lib/bundler/templates/newgem/Cargo.toml.tt7
-rw-r--r--lib/bundler/templates/newgem/Gemfile.tt3
-rw-r--r--lib/bundler/templates/newgem/README.md.tt10
-rw-r--r--lib/bundler/templates/newgem/Rakefile.tt13
-rw-r--r--lib/bundler/templates/newgem/bin/console.tt4
-rw-r--r--lib/bundler/templates/newgem/circleci/config.yml.tt12
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt15
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/extconf-c.rb.tt10
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt6
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt5
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/newgem.c.tt2
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt12
-rw-r--r--lib/bundler/templates/newgem/github/workflows/main.yml.tt10
-rw-r--r--lib/bundler/templates/newgem/gitignore.tt3
-rw-r--r--lib/bundler/templates/newgem/gitlab-ci.yml.tt17
-rw-r--r--lib/bundler/templates/newgem/newgem.gemspec.tt11
-rw-r--r--lib/bundler/templates/newgem/travis.yml.tt6
-rw-r--r--lib/bundler/ui/rg_proxy.rb2
-rw-r--r--lib/bundler/ui/shell.rb47
-rw-r--r--lib/bundler/ui/silent.rb26
-rw-r--r--lib/bundler/uri_normalizer.rb23
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool.rb4
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb6
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb1
-rw-r--r--lib/bundler/vendor/fileutils/lib/fileutils.rb1760
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo.rb11
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb57
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb88
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb255
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb36
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb66
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb62
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb63
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb61
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb126
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb46
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb36
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb164
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/errors.rb149
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb6
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb112
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb67
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb839
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb46
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/state.rb58
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub.rb31
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb20
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb189
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb182
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb150
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb43
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb121
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb45
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb19
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb60
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb105
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb3
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb129
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb411
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb248
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb178
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/basic.rb2
-rw-r--r--lib/bundler/vendor/tmpdir/lib/tmpdir.rb154
-rw-r--r--lib/bundler/vendor/uri/lib/uri.rb5
-rw-r--r--lib/bundler/vendor/uri/lib/uri/common.rb80
-rw-r--r--lib/bundler/vendor/uri/lib/uri/file.rb8
-rw-r--r--lib/bundler/vendor/uri/lib/uri/ftp.rb3
-rw-r--r--lib/bundler/vendor/uri/lib/uri/generic.rb34
-rw-r--r--lib/bundler/vendor/uri/lib/uri/http.rb42
-rw-r--r--lib/bundler/vendor/uri/lib/uri/https.rb3
-rw-r--r--lib/bundler/vendor/uri/lib/uri/ldap.rb2
-rw-r--r--lib/bundler/vendor/uri/lib/uri/ldaps.rb3
-rw-r--r--lib/bundler/vendor/uri/lib/uri/mailto.rb4
-rw-r--r--lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb24
-rw-r--r--lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb17
-rw-r--r--lib/bundler/vendor/uri/lib/uri/version.rb2
-rw-r--r--lib/bundler/vendor/uri/lib/uri/ws.rb3
-rw-r--r--lib/bundler/vendor/uri/lib/uri/wss.rb3
-rw-r--r--lib/bundler/vendored_molinillo.rb4
-rw-r--r--lib/bundler/vendored_persistent.rb34
-rw-r--r--lib/bundler/vendored_pub_grub.rb (renamed from lib/bundler/vendored_tmpdir.rb)2
-rw-r--r--lib/bundler/version.rb6
-rw-r--r--lib/bundler/version_ranges.rb122
-rw-r--r--lib/bundler/worker.rb12
-rw-r--r--lib/cgi.rb2
-rw-r--r--lib/cgi/cookie.rb49
-rw-r--r--lib/cgi/core.rb45
-rw-r--r--lib/cgi/util.rb53
-rw-r--r--lib/csv.rb489
-rw-r--r--lib/csv/fields_converter.rb5
-rw-r--r--lib/csv/input_record_separator.rb15
-rw-r--r--lib/csv/parser.rb293
-rw-r--r--lib/csv/row.rb231
-rw-r--r--lib/csv/table.rb626
-rw-r--r--lib/csv/version.rb2
-rw-r--r--lib/csv/writer.rb10
-rw-r--r--lib/delegate.rb2
-rw-r--r--lib/did_you_mean/spell_checkers/method_name_checker.rb7
-rw-r--r--lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb2
-rw-r--r--lib/did_you_mean/version.rb2
-rw-r--r--lib/drb/version.rb2
-rw-r--r--lib/erb.gemspec13
-rw-r--r--lib/erb.rb579
-rw-r--r--lib/erb/compiler.rb471
-rw-r--r--lib/erb/def_method.rb46
-rw-r--r--lib/erb/util.rb62
-rw-r--r--lib/erb/version.rb2
-rw-r--r--lib/error_highlight/base.rb67
-rw-r--r--lib/error_highlight/core_ext.rb43
-rw-r--r--lib/error_highlight/version.rb2
-rw-r--r--lib/fileutils.rb171
-rw-r--r--lib/forwardable.rb4
-rw-r--r--lib/getoptlong.rb2
-rw-r--r--lib/ipaddr.rb6
-rw-r--r--lib/irb.rb91
-rw-r--r--lib/irb/cmd/backtrace.rb21
-rw-r--r--lib/irb/cmd/break.rb21
-rw-r--r--lib/irb/cmd/catch.rb21
-rw-r--r--lib/irb/cmd/chws.rb6
-rw-r--r--lib/irb/cmd/continue.rb17
-rw-r--r--lib/irb/cmd/debug.rb136
-rw-r--r--lib/irb/cmd/delete.rb17
-rw-r--r--lib/irb/cmd/edit.rb61
-rw-r--r--lib/irb/cmd/finish.rb17
-rw-r--r--lib/irb/cmd/help.rb14
-rw-r--r--lib/irb/cmd/info.rb31
-rw-r--r--lib/irb/cmd/irb_info.rb37
-rw-r--r--lib/irb/cmd/load.rb34
-rw-r--r--lib/irb/cmd/ls.rb13
-rw-r--r--lib/irb/cmd/measure.rb3
-rw-r--r--lib/irb/cmd/next.rb17
-rw-r--r--lib/irb/cmd/nop.rb24
-rw-r--r--lib/irb/cmd/pushws.rb9
-rw-r--r--lib/irb/cmd/show_cmds.rb39
-rw-r--r--lib/irb/cmd/show_source.rb103
-rw-r--r--lib/irb/cmd/step.rb17
-rw-r--r--lib/irb/cmd/subirb.rb38
-rw-r--r--lib/irb/cmd/whereami.rb3
-rw-r--r--lib/irb/color.rb66
-rw-r--r--lib/irb/color_printer.rb3
-rw-r--r--lib/irb/completion.rb136
-rw-r--r--lib/irb/context.rb69
-rw-r--r--lib/irb/ext/multi-irb.rb1
-rw-r--r--lib/irb/ext/save-history.rb10
-rw-r--r--lib/irb/extend-command.rb123
-rw-r--r--lib/irb/init.rb48
-rw-r--r--lib/irb/input-method.rb26
-rw-r--r--lib/irb/irb.gemspec6
-rw-r--r--lib/irb/lc/error.rb5
-rw-r--r--lib/irb/lc/help-message2
-rw-r--r--lib/irb/lc/ja/error.rb5
-rw-r--r--lib/irb/ruby-lex.rb119
-rw-r--r--lib/irb/version.rb4
-rw-r--r--lib/logger.rb10
-rw-r--r--lib/logger/formatter.rb5
-rw-r--r--lib/logger/log_device.rb6
-rw-r--r--lib/logger/logger.gemspec1
-rw-r--r--lib/logger/version.rb2
-rw-r--r--lib/mkmf.rb28
-rw-r--r--lib/mutex_m.rb2
-rw-r--r--lib/net/http.rb2053
-rw-r--r--lib/net/http/backward.rb2
-rw-r--r--lib/net/http/exceptions.rb2
-rw-r--r--lib/net/http/generic_request.rb109
-rw-r--r--lib/net/http/header.rb786
-rw-r--r--lib/net/http/net-http.gemspec13
-rw-r--r--lib/net/http/proxy_delta.rb2
-rw-r--r--lib/net/http/request.rb79
-rw-r--r--lib/net/http/requests.rb352
-rw-r--r--lib/net/http/response.rb200
-rw-r--r--lib/net/http/responses.rb1000
-rw-r--r--lib/net/http/status.rb13
-rw-r--r--lib/net/https.rb2
-rw-r--r--lib/net/protocol.rb75
-rw-r--r--lib/open-uri.gemspec4
-rw-r--r--lib/open-uri.rb35
-rw-r--r--lib/open3/version.rb2
-rw-r--r--lib/optparse.rb47
-rw-r--r--lib/pp.gemspec2
-rw-r--r--lib/pp.rb20
-rw-r--r--lib/pstore.rb10
-rw-r--r--lib/racc/info.rb2
-rw-r--r--lib/racc/parser-text.rb2
-rw-r--r--lib/racc/racc.gemspec2
-rw-r--r--lib/racc/statetransitiontable.rb2
-rw-r--r--lib/random/formatter.rb62
-rw-r--r--lib/rdoc/generator/markup.rb2
-rw-r--r--lib/rdoc/generator/template/darkfish/_head.rhtml20
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml6
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml29
-rw-r--r--lib/rdoc/generator/template/darkfish/class.rhtml42
-rw-r--r--lib/rdoc/generator/template/darkfish/index.rhtml2
-rw-r--r--lib/rdoc/generator/template/darkfish/js/darkfish.js2
-rw-r--r--lib/rdoc/generator/template/darkfish/js/search.js2
-rw-r--r--lib/rdoc/generator/template/darkfish/table_of_contents.rhtml4
-rw-r--r--lib/rdoc/markdown.rb500
-rw-r--r--lib/rdoc/markdown/literals.rb109
-rw-r--r--lib/rdoc/markup.rb633
-rw-r--r--lib/rdoc/markup/attribute_manager.rb33
-rw-r--r--lib/rdoc/markup/parser.rb18
-rw-r--r--lib/rdoc/markup/to_html.rb26
-rw-r--r--lib/rdoc/markup/to_rdoc.rb23
-rw-r--r--lib/rdoc/options.rb12
-rw-r--r--lib/rdoc/parser.rb17
-rw-r--r--lib/rdoc/parser/c.rb17
-rw-r--r--lib/rdoc/parser/ruby.rb10
-rw-r--r--lib/rdoc/rd/block_parser.rb20
-rw-r--r--lib/rdoc/rdoc.gemspec5
-rw-r--r--lib/rdoc/rdoc.rb16
-rw-r--r--lib/rdoc/ri/driver.rb10
-rw-r--r--lib/rdoc/store.rb45
-rw-r--r--lib/rdoc/version.rb2
-rw-r--r--lib/reline.rb60
-rw-r--r--lib/reline/ansi.rb2
-rw-r--r--lib/reline/config.rb75
-rw-r--r--lib/reline/general_io.rb6
-rw-r--r--lib/reline/line_editor.rb8
-rw-r--r--lib/reline/version.rb2
-rw-r--r--lib/resolv-replace.gemspec2
-rw-r--r--lib/resolv.gemspec2
-rw-r--r--lib/resolv.rb6
-rw-r--r--lib/ruby_vm/mjit/c_pointer.rb329
-rw-r--r--lib/ruby_vm/mjit/c_type.rb91
-rw-r--r--lib/ruby_vm/mjit/compiler.rb952
-rw-r--r--lib/ruby_vm/mjit/hooks.rb32
-rw-r--r--lib/rubygems.rb154
-rw-r--r--lib/rubygems/available_set.rb7
-rw-r--r--lib/rubygems/basic_specification.rb9
-rw-r--r--lib/rubygems/bundler_version_finder.rb4
-rw-r--r--lib/rubygems/command.rb73
-rw-r--r--lib/rubygems/command_manager.rb45
-rw-r--r--lib/rubygems/commands/build_command.rb20
-rw-r--r--lib/rubygems/commands/cert_command.rb67
-rw-r--r--lib/rubygems/commands/check_command.rb41
-rw-r--r--lib/rubygems/commands/cleanup_command.rb35
-rw-r--r--lib/rubygems/commands/contents_command.rb27
-rw-r--r--lib/rubygems/commands/dependency_command.rb33
-rw-r--r--lib/rubygems/commands/environment_command.rb11
-rw-r--r--lib/rubygems/commands/exec_command.rb249
-rw-r--r--lib/rubygems/commands/fetch_command.rb19
-rw-r--r--lib/rubygems/commands/generate_index_command.rb35
-rw-r--r--lib/rubygems/commands/help_command.rb13
-rw-r--r--lib/rubygems/commands/info_command.rb6
-rw-r--r--lib/rubygems/commands/install_command.rb51
-rw-r--r--lib/rubygems/commands/list_command.rb7
-rw-r--r--lib/rubygems/commands/lock_command.rb9
-rw-r--r--lib/rubygems/commands/mirror_command.rb7
-rw-r--r--lib/rubygems/commands/open_command.rb19
-rw-r--r--lib/rubygems/commands/outdated_command.rb11
-rw-r--r--lib/rubygems/commands/owner_command.rb31
-rw-r--r--lib/rubygems/commands/pristine_command.rb82
-rw-r--r--lib/rubygems/commands/push_command.rb17
-rw-r--r--lib/rubygems/commands/query_command.rb17
-rw-r--r--lib/rubygems/commands/rdoc_command.rb40
-rw-r--r--lib/rubygems/commands/search_command.rb7
-rw-r--r--lib/rubygems/commands/server_command.rb7
-rw-r--r--lib/rubygems/commands/setup_command.rb175
-rw-r--r--lib/rubygems/commands/signin_command.rb19
-rw-r--r--lib/rubygems/commands/signout_command.rb15
-rw-r--r--lib/rubygems/commands/sources_command.rb39
-rw-r--r--lib/rubygems/commands/specification_command.rb27
-rw-r--r--lib/rubygems/commands/stale_command.rb5
-rw-r--r--lib/rubygems/commands/uninstall_command.rb84
-rw-r--r--lib/rubygems/commands/unpack_command.rb27
-rw-r--r--lib/rubygems/commands/update_command.rb63
-rw-r--r--lib/rubygems/commands/which_command.rb15
-rw-r--r--lib/rubygems/commands/yank_command.rb23
-rw-r--r--lib/rubygems/config_file.rb76
-rw-r--r--lib/rubygems/core_ext/kernel_gem.rb7
-rw-r--r--lib/rubygems/core_ext/kernel_require.rb224
-rw-r--r--lib/rubygems/core_ext/kernel_warn.rb70
-rw-r--r--lib/rubygems/core_ext/tcpsocket_init.rb4
-rw-r--r--lib/rubygems/defaults.rb51
-rw-r--r--lib/rubygems/dependency.rb26
-rw-r--r--lib/rubygems/dependency_installer.rb75
-rw-r--r--lib/rubygems/dependency_list.rb13
-rw-r--r--lib/rubygems/deprecate.rb5
-rw-r--r--lib/rubygems/doctor.rb37
-rw-r--r--lib/rubygems/errors.rb5
-rw-r--r--lib/rubygems/exceptions.rb24
-rw-r--r--lib/rubygems/ext.rb15
-rw-r--r--lib/rubygems/ext/build_error.rb3
-rw-r--r--lib/rubygems/ext/builder.rb58
-rw-r--r--lib/rubygems/ext/cargo_builder.rb261
-rw-r--r--lib/rubygems/ext/cargo_builder/link_flag_converter.rb14
-rw-r--r--lib/rubygems/ext/cmake_builder.rb4
-rw-r--r--lib/rubygems/ext/configure_builder.rb3
-rw-r--r--lib/rubygems/ext/ext_conf_builder.rb20
-rw-r--r--lib/rubygems/ext/rake_builder.rb12
-rw-r--r--lib/rubygems/gem_runner.rb11
-rw-r--r--lib/rubygems/gemcutter_utilities.rb124
-rw-r--r--lib/rubygems/gemcutter_utilities/webauthn_listener.rb105
-rw-r--r--lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb163
-rw-r--r--lib/rubygems/gemcutter_utilities/webauthn_poller.rb78
-rw-r--r--lib/rubygems/indexer.rb57
-rw-r--r--lib/rubygems/install_default_message.rb5
-rw-r--r--lib/rubygems/install_message.rb5
-rw-r--r--lib/rubygems/install_update_options.rb111
-rw-r--r--lib/rubygems/installer.rb100
-rw-r--r--lib/rubygems/installer_uninstaller_utils.rb4
-rw-r--r--lib/rubygems/local_remote_options.rb37
-rw-r--r--lib/rubygems/mock_gem_ui.rb5
-rw-r--r--lib/rubygems/name_tuple.rb9
-rw-r--r--lib/rubygems/optparse.rb2
-rw-r--r--lib/rubygems/optparse/lib/optparse.rb35
-rw-r--r--lib/rubygems/package.rb112
-rw-r--r--lib/rubygems/package/digest_io.rb1
-rw-r--r--lib/rubygems/package/file_source.rb5
-rw-r--r--lib/rubygems/package/io_source.rb1
-rw-r--r--lib/rubygems/package/old.rb17
-rw-r--r--lib/rubygems/package/source.rb1
-rw-r--r--lib/rubygems/package/tar_header.rb125
-rw-r--r--lib/rubygems/package/tar_reader.rb31
-rw-r--r--lib/rubygems/package/tar_reader/entry.rb100
-rw-r--r--lib/rubygems/package/tar_writer.rb15
-rw-r--r--lib/rubygems/package_task.rb9
-rw-r--r--lib/rubygems/path_support.rb1
-rw-r--r--lib/rubygems/platform.rb132
-rw-r--r--lib/rubygems/psych_tree.rb3
-rw-r--r--lib/rubygems/query_utils.rb70
-rw-r--r--lib/rubygems/rdoc.rb5
-rw-r--r--lib/rubygems/remote_fetcher.rb45
-rw-r--r--lib/rubygems/request.rb45
-rw-r--r--lib/rubygems/request/connection_pools.rb8
-rw-r--r--lib/rubygems/request/http_pool.rb3
-rw-r--r--lib/rubygems/request/https_pool.rb1
-rw-r--r--lib/rubygems/request_set.rb39
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb246
-rw-r--r--lib/rubygems/request_set/lockfile.rb11
-rw-r--r--lib/rubygems/request_set/lockfile/parser.rb55
-rw-r--r--lib/rubygems/request_set/lockfile/tokenizer.rb8
-rw-r--r--lib/rubygems/requirement.rb29
-rw-r--r--lib/rubygems/resolver.rb81
-rw-r--r--lib/rubygems/resolver/activation_request.rb7
-rw-r--r--lib/rubygems/resolver/api_set.rb9
-rw-r--r--lib/rubygems/resolver/api_specification.rb13
-rw-r--r--lib/rubygems/resolver/best_set.rb11
-rw-r--r--lib/rubygems/resolver/composed_set.rb1
-rw-r--r--lib/rubygems/resolver/conflict.rb21
-rw-r--r--lib/rubygems/resolver/current_set.rb1
-rw-r--r--lib/rubygems/resolver/dependency_request.rb5
-rw-r--r--lib/rubygems/resolver/git_set.rb5
-rw-r--r--lib/rubygems/resolver/git_specification.rb13
-rw-r--r--lib/rubygems/resolver/index_set.rb7
-rw-r--r--lib/rubygems/resolver/index_specification.rb12
-rw-r--r--lib/rubygems/resolver/installed_specification.rb9
-rw-r--r--lib/rubygems/resolver/installer_set.rb32
-rw-r--r--lib/rubygems/resolver/local_specification.rb5
-rw-r--r--lib/rubygems/resolver/lock_set.rb9
-rw-r--r--lib/rubygems/resolver/lock_specification.rb9
-rw-r--r--lib/rubygems/resolver/molinillo.rb3
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb2
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb58
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb2
-rw-r--r--lib/rubygems/resolver/requirement_list.rb1
-rw-r--r--lib/rubygems/resolver/set.rb1
-rw-r--r--lib/rubygems/resolver/source_set.rb2
-rw-r--r--lib/rubygems/resolver/spec_specification.rb1
-rw-r--r--lib/rubygems/resolver/specification.rb3
-rw-r--r--lib/rubygems/resolver/stats.rb3
-rw-r--r--lib/rubygems/resolver/vendor_set.rb3
-rw-r--r--lib/rubygems/resolver/vendor_specification.rb7
-rw-r--r--lib/rubygems/s3_uri_signer.rb18
-rw-r--r--lib/rubygems/safe_yaml.rb6
-rw-r--r--lib/rubygems/security.rb72
-rw-r--r--lib/rubygems/security/policies.rb95
-rw-r--r--lib/rubygems/security/policy.rb37
-rw-r--r--lib/rubygems/security/signer.rb11
-rw-r--r--lib/rubygems/security/trust_dir.rb9
-rw-r--r--lib/rubygems/security_option.rb11
-rw-r--r--lib/rubygems/shellwords.rb3
-rw-r--r--lib/rubygems/source.rb32
-rw-r--r--lib/rubygems/source/git.rb44
-rw-r--r--lib/rubygems/source/installed.rb3
-rw-r--r--lib/rubygems/source/local.rb5
-rw-r--r--lib/rubygems/source/lock.rb1
-rw-r--r--lib/rubygems/source/specific_file.rb3
-rw-r--r--lib/rubygems/source/vendor.rb1
-rw-r--r--lib/rubygems/spec_fetcher.rb19
-rw-r--r--lib/rubygems/specification.rb214
-rw-r--r--lib/rubygems/specification_policy.rb51
-rw-r--r--lib/rubygems/stub_specification.rb20
-rw-r--r--lib/rubygems/text.rb2
-rw-r--r--lib/rubygems/tsort.rb2
-rw-r--r--lib/rubygems/tsort/lib/tsort.rb618
-rw-r--r--lib/rubygems/uninstaller.rb37
-rw-r--r--lib/rubygems/update_suggestion.rb69
-rw-r--r--lib/rubygems/uri.rb8
-rw-r--r--lib/rubygems/uri_formatter.rb2
-rw-r--r--lib/rubygems/user_interaction.rb26
-rw-r--r--lib/rubygems/util.rb27
-rw-r--r--lib/rubygems/util/licenses.rb7
-rw-r--r--lib/rubygems/util/list.rb1
-rw-r--r--lib/rubygems/validator.rb13
-rw-r--r--lib/rubygems/version.rb26
-rw-r--r--lib/rubygems/version_option.rb7
-rw-r--r--lib/securerandom.gemspec2
-rw-r--r--lib/set.rb4
-rw-r--r--lib/set/set.gemspec2
-rw-r--r--lib/syntax_suggest.rb3
-rw-r--r--lib/syntax_suggest/api.rb201
-rw-r--r--lib/syntax_suggest/around_block_scan.rb232
-rw-r--r--lib/syntax_suggest/block_expand.rb165
-rw-r--r--lib/syntax_suggest/capture/before_after_keyword_ends.rb85
-rw-r--r--lib/syntax_suggest/capture/falling_indent_lines.rb71
-rw-r--r--lib/syntax_suggest/capture_code_context.rb245
-rw-r--r--lib/syntax_suggest/clean_document.rb306
-rw-r--r--lib/syntax_suggest/cli.rb130
-rw-r--r--lib/syntax_suggest/code_block.rb100
-rw-r--r--lib/syntax_suggest/code_frontier.rb178
-rw-r--r--lib/syntax_suggest/code_line.rb237
-rw-r--r--lib/syntax_suggest/code_search.rb139
-rw-r--r--lib/syntax_suggest/core_ext.rb114
-rw-r--r--lib/syntax_suggest/display_code_with_line_numbers.rb70
-rw-r--r--lib/syntax_suggest/display_invalid_blocks.rb83
-rw-r--r--lib/syntax_suggest/explain_syntax.rb103
-rw-r--r--lib/syntax_suggest/left_right_lex_count.rb168
-rw-r--r--lib/syntax_suggest/lex_all.rb55
-rw-r--r--lib/syntax_suggest/lex_value.rb70
-rw-r--r--lib/syntax_suggest/parse_blocks_from_indent_line.rb60
-rw-r--r--lib/syntax_suggest/pathname_from_message.rb59
-rw-r--r--lib/syntax_suggest/priority_engulf_queue.rb63
-rw-r--r--lib/syntax_suggest/priority_queue.rb105
-rw-r--r--lib/syntax_suggest/ripper_errors.rb36
-rw-r--r--lib/syntax_suggest/scan_history.rb134
-rw-r--r--lib/syntax_suggest/syntax_suggest.gemspec32
-rw-r--r--lib/syntax_suggest/unvisited_lines.rb36
-rw-r--r--lib/syntax_suggest/version.rb5
-rw-r--r--lib/tempfile.gemspec2
-rw-r--r--lib/tempfile.rb18
-rw-r--r--lib/time.gemspec2
-rw-r--r--lib/time.rb6
-rw-r--r--lib/timeout.rb3
-rw-r--r--lib/tmpdir.gemspec4
-rw-r--r--lib/tmpdir.rb24
-rw-r--r--lib/tsort.gemspec2
-rw-r--r--lib/un.gemspec2
-rw-r--r--lib/un.rb4
-rw-r--r--lib/unicode_normalize/tables.rb77
-rw-r--r--lib/uri/common.rb2
-rw-r--r--lib/uri/generic.rb15
-rw-r--r--lib/uri/rfc2396_parser.rb4
-rw-r--r--lib/uri/rfc3986_parser.rb7
-rw-r--r--lib/uri/version.rb2
-rw-r--r--lib/weakref.rb2
-rw-r--r--lib/yaml/yaml.gemspec2
-rwxr-xr-xlibexec/bundle21
-rwxr-xr-xlibexec/erb12
-rwxr-xr-xlibexec/syntax_suggest7
-rw-r--r--load.c713
-rw-r--r--localeinit.c10
-rw-r--r--main.c6
-rw-r--r--man/irb.125
-rw-r--r--marshal.c1803
-rw-r--r--math.c68
-rw-r--r--memory_view.c12
-rw-r--r--method.h12
-rw-r--r--mini_builtin.c2
-rwxr-xr-xmisc/lldb_cruby.py59
-rw-r--r--misc/lldb_rb/commands/command_template.py30
-rw-r--r--misc/lldb_rb/commands/heap_page_command.py26
-rw-r--r--misc/lldb_rb/commands/rclass_ext_command.py14
-rw-r--r--misc/lldb_rb/constants.py4
-rw-r--r--misc/lldb_rb/rb_base_command.py69
-rw-r--r--missing/flock.c8
-rw-r--r--mjit.c1390
-rw-r--r--mjit.h155
-rw-r--r--mjit.rb24
-rw-r--r--mjit_c.c43
-rw-r--r--mjit_c.h97
-rw-r--r--mjit_c.rb807
-rw-r--r--mjit_compile.c598
-rw-r--r--mjit_unit.h29
-rw-r--r--node.c1440
-rw-r--r--node.h36
-rw-r--r--numeric.c1631
-rw-r--r--numeric.rb91
-rw-r--r--object.c867
-rw-r--r--pack.c2268
-rw-r--r--pack.rb298
-rw-r--r--parse.y1095
-rw-r--r--probes_helper.h14
-rw-r--r--proc.c916
-rw-r--r--process.c1638
-rw-r--r--ractor.c140
-rw-r--r--ractor.rb8
-rw-r--r--ractor_core.h92
-rw-r--r--random.c530
-rw-r--r--range.c759
-rw-r--r--rational.c783
-rw-r--r--re.c1674
-rw-r--r--regcomp.c2
-rw-r--r--regenc.c15
-rw-r--r--regenc.h14
-rw-r--r--regexec.c822
-rw-r--r--regint.h38
-rw-r--r--regparse.c3
-rw-r--r--ruby-runner.c56
-rw-r--r--ruby.c2037
-rw-r--r--rubystub.c29
-rw-r--r--sample/coverage.rb2
-rw-r--r--sample/from.rb2
-rwxr-xr-xsample/mine.rb8
-rw-r--r--sample/mpart.rb44
-rw-r--r--sample/trick2018/02-mame/entry.rb4
-rw-r--r--sample/trick2022/01-tompng/Gemfile2
-rw-r--r--sample/trick2022/01-tompng/Gemfile.lock13
-rw-r--r--sample/trick2022/01-tompng/authors.markdown3
-rw-r--r--sample/trick2022/01-tompng/entry.rb40
-rw-r--r--sample/trick2022/01-tompng/remarks.markdown51
-rw-r--r--sample/trick2022/02-tompng/authors.markdown3
-rw-r--r--sample/trick2022/02-tompng/entry.rb32
-rw-r--r--sample/trick2022/02-tompng/remarks.markdown32
-rw-r--r--sample/trick2022/03-mame/authors.markdown3
-rw-r--r--sample/trick2022/03-mame/entry.rb27
-rw-r--r--sample/trick2022/03-mame/remarks.markdown96
-rw-r--r--sample/trick2022/03-mame/test.txt13
-rw-r--r--sample/trick2022/README.md14
-rw-r--r--sample/uumerge.rb2
-rw-r--r--scheduler.c413
-rw-r--r--shape.c825
-rw-r--r--shape.h232
-rw-r--r--signal.c507
-rw-r--r--siphash.c90
-rw-r--r--spec/README.md20
-rw-r--r--spec/bundler/bundler/bundler_spec.rb197
-rw-r--r--spec/bundler/bundler/cli_spec.rb46
-rw-r--r--spec/bundler/bundler/definition_spec.rb45
-rw-r--r--spec/bundler/bundler/dep_proxy_spec.rb32
-rw-r--r--spec/bundler/bundler/dependency_spec.rb157
-rw-r--r--spec/bundler/bundler/dsl_spec.rb11
-rw-r--r--spec/bundler/bundler/endpoint_specification_spec.rb25
-rw-r--r--spec/bundler/bundler/env_spec.rb8
-rw-r--r--spec/bundler/bundler/fetcher/dependency_spec.rb18
-rw-r--r--spec/bundler/bundler/fetcher/downloader_spec.rb30
-rw-r--r--spec/bundler/bundler/fetcher/index_spec.rb23
-rw-r--r--spec/bundler/bundler/fetcher_spec.rb30
-rw-r--r--spec/bundler/bundler/friendly_errors_spec.rb13
-rw-r--r--spec/bundler/bundler/gem_helper_spec.rb5
-rw-r--r--spec/bundler/bundler/gem_version_promoter_spec.rb246
-rw-r--r--spec/bundler/bundler/installer/gem_installer_spec.rb15
-rw-r--r--spec/bundler/bundler/installer/parallel_installer_spec.rb38
-rw-r--r--spec/bundler/bundler/plugin/index_spec.rb10
-rw-r--r--spec/bundler/bundler/remote_specification_spec.rb2
-rw-r--r--spec/bundler/bundler/resolver/candidate_spec.rb21
-rw-r--r--spec/bundler/bundler/ruby_dsl_spec.rb27
-rw-r--r--spec/bundler/bundler/ruby_version_spec.rb20
-rw-r--r--spec/bundler/bundler/rubygems_integration_spec.rb11
-rw-r--r--spec/bundler/bundler/settings_spec.rb2
-rw-r--r--spec/bundler/bundler/shared_helpers_spec.rb11
-rw-r--r--spec/bundler/bundler/source/git/git_proxy_spec.rb53
-rw-r--r--spec/bundler/bundler/vendored_persistent_spec.rb77
-rw-r--r--spec/bundler/bundler/version_ranges_spec.rb40
-rw-r--r--spec/bundler/cache/git_spec.rb77
-rw-r--r--spec/bundler/commands/add_spec.rb15
-rw-r--r--spec/bundler/commands/binstubs_spec.rb56
-rw-r--r--spec/bundler/commands/cache_spec.rb2
-rw-r--r--spec/bundler/commands/check_spec.rb6
-rw-r--r--spec/bundler/commands/clean_spec.rb7
-rw-r--r--spec/bundler/commands/config_spec.rb32
-rw-r--r--spec/bundler/commands/doctor_spec.rb2
-rw-r--r--spec/bundler/commands/exec_spec.rb39
-rw-r--r--spec/bundler/commands/fund_spec.rb16
-rw-r--r--spec/bundler/commands/help_spec.rb4
-rw-r--r--spec/bundler/commands/info_spec.rb18
-rw-r--r--spec/bundler/commands/init_spec.rb38
-rw-r--r--spec/bundler/commands/inject_spec.rb2
-rw-r--r--spec/bundler/commands/install_spec.rb83
-rw-r--r--spec/bundler/commands/lock_spec.rb701
-rw-r--r--spec/bundler/commands/newgem_spec.rb201
-rw-r--r--spec/bundler/commands/open_spec.rb60
-rw-r--r--spec/bundler/commands/outdated_spec.rb207
-rw-r--r--spec/bundler/commands/platform_spec.rb (renamed from spec/bundler/other/platform_spec.rb)49
-rw-r--r--spec/bundler/commands/pristine_spec.rb2
-rw-r--r--spec/bundler/commands/remove_spec.rb6
-rw-r--r--spec/bundler/commands/show_spec.rb6
-rw-r--r--spec/bundler/commands/update_spec.rb280
-rw-r--r--spec/bundler/commands/viz_spec.rb6
-rw-r--r--spec/bundler/install/allow_offline_install_spec.rb5
-rw-r--r--spec/bundler/install/binstubs_spec.rb4
-rw-r--r--spec/bundler/install/bundler_spec.rb83
-rw-r--r--spec/bundler/install/deploy_spec.rb125
-rw-r--r--spec/bundler/install/gemfile/gemspec_spec.rb6
-rw-r--r--spec/bundler/install/gemfile/git_spec.rb114
-rw-r--r--spec/bundler/install/gemfile/path_spec.rb109
-rw-r--r--spec/bundler/install/gemfile/platform_spec.rb101
-rw-r--r--spec/bundler/install/gemfile/ruby_spec.rb14
-rw-r--r--spec/bundler/install/gemfile/sources_spec.rb146
-rw-r--r--spec/bundler/install/gemfile/specific_platform_spec.rb656
-rw-r--r--spec/bundler/install/gems/compact_index_spec.rb35
-rw-r--r--spec/bundler/install/gems/dependency_api_spec.rb42
-rw-r--r--spec/bundler/install/gems/flex_spec.rb77
-rw-r--r--spec/bundler/install/gems/fund_spec.rb43
-rw-r--r--spec/bundler/install/gems/native_extensions_spec.rb8
-rw-r--r--spec/bundler/install/gems/resolving_spec.rb291
-rw-r--r--spec/bundler/install/gems/standalone_spec.rb66
-rw-r--r--spec/bundler/install/gems/sudo_spec.rb205
-rw-r--r--spec/bundler/install/gemspecs_spec.rb3
-rw-r--r--spec/bundler/install/git_spec.rb80
-rw-r--r--spec/bundler/install/global_cache_spec.rb4
-rw-r--r--spec/bundler/install/yanked_spec.rb135
-rw-r--r--spec/bundler/lock/git_spec.rb127
-rw-r--r--spec/bundler/lock/lockfile_spec.rb271
-rw-r--r--spec/bundler/other/major_deprecation_spec.rb5
-rw-r--r--spec/bundler/plugins/install_spec.rb30
-rw-r--r--spec/bundler/plugins/source/example_spec.rb14
-rw-r--r--spec/bundler/quality_es_spec.rb4
-rw-r--r--spec/bundler/quality_spec.rb26
-rw-r--r--spec/bundler/realworld/dependency_api_spec.rb10
-rw-r--r--spec/bundler/realworld/edgecases_spec.rb357
-rw-r--r--spec/bundler/realworld/ffi_spec.rb2
-rw-r--r--spec/bundler/realworld/gemfile_source_header_spec.rb10
-rw-r--r--spec/bundler/realworld/git_spec.rb11
-rw-r--r--spec/bundler/realworld/mirror_probe_spec.rb10
-rw-r--r--spec/bundler/realworld/slow_perf_spec.rb19
-rw-r--r--spec/bundler/resolver/basic_spec.rb74
-rw-r--r--spec/bundler/resolver/platform_spec.rb141
-rw-r--r--spec/bundler/runtime/inline_spec.rb182
-rw-r--r--spec/bundler/runtime/platform_spec.rb105
-rw-r--r--spec/bundler/runtime/self_management_spec.rb2
-rw-r--r--spec/bundler/runtime/setup_spec.rb62
-rw-r--r--spec/bundler/spec_helper.rb34
-rw-r--r--spec/bundler/support/api_request_limit_hax.rb16
-rw-r--r--spec/bundler/support/artifice/compact_index.rb118
-rw-r--r--spec/bundler/support/artifice/compact_index_api_missing.rb13
-rw-r--r--spec/bundler/support/artifice/compact_index_basic_authentication.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_checksum_mismatch.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_concurrent_download.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_creds_diff_host.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_extra.rb35
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_api.rb50
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_api_missing.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_missing.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_forbidden.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_host_redirect.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_no_gem.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_precompiled_before.rb25
-rw-r--r--spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_rate_limited.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_redirects.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb8
-rw-r--r--spec/bundler/support/artifice/compact_index_wrong_dependencies.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint.rb113
-rw-r--r--spec/bundler/support/artifice/endpoint_500.rb7
-rw-r--r--spec/bundler/support/artifice/endpoint_api_forbidden.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_basic_authentication.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_creds_diff_host.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_extra.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_extra_api.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_extra_missing.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_fallback.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_host_redirect.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_marshal_fail.rb11
-rw-r--r--spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_mirror_source.rb4
-rw-r--r--spec/bundler/support/artifice/endpoint_redirect.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb8
-rw-r--r--spec/bundler/support/artifice/endpoint_timeout.rb6
-rw-r--r--spec/bundler/support/artifice/fail.rb11
-rw-r--r--spec/bundler/support/artifice/helpers/artifice.rb30
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index.rb118
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index_extra.rb33
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index_extra_api.rb48
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint.rb112
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint_extra.rb29
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint_fallback.rb15
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb9
-rw-r--r--spec/bundler/support/artifice/helpers/rack_request.rb100
-rw-r--r--spec/bundler/support/artifice/used_cassettes.txt20908
-rw-r--r--spec/bundler/support/artifice/vcr.rb33
-rw-r--r--spec/bundler/support/artifice/windows.rb7
-rw-r--r--spec/bundler/support/builders.rb40
-rw-r--r--spec/bundler/support/bundle.rb2
-rw-r--r--spec/bundler/support/filters.rb5
-rw-r--r--spec/bundler/support/hax.rb25
-rw-r--r--spec/bundler/support/helpers.rb45
-rw-r--r--spec/bundler/support/indexes.rb37
-rw-r--r--spec/bundler/support/matchers.rb14
-rw-r--r--spec/bundler/support/path.rb36
-rw-r--r--spec/bundler/support/platforms.rb38
-rw-r--r--spec/bundler/support/rubygems_version_manager.rb2
-rw-r--r--spec/bundler/support/sudo.rb22
-rw-r--r--spec/bundler/update/gems/fund_spec.rb16
-rw-r--r--spec/bundler/update/git_spec.rb14
-rwxr-xr-xspec/mspec/lib/mspec/commands/mkspec.rb16
-rw-r--r--spec/mspec/lib/mspec/guards/superuser.rb10
-rw-r--r--spec/mspec/lib/mspec/runner/actions/leakchecker.rb3
-rw-r--r--spec/mspec/lib/mspec/utils/name_map.rb7
-rw-r--r--spec/mspec/lib/mspec/utils/script.rb1
-rw-r--r--spec/mspec/spec/commands/mkspec_spec.rb2
-rw-r--r--spec/mspec/spec/utils/script_spec.rb5
-rwxr-xr-xspec/mspec/tool/tag_from_output.rb26
-rw-r--r--spec/ruby/.rubocop.yml7
-rw-r--r--spec/ruby/CONTRIBUTING.md5
-rw-r--r--spec/ruby/README.md6
-rw-r--r--spec/ruby/core/array/keep_if_spec.rb1
-rw-r--r--spec/ruby/core/array/pack/c_spec.rb14
-rw-r--r--spec/ruby/core/array/pack/m_spec.rb10
-rw-r--r--spec/ruby/core/array/pack/shared/basic.rb40
-rw-r--r--spec/ruby/core/array/pack/shared/float.rb66
-rw-r--r--spec/ruby/core/array/pack/shared/integer.rb96
-rw-r--r--spec/ruby/core/array/pack/shared/numeric_basic.rb10
-rw-r--r--spec/ruby/core/array/pack/shared/unicode.rb14
-rw-r--r--spec/ruby/core/array/pack/w_spec.rb14
-rw-r--r--spec/ruby/core/array/shared/slice.rb96
-rw-r--r--spec/ruby/core/array/shared/unshift.rb18
-rw-r--r--spec/ruby/core/array/values_at_spec.rb1
-rw-r--r--spec/ruby/core/array/zip_spec.rb6
-rw-r--r--spec/ruby/core/basicobject/instance_eval_spec.rb68
-rw-r--r--spec/ruby/core/builtin_constants/builtin_constants_spec.rb6
-rw-r--r--spec/ruby/core/class/attached_object_spec.rb31
-rw-r--r--spec/ruby/core/class/subclasses_spec.rb22
-rw-r--r--spec/ruby/core/complex/polar_spec.rb16
-rw-r--r--spec/ruby/core/data/constants_spec.rb14
-rw-r--r--spec/ruby/core/dir/fixtures/common.rb1
-rw-r--r--spec/ruby/core/dir/glob_spec.rb2
-rw-r--r--spec/ruby/core/dir/home_spec.rb43
-rw-r--r--spec/ruby/core/dir/mkdir_spec.rb18
-rw-r--r--spec/ruby/core/dir/shared/chroot.rb9
-rw-r--r--spec/ruby/core/dir/shared/glob.rb5
-rw-r--r--spec/ruby/core/encoding/replicate_spec.rb108
-rw-r--r--spec/ruby/core/enumerable/compact_spec.rb11
-rw-r--r--spec/ruby/core/enumerable/each_cons_spec.rb6
-rw-r--r--spec/ruby/core/enumerable/each_slice_spec.rb6
-rw-r--r--spec/ruby/core/enumerable/sum_spec.rb17
-rw-r--r--spec/ruby/core/enumerable/zip_spec.rb5
-rw-r--r--spec/ruby/core/enumerator/lazy/compact_spec.rb11
-rw-r--r--spec/ruby/core/enumerator/lazy/lazy_spec.rb4
-rw-r--r--spec/ruby/core/env/shared/update.rb7
-rw-r--r--spec/ruby/core/false/case_compare_spec.rb14
-rw-r--r--spec/ruby/core/fiber/blocking_spec.rb17
-rw-r--r--spec/ruby/core/fiber/storage_spec.rb117
-rw-r--r--spec/ruby/core/file/atime_spec.rb6
-rw-r--r--spec/ruby/core/file/ctime_spec.rb4
-rw-r--r--spec/ruby/core/file/mtime_spec.rb20
-rw-r--r--spec/ruby/core/file/shared/fnmatch.rb8
-rw-r--r--spec/ruby/core/file/shared/path.rb14
-rw-r--r--spec/ruby/core/file/utime_spec.rb34
-rw-r--r--spec/ruby/core/float/comparison_spec.rb35
-rw-r--r--spec/ruby/core/float/divmod_spec.rb2
-rw-r--r--spec/ruby/core/float/gt_spec.rb21
-rw-r--r--spec/ruby/core/float/gte_spec.rb21
-rw-r--r--spec/ruby/core/float/lt_spec.rb21
-rw-r--r--spec/ruby/core/float/lte_spec.rb21
-rw-r--r--spec/ruby/core/float/shared/equal.rb21
-rw-r--r--spec/ruby/core/float/shared/to_i.rb4
-rw-r--r--spec/ruby/core/hash/hash_spec.rb9
-rw-r--r--spec/ruby/core/io/fixtures/classes.rb12
-rw-r--r--spec/ruby/core/io/gets_spec.rb6
-rw-r--r--spec/ruby/core/io/lineno_spec.rb9
-rw-r--r--spec/ruby/core/io/new_spec.rb2
-rw-r--r--spec/ruby/core/io/path_spec.rb14
-rw-r--r--spec/ruby/core/io/pipe_spec.rb11
-rw-r--r--spec/ruby/core/io/print_spec.rb25
-rw-r--r--spec/ruby/core/io/read_nonblock_spec.rb49
-rw-r--r--spec/ruby/core/io/read_spec.rb25
-rw-r--r--spec/ruby/core/io/readchar_spec.rb66
-rw-r--r--spec/ruby/core/io/readline_spec.rb4
-rw-r--r--spec/ruby/core/io/readlines_spec.rb4
-rw-r--r--spec/ruby/core/io/readpartial_spec.rb17
-rw-r--r--spec/ruby/core/io/rewind_spec.rb15
-rw-r--r--spec/ruby/core/io/set_encoding_by_bom_spec.rb187
-rw-r--r--spec/ruby/core/io/set_encoding_spec.rb49
-rw-r--r--spec/ruby/core/io/shared/each.rb44
-rw-r--r--spec/ruby/core/io/shared/new.rb2
-rw-r--r--spec/ruby/core/io/shared/pos.rb8
-rw-r--r--spec/ruby/core/io/shared/readlines.rb4
-rw-r--r--spec/ruby/core/io/shared/write.rb10
-rw-r--r--spec/ruby/core/io/sysread_spec.rb31
-rw-r--r--spec/ruby/core/io/sysseek_spec.rb2
-rw-r--r--spec/ruby/core/io/syswrite_spec.rb10
-rw-r--r--spec/ruby/core/io/write_nonblock_spec.rb10
-rw-r--r--spec/ruby/core/io/write_spec.rb10
-rw-r--r--spec/ruby/core/kernel/Complex_spec.rb89
-rw-r--r--spec/ruby/core/kernel/fixtures/Complex.rb5
-rw-r--r--spec/ruby/core/kernel/fixtures/warn_core_method.rb2
-rw-r--r--spec/ruby/core/kernel/p_spec.rb6
-rw-r--r--spec/ruby/core/kernel/shared/load.rb45
-rw-r--r--spec/ruby/core/kernel/shared/require.rb11
-rw-r--r--spec/ruby/core/kernel/shared/sprintf.rb85
-rw-r--r--spec/ruby/core/kernel/shared/sprintf_encoding.rb33
-rw-r--r--spec/ruby/core/kernel/singleton_class_spec.rb2
-rw-r--r--spec/ruby/core/main/fixtures/using.rb1
-rw-r--r--spec/ruby/core/main/fixtures/using_in_main.rb5
-rw-r--r--spec/ruby/core/main/fixtures/using_in_method.rb5
-rw-r--r--spec/ruby/core/main/using_spec.rb20
-rw-r--r--spec/ruby/core/marshal/dump_spec.rb45
-rw-r--r--spec/ruby/core/marshal/fixtures/classes.rb4
-rw-r--r--spec/ruby/core/marshal/shared/load.rb11
-rw-r--r--spec/ruby/core/matchdata/element_reference_spec.rb15
-rw-r--r--spec/ruby/core/matchdata/values_at_spec.rb73
-rw-r--r--spec/ruby/core/method/fixtures/classes.rb30
-rw-r--r--spec/ruby/core/method/owner_spec.rb6
-rw-r--r--spec/ruby/core/method/private_spec.rb21
-rw-r--r--spec/ruby/core/method/protected_spec.rb21
-rw-r--r--spec/ruby/core/method/public_spec.rb21
-rw-r--r--spec/ruby/core/method/super_method_spec.rb21
-rw-r--r--spec/ruby/core/method/unbind_spec.rb12
-rw-r--r--spec/ruby/core/module/const_defined_spec.rb7
-rw-r--r--spec/ruby/core/module/define_method_spec.rb43
-rw-r--r--spec/ruby/core/module/fixtures/classes.rb9
-rw-r--r--spec/ruby/core/module/include_spec.rb4
-rw-r--r--spec/ruby/core/module/included_modules_spec.rb2
-rw-r--r--spec/ruby/core/module/instance_method_spec.rb42
-rw-r--r--spec/ruby/core/module/prepend_spec.rb12
-rw-r--r--spec/ruby/core/module/shared/class_eval.rb21
-rw-r--r--spec/ruby/core/objectspace/define_finalizer_spec.rb22
-rw-r--r--spec/ruby/core/process/_fork_spec.rb24
-rw-r--r--spec/ruby/core/process/constants_spec.rb1
-rw-r--r--spec/ruby/core/process/daemon_spec.rb3
-rw-r--r--spec/ruby/core/process/detach_spec.rb29
-rw-r--r--spec/ruby/core/process/spawn_spec.rb28
-rw-r--r--spec/ruby/core/process/times_spec.rb14
-rw-r--r--spec/ruby/core/queue/initialize_spec.rb13
-rw-r--r--spec/ruby/core/range/case_compare_spec.rb6
-rw-r--r--spec/ruby/core/range/clone_spec.rb26
-rw-r--r--spec/ruby/core/range/dup_spec.rb4
-rw-r--r--spec/ruby/core/range/last_spec.rb6
-rw-r--r--spec/ruby/core/range/new_spec.rb10
-rw-r--r--spec/ruby/core/range/size_spec.rb27
-rw-r--r--spec/ruby/core/refinement/import_methods_spec.rb34
-rw-r--r--spec/ruby/core/refinement/include_spec.rb27
-rw-r--r--spec/ruby/core/refinement/prepend_spec.rb27
-rw-r--r--spec/ruby/core/regexp/compile_spec.rb4
-rw-r--r--spec/ruby/core/regexp/initialize_spec.rb2
-rw-r--r--spec/ruby/core/regexp/new_spec.rb14
-rw-r--r--spec/ruby/core/regexp/shared/new.rb118
-rw-r--r--spec/ruby/core/regexp/shared/quote.rb5
-rw-r--r--spec/ruby/core/regexp/source_spec.rb22
-rw-r--r--spec/ruby/core/regexp/timeout_spec.rb35
-rw-r--r--spec/ruby/core/signal/trap_spec.rb12
-rw-r--r--spec/ruby/core/string/byteindex_spec.rb16
-rw-r--r--spec/ruby/core/string/byteslice_spec.rb6
-rw-r--r--spec/ruby/core/string/capitalize_spec.rb7
-rw-r--r--spec/ruby/core/string/chars_spec.rb7
-rw-r--r--spec/ruby/core/string/chomp_spec.rb4
-rw-r--r--spec/ruby/core/string/chop_spec.rb4
-rw-r--r--spec/ruby/core/string/clone_spec.rb4
-rw-r--r--spec/ruby/core/string/delete_prefix_spec.rb8
-rw-r--r--spec/ruby/core/string/delete_spec.rb4
-rw-r--r--spec/ruby/core/string/delete_suffix_spec.rb8
-rw-r--r--spec/ruby/core/string/downcase_spec.rb8
-rw-r--r--spec/ruby/core/string/dump_spec.rb10
-rw-r--r--spec/ruby/core/string/dup_spec.rb4
-rw-r--r--spec/ruby/core/string/element_set_spec.rb4
-rw-r--r--spec/ruby/core/string/encoding_spec.rb1
-rw-r--r--spec/ruby/core/string/fixtures/iso-8859-9-encoding.rb2
-rw-r--r--spec/ruby/core/string/fixtures/to_c.rb5
-rw-r--r--spec/ruby/core/string/gsub_spec.rb35
-rw-r--r--spec/ruby/core/string/include_spec.rb14
-rw-r--r--spec/ruby/core/string/index_spec.rb8
-rw-r--r--spec/ruby/core/string/inspect_spec.rb20
-rw-r--r--spec/ruby/core/string/lines_spec.rb1
-rw-r--r--spec/ruby/core/string/lstrip_spec.rb34
-rw-r--r--spec/ruby/core/string/modulo_spec.rb12
-rw-r--r--spec/ruby/core/string/ord_spec.rb5
-rw-r--r--spec/ruby/core/string/partition_spec.rb22
-rw-r--r--spec/ruby/core/string/reverse_spec.rb21
-rw-r--r--spec/ruby/core/string/rindex_spec.rb17
-rw-r--r--spec/ruby/core/string/rpartition_spec.rb22
-rw-r--r--spec/ruby/core/string/rstrip_spec.rb48
-rw-r--r--spec/ruby/core/string/scan_spec.rb6
-rw-r--r--spec/ruby/core/string/scrub_spec.rb10
-rw-r--r--spec/ruby/core/string/setbyte_spec.rb6
-rw-r--r--spec/ruby/core/string/shared/dedup.rb10
-rw-r--r--spec/ruby/core/string/shared/each_line.rb6
-rw-r--r--spec/ruby/core/string/shared/partition.rb15
-rw-r--r--spec/ruby/core/string/shared/slice.rb13
-rw-r--r--spec/ruby/core/string/shared/strip.rb4
-rw-r--r--spec/ruby/core/string/shared/succ.rb4
-rw-r--r--spec/ruby/core/string/split_spec.rb59
-rw-r--r--spec/ruby/core/string/squeeze_spec.rb5
-rw-r--r--spec/ruby/core/string/start_with_spec.rb10
-rw-r--r--spec/ruby/core/string/strip_spec.rb6
-rw-r--r--spec/ruby/core/string/sub_spec.rb32
-rw-r--r--spec/ruby/core/string/swapcase_spec.rb8
-rw-r--r--spec/ruby/core/string/to_c_spec.rb108
-rw-r--r--spec/ruby/core/string/undump_spec.rb2
-rw-r--r--spec/ruby/core/string/unpack/b_spec.rb34
-rw-r--r--spec/ruby/core/string/unpack/c_spec.rb14
-rw-r--r--spec/ruby/core/string/unpack/h_spec.rb28
-rw-r--r--spec/ruby/core/string/unpack/m_spec.rb5
-rw-r--r--spec/ruby/core/string/unpack/shared/basic.rb28
-rw-r--r--spec/ruby/core/string/unpack/shared/float.rb60
-rw-r--r--spec/ruby/core/string/unpack/shared/integer.rb88
-rw-r--r--spec/ruby/core/string/unpack/shared/unicode.rb14
-rw-r--r--spec/ruby/core/string/unpack/w_spec.rb14
-rw-r--r--spec/ruby/core/string/unpack1_spec.rb12
-rw-r--r--spec/ruby/core/string/unpack_spec.rb34
-rw-r--r--spec/ruby/core/string/upcase_spec.rb8
-rw-r--r--spec/ruby/core/string/valid_encoding/utf_8_spec.rb214
-rw-r--r--spec/ruby/core/struct/initialize_spec.rb8
-rw-r--r--spec/ruby/core/struct/keyword_init_spec.rb21
-rw-r--r--spec/ruby/core/struct/values_at_spec.rb55
-rw-r--r--spec/ruby/core/symbol/shared/id2name.rb7
-rw-r--r--spec/ruby/core/symbol/to_proc_spec.rb27
-rw-r--r--spec/ruby/core/thread/backtrace/limit_spec.rb15
-rw-r--r--spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb9
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/subdir/absolute_path_main_chdir.rb11
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/subdir/sibling.rb1
-rw-r--r--spec/ruby/core/thread/native_thread_id_spec.rb17
-rw-r--r--spec/ruby/core/time/at_spec.rb21
-rw-r--r--spec/ruby/core/time/localtime_spec.rb16
-rw-r--r--spec/ruby/core/time/new_spec.rb145
-rw-r--r--spec/ruby/core/time/now_spec.rb45
-rw-r--r--spec/ruby/core/time/shared/gmtime.rb4
-rw-r--r--spec/ruby/core/time/shared/local.rb11
-rw-r--r--spec/ruby/core/time/shared/time_params.rb11
-rw-r--r--spec/ruby/core/time/strftime_spec.rb41
-rw-r--r--spec/ruby/core/time/utc_spec.rb41
-rw-r--r--spec/ruby/core/time/zone_spec.rb20
-rw-r--r--spec/ruby/core/tracepoint/allow_reentry_spec.rb32
-rw-r--r--spec/ruby/core/tracepoint/inspect_spec.rb9
-rw-r--r--spec/ruby/core/unboundmethod/equal_value_spec.rb76
-rw-r--r--spec/ruby/core/unboundmethod/fixtures/classes.rb16
-rw-r--r--spec/ruby/core/unboundmethod/hash_spec.rb7
-rw-r--r--spec/ruby/core/unboundmethod/owner_spec.rb7
-rw-r--r--spec/ruby/core/unboundmethod/private_spec.rb21
-rw-r--r--spec/ruby/core/unboundmethod/protected_spec.rb21
-rw-r--r--spec/ruby/core/unboundmethod/public_spec.rb21
-rw-r--r--spec/ruby/core/unboundmethod/shared/to_s.rb16
-rw-r--r--spec/ruby/core/unboundmethod/super_method_spec.rb23
-rw-r--r--spec/ruby/fixtures/code/concurrent_require_fixture.rb4
-rw-r--r--spec/ruby/fixtures/code/load_wrap_fixture.rb (renamed from spec/ruby/fixtures/code/wrap_fixture.rb)3
-rw-r--r--spec/ruby/fixtures/constants.rb11
-rw-r--r--spec/ruby/language/block_spec.rb117
-rw-r--r--spec/ruby/language/case_spec.rb4
-rw-r--r--spec/ruby/language/keyword_arguments_spec.rb33
-rw-r--r--spec/ruby/language/method_spec.rb36
-rw-r--r--spec/ruby/language/module_spec.rb15
-rw-r--r--spec/ruby/language/precedence_spec.rb78
-rw-r--r--spec/ruby/language/predefined_spec.rb308
-rw-r--r--spec/ruby/language/proc_spec.rb7
-rw-r--r--spec/ruby/language/range_spec.rb8
-rw-r--r--spec/ruby/language/regexp/character_classes_spec.rb5
-rw-r--r--spec/ruby/language/regexp/escapes_spec.rb84
-rw-r--r--spec/ruby/language/regexp_spec.rb21
-rw-r--r--spec/ruby/language/return_spec.rb15
-rw-r--r--spec/ruby/library/bigdecimal/exponent_spec.rb11
-rw-r--r--spec/ruby/library/bigdecimal/round_spec.rb12
-rw-r--r--spec/ruby/library/bigdecimal/to_r_spec.rb12
-rw-r--r--spec/ruby/library/cgi/cookie/name_spec.rb12
-rw-r--r--spec/ruby/library/cgi/cookie/parse_spec.rb10
-rw-r--r--spec/ruby/library/cmath/math/acos_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/acosh_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/asin_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/asinh_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/atan2_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/atan_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/atanh_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/cos_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/cosh_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/exp_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/fixtures/classes.rb4
-rw-r--r--spec/ruby/library/cmath/math/log10_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/log_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/shared/acos.rb41
-rw-r--r--spec/ruby/library/cmath/math/shared/acosh.rb37
-rw-r--r--spec/ruby/library/cmath/math/shared/asin.rb47
-rw-r--r--spec/ruby/library/cmath/math/shared/asinh.rb32
-rw-r--r--spec/ruby/library/cmath/math/shared/atan.rb32
-rw-r--r--spec/ruby/library/cmath/math/shared/atan2.rb34
-rw-r--r--spec/ruby/library/cmath/math/shared/atanh.rb30
-rw-r--r--spec/ruby/library/cmath/math/shared/cos.rb30
-rw-r--r--spec/ruby/library/cmath/math/shared/cosh.rb28
-rw-r--r--spec/ruby/library/cmath/math/shared/exp.rb28
-rw-r--r--spec/ruby/library/cmath/math/shared/log.rb39
-rw-r--r--spec/ruby/library/cmath/math/shared/log10.rb41
-rw-r--r--spec/ruby/library/cmath/math/shared/sin.rb30
-rw-r--r--spec/ruby/library/cmath/math/shared/sinh.rb28
-rw-r--r--spec/ruby/library/cmath/math/shared/sqrt.rb34
-rw-r--r--spec/ruby/library/cmath/math/shared/tan.rb28
-rw-r--r--spec/ruby/library/cmath/math/shared/tanh.rb32
-rw-r--r--spec/ruby/library/cmath/math/sin_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/sinh_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/sqrt_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/tan_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/tanh_spec.rb1
-rw-r--r--spec/ruby/library/coverage/result_spec.rb54
-rw-r--r--spec/ruby/library/coverage/running_spec.rb20
-rw-r--r--spec/ruby/library/coverage/start_spec.rb8
-rw-r--r--spec/ruby/library/date/civil_spec.rb7
-rw-r--r--spec/ruby/library/datetime/to_time_spec.rb18
-rw-r--r--spec/ruby/library/erb/new_spec.rb16
-rw-r--r--spec/ruby/library/fiddle/handle/initialize_spec.rb10
-rw-r--r--spec/ruby/library/io-wait/wait_readable_spec.rb27
-rw-r--r--spec/ruby/library/io-wait/wait_writable_spec.rb20
-rw-r--r--spec/ruby/library/objectspace/fixtures/trace.rb5
-rw-r--r--spec/ruby/library/objectspace/trace_spec.rb15
-rw-r--r--spec/ruby/library/openssl/x509/name/verify_spec.rb4
-rw-r--r--spec/ruby/library/rbconfig/unicode_emoji_version_spec.rb4
-rw-r--r--spec/ruby/library/rbconfig/unicode_version_spec.rb4
-rw-r--r--spec/ruby/library/scanf/io/block_scanf_spec.rb1
-rw-r--r--spec/ruby/library/scanf/io/fixtures/date.txt4
-rw-r--r--spec/ruby/library/scanf/io/fixtures/helloworld.txt1
-rw-r--r--spec/ruby/library/scanf/io/scanf_spec.rb1
-rw-r--r--spec/ruby/library/scanf/io/shared/block_scanf.rb28
-rw-r--r--spec/ruby/library/scanf/string/block_scanf_spec.rb1
-rw-r--r--spec/ruby/library/scanf/string/scanf_spec.rb1
-rw-r--r--spec/ruby/library/scanf/string/shared/block_scanf.rb25
-rw-r--r--spec/ruby/library/socket/addrinfo/initialize_spec.rb2
-rw-r--r--spec/ruby/library/socket/shared/pack_sockaddr.rb7
-rw-r--r--spec/ruby/library/socket/tcpsocket/shared/new.rb10
-rw-r--r--spec/ruby/library/stringio/initialize_spec.rb85
-rw-r--r--spec/ruby/library/stringio/new_spec.rb8
-rw-r--r--spec/ruby/library/stringio/open_spec.rb12
-rw-r--r--spec/ruby/library/stringio/printf_spec.rb27
-rw-r--r--spec/ruby/library/stringio/putc_spec.rb15
-rw-r--r--spec/ruby/library/stringio/puts_spec.rb14
-rw-r--r--spec/ruby/library/stringio/read_nonblock_spec.rb11
-rw-r--r--spec/ruby/library/stringio/shared/read.rb6
-rw-r--r--spec/ruby/library/stringio/shared/write.rb37
-rw-r--r--spec/ruby/library/stringio/write_nonblock_spec.rb6
-rw-r--r--spec/ruby/library/time/to_datetime_spec.rb18
-rw-r--r--spec/ruby/library/zlib/deflate/deflate_spec.rb5
-rw-r--r--spec/ruby/library/zlib/inflate/inflate_spec.rb7
-rw-r--r--spec/ruby/optional/capi/class_spec.rb31
-rw-r--r--spec/ruby/optional/capi/encoding_spec.rb57
-rw-r--r--spec/ruby/optional/capi/ext/encoding_spec.c29
-rw-r--r--spec/ruby/optional/capi/ext/gc_spec.c35
-rw-r--r--spec/ruby/optional/capi/ext/globals_spec.c34
-rw-r--r--spec/ruby/optional/capi/ext/io_spec.c43
-rw-r--r--spec/ruby/optional/capi/ext/kernel_spec.c5
-rw-r--r--spec/ruby/optional/capi/ext/object_spec.c13
-rw-r--r--spec/ruby/optional/capi/ext/rubyspec.h30
-rw-r--r--spec/ruby/optional/capi/ext/string_spec.c21
-rw-r--r--spec/ruby/optional/capi/ext/util_spec.c21
-rw-r--r--spec/ruby/optional/capi/fixtures/object.rb29
-rw-r--r--spec/ruby/optional/capi/gc_spec.rb30
-rw-r--r--spec/ruby/optional/capi/globals_spec.rb54
-rw-r--r--spec/ruby/optional/capi/io_spec.rb15
-rw-r--r--spec/ruby/optional/capi/kernel_spec.rb19
-rw-r--r--spec/ruby/optional/capi/object_spec.rb20
-rw-r--r--spec/ruby/optional/capi/rbasic_spec.rb1
-rw-r--r--spec/ruby/optional/capi/shared/rbasic.rb2
-rw-r--r--spec/ruby/optional/capi/spec_helper.rb3
-rw-r--r--spec/ruby/optional/capi/string_spec.rb103
-rw-r--r--spec/ruby/optional/capi/util_spec.rb5
-rw-r--r--spec/ruby/security/cve_2019_8325_spec.rb10
-rw-r--r--spec/ruby/shared/file/executable.rb35
-rw-r--r--spec/ruby/shared/file/executable_real.rb35
-rw-r--r--spec/ruby/shared/file/readable.rb16
-rw-r--r--spec/ruby/shared/file/readable_real.rb16
-rw-r--r--spec/ruby/shared/file/writable.rb16
-rw-r--r--spec/ruby/shared/file/writable_real.rb16
-rw-r--r--spec/ruby/shared/kernel/complex.rb133
-rw-r--r--spec/ruby/shared/queue/deque.rb62
-rw-r--r--spec/ruby/shared/rational/Rational.rb48
-rw-r--r--spec/ruby/shared/sizedqueue/enque.rb63
-rw-r--r--spec/ruby/shared/sizedqueue/new.rb9
-rw-r--r--spec/ruby/shared/string/end_with.rb9
-rw-r--r--spec/ruby/shared/string/start_with.rb4
-rw-r--r--spec/ruby/spec_helper.rb3
-rw-r--r--spec/syntax_suggest/fixtures/derailed_require_tree.rb.txt74
-rwxr-xr-xspec/syntax_suggest/fixtures/rexe.rb.txt569
-rw-r--r--spec/syntax_suggest/fixtures/routes.rb.txt121
-rw-r--r--spec/syntax_suggest/fixtures/ruby_buildpack.rb.txt1344
-rw-r--r--spec/syntax_suggest/fixtures/syntax_tree.rb.txt9234
-rw-r--r--spec/syntax_suggest/fixtures/this_project_extra_def.rb.txt64
-rw-r--r--spec/syntax_suggest/fixtures/webmock.rb.txt35
-rw-r--r--spec/syntax_suggest/integration/exe_cli_spec.rb27
-rw-r--r--spec/syntax_suggest/integration/ruby_command_line_spec.rb193
-rw-r--r--spec/syntax_suggest/integration/syntax_suggest_spec.rb239
-rw-r--r--spec/syntax_suggest/spec_helper.rb104
-rw-r--r--spec/syntax_suggest/unit/api_spec.rb108
-rw-r--r--spec/syntax_suggest/unit/around_block_scan_spec.rb165
-rw-r--r--spec/syntax_suggest/unit/block_expand_spec.rb230
-rw-r--r--spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb47
-rw-r--r--spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb44
-rw-r--r--spec/syntax_suggest/unit/capture_code_context_spec.rb229
-rw-r--r--spec/syntax_suggest/unit/clean_document_spec.rb260
-rw-r--r--spec/syntax_suggest/unit/cli_spec.rb224
-rw-r--r--spec/syntax_suggest/unit/code_block_spec.rb77
-rw-r--r--spec/syntax_suggest/unit/code_frontier_spec.rb135
-rw-r--r--spec/syntax_suggest/unit/code_line_spec.rb165
-rw-r--r--spec/syntax_suggest/unit/code_search_spec.rb505
-rw-r--r--spec/syntax_suggest/unit/core_ext_spec.rb34
-rw-r--r--spec/syntax_suggest/unit/display_invalid_blocks_spec.rb174
-rw-r--r--spec/syntax_suggest/unit/explain_syntax_spec.rb255
-rw-r--r--spec/syntax_suggest/unit/lex_all_spec.rb29
-rw-r--r--spec/syntax_suggest/unit/pathname_from_message_spec.rb56
-rw-r--r--spec/syntax_suggest/unit/priority_queue_spec.rb95
-rw-r--r--spec/syntax_suggest/unit/scan_history_spec.rb114
-rw-r--r--sprintf.c1366
-rw-r--r--st.c714
-rw-r--r--strftime.c7
-rw-r--r--string.c6154
-rw-r--r--struct.c1026
-rw-r--r--symbol.c408
-rw-r--r--symbol.h20
-rw-r--r--symbol.rb15
-rw-r--r--template/Doxyfile.tmpl1
-rw-r--r--template/GNUmakefile.in15
-rw-r--r--template/Makefile.in60
-rw-r--r--template/extinit.c.tmpl2
-rw-r--r--template/exts.mk.tmpl2
-rw-r--r--template/fake.rb.in27
-rw-r--r--template/id.c.tmpl5
-rw-r--r--template/id.h.tmpl10
-rw-r--r--template/prelude.c.tmpl107
-rw-r--r--test/-ext-/arith_seq/test_arith_seq_beg_len_step.rb52
-rw-r--r--test/-ext-/bug_reporter/test_bug_reporter.rb2
-rw-r--r--test/-ext-/debug/test_profile_frames.rb24
-rw-r--r--test/-ext-/econv/test_append.rb23
-rw-r--r--test/-ext-/eval/test_eval.rb12
-rw-r--r--test/-ext-/string/test_cstr.rb6
-rw-r--r--test/-ext-/string/test_fstring.rb8
-rw-r--r--test/-ext-/string/test_set_len.rb29
-rw-r--r--test/-ext-/symbol/test_type.rb5
-rw-r--r--test/-ext-/test_abi.rb2
-rw-r--r--test/-ext-/test_random.rb26
-rw-r--r--test/-ext-/thread_fd/test_thread_fd_close.rb1
-rw-r--r--test/bigdecimal/test_bigdecimal.rb14
-rw-r--r--test/cgi/test_cgi_cookie.rb85
-rw-r--r--test/cgi/test_cgi_header.rb8
-rw-r--r--test/cgi/test_cgi_util.rb67
-rw-r--r--test/coverage/autostart.rb2
-rw-r--r--test/coverage/main.rb1
-rw-r--r--test/coverage/test_coverage.rb66
-rw-r--r--test/csv/interface/test_read.rb18
-rw-r--r--test/csv/interface/test_write.rb9
-rw-r--r--test/csv/parse/test_convert.rb55
-rw-r--r--test/csv/parse/test_general.rb82
-rw-r--r--test/csv/parse/test_header.rb9
-rw-r--r--test/csv/parse/test_inputs_scanner.rb63
-rw-r--r--test/csv/parse/test_liberal_parsing.rb11
-rw-r--r--test/csv/parse/test_read.rb27
-rw-r--r--test/csv/test_data_converters.rb84
-rw-r--r--test/csv/test_encodings.rb31
-rw-r--r--test/csv/test_patterns.rb27
-rw-r--r--test/csv/test_table.rb73
-rw-r--r--test/date/test_date.rb29
-rw-r--r--test/date/test_date_conv.rb17
-rw-r--r--test/date/test_date_parse.rb3
-rw-r--r--test/date/test_date_strptime.rb9
-rw-r--r--test/did_you_mean/spell_checking/test_method_name_check.rb18
-rw-r--r--test/did_you_mean/spell_checking/test_variable_name_check.rb12
-rw-r--r--test/drb/drbtest.rb2
-rw-r--r--test/drb/test_drb.rb5
-rw-r--r--test/drb/test_drbssl.rb8
-rw-r--r--test/erb/test_erb.rb30
-rw-r--r--test/erb/test_erb_command.rb18
-rw-r--r--test/error_highlight/test_error_highlight.rb90
-rw-r--r--test/fiber/scheduler.rb97
-rw-r--r--test/fiber/test_address_resolve.rb2
-rw-r--r--test/fiber/test_enumerator.rb14
-rw-r--r--test/fiber/test_io.rb73
-rw-r--r--test/fiber/test_io_buffer.rb33
-rw-r--r--test/fiber/test_mutex.rb22
-rw-r--r--test/fiber/test_process.rb21
-rw-r--r--test/fiber/test_queue.rb54
-rw-r--r--test/fiber/test_scheduler.rb97
-rw-r--r--test/fiber/test_storage.rb115
-rw-r--r--test/fiber/test_thread.rb22
-rw-r--r--test/fiddle/helper.rb10
-rw-r--r--test/fiddle/test_closure.rb111
-rw-r--r--test/fiddle/test_fiddle.rb41
-rw-r--r--test/fiddle/test_func.rb36
-rw-r--r--test/fiddle/test_function.rb28
-rw-r--r--test/fiddle/test_handle.rb7
-rw-r--r--test/fiddle/test_import.rb23
-rw-r--r--test/fiddle/test_pack.rb37
-rw-r--r--test/fiddle/test_pointer.rb3
-rw-r--r--test/fileutils/test_fileutils.rb69
-rw-r--r--test/io/console/test_io_console.rb31
-rw-r--r--test/io/wait/test_io_wait.rb8
-rw-r--r--test/io/wait/test_io_wait_uncommon.rb1
-rw-r--r--test/io/wait/test_ractor.rb1
-rw-r--r--test/irb/helper.rb76
-rw-r--r--test/irb/test_cmd.rb862
-rw-r--r--test/irb/test_color.rb84
-rw-r--r--test/irb/test_color_printer.rb5
-rw-r--r--test/irb/test_completion.rb359
-rw-r--r--test/irb/test_context.rb47
-rw-r--r--test/irb/test_debug_cmd.rb303
-rw-r--r--test/irb/test_history.rb68
-rw-r--r--test/irb/test_init.rb81
-rw-r--r--test/irb/test_input_method.rb79
-rw-r--r--test/irb/test_option.rb4
-rw-r--r--test/irb/test_raise_no_backtrace_exception.rb10
-rw-r--r--test/irb/test_ruby_lex.rb130
-rw-r--r--test/irb/test_workspace.rb5
-rw-r--r--test/irb/yamatanooroti/test_rendering.rb23
-rw-r--r--test/json/json_parser_test.rb4
-rw-r--r--test/lib/jit_support.rb35
-rw-r--r--test/mkmf/base.rb1
-rw-r--r--test/mkmf/test_config.rb4
-rw-r--r--test/mkmf/test_constant.rb8
-rw-r--r--test/mkmf/test_pkg_config.rb4
-rw-r--r--test/net/fixtures/Makefile6
-rw-r--r--test/net/fixtures/cacert.pem44
-rw-r--r--test/net/fixtures/server.crt99
-rw-r--r--test/net/fixtures/server.key55
-rw-r--r--test/net/http/test_http.rb34
-rw-r--r--test/net/http/test_httpheader.rb6
-rw-r--r--test/net/http/test_httpresponse.rb39
-rw-r--r--test/net/http/test_https.rb8
-rw-r--r--test/net/protocol/test_protocol.rb37
-rw-r--r--test/objspace/test_objspace.rb155
-rw-r--r--test/objspace/test_ractor.rb17
-rw-r--r--test/open-uri/test_open-uri.rb11
-rw-r--r--test/open-uri/test_ssl.rb19
-rw-r--r--test/openssl/fixtures/pkey/p256_too_large.pem5
-rw-r--r--test/openssl/fixtures/pkey/p384_invalid.pem6
-rw-r--r--test/openssl/test_asn1.rb22
-rw-r--r--test/openssl/test_bn.rb6
-rw-r--r--test/openssl/test_cipher.rb6
-rw-r--r--test/openssl/test_hmac.rb9
-rw-r--r--test/openssl/test_ns_spki.rb2
-rw-r--r--test/openssl/test_pair.rb2
-rw-r--r--test/openssl/test_pkey.rb5
-rw-r--r--test/openssl/test_pkey_dsa.rb23
-rw-r--r--test/openssl/test_pkey_ec.rb36
-rw-r--r--test/openssl/test_pkey_rsa.rb23
-rw-r--r--test/openssl/test_ssl.rb87
-rw-r--r--test/openssl/test_ssl_session.rb2
-rw-r--r--test/openssl/test_x509cert.rb4
-rw-r--r--test/openssl/test_x509crl.rb20
-rw-r--r--test/openssl/test_x509req.rb30
-rw-r--r--test/optparse/test_load.rb141
-rw-r--r--test/optparse/test_optparse.rb15
-rw-r--r--test/optparse/test_placearg.rb6
-rw-r--r--test/optparse/test_summary.rb23
-rw-r--r--test/psych/helper.rb6
-rw-r--r--test/psych/test_array.rb16
-rw-r--r--test/psych/test_coder.rb6
-rw-r--r--test/psych/test_date_time.rb20
-rw-r--r--test/psych/test_encoding.rb7
-rw-r--r--test/psych/test_hash.rb64
-rw-r--r--test/psych/test_merge_keys.rb2
-rw-r--r--test/psych/test_object.rb13
-rw-r--r--test/psych/test_parser.rb8
-rw-r--r--test/psych/test_safe_load.rb31
-rw-r--r--test/psych/test_yaml.rb2
-rw-r--r--test/racc/case.rb2
-rw-r--r--test/rdoc/support/test_case.rb4
-rw-r--r--test/rdoc/test_rdoc_generator_darkfish.rb70
-rw-r--r--test/rdoc/test_rdoc_generator_json_index.rb14
-rw-r--r--test/rdoc/test_rdoc_markdown.rb22
-rw-r--r--test/rdoc/test_rdoc_markup_to_html.rb100
-rw-r--r--test/rdoc/test_rdoc_options.rb11
-rw-r--r--test/rdoc/test_rdoc_parser_ruby.rb8
-rw-r--r--test/rdoc/test_rdoc_rd_block_parser.rb21
-rw-r--r--test/rdoc/test_rdoc_rdoc.rb42
-rw-r--r--test/rdoc/test_rdoc_ri_driver.rb17
-rw-r--r--test/rdoc/test_rdoc_store.rb3
-rw-r--r--test/readline/test_readline.rb7
-rw-r--r--test/reline/test_config.rb14
-rw-r--r--test/reline/test_key_actor_vi.rb8
-rw-r--r--test/reline/test_reline.rb35
-rw-r--r--test/reline/yamatanooroti/termination_checker.rb4
-rw-r--r--test/resolv/test_dns.rb7
-rw-r--r--test/rinda/test_rinda.rb73
-rw-r--r--test/ripper/test_lexer.rb86
-rw-r--r--test/ripper/test_parser_events.rb17
-rw-r--r--test/ripper/test_ripper.rb22
-rw-r--r--test/ripper/test_scanner_events.rb13
-rw-r--r--test/ruby/enc/test_cesu8.rb4
-rw-r--r--test/ruby/enc/test_emoji_breaks.rb3
-rw-r--r--test/ruby/test_argf.rb1
-rw-r--r--test/ruby/test_array.rb122
-rw-r--r--test/ruby/test_assignment.rb10
-rw-r--r--test/ruby/test_ast.rb497
-rw-r--r--test/ruby/test_autoload.rb18
-rw-r--r--test/ruby/test_call.rb7
-rw-r--r--test/ruby/test_class.rb41
-rw-r--r--test/ruby/test_clone.rb7
-rw-r--r--test/ruby/test_complex.rb119
-rw-r--r--test/ruby/test_data.rb249
-rw-r--r--test/ruby/test_dir.rb67
-rw-r--r--test/ruby/test_encoding.rb21
-rw-r--r--test/ruby/test_enumerator.rb117
-rw-r--r--test/ruby/test_env.rb64
-rw-r--r--test/ruby/test_exception.rb73
-rw-r--r--test/ruby/test_file.rb44
-rw-r--r--test/ruby/test_file_exhaustive.rb2
-rw-r--r--test/ruby/test_float.rb17
-rw-r--r--test/ruby/test_gc.rb70
-rw-r--r--test/ruby/test_gc_compact.rb208
-rw-r--r--test/ruby/test_hash.rb85
-rw-r--r--test/ruby/test_integer.rb50
-rw-r--r--test/ruby/test_io.rb149
-rw-r--r--test/ruby/test_io_buffer.rb244
-rw-r--r--test/ruby/test_io_timeout.rb58
-rw-r--r--test/ruby/test_iseq.rb52
-rw-r--r--test/ruby/test_keyword.rb214
-rw-r--r--test/ruby/test_lazy_enumerator.rb20
-rw-r--r--test/ruby/test_m17n.rb54
-rw-r--r--test/ruby/test_marshal.rb30
-rw-r--r--test/ruby/test_method.rb230
-rw-r--r--test/ruby/test_mjit.rb336
-rw-r--r--test/ruby/test_module.rb91
-rw-r--r--test/ruby/test_numeric.rb3
-rw-r--r--test/ruby/test_object.rb31
-rw-r--r--test/ruby/test_objectspace.rb2
-rw-r--r--test/ruby/test_optimization.rb14
-rw-r--r--test/ruby/test_pack.rb60
-rw-r--r--test/ruby/test_parse.rb22
-rw-r--r--test/ruby/test_pattern_matching.rb14
-rw-r--r--test/ruby/test_proc.rb44
-rw-r--r--test/ruby/test_process.rb157
-rw-r--r--test/ruby/test_rand.rb8
-rw-r--r--test/ruby/test_range.rb79
-rw-r--r--test/ruby/test_rational.rb2
-rw-r--r--test/ruby/test_regexp.rb294
-rw-r--r--test/ruby/test_require.rb67
-rw-r--r--test/ruby/test_rubyoptions.rb29
-rw-r--r--test/ruby/test_rubyvm.rb2
-rw-r--r--test/ruby/test_rubyvm_mjit.rb23
-rw-r--r--test/ruby/test_settracefunc.rb191
-rw-r--r--test/ruby/test_shapes.rb441
-rw-r--r--test/ruby/test_sprintf.rb29
-rw-r--r--test/ruby/test_string.rb76
-rw-r--r--test/ruby/test_struct.rb14
-rw-r--r--test/ruby/test_super.rb29
-rw-r--r--test/ruby/test_syntax.rb65
-rw-r--r--test/ruby/test_thread.rb12
-rw-r--r--test/ruby/test_thread_queue.rb53
-rw-r--r--test/ruby/test_time.rb123
-rw-r--r--test/ruby/test_time_tz.rb36
-rw-r--r--test/ruby/test_transcode.rb14
-rw-r--r--test/ruby/test_variable.rb25
-rw-r--r--test/ruby/test_vm_dump.rb2
-rw-r--r--test/ruby/test_weakmap.rb46
-rw-r--r--test/ruby/test_yjit.rb453
-rw-r--r--test/ruby/test_yjit_exit_locations.rb4
-rw-r--r--test/rubygems/alternate_cert.pem28
-rw-r--r--test/rubygems/alternate_cert_32.pem30
-rw-r--r--test/rubygems/alternate_key.pem50
-rw-r--r--test/rubygems/bad_rake.rb1
-rw-r--r--test/rubygems/bundler_test_gem.rb421
-rw-r--r--test/rubygems/child_cert.pem31
-rw-r--r--test/rubygems/child_cert_32.pem31
-rw-r--r--test/rubygems/child_key.pem50
-rw-r--r--test/rubygems/encrypted_private_key.pem52
-rw-r--r--test/rubygems/expired_cert.pem30
-rw-r--r--test/rubygems/fake_certlib/openssl.rb1
-rw-r--r--test/rubygems/future_cert.pem30
-rw-r--r--test/rubygems/future_cert_32.pem30
-rw-r--r--test/rubygems/good_rake.rb1
-rw-r--r--test/rubygems/grandchild_cert.pem31
-rw-r--r--test/rubygems/grandchild_cert_32.pem31
-rw-r--r--test/rubygems/grandchild_key.pem50
-rw-r--r--test/rubygems/helper.rb323
-rw-r--r--test/rubygems/installer_test_case.rb27
-rw-r--r--test/rubygems/invalid_issuer_cert.pem32
-rw-r--r--test/rubygems/invalid_issuer_cert_32.pem32
-rw-r--r--test/rubygems/invalid_key.pem50
-rw-r--r--test/rubygems/invalid_signer_cert.pem30
-rw-r--r--test/rubygems/invalid_signer_cert_32.pem30
-rw-r--r--test/rubygems/invalidchild_cert.pem31
-rw-r--r--test/rubygems/invalidchild_cert_32.pem31
-rw-r--r--test/rubygems/invalidchild_key.pem50
-rw-r--r--test/rubygems/multifactor_auth_utilities.rb111
-rw-r--r--test/rubygems/package/tar_test_case.rb70
-rw-r--r--test/rubygems/packages/Bluebie-legs-0.6.2.gembin0 -> 14336 bytes-rw-r--r--test/rubygems/plugin/exception/rubygems_plugin.rb3
-rw-r--r--test/rubygems/plugin/load/rubygems_plugin.rb1
-rw-r--r--test/rubygems/plugin/standarderror/rubygems_plugin.rb3
-rw-r--r--test/rubygems/private_key.pem50
-rw-r--r--test/rubygems/public_cert.pem32
-rw-r--r--test/rubygems/public_cert_32.pem30
-rw-r--r--test/rubygems/public_key.pem14
-rw-r--r--test/rubygems/rubygems/commands/crash_command.rb1
-rw-r--r--test/rubygems/rubygems_plugin.rb5
-rw-r--r--test/rubygems/simple_gem.rb3
-rw-r--r--test/rubygems/specifications/bar-0.0.2.gemspec2
-rw-r--r--test/rubygems/specifications/rubyforge-0.0.1.gemspec10
-rw-r--r--test/rubygems/test_bundled_ca.rb25
-rw-r--r--test/rubygems/test_config.rb9
-rw-r--r--test/rubygems/test_deprecate.rb9
-rw-r--r--test/rubygems/test_exit.rb12
-rw-r--r--test/rubygems/test_gem.rb1016
-rw-r--r--test/rubygems/test_gem_available_set.rb43
-rw-r--r--test/rubygems/test_gem_bundler_version_finder.rb10
-rw-r--r--test/rubygems/test_gem_command.rb89
-rw-r--r--test/rubygems/test_gem_command_manager.rb126
-rw-r--r--test/rubygems/test_gem_commands_build_command.rb137
-rw-r--r--test/rubygems/test_gem_commands_cert_command.rb195
-rw-r--r--test/rubygems/test_gem_commands_check_command.rb9
-rw-r--r--test/rubygems/test_gem_commands_cleanup_command.rb81
-rw-r--r--test/rubygems/test_gem_commands_contents_command.rb55
-rw-r--r--test/rubygems/test_gem_commands_dependency_command.rb73
-rw-r--r--test/rubygems/test_gem_commands_environment_command.rb33
-rw-r--r--test/rubygems/test_gem_commands_exec_command.rb853
-rw-r--r--test/rubygems/test_gem_commands_fetch_command.rb75
-rw-r--r--test/rubygems/test_gem_commands_generate_index_command.rb15
-rw-r--r--test/rubygems/test_gem_commands_help_command.rb27
-rw-r--r--test/rubygems/test_gem_commands_info_command.rb31
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb284
-rw-r--r--test/rubygems/test_gem_commands_list_command.rb9
-rw-r--r--test/rubygems/test_gem_commands_lock_command.rb21
-rw-r--r--test/rubygems/test_gem_commands_mirror.rb5
-rw-r--r--test/rubygems/test_gem_commands_open_command.rb9
-rw-r--r--test/rubygems/test_gem_commands_outdated_command.rb19
-rw-r--r--test/rubygems/test_gem_commands_owner_command.rb277
-rw-r--r--test/rubygems/test_gem_commands_pristine_command.rb235
-rw-r--r--test/rubygems/test_gem_commands_push_command.rb253
-rw-r--r--test/rubygems/test_gem_commands_query_command.rb147
-rw-r--r--test/rubygems/test_gem_commands_search_command.rb5
-rw-r--r--test/rubygems/test_gem_commands_server_command.rb5
-rw-r--r--test/rubygems/test_gem_commands_setup_command.rb157
-rw-r--r--test/rubygems/test_gem_commands_signin_command.rb102
-rw-r--r--test/rubygems/test_gem_commands_signout_command.rb6
-rw-r--r--test/rubygems/test_gem_commands_sources_command.rb57
-rw-r--r--test/rubygems/test_gem_commands_specification_command.rb65
-rw-r--r--test/rubygems/test_gem_commands_stale_command.rb9
-rw-r--r--test/rubygems/test_gem_commands_uninstall_command.rb180
-rw-r--r--test/rubygems/test_gem_commands_unpack_command.rb63
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb191
-rw-r--r--test/rubygems/test_gem_commands_which_command.rb13
-rw-r--r--test/rubygems/test_gem_commands_yank_command.rb205
-rw-r--r--test/rubygems/test_gem_config_file.rb175
-rw-r--r--test/rubygems/test_gem_dependency.rb149
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb365
-rw-r--r--test/rubygems/test_gem_dependency_list.rb95
-rw-r--r--test/rubygems/test_gem_dependency_resolution_error.rb9
-rw-r--r--test/rubygems/test_gem_doctor.rb53
-rw-r--r--test/rubygems/test_gem_ext_builder.rb121
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder.rb107
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/build.rb21
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/custom_name.gemspec8
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock (renamed from test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.lock)56
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml (renamed from test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.toml)2
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/src/lib.rs (renamed from test/rubygems/test_gem_ext_cargo_builder/custom_name/src/lib.rs)2
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/lib/custom_name.rb3
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock70
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml2
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/build.rb21
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/rust_ruby_example.gemspec2
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/src/lib.rs12
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb39
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder_unit.rb57
-rw-r--r--test/rubygems/test_gem_ext_cmake_builder.rb33
-rw-r--r--test/rubygems/test_gem_ext_configure_builder.rb27
-rw-r--r--test/rubygems/test_gem_ext_ext_conf_builder.rb102
-rw-r--r--test/rubygems/test_gem_ext_rake_builder.rb31
-rw-r--r--test/rubygems/test_gem_gem_runner.rb24
-rw-r--r--test/rubygems/test_gem_gemcutter_utilities.rb254
-rw-r--r--test/rubygems/test_gem_impossible_dependencies_error.rb9
-rw-r--r--test/rubygems/test_gem_indexer.rb154
-rw-r--r--test/rubygems/test_gem_install_update_options.rb33
-rw-r--r--test/rubygems/test_gem_installer.rb726
-rw-r--r--test/rubygems/test_gem_local_remote_options.rb21
-rw-r--r--test/rubygems/test_gem_name_tuple.rb9
-rw-r--r--test/rubygems/test_gem_package.rb474
-rw-r--r--test/rubygems/test_gem_package_old.rb27
-rw-r--r--test/rubygems/test_gem_package_tar_header.rb95
-rw-r--r--test/rubygems/test_gem_package_tar_reader.rb64
-rw-r--r--test/rubygems/test_gem_package_tar_reader_entry.rb178
-rw-r--r--test/rubygems/test_gem_package_tar_writer.rb153
-rw-r--r--test/rubygems/test_gem_package_task.rb37
-rw-r--r--test/rubygems/test_gem_path_support.rb29
-rw-r--r--test/rubygems/test_gem_platform.rb560
-rw-r--r--test/rubygems/test_gem_rdoc.rb29
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb309
-rw-r--r--test/rubygems/test_gem_request.rb122
-rw-r--r--test/rubygems/test_gem_request_connection_pools.rb59
-rw-r--r--test/rubygems/test_gem_request_set.rb201
-rw-r--r--test/rubygems/test_gem_request_set_gem_dependency_api.rb419
-rw-r--r--test/rubygems/test_gem_request_set_lockfile.rb173
-rw-r--r--test/rubygems/test_gem_request_set_lockfile_parser.rb115
-rw-r--r--test/rubygems/test_gem_request_set_lockfile_tokenizer.rb125
-rw-r--r--test/rubygems/test_gem_requirement.rb89
-rw-r--r--test/rubygems/test_gem_resolver.rb266
-rw-r--r--test/rubygems/test_gem_resolver_activation_request.rb13
-rw-r--r--test/rubygems/test_gem_resolver_api_set.rb67
-rw-r--r--test/rubygems/test_gem_resolver_api_specification.rb95
-rw-r--r--test/rubygems/test_gem_resolver_best_set.rb45
-rw-r--r--test/rubygems/test_gem_resolver_composed_set.rb3
-rw-r--r--test/rubygems/test_gem_resolver_conflict.rb25
-rw-r--r--test/rubygems/test_gem_resolver_dependency_request.rb31
-rw-r--r--test/rubygems/test_gem_resolver_git_set.rb43
-rw-r--r--test/rubygems/test_gem_resolver_git_specification.rb43
-rw-r--r--test/rubygems/test_gem_resolver_index_set.rb25
-rw-r--r--test/rubygems/test_gem_resolver_index_specification.rb33
-rw-r--r--test/rubygems/test_gem_resolver_installed_specification.rb11
-rw-r--r--test/rubygems/test_gem_resolver_installer_set.rb113
-rw-r--r--test/rubygems/test_gem_resolver_local_specification.rb15
-rw-r--r--test/rubygems/test_gem_resolver_lock_set.rb25
-rw-r--r--test/rubygems/test_gem_resolver_lock_specification.rb35
-rw-r--r--test/rubygems/test_gem_resolver_requirement_list.rb3
-rw-r--r--test/rubygems/test_gem_resolver_specification.rb17
-rw-r--r--test/rubygems/test_gem_resolver_vendor_set.rb13
-rw-r--r--test/rubygems/test_gem_resolver_vendor_specification.rb21
-rw-r--r--test/rubygems/test_gem_security.rb137
-rw-r--r--test/rubygems/test_gem_security_policy.rb144
-rw-r--r--test/rubygems/test_gem_security_signer.rb69
-rw-r--r--test/rubygems/test_gem_security_trust_dir.rb13
-rw-r--r--test/rubygems/test_gem_silent_ui.rb65
-rw-r--r--test/rubygems/test_gem_source.rb89
-rw-r--r--test/rubygems/test_gem_source_fetch_problem.rb19
-rw-r--r--test/rubygems/test_gem_source_git.rb131
-rw-r--r--test/rubygems/test_gem_source_installed.rb33
-rw-r--r--test/rubygems/test_gem_source_list.rb11
-rw-r--r--test/rubygems/test_gem_source_local.rb29
-rw-r--r--test/rubygems/test_gem_source_lock.rb63
-rw-r--r--test/rubygems/test_gem_source_specific_file.rb35
-rw-r--r--test/rubygems/test_gem_source_subpath_problem.rb15
-rw-r--r--test/rubygems/test_gem_source_vendor.rb27
-rw-r--r--test/rubygems/test_gem_spec_fetcher.rb145
-rw-r--r--test/rubygems/test_gem_specification.rb965
-rw-r--r--test/rubygems/test_gem_stream_ui.rb75
-rw-r--r--test/rubygems/test_gem_stub_specification.rb65
-rw-r--r--test/rubygems/test_gem_text.rb3
-rw-r--r--test/rubygems/test_gem_uninstaller.rb235
-rw-r--r--test/rubygems/test_gem_unsatisfiable_dependency_error.rb7
-rw-r--r--test/rubygems/test_gem_update_suggestion.rb209
-rw-r--r--test/rubygems/test_gem_uri.rb10
-rw-r--r--test/rubygems/test_gem_uri_formatter.rb29
-rw-r--r--test/rubygems/test_gem_util.rb47
-rw-r--r--test/rubygems/test_gem_validator.rb16
-rw-r--r--test/rubygems/test_gem_version.rb40
-rw-r--r--test/rubygems/test_gem_version_option.rb31
-rw-r--r--test/rubygems/test_kernel.rb91
-rw-r--r--test/rubygems/test_project_sanity.rb35
-rw-r--r--test/rubygems/test_remote_fetch_error.rb15
-rw-r--r--test/rubygems/test_require.rb221
-rw-r--r--test/rubygems/test_rubygems.rb18
-rw-r--r--test/rubygems/test_webauthn_listener.rb143
-rw-r--r--test/rubygems/test_webauthn_listener_response.rb93
-rw-r--r--test/rubygems/test_webauthn_poller.rb124
-rw-r--r--test/rubygems/utilities.rb132
-rw-r--r--test/rubygems/wrong_key_cert.pem30
-rw-r--r--test/rubygems/wrong_key_cert_32.pem30
-rw-r--r--test/runner.rb12
-rw-r--r--test/socket/test_addrinfo.rb4
-rw-r--r--test/socket/test_nonblock.rb4
-rw-r--r--test/socket/test_socket.rb2
-rw-r--r--test/socket/test_tcp.rb2
-rw-r--r--test/socket/test_unix.rb147
-rw-r--r--test/stringio/test_stringio.rb2
-rw-r--r--test/strscan/test_ractor.rb2
-rw-r--r--test/strscan/test_stringscanner.rb63
-rw-r--r--test/test_extlibs.rb2
-rw-r--r--test/test_pp.rb9
-rw-r--r--test/test_rbconfig.rb9
-rw-r--r--test/test_time.rb9
-rw-r--r--test/test_timeout.rb13
-rw-r--r--test/test_tmpdir.rb18
-rw-r--r--test/test_trick.rb23
-rw-r--r--test/uri/test_common.rb11
-rw-r--r--test/uri/test_generic.rb27
-rw-r--r--test/uri/test_ldap.rb6
-rw-r--r--test/uri/test_parser.rb29
-rw-r--r--test/yaml/test_store.rb2
-rw-r--r--test/zlib/test_zlib.rb4
-rw-r--r--thread.c1191
-rw-r--r--thread_none.c7
-rw-r--r--thread_pthread.c360
-rw-r--r--thread_pthread.h35
-rw-r--r--thread_sync.c341
-rw-r--r--thread_sync.rb68
-rw-r--r--thread_win32.c206
-rw-r--r--time.c2039
-rw-r--r--timev.h11
-rw-r--r--timev.rb277
-rw-r--r--tool/bundler/dev_gems.rb12
-rw-r--r--tool/bundler/dev_gems.rb.lock43
-rw-r--r--tool/bundler/rubocop_gems.rb1
-rw-r--r--tool/bundler/rubocop_gems.rb.lock60
-rw-r--r--tool/bundler/standard_gems.rb1
-rw-r--r--tool/bundler/standard_gems.rb.lock70
-rw-r--r--tool/bundler/test_gems.rb2
-rw-r--r--tool/bundler/test_gems.rb.lock11
-rwxr-xr-xtool/checksum.rb4
-rw-r--r--tool/downloader.rb84
-rwxr-xr-x[-rw-r--r--]tool/enc-case-folding.rb (renamed from enc/unicode/case-folding.rb)10
-rw-r--r--tool/enc-emoji-citrus-gen.rb4
-rwxr-xr-xtool/enc-unicode.rb31
-rwxr-xr-xtool/expand-config.rb14
-rw-r--r--tool/fake.rb10
-rwxr-xr-xtool/fetch-bundled_gems.rb2
-rwxr-xr-xtool/file2lastrev.rb91
-rw-r--r--tool/gem-unpack.rb34
-rwxr-xr-xtool/gen-mailmap.rb4
-rw-r--r--tool/generic_erb.rb44
-rwxr-xr-xtool/id2token.rb11
-rw-r--r--tool/lib/bundled_gem.rb68
-rw-r--r--tool/lib/colorize.rb4
-rw-r--r--tool/lib/core_assertions.rb86
-rw-r--r--tool/lib/envutil.rb18
-rw-r--r--tool/lib/leakchecker.rb6
-rw-r--r--tool/lib/output.rb57
-rw-r--r--tool/lib/test/unit.rb34
-rw-r--r--tool/lib/vcs.rb149
-rw-r--r--tool/lib/vpath.rb7
-rw-r--r--tool/lib/webrick/httpserver.rb1
-rw-r--r--tool/lib/webrick/httputils.rb2
-rwxr-xr-xtool/ln_sr.rb8
-rw-r--r--tool/m4/ruby_default_arch.m41
-rw-r--r--tool/m4/ruby_wasm_tools.m413
-rwxr-xr-xtool/make-snapshot38
-rw-r--r--tool/make_hgraph.rb7
-rwxr-xr-xtool/merger.rb189
-rwxr-xr-xtool/mjit/bindgen.rb435
-rw-r--r--tool/mk_builtin_loader.rb67
-rwxr-xr-xtool/mkconfig.rb12
-rwxr-xr-xtool/mkrunnable.rb4
-rwxr-xr-xtool/outdate-bundled-gems.rb135
-rwxr-xr-xtool/pure_parser.rb24
-rwxr-xr-xtool/rbinstall.rb222
-rw-r--r--tool/rbs_skip_tests11
-rwxr-xr-xtool/redmine-backporter.rb158
-rw-r--r--tool/ruby_vm/controllers/application_controller.rb5
-rw-r--r--tool/ruby_vm/helpers/dumper.rb7
-rw-r--r--tool/ruby_vm/scripts/insns2vm.rb12
-rw-r--r--tool/ruby_vm/views/_mjit_compile_getinlinecache.erb31
-rw-r--r--tool/ruby_vm/views/_mjit_compile_insn.erb92
-rw-r--r--tool/ruby_vm/views/_mjit_compile_insn_body.erb129
-rw-r--r--tool/ruby_vm/views/_mjit_compile_invokebuiltin.erb29
-rw-r--r--tool/ruby_vm/views/_mjit_compile_ivar.erb110
-rw-r--r--tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb38
-rw-r--r--tool/ruby_vm/views/_mjit_compile_send.erb119
-rw-r--r--tool/ruby_vm/views/lib/ruby_vm/mjit/instruction.rb.erb40
-rw-r--r--tool/ruby_vm/views/mjit_compile.inc.erb110
-rw-r--r--tool/ruby_vm/views/mjit_sp_inc.inc.erb17
-rw-r--r--tool/ruby_vm/views/optinsn.inc.erb4
-rwxr-xr-xtool/runruby.rb9
-rwxr-xr-xtool/sync_default_gems.rb1261
-rw-r--r--tool/test-bundled-gems.rb84
-rwxr-xr-xtool/test/test_sync_default_gems.rb76
-rw-r--r--tool/test/testunit/test_assertion.rb24
-rw-r--r--tool/test/webrick/test_filehandler.rb4
-rw-r--r--tool/test/webrick/test_httprequest.rb2
-rw-r--r--tool/transcode-tblgen.rb2
-rw-r--r--tool/transform_mjit_header.rb14
-rw-r--r--tool/update-NEWS-refs.rb37
-rwxr-xr-xtool/update-bundled_gems.rb5
-rwxr-xr-xtool/update-deps6
-rw-r--r--trace_point.rb51
-rw-r--r--transcode.c574
-rw-r--r--transcode_data.h32
-rw-r--r--transient_heap.c3
-rw-r--r--util.c172
-rw-r--r--variable.c1750
-rw-r--r--variable.h10
-rw-r--r--version.c47
-rw-r--r--version.h58
-rw-r--r--vm.c1353
-rw-r--r--vm_args.c529
-rw-r--r--vm_backtrace.c459
-rw-r--r--vm_callinfo.h82
-rw-r--r--vm_core.h400
-rw-r--r--vm_debug.h11
-rw-r--r--vm_dump.c1005
-rw-r--r--vm_eval.c671
-rw-r--r--vm_exec.c28
-rw-r--r--vm_insnhelper.c2725
-rw-r--r--vm_insnhelper.h7
-rw-r--r--vm_method.c966
-rw-r--r--vm_sync.c6
-rw-r--r--vm_trace.c473
-rw-r--r--wasm/README.md10
-rw-r--r--wasm/machine.c6
-rw-r--r--wasm/machine.h5
-rw-r--r--win32/Makefile.sub196
-rwxr-xr-xwin32/configure.bat27
-rw-r--r--win32/dir.h2
-rw-r--r--win32/file.c593
-rw-r--r--win32/file.h38
-rwxr-xr-xwin32/ifchange.bat1
-rwxr-xr-xwin32/mkexports.rb4
-rwxr-xr-xwin32/resource.rb2
-rw-r--r--win32/setup.mak25
-rw-r--r--win32/win32.c5904
-rw-r--r--win32/winmain.c4
-rw-r--r--yjit.c213
-rw-r--r--yjit.h29
-rw-r--r--yjit.rb128
-rw-r--r--yjit/Cargo.lock7
-rw-r--r--yjit/Cargo.toml14
-rw-r--r--yjit/bindgen/Cargo.lock131
-rw-r--r--yjit/bindgen/Cargo.toml2
-rw-r--r--yjit/bindgen/src/main.rs113
-rw-r--r--yjit/not_gmake.mk14
-rw-r--r--yjit/src/asm/arm64/README.md16
-rw-r--r--yjit/src/asm/arm64/arg/bitmask_imm.rs255
-rw-r--r--yjit/src/asm/arm64/arg/condition.rs52
-rw-r--r--yjit/src/asm/arm64/arg/inst_offset.rs47
-rw-r--r--yjit/src/asm/arm64/arg/mod.rs18
-rw-r--r--yjit/src/asm/arm64/arg/sf.rs19
-rw-r--r--yjit/src/asm/arm64/arg/shifted_imm.rs81
-rw-r--r--yjit/src/asm/arm64/arg/sys_reg.rs6
-rw-r--r--yjit/src/asm/arm64/arg/truncate.rs66
-rw-r--r--yjit/src/asm/arm64/inst/atomic.rs86
-rw-r--r--yjit/src/asm/arm64/inst/branch.rs100
-rw-r--r--yjit/src/asm/arm64/inst/branch_cond.rs78
-rw-r--r--yjit/src/asm/arm64/inst/breakpoint.rs55
-rw-r--r--yjit/src/asm/arm64/inst/call.rs104
-rw-r--r--yjit/src/asm/arm64/inst/conditional.rs73
-rw-r--r--yjit/src/asm/arm64/inst/data_imm.rs143
-rw-r--r--yjit/src/asm/arm64/inst/data_reg.rs192
-rw-r--r--yjit/src/asm/arm64/inst/halfword_imm.rs179
-rw-r--r--yjit/src/asm/arm64/inst/load_literal.rs89
-rw-r--r--yjit/src/asm/arm64/inst/load_register.rs108
-rw-r--r--yjit/src/asm/arm64/inst/load_store.rs249
-rw-r--r--yjit/src/asm/arm64/inst/load_store_exclusive.rs109
-rw-r--r--yjit/src/asm/arm64/inst/logical_imm.rs154
-rw-r--r--yjit/src/asm/arm64/inst/logical_reg.rs207
-rw-r--r--yjit/src/asm/arm64/inst/mod.rs50
-rw-r--r--yjit/src/asm/arm64/inst/mov.rs155
-rw-r--r--yjit/src/asm/arm64/inst/nop.rs44
-rw-r--r--yjit/src/asm/arm64/inst/pc_rel.rs107
-rw-r--r--yjit/src/asm/arm64/inst/reg_pair.rs212
-rw-r--r--yjit/src/asm/arm64/inst/sbfm.rs103
-rw-r--r--yjit/src/asm/arm64/inst/shift_imm.rs147
-rw-r--r--yjit/src/asm/arm64/inst/sys_reg.rs86
-rw-r--r--yjit/src/asm/arm64/inst/test_bit.rs133
-rw-r--r--yjit/src/asm/arm64/mod.rs1580
-rw-r--r--yjit/src/asm/arm64/opnd.rs195
-rw-r--r--yjit/src/asm/mod.rs626
-rw-r--r--yjit/src/asm/x86_64/mod.rs368
-rw-r--r--yjit/src/asm/x86_64/tests.rs29
-rw-r--r--yjit/src/backend/arm64/mod.rs1491
-rw-r--r--yjit/src/backend/ir.rs1576
-rw-r--r--yjit/src/backend/mod.rs8
-rw-r--r--yjit/src/backend/tests.rs331
-rw-r--r--yjit/src/backend/x86_64/mod.rs895
-rw-r--r--yjit/src/codegen.rs5101
-rw-r--r--yjit/src/core.rs939
-rw-r--r--yjit/src/cruby.rs351
-rw-r--r--yjit/src/cruby_bindings.inc.rs1185
-rw-r--r--yjit/src/disasm.rs163
-rw-r--r--yjit/src/invariants.rs251
-rw-r--r--yjit/src/lib.rs2
-rw-r--r--yjit/src/options.rs60
-rw-r--r--yjit/src/stats.rs123
-rw-r--r--yjit/src/utils.rs262
-rw-r--r--yjit/src/virtualmem.rs111
-rw-r--r--yjit/src/yjit.rs47
-rw-r--r--yjit/yjit.mk68
2289 files changed, 180025 insertions, 81922 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index ea9b81aa47..05ff204541 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -16,18 +16,19 @@ skip_commits:
- doc/*
- '**/*.md'
- '**/*.rdoc'
+ - '**/.document'
+ - '**/*.[1-8]'
+ - '**/*.ronn'
environment:
ruby_version: "24-%Platform%"
- zlib_version: "1.2.12"
matrix:
+ # Test only the oldest supported version because AppVeyor is unstable, its concurrency
+ # is limited, and compatibility issues that happen only in newer versions are rare.
+ # You may test some other stuff on GitHub Actions instead.
- build: vs
- vs: 120
- ssl: OpenSSL
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- GEMS_FOR_TEST: ""
- - build: vs
- vs: 140
+ vs: 120 # Visual Studio 2013
ssl: OpenSSL-v111
+ # The worker image name. This is NOT the Visual Studio version we're using here.
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
GEMS_FOR_TEST: ""
RELINE_TEST_ENCODING: "UTF-8"
@@ -47,8 +48,9 @@ for:
- cd C:\Tools\vcpkg
- git pull -q
- .\bootstrap-vcpkg.bat
+ - ps: Start-FileDownload 'https://github.com/microsoft/vcpkg-tool/releases/download/2023-08-09/vcpkg.exe' -FileName 'C:\Tools\vcpkg\vcpkg.exe'
- cd %APPVEYOR_BUILD_FOLDER%
- - vcpkg --triplet %Platform%-windows install libffi libyaml readline zlib
+ - vcpkg --triplet %Platform%-windows install --x-use-aria2 libffi libyaml readline zlib
- CALL SET vcvars=%%^VS%VS%COMNTOOLS^%%..\..\VC\vcvarsall.bat
- SET vcvars
- '"%vcvars%" %Platform:x64=amd64%'
@@ -68,9 +70,6 @@ for:
- mkdir \usr\local\bin
- mkdir \usr\local\include
- mkdir \usr\local\lib
- - SET ZLIB_ZIP=.downloaded-cache\zlib%zlib_version:.=%.zip
- - if not exist %ZLIB_ZIP% curl -fsSL -o %ZLIB_ZIP% --retry 10 https://zlib.net/zlib%zlib_version:.=%.zip
- - 7z x -aos -o%APPVEYOR_BUILD_FOLDER%\ext\zlib %ZLIB_ZIP%
- for %%I in (%OPENSSL_DIR%\*.dll) do mklink /h \usr\local\bin\%%~nxI %%I
- for %%I in (c:\Tools\vcpkg\installed\%Platform%-windows\bin\*.dll) do (
if not %%~nI == readline mklink \usr\local\bin\%%~nxI %%I
@@ -78,6 +77,7 @@ for:
- attrib +r /s /d
- mkdir %Platform%-mswin_%vs%
build_script:
+ - set HAVE_GIT=no
- cd %APPVEYOR_BUILD_FOLDER%
- cd %Platform%-mswin_%vs%
- >-
@@ -94,7 +94,7 @@ for:
- nmake -l "TESTOPTS=-v -q" btest
- nmake -l "TESTOPTS=-v -q" test-basic
- >-
- nmake -l "TESTOPTS=-v --timeout-scale=3.0
+ nmake -l "TESTOPTS=--timeout-scale=3.0
--excludes=../test/excludes/_appveyor -j%JOBS%
--exclude win32ole
--exclude test_bignum
@@ -105,7 +105,7 @@ for:
# separately execute tests without -j which may crash worker with -j.
- >-
nmake -l
- "TESTOPTS=-v --timeout-scale=3.0 --excludes=../test/excludes/_appveyor"
+ "TESTOPTS=--timeout-scale=3.0 --excludes=../test/excludes/_appveyor"
TESTS="
../test/win32ole
../test/ruby/test_bignum.rb
@@ -123,7 +123,7 @@ notifications:
{{^isPullRequest}}
{
"ci": "AppVeyor CI",
- "env": "Visual Studio 2013 / 2015",
+ "env": "Visual Studio 2013",
"url": "{{buildUrl}}",
"commit": "{{commitId}}",
"branch": "{{branch}}"
diff --git a/.cirrus.yml b/.cirrus.yml
deleted file mode 100644
index 0cab0023c2..0000000000
--- a/.cirrus.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-# This CI is used to test Arm cases. We can set the maximum 16 tasks.
-# The entire testing design is inspired from .github/workflows/compilers.yml.
-
-# By default, Cirrus mounts an empty volume to `/tmp`
-# which triggers all sorts of warnings like "system temporary path is world-writable: /tmp".
-# Lets workaround it by specifying a custom volume mount point.
-env:
- CIRRUS_VOLUME: /cirrus-ci-volume
- LANG: C.UTF-8
-
-task:
- name: Arm64 Graviton2 / $CC
- skip: "changesIncludeOnly('doc/**', '**.{md,rdoc}')"
- arm_container:
- # We use the arm64 images at https://github.com/ruby/ruby-ci-image/pkgs/container/ruby-ci-image .
- image: ghcr.io/ruby/ruby-ci-image:$CC
- # Define the used cpu core in each matrix task. We can use total 16 cpu
- # cores in entire matrix. [cpu] = [total cpu: 16] / [number of tasks]
- cpu: 8
- # We can request maximum 4 GB per cpu.
- # [memory per task] = [memory per cpu: 4 GB] * [cpu]
- memory: 32G
- env:
- CIRRUS_CLONE_DEPTH: 50
- optflags: '-O1'
- debugflags: '-ggdb3'
- RUBY_PREFIX: /tmp/ruby-prefix
- RUBY_DEBUG: ci rgengc
- RUBY_TESTOPTS: >-
- -q
- --color=always
- --tty=no
- matrix:
- CC: clang-12
- CC: gcc-11
- id_script: id
- set_env_script:
- # Set `GNUMAKEFLAGS`, because the flags are GNU make specific. Note using
- # the `make` environment variable used in compilers.yml causes some rubygems
- # tests to fail.
- # https://github.com/rubygems/rubygems/issues/4921
- - echo "GNUMAKEFLAGS=-s -j$((1 + $CIRRUS_CPU))" >> $CIRRUS_ENV
- print_env_script:
- - echo "GNUMAKEFLAGS=$GNUMAKEFLAGS"
- # Arm containers are executed in AWS's EKS, and it's not yet supporting IPv6
- # See https://github.com/aws/containers-roadmap/issues/835
- disable_ipv6_script: sudo ./tool/disable_ipv6.sh
- autogen_script: ./autogen.sh
- configure_script: >-
- ./configure -C
- --enable-debug-env
- --disable-install-doc
- --with-ext=-test-/cxxanyargs,+
- --prefix="$RUBY_PREFIX"
- make_extract-extlibs_script: make extract-extlibs
- make_incs_script: make incs
- make_script: make
- make_leaked-globals_script: make leaked-globals
- make_test_script: make test
- make_install_script: make install
- install_gems_for_test_script: $RUBY_PREFIX/bin/gem install --no-doc timezone tzinfo
- make_test-tool_script: make test-tool
- make_test-all_script: make test-all
- make_test-spec_script: make test-spec
diff --git a/.document b/.document
index 5494bcc7fe..3a6b0c238c 100644
--- a/.document
+++ b/.document
@@ -18,14 +18,18 @@ gc.rb
io.rb
kernel.rb
marshal.rb
+mjit.rb
numeric.rb
nilclass.rb
pack.rb
ractor.rb
string.rb
+symbol.rb
timev.rb
+thread_sync.rb
trace_point.rb
warning.rb
+yjit.rb
# the lib/ directory (which has its own .document file)
lib
diff --git a/.gdbinit b/.gdbinit
index 8979e8b47c..34d044caf6 100644
--- a/.gdbinit
+++ b/.gdbinit
@@ -544,13 +544,13 @@ end
define rp_class
printf "(struct RClass *) %p", (void*)$arg0
- if ((struct RClass *)($arg0))->ptr.origin_ != $arg0
- printf " -> %p", ((struct RClass *)($arg0))->ptr.origin_
+ if RCLASS_ORIGIN((struct RClass *)($arg0)) != $arg0
+ printf " -> %p", RCLASS_ORIGIN((struct RClass *)($arg0))
end
printf "\n"
rb_classname $arg0
print/x *(struct RClass *)($arg0)
- print *((struct RClass *)($arg0))->ptr
+ print *RCLASS_EXT((struct RClass *)($arg0))
end
document rp_class
Print the content of a Class/Module.
@@ -979,8 +979,8 @@ end
define rb_ps_vm
print $ps_vm = (rb_vm_t*)$arg0
- set $ps_thread_ln = $ps_vm->living_threads.n.next
- set $ps_thread_ln_last = $ps_vm->living_threads.n.prev
+ set $ps_thread_ln = $ps_vm->ractor.main_ractor.threads.set.n.next
+ set $ps_thread_ln_last = $ps_vm->ractor.main_ractor.threads.set.n.prev
while 1
set $ps_thread_th = (rb_thread_t *)$ps_thread_ln
set $ps_thread = (VALUE)($ps_thread_th->self)
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000000..6c5eac5a0f
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,23 @@
+# This is a file used by GitHub to ignore the following commits on `git blame`.
+#
+# You can also do the same thing in your local repository with:
+# $ git config --local blame.ignoreRevsFile .git-blame-ignore-revs
+
+# Expand tabs
+5b21e94bebed90180d8ff63dad03b8b948361089
+
+# Enable Style/StringLiterals cop for RubyGems/Bundler
+d7ffd3fea402239b16833cc434404a7af82d44f3
+
+# [ruby/digest] Revert tab-expansion in external files
+48b09aae7ec5632209229dcc294dd0d75a93a17f
+8a65cf3b61c60e4cb886f59a73ff6db44364bfa9
+39dc9f9093901d40d2998653948d5da38b18ee2c
+
+# [ruby/io-nonblock] Revert tab expansion
+f28287d34c03f472ffe90ea262bdde9affd4b965
+0d842fecb4f75ab3b1d4097ebdb8e88f51558041
+4ba2c66761d6a293abdfba409241d31063cefd62
+
+# Make benchmark indentation consistent
+fc4acf8cae82e5196186d3278d831f2438479d91
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
deleted file mode 100644
index c8d7ec5e0d..0000000000
--- a/.github/CODEOWNERS
+++ /dev/null
@@ -1,11 +0,0 @@
-# Lines starting with '#' are comments.
-# Each line is a file pattern followed by one or more owners.
-# Code owners will be automatically tagged as reviewers when a pull request is opened
-
-# YJIT sources and tests
-yjit* @maximecb @xrxr @tenderlove
-yjit/* @maximecb @xrxr @tenderlove
-doc/yjit/* @maximecb @xrxr @tenderlove
-bootstraptest/test_yjit* @maximecb @xrxr @tenderlove
-test/ruby/test_yjit* @maximecb @xrxr @tenderlove
-.github/workflows/yjit* @maximecb @xrxr @tenderlove
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index b18fd29357..bc63aca35b 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -3,4 +3,4 @@ updates:
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
- interval: 'weekly'
+ interval: 'monthly'
diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml
index 1c314da911..ebaafe3bf0 100644
--- a/.github/workflows/baseruby.yml
+++ b/.github/workflows/baseruby.yml
@@ -4,22 +4,36 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
baseruby:
name: BASERUBY
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
strategy:
matrix:
@@ -34,12 +48,12 @@ jobs:
- ruby-3.1
steps:
- - uses: actions/checkout@v3
- - uses: actions/cache@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: .downloaded-cache
key: downloaded-cache
- - uses: ruby/setup-ruby@v1
+ - uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
with:
ruby-version: ${{ matrix.ruby }}
bundler: none
@@ -51,7 +65,7 @@ jobs:
- run: make incs
- run: make all
- run: make test
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -59,7 +73,7 @@ jobs:
"env": "${{ github.workflow }} / BASERUBY @ ${{ matrix.ruby }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/bundled_gems.yml b/.github/workflows/bundled_gems.yml
index 923fc524da..070c0fa1dd 100644
--- a/.github/workflows/bundled_gems.yml
+++ b/.github/workflows/bundled_gems.yml
@@ -2,18 +2,31 @@ name: bundled_gems
on:
push:
+ branches: [ "master" ]
paths:
- '.github/workflows/bundled_gems.yml'
- 'gems/bundled_gems'
pull_request:
+ branches: [ "master" ]
+ paths:
+ - '.github/workflows/bundled_gems.yml'
+ - 'gems/bundled_gems'
+ merge_group:
+ branches: [ "master" ]
paths:
- '.github/workflows/bundled_gems.yml'
- 'gems/bundled_gems'
schedule:
- cron: '45 6 * * *'
+ workflow_dispatch:
+
+permissions: # added using https://github.com/step-security/secure-workflows
+ contents: read
jobs:
update:
+ permissions:
+ contents: write # for Git to git push
if: ${{ github.event_name != 'schedule' || github.repository == 'ruby/ruby' }}
name: update ${{ github.workflow }}
runs-on: ubuntu-latest
@@ -28,9 +41,9 @@ jobs:
echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
echo "TODAY=$(date +%F)" >> $GITHUB_ENV
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: .downloaded-cache
key: downloaded-cache-${{ github.sha }}
@@ -68,14 +81,14 @@ jobs:
[g, v] unless last[g] == v
end
changed, added = changed.partition {|g, _| last[g]}
- news.sub!(/^\*( +)The following #{type} gems? are updated\.\n\K(?: \1\* .*\n)*/) do
- mark = "#{$1} * "
+ news.sub!(/^\*( +)The following #{type} gems? are updated\.\n+\K(?: \1\*( +).*\n)*/) do
+ mark = "#{$1} *#{$2}"
changed.map {|g, v|"#{mark}#{g} #{v}\n"}.join("")
end or next
- news.sub!(/^\*( +)The following default gems are now bundled gems\.\n\K(?: \1\* .*\n)*/) do
- mark = "#{$1} * "
+ news.sub!(/^\*( +)The following default gems are now bundled gems\.\n+\K(?: \1\*( +).*\n)*/) do
+ mark = "#{$1} *#{$2}"
added.map {|g, v|"#{mark}#{g} #{v}\n"}.join("")
- end or next if added
+ end or next unless added.empty?
File.write("NEWS.md", news)
end
shell: ruby {0}
@@ -101,6 +114,11 @@ jobs:
make
if: ${{ steps.diff.outcome == 'failure' }}
+ - name: Prepare bundled gems
+ run: |
+ make -s prepare-gems
+ if: ${{ steps.diff.outcome == 'failure' }}
+
- name: Test bundled gems
run: |
make -s test-bundled-gems
@@ -132,3 +150,17 @@ jobs:
GIT_AUTHOR_NAME: git
GIT_COMMITTER_NAME: git
if: ${{ github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull') && steps.show.outcome == 'failure' }}
+
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} / update",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml
index 6834d2c9c8..79b2916feb 100644
--- a/.github/workflows/check_dependencies.yml
+++ b/.github/workflows/check_dependencies.yml
@@ -3,23 +3,37 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
update-deps:
strategy:
matrix:
- os: [ubuntu-20.04]
+ os: [ubuntu-22.04]
fail-fast: true
runs-on: ${{ matrix.os }}
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
@@ -32,15 +46,14 @@ jobs:
if: ${{ contains(matrix.os, 'ubuntu') }}
- name: Install libraries
run: |
- brew upgrade
brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline
if: ${{ contains(matrix.os, 'macos') }}
- name: git config
run: |
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
- - uses: actions/cache@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: .downloaded-cache
key: downloaded-cache
@@ -50,7 +63,7 @@ jobs:
- run: make all golf
- run: ruby tool/update-deps --fix
- run: git diff --no-ext-diff --ignore-submodules --exit-code
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -58,7 +71,7 @@ jobs:
"env": "${{ matrix.os }} / Dependencies need to update",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/check_misc.yml b/.github/workflows/check_misc.yml
deleted file mode 100644
index 32a07f7fd6..0000000000
--- a/.github/workflows/check_misc.yml
+++ /dev/null
@@ -1,99 +0,0 @@
-name: Miscellaneous checks
-on: [push, pull_request]
-
-concurrency:
- group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
- cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
-
-jobs:
- checks:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - name: Check if C-sources are US-ASCII
- run: |
- ! grep -r -n '[^ -~]' *.[chy] include internal win32/*.[ch]
- - name: Check for trailing spaces
- run: |
- ! git grep -n '[ ]$' '*.rb' '*.[chy]'
- - name: Check for header macros
- run: |
- ! for header in ruby/*.h; do \
- git grep -l -F -e $header -e HAVE_`echo $header | tr a-z./ A-Z__` -- . > /dev/null || echo $header
- done | grep -F .
- working-directory: include
-
- - uses: actions/cache@v3
- with:
- path: .downloaded-cache
- key: downloaded-cache-${{ github.sha }}
- restore-keys: |
- downloaded-cache
-
- - name: Download previous gems list
- run: |
- data=default_gems.json
- mkdir -p .downloaded-cache
- ln -s .downloaded-cache/$data .
- curl -O -R -z ./$data https://stdgems.org/$data
-
- - name: Make default gems list
- run: |
- #!ruby
- require 'rubygems'
- $:.unshift "lib"
- rgver = File.foreach("lib/rubygems.rb") do |line|
- break $1 if /^\s*VERSION\s*=\s*"([^"]+)"/ =~ line
- end
- gems = Dir.glob("{ext,lib}/**/*.gemspec").map do |f|
- spec = Gem::Specification.load(f)
- "#{spec.name} #{spec.version}"
- end.sort
- File.open("gems/default_gems", "w") do |f|
- f.puts "RubyGems #{rgver}"
- f.puts gems
- end
- shell: ruby --disable=gems {0}
-
- - name: Maintain updated gems list in NEWS
- run: |
- #!ruby
- require 'json'
- news = File.read("NEWS.md")
- prev = news[/since the \*+(\d+\.\d+\.\d+)\*+/, 1]
- prevs = [prev, prev.sub(/\.\d+\z/, '')]
- %W[default].each do |type|
- last = JSON.parse(File.read("#{type}_gems.json"))['gems'].filter_map do |g|
- v = g['versions'].values_at(*prevs).compact.first
- g = g['gem']
- g = 'RubyGems' if g == 'rubygems'
- [g, v] if v
- end.to_h
- changed = File.foreach("gems/#{type}_gems").filter_map do |l|
- next if l.start_with?("#")
- g, v = l.split(" ", 3)
- [g, v] unless last[g] == v
- end
- news.sub!(/^\*( +)The following #{type} gems? are updated\.\n\K(?: \1\* .*\n)*/) do
- mark = "#{$1} * "
- changed.map {|g, v|"#{mark}#{g} #{v}\n"}.join("")
- end or next
- File.write("NEWS.md", news)
- end
- shell: ruby {0}
-
- - name: Check diffs
- id: diff
- run: |
- git diff --color --no-ext-diff --ignore-submodules --exit-code NEWS.md
- continue-on-error: true
- - name: Commit
- run: |
- git pull --ff-only origin ${GITHUB_REF#refs/heads/}
- git commit --message="Update default gems list at ${GITHUB_SHA:0:30} [ci skip]" NEWS.md
- git push origin ${GITHUB_REF#refs/heads/}
- env:
- EMAIL: svn-admin@ruby-lang.org
- GIT_AUTHOR_NAME: git
- GIT_COMMITTER_NAME: git
- if: ${{ github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull') && steps.diff.outcome == 'failure' }}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 299c6b220a..8dba76fbe2 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -1,29 +1,42 @@
name: "Code scanning - action"
on:
- push:
- paths-ignore:
- - 'doc/**'
- - '**.md'
- - '**.rdoc'
- pull_request:
- paths-ignore:
- - 'doc/**'
- - '**.md'
- - '**.rdoc'
+ # push:
+ # paths-ignore:
+ # - 'doc/**'
+ # - '**/man'
+ # - '**.md'
+ # - '**.rdoc'
+ # - '**/.document'
+ # pull_request:
+ # paths-ignore:
+ # - 'doc/**'
+ # - '**/man'
+ # - '**.md'
+ # - '**.rdoc'
+ # - '**/.document'
schedule:
- - cron: '0 12 * * 4'
+ - cron: '0 12 * * *'
+ workflow_dispatch:
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions: # added using https://github.com/step-security/secure-workflows
+ contents: read
+
jobs:
CodeQL-Build:
# CodeQL runs on ubuntu-latest and windows-latest
+ permissions:
+ actions: read # for github/codeql-action/init to get workflow details
+ contents: read # for actions/checkout to fetch code
+ security-events: write # for github/codeql-action/autobuild to send a status report
runs-on: ubuntu-latest
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ # CodeQL fails to run pull requests from dependabot due to missing write access to upload results.
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') && github.event.head_commit.pusher.name != 'dependabot[bot]' }}
env:
enable_install_doc: no
@@ -36,9 +49,9 @@ jobs:
sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison autoconf ruby
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: .downloaded-cache
key: downloaded-cache
@@ -47,15 +60,16 @@ jobs:
run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.37
with:
config-file: ./.github/codeql/codeql-config.yml
+ trap-caching: false
- name: Set ENV
run: echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.37
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.37
diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml
index 4ec02e2b59..caf12cc0f4 100644
--- a/.github/workflows/compilers.yml
+++ b/.github/workflows/compilers.yml
@@ -4,13 +4,22 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
- - '**.md'
+ - '**/man'
- '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.rdoc'
+ - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -20,7 +29,7 @@ concurrency:
# environment variables (plus the "echo $GITHUB_ENV" hack) is to reroute that
# restriction.
env:
- default_cc: clang-14
+ default_cc: clang-15
append_cc: ''
# -O1 is faster than -O3 in our tests... Majority of time are consumed trying
@@ -54,6 +63,9 @@ env:
--color=always
--tty=no
+permissions:
+ contents: read
+
jobs:
compile:
strategy:
@@ -68,44 +80,29 @@ jobs:
- { name: gcc-9, env: { default_cc: gcc-9 } }
- { name: gcc-8, env: { default_cc: gcc-8 } }
- { name: gcc-7, env: { default_cc: gcc-7 } }
- - { name: gcc-6, env: { default_cc: gcc-6 } }
- - { name: gcc-5, env: { default_cc: gcc-5 } }
- - { name: gcc-4.8, env: { default_cc: gcc-4.8 } }
- - name: 'gcc-11 LTO'
- container: gcc-11
+ - name: 'gcc-13 LTO'
+ container: gcc-13
env:
- default_cc: 'gcc-11 -flto=auto -ffat-lto-objects'
+ default_cc: 'gcc-13 -flto=auto -ffat-lto-objects -Werror=lto-type-mismatch'
optflags: '-O2'
shared: disable
# check: true
- - name: 'gcc-11 annocheck'
- container: gcc-11
- env:
- # Minimal flags to pass the check.
- default_cc: 'gcc-11 -O2 -fcf-protection -Wa,--generate-missing-build-notes=yes'
- LDFLAGS: '-Wl,-z,now'
- # FIXME: Drop skipping options
- # https://bugs.ruby-lang.org/issues/18061
- # https://sourceware.org/annobin/annobin.html/Test-pie.html
- TEST_ANNOCHECK_OPTS: "--skip-pie"
- check: true
+ - { name: clang-16, env: { default_cc: clang-16 } }
- { name: clang-15, env: { default_cc: clang-15 } }
- { name: clang-14, env: { default_cc: clang-14 } }
- { name: clang-13, env: { default_cc: clang-13 } }
- { name: clang-12, env: { default_cc: clang-12 } }
- { name: clang-11, env: { default_cc: clang-11 } }
- { name: clang-10, env: { default_cc: clang-10 } }
- - { name: clang-9, env: { default_cc: clang-9 } }
- - { name: clang-8, env: { default_cc: clang-8 } }
- - { name: clang-7, env: { default_cc: clang-7 } }
- - { name: clang-6.0, env: { default_cc: clang-6.0 } }
- - { name: clang-5.0, env: { default_cc: clang-5.0 } }
- - { name: clang-4.0, env: { default_cc: clang-4.0 } }
- - { name: clang-3.9, env: { default_cc: clang-3.9 } }
- - name: 'clang-14 LTO'
- container: clang-14
+ # llvm-objcopy<=9 doesn't have --wildcard. It compiles, but leaves Rust symbols in libyjit.o.
+ - { name: clang-9, env: { default_cc: clang-9, append_configure: '--disable-yjit' } }
+ - { name: clang-8, env: { default_cc: clang-8, append_configure: '--disable-yjit' } }
+ - { name: clang-7, env: { default_cc: clang-7, append_configure: '--disable-yjit' } }
+ - { name: clang-6.0, env: { default_cc: clang-6.0, append_configure: '--disable-yjit' } }
+ - name: 'clang-16 LTO'
+ container: clang-16
env:
- default_cc: 'clang-14 -flto=auto'
+ default_cc: 'clang-16 -flto=auto'
optflags: '-O2'
shared: disable
# check: true
@@ -147,6 +144,7 @@ jobs:
- { name: disable-dln, env: { append_configure: '--disable-dln' } }
- { name: enable-mkmf-verbose, env: { append_configure: '--enable-mkmf-verbose' } }
- { name: disable-rubygems, env: { append_configure: '--disable-rubygems' } }
+ - { name: RUBY_DEVEL, env: { append_configure: '--enable-devel' } }
- { name: OPT_THREADED_CODE=1, env: { cppflags: '-DOPT_THREADED_CODE=1' } }
- { name: OPT_THREADED_CODE=2, env: { cppflags: '-DOPT_THREADED_CODE=2' } }
@@ -170,7 +168,11 @@ jobs:
# - { name: VM_CHECK_MODE, env: { cppflags: '-DVM_CHECK_MODE' } }
- { name: USE_EMBED_CI=0, env: { cppflags: '-DUSE_EMBED_CI=0' } }
- - { name: USE_FLONUM=0, env: { cppflags: '-DUSE_FLONUM=0' } }
+ - name: USE_FLONUM=0,
+ env:
+ cppflags: '-DUSE_FLONUM=0'
+ # yjit requires FLONUM for the pointer tagging scheme
+ append_configure: '--disable-yjit'
# - { name: USE_GC_MALLOC_OBJ_INFO_DETAILS, env: { cppflags: '-DUSE_GC_MALLOC_OBJ_INFO_DETAILS' } }
- { name: USE_LAZY_LOAD, env: { cppflags: '-DUSE_LAZY_LOAD' } }
# - { name: USE_RINCGC=0, env: { cppflags: '-DUSE_RINCGC=0' } }
@@ -211,7 +213,7 @@ jobs:
name: ${{ matrix.entry.name }}
runs-on: ubuntu-latest
container:
- image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || matrix.entry.env.default_cc || 'clang-14' }}
+ image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || matrix.entry.env.default_cc || 'clang-15' }}
options: --user root
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
env: ${{ matrix.entry.env || matrix.env }}
@@ -223,10 +225,10 @@ jobs:
- name: setenv
run: |
echo "GNUMAKEFLAGS=-sj$((1 + $(nproc --all)))" >> $GITHUB_ENV
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
@@ -249,18 +251,18 @@ jobs:
- run: make test
- run: make install
if: ${{ matrix.entry.check }}
- - run: make prepare-gems
- if: ${{ matrix.entry.check }}
- run: make test-tool
if: ${{ matrix.entry.check }}
- run: make test-all TESTS='-- ruby -ext-'
if: ${{ matrix.entry.check }}
- run: make test-spec
+ env:
+ CHECK_LEAKS: true
if: ${{ matrix.entry.check }}
- run: make test-annocheck
if: ${{ matrix.entry.check && endsWith(matrix.entry.name, 'annocheck') }}
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -268,7 +270,7 @@ jobs:
"env": "${{ github.workflow }} / ${{ matrix.entry.name }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index d683633314..d8dc58b119 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -3,27 +3,41 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
make:
strategy:
matrix:
test_task: ["check"] # "test-bundler-parallel", "test-bundled-gems"
os:
- - macos-10.15
- - macos-11
- - macos-12
+ - macos-13
+ - macos-14
+ - macos-15
fail-fast: false
env:
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
@@ -36,31 +50,31 @@ jobs:
run: |
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
- name: Install libraries
run: |
- brew upgrade
- brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline
+ brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline bison
working-directory: src
- name: Set ENV
run: |
echo "MAKEFLAGS=-j$((1 + $(sysctl -n hw.activecpu)))" >> $GITHUB_ENV
+ echo "PATH="/usr/local/opt/bison/bin:/opt/homebrew/opt/bison/bin:$PATH"" >> $GITHUB_ENV
- run: ./autogen.sh
working-directory: src
- name: Run configure
run: ../src/configure -C --disable-install-doc --with-openssl-dir=$(brew --prefix openssl@1.1) --with-readline-dir=$(brew --prefix readline)
- run: make incs
+ - run: make prepare-gems
+ if: ${{ matrix.test_task == 'test-bundled-gems' }}
- run: make
- run: make leaked-globals
if: ${{ matrix.test_task == 'check' }}
- - run: make prepare-gems
- if: ${{ matrix.test_task == 'check' }}
- name: make ${{ matrix.test_task }}
run: |
make -s ${{ matrix.test_task }} ${TESTS:+TESTS=`echo "$TESTS" | sed 's| |$$/ -n!/|g;s|^|-n!/|;s|$|$$/|'`}
@@ -80,7 +94,7 @@ jobs:
PRECHECK_BUNDLED_GEMS: "no"
if: ${{ matrix.test_task == 'check' && matrix.skipped_tests != '' }}
continue-on-error: ${{ matrix.continue-on-skipped_tests || false }}
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -88,7 +102,7 @@ jobs:
"env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml
index 80b7a92f15..0df917d3d8 100644
--- a/.github/workflows/mingw.yml
+++ b/.github/workflows/mingw.yml
@@ -3,18 +3,32 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
# Notes:
# Actions console encoding causes issues, see test-all & test-spec steps
#
@@ -26,19 +40,16 @@ jobs:
MSYSTEM: ${{ matrix.msystem }}
MSYS2_ARCH: x86_64
CHOST: "x86_64-w64-mingw32"
- CFLAGS: "-march=x86-64 -mtune=generic -O3 -pipe -fstack-protector-strong"
+ CFLAGS: "-march=x86-64 -mtune=generic -O3 -pipe"
CXXFLAGS: "-march=x86-64 -mtune=generic -O3 -pipe"
CPPFLAGS: "-D_FORTIFY_SOURCE=2 -D__USE_MINGW_ANSI_STDIO=1 -DFD_SETSIZE=2048"
- LDFLAGS: "-pipe -fstack-protector-strong"
+ LDFLAGS: "-pipe"
UPDATE_UNICODE: "UNICODE_FILES=. UNICODE_PROPERTY_FILES=. UNICODE_AUXILIARY_FILES=. UNICODE_EMOJI_FILES=."
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
strategy:
matrix:
include:
- - msystem: "MINGW64"
- base_ruby: 2.6
- test_task: "check"
- test-all-opts: "--name=!/TestObjSpace#test_reachable_objects_during_iteration/"
+ # To mitigate flakiness of MinGW CI, we test only one runtime that newer MSYS2 uses.
- msystem: "UCRT64"
base_ruby: head
test_task: "check"
@@ -54,21 +65,20 @@ jobs:
git config --global core.eol lf
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
- name: Set up Ruby & MSYS2
- uses: ruby/setup-ruby@v1
+ uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
with:
ruby-version: ${{ matrix.base_ruby }}
- name: set env
run: |
echo "GNUMAKEFLAGS=-j$((2 * NUMBER_OF_PROCESSORS))" >> $GITHUB_ENV
- echo "TEST_JOBS=$((15 * NUMBER_OF_PROCESSORS / 10))" >> $GITHUB_ENV
- name: where check
run: |
@@ -114,7 +124,7 @@ jobs:
make update-gems
- name: make all
- timeout-minutes: 20
+ timeout-minutes: 30
run: |
make
@@ -138,7 +148,7 @@ jobs:
make ${{ StartsWith(matrix.test_task, 'test/') && matrix.test_task || 'test-all' }}
env:
RUBY_TESTOPTS: >-
- -j${{env.TEST_JOBS}} --retry --job-status=normal --show-skip --timeout-scale=1.5
+ --retry --job-status=normal --show-skip --timeout-scale=1.5
${{ matrix.test-all-opts }}
BUNDLER_VERSION:
if: ${{matrix.test_task == 'check' || matrix.test_task == 'test-all' || StartsWith(matrix.test_task, 'test/')}}
@@ -149,7 +159,7 @@ jobs:
make ${{ StartsWith(matrix.test_task, 'spec/') && matrix.test_task || 'test-spec' }}
if: ${{matrix.test_task == 'check' || matrix.test_task == 'test-spec' || StartsWith(matrix.test_task, 'spec/')}}
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -157,7 +167,7 @@ jobs:
"env": "${{ github.workflow }} ${{ matrix.msystem }} / ${{ matrix.test_task }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/mjit-bindgen.yml b/.github/workflows/mjit-bindgen.yml
new file mode 100644
index 0000000000..26f8a1b2aa
--- /dev/null
+++ b/.github/workflows/mjit-bindgen.yml
@@ -0,0 +1,104 @@
+name: MJIT bindgen
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ make:
+ strategy:
+ matrix:
+ include:
+ - task: mjit-bindgen
+ fail-fast: false
+ runs-on: ubuntu-22.04
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ steps:
+ - run: mkdir build
+ working-directory:
+ - name: Set ENV
+ run: |
+ echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
+ - name: Install libraries
+ run: |
+ set -x
+ sudo apt-get update -q || :
+ sudo apt-get install --no-install-recommends -q -y \
+ build-essential \
+ libssl-dev libyaml-dev libreadline6-dev \
+ zlib1g-dev libncurses5-dev libffi-dev \
+ libclang1-14 \
+ bison autoconf
+ sudo apt-get install -q -y pkg-config || :
+ - name: Set up Ruby
+ uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
+ with:
+ ruby-version: '3.1'
+ - name: git config
+ run: |
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ path: src
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: src/.downloaded-cache
+ key: downloaded-cache
+ - name: Fixed world writable dirs
+ run: |
+ chmod -v go-w $HOME $HOME/.config
+ sudo chmod -R go-w /usr/share
+ sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || :
+ - run: ./autogen.sh
+ working-directory: src
+ - name: Run configure
+ run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install --enable-yjit=dev_nodebug
+ - run: make incs
+ - run: make
+ - run: make install
+ - run: make ${{ matrix.task }}
+ - run: git diff --exit-code
+ working-directory: src
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
+defaults:
+ run:
+ working-directory: build
diff --git a/.github/workflows/mjit.yml b/.github/workflows/mjit.yml
index c2479f9467..6f7181489a 100644
--- a/.github/workflows/mjit.yml
+++ b/.github/workflows/mjit.yml
@@ -3,30 +3,47 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
+ - '**.[1-8]'
+ - '**.ronn'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ - '**.[1-8]'
+ - '**.ronn'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
make:
strategy:
matrix:
- test_task: [ "check" ] # to make job names consistent
- jit_opts: [ "--mjit", "--mjit-wait" ]
+ test_task: [check] # to make job names consistent
+ mjit_opts: [--mjit-wait]
fail-fast: false
runs-on: ubuntu-latest
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
env:
TESTOPTS: '-q --tty=no'
- RUN_OPTS: '--disable-gems ${{ matrix.jit_opts }} --mjit-debug=-ggdb3'
+ RUN_OPTS: '--disable-gems ${{ matrix.mjit_opts }} --mjit-debug=-ggdb3'
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
steps:
- run: mkdir build
@@ -40,10 +57,10 @@ jobs:
run: |
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
@@ -62,31 +79,30 @@ jobs:
- run: make incs
- run: make
- run: sudo make -s install
- - run: sudo apt-get install gdb # used by test / test-all failure
- name: Run test
run: |
- ulimit -c unlimited
+ unset GNUMAKEFLAGS
make -s test RUN_OPTS="$RUN_OPTS"
timeout-minutes: 60
- - name: Run test-all
- run: |
- ulimit -c unlimited
- make -s test-all RUN_OPTS="$RUN_OPTS"
- timeout-minutes: 60
+ # - name: Run test-all
+ # run: |
+ # ulimit -c unlimited
+ # make -s test-all RUN_OPTS="$RUN_OPTS"
+ # timeout-minutes: 60
- name: Run test-spec
run: |
- ulimit -c unlimited
+ unset GNUMAKEFLAGS
make -s test-spec RUN_OPTS="$RUN_OPTS"
timeout-minutes: 60
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
"ci": "GitHub Actions",
- "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.jit_opts }}",
+ "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.mjit_opts }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000000..5d4474d978
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,18 @@
+name: Start release workflow
+on:
+ push:
+ tags:
+ - '*'
+
+jobs:
+ notify:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Build release package
+ run: |
+ curl -L -X POST \
+ -H "Authorization: Bearer ${{ secrets.MATZBOT_GITHUB_WORKFLOW_TOKEN }}" \
+ -H "Accept: application/vnd.github+json" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ https://api.github.com/repos/ruby/actions/dispatches \
+ -d '{"event_type": "${{ github.ref }}"}'
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
new file mode 100644
index 0000000000..c12a95362d
--- /dev/null
+++ b/.github/workflows/scorecards.yml
@@ -0,0 +1,72 @@
+# This workflow uses actions that are not certified by GitHub. They are provided
+# by a third-party and are governed by separate terms of service, privacy
+# policy, and support documentation.
+
+name: Scorecards supply-chain security
+on:
+ # For Branch-Protection check. Only the default branch is supported. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
+ branch_protection_rule:
+ # To guarantee Maintained check is occasionally updated. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
+ schedule:
+ - cron: '22 4 * * 2'
+ push:
+ branches: [ "master" ]
+
+# Declare default permissions as read only.
+permissions: read-all
+
+jobs:
+ analysis:
+ name: Scorecards analysis
+ runs-on: ubuntu-latest
+ permissions:
+ # Needed to upload the results to code-scanning dashboard.
+ security-events: write
+ # Needed to publish results and get a badge (see publish_results below).
+ id-token: write
+ # Uncomment the permissions below if installing in a private repository.
+ # contents: read
+ # actions: read
+
+ steps:
+ - name: "Checkout code"
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ persist-credentials: false
+
+ - name: "Run analysis"
+ uses: ossf/scorecard-action@ea651e62978af7915d09fe2e282747c798bf2dab # v2.4.1
+ with:
+ results_file: results.sarif
+ results_format: sarif
+ # (Optional) Read-only PAT token. Uncomment the `repo_token` line below if:
+ # - you want to enable the Branch-Protection check on a *public* repository, or
+ # - you are installing Scorecards on a *private* repository
+ # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
+ repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
+
+ # Public repositories:
+ # - Publish results to OpenSSF REST API for easy access by consumers
+ # - Allows the repository to include the Scorecard badge.
+ # - See https://github.com/ossf/scorecard-action#publishing-results.
+ # For private repositories:
+ # - `publish_results` will always be set to `false`, regardless
+ # of the value entered here.
+ publish_results: true
+
+ # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
+ # format to the repository Actions tab.
+ - name: "Upload artifact"
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ with:
+ name: SARIF file
+ path: results.sarif
+ retention-days: 5
+
+ # Upload the results to GitHub's code scanning dashboard.
+ - name: "Upload to code-scanning"
+ uses: github/codeql-action/upload-sarif@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.27
+ with:
+ sarif_file: results.sarif
diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml
index 480731ad93..4521195a2b 100644
--- a/.github/workflows/spec_guards.yml
+++ b/.github/workflows/spec_guards.yml
@@ -2,43 +2,61 @@ name: Rubyspec Version Guards Check
on:
push:
- paths-ignore:
- - 'doc/**'
- - '**.md'
- - '**.rdoc'
+ paths:
+ - 'spec/**'
+ - '!spec/*.md'
pull_request:
- paths-ignore:
- - 'doc/**'
- - '**.md'
- - '**.rdoc'
+ paths:
+ - 'spec/**'
+ - '!spec/*.md'
+ merge_group:
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
rubyspec:
name: Rubyspec
- runs-on: ubuntu-20.04
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+
+ runs-on: ubuntu-22.04
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
strategy:
matrix:
# Specs from ruby/spec should still run on all supported Ruby versions.
# This also ensures the needed ruby_version_is guards are there, see spec/README.md.
ruby:
- - ruby-2.7
- ruby-3.1
+ - ruby-3.2
steps:
- - uses: actions/checkout@v3
- - uses: ruby/setup-ruby@v1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+
+ - uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
with:
ruby-version: ${{ matrix.ruby }}
bundler: none
+
- run: gem install webrick
+
- run: ruby ../mspec/bin/mspec
working-directory: spec/ruby
- - uses: k0kubun/action-slack@v2.0.0
+ env:
+ CHECK_LEAKS: true
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -46,8 +64,8 @@ jobs:
"env": "${{ github.workflow }} / rubyspec @ ${{ matrix.ruby }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
index 21bc285d7d..4fbca1170e 100644
--- a/.github/workflows/ubuntu.yml
+++ b/.github/workflows/ubuntu.yml
@@ -3,49 +3,62 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
make:
strategy:
matrix:
- test_task: ["check", "test-bundler-parallel", "test-bundled-gems"]
- os:
- - ubuntu-20.04
-# - ubuntu-18.04
- configure: ["", "cppflags=-DRUBY_DEBUG"]
+ # main variables included in the job name
+ test_task: [check]
+ configure: [cppflags=-DRUBY_DEBUG] # default to use more assertions
+ arch: ['']
+ # specify all jobs with `include` to avoid testing duplicated things
include:
- - test_task: "check"
- configure: ""
+ - test_task: check
+ - test_task: check
arch: i686
- - test_task: "check"
+ configure: '' # test without -DRUBY_DEBUG as well
+ - test_task: check
configure: "--enable-shared --enable-load-relative"
- skipped_tests: "TestGem#test_.*_from_binstubs.*"
- continue-on-skipped_tests: true
- - test_task: "test-all TESTS=--repeat-count=2"
+ - test_task: test-all TESTS=--repeat-count=2
+ - test_task: test-bundler-parallel
+ - test_task: test-bundled-gems
fail-fast: false
env:
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
RUBY_DEBUG: ci
SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }}
- runs-on: ${{ matrix.os || 'ubuntu-20.04' }}
+ runs-on: ubuntu-22.04
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
steps:
- run: mkdir build
working-directory:
- name: Set ENV
- env:
- configure: ${{matrix.configure}}
run: |
echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
- name: Install libraries
@@ -66,10 +79,10 @@ jobs:
run: |
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
@@ -87,11 +100,11 @@ jobs:
$SETARCH ../src/configure -C --disable-install-doc ${{ matrix.configure }}
${arch:+--target=$arch-$OSTYPE --host=$arch-$OSTYPE}
- run: $SETARCH make incs
+ - run: $SETARCH make prepare-gems
+ if: ${{ matrix.test_task == 'test-bundled-gems' }}
- run: $SETARCH make
- run: $SETARCH make leaked-globals
if: ${{ matrix.test_task == 'check' }}
- - run: $SETARCH make prepare-gems
- if: ${{ matrix.test_task == 'check' }}
- name: Create dummy files in build dir
run: |
$SETARCH ./miniruby -e '(("a".."z").to_a+("A".."Z").to_a+("0".."9").to_a+%w[foo bar test zzz]).each{|basename|File.write("#{basename}.rb", "raise %(do not load #{basename}.rb)")}'
@@ -114,15 +127,15 @@ jobs:
TESTS: ${{ matrix.skipped_tests }}
if: ${{ matrix.test_task == 'check' && matrix.skipped_tests != '' }}
continue-on-error: ${{ matrix.continue-on-skipped_tests || false }}
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
"ci": "GitHub Actions",
- "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}",
+ "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.configure }}${{ matrix.arch }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
index 83688fbaca..27920b5821 100644
--- a/.github/workflows/wasm.yml
+++ b/.github/workflows/wasm.yml
@@ -3,18 +3,32 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions: # added using https://github.com/step-security/secure-workflows
+ contents: read
+
jobs:
make:
strategy:
@@ -36,7 +50,7 @@ jobs:
WASI_SDK_VERSION_MINOR: 0
BINARYEN_VERSION: 109
WASMTIME_VERSION: v0.33.0
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
steps:
- run: mkdir build
@@ -45,7 +59,7 @@ jobs:
run: |
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- name: Install libraries
@@ -77,6 +91,18 @@ jobs:
echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV
- run: ./autogen.sh
working-directory: src
+
+ - uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
+ with:
+ ruby-version: '3.0'
+ bundler: none
+
+ - name: Download config.guess with wasi version
+ run: |
+ rm tool/config.guess tool/config.sub
+ ruby tool/downloader.rb -d tool -e gnu config.guess config.sub
+ working-directory: src
+
- name: Run configure
run: |
../src/configure \
@@ -90,7 +116,8 @@ jobs:
debugflags="${{ matrix.entry.debugflags }}" \
wasmoptflags="${{ matrix.entry.wasmoptflags }} ${{ matrix.entry.debugflags }}"
- - run: make ruby
+ # miniruby may not be built when cross-compling
+ - run: make mini ruby
- name: Run basictest
run: wasmtime run ./../build/miniruby --mapdir /::./ -- basictest/test.rb
working-directory: src
@@ -100,6 +127,20 @@ jobs:
ruby ./bootstraptest/runner.rb --ruby="$(which wasmtime) run $PWD/../build/ruby --mapdir /::./ -- " --verbose "--sets=$NO_THREAD_TESTS"
working-directory: src
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} / ${{ matrix.name }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
defaults:
run:
working-directory: build
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 2c5b823d20..c2bd4881c2 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -3,108 +3,107 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
make:
strategy:
matrix:
include:
- - vs: 2019
- vs: 2022
+ vcvers: -vcvars_ver=14.2
fail-fast: false
- runs-on: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }}
+ runs-on: windows-${{ matrix.vs }}
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
name: VisualStudio ${{ matrix.vs }}
env:
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
PATCH: C:\msys64\usr\bin\patch.exe
- OS_VER: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }}
+ OS_VER: windows-${{ matrix.vs }}
steps:
- run: md build
working-directory:
- - uses: msys2/setup-msys2@v2
+ - uses: msys2/setup-msys2@61f9e5e925871ba6c9e3e8da24ede83ea27fa91f # v2.27.0
id: setup-msys2
with:
update: true
- install: >-
- patch
- if: ${{ env.OS_VER != 'windows-2019' }}
+ install: bison patch
- name: patch path
shell: msys2 {0}
run: echo PATCH=$(cygpath -wa $(command -v patch)) >> $GITHUB_ENV
if: ${{ steps.setup-msys2.outcome == 'success' }}
- - uses: actions/cache@v3
- with:
- path: C:\vcpkg\downloads
- key: ${{ runner.os }}-vcpkg-download-${{ env.OS_VER }}-${{ github.sha }}
- restore-keys: |
- ${{ runner.os }}-vcpkg-download-${{ env.OS_VER }}-
- ${{ runner.os }}-vcpkg-download-
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: C:\vcpkg\installed
- key: ${{ runner.os }}-vcpkg-installed-${{ matrix.os }}-${{ github.sha }}
+ key: ${{ runner.os }}-vcpkg-installed-windows-${{ matrix.vs }}-${{ github.sha }}
restore-keys: |
- ${{ runner.os }}-vcpkg-installed-${{ matrix.os }}-
- ${{ runner.os }}-vcpkg-installed-
+ ${{ runner.os }}-vcpkg-installed-windows-${{ matrix.vs }}-
+ ${{ runner.os }}-vcpkg-installed-windows-
- name: Install libraries with vcpkg
run: |
+ iex "& {$(irm get.scoop.sh)} -RunAsAdmin"
+ Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH
+ scoop install cmake@3.31.6
vcpkg --triplet x64-windows install libffi libyaml openssl readline zlib
- - uses: actions/cache@v3
- with:
- path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey
- key: ${{ runner.os }}-chocolatey-${{ env.OS_VER }}-${{ github.sha }}
- restore-keys: |
- ${{ runner.os }}-chocolatey-${{ env.OS_VER }}-
- ${{ runner.os }}-chocolatey-
- - name: Install libraries with chocolatey
- run: |
- # Using Choco-Install for retries, but it doesn't detect failures properly
- # if you pass multiple package names in a single command.
- Choco-Install -PackageName winflexbison3
- shell: pwsh
+ shell:
+ pwsh
- name: git config
run: |
git config --global core.autocrlf false
git config --global core.eol lf
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
- name: setup env
# %TEMP% is inconsistent with %TMP% and test-all expects they are consistent.
# https://github.com/actions/virtual-environments/issues/712#issuecomment-613004302
+ # msys2/setup-msys2 installs MSYS2 to D:/a/_temp/msys64/usr/bin
run: |
- set VS=${{ matrix.vs }}
- set VCVARS=${{ matrix.vcvars }}
+ set Path=D:/a/_temp/msys64/usr/bin;%Path%
if not "%VCVARS%" == "" goto :vcset
- set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
- if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\${{ matrix.vs }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\${{ matrix.vs }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
:vcset
set | C:\msys64\usr\bin\sort > old.env
- call %VCVARS%
+ call %VCVARS% ${{ matrix.vcvers || ''}}
set TMP=%USERPROFILE%\AppData\Local\Temp
set TEMP=%USERPROFILE%\AppData\Local\Temp
set /a TEST_JOBS=(15 * %NUMBER_OF_PROCESSORS% / 10) > nul
set | C:\msys64\usr\bin\sort > new.env
C:\msys64\usr\bin\comm -13 old.env new.env >> %GITHUB_ENV%
del *.env
+ - name: compiler version
+ run: cl
- name: link libraries
run: |
for %%I in (C:\vcpkg\installed\x64-windows\bin\*.dll) do (
@@ -121,18 +120,16 @@ jobs:
- run: nmake extract-extlibs
- run: nmake
env:
- YACC: win_bison
+ YACC: bison.exe
- run: nmake test
timeout-minutes: 5
+ - run: nmake test-spec
+ timeout-minutes: 10
- run: nmake test-all
env:
RUBY_TESTOPTS: -j${{env.TEST_JOBS}} --job-status=normal
timeout-minutes: 60
- continue-on-error: ${{ matrix.continue-on-error || false }}
- - run: nmake test-spec
- timeout-minutes: 10
- continue-on-error: ${{ matrix.continue-on-error || false }}
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -140,7 +137,7 @@ jobs:
"env": "VS${{ matrix.vs }} / ${{ matrix.test_task || 'check' }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml
index 5a86acb62c..0b7b9046e9 100644
--- a/.github/workflows/yjit-ubuntu.yml
+++ b/.github/workflows/yjit-ubuntu.yml
@@ -3,25 +3,39 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
cargo:
name: Rust cargo test
# GitHub Action's image seems to already contain a Rust 1.58.0.
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# For now we can't run cargo test --offline because it complains about the
# capstone dependency, even though the dependency is optional
#- run: cargo test --offline
@@ -38,30 +52,40 @@ jobs:
fail-fast: false
matrix:
include:
- - test_task: "check-yjit-bindings"
- configure: "--with-gcc=clang-12 --enable-yjit=dev"
+ - test_task: 'yjit-bindgen'
+ hint: 'To fix: use patch in logs'
+ configure: '--with-gcc=clang-14 --enable-yjit=dev'
+ libclang_path: '/usr/lib/llvm-14/lib/libclang.so.1'
- test_task: "check"
- configure: "--enable-yjit RUSTC='rustc +1.58.1'" # release build
- rust_version: "1.58.1"
+ # YJIT should be automatically built in release mode on x86-64 Linux with rustc present
+ #configure: "--enable-yjit RUSTC='rustc +1.58.0'"
+ configure: "RUSTC='rustc +1.58.0'"
+ rust_version: "1.58.0"
- test_task: "check"
configure: "--enable-yjit=dev"
- test_task: "check"
configure: "--enable-yjit=dev"
- yjit_opts: "--yjit-call-threshold=1"
+ yjit_opts: "--yjit-call-threshold=1 --yjit-verify-ctx"
- test_task: "test-all TESTS=--repeat-count=2"
configure: "--enable-yjit=dev"
- test_task: "test-bundled-gems"
configure: "--enable-yjit=dev"
+
+ - test_task: "yjit-bench"
+ configure: "--enable-yjit=dev"
+ yjit_bench_opts: "--yjit-stats"
env:
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
RUN_OPTS: ${{ matrix.yjit_opts }}
+ YJIT_BENCH_OPTS: ${{ matrix.yjit_bench_opts }}
RUBY_DEBUG: ci
- runs-on: ubuntu-20.04
+ BUNDLE_JOBS: 8 # for yjit-bench
+ runs-on: ubuntu-22.04
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
steps:
- run: mkdir build
@@ -78,10 +102,10 @@ jobs:
run: |
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
@@ -96,34 +120,46 @@ jobs:
- run: ./autogen.sh
working-directory: src
- name: Run configure
- run: ../src/configure -C --disable-install-doc ${{ matrix.configure }}
+ run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install ${{ matrix.configure }}
- run: make incs
+ - run: make prepare-gems
+ if: ${{ matrix.test_task == 'test-bundled-gems' }}
- run: make -j
- run: make leaked-globals
if: ${{ matrix.test_task == 'check' }}
- - run: make prepare-gems
- if: ${{ matrix.test_task == 'check' }}
- name: Create dummy files in build dir
run: |
./miniruby -e '(("a".."z").to_a+("A".."Z").to_a+("0".."9").to_a+%w[foo bar test zzz]).each{|basename|File.write("#{basename}.rb", "raise %(do not load #{basename}.rb)")}'
if: ${{ matrix.test_task == 'check' }}
- name: Enable YJIT through ENV
run: echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV
- - run: make -s ${{ matrix.test_task }} RUN_OPTS="$RUN_OPTS"
+ # Check that the binary was built with YJIT
+ - name: Check YJIT enabled
+ run: ./miniruby --yjit -v | grep "+YJIT"
+ - name: make ${{ matrix.test_task }}
+ run: make -s -j ${{ matrix.test_task }} RUN_OPTS="$RUN_OPTS" YJIT_BENCH_OPTS="$YJIT_BENCH_OPTS"
timeout-minutes: 60
env:
RUBY_TESTOPTS: "-q --tty=no"
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ""
PRECHECK_BUNDLED_GEMS: "no"
- - uses: k0kubun/action-slack@v2.0.0
+ LIBCLANG_PATH: ${{ matrix.libclang_path }}
+ continue-on-error: ${{ matrix.test_task == 'yjit-bench' }}
+ - name: Show ${{ github.event.pull_request.base.ref }} GitHub URL for yjit-bench comparison
+ run: echo "https://github.com/${BASE_REPO}/commit/${BASE_SHA}"
+ env:
+ BASE_REPO: ${{ github.event.pull_request.base.repo.full_name }}
+ BASE_SHA: ${{ github.event.pull_request.base.sha }}
+ if: ${{ matrix.test_task == 'yjit-bench' && startsWith(github.event_name, 'pull') }}
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
"ci": "GitHub Actions",
- "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}",
+ "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.configure }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.gitignore b/.gitignore
index 521f4ec807..99d32a1825 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@
*.inc
*.log
*.o
+*.o.tmp
*.obj
*.old
*.orig
@@ -25,6 +26,7 @@
*.sav
*.sl
*.so
+*.so.*
*.swp
*.yarb
*~
@@ -145,6 +147,8 @@ lcov*.info
/bin/*.exe
/bin/*.dll
+/bin/goruby
+/bin/ruby
# /benchmark/
/benchmark/bm_require.data
@@ -219,6 +223,9 @@ lcov*.info
/lib/ruby/[1-9]*.*
/lib/ruby/vendor_ruby
+# /misc/
+/misc/**/__pycache__
+
# /spec/bundler
/.rspec_status
@@ -230,9 +237,14 @@ lcov*.info
/win32/*.ico
# MJIT
-/rb_mjit_header.h
-/mjit_config.h
/include/ruby-*/*/rb_mjit_min_header-*.h
+/lib/ruby_vm/mjit/instruction.rb
+/mjit_config.h
+/rb_mjit_header.h
+
+# YJIT
+/yjit-bench
+/yjit_exit_locations.dump
# /wasm/
/wasm/tests/*.wasm
diff --git a/.indent.pro b/.indent.pro
new file mode 100644
index 0000000000..1d61cbcad1
--- /dev/null
+++ b/.indent.pro
@@ -0,0 +1,32 @@
+-bap
+-nbbb
+-nbc
+-br
+-brs
+-nbs
+-ncdb
+-nce
+-cdw
+-cli2
+-cbi2
+-ndj
+-ncs
+-nfc1
+-i4
+-l120
+-lp
+-npcs
+-psl
+-sc
+-sob
+-sbi4
+-nut
+-par
+
+-TID
+-TVALUE
+-Tst_data_t
+-Tst_index_t
+-Tst_table
+-Trb_data_type_t
+-TFILE
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 6875c766a9..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,234 +0,0 @@
-# -*- YAML -*-
-# Copyright (C) 2011 Urabe, Shyouhei. All rights reserved.
-#
-# This file is a part of the programming language Ruby. Permission is hereby
-# granted, to either redistribute or modify this file, provided that the
-# conditions mentioned in the file COPYING are met. Consult the file for
-# details.
-
-# We only manage non-amd64 free pipelines.
-# https://docs.travis-ci.com/user/billing-overview/
-
-language: c
-
-os: linux
-
-if: commit_message !~ /\[DOC\]/
-
-dist: focal
-
-git:
- quiet: true
-
-cache:
- ccache: true
- directories:
- - $HOME/config_2nd
- - $HOME/.downloaded-cache
-
-env:
- global:
- # The tests skipped in `make test-all`.
- - TEST_ALL_SKIPPED_TESTS=
- # The tests executed separately by `make test-all`.
- - TEST_ALL_SEPARATED_TESTS=
- # Reset timestamps early
- - _=$(touch NEWS && find . -type f -exec touch -r NEWS {} +)
- - CONFIGURE_TTY=no
- - CCACHE_COMPILERCHECK=none
- - CCACHE_NOCOMPRESS=1
- - CCACHE_MAXSIZE=512Mi
- - NPROC="`nproc`"
- # JOBS and SETARCH are overridden when necessary; see below.
- - JOBS=-j$((1+${NPROC}))
- - SETARCH=
- - RUBY_PREFIX=/tmp/ruby-prefix
- - GEMS_FOR_TEST='timezone tzinfo'
- # https://github.com/travis-ci/travis-build/blob/e411371dda21430a60f61b8f3f57943d2fe4d344/lib/travis/build/bash/travis_apt_get_options.bash#L7
- - travis_apt_get_options='--allow-downgrades --allow-remove-essential --allow-change-held-packages'
- - travis_apt_get_options="-yq --no-install-suggests --no-install-recommends $travis_apt_get_options"
- # -O1 is faster than -O3 in our tests.
- - optflags=-O1
- # -g0 disables backtraces when SEGV. Do not set that.
- - debugflags=-ggdb3
-
-.org.ruby-lang.ci.matrix-definitions:
-
- - &gcc-10
- compiler: gcc-10
- before_install:
- - tool/travis_retry.sh sudo bash -c "rm -rf '${TRAVIS_ROOT}/var/lib/apt/lists/'* && exec apt-get update -yq"
- - >-
- tool/travis_retry.sh sudo -E apt-get $travis_apt_get_options install
- ccache
- gcc-10
- g++-10
- libffi-dev
- libncurses-dev
- libncursesw5-dev
- libreadline-dev
- libssl-dev
- libyaml-dev
- openssl
- zlib1g-dev
-
- # --------
-
- - &arm64-linux
- name: arm64-linux
- arch: arm64
- <<: *gcc-10
-
- - &ppc64le-linux
- name: ppc64le-linux
- arch: ppc64le
- <<: *gcc-10
-
- - &s390x-linux
- name: s390x-linux
- arch: s390x
- <<: *gcc-10
-
- - &arm32-linux
- name: arm32-linux
- arch: arm64
- # https://packages.ubuntu.com/focal/crossbuild-essential-armhf
- compiler: arm-linux-gnueabihf-gcc
- env:
- - SETARCH='setarch linux32 --verbose --32bit'
- # The "TestReadline#test_interrupt_in_other_thread" started failing on arm32
- # from https://www.travis-ci.com/github/ruby/ruby/jobs/529005145
- - TEST_ALL_SKIPPED_TESTS=test_interrupt_in_other_thread
- before_install:
- - sudo dpkg --add-architecture armhf
- - tool/travis_retry.sh sudo bash -c "rm -rf '${TRAVIS_ROOT}/var/lib/apt/lists/'* && exec apt-get update -yq"
- - >-
- tool/travis_retry.sh sudo -E apt-get $travis_apt_get_options install
- ccache
- crossbuild-essential-armhf
- libc6:armhf
- libstdc++-10-dev:armhf
- libffi-dev:armhf
- libncurses-dev:armhf
- libncursesw5-dev:armhf
- libreadline-dev:armhf
- libssl-dev:armhf
- linux-libc-dev:armhf
- zlib1g-dev:armhf
-
-matrix:
- include:
- # Build every commit (Allowed Failures):
- - <<: *arm32-linux
- # Comment out as the 2nd arm64 pipeline is unstable.
- # - <<: *arm64-linux
- - <<: *ppc64le-linux
- - <<: *s390x-linux
- allow_failures:
- # We see multiple errors indicating errors on the Travis environment itself in a short while:
- # https://app.travis-ci.com/github/ruby/ruby/jobs/544382885
- # https://app.travis-ci.com/github/ruby/ruby/jobs/544361370
- # It's not a fault of Ruby's arm32 support but just Travis arm32 seems unsable.
- - name: arm32-linux
- # - name: arm64-linux
- # We see "Some worker was crashed." in about 40% of recent ppc64le-linux jobs
- # e.g. https://app.travis-ci.com/github/ruby/ruby/jobs/530959548
- - name: ppc64le-linux
- # Tentatively disable, because often hungs up **after** all tests
- # have finished successfully and saving caches.
- - name: s390x-linux
- fast_finish: true
-
-before_script:
- - . tool/ci_functions.sh
- - |-
- if [ -n "${TEST_ALL_SKIPPED_TESTS}" ]; then
- TEST_ALL_OPTS="${TEST_ALL_OPTS} $(ci_to_excluded_test_opts "${TEST_ALL_SKIPPED_TESTS}")"
- if [ -z "${TEST_ALL_SEPARATED_TESTS}" ]; then
- TEST_ALL_SEPARATED_TESTS="${TEST_ALL_SKIPPED_TESTS}"
- fi
- fi
- - |-
- if [ -n "${TEST_ALL_SEPARATED_TESTS}" ]; then
- TEST_ALL_OPTS_SEPARATED="$(ci_to_included_test_opts "${TEST_ALL_SEPARATED_TESTS}")"
- fi
- - echo TEST_ALL_OPTS="${TEST_ALL_OPTS}" TEST_ALL_OPTS_SEPARATED="${TEST_ALL_OPTS_SEPARATED}"
- - rm -fr .ext autom4te.cache
- - |-
- [ -d ~/.downloaded-cache ] ||
- mkdir ~/.downloaded-cache
- - ln -s ~/.downloaded-cache
- - "> config.status"
- - "> .rbconfig.time"
- - sed -f tool/prereq.status template/Makefile.in common.mk > Makefile
- - make -s $JOBS up
- - make -s $JOBS srcs
- - rm -f config.status Makefile rbconfig.rb .rbconfig.time
- - |-
- if [ -d ~/config_2nd ]; then
- cp -pr ~/config_2nd build
- else
- mkdir build
- fi
- - mkdir config_1st config_2nd
- - chmod -R a-w .
- - chmod -R u+w build config_1st config_2nd
- - cd build
- - |-
- case "$CC" in
- gcc*) CC="ccache $CC${GCC_FLAGS:+ }$GCC_FLAGS -fno-diagnostics-color";;
- clang*) CC="ccache $CC${GCC_FLAGS:+ }$GCC_FLAGS -fno-color-diagnostics";;
- esac
- - |-
- [ ! -f config.cache ] ||
- [ "$CC" = "`sed -n s/^ac_cv_prog_CC=//p config.cache`" ] ||
- (set -x; exec rm config.cache)
- - $SETARCH ../configure -C --disable-install-doc --prefix=$RUBY_PREFIX $CONFIG_FLAG
- - cp -pr config.cache config.status .ext/include ../config_1st
- - $SETARCH make reconfig
- - cp -pr config.cache config.status .ext/include ../config_2nd
- - (cd .. && exec diff -ru config_1st config_2nd)
- - chmod u+w ..
- - rm -rf ~/config_2nd
- - mv ../config_2nd ~
- - chmod u-w ..
- - $SETARCH make -s $JOBS
- - make -s install
- - |-
- [ -z "${GEMS_FOR_TEST}" ] ||
- $RUBY_PREFIX/bin/gem install --no-document $GEMS_FOR_TEST
- - echo "raise 'do not load ~/.irbrc in test'" > ~/.irbrc
-
-script:
- - $SETARCH make -s test -o showflags TESTOPTS="${TESTOPTS=$JOBS -q --tty=no}"
- - ../tool/travis_wait.sh $SETARCH make -s test-all -o exts TESTOPTS="$JOBS -q --tty=no ${TEST_ALL_OPTS}" RUBYOPT="-w"
- # Run the failing tests separately returning ok status to check if it works,
- # visualize them.
- - |
- if [ -n "${TEST_ALL_OPTS_SEPARATED}" ]; then
- $SETARCH make -s test-all -o exts TESTOPTS="$JOBS -v --tty=no ${TEST_ALL_OPTS_SEPARATED}" RUBYOPT="-w" || :
- fi
- - $SETARCH make -s test-spec MSPECOPT=-ff # not using `-j` because sometimes `mspec -j` silently dies
- - $SETARCH make -s -o showflags leaked-globals
-
-# We enable Travis on the specific branches or forked repositories here.
-if: (repo = ruby/ruby AND (branch = master OR branch =~ /^ruby_\d_\d$/)) OR repo != ruby/ruby
-
-# We want to be notified when something happens.
-notifications:
- irc:
- channels:
- - "chat.freenode.net#ruby-core"
- on_success: change # [always|never|change] # default: always
- on_failure: always # [always|never|change] # default: always
- template:
- - "%{message} by @%{author}: See %{build_url}"
-
- webhooks:
- urls:
- - secure: mRsoS/UbqDkKkW5p3AEqM27d4SZnV6Gsylo3bm8T/deltQzTsGzZwrm7OIBXZv0UFZdE68XmPlyHfZFLSP2V9QZ7apXMf9/vw0GtcSe1gchtnjpAPF6lYBn7nMCbVPPx9cS0dwL927fjdRM1vj7IKZ2bk4F0lAJ25R25S6teqdk= # ruby-lang slack: ruby/simpler-alerts-bot (travis)
- on_success: never
- on_failure: always
-
- email:
- - jaruga@ruby-lang.org
diff --git a/LEGAL b/LEGAL
index 9645728efe..0423d57ac9 100644
--- a/LEGAL
+++ b/LEGAL
@@ -979,7 +979,6 @@ mentioned below.
{MIT License}[rdoc-label:label-MIT+License]
[lib/rubygems/resolver/molinillo]
-[lib/bundler/vendor/molinillo]
molinillo is under the following license.
@@ -988,6 +987,15 @@ mentioned below.
{MIT License}[rdoc-label:label-MIT+License]
+[lib/bundler/vendor/pub_grub]
+
+ pub_grub is under the following license.
+
+ >>>
+ Copyright (c) 2018 John Hawthorn
+
+ {MIT License}[rdoc-label:label-MIT+License]
+
[lib/bundler/vendor/connection_pool]
connection_pool is under the following license.
diff --git a/NEWS.md b/NEWS.md
index fdb58e4bb7..f6c3c6fc97 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -23,13 +23,13 @@ Note that each entry is kept to a minimum, see links for details.
* A proc that accepts a single positional argument and keywords will
no longer autosplat. [[Bug #18633]]
- ```ruby
- proc{|a, **k| a}.call([1, 2])
- # Ruby 3.1 and before
- # => 1
- # Ruby 3.2 and after
- # => [1, 2]
- ```
+ ```ruby
+ proc{|a, **k| a}.call([1, 2])
+ # Ruby 3.1 and before
+ # => 1
+ # Ruby 3.2 and after
+ # => [1, 2]
+ ```
* Constant assignment evaluation order for constants set on explicit
objects has been made consistent with single attribute assignment
@@ -39,24 +39,24 @@ Note that each entry is kept to a minimum, see links for details.
foo::BAR = baz
```
- `foo` is now called before `baz`. Similarly, for multiple assignments
- to constants, left-to-right evaluation order is used. With this
- code:
+ `foo` is now called before `baz`. Similarly, for multiple assignments
+ to constants, left-to-right evaluation order is used. With this
+ code:
```ruby
- foo1::BAR1, foo2::BAR2 = baz1, baz2
+ foo1::BAR1, foo2::BAR2 = baz1, baz2
```
- The following evaluation order is now used:
+ The following evaluation order is now used:
- 1. `foo1`
- 2. `foo2`
- 3. `baz1`
- 4. `baz2`
+ 1. `foo1`
+ 2. `foo2`
+ 3. `baz1`
+ 4. `baz2`
- [[Bug #15928]]
+ [[Bug #15928]]
-* Find pattern is no longer experimental.
+* "Find pattern" is no longer experimental.
[[Feature #18585]]
* Methods taking a rest parameter (like `*args`) and wishing to delegate keyword
@@ -90,97 +90,487 @@ Note that each entry is kept to a minimum, see links for details.
foo(k: 1)
```
-## Command line options
-
## Core classes updates
Note: We're only listing outstanding class updates.
+* Fiber
+
+ * Introduce Fiber.[] and Fiber.[]= for inheritable fiber storage.
+ Introduce Fiber#storage and Fiber#storage= (experimental) for
+ getting and resetting the current storage. Introduce
+ `Fiber.new(storage:)` for setting the storage when creating a
+ fiber. [[Feature #19078]]
+
+ Existing Thread and Fiber local variables can be tricky to use.
+ Thread-local variables are shared between all fibers, making it
+ hard to isolate, while Fiber-local variables can be hard to
+ share. It is often desirable to define unit of execution
+ ("execution context") such that some state is shared between all
+ fibers and threads created in that context. This is what Fiber
+ storage provides.
+
+ ```ruby
+ def log(message)
+ puts "#{Fiber[:request_id]}: #{message}"
+ end
+
+ def handle_requests
+ while request = read_request
+ Fiber.schedule do
+ Fiber[:request_id] = SecureRandom.uuid
+
+ request.messages.each do |message|
+ Fiber.schedule do
+ log("Handling #{message}") # Log includes inherited request_id.
+ end
+ end
+ end
+ end
+ end
+ ```
+
+ You should generally consider Fiber storage for any state which
+ you want to be shared implicitly between all fibers and threads
+ created in a given context, e.g. a connection pool, a request
+ id, a logger level, environment variables, configuration, etc.
+
+* Fiber::Scheduler
+
+ * Introduce `Fiber::Scheduler#io_select` for non-blocking IO.select.
+ [[Feature #19060]]
+
+* IO
+
+ * Introduce IO#timeout= and IO#timeout which can cause
+ IO::TimeoutError to be raised if a blocking operation exceeds the
+ specified timeout. [[Feature #18630]]
+
+ ```ruby
+ STDIN.timeout = 1
+ STDIN.read # => Blocking operation timed out! (IO::TimeoutError)
+ ```
+
+ * Introduce `IO.new(..., path:)` and promote `File#path` to `IO#path`.
+ [[Feature #19036]]
+
+* Class
+
+ * Class#attached_object, which returns the object for which
+ the receiver is the singleton class. Raises TypeError if the
+ receiver is not a singleton class.
+ [[Feature #12084]]
+
+ ```ruby
+ class Foo; end
+
+ Foo.singleton_class.attached_object #=> Foo
+ Foo.new.singleton_class.attached_object #=> #<Foo:0x000000010491a370>
+ Foo.attached_object #=> TypeError: `Foo' is not a singleton class
+ nil.singleton_class.attached_object #=> TypeError: `NilClass' is not a singleton class
+ ```
+
+* Data
+
+ * New core class to represent simple immutable value object. The class is
+ similar to Struct and partially shares an implementation, but has more
+ lean and strict API. [[Feature #16122]]
+
+ ```ruby
+ Measure = Data.define(:amount, :unit)
+ distance = Measure.new(100, 'km') #=> #<data Measure amount=100, unit="km">
+ weight = Measure.new(amount: 50, unit: 'kg') #=> #<data Measure amount=50, unit="kg">
+ weight.with(amount: 40) #=> #<data Measure amount=40, unit="kg">
+ weight.amount #=> 50
+ weight.amount = 40 #=> NoMethodError: undefined method `amount='
+ ```
+
+* Encoding
+
+ * Encoding#replicate has been deprecated and will be removed in 3.3. [[Feature #18949]]
+ * The dummy `Encoding::UTF_16` and `Encoding::UTF_32` encodings no longer
+ try to dynamically guess the endian based on a byte order mark.
+ Use `Encoding::UTF_16BE`/`UTF_16LE` and `Encoding::UTF_32BE`/`UTF_32LE` instead.
+ This change speeds up getting the encoding of a String. [[Feature #18949]]
+ * Limit maximum encoding set size by 256.
+ If exceeding maximum size, `EncodingError` will be raised. [[Feature #18949]]
+
+* Enumerator
+
+ * Enumerator.product has been added. Enumerator::Product is the implementation. [[Feature #18685]]
+
+* Exception
+
+ * Exception#detailed_message has been added.
+ The default error printer calls this method on the Exception object
+ instead of #message. [[Feature #18564]]
+
* Hash
+
* Hash#shift now always returns nil if the hash is
empty, instead of returning the default value or
calling the default proc. [[Bug #16908]]
+* Integer
+
+ * Integer#ceildiv has been added. [[Feature #18809]]
+
* Kernel
+
* Kernel#binding raises RuntimeError if called from a non-Ruby frame
(such as a method defined in C). [[Bug #18487]]
* MatchData
+
* MatchData#byteoffset has been added. [[Feature #13110]]
+ * MatchData#deconstruct has been added. [[Feature #18821]]
+ * MatchData#deconstruct_keys has been added. [[Feature #18821]]
* Module
+
* Module.used_refinements has been added. [[Feature #14332]]
* Module#refinements has been added. [[Feature #12737]]
* Module#const_added has been added. [[Feature #17881]]
* Module#undefined_instance_methods has been added. [[Feature #12655]]
* Proc
+
* Proc#dup returns an instance of subclass. [[Bug #17545]]
* Proc#parameters now accepts lambda keyword. [[Feature #15357]]
+* Process
+ * Added `RLIMIT_NPTS` constant to FreeBSD platform
+
* Regexp
+
+ * The cache-based optimization is introduced.
+ Many (but not all) Regexp matching is now in linear time, which
+ will prevent regular expression denial of service (ReDoS)
+ vulnerability. [[Feature #19104]]
+
+ * Regexp.linear_time? is introduced. [[Feature #19194]]
+
* Regexp.new now supports passing the regexp flags not only as an Integer,
- but also as a String Unknown flags raise errors. Otherwise, anything
- other than `true`, `false`, `nil` or Integer will be warned.
+ but also as a String. Unknown flags raise ArgumentError.
+ Otherwise, anything other than `true`, `false`, `nil` or Integer will be warned.
[[Feature #18788]]
+ * Regexp.timeout= has been added. Also, Regexp.new new supports timeout keyword.
+ See [[Feature #17837]]
+
* Refinement
+
* Refinement#refined_class has been added. [[Feature #12737]]
+* RubyVM::AbstractSyntaxTree
+
+ * Add `error_tolerant` option for `parse`, `parse_file` and `of`. [[Feature #19013]]
+ With this option
+
+ 1. SyntaxError is suppressed
+ 2. AST is returned for invalid input
+ 3. `end` is complemented when a parser reaches to the end of input but `end` is insufficient
+ 4. `end` is treated as keyword based on indent
+
+ ```ruby
+ # Without error_tolerant option
+ root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY)
+ def m
+ a = 10
+ if
+ end
+ RUBY
+ # => <internal:ast>:33:in `parse': syntax error, unexpected `end' (SyntaxError)
+
+ # With error_tolerant option
+ root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY, error_tolerant: true)
+ def m
+ a = 10
+ if
+ end
+ RUBY
+ p root # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-4:3>
+
+ # `end` is treated as keyword based on indent
+ root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY, error_tolerant: true)
+ module Z
+ class Foo
+ foo.
+ end
+
+ def bar
+ end
+ end
+ RUBY
+ p root.children[-1].children[-1].children[-1].children[-2..-1]
+ # => [#<RubyVM::AbstractSyntaxTree::Node:CLASS@2:2-4:5>, #<RubyVM::AbstractSyntaxTree::Node:DEFN@6:2-7:5>]
+ ```
+
+ * Add `keep_tokens` option for `parse`, `parse_file` and `of`. Add `#tokens` and `#all_tokens`
+ for RubyVM::AbstractSyntaxTree::Node [[Feature #19070]]
+
+ ```ruby
+ root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true)
+ root.tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
+ root.tokens.map{_1[2]}.join # => "x = 1 + 2"
+ ```
+
* Set
+
* Set is now available as a built-in class without the need for `require "set"`. [[Feature #16989]]
- It is currently autoloaded via the `Set` constant or a call to `Enumerable#to_set`.
+ It is currently autoloaded via the Set constant or a call to Enumerable#to_set.
* String
+
* String#byteindex and String#byterindex have been added. [[Feature #13110]]
- * Update Unicode to Version 14.0.0 and Emoji Version 14.0. [[Feature #18037]]
+ * Update Unicode to Version 15.0.0 and Emoji Version 15.0. [[Feature #18639]]
(also applies to Regexp)
* String#bytesplice has been added. [[Feature #18598]]
+ * String#dedup has been added as an alias to String#-@. [[Feature #18595]]
* Struct
+
* A Struct class can also be initialized with keyword arguments
- without `keyword_init: true` on `Struct.new` [[Feature #16806]]
+ without `keyword_init: true` on Struct.new [[Feature #16806]]
+
+ ```ruby
+ Post = Struct.new(:id, :name)
+ Post.new(1, "hello") #=> #<struct Post id=1, name="hello">
+ # From Ruby 3.2, the following code also works without keyword_init: true.
+ Post.new(id: 1, name: "hello") #=> #<struct Post id=1, name="hello">
+ ```
+
+* Thread
+
+ * Thread.each_caller_location is added. [[Feature #16663]]
+
+* Thread::Queue
+
+ * Thread::Queue#pop(timeout: sec) is added. [[Feature #18774]]
+
+* Thread::SizedQueue
+
+ * Thread::SizedQueue#pop(timeout: sec) is added. [[Feature #18774]]
+ * Thread::SizedQueue#push(timeout: sec) is added. [[Feature #18944]]
+
+* Time
+
+ * Time#deconstruct_keys is added, allowing to use Time instances
+ in pattern-matching expressions [[Feature #19071]]
+
+ * Time.new now can parse a string like generated by Time#inspect
+ and return a Time instance based on the given argument.
+ [[Feature #18033]]
+
+* SyntaxError
+ * SyntaxError#path has been added. [[Feature #19138]]
* TracePoint
+
* TracePoint#binding now returns `nil` for `c_call`/`c_return` TracePoints.
[[Bug #18487]]
* TracePoint#enable `target_thread` keyword argument now defaults to the
- current thread if `target` and `target_line` keyword arguments are not
- passed. [[Bug #16889]]
+ current thread if a block is given and `target` and `target_line` keyword
+ arguments are not passed. [[Bug #16889]]
+
+* UnboundMethod
+
+ * `UnboundMethod#==` returns `true` if the actual method is same. For example,
+ `String.instance_method(:object_id) == Array.instance_method(:object_id)`
+ returns `true`. [[Feature #18798]]
+
+ * `UnboundMethod#inspect` does not show the receiver of `instance_method`.
+ For example `String.instance_method(:object_id).inspect` returns
+ `"#<UnboundMethod: Kernel#object_id()>"`
+ (was `"#<UnboundMethod: String(Kernel)#object_id()>"`).
+
+* GC
+
+ * Expose `need_major_gc` via `GC.latest_gc_info`. [GH-6791]
+
+* ObjectSpace
+
+ * `ObjectSpace.dump_all` dump shapes as well. [GH-6868]
## Stdlib updates
+* Bundler
+
+ * Bundler now uses [PubGrub] resolver instead of [Molinillo] for performance improvement.
+ * Add --ext=rust support to bundle gem for creating simple gems with Rust extensions.
+ [[GH-rubygems-6149]]
+ * Make cloning git repos faster [[GH-rubygems-4475]]
+
+* RubyGems
+
+ * Add mswin support for cargo builder. [[GH-rubygems-6167]]
+
+* CGI
+
+ * `CGI.escapeURIComponent` and `CGI.unescapeURIComponent` are added.
+ [[Feature #18822]]
+
+* Coverage
+
+ * `Coverage.setup` now accepts `eval: true`. By this, `eval` and related methods are
+ able to generate code coverage. [[Feature #19008]]
+
+ * `Coverage.supported?(mode)` enables detection of what coverage modes are
+ supported. [[Feature #19026]]
+
+* Date
+
+ * Added `Date#deconstruct_keys` and `DateTime#deconstruct_keys` same as [[Feature #19071]]
+
+* ERB
+
+ * `ERB::Util.html_escape` is made faster than `CGI.escapeHTML`.
+ * It no longer allocates a String object when no character needs to be escaped.
+ * It skips calling `#to_s` method when an argument is already a String.
+ * `ERB::Escape.html_escape` is added as an alias to `ERB::Util.html_escape`,
+ which has not been monkey-patched by Rails.
+ * `ERB::Util.url_encode` is made faster using `CGI.escapeURIComponent`.
+ * `-S` option is removed from `erb` command.
+
+* FileUtils
+
+ * Add FileUtils.ln_sr method and `relative:` option to FileUtils.ln_s.
+ [[Feature #18925]]
+
+* IRB
+
+ * debug.gem integration commands have been added: `debug`, `break`, `catch`,
+ `next`, `delete`, `step`, `continue`, `finish`, `backtrace`, `info`
+ * They work even if you don't have `gem "debug"` in your Gemfile.
+ * See also: [What's new in Ruby 3.2's IRB?](https://st0012.dev/whats-new-in-ruby-3-2-irb)
+ * More Pry-like commands and features have been added.
+ * `edit` and `show_cmds` (like Pry's `help`) are added.
+ * `ls` takes `-g` or `-G` option to filter out outputs.
+ * `show_source` is aliased from `$` and accepts unquoted inputs.
+ * `whereami` is aliased from `@`.
+
+* Net::Protocol
+
+ * Improve `Net::BufferedIO` performance. [[GH-net-protocol-14]]
+
+* Pathname
+
+ * Added `Pathname#lutime`. [[GH-pathname-20]]
+
+* Socket
+
+ * Added the following constants for supported platforms.
+ * `SO_INCOMING_CPU`
+ * `SO_INCOMING_NAPI_ID`
+ * `SO_RTABLE`
+ * `SO_SETFIB`
+ * `SO_USER_COOKIE`
+ * `TCP_KEEPALIVE`
+ * `TCP_CONNECTION_INFO`
+
+* SyntaxSuggest
+
+ * The feature of `syntax_suggest` formerly `dead_end` is integrated in Ruby.
+ [[Feature #18159]]
+
+* UNIXSocket
+
+ * Add support for UNIXSocket on Windows. Emulate anonymous sockets. Add
+ support for File.socket? and File::Stat#socket? where possible.
+ [[Feature #19135]]
+
* The following default gems are updated.
- * RubyGems 3.4.0.dev
- * bigdecimal 3.1.2
- * bundler 2.4.0.dev
- * cgi 0.3.2
- * etc 1.4.0
- * io-console 0.5.11
- * io-nonblock 0.1.1
- * io-wait 0.3.0.pre
- * ipaddr 1.2.4
- * json 2.6.2
- * logger 1.5.1
- * net-http 0.2.2
- * net-protocol 0.1.3
+
+ * RubyGems 3.4.1
+ * abbrev 0.1.1
+ * benchmark 0.2.1
+ * bigdecimal 3.1.3
+ * bundler 2.4.1
+ * cgi 0.3.6
+ * csv 3.2.6
+ * date 3.3.3
+ * delegate 0.3.0
+ * did_you_mean 1.6.3
+ * digest 3.1.1
+ * drb 2.1.1
+ * english 0.7.2
+ * erb 4.0.2
+ * error_highlight 0.5.1
+ * etc 1.4.2
+ * fcntl 1.0.2
+ * fiddle 1.1.1
+ * fileutils 1.7.0
+ * forwardable 1.3.3
+ * getoptlong 0.2.0
+ * io-console 0.6.0
+ * io-nonblock 0.2.0
+ * io-wait 0.3.0
+ * ipaddr 1.2.5
+ * irb 1.6.2
+ * json 2.6.3
+ * logger 1.5.3
+ * mutex_m 0.1.2
+ * net-http 0.4.0
+ * net-protocol 0.2.1
+ * nkf 0.1.2
+ * open-uri 0.3.0
+ * open3 0.1.2
+ * openssl 3.1.0
+ * optparse 0.3.1
* ostruct 0.5.5
- * psych 5.0.0.dev
- * reline 0.3.1
- * securerandom 0.2.0
- * stringio 3.0.3
- * timeout 0.3.0
+ * pathname 0.2.1
+ * pp 0.4.0
+ * pstore 0.1.2
+ * psych 5.0.1
+ * racc 1.6.2
+ * rdoc 6.5.0
+ * readline-ext 0.1.5
+ * reline 0.3.2
+ * resolv 0.2.2
+ * resolv-replace 0.1.1
+ * securerandom 0.2.2
+ * set 1.0.3
+ * stringio 3.0.4
+ * strscan 3.0.5
+ * syntax_suggest 1.0.2
+ * syslog 0.1.1
+ * tempfile 0.1.3
+ * time 0.2.1
+ * timeout 0.3.1
+ * tmpdir 0.1.3
+ * tsort 0.1.1
+ * un 0.2.1
+ * uri 0.12.0
+ * weakref 0.1.2
+ * win32ole 1.8.9
+ * yaml 0.2.1
+ * zlib 3.0.0
+
* The following bundled gems are updated.
- * minitest 5.16.2
- * net-imap 0.2.3
- * rbs 2.6.0
+
+ * minitest 5.16.3
+ * power_assert 2.0.3
+ * test-unit 3.5.7
+ * net-ftp 0.2.0
+ * net-imap 0.3.4
+ * net-pop 0.1.2
+ * net-smtp 0.3.3
+ * rbs 2.8.2
* typeprof 0.21.3
- * debug 1.6.1
-* The following default gems are now bundled gems.
+ * debug 1.7.1
+
+See GitHub releases like [GitHub Releases of Logger](https://github.com/ruby/logger/releases) or changelog for details of the default gems or bundled gems.
+
+## Supported platforms
+
+* WebAssembly/WASI is added. See [wasm/README.md] and [ruby.wasm] for more details. [[Feature #18462]]
## Compatibility issues
-Note: Excluding feature bug fixes.
+* `String#to_c` currently treat a sequence of underscores as an end of Complex
+ string. [[Bug #19087]]
+
+* Now `ENV.clone` raises `TypeError` as well as `ENV.dup` [[Bug #17767]]
### Removed constants
@@ -202,15 +592,77 @@ The following deprecated methods are removed.
[[Feature #16131]]
* `Kernel#trust`, `Kernel#untrust`, `Kernel#untrusted?`
[[Feature #16131]]
+* `Method#public?`, `Method#private?`, `Method#protected?`,
+ `UnboundMethod#public?`, `UnboundMethod#private?`, `UnboundMethod#protected?`
+ [[Bug #18729]] [[Bug #18751]] [[Bug #18435]]
+
+### Source code incompatibility of extension libraries
+
+* Extension libraries provide PRNG, subclasses of Random, need updates.
+ See [PRNG update] below for more information. [[Bug #19100]]
+
+### Error printer
+
+* Ruby no longer escapes control characters and backslashes in an
+ error message. [[Feature #18367]]
+
+### Constant lookup when defining a class/module
+
+* When defining a class/module directly under the Object class by class/module
+ statement, if there is already a class/module defined by `Module#include`
+ with the same name, the statement was handled as "open class" in Ruby 3.1 or before.
+ Since Ruby 3.2, a new class is defined instead. [[Feature #18832]]
## Stdlib compatibility issues
-* `Psych` no longer bundles libyaml sources.
- Users need to install the libyaml library themselves via the package
- system. [[Feature #18571]]
+* Psych no longer bundles libyaml sources.
+ And also Fiddle no longer bundles libffi sources.
+ Users need to install the libyaml/libffi library themselves via the package
+ manager like apt, yum, brew, etc.
+
+ Psych and fiddle supported the static build with specific version of libyaml
+ and libffi sources. You can build psych with libyaml-0.2.5 like this.
+
+ ```bash
+ $ ./configure --with-libyaml-source-dir=/path/to/libyaml-0.2.5
+ ```
+
+ And you can build fiddle with libffi-3.4.4 like this.
+
+ ```bash
+ $ ./configure --with-libffi-source-dir=/path/to/libffi-3.4.4
+ ```
+
+ [[Feature #18571]]
+
+* Check cookie name/path/domain characters in `CGI::Cookie`. [[CVE-2021-33621]]
+
+* `URI.parse` return empty string in host instead of nil. [[sec-156615]]
## C API updates
+### Updated C APIs
+
+The following APIs are updated.
+
+* PRNG update
+
+ `rb_random_interface_t` in ruby/random.h updated and versioned.
+ Extension libraries which use this interface and built for older
+ versions need to rebuild with adding `init_int32` function.
+
+### Added C APIs
+
+* `VALUE rb_hash_new_capa(long capa)` was added to created hashes with the desired capacity.
+* `rb_internal_thread_add_event_hook` and `rb_internal_thread_add_event_hook` were added to instrument threads scheduling.
+ The following events are available:
+ * `RUBY_INTERNAL_THREAD_EVENT_STARTED`
+ * `RUBY_INTERNAL_THREAD_EVENT_READY`
+ * `RUBY_INTERNAL_THREAD_EVENT_RESUMED`
+ * `RUBY_INTERNAL_THREAD_EVENT_SUSPENDED`
+ * `RUBY_INTERNAL_THREAD_EVENT_EXITED`
+* `rb_debug_inspector_current_depth` and `rb_debug_inspector_frame_depth` are added for debuggers.
+
### Removed C APIs
The following deprecated APIs are removed.
@@ -220,53 +672,149 @@ The following deprecated APIs are removed.
## Implementation improvements
-* Fixed several race conditions in `Kernel#autoload`. [[Bug #18782]]
+* Fixed several race conditions in Kernel#autoload. [[Bug #18782]]
+* Cache invalidation for expressions referencing constants is now
+ more fine-grained. `RubyVM.stat(:global_constant_state)` was
+ removed because it was closely tied to the previous caching scheme
+ where setting any constant invalidates all caches in the system.
+ New keys, `:constant_cache_invalidations` and `:constant_cache_misses`,
+ were introduced to help with use cases for `:global_constant_state`.
+ [[Feature #18589]]
+* The cache-based optimization for Regexp matching is introduced.
+ [[Feature #19104]]
+* [Variable Width Allocation](https://shopify.engineering/ruby-variable-width-allocation)
+ is now enabled by default. [[Feature #18239]]
+* Added a new instance variable caching mechanism, called object shapes, which
+ improves inline cache hits for most objects and allows us to generate very
+ efficient JIT code. Objects whose instance variables are defined in a
+ consistent order will see the most performance benefits.
+ [[Feature #18776]]
+* Speed up marking instruction sequences by using a bitmap to find "markable"
+ objects. This change results in faster major collections.
+ [[Feature #18875]]
## JIT
+### YJIT
+
+* YJIT is no longer experimental
+ * Has been tested on production workloads for over a year and proven to be quite stable.
+* YJIT now supports both x86-64 and arm64/aarch64 CPUs on Linux, MacOS, BSD and other UNIX platforms.
+ * This release brings support for Mac M1/M2, AWS Graviton and Raspberry Pi 4.
+* Building YJIT now requires Rust 1.58.0+. [[Feature #18481]]
+ * In order to ensure that CRuby is built with YJIT, please install `rustc` >= 1.58.0
+ before running `./configure`
+ * Please reach out to the YJIT team should you run into any issues.
+* Physical memory for JIT code is lazily allocated. Unlike Ruby 3.1,
+ the RSS of a Ruby process is minimized because virtual memory pages
+ allocated by `--yjit-exec-mem-size` will not be mapped to physical
+ memory pages until actually utilized by JIT code.
+* Introduce Code GC that frees all code pages when the memory consumption
+ by JIT code reaches `--yjit-exec-mem-size`.
+ * `RubyVM::YJIT.runtime_stats` returns Code GC metrics in addition to
+ existing `inline_code_size` and `outlined_code_size` keys:
+ `code_gc_count`, `live_page_count`, `freed_page_count`, and `freed_code_size`.
+* Most of the statistics produced by `RubyVM::YJIT.runtime_stats` are now available in release builds.
+ * Simply run ruby with `--yjit-stats` to compute and dump stats (incurs some run-time overhead).
+* YJIT is now optimized to take advantage of object shapes. [[Feature #18776]]
+* Take advantage of finer-grained constant invalidation to invalidate less code when defining new constants. [[Feature #18589]]
+* The default `--yjit-exec-mem-size` is changed to 64 (MiB).
+* The default `--yjit-call-threshold` is changed to 30.
+
### MJIT
-### YJIT: New experimental in-process JIT compiler
-
-## Static analysis
-
-### RBS
-
-### TypeProf
-
-## Debugger
-
-## error_highlight
-
-## IRB Autocomplete and Document Display
-
-## Miscellaneous changes
-
-[Feature #12005]: https://bugs.ruby-lang.org/issues/12005
-[Feature #12655]: https://bugs.ruby-lang.org/issues/12655
-[Feature #12737]: https://bugs.ruby-lang.org/issues/12737
-[Feature #13110]: https://bugs.ruby-lang.org/issues/13110
-[Feature #14332]: https://bugs.ruby-lang.org/issues/14332
-[Feature #15231]: https://bugs.ruby-lang.org/issues/15231
-[Feature #15357]: https://bugs.ruby-lang.org/issues/15357
-[Bug #15928]: https://bugs.ruby-lang.org/issues/15928
-[Feature #16131]: https://bugs.ruby-lang.org/issues/16131
-[Bug #16466]: https://bugs.ruby-lang.org/issues/16466
-[Feature #16806]: https://bugs.ruby-lang.org/issues/16806
-[Bug #16889]: https://bugs.ruby-lang.org/issues/16889
-[Bug #16908]: https://bugs.ruby-lang.org/issues/16908
-[Feature #16989]: https://bugs.ruby-lang.org/issues/16989
-[Feature #17351]: https://bugs.ruby-lang.org/issues/17351
-[Feature #17391]: https://bugs.ruby-lang.org/issues/17391
-[Bug #17545]: https://bugs.ruby-lang.org/issues/17545
-[Feature #17881]: https://bugs.ruby-lang.org/issues/17881
-[Feature #18037]: https://bugs.ruby-lang.org/issues/18037
-[Feature #18351]: https://bugs.ruby-lang.org/issues/18351
-[Bug #18487]: https://bugs.ruby-lang.org/issues/18487
-[Feature #18571]: https://bugs.ruby-lang.org/issues/18571
-[Feature #18585]: https://bugs.ruby-lang.org/issues/18585
-[Feature #18598]: https://bugs.ruby-lang.org/issues/18598
-[Bug #18625]: https://bugs.ruby-lang.org/issues/18625
-[Bug #18633]: https://bugs.ruby-lang.org/issues/18633
-[Bug #18782]: https://bugs.ruby-lang.org/issues/18782
-[Feature #18788]: https://bugs.ruby-lang.org/issues/18788
+* The MJIT compiler is re-implemented in Ruby as `ruby_vm/mjit/compiler`.
+* MJIT compiler is executed under a forked Ruby process instead of
+ doing it in a native thread called MJIT worker. [[Feature #18968]]
+ * As a result, Microsoft Visual Studio (MSWIN) is no longer supported.
+* MinGW is no longer supported. [[Feature #18824]]
+* Rename `--mjit-min-calls` to `--mjit-call-threshold`.
+* Change default `--mjit-max-cache` back from 10000 to 100.
+
+[Feature #12005]: https://bugs.ruby-lang.org/issues/12005
+[Feature #12084]: https://bugs.ruby-lang.org/issues/12084
+[Feature #12655]: https://bugs.ruby-lang.org/issues/12655
+[Feature #12737]: https://bugs.ruby-lang.org/issues/12737
+[Feature #13110]: https://bugs.ruby-lang.org/issues/13110
+[Feature #14332]: https://bugs.ruby-lang.org/issues/14332
+[Feature #15231]: https://bugs.ruby-lang.org/issues/15231
+[Feature #15357]: https://bugs.ruby-lang.org/issues/15357
+[Bug #15928]: https://bugs.ruby-lang.org/issues/15928
+[Feature #16122]: https://bugs.ruby-lang.org/issues/16122
+[Feature #16131]: https://bugs.ruby-lang.org/issues/16131
+[Bug #16466]: https://bugs.ruby-lang.org/issues/16466
+[Feature #16663]: https://bugs.ruby-lang.org/issues/16663
+[Feature #16806]: https://bugs.ruby-lang.org/issues/16806
+[Bug #16889]: https://bugs.ruby-lang.org/issues/16889
+[Bug #16908]: https://bugs.ruby-lang.org/issues/16908
+[Feature #16989]: https://bugs.ruby-lang.org/issues/16989
+[Feature #17351]: https://bugs.ruby-lang.org/issues/17351
+[Feature #17391]: https://bugs.ruby-lang.org/issues/17391
+[Bug #17545]: https://bugs.ruby-lang.org/issues/17545
+[Bug #17767]: https://bugs.ruby-lang.org/issues/17767
+[Feature #17837]: https://bugs.ruby-lang.org/issues/17837
+[Feature #17881]: https://bugs.ruby-lang.org/issues/17881
+[Feature #18033]: https://bugs.ruby-lang.org/issues/18033
+[Feature #18159]: https://bugs.ruby-lang.org/issues/18159
+[Feature #18239]: https://bugs.ruby-lang.org/issues/18239#note-17
+[Feature #18351]: https://bugs.ruby-lang.org/issues/18351
+[Feature #18367]: https://bugs.ruby-lang.org/issues/18367
+[Bug #18435]: https://bugs.ruby-lang.org/issues/18435
+[Feature #18462]: https://bugs.ruby-lang.org/issues/18462
+[Feature #18481]: https://bugs.ruby-lang.org/issues/18481
+[Bug #18487]: https://bugs.ruby-lang.org/issues/18487
+[Feature #18564]: https://bugs.ruby-lang.org/issues/18564
+[Feature #18571]: https://bugs.ruby-lang.org/issues/18571
+[Feature #18585]: https://bugs.ruby-lang.org/issues/18585
+[Feature #18589]: https://bugs.ruby-lang.org/issues/18589
+[Feature #18595]: https://bugs.ruby-lang.org/issues/18595
+[Feature #18598]: https://bugs.ruby-lang.org/issues/18598
+[Bug #18625]: https://bugs.ruby-lang.org/issues/18625
+[Feature #18630]: https://bugs.ruby-lang.org/issues/18630
+[Bug #18633]: https://bugs.ruby-lang.org/issues/18633
+[Feature #18639]: https://bugs.ruby-lang.org/issues/18639
+[Feature #18685]: https://bugs.ruby-lang.org/issues/18685
+[Bug #18729]: https://bugs.ruby-lang.org/issues/18729
+[Bug #18751]: https://bugs.ruby-lang.org/issues/18751
+[Feature #18774]: https://bugs.ruby-lang.org/issues/18774
+[Feature #18776]: https://bugs.ruby-lang.org/issues/18776
+[Bug #18782]: https://bugs.ruby-lang.org/issues/18782
+[Feature #18788]: https://bugs.ruby-lang.org/issues/18788
+[Feature #18798]: https://bugs.ruby-lang.org/issues/18798
+[Feature #18809]: https://bugs.ruby-lang.org/issues/18809
+[Feature #18821]: https://bugs.ruby-lang.org/issues/18821
+[Feature #18822]: https://bugs.ruby-lang.org/issues/18822
+[Feature #18824]: https://bugs.ruby-lang.org/issues/18824
+[Feature #18832]: https://bugs.ruby-lang.org/issues/18832
+[Feature #18875]: https://bugs.ruby-lang.org/issues/18875
+[Feature #18925]: https://bugs.ruby-lang.org/issues/18925
+[Feature #18944]: https://bugs.ruby-lang.org/issues/18944
+[Feature #18949]: https://bugs.ruby-lang.org/issues/18949
+[Feature #18968]: https://bugs.ruby-lang.org/issues/18968
+[Feature #19008]: https://bugs.ruby-lang.org/issues/19008
+[Feature #19013]: https://bugs.ruby-lang.org/issues/19013
+[Feature #19026]: https://bugs.ruby-lang.org/issues/19026
+[Feature #19036]: https://bugs.ruby-lang.org/issues/19036
+[Feature #19060]: https://bugs.ruby-lang.org/issues/19060
+[Feature #19070]: https://bugs.ruby-lang.org/issues/19070
+[Feature #19071]: https://bugs.ruby-lang.org/issues/19071
+[Feature #19078]: https://bugs.ruby-lang.org/issues/19078
+[Bug #19087]: https://bugs.ruby-lang.org/issues/19087
+[Bug #19100]: https://bugs.ruby-lang.org/issues/19100
+[Feature #19104]: https://bugs.ruby-lang.org/issues/19104
+[Feature #19135]: https://bugs.ruby-lang.org/issues/19135
+[Feature #19138]: https://bugs.ruby-lang.org/issues/19138
+[Feature #19194]: https://bugs.ruby-lang.org/issues/19194
+[Molinillo]: https://github.com/CocoaPods/Molinillo
+[PubGrub]: https://github.com/jhawthorn/pub_grub
+[GH-net-protocol-14]: https://github.com/ruby/net-protocol/pull/14
+[GH-pathname-20]: https://github.com/ruby/pathname/pull/20
+[GH-6791]: https://github.com/ruby/ruby/pull/6791
+[GH-6868]: https://github.com/ruby/ruby/pull/6868
+[GH-rubygems-4475]: https://github.com/rubygems/rubygems/pull/4475
+[GH-rubygems-6149]: https://github.com/rubygems/rubygems/pull/6149
+[GH-rubygems-6167]: https://github.com/rubygems/rubygems/pull/6167
+[sec-156615]: https://hackerone.com/reports/156615
+[CVE-2021-33621]: https://www.ruby-lang.org/en/news/2022/11/22/http-response-splitting-in-cgi-cve-2021-33621/
+[wasm/README.md]: https://github.com/ruby/ruby/blob/master/wasm/README.md
+[ruby.wasm]: https://github.com/ruby/ruby.wasm
diff --git a/README.ja.md b/README.ja.md
index bb69c09055..93c0131690 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -4,7 +4,6 @@
[![Actions Status: Windows](https://github.com/ruby/ruby/workflows/Windows/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows")
[![AppVeyor status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/master?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/master)
[![Travis Status](https://app.travis-ci.com/ruby/ruby.svg?branch=master)](https://app.travis-ci.com/ruby/ruby)
-[![Cirrus Status](https://api.cirrus-ci.com/github/ruby/ruby.svg)](https://cirrus-ci.com/github/ruby/ruby/master)
# Rubyとは
diff --git a/README.md b/README.md
index da2bbbdd22..c445448c71 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,6 @@
[![Actions Status: Windows](https://github.com/ruby/ruby/workflows/Windows/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows")
[![AppVeyor status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/master?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/master)
[![Travis Status](https://app.travis-ci.com/ruby/ruby.svg?branch=master)](https://app.travis-ci.com/ruby/ruby)
-[![Cirrus Status](https://api.cirrus-ci.com/github/ruby/ruby.svg)](https://cirrus-ci.com/github/ruby/ruby/master)
# What is Ruby?
@@ -46,6 +45,10 @@ to see the list of branches:
You may also want to use https://git.ruby-lang.org/ruby.git (actual master of Ruby source)
if you are a committer.
+## How to build
+
+see [Building Ruby](doc/contributing/building_ruby.md)
+
## Ruby home page
https://www.ruby-lang.org/
diff --git a/addr2line.c b/addr2line.c
index fe4ad84423..e5f25293e2 100644
--- a/addr2line.c
+++ b/addr2line.c
@@ -159,12 +159,15 @@ typedef struct obj_info {
struct dwarf_section debug_info;
struct dwarf_section debug_line;
struct dwarf_section debug_ranges;
+ struct dwarf_section debug_str_offsets;
+ struct dwarf_section debug_addr;
struct dwarf_section debug_rnglists;
struct dwarf_section debug_str;
+ struct dwarf_section debug_line_str;
struct obj_info *next;
} obj_info_t;
-#define DWARF_SECTION_COUNT 6
+#define DWARF_SECTION_COUNT 9
static struct dwarf_section *
obj_dwarf_section_at(obj_info_t *obj, int n)
@@ -174,8 +177,11 @@ obj_dwarf_section_at(obj_info_t *obj, int n)
&obj->debug_info,
&obj->debug_line,
&obj->debug_ranges,
+ &obj->debug_str_offsets,
+ &obj->debug_addr,
&obj->debug_rnglists,
- &obj->debug_str
+ &obj->debug_str,
+ &obj->debug_line_str
};
if (n < 0 || DWARF_SECTION_COUNT <= n) {
abort();
@@ -248,39 +254,51 @@ get_nth_dirname(unsigned long dir, const char *p)
return p;
}
+static const char *parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t *obj, const char **out_path, uint64_t *out_directory_index);
+
static void
-fill_filename(int file, const char *include_directories, const char *filenames, line_info_t *line, obj_info_t *obj)
+fill_filename(int file, uint8_t format, uint16_t version, const char *include_directories, const char *filenames, line_info_t *line, obj_info_t *obj)
{
int i;
const char *p = filenames;
const char *filename;
unsigned long dir;
- for (i = 1; i <= file; i++) {
- filename = p;
- if (!*p) {
- /* Need to output binary file name? */
- kprintf("Unexpected file number %d in %s at %tx\n",
- file, binary_filename, filenames - obj->mapped);
- return;
- }
- while (*p) p++;
- p++;
- dir = uleb128(&p);
- /* last modified. */
- uleb128(&p);
- /* size of the file. */
- uleb128(&p);
-
- if (i == file) {
- line->filename = filename;
- line->dirname = get_nth_dirname(dir, include_directories);
- }
+ if (version >= 5) {
+ const char *path;
+ uint64_t directory_index = -1;
+ parse_ver5_debug_line_header(filenames, file, format, obj, &path, &directory_index);
+ line->filename = path;
+ parse_ver5_debug_line_header(include_directories, (int)directory_index, format, obj, &path, NULL);
+ line->dirname = path;
+ }
+ else {
+ for (i = 1; i <= file; i++) {
+ filename = p;
+ if (!*p) {
+ /* Need to output binary file name? */
+ kprintf("Unexpected file number %d in %s at %tx\n",
+ file, binary_filename, filenames - obj->mapped);
+ return;
+ }
+ while (*p) p++;
+ p++;
+ dir = uleb128(&p);
+ /* last modified. */
+ uleb128(&p);
+ /* size of the file. */
+ uleb128(&p);
+
+ if (i == file) {
+ line->filename = filename;
+ line->dirname = get_nth_dirname(dir, include_directories);
+ }
+ }
}
}
static void
fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line,
- const char *include_directories, const char *filenames,
+ uint8_t format, uint16_t version, const char *include_directories, const char *filenames,
obj_info_t *obj, line_info_t *lines, int offset)
{
int i;
@@ -290,7 +308,7 @@ fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line,
/* We assume one line code doesn't result >100 bytes of native code.
We may want more reliable way eventually... */
if (addr < a && a < addr + 100) {
- fill_filename(file, include_directories, filenames, &lines[i], obj);
+ fill_filename(file, format, version, include_directories, filenames, &lines[i], obj);
lines[i].line = line;
}
}
@@ -315,7 +333,7 @@ struct LineNumberProgramHeader {
};
static int
-parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header)
+parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgramHeader *header)
{
const char *p = *pp;
header->unit_length = *(uint32_t *)p;
@@ -332,7 +350,13 @@ parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header)
header->version = *(uint16_t *)p;
p += sizeof(uint16_t);
- if (header->version > 4) return -1;
+ if (header->version > 5) return -1;
+
+ if (header->version >= 5) {
+ /* address_size = *(uint8_t *)p++; */
+ /* segment_selector_size = *(uint8_t *)p++; */
+ p += 2;
+ }
header->header_length = header->format == 4 ? *(uint32_t *)p : *(uint64_t *)p;
p += header->format;
@@ -353,20 +377,27 @@ parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header)
/* header->standard_opcode_lengths = (uint8_t *)p - 1; */
p += header->opcode_base - 1;
- header->include_directories = p;
+ if (header->version >= 5) {
+ header->include_directories = p;
+ p = parse_ver5_debug_line_header(p, -1, header->format, obj, NULL, NULL);
+ header->filenames = p;
+ }
+ else {
+ header->include_directories = p;
- /* temporary measure for compress-debug-sections */
- if (p >= header->cu_end) return -1;
+ /* temporary measure for compress-debug-sections */
+ if (p >= header->cu_end) return -1;
- /* skip include directories */
- while (*p) {
- p = memchr(p, '\0', header->cu_end - p);
- if (!p) return -1;
- p++;
- }
- p++;
+ /* skip include directories */
+ while (*p) {
+ p = memchr(p, '\0', header->cu_end - p);
+ if (!p) return -1;
+ p++;
+ }
+ p++;
- header->filenames = p;
+ header->filenames = p;
+ }
*pp = header->cu_start;
@@ -392,13 +423,15 @@ parse_debug_line_cu(int num_traces, void **traces, const char **debug_line,
/* int epilogue_begin = 0; */
/* unsigned int isa = 0; */
- if (parse_debug_line_header(&p, &header))
+ if (parse_debug_line_header(obj, &p, &header))
return -1;
is_stmt = header.default_is_stmt;
#define FILL_LINE() \
do { \
fill_line(num_traces, traces, addr, file, line, \
+ header.format, \
+ header.version, \
header.include_directories, \
header.filenames, \
obj, lines, offset); \
@@ -827,16 +860,23 @@ enum {
VAL_cstr = 1,
VAL_data = 2,
VAL_uint = 3,
- VAL_int = 4
+ VAL_int = 4,
+ VAL_addr = 5
};
# define ABBREV_TABLE_SIZE 256
typedef struct {
obj_info_t *obj;
const char *file;
+ uint8_t current_version;
const char *current_cu;
uint64_t current_low_pc;
+ uint64_t current_str_offsets_base;
+ uint64_t current_addr_base;
+ uint64_t current_rnglists_base;
const char *debug_line_cu_end;
+ uint8_t debug_line_format;
+ uint16_t debug_line_version;
const char *debug_line_files;
const char *debug_line_directories;
const char *p;
@@ -861,6 +901,7 @@ typedef struct {
const char *ptr;
uint64_t uint64;
int64_t int64;
+ uint64_t addr_idx;
} as;
uint64_t off;
uint64_t at;
@@ -869,8 +910,11 @@ typedef struct {
int type;
} DebugInfoValue;
-/* TODO: Big Endian */
+#if defined(WORDS_BIGENDIAN)
+#define MERGE_2INTS(a,b,sz) (((uint64_t)(a)<<sz)|(b))
+#else
#define MERGE_2INTS(a,b,sz) (((uint64_t)(b)<<sz)|(a))
+#endif
static uint16_t
get_uint16(const uint8_t *p)
@@ -973,6 +1017,9 @@ debug_info_reader_init(DebugInfoReader *reader, obj_info_t *obj)
reader->pend = obj->debug_info.ptr + obj->debug_info.size;
reader->debug_line_cu_end = obj->debug_line.ptr;
reader->current_low_pc = 0;
+ reader->current_str_offsets_base = 0;
+ reader->current_addr_base = 0;
+ reader->current_rnglists_base = 0;
}
static void
@@ -1017,10 +1064,12 @@ di_read_debug_line_cu(DebugInfoReader *reader)
struct LineNumberProgramHeader header;
p = (const char *)reader->debug_line_cu_end;
- if (parse_debug_line_header(&p, &header))
+ if (parse_debug_line_header(reader->obj, &p, &header))
return -1;
reader->debug_line_cu_end = (char *)header.cu_end;
+ reader->debug_line_format = header.format;
+ reader->debug_line_version = header.version;
reader->debug_line_directories = (char *)header.include_directories;
reader->debug_line_files = (char *)header.filenames;
@@ -1028,6 +1077,13 @@ di_read_debug_line_cu(DebugInfoReader *reader)
}
static void
+set_addr_idx_value(DebugInfoValue *v, uint64_t n)
+{
+ v->as.addr_idx = n;
+ v->type = VAL_addr;
+}
+
+static void
set_uint_value(DebugInfoValue *v, uint64_t n)
{
v->as.uint64 = n;
@@ -1074,19 +1130,39 @@ get_cstr_value(DebugInfoValue *v)
}
}
+static const char *
+resolve_strx(DebugInfoReader *reader, uint64_t idx)
+{
+ const char *p = reader->obj->debug_str_offsets.ptr + reader->current_str_offsets_base;
+ uint64_t off;
+ if (reader->format == 4) {
+ off = ((uint32_t *)p)[idx];
+ }
+ else {
+ off = ((uint64_t *)p)[idx];
+ }
+ return reader->obj->debug_str.ptr + off;
+}
+
+static void
+debug_info_reader_read_addr_value(DebugInfoReader *reader, DebugInfoValue *v)
+{
+ if (reader->address_size == 4) {
+ set_uint_value(v, read_uint32(&reader->p));
+ } else if (reader->address_size == 8) {
+ set_uint_value(v, read_uint64(&reader->p));
+ } else {
+ fprintf(stderr,"unknown address_size:%d", reader->address_size);
+ abort();
+ }
+}
+
static void
debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoValue *v)
{
switch (form) {
case DW_FORM_addr:
- if (reader->address_size == 4) {
- set_uint_value(v, read_uint32(&reader->p));
- } else if (reader->address_size == 8) {
- set_uint_value(v, read_uint64(&reader->p));
- } else {
- fprintf(stderr,"unknown address_size:%d", reader->address_size);
- abort();
- }
+ debug_info_reader_read_addr_value(reader, v);
break;
case DW_FORM_block2:
v->size = read_uint16(&reader->p);
@@ -1138,13 +1214,19 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa
set_uint_value(v, read_uleb128(reader));
break;
case DW_FORM_ref_addr:
- if (reader->format == 4) {
- set_uint_value(v, read_uint32(&reader->p));
- } else if (reader->format == 8) {
- set_uint_value(v, read_uint64(&reader->p));
+ if (reader->current_version <= 2) {
+ // DWARF Version 2 specifies that references have
+ // the same size as an address on the target system
+ debug_info_reader_read_addr_value(reader, v);
} else {
- fprintf(stderr,"unknown format:%d", reader->format);
- abort();
+ if (reader->format == 4) {
+ set_uint_value(v, read_uint32(&reader->p));
+ } else if (reader->format == 8) {
+ set_uint_value(v, read_uint64(&reader->p));
+ } else {
+ fprintf(stderr,"unknown format:%d", reader->format);
+ abort();
+ }
}
break;
case DW_FORM_ref1:
@@ -1186,11 +1268,10 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa
set_uint_value(v, 1);
break;
case DW_FORM_strx:
- set_uint_value(v, uleb128(&reader->p));
+ set_cstr_value(v, resolve_strx(reader, uleb128(&reader->p)));
break;
case DW_FORM_addrx:
- /* TODO: read .debug_addr */
- set_uint_value(v, uleb128(&reader->p));
+ set_addr_idx_value(v, uleb128(&reader->p));
break;
case DW_FORM_ref_sup4:
set_uint_value(v, read_uint32(&reader->p));
@@ -1205,8 +1286,7 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa
reader->p += v->size;
break;
case DW_FORM_line_strp:
- set_uint_value(v, read_uint(reader));
- /* *p = reader->file + reader->line->sh_offset + ret; */
+ set_cstrp_value(v, reader->obj->debug_line_str.ptr, read_uint(reader));
break;
case DW_FORM_ref_sig8:
set_uint_value(v, read_uint64(&reader->p));
@@ -1224,28 +1304,28 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa
set_uint_value(v, read_uint64(&reader->p));
break;
case DW_FORM_strx1:
- set_uint_value(v, read_uint8(&reader->p));
+ set_cstr_value(v, resolve_strx(reader, read_uint8(&reader->p)));
break;
case DW_FORM_strx2:
- set_uint_value(v, read_uint16(&reader->p));
+ set_cstr_value(v, resolve_strx(reader, read_uint16(&reader->p)));
break;
case DW_FORM_strx3:
- set_uint_value(v, read_uint24(&reader->p));
+ set_cstr_value(v, resolve_strx(reader, read_uint24(&reader->p)));
break;
case DW_FORM_strx4:
- set_uint_value(v, read_uint32(&reader->p));
+ set_cstr_value(v, resolve_strx(reader, read_uint32(&reader->p)));
break;
case DW_FORM_addrx1:
- set_uint_value(v, read_uint8(&reader->p));
+ set_addr_idx_value(v, read_uint8(&reader->p));
break;
case DW_FORM_addrx2:
- set_uint_value(v, read_uint16(&reader->p));
+ set_addr_idx_value(v, read_uint16(&reader->p));
break;
case DW_FORM_addrx3:
- set_uint_value(v, read_uint24(&reader->p));
+ set_addr_idx_value(v, read_uint24(&reader->p));
break;
case DW_FORM_addrx4:
- set_uint_value(v, read_uint32(&reader->p));
+ set_addr_idx_value(v, read_uint32(&reader->p));
break;
case 0:
goto fail;
@@ -1373,6 +1453,76 @@ di_skip_records(DebugInfoReader *reader)
}
}
+typedef struct addr_header {
+ const char *ptr;
+ uint64_t unit_length;
+ uint8_t format;
+ uint8_t address_size;
+ /* uint8_t segment_selector_size; */
+} addr_header_t;
+
+static void
+addr_header_init(obj_info_t *obj, addr_header_t *header) {
+ const char *p = obj->debug_addr.ptr;
+
+ header->ptr = p;
+
+ if (!p) return;
+
+ header->unit_length = *(uint32_t *)p;
+ p += sizeof(uint32_t);
+
+ header->format = 4;
+ if (header->unit_length == 0xffffffff) {
+ header->unit_length = *(uint64_t *)p;
+ p += sizeof(uint64_t);
+ header->format = 8;
+ }
+
+ p += 2; /* version */
+ header->address_size = *p++;
+ p++; /* segment_selector_size */
+}
+
+static uint64_t
+read_addr(addr_header_t *header, uint64_t addr_base, uint64_t idx) {
+ if (header->address_size == 4) {
+ return ((uint32_t*)(header->ptr + addr_base))[idx];
+ }
+ else {
+ return ((uint64_t*)(header->ptr + addr_base))[idx];
+ }
+}
+
+typedef struct rnglists_header {
+ uint64_t unit_length;
+ uint8_t format;
+ uint8_t address_size;
+ uint32_t offset_entry_count;
+} rnglists_header_t;
+
+static void
+rnglists_header_init(obj_info_t *obj, rnglists_header_t *header) {
+ const char *p = obj->debug_rnglists.ptr;
+
+ if (!p) return;
+
+ header->unit_length = *(uint32_t *)p;
+ p += sizeof(uint32_t);
+
+ header->format = 4;
+ if (header->unit_length == 0xffffffff) {
+ header->unit_length = *(uint64_t *)p;
+ p += sizeof(uint64_t);
+ header->format = 8;
+ }
+
+ p += 2; /* version */
+ header->address_size = *p++;
+ p++; /* segment_selector_size */
+ header->offset_entry_count = *(uint32_t *)p;
+}
+
typedef struct {
uint64_t low_pc;
uint64_t high_pc;
@@ -1383,24 +1533,31 @@ typedef struct {
} ranges_t;
static void
-ranges_set(ranges_t *ptr, DebugInfoValue *v)
+ranges_set(ranges_t *ptr, DebugInfoValue *v, addr_header_t *addr_header, uint64_t addr_base)
{
+ uint64_t n = 0;
+ if (v->type == VAL_uint) {
+ n = v->as.uint64;
+ }
+ else if (v->type == VAL_addr) {
+ n = read_addr(addr_header, addr_base, v->as.addr_idx);
+ }
switch (v->at) {
case DW_AT_low_pc:
- ptr->low_pc = v->as.uint64;
+ ptr->low_pc = n;
ptr->low_pc_set = true;
break;
case DW_AT_high_pc:
if (v->form == DW_FORM_addr) {
- ptr->high_pc = v->as.uint64;
+ ptr->high_pc = n;
}
else {
- ptr->high_pc = ptr->low_pc + v->as.uint64;
+ ptr->high_pc = ptr->low_pc + n;
}
ptr->high_pc_set = true;
break;
case DW_AT_ranges:
- ptr->ranges = v->as.uint64;
+ ptr->ranges = n;
ptr->ranges_set = true;
break;
}
@@ -1422,7 +1579,7 @@ read_dw_form_addr(DebugInfoReader *reader, const char **ptr)
}
static uintptr_t
-ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr)
+ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_header_t *rnglists_header)
{
if (ptr->high_pc_set) {
if (ptr->ranges_set || !ptr->low_pc_set) {
@@ -1437,8 +1594,21 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr)
const char *p;
uint64_t base = ptr->low_pc_set ? ptr->low_pc : reader->current_low_pc;
bool base_valid = true;
- if (reader->obj->debug_rnglists.ptr) {
- p = reader->obj->debug_rnglists.ptr + ptr->ranges;
+ if (reader->current_version >= 5) {
+ if (rnglists_header->offset_entry_count == 0) {
+ // DW_FORM_sec_offset
+ p = reader->obj->debug_rnglists.ptr + ptr->ranges + reader->current_rnglists_base;
+ }
+ else {
+ // DW_FORM_rnglistx
+ const char *offset_array = reader->obj->debug_rnglists.ptr + reader->current_rnglists_base;
+ if (rnglists_header->format == 4) {
+ p = offset_array + ((uint32_t *)offset_array)[ptr->ranges];
+ }
+ else {
+ p = offset_array + ((uint64_t *)offset_array)[ptr->ranges];
+ }
+ }
for (;;) {
uint8_t rle = read_uint8(&p);
uintptr_t from = 0, to = 0;
@@ -1548,6 +1718,7 @@ di_read_cu(DebugInfoReader *reader)
}
reader->cu_end = reader->p + unit_length;
version = read_uint16(&reader->p);
+ reader->current_version = version;
if (version > 5) {
return -1;
}
@@ -1580,16 +1751,45 @@ di_read_cu(DebugInfoReader *reader)
break;
}
+ reader->current_str_offsets_base = 0;
+ reader->current_addr_base = 0;
+ reader->current_rnglists_base = 0;
+
+ DebugInfoValue low_pc = {{}};
/* enumerate abbrev */
for (;;) {
DebugInfoValue v = {{}};
if (!di_read_record(reader, &v)) break;
switch (v.at) {
case DW_AT_low_pc:
- reader->current_low_pc = v.as.uint64;
+ // clang may output DW_AT_addr_base after DW_AT_low_pc.
+ // We need to resolve the DW_FORM_addr* after DW_AT_addr_base is parsed.
+ low_pc = v;
+ break;
+ case DW_AT_str_offsets_base:
+ reader->current_str_offsets_base = v.as.uint64;
+ break;
+ case DW_AT_addr_base:
+ reader->current_addr_base = v.as.uint64;
+ break;
+ case DW_AT_rnglists_base:
+ reader->current_rnglists_base = v.as.uint64;
break;
}
}
+ // Resolve the DW_FORM_addr of DW_AT_low_pc
+ switch (low_pc.type) {
+ case VAL_uint:
+ reader->current_low_pc = low_pc.as.uint64;
+ break;
+ case VAL_addr:
+ {
+ addr_header_t header;
+ addr_header_init(reader->obj, &header);
+ reader->current_low_pc = read_addr(&header, reader->current_addr_base, low_pc.as.addr_idx);
+ }
+ break;
+ }
} while (0);
#endif
return 0;
@@ -1643,6 +1843,13 @@ read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_o
static void
debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
line_info_t *lines, int offset) {
+
+ addr_header_t addr_header = {};
+ addr_header_init(reader->obj, &addr_header);
+
+ rnglists_header_t rnglists_header = {};
+ rnglists_header_init(reader->obj, &rnglists_header);
+
while (reader->p < reader->cu_end) {
DIE die;
ranges_t ranges = {};
@@ -1669,7 +1876,7 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
line.sname = get_cstr_value(&v);
break;
case DW_AT_call_file:
- fill_filename((int)v.as.uint64, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj);
+ fill_filename((int)v.as.uint64, reader->debug_line_format, reader->debug_line_version, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj);
break;
case DW_AT_call_line:
line.line = (int)v.as.uint64;
@@ -1677,7 +1884,7 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
case DW_AT_low_pc:
case DW_AT_high_pc:
case DW_AT_ranges:
- ranges_set(&ranges, &v);
+ ranges_set(&ranges, &v, &addr_header, reader->current_addr_base);
break;
case DW_AT_declaration:
goto skip_die;
@@ -1694,9 +1901,9 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
for (int i=offset; i < num_traces; i++) {
uintptr_t addr = (uintptr_t)traces[i];
uintptr_t offset = addr - reader->obj->base_addr + reader->obj->vmaddr;
- uintptr_t saddr = ranges_include(reader, &ranges, offset);
+ uintptr_t saddr = ranges_include(reader, &ranges, offset, &rnglists_header);
if (saddr) {
- /* fprintf(stderr, "%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */
+ /* fprintf(stdout, "%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */
if (lines[i].sname) {
line_info_t *lp = malloc(sizeof(line_info_t));
memcpy(lp, &lines[i], sizeof(line_info_t));
@@ -1715,6 +1922,54 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
}
}
+// This function parses the following attributes of Line Number Program Header in DWARF 5:
+//
+// * directory_entry_format_count
+// * directory_entry_format
+// * directories_count
+// * directories
+//
+// or
+//
+// * file_name_entry_format_count
+// * file_name_entry_format
+// * file_names_count
+// * file_names
+//
+// It records DW_LNCT_path and DW_LNCT_directory_index at the index "idx".
+static const char *
+parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t *obj, const char **out_path, uint64_t *out_directory_index) {
+ int i, j;
+ int entry_format_count = *(uint8_t *)p++;
+ const char *entry_format = p;
+
+ /* skip the part of entry_format */
+ for (i = 0; i < entry_format_count * 2; i++) uleb128(&p);
+
+ int entry_count = (int)uleb128(&p);
+
+ DebugInfoReader reader;
+ debug_info_reader_init(&reader, obj);
+ reader.format = format;
+ reader.p = p;
+ for (j = 0; j < entry_count; j++) {
+ const char *format = entry_format;
+ for (i = 0; i < entry_format_count; i++) {
+ DebugInfoValue v = {{}};
+ unsigned long dw_lnct = uleb128(&format);
+ unsigned long dw_form = uleb128(&format);
+ debug_info_reader_read_value(&reader, dw_form, &v);
+ if (dw_lnct == 1 /* DW_LNCT_path */ && v.type == VAL_cstr && out_path)
+ *out_path = v.as.ptr + v.off;
+ if (dw_lnct == 2 /* DW_LNCT_directory_index */ && v.type == VAL_uint && out_directory_index)
+ *out_directory_index = v.as.uint64;
+ }
+ if (j == idx) return 0;
+ }
+
+ return reader.p;
+}
+
#ifdef USE_ELF
static unsigned long
uncompress_debug_section(ElfW(Shdr) *shdr, char *file, char **ptr)
@@ -1843,8 +2098,11 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
".debug_info",
".debug_line",
".debug_ranges",
+ ".debug_str_offsets",
+ ".debug_addr",
".debug_rnglists",
- ".debug_str"
+ ".debug_str",
+ ".debug_line_str"
};
for (j=0; j < DWARF_SECTION_COUNT; j++) {
@@ -2100,8 +2358,11 @@ found_mach_header:
"__debug_info",
"__debug_line",
"__debug_ranges",
+ "__debug_str_offsets",
+ "__debug_addr",
"__debug_rnglists",
- "__debug_str"
+ "__debug_str",
+ "__debug_line_str",
};
struct LP(segment_command) *scmd = (struct LP(segment_command) *)lcmd;
if (strcmp(scmd->segname, "__TEXT") == 0) {
@@ -2299,6 +2560,7 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces)
obj_info_t *obj = NULL;
/* 2 is NULL + main executable */
void **dladdr_fbases = (void **)calloc(num_traces+2, sizeof(void *));
+
#ifdef HAVE_MAIN_EXE_PATH
char *main_path = NULL; /* used on printing backtrace */
ssize_t len;
diff --git a/array.c b/array.c
index d877066000..b76e9a64a3 100644
--- a/array.c
+++ b/array.c
@@ -39,6 +39,37 @@
VALUE rb_cArray;
+/* Flags of RArray
+ *
+ * 1: RARRAY_EMBED_FLAG
+ * The array is embedded (its contents follow the header, rather than
+ * being on a separately allocated buffer).
+ * 2: RARRAY_SHARED_FLAG (equal to ELTS_SHARED)
+ * The array is shared. The buffer this array points to is owned by
+ * another array (the shared root).
+ * if USE_RVARGC
+ * 3-9: RARRAY_EMBED_LEN
+ * The length of the array when RARRAY_EMBED_FLAG is set.
+ * else
+ * 3-4: RARRAY_EMBED_LEN
+ * The length of the array when RARRAY_EMBED_FLAG is set.
+ * endif
+ * 12: RARRAY_SHARED_ROOT_FLAG
+ * The array is a shared root that does reference counting. The buffer
+ * this array points to is owned by this array but may be pointed to
+ * by other arrays.
+ * Note: Frozen arrays may be a shared root without this flag being
+ * set. Frozen arrays do not have reference counting because
+ * they cannot be modified. Not updating the reference count
+ * improves copy-on-write performance. Their reference count is
+ * assumed to be infinity.
+ * 13: RARRAY_TRANSIENT_FLAG
+ * The buffer of the array is allocated on the transient heap.
+ * 14: RARRAY_PTR_IN_USE_FLAG
+ * The buffer of the array is in use. This is only used during
+ * debugging.
+ */
+
/* for OPTIMIZED_CMP: */
#define id_cmp idCmp
@@ -62,7 +93,7 @@ should_be_T_ARRAY(VALUE ary)
#define ARY_EMBED_LEN(a) \
(assert(ARY_EMBED_P(a)), \
(long)((RBASIC(a)->flags >> RARRAY_EMBED_LEN_SHIFT) & \
- (RARRAY_EMBED_LEN_MASK >> RARRAY_EMBED_LEN_SHIFT)))
+ (RARRAY_EMBED_LEN_MASK >> RARRAY_EMBED_LEN_SHIFT)))
#define ARY_HEAP_SIZE(a) (assert(!ARY_EMBED_P(a)), assert(ARY_OWNS_HEAP_P(a)), ARY_CAPA(a) * sizeof(VALUE))
#define ARY_OWNS_HEAP_P(a) (assert(should_be_T_ARRAY((VALUE)(a))), \
@@ -135,18 +166,20 @@ should_be_T_ARRAY(VALUE ary)
const VALUE _value_ = (value); \
assert(!ARY_EMBED_P(_ary_)); \
assert(ARY_SHARED_P(_ary_)); \
- assert(!ARY_LITERAL_P(_ary_)); \
- assert(ARY_SHARED_ROOT_P(_value_) || ARY_LITERAL_P(_value_)); \
+ assert(!OBJ_FROZEN(_ary_)); \
+ assert(ARY_SHARED_ROOT_P(_value_) || OBJ_FROZEN(_value_)); \
RB_OBJ_WRITE(_ary_, &RARRAY(_ary_)->as.heap.aux.shared_root, _value_); \
} while (0)
-#define ARY_SHARED_ROOT_OCCUPIED(ary) (!ARY_LITERAL_P(ary) && ARY_SHARED_ROOT_REFCNT(ary) == 1)
+#define ARY_SHARED_ROOT_OCCUPIED(ary) (!OBJ_FROZEN(ary) && ARY_SHARED_ROOT_REFCNT(ary) == 1)
#define ARY_SET_SHARED_ROOT_REFCNT(ary, value) do { \
assert(ARY_SHARED_ROOT_P(ary)); \
+ assert(!OBJ_FROZEN(ary)); \
assert((value) >= 0); \
RARRAY(ary)->as.heap.aux.capa = (value); \
} while (0)
#define FL_SET_SHARED_ROOT(ary) do { \
+ assert(!OBJ_FROZEN(ary)); \
assert(!ARY_EMBED_P(ary)); \
assert(!RARRAY_TRANSIENT_P(ary)); \
FL_SET((ary), RARRAY_SHARED_ROOT_FLAG); \
@@ -193,8 +226,15 @@ ary_embeddable_p(long capa)
bool
rb_ary_embeddable_p(VALUE ary)
{
- // if the array is shared or a shared root then it's not moveable
- return !(ARY_SHARED_P(ary) || ARY_SHARED_ROOT_P(ary));
+ /* An array cannot be turned embeddable when the array is:
+ * - Shared root: other objects may point to the buffer of this array
+ * so we cannot make it embedded.
+ * - Frozen: this array may also be a shared root without the shared root
+ * flag.
+ * - Shared: we don't want to re-embed an array that points to a shared
+ * root (to save memory).
+ */
+ return !(ARY_SHARED_ROOT_P(ary) || OBJ_FROZEN(ary) || ARY_SHARED_P(ary));
}
size_t
@@ -209,7 +249,7 @@ rb_ary_size_as_embedded(VALUE ary)
real_size = ary_embed_size(ARY_HEAP_CAPA(ary));
}
else {
- real_size = sizeof(struct RString);
+ real_size = sizeof(struct RArray);
}
return real_size;
}
@@ -228,7 +268,7 @@ ary_verify_(VALUE ary, const char *file, int line)
const VALUE *ptr = ARY_HEAP_PTR(ary);
const VALUE *root_ptr = RARRAY_CONST_PTR_TRANSIENT(root);
long len = ARY_HEAP_LEN(ary), root_len = RARRAY_LEN(root);
- assert(ARY_SHARED_ROOT_P(root) || ARY_LITERAL_P(root));
+ assert(ARY_SHARED_ROOT_P(root) || OBJ_FROZEN(root));
assert(root_ptr <= ptr && ptr + len <= root_ptr + root_len);
ary_verify(root);
}
@@ -291,7 +331,7 @@ void
rb_mem_clear(VALUE *mem, long size)
{
while (size--) {
- *mem++ = Qnil;
+ *mem++ = Qnil;
}
}
@@ -299,7 +339,7 @@ static void
ary_mem_clear(VALUE ary, long beg, long size)
{
RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
- rb_mem_clear(ptr + beg, size);
+ rb_mem_clear(ptr + beg, size);
});
}
@@ -307,7 +347,7 @@ static inline void
memfill(register VALUE *mem, register long size, register VALUE val)
{
while (size--) {
- *mem++ = val;
+ *mem++ = val;
}
}
@@ -315,8 +355,8 @@ static void
ary_memfill(VALUE ary, long beg, long size, VALUE val)
{
RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
- memfill(ptr + beg, size, val);
- RB_OBJ_WRITTEN(ary, Qundef, val);
+ memfill(ptr + beg, size, val);
+ RB_OBJ_WRITTEN(ary, Qundef, val);
});
}
@@ -421,14 +461,11 @@ static inline void
rb_ary_transient_heap_evacuate_(VALUE ary, int transient, int promote)
{
if (transient) {
+ assert(!ARY_SHARED_ROOT_P(ary));
+
VALUE *new_ptr;
const VALUE *old_ptr = ARY_HEAP_PTR(ary);
long capa = ARY_HEAP_CAPA(ary);
- long len = ARY_HEAP_LEN(ary);
-
- if (ARY_SHARED_ROOT_P(ary)) {
- capa = len;
- }
assert(ARY_OWNS_HEAP_P(ary));
assert(RARRAY_TRANSIENT_P(ary));
@@ -475,15 +512,19 @@ rb_ary_make_embedded(VALUE ary)
{
assert(rb_ary_embeddable_p(ary));
if (!ARY_EMBED_P(ary)) {
- VALUE *buf = RARRAY_PTR(ary);
- long len = RARRAY_LEN(ary);
+ const VALUE *buf = ARY_HEAP_PTR(ary);
+ long len = ARY_HEAP_LEN(ary);
+ bool was_transient = RARRAY_TRANSIENT_P(ary);
+ // FL_SET_EMBED also unsets the transient flag
FL_SET_EMBED(ary);
ARY_SET_EMBED_LEN(ary, len);
- RARY_TRANSIENT_UNSET(ary);
- memmove(RARRAY_PTR(ary), buf, len * sizeof(VALUE));
- ary_heap_free_ptr(ary, buf, len * sizeof(VALUE));
+ MEMCPY((void *)ARY_EMBED_PTR(ary), (void *)buf, VALUE, len);
+
+ if (!was_transient) {
+ ary_heap_free_ptr(ary, buf, len * sizeof(VALUE));
+ }
}
}
@@ -546,10 +587,10 @@ ary_double_capa(VALUE ary, long min)
long new_capa = ARY_CAPA(ary) / 2;
if (new_capa < ARY_DEFAULT_SIZE) {
- new_capa = ARY_DEFAULT_SIZE;
+ new_capa = ARY_DEFAULT_SIZE;
}
if (new_capa >= ARY_MAX_SIZE - min) {
- new_capa = (ARY_MAX_SIZE - min) / 2;
+ new_capa = (ARY_MAX_SIZE - min) / 2;
}
new_capa += min;
ary_resize_capa(ary, new_capa);
@@ -560,7 +601,7 @@ ary_double_capa(VALUE ary, long min)
static void
rb_ary_decrement_share(VALUE shared_root)
{
- if (!ARY_LITERAL_P(shared_root)) {
+ if (!OBJ_FROZEN(shared_root)) {
long num = ARY_SHARED_ROOT_REFCNT(shared_root);
ARY_SET_SHARED_ROOT_REFCNT(shared_root, num - 1);
}
@@ -591,7 +632,7 @@ rb_ary_reset(VALUE ary)
static VALUE
rb_ary_increment_share(VALUE shared_root)
{
- if (!ARY_LITERAL_P(shared_root)) {
+ if (!OBJ_FROZEN(shared_root)) {
long num = ARY_SHARED_ROOT_REFCNT(shared_root);
assert(num >= 0);
ARY_SET_SHARED_ROOT_REFCNT(shared_root, num + 1);
@@ -671,40 +712,40 @@ ary_ensure_room_for_push(VALUE ary, long add_len)
long capa;
if (old_len > ARY_MAX_SIZE - add_len) {
- rb_raise(rb_eIndexError, "index %ld too big", new_len);
+ rb_raise(rb_eIndexError, "index %ld too big", new_len);
}
if (ARY_SHARED_P(ary)) {
- if (new_len > ary_embed_capa(ary)) {
+ if (new_len > ary_embed_capa(ary)) {
VALUE shared_root = ARY_SHARED_ROOT(ary);
if (ARY_SHARED_ROOT_OCCUPIED(shared_root)) {
if (ARY_HEAP_PTR(ary) - RARRAY_CONST_PTR_TRANSIENT(shared_root) + new_len <= RARRAY_LEN(shared_root)) {
- rb_ary_modify_check(ary);
+ rb_ary_modify_check(ary);
ary_verify(ary);
ary_verify(shared_root);
return shared_root;
- }
- else {
- /* if array is shared, then it is likely it participate in push/shift pattern */
- rb_ary_modify(ary);
- capa = ARY_CAPA(ary);
- if (new_len > capa - (capa >> 6)) {
- ary_double_capa(ary, new_len);
- }
+ }
+ else {
+ /* if array is shared, then it is likely it participate in push/shift pattern */
+ rb_ary_modify(ary);
+ capa = ARY_CAPA(ary);
+ if (new_len > capa - (capa >> 6)) {
+ ary_double_capa(ary, new_len);
+ }
ary_verify(ary);
- return ary;
- }
- }
- }
+ return ary;
+ }
+ }
+ }
ary_verify(ary);
rb_ary_modify(ary);
}
else {
- rb_ary_modify_check(ary);
+ rb_ary_modify_check(ary);
}
capa = ARY_CAPA(ary);
if (new_len > capa) {
- ary_double_capa(ary, new_len);
+ ary_double_capa(ary, new_len);
}
ary_verify(ary);
@@ -790,10 +831,10 @@ ary_new(VALUE klass, long capa)
VALUE ary,*ptr;
if (capa < 0) {
- rb_raise(rb_eArgError, "negative array size (or size too big)");
+ rb_raise(rb_eArgError, "negative array size (or size too big)");
}
if (capa > ARY_MAX_SIZE) {
- rb_raise(rb_eArgError, "array size too big");
+ rb_raise(rb_eArgError, "array size too big");
}
RUBY_DTRACE_CREATE_HOOK(ARRAY, capa);
@@ -803,11 +844,11 @@ ary_new(VALUE klass, long capa)
}
else {
ary = ary_alloc_heap(klass);
+ ARY_SET_CAPA(ary, capa);
assert(!ARY_EMBED_P(ary));
ptr = ary_heap_alloc(ary, capa);
ARY_SET_PTR(ary, ptr);
- ARY_SET_CAPA(ary, capa);
ARY_SET_HEAP_LEN(ary, 0);
}
@@ -837,7 +878,7 @@ VALUE
va_start(ar, n);
for (i=0; i<n; i++) {
- ARY_SET(ary, i, va_arg(ar, VALUE));
+ ARY_SET(ary, i, va_arg(ar, VALUE));
}
va_end(ar);
@@ -852,8 +893,8 @@ rb_ary_tmp_new_from_values(VALUE klass, long n, const VALUE *elts)
ary = ary_new(klass, n);
if (n > 0 && elts) {
- ary_memcpy(ary, 0, n, elts);
- ARY_SET_LEN(ary, n);
+ ary_memcpy(ary, 0, n, elts);
+ ARY_SET_LEN(ary, n);
}
return ary;
@@ -898,10 +939,10 @@ ec_ary_new(rb_execution_context_t *ec, VALUE klass, long capa)
VALUE ary,*ptr;
if (capa < 0) {
- rb_raise(rb_eArgError, "negative array size (or size too big)");
+ rb_raise(rb_eArgError, "negative array size (or size too big)");
}
if (capa > ARY_MAX_SIZE) {
- rb_raise(rb_eArgError, "array size too big");
+ rb_raise(rb_eArgError, "array size too big");
}
RUBY_DTRACE_CREATE_HOOK(ARRAY, capa);
@@ -911,11 +952,11 @@ ec_ary_new(rb_execution_context_t *ec, VALUE klass, long capa)
}
else {
ary = ec_ary_alloc_heap(ec, klass);
+ ARY_SET_CAPA(ary, capa);
assert(!ARY_EMBED_P(ary));
ptr = ary_heap_alloc(ary, capa);
ARY_SET_PTR(ary, ptr);
- ARY_SET_CAPA(ary, capa);
ARY_SET_HEAP_LEN(ary, 0);
}
@@ -929,15 +970,15 @@ rb_ec_ary_new_from_values(rb_execution_context_t *ec, long n, const VALUE *elts)
ary = ec_ary_new(ec, rb_cArray, n);
if (n > 0 && elts) {
- ary_memcpy(ary, 0, n, elts);
- ARY_SET_LEN(ary, n);
+ ary_memcpy(ary, 0, n, elts);
+ ARY_SET_LEN(ary, n);
}
return ary;
}
VALUE
-rb_ary_tmp_new(long capa)
+rb_ary_hidden_new(long capa)
{
VALUE ary = ary_new(0, capa);
rb_ary_transient_heap_evacuate(ary, TRUE);
@@ -945,21 +986,11 @@ rb_ary_tmp_new(long capa)
}
VALUE
-rb_ary_tmp_new_fill(long capa)
+rb_ary_hidden_new_fill(long capa)
{
- VALUE ary = ary_new(0, capa);
+ VALUE ary = rb_ary_hidden_new(capa);
ary_memfill(ary, 0, capa, Qnil);
ARY_SET_LEN(ary, capa);
- rb_ary_transient_heap_evacuate(ary, TRUE);
- return ary;
-}
-
-VALUE
-rb_ary_literal_new(long capa)
-{
- VALUE ary = ary_new(0, capa);
- rb_ary_transient_heap_evacuate(ary, TRUE);
- FL_SET(ary, RARRAY_LITERAL_FLAG);
return ary;
}
@@ -997,59 +1028,64 @@ RUBY_FUNC_EXPORTED size_t
rb_ary_memsize(VALUE ary)
{
if (ARY_OWNS_HEAP_P(ary)) {
- return ARY_CAPA(ary) * sizeof(VALUE);
+ return ARY_CAPA(ary) * sizeof(VALUE);
}
else {
- return 0;
+ return 0;
}
}
-static inline void
-ary_discard(VALUE ary)
-{
- rb_ary_free(ary);
- RBASIC(ary)->flags |= RARRAY_EMBED_FLAG;
- RBASIC(ary)->flags &= ~(RARRAY_EMBED_LEN_MASK | RARRAY_TRANSIENT_FLAG);
-}
-
static VALUE
ary_make_shared(VALUE ary)
{
- assert(!ARY_EMBED_P(ary));
- assert(!ARY_LITERAL_P(ary));
+ assert(USE_RVARGC || !ARY_EMBED_P(ary));
ary_verify(ary);
if (ARY_SHARED_P(ary)) {
return ARY_SHARED_ROOT(ary);
}
else if (ARY_SHARED_ROOT_P(ary)) {
- return ary;
+ return ary;
}
else if (OBJ_FROZEN(ary)) {
- rb_ary_transient_heap_evacuate(ary, TRUE);
- ary_shrink_capa(ary);
- FL_SET_SHARED_ROOT(ary);
- ARY_SET_SHARED_ROOT_REFCNT(ary, 1);
- return ary;
+ if (!ARY_EMBED_P(ary)) {
+ rb_ary_transient_heap_evacuate(ary, TRUE);
+ ary_shrink_capa(ary);
+ }
+ return ary;
}
else {
- long capa = ARY_CAPA(ary), len = RARRAY_LEN(ary);
- const VALUE *ptr;
+ rb_ary_transient_heap_evacuate(ary, TRUE);
+
+ long capa = ARY_CAPA(ary);
+ long len = RARRAY_LEN(ary);
+
+ /* Shared roots cannot be embedded because the reference count
+ * (refcnt) is stored in as.heap.aux.capa. */
VALUE shared = ary_alloc_heap(0);
+ FL_SET_SHARED_ROOT(shared);
- rb_ary_transient_heap_evacuate(ary, TRUE);
- ptr = ARY_HEAP_PTR(ary);
+ if (ARY_EMBED_P(ary)) {
+ /* Cannot use ary_heap_alloc because we don't want to allocate
+ * on the transient heap. */
+ VALUE *ptr = ALLOC_N(VALUE, capa);
+ ARY_SET_PTR(shared, ptr);
+ ary_memcpy(shared, 0, len, RARRAY_PTR(ary));
+
+ FL_UNSET_EMBED(ary);
+ ARY_SET_HEAP_LEN(ary, len);
+ ARY_SET_PTR(ary, ptr);
+ }
+ else {
+ ARY_SET_PTR(shared, RARRAY_PTR(ary));
+ }
- FL_UNSET_EMBED(shared);
ARY_SET_LEN(shared, capa);
- ARY_SET_PTR(shared, ptr);
ary_mem_clear(shared, len, capa - len);
- FL_SET_SHARED_ROOT(shared);
ARY_SET_SHARED_ROOT_REFCNT(shared, 1);
FL_SET_SHARED(ary);
RB_DEBUG_COUNTER_INC(obj_ary_shared_create);
ARY_SET_SHARED(ary, shared);
- OBJ_FREEZE(shared);
ary_verify(shared);
ary_verify(ary);
@@ -1210,45 +1246,45 @@ rb_ary_initialize(int argc, VALUE *argv, VALUE ary)
rb_ary_reset(ary);
assert(ARY_EMBED_P(ary));
assert(ARY_EMBED_LEN(ary) == 0);
- if (rb_block_given_p()) {
- rb_warning("given block not used");
- }
- return ary;
+ if (rb_block_given_p()) {
+ rb_warning("given block not used");
+ }
+ return ary;
}
rb_scan_args(argc, argv, "02", &size, &val);
if (argc == 1 && !FIXNUM_P(size)) {
- val = rb_check_array_type(size);
- if (!NIL_P(val)) {
- rb_ary_replace(ary, val);
- return ary;
- }
+ val = rb_check_array_type(size);
+ if (!NIL_P(val)) {
+ rb_ary_replace(ary, val);
+ return ary;
+ }
}
len = NUM2LONG(size);
/* NUM2LONG() may call size.to_int, ary can be frozen, modified, etc */
if (len < 0) {
- rb_raise(rb_eArgError, "negative array size");
+ rb_raise(rb_eArgError, "negative array size");
}
if (len > ARY_MAX_SIZE) {
- rb_raise(rb_eArgError, "array size too big");
+ rb_raise(rb_eArgError, "array size too big");
}
/* recheck after argument conversion */
rb_ary_modify(ary);
ary_resize_capa(ary, len);
if (rb_block_given_p()) {
- long i;
+ long i;
- if (argc == 2) {
- rb_warn("block supersedes default value argument");
- }
- for (i=0; i<len; i++) {
- rb_ary_store(ary, i, rb_yield(LONG2NUM(i)));
- ARY_SET_LEN(ary, i + 1);
- }
+ if (argc == 2) {
+ rb_warn("block supersedes default value argument");
+ }
+ for (i=0; i<len; i++) {
+ rb_ary_store(ary, i, rb_yield(LONG2NUM(i)));
+ ARY_SET_LEN(ary, i + 1);
+ }
}
else {
- ary_memfill(ary, 0, len, val);
- ARY_SET_LEN(ary, len);
+ ary_memfill(ary, 0, len, val);
+ ARY_SET_LEN(ary, len);
}
return ary;
}
@@ -1279,26 +1315,26 @@ rb_ary_store(VALUE ary, long idx, VALUE val)
long len = RARRAY_LEN(ary);
if (idx < 0) {
- idx += len;
- if (idx < 0) {
- rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
- idx - len, -len);
- }
+ idx += len;
+ if (idx < 0) {
+ rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
+ idx - len, -len);
+ }
}
else if (idx >= ARY_MAX_SIZE) {
- rb_raise(rb_eIndexError, "index %ld too big", idx);
+ rb_raise(rb_eIndexError, "index %ld too big", idx);
}
rb_ary_modify(ary);
if (idx >= ARY_CAPA(ary)) {
- ary_double_capa(ary, idx);
+ ary_double_capa(ary, idx);
}
if (idx > len) {
- ary_mem_clear(ary, len, idx - len + 1);
+ ary_mem_clear(ary, len, idx - len + 1);
}
if (idx >= len) {
- ARY_SET_LEN(ary, idx + 1);
+ ARY_SET_LEN(ary, idx + 1);
}
ARY_SET(ary, idx, val);
}
@@ -1310,18 +1346,20 @@ ary_make_partial(VALUE ary, VALUE klass, long offset, long len)
assert(len >= 0);
assert(offset+len <= RARRAY_LEN(ary));
- if (ary_embeddable_p(len)) {
+ const size_t rarray_embed_capa_max = (sizeof(struct RArray) - offsetof(struct RArray, as.ary)) / sizeof(VALUE);
+
+ if ((size_t)len <= rarray_embed_capa_max && ary_embeddable_p(len)) {
VALUE result = ary_alloc_embed(klass, len);
ary_memcpy(result, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary) + offset);
ARY_SET_EMBED_LEN(result, len);
return result;
}
else {
+ VALUE shared = ary_make_shared(ary);
+
VALUE result = ary_alloc_heap(klass);
assert(!ARY_EMBED_P(result));
- VALUE shared = ARY_LITERAL_P(ary) ? ary : ary_make_shared(ary);
-
ARY_SET_PTR(result, RARRAY_CONST_PTR_TRANSIENT(ary));
ARY_SET_LEN(result, RARRAY_LEN(ary));
rb_ary_set_shared(result, shared);
@@ -1346,16 +1384,19 @@ ary_make_partial_step(VALUE ary, VALUE klass, long offset, long len, long step)
const VALUE *values = RARRAY_CONST_PTR_TRANSIENT(ary);
const long orig_len = len;
- if ((step > 0 && step >= len) || (step < 0 && (step < -len))) {
+ if (step > 0 && step >= len) {
VALUE result = ary_new(klass, 1);
VALUE *ptr = (VALUE *)ARY_EMBED_PTR(result);
RB_OBJ_WRITE(result, ptr, values[offset]);
ARY_SET_EMBED_LEN(result, 1);
return result;
}
+ else if (step < 0 && step < -len) {
+ step = -len;
+ }
long ustep = (step < 0) ? -step : step;
- len = (len + ustep - 1) / ustep;
+ len = roomof(len, ustep);
long i;
long j = offset + ((step > 0) ? 0 : (orig_len - 1));
@@ -1410,13 +1451,13 @@ ary_take_first_or_last(int argc, const VALUE *argv, VALUE ary, enum ary_take_pos
n = NUM2LONG(argv[0]);
len = RARRAY_LEN(ary);
if (n > len) {
- n = len;
+ n = len;
}
else if (n < 0) {
- rb_raise(rb_eArgError, "negative array size");
+ rb_raise(rb_eArgError, "negative array size");
}
if (last) {
- offset = len - n;
+ offset = len - n;
}
return ary_make_partial(ary, rb_cArray, offset, n);
}
@@ -1444,7 +1485,7 @@ rb_ary_push(VALUE ary, VALUE item)
long idx = RARRAY_LEN((ary_verify(ary), ary));
VALUE target_ary = ary_ensure_room_for_push(ary, 1);
RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
- RB_OBJ_WRITE(target_ary, &ptr[idx], item);
+ RB_OBJ_WRITE(target_ary, &ptr[idx], item);
});
ARY_SET_LEN(ary, idx + 1);
ary_verify(ary);
@@ -1497,10 +1538,10 @@ rb_ary_pop(VALUE ary)
n = RARRAY_LEN(ary);
if (n == 0) return Qnil;
if (ARY_OWNS_HEAP_P(ary) &&
- n * 3 < ARY_CAPA(ary) &&
- ARY_CAPA(ary) > ARY_DEFAULT_SIZE)
+ n * 3 < ARY_CAPA(ary) &&
+ ARY_CAPA(ary) > ARY_DEFAULT_SIZE)
{
- ary_resize_capa(ary, n * 2);
+ ary_resize_capa(ary, n * 2);
}
--n;
ARY_SET_LEN(ary, n);
@@ -1545,7 +1586,7 @@ rb_ary_pop_m(int argc, VALUE *argv, VALUE ary)
VALUE result;
if (argc == 0) {
- return rb_ary_pop(ary);
+ return rb_ary_pop(ary);
}
rb_ary_modify_check(ary);
@@ -1613,7 +1654,7 @@ rb_ary_shift_m(int argc, VALUE *argv, VALUE ary)
long n;
if (argc == 0) {
- return rb_ary_shift(ary);
+ return rb_ary_shift(ary);
}
rb_ary_modify_check(ary);
@@ -1685,7 +1726,7 @@ ary_modify_for_unshift(VALUE ary, int argc)
rb_ary_modify(ary);
capa = ARY_CAPA(ary);
if (capa - (capa >> 6) <= new_len) {
- ary_double_capa(ary, new_len);
+ ary_double_capa(ary, new_len);
}
/* use shared array for big "queues" */
@@ -1693,20 +1734,20 @@ ary_modify_for_unshift(VALUE ary, int argc)
ary_verify(ary);
/* make a room for unshifted items */
- capa = ARY_CAPA(ary);
- ary_make_shared(ary);
+ capa = ARY_CAPA(ary);
+ ary_make_shared(ary);
head = sharedp = RARRAY_CONST_PTR_TRANSIENT(ary);
return make_room_for_unshift(ary, head, (void *)sharedp, argc, capa, len);
}
else {
- /* sliding items */
+ /* sliding items */
RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
- MEMMOVE(ptr + argc, ptr, VALUE, len);
- });
+ MEMMOVE(ptr + argc, ptr, VALUE, len);
+ });
ary_verify(ary);
- return ary;
+ return ary;
}
}
@@ -1763,8 +1804,8 @@ rb_ary_unshift_m(int argc, VALUE *argv, VALUE ary)
VALUE target_ary;
if (argc == 0) {
- rb_ary_modify_check(ary);
- return ary;
+ rb_ary_modify_check(ary);
+ return ary;
}
target_ary = ary_ensure_room_for_unshift(ary, argc);
@@ -1786,7 +1827,7 @@ rb_ary_elt(VALUE ary, long offset)
long len = RARRAY_LEN(ary);
if (len == 0) return Qnil;
if (offset < 0 || len <= offset) {
- return Qnil;
+ return Qnil;
}
return RARRAY_AREF(ary, offset);
}
@@ -1807,7 +1848,7 @@ rb_ary_subseq_step(VALUE ary, long beg, long len, long step)
if (beg < 0 || len < 0) return Qnil;
if (alen < len || alen < beg + len) {
- len = alen - beg;
+ len = alen - beg;
}
klass = rb_cArray;
if (len == 0) return ary_new(klass, 0);
@@ -1937,7 +1978,7 @@ rb_ary_aref(int argc, const VALUE *argv, VALUE ary)
{
rb_check_arity(argc, 1, 2);
if (argc == 2) {
- return rb_ary_aref2(ary, argv[0], argv[1]);
+ return rb_ary_aref2(ary, argv[0], argv[1]);
}
return rb_ary_aref1(ary, argv[0]);
}
@@ -1948,7 +1989,7 @@ rb_ary_aref2(VALUE ary, VALUE b, VALUE e)
long beg = NUM2LONG(b);
long len = NUM2LONG(e);
if (beg < 0) {
- beg += RARRAY_LEN(ary);
+ beg += RARRAY_LEN(ary);
}
return rb_ary_subseq(ary, beg, len);
}
@@ -1960,7 +2001,7 @@ rb_ary_aref1(VALUE ary, VALUE arg)
/* special case - speeding up */
if (FIXNUM_P(arg)) {
- return rb_ary_entry(ary, FIX2LONG(arg));
+ return rb_ary_entry(ary, FIX2LONG(arg));
}
/* check if idx is Range or ArithmeticSequence */
switch (rb_arithmetic_sequence_beg_len_step(arg, &beg, &len, &step, RARRAY_LEN(ary), 0)) {
@@ -2029,11 +2070,11 @@ static VALUE
rb_ary_first(int argc, VALUE *argv, VALUE ary)
{
if (argc == 0) {
- if (RARRAY_LEN(ary) == 0) return Qnil;
- return RARRAY_AREF(ary, 0);
+ if (RARRAY_LEN(ary) == 0) return Qnil;
+ return RARRAY_AREF(ary, 0);
}
else {
- return ary_take_first_or_last(argc, argv, ary, ARY_TAKE_FIRST);
+ return ary_take_first_or_last(argc, argv, ary, ARY_TAKE_FIRST);
}
}
@@ -2052,7 +2093,7 @@ rb_ary_first(int argc, VALUE *argv, VALUE ary)
*
* If +self+ is empty, returns +nil+.
*
- * When non-negative \Innteger argument +n+ is given,
+ * When non-negative \Integer argument +n+ is given,
* returns the last +n+ elements in a new \Array:
*
* a = [:foo, 'bar', 2]
@@ -2075,12 +2116,12 @@ VALUE
rb_ary_last(int argc, const VALUE *argv, VALUE ary)
{
if (argc == 0) {
- long len = RARRAY_LEN(ary);
- if (len == 0) return Qnil;
- return RARRAY_AREF(ary, len-1);
+ long len = RARRAY_LEN(ary);
+ if (len == 0) return Qnil;
+ return RARRAY_AREF(ary, len-1);
}
else {
- return ary_take_first_or_last(argc, argv, ary, ARY_TAKE_LAST);
+ return ary_take_first_or_last(argc, argv, ary, ARY_TAKE_LAST);
}
}
@@ -2131,20 +2172,20 @@ rb_ary_fetch(int argc, VALUE *argv, VALUE ary)
rb_scan_args(argc, argv, "11", &pos, &ifnone);
block_given = rb_block_given_p();
if (block_given && argc == 2) {
- rb_warn("block supersedes default value argument");
+ rb_warn("block supersedes default value argument");
}
idx = NUM2LONG(pos);
if (idx < 0) {
- idx += RARRAY_LEN(ary);
+ idx += RARRAY_LEN(ary);
}
if (idx < 0 || RARRAY_LEN(ary) <= idx) {
- if (block_given) return rb_yield(pos);
- if (argc == 1) {
- rb_raise(rb_eIndexError, "index %ld outside of array bounds: %ld...%ld",
- idx - (idx < 0 ? RARRAY_LEN(ary) : 0), -RARRAY_LEN(ary), RARRAY_LEN(ary));
- }
- return ifnone;
+ if (block_given) return rb_yield(pos);
+ if (argc == 1) {
+ rb_raise(rb_eIndexError, "index %ld outside of array bounds: %ld...%ld",
+ idx - (idx < 0 ? RARRAY_LEN(ary) : 0), -RARRAY_LEN(ary), RARRAY_LEN(ary));
+ }
+ return ifnone;
}
return RARRAY_AREF(ary, idx);
}
@@ -2194,23 +2235,23 @@ rb_ary_index(int argc, VALUE *argv, VALUE ary)
long i;
if (argc == 0) {
- RETURN_ENUMERATOR(ary, 0, 0);
- for (i=0; i<RARRAY_LEN(ary); i++) {
- if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
- return LONG2NUM(i);
- }
- }
- return Qnil;
+ RETURN_ENUMERATOR(ary, 0, 0);
+ for (i=0; i<RARRAY_LEN(ary); i++) {
+ if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
+ return LONG2NUM(i);
+ }
+ }
+ return Qnil;
}
rb_check_arity(argc, 0, 1);
val = argv[0];
if (rb_block_given_p())
- rb_warn("given block not used");
+ rb_warn("given block not used");
for (i=0; i<RARRAY_LEN(ary); i++) {
- VALUE e = RARRAY_AREF(ary, i);
- if (rb_equal(e, val)) {
- return LONG2NUM(i);
- }
+ VALUE e = RARRAY_AREF(ary, i);
+ if (rb_equal(e, val)) {
+ return LONG2NUM(i);
+ }
}
return Qnil;
}
@@ -2255,25 +2296,25 @@ rb_ary_rindex(int argc, VALUE *argv, VALUE ary)
long i = RARRAY_LEN(ary), len;
if (argc == 0) {
- RETURN_ENUMERATOR(ary, 0, 0);
- while (i--) {
- if (RTEST(rb_yield(RARRAY_AREF(ary, i))))
- return LONG2NUM(i);
- if (i > (len = RARRAY_LEN(ary))) {
- i = len;
- }
- }
- return Qnil;
+ RETURN_ENUMERATOR(ary, 0, 0);
+ while (i--) {
+ if (RTEST(rb_yield(RARRAY_AREF(ary, i))))
+ return LONG2NUM(i);
+ if (i > (len = RARRAY_LEN(ary))) {
+ i = len;
+ }
+ }
+ return Qnil;
}
rb_check_arity(argc, 0, 1);
val = argv[0];
if (rb_block_given_p())
- rb_warn("given block not used");
+ rb_warn("given block not used");
while (i--) {
- VALUE e = RARRAY_AREF(ary, i);
- if (rb_equal(e, val)) {
- return LONG2NUM(i);
- }
+ VALUE e = RARRAY_AREF(ary, i);
+ if (rb_equal(e, val)) {
+ return LONG2NUM(i);
+ }
if (i > RARRAY_LEN(ary)) {
break;
}
@@ -2299,54 +2340,54 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
if (len < 0) rb_raise(rb_eIndexError, "negative length (%ld)", len);
olen = RARRAY_LEN(ary);
if (beg < 0) {
- beg += olen;
- if (beg < 0) {
- rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
- beg - olen, -olen);
- }
+ beg += olen;
+ if (beg < 0) {
+ rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
+ beg - olen, -olen);
+ }
}
if (olen < len || olen < beg + len) {
- len = olen - beg;
+ len = olen - beg;
}
{
const VALUE *optr = RARRAY_CONST_PTR_TRANSIENT(ary);
- rofs = (rptr >= optr && rptr < optr + olen) ? rptr - optr : -1;
+ rofs = (rptr >= optr && rptr < optr + olen) ? rptr - optr : -1;
}
if (beg >= olen) {
- VALUE target_ary;
- if (beg > ARY_MAX_SIZE - rlen) {
- rb_raise(rb_eIndexError, "index %ld too big", beg);
- }
- target_ary = ary_ensure_room_for_push(ary, rlen-len); /* len is 0 or negative */
- len = beg + rlen;
- ary_mem_clear(ary, olen, beg - olen);
- if (rlen > 0) {
+ VALUE target_ary;
+ if (beg > ARY_MAX_SIZE - rlen) {
+ rb_raise(rb_eIndexError, "index %ld too big", beg);
+ }
+ target_ary = ary_ensure_room_for_push(ary, rlen-len); /* len is 0 or negative */
+ len = beg + rlen;
+ ary_mem_clear(ary, olen, beg - olen);
+ if (rlen > 0) {
if (rofs != -1) rptr = RARRAY_CONST_PTR_TRANSIENT(ary) + rofs;
- ary_memcpy0(ary, beg, rlen, rptr, target_ary);
- }
- ARY_SET_LEN(ary, len);
+ ary_memcpy0(ary, beg, rlen, rptr, target_ary);
+ }
+ ARY_SET_LEN(ary, len);
}
else {
- long alen;
-
- if (olen - len > ARY_MAX_SIZE - rlen) {
- rb_raise(rb_eIndexError, "index %ld too big", olen + rlen - len);
- }
- rb_ary_modify(ary);
- alen = olen + rlen - len;
- if (alen >= ARY_CAPA(ary)) {
- ary_double_capa(ary, alen);
- }
-
- if (len != rlen) {
+ long alen;
+
+ if (olen - len > ARY_MAX_SIZE - rlen) {
+ rb_raise(rb_eIndexError, "index %ld too big", olen + rlen - len);
+ }
+ rb_ary_modify(ary);
+ alen = olen + rlen - len;
+ if (alen >= ARY_CAPA(ary)) {
+ ary_double_capa(ary, alen);
+ }
+
+ if (len != rlen) {
RARRAY_PTR_USE_TRANSIENT(ary, ptr,
MEMMOVE(ptr + beg + rlen, ptr + beg + len,
VALUE, olen - (beg + len)));
- ARY_SET_LEN(ary, alen);
- }
- if (rlen > 0) {
+ ARY_SET_LEN(ary, alen);
+ }
+ if (rlen > 0) {
if (rofs != -1) rptr = RARRAY_CONST_PTR_TRANSIENT(ary) + rofs;
/* give up wb-protected ary */
RB_OBJ_WB_UNPROTECT_FOR(ARRAY, ary);
@@ -2356,7 +2397,7 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
*/
RARRAY_PTR_USE_TRANSIENT(ary, ptr,
MEMMOVE(ptr + beg, rptr, VALUE, rlen));
- }
+ }
}
}
@@ -2367,10 +2408,10 @@ rb_ary_set_len(VALUE ary, long len)
rb_ary_modify_check(ary);
if (ARY_SHARED_P(ary)) {
- rb_raise(rb_eRuntimeError, "can't set length of shared ");
+ rb_raise(rb_eRuntimeError, "can't set length of shared ");
}
if (len > (capa = (long)ARY_CAPA(ary))) {
- rb_bug("probable buffer overflow: %ld for %ld", len, capa);
+ rb_bug("probable buffer overflow: %ld for %ld", len, capa);
}
ARY_SET_LEN(ary, len);
}
@@ -2384,14 +2425,14 @@ rb_ary_resize(VALUE ary, long len)
olen = RARRAY_LEN(ary);
if (len == olen) return ary;
if (len > ARY_MAX_SIZE) {
- rb_raise(rb_eIndexError, "index %ld too big", len);
+ rb_raise(rb_eIndexError, "index %ld too big", len);
}
if (len > olen) {
- if (len >= ARY_CAPA(ary)) {
- ary_double_capa(ary, len);
- }
- ary_mem_clear(ary, olen, len - olen);
- ARY_SET_LEN(ary, len);
+ if (len >= ARY_CAPA(ary)) {
+ ary_double_capa(ary, len);
+ }
+ ary_mem_clear(ary, olen, len - olen);
+ ARY_SET_LEN(ary, len);
}
else if (ARY_EMBED_P(ary)) {
ARY_SET_EMBED_LEN(ary, len);
@@ -2410,11 +2451,11 @@ rb_ary_resize(VALUE ary, long len)
if (is_malloc_ptr) ruby_sized_xfree((void *)ptr, ptr_capa);
}
else {
- if (olen > len + ARY_DEFAULT_SIZE) {
+ if (olen > len + ARY_DEFAULT_SIZE) {
size_t new_capa = ary_heap_realloc(ary, len);
ARY_SET_CAPA(ary, new_capa);
- }
- ARY_SET_HEAP_LEN(ary, len);
+ }
+ ARY_SET_HEAP_LEN(ary, len);
}
ary_verify(ary);
return ary;
@@ -2558,16 +2599,16 @@ rb_ary_aset(int argc, VALUE *argv, VALUE ary)
rb_check_arity(argc, 2, 3);
rb_ary_modify_check(ary);
if (argc == 3) {
- beg = NUM2LONG(argv[0]);
- len = NUM2LONG(argv[1]);
+ beg = NUM2LONG(argv[0]);
+ len = NUM2LONG(argv[1]);
return ary_aset_by_rb_ary_splice(ary, beg, len, argv[2]);
}
if (FIXNUM_P(argv[0])) {
- offset = FIX2LONG(argv[0]);
+ offset = FIX2LONG(argv[0]);
return ary_aset_by_rb_ary_store(ary, offset, argv[1]);
}
if (rb_range_beg_len(argv[0], &beg, &len, RARRAY_LEN(ary), 1)) {
- /* check if idx is Range */
+ /* check if idx is Range */
return ary_aset_by_rb_ary_splice(ary, beg, len, argv[1]);
}
@@ -2621,15 +2662,15 @@ rb_ary_insert(int argc, VALUE *argv, VALUE ary)
pos = NUM2LONG(argv[0]);
if (argc == 1) return ary;
if (pos == -1) {
- pos = RARRAY_LEN(ary);
+ pos = RARRAY_LEN(ary);
}
else if (pos < 0) {
- long minpos = -RARRAY_LEN(ary) - 1;
- if (pos < minpos) {
- rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
- pos, minpos);
- }
- pos++;
+ long minpos = -RARRAY_LEN(ary) - 1;
+ if (pos < minpos) {
+ rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
+ pos, minpos);
+ }
+ pos++;
}
rb_ary_splice(ary, pos, 0, argv + 1, argc - 1);
return ary;
@@ -2696,7 +2737,7 @@ rb_ary_each(VALUE ary)
ary_verify(ary);
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
for (i=0; i<RARRAY_LEN(ary); i++) {
- rb_yield(RARRAY_AREF(ary, i));
+ rb_yield(RARRAY_AREF(ary, i));
}
return ary;
}
@@ -2753,7 +2794,7 @@ rb_ary_each_index(VALUE ary)
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
for (i=0; i<RARRAY_LEN(ary); i++) {
- rb_yield(LONG2NUM(i));
+ rb_yield(LONG2NUM(i));
}
return ary;
}
@@ -2811,12 +2852,12 @@ rb_ary_reverse_each(VALUE ary)
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
len = RARRAY_LEN(ary);
while (len--) {
- long nlen;
- rb_yield(RARRAY_AREF(ary, len));
- nlen = RARRAY_LEN(ary);
- if (nlen < len) {
- len = nlen;
- }
+ long nlen;
+ rb_yield(RARRAY_AREF(ary, len));
+ nlen = RARRAY_LEN(ary);
+ if (nlen < len) {
+ len = nlen;
+ }
}
return ary;
}
@@ -2882,10 +2923,10 @@ recursive_join(VALUE obj, VALUE argp, int recur)
int *first = (int *)arg[3];
if (recur) {
- rb_raise(rb_eArgError, "recursive array join");
+ rb_raise(rb_eArgError, "recursive array join");
}
else {
- ary_join_1(obj, ary, sep, 0, result, first);
+ ary_join_1(obj, ary, sep, 0, result, first);
}
return Qnil;
}
@@ -2898,11 +2939,11 @@ ary_join_0(VALUE ary, VALUE sep, long max, VALUE result)
if (max > 0) rb_enc_copy(result, RARRAY_AREF(ary, 0));
for (i=0; i<max; i++) {
- val = RARRAY_AREF(ary, i);
+ val = RARRAY_AREF(ary, i);
if (!RB_TYPE_P(val, T_STRING)) break;
- if (i > 0 && !NIL_P(sep))
- rb_str_buf_append(result, sep);
- rb_str_buf_append(result, val);
+ if (i > 0 && !NIL_P(sep))
+ rb_str_buf_append(result, sep);
+ rb_str_buf_append(result, val);
}
return i;
}
@@ -2941,16 +2982,16 @@ ary_join_1(VALUE obj, VALUE ary, VALUE sep, long i, VALUE result, int *first)
VALUE val, tmp;
for (; i<RARRAY_LEN(ary); i++) {
- if (i > 0 && !NIL_P(sep))
- rb_str_buf_append(result, sep);
+ if (i > 0 && !NIL_P(sep))
+ rb_str_buf_append(result, sep);
- val = RARRAY_AREF(ary, i);
- if (RB_TYPE_P(val, T_STRING)) {
+ val = RARRAY_AREF(ary, i);
+ if (RB_TYPE_P(val, T_STRING)) {
ary_join_1_str(result, val, first);
- }
- else if (RB_TYPE_P(val, T_ARRAY)) {
+ }
+ else if (RB_TYPE_P(val, T_ARRAY)) {
ary_join_1_ary(val, ary, sep, result, val, first);
- }
+ }
else if (!NIL_P(tmp = rb_check_string_type(val))) {
ary_join_1_str(result, tmp, first);
}
@@ -2959,7 +3000,7 @@ ary_join_1(VALUE obj, VALUE ary, VALUE sep, long i, VALUE result, int *first)
}
else {
ary_join_1_str(result, rb_obj_as_string(val), first);
- }
+ }
}
}
@@ -2972,26 +3013,26 @@ rb_ary_join(VALUE ary, VALUE sep)
if (RARRAY_LEN(ary) == 0) return rb_usascii_str_new(0, 0);
if (!NIL_P(sep)) {
- StringValue(sep);
- len += RSTRING_LEN(sep) * (RARRAY_LEN(ary) - 1);
+ StringValue(sep);
+ len += RSTRING_LEN(sep) * (RARRAY_LEN(ary) - 1);
}
for (i=0; i<RARRAY_LEN(ary); i++) {
- val = RARRAY_AREF(ary, i);
- tmp = rb_check_string_type(val);
+ val = RARRAY_AREF(ary, i);
+ tmp = rb_check_string_type(val);
- if (NIL_P(tmp) || tmp != val) {
- int first;
+ if (NIL_P(tmp) || tmp != val) {
+ int first;
long n = RARRAY_LEN(ary);
if (i > n) i = n;
result = rb_str_buf_new(len + (n-i)*10);
- rb_enc_associate(result, rb_usascii_encoding());
+ rb_enc_associate(result, rb_usascii_encoding());
i = ary_join_0(ary, sep, i, result);
- first = i == 0;
- ary_join_1(ary, ary, sep, i, result, &first);
- return result;
- }
+ first = i == 0;
+ ary_join_1(ary, ary, sep, i, result, &first);
+ return result;
+ }
- len += RSTRING_LEN(tmp);
+ len += RSTRING_LEN(tmp);
}
result = rb_str_new(0, len);
@@ -3054,10 +3095,10 @@ inspect_ary(VALUE ary, VALUE dummy, int recur)
if (recur) return rb_usascii_str_new_cstr("[...]");
str = rb_str_buf_new2("[");
for (i=0; i<RARRAY_LEN(ary); i++) {
- s = rb_inspect(RARRAY_AREF(ary, i));
- if (i > 0) rb_str_buf_cat2(str, ", ");
- else rb_enc_copy(str, s);
- rb_str_buf_append(str, s);
+ s = rb_inspect(RARRAY_AREF(ary, i));
+ if (i > 0) rb_str_buf_cat2(str, ", ");
+ else rb_enc_copy(str, s);
+ rb_str_buf_append(str, s);
}
rb_str_buf_cat2(str, "]");
return str;
@@ -3114,9 +3155,9 @@ static VALUE
rb_ary_to_a(VALUE ary)
{
if (rb_obj_class(ary) != rb_cArray) {
- VALUE dup = rb_ary_new2(RARRAY_LEN(ary));
- rb_ary_replace(dup, ary);
- return dup;
+ VALUE dup = rb_ary_new2(RARRAY_LEN(ary));
+ rb_ary_replace(dup, ary);
+ return dup;
}
return ary;
}
@@ -3154,18 +3195,18 @@ rb_ary_to_h(VALUE ary)
int block_given = rb_block_given_p();
for (i=0; i<RARRAY_LEN(ary); i++) {
- const VALUE e = rb_ary_elt(ary, i);
- const VALUE elt = block_given ? rb_yield_force_blockarg(e) : e;
- const VALUE key_value_pair = rb_check_array_type(elt);
- if (NIL_P(key_value_pair)) {
- rb_raise(rb_eTypeError, "wrong element type %"PRIsVALUE" at %ld (expected array)",
- rb_obj_class(elt), i);
- }
- if (RARRAY_LEN(key_value_pair) != 2) {
- rb_raise(rb_eArgError, "wrong array length at %ld (expected 2, was %ld)",
- i, RARRAY_LEN(key_value_pair));
- }
- rb_hash_aset(hash, RARRAY_AREF(key_value_pair, 0), RARRAY_AREF(key_value_pair, 1));
+ const VALUE e = rb_ary_elt(ary, i);
+ const VALUE elt = block_given ? rb_yield_force_blockarg(e) : e;
+ const VALUE key_value_pair = rb_check_array_type(elt);
+ if (NIL_P(key_value_pair)) {
+ rb_raise(rb_eTypeError, "wrong element type %"PRIsVALUE" at %ld (expected array)",
+ rb_obj_class(elt), i);
+ }
+ if (RARRAY_LEN(key_value_pair) != 2) {
+ rb_raise(rb_eArgError, "wrong array length at %ld (expected 2, was %ld)",
+ i, RARRAY_LEN(key_value_pair));
+ }
+ rb_hash_aset(hash, RARRAY_AREF(key_value_pair, 0), RARRAY_AREF(key_value_pair, 1));
}
return hash;
}
@@ -3187,9 +3228,9 @@ static void
ary_reverse(VALUE *p1, VALUE *p2)
{
while (p1 < p2) {
- VALUE tmp = *p1;
- *p1++ = *p2;
- *p2-- = tmp;
+ VALUE tmp = *p1;
+ *p1++ = *p2;
+ *p2-- = tmp;
}
}
@@ -3204,7 +3245,7 @@ rb_ary_reverse(VALUE ary)
RARRAY_PTR_USE_TRANSIENT(ary, p1, {
p2 = p1 + len - 1; /* points last item */
ary_reverse(p1, p2);
- }); /* WB: no new reference */
+ }); /* WB: no new reference */
}
return ary;
}
@@ -3247,7 +3288,7 @@ rb_ary_reverse_m(VALUE ary)
if (len > 0) {
const VALUE *p1 = RARRAY_CONST_PTR_TRANSIENT(ary);
VALUE *p2 = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(dup) + len - 1;
- do *p2-- = *p1++; while (--len > 0);
+ do *p2-- = *p1++; while (--len > 0);
}
ARY_SET_LEN(dup, RARRAY_LEN(ary));
return dup;
@@ -3409,11 +3450,11 @@ rb_ary_rotate_m(int argc, VALUE *argv, VALUE ary)
len = RARRAY_LEN(ary);
rotated = rb_ary_new2(len);
if (len > 0) {
- cnt = rotate_count(cnt, len);
+ cnt = rotate_count(cnt, len);
ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
- len -= cnt;
- ary_memcpy(rotated, 0, len, ptr + cnt);
- ary_memcpy(rotated, len, cnt, ptr);
+ len -= cnt;
+ ary_memcpy(rotated, 0, len, ptr + cnt);
+ ary_memcpy(rotated, len, cnt, ptr);
}
ARY_SET_LEN(rotated, RARRAY_LEN(ary));
return rotated;
@@ -3422,14 +3463,13 @@ rb_ary_rotate_m(int argc, VALUE *argv, VALUE ary)
struct ary_sort_data {
VALUE ary;
VALUE receiver;
- struct cmp_opt_data cmp_opt;
};
static VALUE
sort_reentered(VALUE ary)
{
if (RBASIC(ary)->klass) {
- rb_raise(rb_eRuntimeError, "sort reentered");
+ rb_raise(rb_eRuntimeError, "sort reentered");
}
return Qnil;
}
@@ -3468,16 +3508,16 @@ sort_2(const void *ap, const void *bp, void *dummy)
VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp;
int n;
- if (FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(data->cmp_opt, Integer)) {
- if ((long)a > (long)b) return 1;
- if ((long)a < (long)b) return -1;
- return 0;
+ if (FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(INTEGER)) {
+ if ((long)a > (long)b) return 1;
+ if ((long)a < (long)b) return -1;
+ return 0;
}
- if (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(data->cmp_opt, String)) {
- return rb_str_cmp(a, b);
+ if (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(STRING)) {
+ return rb_str_cmp(a, b);
}
- if (RB_FLOAT_TYPE_P(a) && CMP_OPTIMIZABLE(data->cmp_opt, Float)) {
- return rb_float_cmp(a, b);
+ if (RB_FLOAT_TYPE_P(a) && CMP_OPTIMIZABLE(FLOAT)) {
+ return rb_float_cmp(a, b);
}
retval = rb_funcallv(a, id_cmp, 1, &b);
@@ -3534,25 +3574,23 @@ rb_ary_sort_bang(VALUE ary)
rb_ary_modify(ary);
assert(!ARY_SHARED_P(ary));
if (RARRAY_LEN(ary) > 1) {
- VALUE tmp = ary_make_substitution(ary); /* only ary refers tmp */
- struct ary_sort_data data;
- long len = RARRAY_LEN(ary);
- RBASIC_CLEAR_CLASS(tmp);
- data.ary = tmp;
+ VALUE tmp = ary_make_substitution(ary); /* only ary refers tmp */
+ struct ary_sort_data data;
+ long len = RARRAY_LEN(ary);
+ RBASIC_CLEAR_CLASS(tmp);
+ data.ary = tmp;
data.receiver = ary;
- data.cmp_opt.opt_methods = 0;
- data.cmp_opt.opt_inited = 0;
- RARRAY_PTR_USE(tmp, ptr, {
+ RARRAY_PTR_USE(tmp, ptr, {
ruby_qsort(ptr, len, sizeof(VALUE),
rb_block_given_p()?sort_1:sort_2, &data);
- }); /* WB: no new reference */
- rb_ary_modify(ary);
+ }); /* WB: no new reference */
+ rb_ary_modify(ary);
if (ARY_EMBED_P(tmp)) {
if (ARY_SHARED_P(ary)) { /* ary might be destructively operated in the given block */
rb_ary_unshare(ary);
- FL_SET_EMBED(ary);
+ FL_SET_EMBED(ary);
}
- ary_memcpy(ary, 0, ARY_EMBED_LEN(tmp), ARY_EMBED_PTR(tmp));
+ ary_memcpy(ary, 0, ARY_EMBED_LEN(tmp), ARY_EMBED_PTR(tmp));
ARY_SET_LEN(ary, ARY_EMBED_LEN(tmp));
}
else {
@@ -3657,7 +3695,7 @@ rb_ary_bsearch(VALUE ary)
VALUE index_result = rb_ary_bsearch_index(ary);
if (FIXNUM_P(index_result)) {
- return rb_ary_entry(ary, FIX2LONG(index_result));
+ return rb_ary_entry(ary, FIX2LONG(index_result));
}
return index_result;
}
@@ -3680,39 +3718,39 @@ rb_ary_bsearch_index(VALUE ary)
RETURN_ENUMERATOR(ary, 0, 0);
while (low < high) {
- mid = low + ((high - low) / 2);
- val = rb_ary_entry(ary, mid);
- v = rb_yield(val);
- if (FIXNUM_P(v)) {
- if (v == INT2FIX(0)) return INT2FIX(mid);
- smaller = (SIGNED_VALUE)v < 0; /* Fixnum preserves its sign-bit */
- }
- else if (v == Qtrue) {
- satisfied = 1;
- smaller = 1;
- }
- else if (!RTEST(v)) {
- smaller = 0;
- }
- else if (rb_obj_is_kind_of(v, rb_cNumeric)) {
- const VALUE zero = INT2FIX(0);
- switch (rb_cmpint(rb_funcallv(v, id_cmp, 1, &zero), v, zero)) {
- case 0: return INT2FIX(mid);
- case 1: smaller = 1; break;
- case -1: smaller = 0;
- }
- }
- else {
- rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE
- " (must be numeric, true, false or nil)",
- rb_obj_class(v));
- }
- if (smaller) {
- high = mid;
- }
- else {
- low = mid + 1;
- }
+ mid = low + ((high - low) / 2);
+ val = rb_ary_entry(ary, mid);
+ v = rb_yield(val);
+ if (FIXNUM_P(v)) {
+ if (v == INT2FIX(0)) return INT2FIX(mid);
+ smaller = (SIGNED_VALUE)v < 0; /* Fixnum preserves its sign-bit */
+ }
+ else if (v == Qtrue) {
+ satisfied = 1;
+ smaller = 1;
+ }
+ else if (!RTEST(v)) {
+ smaller = 0;
+ }
+ else if (rb_obj_is_kind_of(v, rb_cNumeric)) {
+ const VALUE zero = INT2FIX(0);
+ switch (rb_cmpint(rb_funcallv(v, id_cmp, 1, &zero), v, zero)) {
+ case 0: return INT2FIX(mid);
+ case 1: smaller = 0; break;
+ case -1: smaller = 1;
+ }
+ }
+ else {
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE
+ " (must be numeric, true, false or nil)",
+ rb_obj_class(v));
+ }
+ if (smaller) {
+ high = mid;
+ }
+ else {
+ low = mid + 1;
+ }
}
if (!satisfied) return Qnil;
return INT2FIX(low);
@@ -3827,7 +3865,7 @@ rb_ary_collect_bang(VALUE ary)
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
rb_ary_modify(ary);
for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_ary_store(ary, i, rb_yield(RARRAY_AREF(ary, i)));
+ rb_ary_store(ary, i, rb_yield(RARRAY_AREF(ary, i)));
}
return ary;
}
@@ -3839,21 +3877,21 @@ rb_get_values_at(VALUE obj, long olen, int argc, const VALUE *argv, VALUE (*func
long beg, len, i, j;
for (i=0; i<argc; i++) {
- if (FIXNUM_P(argv[i])) {
- rb_ary_push(result, (*func)(obj, FIX2LONG(argv[i])));
- continue;
- }
- /* check if idx is Range */
- if (rb_range_beg_len(argv[i], &beg, &len, olen, 1)) {
- long end = olen < beg+len ? olen : beg+len;
- for (j = beg; j < end; j++) {
- rb_ary_push(result, (*func)(obj, j));
- }
- if (beg + len > j)
- rb_ary_resize(result, RARRAY_LEN(result) + (beg + len) - j);
- continue;
- }
- rb_ary_push(result, (*func)(obj, NUM2LONG(argv[i])));
+ if (FIXNUM_P(argv[i])) {
+ rb_ary_push(result, (*func)(obj, FIX2LONG(argv[i])));
+ continue;
+ }
+ /* check if idx is Range */
+ if (rb_range_beg_len(argv[i], &beg, &len, olen, 1)) {
+ long end = olen < beg+len ? olen : beg+len;
+ for (j = beg; j < end; j++) {
+ rb_ary_push(result, (*func)(obj, j));
+ }
+ if (beg + len > j)
+ rb_ary_resize(result, RARRAY_LEN(result) + (beg + len) - j);
+ continue;
+ }
+ rb_ary_push(result, (*func)(obj, NUM2LONG(argv[i])));
}
return result;
}
@@ -3863,25 +3901,25 @@ append_values_at_single(VALUE result, VALUE ary, long olen, VALUE idx)
{
long beg, len;
if (FIXNUM_P(idx)) {
- beg = FIX2LONG(idx);
+ beg = FIX2LONG(idx);
}
/* check if idx is Range */
else if (rb_range_beg_len(idx, &beg, &len, olen, 1)) {
- if (len > 0) {
+ if (len > 0) {
const VALUE *const src = RARRAY_CONST_PTR_TRANSIENT(ary);
- const long end = beg + len;
- const long prevlen = RARRAY_LEN(result);
- if (beg < olen) {
- rb_ary_cat(result, src + beg, end > olen ? olen-beg : len);
- }
- if (end > olen) {
- rb_ary_store(result, prevlen + len - 1, Qnil);
- }
- }
- return result;
+ const long end = beg + len;
+ const long prevlen = RARRAY_LEN(result);
+ if (beg < olen) {
+ rb_ary_cat(result, src + beg, end > olen ? olen-beg : len);
+ }
+ if (end > olen) {
+ rb_ary_store(result, prevlen + len - 1, Qnil);
+ }
+ }
+ return result;
}
else {
- beg = NUM2LONG(idx);
+ beg = NUM2LONG(idx);
}
return rb_ary_push(result, rb_ary_entry(ary, beg));
}
@@ -3935,7 +3973,7 @@ rb_ary_values_at(int argc, VALUE *argv, VALUE ary)
long i, olen = RARRAY_LEN(ary);
VALUE result = rb_ary_new_capa(argc);
for (i = 0; i < argc; ++i) {
- append_values_at_single(result, ary, olen, argv[i]);
+ append_values_at_single(result, ary, olen, argv[i]);
}
RB_GC_GUARD(ary);
return result;
@@ -3972,9 +4010,9 @@ rb_ary_select(VALUE ary)
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
result = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
- if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
- rb_ary_push(result, rb_ary_elt(ary, i));
- }
+ if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
+ rb_ary_push(result, rb_ary_elt(ary, i));
+ }
}
return result;
}
@@ -3992,12 +4030,12 @@ select_bang_i(VALUE a)
long i1, i2;
for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); arg->len[0] = ++i1) {
- VALUE v = RARRAY_AREF(ary, i1);
- if (!RTEST(rb_yield(v))) continue;
- if (i1 != i2) {
- rb_ary_store(ary, i2, v);
- }
- arg->len[1] = ++i2;
+ VALUE v = RARRAY_AREF(ary, i1);
+ if (!RTEST(rb_yield(v))) continue;
+ if (i1 != i2) {
+ rb_ary_store(ary, i2, v);
+ }
+ arg->len[1] = ++i2;
}
return (i1 == i2) ? Qnil : ary;
}
@@ -4011,15 +4049,15 @@ select_bang_ensure(VALUE a)
long i1 = arg->len[0], i2 = arg->len[1];
if (i2 < len && i2 < i1) {
- long tail = 0;
+ long tail = 0;
rb_ary_modify(ary);
- if (i1 < len) {
- tail = len - i1;
+ if (i1 < len) {
+ tail = len - i1;
RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
- MEMMOVE(ptr + i2, ptr + i1, VALUE, tail);
- });
- }
- ARY_SET_LEN(ary, i2 + tail);
+ MEMMOVE(ptr + i2, ptr + i1, VALUE, tail);
+ });
+ }
+ ARY_SET_LEN(ary, i2 + tail);
}
return ary;
}
@@ -4091,11 +4129,11 @@ ary_resize_smaller(VALUE ary, long len)
{
rb_ary_modify(ary);
if (RARRAY_LEN(ary) > len) {
- ARY_SET_LEN(ary, len);
- if (len * 2 < ARY_CAPA(ary) &&
- ARY_CAPA(ary) > ARY_DEFAULT_SIZE) {
- ary_resize_capa(ary, len * 2);
- }
+ ARY_SET_LEN(ary, len);
+ if (len * 2 < ARY_CAPA(ary) &&
+ ARY_CAPA(ary) > ARY_DEFAULT_SIZE) {
+ ary_resize_capa(ary, len * 2);
+ }
}
}
@@ -4142,22 +4180,22 @@ rb_ary_delete(VALUE ary, VALUE item)
long i1, i2;
for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); i1++) {
- VALUE e = RARRAY_AREF(ary, i1);
+ VALUE e = RARRAY_AREF(ary, i1);
- if (rb_equal(e, item)) {
- v = e;
- continue;
- }
- if (i1 != i2) {
- rb_ary_store(ary, i2, e);
- }
- i2++;
+ if (rb_equal(e, item)) {
+ v = e;
+ continue;
+ }
+ if (i1 != i2) {
+ rb_ary_store(ary, i2, e);
+ }
+ i2++;
}
if (RARRAY_LEN(ary) == i2) {
- if (rb_block_given_p()) {
- return rb_yield(item);
- }
- return Qnil;
+ if (rb_block_given_p()) {
+ return rb_yield(item);
+ }
+ return Qnil;
}
ary_resize_smaller(ary, i2);
@@ -4172,18 +4210,18 @@ rb_ary_delete_same(VALUE ary, VALUE item)
long i1, i2;
for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); i1++) {
- VALUE e = RARRAY_AREF(ary, i1);
+ VALUE e = RARRAY_AREF(ary, i1);
- if (e == item) {
- continue;
- }
- if (i1 != i2) {
- rb_ary_store(ary, i2, e);
- }
- i2++;
+ if (e == item) {
+ continue;
+ }
+ if (i1 != i2) {
+ rb_ary_store(ary, i2, e);
+ }
+ i2++;
}
if (RARRAY_LEN(ary) == i2) {
- return;
+ return;
}
ary_resize_smaller(ary, i2);
@@ -4197,8 +4235,8 @@ rb_ary_delete_at(VALUE ary, long pos)
if (pos >= len) return Qnil;
if (pos < 0) {
- pos += len;
- if (pos < 0) return Qnil;
+ pos += len;
+ if (pos < 0) return Qnil;
}
rb_ary_modify(ary);
@@ -4350,23 +4388,23 @@ rb_ary_slice_bang(int argc, VALUE *argv, VALUE ary)
arg1 = argv[0];
if (argc == 2) {
- pos = NUM2LONG(argv[0]);
- len = NUM2LONG(argv[1]);
+ pos = NUM2LONG(argv[0]);
+ len = NUM2LONG(argv[1]);
return ary_slice_bang_by_rb_ary_splice(ary, pos, len);
}
if (!FIXNUM_P(arg1)) {
- switch (rb_range_beg_len(arg1, &pos, &len, RARRAY_LEN(ary), 0)) {
- case Qtrue:
- /* valid range */
+ switch (rb_range_beg_len(arg1, &pos, &len, RARRAY_LEN(ary), 0)) {
+ case Qtrue:
+ /* valid range */
return ary_slice_bang_by_rb_ary_splice(ary, pos, len);
- case Qnil:
- /* invalid range */
- return Qnil;
- default:
- /* not a range */
- break;
- }
+ case Qnil:
+ /* invalid range */
+ return Qnil;
+ default:
+ /* not a range */
+ break;
+ }
}
return rb_ary_delete_at(ary, NUM2LONG(arg1));
@@ -4378,11 +4416,11 @@ ary_reject(VALUE orig, VALUE result)
long i;
for (i = 0; i < RARRAY_LEN(orig); i++) {
- VALUE v = RARRAY_AREF(orig, i);
+ VALUE v = RARRAY_AREF(orig, i);
if (!RTEST(rb_yield(v))) {
- rb_ary_push(result, v);
- }
+ rb_ary_push(result, v);
+ }
}
return result;
}
@@ -4395,12 +4433,12 @@ reject_bang_i(VALUE a)
long i1, i2;
for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); arg->len[0] = ++i1) {
- VALUE v = RARRAY_AREF(ary, i1);
- if (RTEST(rb_yield(v))) continue;
- if (i1 != i2) {
- rb_ary_store(ary, i2, v);
- }
- arg->len[1] = ++i2;
+ VALUE v = RARRAY_AREF(ary, i1);
+ if (RTEST(rb_yield(v))) continue;
+ if (i1 != i2) {
+ rb_ary_store(ary, i2, v);
+ }
+ arg->len[1] = ++i2;
}
return (i1 == i2) ? Qnil : ary;
}
@@ -4521,9 +4559,9 @@ take_items(VALUE obj, long n)
if (!NIL_P(result)) return rb_ary_subseq(result, 0, n);
result = rb_ary_new2(n);
args[0] = result; args[1] = (VALUE)n;
- if (rb_check_block_call(obj, idEach, 0, 0, take_i, (VALUE)args) == Qundef)
- rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (must respond to :each)",
- rb_obj_class(obj));
+ if (UNDEF_P(rb_check_block_call(obj, idEach, 0, 0, take_i, (VALUE)args)))
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (must respond to :each)",
+ rb_obj_class(obj));
return result;
}
@@ -4592,51 +4630,51 @@ rb_ary_zip(int argc, VALUE *argv, VALUE ary)
VALUE result = Qnil;
for (i=0; i<argc; i++) {
- argv[i] = take_items(argv[i], len);
+ argv[i] = take_items(argv[i], len);
}
if (rb_block_given_p()) {
- int arity = rb_block_arity();
-
- if (arity > 1) {
- VALUE work, *tmp;
-
- tmp = ALLOCV_N(VALUE, work, argc+1);
-
- for (i=0; i<RARRAY_LEN(ary); i++) {
- tmp[0] = RARRAY_AREF(ary, i);
- for (j=0; j<argc; j++) {
- tmp[j+1] = rb_ary_elt(argv[j], i);
- }
- rb_yield_values2(argc+1, tmp);
- }
-
- if (work) ALLOCV_END(work);
- }
- else {
- for (i=0; i<RARRAY_LEN(ary); i++) {
- VALUE tmp = rb_ary_new2(argc+1);
-
- rb_ary_push(tmp, RARRAY_AREF(ary, i));
- for (j=0; j<argc; j++) {
- rb_ary_push(tmp, rb_ary_elt(argv[j], i));
- }
- rb_yield(tmp);
- }
- }
+ int arity = rb_block_arity();
+
+ if (arity > 1) {
+ VALUE work, *tmp;
+
+ tmp = ALLOCV_N(VALUE, work, argc+1);
+
+ for (i=0; i<RARRAY_LEN(ary); i++) {
+ tmp[0] = RARRAY_AREF(ary, i);
+ for (j=0; j<argc; j++) {
+ tmp[j+1] = rb_ary_elt(argv[j], i);
+ }
+ rb_yield_values2(argc+1, tmp);
+ }
+
+ if (work) ALLOCV_END(work);
+ }
+ else {
+ for (i=0; i<RARRAY_LEN(ary); i++) {
+ VALUE tmp = rb_ary_new2(argc+1);
+
+ rb_ary_push(tmp, RARRAY_AREF(ary, i));
+ for (j=0; j<argc; j++) {
+ rb_ary_push(tmp, rb_ary_elt(argv[j], i));
+ }
+ rb_yield(tmp);
+ }
+ }
}
else {
- result = rb_ary_new_capa(len);
+ result = rb_ary_new_capa(len);
- for (i=0; i<len; i++) {
- VALUE tmp = rb_ary_new_capa(argc+1);
+ for (i=0; i<len; i++) {
+ VALUE tmp = rb_ary_new_capa(argc+1);
- rb_ary_push(tmp, RARRAY_AREF(ary, i));
- for (j=0; j<argc; j++) {
- rb_ary_push(tmp, rb_ary_elt(argv[j], i));
- }
- rb_ary_push(result, tmp);
- }
+ rb_ary_push(tmp, RARRAY_AREF(ary, i));
+ for (j=0; j<argc; j++) {
+ rb_ary_push(tmp, rb_ary_elt(argv[j], i));
+ }
+ rb_ary_push(result, tmp);
+ }
}
return result;
@@ -4663,21 +4701,21 @@ rb_ary_transpose(VALUE ary)
alen = RARRAY_LEN(ary);
if (alen == 0) return rb_ary_dup(ary);
for (i=0; i<alen; i++) {
- tmp = to_ary(rb_ary_elt(ary, i));
- if (elen < 0) { /* first element */
- elen = RARRAY_LEN(tmp);
- result = rb_ary_new2(elen);
- for (j=0; j<elen; j++) {
- rb_ary_store(result, j, rb_ary_new2(alen));
- }
- }
- else if (elen != RARRAY_LEN(tmp)) {
- rb_raise(rb_eIndexError, "element size differs (%ld should be %ld)",
- RARRAY_LEN(tmp), elen);
- }
- for (j=0; j<elen; j++) {
- rb_ary_store(rb_ary_elt(result, j), i, rb_ary_elt(tmp, j));
- }
+ tmp = to_ary(rb_ary_elt(ary, i));
+ if (elen < 0) { /* first element */
+ elen = RARRAY_LEN(tmp);
+ result = rb_ary_new2(elen);
+ for (j=0; j<elen; j++) {
+ rb_ary_store(result, j, rb_ary_new2(alen));
+ }
+ }
+ else if (elen != RARRAY_LEN(tmp)) {
+ rb_raise(rb_eIndexError, "element size differs (%ld should be %ld)",
+ RARRAY_LEN(tmp), elen);
+ }
+ for (j=0; j<elen; j++) {
+ rb_ary_store(rb_ary_elt(result, j), i, rb_ary_elt(tmp, j));
+ }
}
return result;
}
@@ -4754,11 +4792,11 @@ rb_ary_clear(VALUE ary)
{
rb_ary_modify_check(ary);
if (ARY_SHARED_P(ary)) {
- if (!ARY_EMBED_P(ary)) {
- rb_ary_unshare(ary);
- FL_SET_EMBED(ary);
+ if (!ARY_EMBED_P(ary)) {
+ rb_ary_unshare(ary);
+ FL_SET_EMBED(ary);
ARY_SET_EMBED_LEN(ary, 0);
- }
+ }
}
else {
ARY_SET_LEN(ary, 0);
@@ -4973,59 +5011,59 @@ rb_ary_fill(int argc, VALUE *argv, VALUE ary)
long beg = 0, end = 0, len = 0;
if (rb_block_given_p()) {
- rb_scan_args(argc, argv, "02", &arg1, &arg2);
- argc += 1; /* hackish */
+ rb_scan_args(argc, argv, "02", &arg1, &arg2);
+ argc += 1; /* hackish */
}
else {
- rb_scan_args(argc, argv, "12", &item, &arg1, &arg2);
+ rb_scan_args(argc, argv, "12", &item, &arg1, &arg2);
}
switch (argc) {
case 1:
- beg = 0;
- len = RARRAY_LEN(ary);
- break;
+ beg = 0;
+ len = RARRAY_LEN(ary);
+ break;
case 2:
- if (rb_range_beg_len(arg1, &beg, &len, RARRAY_LEN(ary), 1)) {
- break;
- }
- /* fall through */
+ if (rb_range_beg_len(arg1, &beg, &len, RARRAY_LEN(ary), 1)) {
+ break;
+ }
+ /* fall through */
case 3:
- beg = NIL_P(arg1) ? 0 : NUM2LONG(arg1);
- if (beg < 0) {
- beg = RARRAY_LEN(ary) + beg;
- if (beg < 0) beg = 0;
- }
- len = NIL_P(arg2) ? RARRAY_LEN(ary) - beg : NUM2LONG(arg2);
- break;
+ beg = NIL_P(arg1) ? 0 : NUM2LONG(arg1);
+ if (beg < 0) {
+ beg = RARRAY_LEN(ary) + beg;
+ if (beg < 0) beg = 0;
+ }
+ len = NIL_P(arg2) ? RARRAY_LEN(ary) - beg : NUM2LONG(arg2);
+ break;
}
rb_ary_modify(ary);
if (len < 0) {
return ary;
}
if (beg >= ARY_MAX_SIZE || len > ARY_MAX_SIZE - beg) {
- rb_raise(rb_eArgError, "argument too big");
+ rb_raise(rb_eArgError, "argument too big");
}
end = beg + len;
if (RARRAY_LEN(ary) < end) {
- if (end >= ARY_CAPA(ary)) {
- ary_resize_capa(ary, end);
- }
- ary_mem_clear(ary, RARRAY_LEN(ary), end - RARRAY_LEN(ary));
- ARY_SET_LEN(ary, end);
+ if (end >= ARY_CAPA(ary)) {
+ ary_resize_capa(ary, end);
+ }
+ ary_mem_clear(ary, RARRAY_LEN(ary), end - RARRAY_LEN(ary));
+ ARY_SET_LEN(ary, end);
}
- if (item == Qundef) {
- VALUE v;
- long i;
+ if (UNDEF_P(item)) {
+ VALUE v;
+ long i;
- for (i=beg; i<end; i++) {
- v = rb_yield(LONG2NUM(i));
- if (i>=RARRAY_LEN(ary)) break;
- ARY_SET(ary, i, v);
- }
+ for (i=beg; i<end; i++) {
+ v = rb_yield(LONG2NUM(i));
+ if (i>=RARRAY_LEN(ary)) break;
+ ARY_SET(ary, i, v);
+ }
}
else {
- ary_memfill(ary, beg, len, item);
+ ary_memfill(ary, beg, len, item);
}
return ary;
}
@@ -5088,15 +5126,15 @@ rb_ary_concat_multi(int argc, VALUE *argv, VALUE ary)
rb_ary_modify_check(ary);
if (argc == 1) {
- rb_ary_concat(ary, argv[0]);
+ rb_ary_concat(ary, argv[0]);
}
else if (argc > 1) {
- int i;
- VALUE args = rb_ary_tmp_new(argc);
- for (i = 0; i < argc; i++) {
- rb_ary_concat(args, argv[i]);
- }
- ary_append(ary, args);
+ int i;
+ VALUE args = rb_ary_hidden_new(argc);
+ for (i = 0; i < argc; i++) {
+ rb_ary_concat(args, argv[i]);
+ }
+ ary_append(ary, args);
}
ary_verify(ary);
@@ -5136,19 +5174,19 @@ rb_ary_times(VALUE ary, VALUE times)
tmp = rb_check_string_type(times);
if (!NIL_P(tmp)) {
- return rb_ary_join(ary, tmp);
+ return rb_ary_join(ary, tmp);
}
len = NUM2LONG(times);
if (len == 0) {
ary2 = ary_new(rb_cArray, 0);
- goto out;
+ goto out;
}
if (len < 0) {
- rb_raise(rb_eArgError, "negative argument");
+ rb_raise(rb_eArgError, "negative argument");
}
if (ARY_MAX_SIZE/len < RARRAY_LEN(ary)) {
- rb_raise(rb_eArgError, "argument too big");
+ rb_raise(rb_eArgError, "argument too big");
}
len *= RARRAY_LEN(ary);
@@ -5158,8 +5196,8 @@ rb_ary_times(VALUE ary, VALUE times)
ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
t = RARRAY_LEN(ary);
if (0 < t) {
- ary_memcpy(ary2, 0, t, ptr);
- while (t <= len/2) {
+ ary_memcpy(ary2, 0, t, ptr);
+ while (t <= len/2) {
ary_memcpy(ary2, t, t, RARRAY_CONST_PTR_TRANSIENT(ary2));
t *= 2;
}
@@ -5193,10 +5231,10 @@ rb_ary_assoc(VALUE ary, VALUE key)
VALUE v;
for (i = 0; i < RARRAY_LEN(ary); ++i) {
- v = rb_check_array_type(RARRAY_AREF(ary, i));
- if (!NIL_P(v) && RARRAY_LEN(v) > 0 &&
- rb_equal(RARRAY_AREF(v, 0), key))
- return v;
+ v = rb_check_array_type(RARRAY_AREF(ary, i));
+ if (!NIL_P(v) && RARRAY_LEN(v) > 0 &&
+ rb_equal(RARRAY_AREF(v, 0), key))
+ return v;
}
return Qnil;
}
@@ -5223,11 +5261,11 @@ rb_ary_rassoc(VALUE ary, VALUE value)
VALUE v;
for (i = 0; i < RARRAY_LEN(ary); ++i) {
- v = RARRAY_AREF(ary, i);
- if (RB_TYPE_P(v, T_ARRAY) &&
- RARRAY_LEN(v) > 1 &&
- rb_equal(RARRAY_AREF(v, 1), value))
- return v;
+ v = RARRAY_AREF(ary, i);
+ if (RB_TYPE_P(v, T_ARRAY) &&
+ RARRAY_LEN(v) > 1 &&
+ rb_equal(RARRAY_AREF(v, 1), value))
+ return v;
}
return Qnil;
}
@@ -5246,22 +5284,22 @@ recursive_equal(VALUE ary1, VALUE ary2, int recur)
len1 = RARRAY_LEN(ary1);
for (i = 0; i < len1; i++) {
- if (*p1 != *p2) {
- if (rb_equal(*p1, *p2)) {
- len1 = RARRAY_LEN(ary1);
- if (len1 != RARRAY_LEN(ary2))
- return Qfalse;
- if (len1 < i)
- return Qtrue;
+ if (*p1 != *p2) {
+ if (rb_equal(*p1, *p2)) {
+ len1 = RARRAY_LEN(ary1);
+ if (len1 != RARRAY_LEN(ary2))
+ return Qfalse;
+ if (len1 < i)
+ return Qtrue;
p1 = RARRAY_CONST_PTR(ary1) + i;
p2 = RARRAY_CONST_PTR(ary2) + i;
- }
- else {
- return Qfalse;
- }
- }
- p1++;
- p2++;
+ }
+ else {
+ return Qfalse;
+ }
+ }
+ p1++;
+ p2++;
}
return Qtrue;
}
@@ -5289,10 +5327,10 @@ rb_ary_equal(VALUE ary1, VALUE ary2)
{
if (ary1 == ary2) return Qtrue;
if (!RB_TYPE_P(ary2, T_ARRAY)) {
- if (!rb_respond_to(ary2, idTo_ary)) {
- return Qfalse;
- }
- return rb_equal(ary2, ary1);
+ if (!rb_respond_to(ary2, idTo_ary)) {
+ return Qfalse;
+ }
+ return rb_equal(ary2, ary1);
}
if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return Qfalse;
if (RARRAY_CONST_PTR_TRANSIENT(ary1) == RARRAY_CONST_PTR_TRANSIENT(ary2)) return Qtrue;
@@ -5306,8 +5344,8 @@ recursive_eql(VALUE ary1, VALUE ary2, int recur)
if (recur) return Qtrue; /* Subtle! */
for (i=0; i<RARRAY_LEN(ary1); i++) {
- if (!rb_eql(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i)))
- return Qfalse;
+ if (!rb_eql(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i)))
+ return Qfalse;
}
return Qtrue;
}
@@ -5362,8 +5400,8 @@ rb_ary_hash(VALUE ary)
h = rb_hash_start(RARRAY_LEN(ary));
h = rb_hash_uint(h, (st_index_t)rb_ary_hash);
for (i=0; i<RARRAY_LEN(ary); i++) {
- n = rb_hash(RARRAY_AREF(ary, i));
- h = rb_hash_uint(h, NUM2LONG(n));
+ n = rb_hash(RARRAY_AREF(ary, i));
+ h = rb_hash_uint(h, NUM2LONG(n));
}
h = rb_hash_end(h);
return ST2FIX(h);
@@ -5387,10 +5425,10 @@ rb_ary_includes(VALUE ary, VALUE item)
VALUE e;
for (i=0; i<RARRAY_LEN(ary); i++) {
- e = RARRAY_AREF(ary, i);
- if (rb_equal(e, item)) {
- return Qtrue;
- }
+ e = RARRAY_AREF(ary, i);
+ if (rb_equal(e, item)) {
+ return Qtrue;
+ }
}
return Qfalse;
}
@@ -5402,10 +5440,10 @@ rb_ary_includes_by_eql(VALUE ary, VALUE item)
VALUE e;
for (i=0; i<RARRAY_LEN(ary); i++) {
- e = RARRAY_AREF(ary, i);
- if (rb_eql(item, e)) {
- return Qtrue;
- }
+ e = RARRAY_AREF(ary, i);
+ if (rb_eql(item, e)) {
+ return Qtrue;
+ }
}
return Qfalse;
}
@@ -5418,14 +5456,14 @@ recursive_cmp(VALUE ary1, VALUE ary2, int recur)
if (recur) return Qundef; /* Subtle! */
len = RARRAY_LEN(ary1);
if (len > RARRAY_LEN(ary2)) {
- len = RARRAY_LEN(ary2);
+ len = RARRAY_LEN(ary2);
}
for (i=0; i<len; i++) {
- VALUE e1 = rb_ary_elt(ary1, i), e2 = rb_ary_elt(ary2, i);
- VALUE v = rb_funcallv(e1, id_cmp, 1, &e2);
- if (v != INT2FIX(0)) {
- return v;
- }
+ VALUE e1 = rb_ary_elt(ary1, i), e2 = rb_ary_elt(ary2, i);
+ VALUE v = rb_funcallv(e1, id_cmp, 1, &e2);
+ if (v != INT2FIX(0)) {
+ return v;
+ }
}
return Qundef;
}
@@ -5471,7 +5509,7 @@ rb_ary_cmp(VALUE ary1, VALUE ary2)
if (NIL_P(ary2)) return Qnil;
if (ary1 == ary2) return INT2FIX(0);
v = rb_exec_recursive_paired(recursive_cmp, ary1, ary2, ary2);
- if (v != Qundef) return v;
+ if (!UNDEF_P(v)) return v;
len = RARRAY_LEN(ary1) - RARRAY_LEN(ary2);
if (len == 0) return INT2FIX(0);
if (len > 0) return INT2FIX(1);
@@ -5484,8 +5522,8 @@ ary_add_hash(VALUE hash, VALUE ary)
long i;
for (i=0; i<RARRAY_LEN(ary); i++) {
- VALUE elt = RARRAY_AREF(ary, i);
- rb_hash_add_new_element(hash, elt, elt);
+ VALUE elt = RARRAY_AREF(ary, i);
+ rb_hash_add_new_element(hash, elt, elt);
}
return hash;
}
@@ -5513,8 +5551,8 @@ ary_add_hash_by(VALUE hash, VALUE ary)
long i;
for (i = 0; i < RARRAY_LEN(ary); ++i) {
- VALUE v = rb_ary_elt(ary, i), k = rb_yield(v);
- rb_hash_add_new_element(hash, k, v);
+ VALUE v = rb_ary_elt(ary, i), k = rb_yield(v);
+ rb_hash_add_new_element(hash, k, v);
}
return hash;
}
@@ -5532,7 +5570,7 @@ ary_recycle_hash(VALUE hash)
assert(RBASIC_CLASS(hash) == 0);
if (RHASH_ST_TABLE_P(hash)) {
st_table *tbl = RHASH_ST_TABLE(hash);
- st_free_table(tbl);
+ st_free_table(tbl);
RHASH_ST_CLEAR(hash);
}
}
@@ -5553,7 +5591,7 @@ ary_recycle_hash(VALUE hash)
* Related: Array#difference.
*/
-static VALUE
+VALUE
rb_ary_diff(VALUE ary1, VALUE ary2)
{
VALUE ary3;
@@ -5565,18 +5603,18 @@ rb_ary_diff(VALUE ary1, VALUE ary2)
ary3 = rb_ary_new();
if (RARRAY_LEN(ary1) <= SMALL_ARRAY_LEN || RARRAY_LEN(ary2) <= SMALL_ARRAY_LEN) {
- for (i=0; i<RARRAY_LEN(ary1); i++) {
- VALUE elt = rb_ary_elt(ary1, i);
- if (rb_ary_includes_by_eql(ary2, elt)) continue;
- rb_ary_push(ary3, elt);
- }
- return ary3;
+ for (i=0; i<RARRAY_LEN(ary1); i++) {
+ VALUE elt = rb_ary_elt(ary1, i);
+ if (rb_ary_includes_by_eql(ary2, elt)) continue;
+ rb_ary_push(ary3, elt);
+ }
+ return ary3;
}
hash = ary_make_hash(ary2);
for (i=0; i<RARRAY_LEN(ary1); i++) {
if (rb_hash_stlike_lookup(hash, RARRAY_AREF(ary1, i), NULL)) continue;
- rb_ary_push(ary3, rb_ary_elt(ary1, i));
+ rb_ary_push(ary3, rb_ary_elt(ary1, i));
}
ary_recycle_hash(hash);
return ary3;
@@ -5666,23 +5704,23 @@ rb_ary_and(VALUE ary1, VALUE ary2)
if (RARRAY_LEN(ary1) == 0 || RARRAY_LEN(ary2) == 0) return ary3;
if (RARRAY_LEN(ary1) <= SMALL_ARRAY_LEN && RARRAY_LEN(ary2) <= SMALL_ARRAY_LEN) {
- for (i=0; i<RARRAY_LEN(ary1); i++) {
- v = RARRAY_AREF(ary1, i);
- if (!rb_ary_includes_by_eql(ary2, v)) continue;
- if (rb_ary_includes_by_eql(ary3, v)) continue;
- rb_ary_push(ary3, v);
- }
- return ary3;
+ for (i=0; i<RARRAY_LEN(ary1); i++) {
+ v = RARRAY_AREF(ary1, i);
+ if (!rb_ary_includes_by_eql(ary2, v)) continue;
+ if (rb_ary_includes_by_eql(ary3, v)) continue;
+ rb_ary_push(ary3, v);
+ }
+ return ary3;
}
hash = ary_make_hash(ary2);
for (i=0; i<RARRAY_LEN(ary1); i++) {
- v = RARRAY_AREF(ary1, i);
- vv = (st_data_t)v;
+ v = RARRAY_AREF(ary1, i);
+ vv = (st_data_t)v;
if (rb_hash_stlike_delete(hash, &vv, 0)) {
- rb_ary_push(ary3, v);
- }
+ rb_ary_push(ary3, v);
+ }
}
ary_recycle_hash(hash);
@@ -5775,10 +5813,10 @@ rb_ary_or(VALUE ary1, VALUE ary2)
ary2 = to_ary(ary2);
if (RARRAY_LEN(ary1) + RARRAY_LEN(ary2) <= SMALL_ARRAY_LEN) {
- ary3 = rb_ary_new();
+ ary3 = rb_ary_new();
rb_ary_union(ary3, ary1);
rb_ary_union(ary3, ary2);
- return ary3;
+ return ary3;
}
hash = ary_make_hash(ary1);
@@ -6022,7 +6060,6 @@ ary_max_opt_string(VALUE ary, long i, VALUE vmax)
static VALUE
rb_ary_max(int argc, VALUE *argv, VALUE ary)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE result = Qundef, v;
VALUE num;
long i;
@@ -6032,23 +6069,23 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary)
const long n = RARRAY_LEN(ary);
if (rb_block_given_p()) {
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- v = RARRAY_AREF(ary, i);
- if (result == Qundef || rb_cmpint(rb_yield_values(2, v, result), v, result) > 0) {
- result = v;
- }
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ v = RARRAY_AREF(ary, i);
+ if (UNDEF_P(result) || rb_cmpint(rb_yield_values(2, v, result), v, result) > 0) {
+ result = v;
+ }
+ }
}
else if (n > 0) {
result = RARRAY_AREF(ary, 0);
if (n > 1) {
- if (FIXNUM_P(result) && CMP_OPTIMIZABLE(cmp_opt, Integer)) {
+ if (FIXNUM_P(result) && CMP_OPTIMIZABLE(INTEGER)) {
return ary_max_opt_fixnum(ary, 1, result);
}
- else if (STRING_P(result) && CMP_OPTIMIZABLE(cmp_opt, String)) {
+ else if (STRING_P(result) && CMP_OPTIMIZABLE(STRING)) {
return ary_max_opt_string(ary, 1, result);
}
- else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(cmp_opt, Float)) {
+ else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(FLOAT)) {
return ary_max_opt_float(ary, 1, result);
}
else {
@@ -6056,7 +6093,7 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary)
}
}
}
- if (result == Qundef) return Qnil;
+ if (UNDEF_P(result)) return Qnil;
return result;
}
@@ -6191,7 +6228,6 @@ ary_min_opt_string(VALUE ary, long i, VALUE vmin)
static VALUE
rb_ary_min(int argc, VALUE *argv, VALUE ary)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE result = Qundef, v;
VALUE num;
long i;
@@ -6201,23 +6237,23 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary)
const long n = RARRAY_LEN(ary);
if (rb_block_given_p()) {
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- v = RARRAY_AREF(ary, i);
- if (result == Qundef || rb_cmpint(rb_yield_values(2, v, result), v, result) < 0) {
- result = v;
- }
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ v = RARRAY_AREF(ary, i);
+ if (UNDEF_P(result) || rb_cmpint(rb_yield_values(2, v, result), v, result) < 0) {
+ result = v;
+ }
+ }
}
else if (n > 0) {
result = RARRAY_AREF(ary, 0);
if (n > 1) {
- if (FIXNUM_P(result) && CMP_OPTIMIZABLE(cmp_opt, Integer)) {
+ if (FIXNUM_P(result) && CMP_OPTIMIZABLE(INTEGER)) {
return ary_min_opt_fixnum(ary, 1, result);
}
- else if (STRING_P(result) && CMP_OPTIMIZABLE(cmp_opt, String)) {
+ else if (STRING_P(result) && CMP_OPTIMIZABLE(STRING)) {
return ary_min_opt_string(ary, 1, result);
}
- else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(cmp_opt, Float)) {
+ else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(FLOAT)) {
return ary_min_opt_float(ary, 1, result);
}
else {
@@ -6225,7 +6261,7 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary)
}
}
}
- if (result == Qundef) return Qnil;
+ if (UNDEF_P(result)) return Qnil;
return result;
}
@@ -6307,19 +6343,19 @@ rb_ary_uniq_bang(VALUE ary)
if (RARRAY_LEN(ary) <= 1)
return Qnil;
if (rb_block_given_p())
- hash = ary_make_hash_by(ary);
+ hash = ary_make_hash_by(ary);
else
- hash = ary_make_hash(ary);
+ hash = ary_make_hash(ary);
hash_size = RHASH_SIZE(hash);
if (RARRAY_LEN(ary) == hash_size) {
- return Qnil;
+ return Qnil;
}
rb_ary_modify_check(ary);
ARY_SET_LEN(ary, 0);
if (ARY_SHARED_P(ary) && !ARY_EMBED_P(ary)) {
- rb_ary_unshare(ary);
- FL_SET_EMBED(ary);
+ rb_ary_unshare(ary);
+ FL_SET_EMBED(ary);
}
ary_resize_capa(ary, hash_size);
rb_hash_foreach(hash, push_value, ary);
@@ -6361,12 +6397,12 @@ rb_ary_uniq(VALUE ary)
uniq = rb_ary_dup(ary);
}
else if (rb_block_given_p()) {
- hash = ary_make_hash_by(ary);
- uniq = rb_hash_values(hash);
+ hash = ary_make_hash_by(ary);
+ uniq = rb_hash_values(hash);
}
else {
- hash = ary_make_hash(ary);
- uniq = rb_hash_values(hash);
+ hash = ary_make_hash(ary);
+ uniq = rb_hash_values(hash);
}
if (hash) {
ary_recycle_hash(hash);
@@ -6395,12 +6431,12 @@ rb_ary_compact_bang(VALUE ary)
end = p + RARRAY_LEN(ary);
while (t < end) {
- if (NIL_P(*t)) t++;
- else *p++ = *t++;
+ if (NIL_P(*t)) t++;
+ else *p++ = *t++;
}
n = p - RARRAY_CONST_PTR_TRANSIENT(ary);
if (RARRAY_LEN(ary) == n) {
- return Qnil;
+ return Qnil;
}
ary_resize_smaller(ary, n);
@@ -6458,25 +6494,25 @@ rb_ary_count(int argc, VALUE *argv, VALUE ary)
long i, n = 0;
if (rb_check_arity(argc, 0, 1) == 0) {
- VALUE v;
+ VALUE v;
- if (!rb_block_given_p())
- return LONG2NUM(RARRAY_LEN(ary));
+ if (!rb_block_given_p())
+ return LONG2NUM(RARRAY_LEN(ary));
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- v = RARRAY_AREF(ary, i);
- if (RTEST(rb_yield(v))) n++;
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ v = RARRAY_AREF(ary, i);
+ if (RTEST(rb_yield(v))) n++;
+ }
}
else {
VALUE obj = argv[0];
- if (rb_block_given_p()) {
- rb_warn("given block not used");
- }
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- if (rb_equal(RARRAY_AREF(ary, i), obj)) n++;
- }
+ if (rb_block_given_p()) {
+ rb_warn("given block not used");
+ }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ if (rb_equal(RARRAY_AREF(ary, i), obj)) n++;
+ }
}
return LONG2NUM(n);
@@ -6510,64 +6546,64 @@ flatten(VALUE ary, int level)
rb_ary_push(stack, LONG2NUM(i + 1));
if (level < 0) {
- vmemo = rb_hash_new();
- RBASIC_CLEAR_CLASS(vmemo);
- memo = st_init_numtable();
- rb_hash_st_table_set(vmemo, memo);
- st_insert(memo, (st_data_t)ary, (st_data_t)Qtrue);
- st_insert(memo, (st_data_t)tmp, (st_data_t)Qtrue);
+ vmemo = rb_hash_new();
+ RBASIC_CLEAR_CLASS(vmemo);
+ memo = st_init_numtable();
+ rb_hash_st_table_set(vmemo, memo);
+ st_insert(memo, (st_data_t)ary, (st_data_t)Qtrue);
+ st_insert(memo, (st_data_t)tmp, (st_data_t)Qtrue);
}
ary = tmp;
i = 0;
while (1) {
- while (i < RARRAY_LEN(ary)) {
- elt = RARRAY_AREF(ary, i++);
- if (level >= 0 && RARRAY_LEN(stack) / 2 >= level) {
- rb_ary_push(result, elt);
- continue;
- }
- tmp = rb_check_array_type(elt);
- if (RBASIC(result)->klass) {
- if (memo) {
- RB_GC_GUARD(vmemo);
- st_clear(memo);
- }
- rb_raise(rb_eRuntimeError, "flatten reentered");
- }
- if (NIL_P(tmp)) {
- rb_ary_push(result, elt);
- }
- else {
- if (memo) {
- id = (st_data_t)tmp;
- if (st_is_member(memo, id)) {
- st_clear(memo);
- rb_raise(rb_eArgError, "tried to flatten recursive array");
- }
- st_insert(memo, id, (st_data_t)Qtrue);
- }
- rb_ary_push(stack, ary);
- rb_ary_push(stack, LONG2NUM(i));
- ary = tmp;
- i = 0;
- }
- }
- if (RARRAY_LEN(stack) == 0) {
- break;
- }
- if (memo) {
- id = (st_data_t)ary;
- st_delete(memo, &id, 0);
- }
- tmp = rb_ary_pop(stack);
- i = NUM2LONG(tmp);
- ary = rb_ary_pop(stack);
+ while (i < RARRAY_LEN(ary)) {
+ elt = RARRAY_AREF(ary, i++);
+ if (level >= 0 && RARRAY_LEN(stack) / 2 >= level) {
+ rb_ary_push(result, elt);
+ continue;
+ }
+ tmp = rb_check_array_type(elt);
+ if (RBASIC(result)->klass) {
+ if (memo) {
+ RB_GC_GUARD(vmemo);
+ st_clear(memo);
+ }
+ rb_raise(rb_eRuntimeError, "flatten reentered");
+ }
+ if (NIL_P(tmp)) {
+ rb_ary_push(result, elt);
+ }
+ else {
+ if (memo) {
+ id = (st_data_t)tmp;
+ if (st_is_member(memo, id)) {
+ st_clear(memo);
+ rb_raise(rb_eArgError, "tried to flatten recursive array");
+ }
+ st_insert(memo, id, (st_data_t)Qtrue);
+ }
+ rb_ary_push(stack, ary);
+ rb_ary_push(stack, LONG2NUM(i));
+ ary = tmp;
+ i = 0;
+ }
+ }
+ if (RARRAY_LEN(stack) == 0) {
+ break;
+ }
+ if (memo) {
+ id = (st_data_t)ary;
+ st_delete(memo, &id, 0);
+ }
+ tmp = rb_ary_pop(stack);
+ i = NUM2LONG(tmp);
+ ary = rb_ary_pop(stack);
}
if (memo) {
- st_clear(memo);
+ st_clear(memo);
}
RBASIC_SET_CLASS(result, rb_cArray);
@@ -6618,7 +6654,7 @@ rb_ary_flatten_bang(int argc, VALUE *argv, VALUE ary)
result = flatten(ary, level);
if (result == ary) {
- return Qnil;
+ return Qnil;
}
if (!(mod = ARY_EMBED_P(result))) rb_obj_freeze(result);
rb_ary_replace(ary, result);
@@ -6689,16 +6725,16 @@ rb_ary_shuffle_bang(rb_execution_context_t *ec, VALUE ary, VALUE randgen)
rb_ary_modify(ary);
i = len = RARRAY_LEN(ary);
RARRAY_PTR_USE(ary, ptr, {
- while (i) {
- long j = RAND_UPTO(i);
- VALUE tmp;
+ while (i) {
+ long j = RAND_UPTO(i);
+ VALUE tmp;
if (len != RARRAY_LEN(ary) || ptr != RARRAY_CONST_PTR_TRANSIENT(ary)) {
rb_raise(rb_eRuntimeError, "modified during shuffle");
- }
- tmp = ptr[--i];
- ptr[i] = ptr[j];
- ptr[j] = tmp;
- }
+ }
+ tmp = ptr[--i];
+ ptr[i] = ptr[j];
+ ptr[j] = tmp;
+ }
}); /* WB: no new reference */
return ary;
}
@@ -6721,120 +6757,120 @@ ary_sample(rb_execution_context_t *ec, VALUE ary, VALUE randgen, VALUE nv, VALUE
len = RARRAY_LEN(ary);
if (!to_array) {
- if (len < 2)
- i = 0;
- else
- i = RAND_UPTO(len);
+ if (len < 2)
+ i = 0;
+ else
+ i = RAND_UPTO(len);
- return rb_ary_elt(ary, i);
+ return rb_ary_elt(ary, i);
}
n = NUM2LONG(nv);
if (n < 0) rb_raise(rb_eArgError, "negative sample number");
if (n > len) n = len;
if (n <= numberof(idx)) {
- for (i = 0; i < n; ++i) {
- rnds[i] = RAND_UPTO(len - i);
- }
+ for (i = 0; i < n; ++i) {
+ rnds[i] = RAND_UPTO(len - i);
+ }
}
k = len;
len = RARRAY_LEN(ary);
if (len < k && n <= numberof(idx)) {
- for (i = 0; i < n; ++i) {
- if (rnds[i] >= len) return rb_ary_new_capa(0);
- }
+ for (i = 0; i < n; ++i) {
+ if (rnds[i] >= len) return rb_ary_new_capa(0);
+ }
}
if (n > len) n = len;
switch (n) {
case 0:
- return rb_ary_new_capa(0);
+ return rb_ary_new_capa(0);
case 1:
- i = rnds[0];
- return rb_ary_new_from_args(1, RARRAY_AREF(ary, i));
+ i = rnds[0];
+ return rb_ary_new_from_args(1, RARRAY_AREF(ary, i));
case 2:
- i = rnds[0];
- j = rnds[1];
- if (j >= i) j++;
- return rb_ary_new_from_args(2, RARRAY_AREF(ary, i), RARRAY_AREF(ary, j));
+ i = rnds[0];
+ j = rnds[1];
+ if (j >= i) j++;
+ return rb_ary_new_from_args(2, RARRAY_AREF(ary, i), RARRAY_AREF(ary, j));
case 3:
- i = rnds[0];
- j = rnds[1];
- k = rnds[2];
- {
- long l = j, g = i;
- if (j >= i) l = i, g = ++j;
- if (k >= l && (++k >= g)) ++k;
- }
- return rb_ary_new_from_args(3, RARRAY_AREF(ary, i), RARRAY_AREF(ary, j), RARRAY_AREF(ary, k));
+ i = rnds[0];
+ j = rnds[1];
+ k = rnds[2];
+ {
+ long l = j, g = i;
+ if (j >= i) l = i, g = ++j;
+ if (k >= l && (++k >= g)) ++k;
+ }
+ return rb_ary_new_from_args(3, RARRAY_AREF(ary, i), RARRAY_AREF(ary, j), RARRAY_AREF(ary, k));
}
memo_threshold =
- len < 2560 ? len / 128 :
- len < 5120 ? len / 64 :
- len < 10240 ? len / 32 :
- len / 16;
+ len < 2560 ? len / 128 :
+ len < 5120 ? len / 64 :
+ len < 10240 ? len / 32 :
+ len / 16;
if (n <= numberof(idx)) {
- long sorted[numberof(idx)];
- sorted[0] = idx[0] = rnds[0];
- for (i=1; i<n; i++) {
- k = rnds[i];
- for (j = 0; j < i; ++j) {
- if (k < sorted[j]) break;
- ++k;
- }
- memmove(&sorted[j+1], &sorted[j], sizeof(sorted[0])*(i-j));
- sorted[j] = idx[i] = k;
- }
- result = rb_ary_new_capa(n);
+ long sorted[numberof(idx)];
+ sorted[0] = idx[0] = rnds[0];
+ for (i=1; i<n; i++) {
+ k = rnds[i];
+ for (j = 0; j < i; ++j) {
+ if (k < sorted[j]) break;
+ ++k;
+ }
+ memmove(&sorted[j+1], &sorted[j], sizeof(sorted[0])*(i-j));
+ sorted[j] = idx[i] = k;
+ }
+ result = rb_ary_new_capa(n);
RARRAY_PTR_USE_TRANSIENT(result, ptr_result, {
- for (i=0; i<n; i++) {
- ptr_result[i] = RARRAY_AREF(ary, idx[i]);
- }
- });
+ for (i=0; i<n; i++) {
+ ptr_result[i] = RARRAY_AREF(ary, idx[i]);
+ }
+ });
}
else if (n <= memo_threshold / 2) {
- long max_idx = 0;
+ long max_idx = 0;
#undef RUBY_UNTYPED_DATA_WARNING
#define RUBY_UNTYPED_DATA_WARNING 0
- VALUE vmemo = Data_Wrap_Struct(0, 0, st_free_table, 0);
- st_table *memo = st_init_numtable_with_size(n);
- DATA_PTR(vmemo) = memo;
- result = rb_ary_new_capa(n);
- RARRAY_PTR_USE(result, ptr_result, {
- for (i=0; i<n; i++) {
- long r = RAND_UPTO(len-i) + i;
- ptr_result[i] = r;
- if (r > max_idx) max_idx = r;
- }
- len = RARRAY_LEN(ary);
- if (len <= max_idx) n = 0;
- else if (n > len) n = len;
+ VALUE vmemo = Data_Wrap_Struct(0, 0, st_free_table, 0);
+ st_table *memo = st_init_numtable_with_size(n);
+ DATA_PTR(vmemo) = memo;
+ result = rb_ary_new_capa(n);
+ RARRAY_PTR_USE(result, ptr_result, {
+ for (i=0; i<n; i++) {
+ long r = RAND_UPTO(len-i) + i;
+ ptr_result[i] = r;
+ if (r > max_idx) max_idx = r;
+ }
+ len = RARRAY_LEN(ary);
+ if (len <= max_idx) n = 0;
+ else if (n > len) n = len;
RARRAY_PTR_USE_TRANSIENT(ary, ptr_ary, {
- for (i=0; i<n; i++) {
- long j2 = j = ptr_result[i];
- long i2 = i;
- st_data_t value;
- if (st_lookup(memo, (st_data_t)i, &value)) i2 = (long)value;
- if (st_lookup(memo, (st_data_t)j, &value)) j2 = (long)value;
- st_insert(memo, (st_data_t)j, (st_data_t)i2);
- ptr_result[i] = ptr_ary[j2];
- }
- });
- });
- DATA_PTR(vmemo) = 0;
- st_free_table(memo);
+ for (i=0; i<n; i++) {
+ long j2 = j = ptr_result[i];
+ long i2 = i;
+ st_data_t value;
+ if (st_lookup(memo, (st_data_t)i, &value)) i2 = (long)value;
+ if (st_lookup(memo, (st_data_t)j, &value)) j2 = (long)value;
+ st_insert(memo, (st_data_t)j, (st_data_t)i2);
+ ptr_result[i] = ptr_ary[j2];
+ }
+ });
+ });
+ DATA_PTR(vmemo) = 0;
+ st_free_table(memo);
}
else {
- result = rb_ary_dup(ary);
- RBASIC_CLEAR_CLASS(result);
- RB_GC_GUARD(ary);
- RARRAY_PTR_USE(result, ptr_result, {
- for (i=0; i<n; i++) {
- j = RAND_UPTO(len-i) + i;
- nv = ptr_result[j];
- ptr_result[j] = ptr_result[i];
- ptr_result[i] = nv;
- }
- });
- RBASIC_SET_CLASS_RAW(result, rb_cArray);
+ result = rb_ary_dup(ary);
+ RBASIC_CLEAR_CLASS(result);
+ RB_GC_GUARD(ary);
+ RARRAY_PTR_USE(result, ptr_result, {
+ for (i=0; i<n; i++) {
+ j = RAND_UPTO(len-i) + i;
+ nv = ptr_result[j];
+ ptr_result[j] = ptr_result[i];
+ ptr_result[i] = nv;
+ }
+ });
+ RBASIC_SET_CLASS_RAW(result, rb_cArray);
}
ARY_SET_LEN(result, n);
@@ -6853,7 +6889,7 @@ rb_ary_cycle_size(VALUE self, VALUE args, VALUE eobj)
long mul;
VALUE n = Qnil;
if (args && (RARRAY_LEN(args) > 0)) {
- n = RARRAY_AREF(args, 0);
+ n = RARRAY_AREF(args, 0);
}
if (RARRAY_LEN(self) == 0) return INT2FIX(0);
if (NIL_P(n)) return DBL2NUM(HUGE_VAL);
@@ -6920,9 +6956,6 @@ rb_ary_cycle(int argc, VALUE *argv, VALUE ary)
return Qnil;
}
-#define tmpary(n) rb_ary_tmp_new(n)
-#define tmpary_discard(a) (ary_discard(a), RBASIC_SET_CLASS_RAW(a, rb_cArray))
-
/*
* Build a ruby array of the corresponding values and yield it to the
* associated block.
@@ -6958,32 +6991,32 @@ permute0(const long n, const long r, long *const p, char *const used, const VALU
long i = 0, index = 0;
for (;;) {
- const char *const unused = memchr(&used[i], 0, n-i);
- if (!unused) {
- if (!index) break;
- i = p[--index]; /* pop index */
- used[i++] = 0; /* index unused */
- }
- else {
- i = unused - used;
- p[index] = i;
- used[i] = 1; /* mark index used */
- ++index;
- if (index < r-1) { /* if not done yet */
- p[index] = i = 0;
- continue;
- }
- for (i = 0; i < n; ++i) {
- if (used[i]) continue;
- p[index] = i;
- if (!yield_indexed_values(values, r, p)) {
- rb_raise(rb_eRuntimeError, "permute reentered");
- }
- }
- i = p[--index]; /* pop index */
- used[i] = 0; /* index unused */
- p[index] = ++i;
- }
+ const char *const unused = memchr(&used[i], 0, n-i);
+ if (!unused) {
+ if (!index) break;
+ i = p[--index]; /* pop index */
+ used[i++] = 0; /* index unused */
+ }
+ else {
+ i = unused - used;
+ p[index] = i;
+ used[i] = 1; /* mark index used */
+ ++index;
+ if (index < r-1) { /* if not done yet */
+ p[index] = i = 0;
+ continue;
+ }
+ for (i = 0; i < n; ++i) {
+ if (used[i]) continue;
+ p[index] = i;
+ if (!yield_indexed_values(values, r, p)) {
+ rb_raise(rb_eRuntimeError, "permute reentered");
+ }
+ }
+ i = p[--index]; /* pop index */
+ used[i] = 0; /* index unused */
+ p[index] = ++i;
+ }
}
}
@@ -6996,14 +7029,14 @@ descending_factorial(long from, long how_many)
{
VALUE cnt;
if (how_many > 0) {
- cnt = LONG2FIX(from);
- while (--how_many > 0) {
- long v = --from;
- cnt = rb_int_mul(cnt, LONG2FIX(v));
- }
+ cnt = LONG2FIX(from);
+ while (--how_many > 0) {
+ long v = --from;
+ cnt = rb_int_mul(cnt, LONG2FIX(v));
+ }
}
else {
- cnt = LONG2FIX(how_many == 0);
+ cnt = LONG2FIX(how_many == 0);
}
return cnt;
}
@@ -7014,18 +7047,18 @@ binomial_coefficient(long comb, long size)
VALUE r;
long i;
if (comb > size-comb) {
- comb = size-comb;
+ comb = size-comb;
}
if (comb < 0) {
- return LONG2FIX(0);
+ return LONG2FIX(0);
}
else if (comb == 0) {
- return LONG2FIX(1);
+ return LONG2FIX(1);
}
r = LONG2FIX(size);
for (i = 1; i < comb; ++i) {
- r = rb_int_mul(r, LONG2FIX(size - i));
- r = rb_int_idiv(r, LONG2FIX(i + 1));
+ r = rb_int_mul(r, LONG2FIX(size - i));
+ r = rb_int_idiv(r, LONG2FIX(i + 1));
}
return r;
}
@@ -7131,28 +7164,28 @@ rb_ary_permutation(int argc, VALUE *argv, VALUE ary)
r = NUM2LONG(argv[0]); /* Permutation size from argument */
if (r < 0 || n < r) {
- /* no permutations: yield nothing */
+ /* no permutations: yield nothing */
}
else if (r == 0) { /* exactly one permutation: the zero-length array */
- rb_yield(rb_ary_new2(0));
+ rb_yield(rb_ary_new2(0));
}
else if (r == 1) { /* this is a special, easy case */
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
+ }
}
else { /* this is the general case */
- volatile VALUE t0;
- long *p = ALLOCV_N(long, t0, r+roomof(n, sizeof(long)));
- char *used = (char*)(p + r);
- VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
- RBASIC_CLEAR_CLASS(ary0);
+ volatile VALUE t0;
+ long *p = ALLOCV_N(long, t0, r+roomof(n, sizeof(long)));
+ char *used = (char*)(p + r);
+ VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
+ RBASIC_CLEAR_CLASS(ary0);
- MEMZERO(used, char, n); /* initialize array */
+ MEMZERO(used, char, n); /* initialize array */
- permute0(n, r, p, used, ary0); /* compute and yield permutations */
- ALLOCV_END(t0);
- RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
+ permute0(n, r, p, used, ary0); /* compute and yield permutations */
+ ALLOCV_END(t0);
+ RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
}
return ary;
}
@@ -7165,16 +7198,16 @@ combinate0(const long len, const long n, long *const stack, const VALUE values)
MEMZERO(stack+1, long, n);
stack[0] = -1;
for (;;) {
- for (lev++; lev < n; lev++) {
- stack[lev+1] = stack[lev]+1;
- }
- if (!yield_indexed_values(values, n, stack+1)) {
- rb_raise(rb_eRuntimeError, "combination reentered");
- }
- do {
- if (lev == 0) return;
- stack[lev--]++;
- } while (stack[lev+1]+n == len+lev+1);
+ for (lev++; lev < n; lev++) {
+ stack[lev+1] = stack[lev]+1;
+ }
+ if (!yield_indexed_values(values, n, stack+1)) {
+ rb_raise(rb_eRuntimeError, "combination reentered");
+ }
+ do {
+ if (lev == 0) return;
+ stack[lev--]++;
+ } while (stack[lev+1]+n == len+lev+1);
}
}
@@ -7250,25 +7283,25 @@ rb_ary_combination(VALUE ary, VALUE num)
RETURN_SIZED_ENUMERATOR(ary, 1, &num, rb_ary_combination_size);
len = RARRAY_LEN(ary);
if (n < 0 || len < n) {
- /* yield nothing */
+ /* yield nothing */
}
else if (n == 0) {
- rb_yield(rb_ary_new2(0));
+ rb_yield(rb_ary_new2(0));
}
else if (n == 1) {
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
+ }
}
else {
- VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
- volatile VALUE t0;
- long *stack = ALLOCV_N(long, t0, n+1);
+ VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
+ volatile VALUE t0;
+ long *stack = ALLOCV_N(long, t0, n+1);
- RBASIC_CLEAR_CLASS(ary0);
- combinate0(len, n, stack, ary0);
- ALLOCV_END(t0);
- RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
+ RBASIC_CLEAR_CLASS(ary0);
+ combinate0(len, n, stack, ary0);
+ ALLOCV_END(t0);
+ RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
}
return ary;
}
@@ -7292,19 +7325,19 @@ rpermute0(const long n, const long r, long *const p, const VALUE values)
p[index] = i;
for (;;) {
- if (++index < r-1) {
- p[index] = i = 0;
- continue;
- }
- for (i = 0; i < n; ++i) {
- p[index] = i;
- if (!yield_indexed_values(values, r, p)) {
- rb_raise(rb_eRuntimeError, "repeated permute reentered");
- }
- }
- do {
- if (index <= 0) return;
- } while ((i = ++p[--index]) >= n);
+ if (++index < r-1) {
+ p[index] = i = 0;
+ continue;
+ }
+ for (i = 0; i < n; ++i) {
+ p[index] = i;
+ if (!yield_indexed_values(values, r, p)) {
+ rb_raise(rb_eRuntimeError, "repeated permute reentered");
+ }
+ }
+ do {
+ if (index <= 0) return;
+ } while ((i = ++p[--index]) >= n);
}
}
@@ -7315,10 +7348,10 @@ rb_ary_repeated_permutation_size(VALUE ary, VALUE args, VALUE eobj)
long k = NUM2LONG(RARRAY_AREF(args, 0));
if (k < 0) {
- return LONG2FIX(0);
+ return LONG2FIX(0);
}
if (n <= 0) {
- return LONG2FIX(!k);
+ return LONG2FIX(!k);
}
return rb_int_positive_pow(n, (unsigned long)k);
}
@@ -7398,25 +7431,25 @@ rb_ary_repeated_permutation(VALUE ary, VALUE num)
r = NUM2LONG(num); /* Permutation size from argument */
if (r < 0) {
- /* no permutations: yield nothing */
+ /* no permutations: yield nothing */
}
else if (r == 0) { /* exactly one permutation: the zero-length array */
- rb_yield(rb_ary_new2(0));
+ rb_yield(rb_ary_new2(0));
}
else if (r == 1) { /* this is a special, easy case */
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
+ }
}
else { /* this is the general case */
- volatile VALUE t0;
- long *p = ALLOCV_N(long, t0, r);
- VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
- RBASIC_CLEAR_CLASS(ary0);
+ volatile VALUE t0;
+ long *p = ALLOCV_N(long, t0, r);
+ VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
+ RBASIC_CLEAR_CLASS(ary0);
- rpermute0(n, r, p, ary0); /* compute and yield repeated permutations */
- ALLOCV_END(t0);
- RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
+ rpermute0(n, r, p, ary0); /* compute and yield repeated permutations */
+ ALLOCV_END(t0);
+ RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
}
return ary;
}
@@ -7428,19 +7461,19 @@ rcombinate0(const long n, const long r, long *const p, const long rest, const VA
p[index] = i;
for (;;) {
- if (++index < r-1) {
- p[index] = i;
- continue;
- }
- for (; i < n; ++i) {
- p[index] = i;
- if (!yield_indexed_values(values, r, p)) {
- rb_raise(rb_eRuntimeError, "repeated combination reentered");
- }
- }
- do {
- if (index <= 0) return;
- } while ((i = ++p[--index]) >= n);
+ if (++index < r-1) {
+ p[index] = i;
+ continue;
+ }
+ for (; i < n; ++i) {
+ p[index] = i;
+ if (!yield_indexed_values(values, r, p)) {
+ rb_raise(rb_eRuntimeError, "repeated combination reentered");
+ }
+ }
+ do {
+ if (index <= 0) return;
+ } while ((i = ++p[--index]) >= n);
}
}
@@ -7450,7 +7483,7 @@ rb_ary_repeated_combination_size(VALUE ary, VALUE args, VALUE eobj)
long n = RARRAY_LEN(ary);
long k = NUM2LONG(RARRAY_AREF(args, 0));
if (k == 0) {
- return LONG2FIX(1);
+ return LONG2FIX(1);
}
return binomial_coefficient(k, n + k - 1);
}
@@ -7527,28 +7560,28 @@ rb_ary_repeated_combination(VALUE ary, VALUE num)
RETURN_SIZED_ENUMERATOR(ary, 1, &num, rb_ary_repeated_combination_size); /* Return enumerator if no block */
len = RARRAY_LEN(ary);
if (n < 0) {
- /* yield nothing */
+ /* yield nothing */
}
else if (n == 0) {
- rb_yield(rb_ary_new2(0));
+ rb_yield(rb_ary_new2(0));
}
else if (n == 1) {
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
+ }
}
else if (len == 0) {
- /* yield nothing */
+ /* yield nothing */
}
else {
- volatile VALUE t0;
- long *p = ALLOCV_N(long, t0, n);
- VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
- RBASIC_CLEAR_CLASS(ary0);
+ volatile VALUE t0;
+ long *p = ALLOCV_N(long, t0, n);
+ VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
+ RBASIC_CLEAR_CLASS(ary0);
- rcombinate0(len, n, p, n, ary0); /* compute and yield repeated combinations */
- ALLOCV_END(t0);
- RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
+ rcombinate0(len, n, p, n, ary0); /* compute and yield repeated combinations */
+ ALLOCV_END(t0);
+ RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
}
return ary;
}
@@ -7617,7 +7650,7 @@ static VALUE
rb_ary_product(int argc, VALUE *argv, VALUE ary)
{
int n = argc+1; /* How many arrays we're operating on */
- volatile VALUE t0 = tmpary(n);
+ volatile VALUE t0 = rb_ary_hidden_new(n);
volatile VALUE t1 = Qundef;
VALUE *arrays = RARRAY_PTR(t0); /* The arrays we're computing the product of */
int *counters = ALLOCV_N(int, t1, n); /* The current position in each one */
@@ -7638,64 +7671,64 @@ rb_ary_product(int argc, VALUE *argv, VALUE ary)
/* Otherwise, allocate and fill in an array of results */
if (rb_block_given_p()) {
- /* Make defensive copies of arrays; exit if any is empty */
- for (i = 0; i < n; i++) {
- if (RARRAY_LEN(arrays[i]) == 0) goto done;
- arrays[i] = ary_make_shared_copy(arrays[i]);
- }
+ /* Make defensive copies of arrays; exit if any is empty */
+ for (i = 0; i < n; i++) {
+ if (RARRAY_LEN(arrays[i]) == 0) goto done;
+ arrays[i] = ary_make_shared_copy(arrays[i]);
+ }
}
else {
- /* Compute the length of the result array; return [] if any is empty */
- for (i = 0; i < n; i++) {
- long k = RARRAY_LEN(arrays[i]);
- if (k == 0) {
- result = rb_ary_new2(0);
- goto done;
- }
+ /* Compute the length of the result array; return [] if any is empty */
+ for (i = 0; i < n; i++) {
+ long k = RARRAY_LEN(arrays[i]);
+ if (k == 0) {
+ result = rb_ary_new2(0);
+ goto done;
+ }
if (MUL_OVERFLOW_LONG_P(resultlen, k))
- rb_raise(rb_eRangeError, "too big to product");
- resultlen *= k;
- }
- result = rb_ary_new2(resultlen);
+ rb_raise(rb_eRangeError, "too big to product");
+ resultlen *= k;
+ }
+ result = rb_ary_new2(resultlen);
}
for (;;) {
- int m;
- /* fill in one subarray */
- VALUE subarray = rb_ary_new2(n);
- for (j = 0; j < n; j++) {
- rb_ary_push(subarray, rb_ary_entry(arrays[j], counters[j]));
- }
-
- /* put it on the result array */
- if (NIL_P(result)) {
+ int m;
+ /* fill in one subarray */
+ VALUE subarray = rb_ary_new2(n);
+ for (j = 0; j < n; j++) {
+ rb_ary_push(subarray, rb_ary_entry(arrays[j], counters[j]));
+ }
+
+ /* put it on the result array */
+ if (NIL_P(result)) {
FL_SET(t0, RARRAY_SHARED_ROOT_FLAG);
- rb_yield(subarray);
+ rb_yield(subarray);
if (!FL_TEST(t0, RARRAY_SHARED_ROOT_FLAG)) {
- rb_raise(rb_eRuntimeError, "product reentered");
- }
- else {
+ rb_raise(rb_eRuntimeError, "product reentered");
+ }
+ else {
FL_UNSET(t0, RARRAY_SHARED_ROOT_FLAG);
- }
- }
- else {
- rb_ary_push(result, subarray);
- }
-
- /*
- * Increment the last counter. If it overflows, reset to 0
- * and increment the one before it.
- */
- m = n-1;
- counters[m]++;
- while (counters[m] == RARRAY_LEN(arrays[m])) {
- counters[m] = 0;
- /* If the first counter overflows, we are done */
- if (--m < 0) goto done;
- counters[m]++;
- }
+ }
+ }
+ else {
+ rb_ary_push(result, subarray);
+ }
+
+ /*
+ * Increment the last counter. If it overflows, reset to 0
+ * and increment the one before it.
+ */
+ m = n-1;
+ counters[m]++;
+ while (counters[m] == RARRAY_LEN(arrays[m])) {
+ counters[m] = 0;
+ /* If the first counter overflows, we are done */
+ if (--m < 0) goto done;
+ counters[m]++;
+ }
}
+
done:
- tmpary_discard(t0);
ALLOCV_END(t1);
return NIL_P(result) ? ary : result;
@@ -7724,7 +7757,7 @@ rb_ary_take(VALUE obj, VALUE n)
{
long len = NUM2LONG(n);
if (len < 0) {
- rb_raise(rb_eArgError, "attempt to take negative size");
+ rb_raise(rb_eArgError, "attempt to take negative size");
}
return rb_ary_subseq(obj, 0, len);
}
@@ -7759,7 +7792,7 @@ rb_ary_take_while(VALUE ary)
RETURN_ENUMERATOR(ary, 0, 0);
for (i = 0; i < RARRAY_LEN(ary); i++) {
- if (!RTEST(rb_yield(RARRAY_AREF(ary, i)))) break;
+ if (!RTEST(rb_yield(RARRAY_AREF(ary, i)))) break;
}
return rb_ary_take(ary, LONG2FIX(i));
}
@@ -7787,7 +7820,7 @@ rb_ary_drop(VALUE ary, VALUE n)
VALUE result;
long pos = NUM2LONG(n);
if (pos < 0) {
- rb_raise(rb_eArgError, "attempt to drop negative size");
+ rb_raise(rb_eArgError, "attempt to drop negative size");
}
result = rb_ary_subseq(ary, pos, RARRAY_LEN(ary));
@@ -7823,7 +7856,7 @@ rb_ary_drop_while(VALUE ary)
RETURN_ENUMERATOR(ary, 0, 0);
for (i = 0; i < RARRAY_LEN(ary); i++) {
- if (!RTEST(rb_yield(RARRAY_AREF(ary, i)))) break;
+ if (!RTEST(rb_yield(RARRAY_AREF(ary, i)))) break;
}
return rb_ary_drop(ary, LONG2FIX(i));
}
@@ -7872,9 +7905,9 @@ rb_ary_any_p(int argc, VALUE *argv, VALUE ary)
if (rb_block_given_p()) {
rb_warn("given block not used");
}
- for (i = 0; i < RARRAY_LEN(ary); ++i) {
- if (RTEST(rb_funcall(argv[0], idEqq, 1, RARRAY_AREF(ary, i)))) return Qtrue;
- }
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (RTEST(rb_funcall(argv[0], idEqq, 1, RARRAY_AREF(ary, i)))) return Qtrue;
+ }
}
else if (!rb_block_given_p()) {
for (i = 0; i < len; ++i) {
@@ -7882,9 +7915,9 @@ rb_ary_any_p(int argc, VALUE *argv, VALUE ary)
}
}
else {
- for (i = 0; i < RARRAY_LEN(ary); ++i) {
- if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) return Qtrue;
- }
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) return Qtrue;
+ }
}
return Qfalse;
}
@@ -8117,7 +8150,7 @@ finish_exact_sum(long n, VALUE r, VALUE v, int z)
{
if (n != 0)
v = rb_fix_plus(LONG2FIX(n), v);
- if (r != Qundef) {
+ if (!UNDEF_P(r)) {
v = rb_rational_plus(r, v);
}
else if (!n && z) {
@@ -8196,7 +8229,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
else if (RB_BIGNUM_TYPE_P(e))
v = rb_big_plus(e, v);
else if (RB_TYPE_P(e, T_RATIONAL)) {
- if (r == Qundef)
+ if (UNDEF_P(r))
r = e;
else
r = rb_rational_plus(r, e);
@@ -8706,7 +8739,7 @@ rb_ary_deconstruct(VALUE ary)
*
* - #pop: Removes and returns the last element.
* - #shift: Removes and returns the first element.
- * - #compact!: Removes all non-+nil+ elements.
+ * - #compact!: Removes all +nil+ elements.
* - #delete: Removes elements equal to a given object.
* - #delete_at: Removes the element at a given offset.
* - #delete_if: Removes elements specified by a given block.
diff --git a/ast.c b/ast.c
index 42d4126a5b..adb7287ed3 100644
--- a/ast.c
+++ b/ast.c
@@ -64,8 +64,8 @@ ast_new_internal(rb_ast_t *ast, const NODE *node)
return obj;
}
-static VALUE rb_ast_parse_str(VALUE str, VALUE keep_script_lines);
-static VALUE rb_ast_parse_file(VALUE path, VALUE keep_script_lines);
+static VALUE rb_ast_parse_str(VALUE str, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens);
+static VALUE rb_ast_parse_file(VALUE path, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens);
static VALUE
ast_parse_new(void)
@@ -85,31 +85,33 @@ ast_parse_done(rb_ast_t *ast)
}
static VALUE
-ast_s_parse(rb_execution_context_t *ec, VALUE module, VALUE str, VALUE keep_script_lines)
+ast_s_parse(rb_execution_context_t *ec, VALUE module, VALUE str, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
- return rb_ast_parse_str(str, keep_script_lines);
+ return rb_ast_parse_str(str, keep_script_lines, error_tolerant, keep_tokens);
}
static VALUE
-rb_ast_parse_str(VALUE str, VALUE keep_script_lines)
+rb_ast_parse_str(VALUE str, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
rb_ast_t *ast = 0;
StringValue(str);
VALUE vparser = ast_parse_new();
if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
+ if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
+ if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser);
ast = rb_parser_compile_string_path(vparser, Qnil, str, 1);
return ast_parse_done(ast);
}
static VALUE
-ast_s_parse_file(rb_execution_context_t *ec, VALUE module, VALUE path, VALUE keep_script_lines)
+ast_s_parse_file(rb_execution_context_t *ec, VALUE module, VALUE path, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
- return rb_ast_parse_file(path, keep_script_lines);
+ return rb_ast_parse_file(path, keep_script_lines, error_tolerant, keep_tokens);
}
static VALUE
-rb_ast_parse_file(VALUE path, VALUE keep_script_lines)
+rb_ast_parse_file(VALUE path, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
VALUE f;
rb_ast_t *ast = 0;
@@ -120,6 +122,8 @@ rb_ast_parse_file(VALUE path, VALUE keep_script_lines)
rb_funcall(f, rb_intern("set_encoding"), 2, rb_enc_from_encoding(enc), rb_str_new_cstr("-"));
VALUE vparser = ast_parse_new();
if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
+ if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
+ if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser);
ast = rb_parser_compile_file_path(vparser, Qnil, f, 1);
rb_io_close(f);
return ast_parse_done(ast);
@@ -139,13 +143,15 @@ lex_array(VALUE array, int index)
}
static VALUE
-rb_ast_parse_array(VALUE array, VALUE keep_script_lines)
+rb_ast_parse_array(VALUE array, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
rb_ast_t *ast = 0;
array = rb_check_array_type(array);
VALUE vparser = ast_parse_new();
if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
+ if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
+ if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser);
ast = rb_parser_compile_generic(vparser, lex_array, Qnil, array, 1);
return ast_parse_done(ast);
}
@@ -193,7 +199,24 @@ script_lines(VALUE path)
}
static VALUE
-ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script_lines)
+node_id_for_backtrace_location(rb_execution_context_t *ec, VALUE module, VALUE location)
+{
+ int node_id;
+
+ if (!rb_frame_info_p(location)) {
+ rb_raise(rb_eTypeError, "Thread::Backtrace::Location object expected");
+ }
+
+ node_id = rb_get_node_id_from_frame_info(location);
+ if (node_id == -1) {
+ return Qnil;
+ }
+
+ return INT2NUM(node_id);
+}
+
+static VALUE
+ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
VALUE node, lines = Qnil;
const rb_iseq_t *iseq;
@@ -232,13 +255,13 @@ ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script
}
if (!NIL_P(lines) || !NIL_P(lines = script_lines(path))) {
- node = rb_ast_parse_array(lines, keep_script_lines);
+ node = rb_ast_parse_array(lines, keep_script_lines, error_tolerant, keep_tokens);
}
else if (e_option) {
- node = rb_ast_parse_str(rb_e_script, keep_script_lines);
+ node = rb_ast_parse_str(rb_e_script, keep_script_lines, error_tolerant, keep_tokens);
}
else {
- node = rb_ast_parse_file(path, keep_script_lines);
+ node = rb_ast_parse_file(path, keep_script_lines, error_tolerant, keep_tokens);
}
return node_find(node, node_id);
@@ -645,6 +668,8 @@ node_children(rb_ast_t *ast, const NODE *node)
NEW_CHILD(ast, node->nd_pkwargs),
kwrest);
}
+ case NODE_ERROR:
+ return rb_ary_new_from_node_args(ast, 0);
case NODE_ARGS_AUX:
case NODE_LAST:
break;
@@ -699,6 +724,15 @@ ast_node_last_column(rb_execution_context_t *ec, VALUE self)
}
static VALUE
+ast_node_all_tokens(rb_execution_context_t *ec, VALUE self)
+{
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ return rb_ast_tokens(data->ast);
+}
+
+static VALUE
ast_node_inspect(rb_execution_context_t *ec, VALUE self)
{
VALUE str;
diff --git a/ast.rb b/ast.rb
index f866bd23e5..f3f72c747f 100644
--- a/ast.rb
+++ b/ast.rb
@@ -20,21 +20,47 @@
module RubyVM::AbstractSyntaxTree
# call-seq:
- # RubyVM::AbstractSyntaxTree.parse(string) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.parse(string, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
#
# Parses the given _string_ into an abstract syntax tree,
# returning the root node of that tree.
#
- # SyntaxError is raised if the given _string_ is invalid syntax.
- #
# RubyVM::AbstractSyntaxTree.parse("x = 1 + 2")
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-1:9>
- def self.parse string, keep_script_lines: false
- Primitive.ast_s_parse string, keep_script_lines
+ #
+ # If <tt>keep_script_lines: true</tt> option is provided, the text of the parsed
+ # source is associated with nodes and is available via Node#script_lines.
+ #
+ # If <tt>keep_tokens: true</tt> option is provided, Node#tokens are populated.
+ #
+ # SyntaxError is raised if the given _string_ is invalid syntax. To overwrite this
+ # behavior, <tt>error_tolerant: true</tt> can be provided. In this case, the parser
+ # will produce a tree where expressions with syntax errors would be represented by
+ # Node with <tt>type=:ERROR</tt>.
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1; p(x; y=2")
+ # # <internal:ast>:33:in `parse': syntax error, unexpected ';', expecting ')' (SyntaxError)
+ # # x = 1; p(x; y=2
+ # # ^
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1; p(x; y=2", error_tolerant: true)
+ # # (SCOPE@1:0-1:15
+ # # tbl: [:x, :y]
+ # # args: nil
+ # # body: (BLOCK@1:0-1:15 (LASGN@1:0-1:5 :x (LIT@1:4-1:5 1)) (ERROR@1:7-1:11) (LASGN@1:12-1:15 :y (LIT@1:14-1:15 2))))
+ # root.children.last.children
+ # # [(LASGN@1:0-1:5 :x (LIT@1:4-1:5 1)),
+ # # (ERROR@1:7-1:11),
+ # # (LASGN@1:12-1:15 :y (LIT@1:14-1:15 2))]
+ #
+ # Note that parsing continues even after the errored expresion.
+ #
+ def self.parse string, keep_script_lines: false, error_tolerant: false, keep_tokens: false
+ Primitive.ast_s_parse string, keep_script_lines, error_tolerant, keep_tokens
end
# call-seq:
- # RubyVM::AbstractSyntaxTree.parse_file(pathname) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.parse_file(pathname, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
#
# Reads the file from _pathname_, then parses it like ::parse,
# returning the root node of the abstract syntax tree.
@@ -44,13 +70,15 @@ module RubyVM::AbstractSyntaxTree
#
# RubyVM::AbstractSyntaxTree.parse_file("my-app/app.rb")
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-31:3>
- def self.parse_file pathname, keep_script_lines: false
- Primitive.ast_s_parse_file pathname, keep_script_lines
+ #
+ # See ::parse for explanation of keyword argument meaning and usage.
+ def self.parse_file pathname, keep_script_lines: false, error_tolerant: false, keep_tokens: false
+ Primitive.ast_s_parse_file pathname, keep_script_lines, error_tolerant, keep_tokens
end
# call-seq:
- # RubyVM::AbstractSyntaxTree.of(proc) -> RubyVM::AbstractSyntaxTree::Node
- # RubyVM::AbstractSyntaxTree.of(method) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.of(proc, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.of(method, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
#
# Returns AST nodes of the given _proc_ or _method_.
#
@@ -63,8 +91,25 @@ module RubyVM::AbstractSyntaxTree
#
# RubyVM::AbstractSyntaxTree.of(method(:hello))
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-3:3>
- def self.of body, keep_script_lines: false
- Primitive.ast_s_of body, keep_script_lines
+ #
+ # See ::parse for explanation of keyword argument meaning and usage.
+ def self.of body, keep_script_lines: false, error_tolerant: false, keep_tokens: false
+ Primitive.ast_s_of body, keep_script_lines, error_tolerant, keep_tokens
+ end
+
+ # call-seq:
+ # RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(backtrace_location) -> integer
+ #
+ # Returns the node id for the given backtrace location.
+ #
+ # begin
+ # raise
+ # rescue => e
+ # loc = e.backtrace_locations.first
+ # RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(loc)
+ # end # => 0
+ def self.node_id_for_backtrace_location backtrace_location
+ Primitive.node_id_for_backtrace_location backtrace_location
end
# RubyVM::AbstractSyntaxTree::Node instances are created by parse methods in
@@ -122,6 +167,47 @@ module RubyVM::AbstractSyntaxTree
end
# call-seq:
+ # node.tokens -> array
+ #
+ # Returns tokens corresponding to the location of the node.
+ # Returns +nil+ if +keep_tokens+ is not enabled when #parse method is called.
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true)
+ # root.tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
+ # root.tokens.map{_1[2]}.join # => "x = 1 + 2"
+ #
+ # Token is an array of:
+ #
+ # - id
+ # - token type
+ # - source code text
+ # - location [ first_lineno, first_column, last_lineno, last_column ]
+ def tokens
+ return nil unless all_tokens
+
+ all_tokens.each_with_object([]) do |token, a|
+ loc = token.last
+ if ([first_lineno, first_column] <=> [loc[0], loc[1]]) <= 0 &&
+ ([last_lineno, last_column] <=> [loc[2], loc[3]]) >= 0
+ a << token
+ end
+ end
+ end
+
+ # call-seq:
+ # node.all_tokens -> array
+ #
+ # Returns all tokens for the input script regardless the receiver node.
+ # Returns +nil+ if +keep_tokens+ is not enabled when #parse method is called.
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true)
+ # root.all_tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
+ # root.children[-1].all_tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
+ def all_tokens
+ Primitive.ast_node_all_tokens
+ end
+
+ # call-seq:
# node.children -> array
#
# Returns AST nodes under this one. Each kind of node
diff --git a/benchmark/array_sort_int.yml b/benchmark/array_sort_int.yml
new file mode 100644
index 0000000000..7b9027ebf7
--- /dev/null
+++ b/benchmark/array_sort_int.yml
@@ -0,0 +1,15 @@
+prelude: |
+ ary2 = 2.times.to_a.shuffle
+ ary10 = 10.times.to_a.shuffle
+ ary100 = 100.times.to_a.shuffle
+ ary1000 = 1000.times.to_a.shuffle
+ ary10000 = 10000.times.to_a.shuffle
+
+benchmark:
+ ary2.sort: ary2.sort
+ ary10.sort: ary10.sort
+ ary100.sort: ary100.sort
+ ary1000.sort: ary1000.sort
+ ary10000.sort: ary10000.sort
+
+loop_count: 10000
diff --git a/benchmark/buffer_each.yml b/benchmark/buffer_each.yml
new file mode 100644
index 0000000000..417941104e
--- /dev/null
+++ b/benchmark/buffer_each.yml
@@ -0,0 +1,27 @@
+prelude: |
+ # frozen_string_literal: true
+ Warning[:experimental] = false
+ string = "The quick brown fox jumped over the lazy dog."
+ array = string.bytes
+ buffer = IO::Buffer.for(string)
+benchmark:
+ string.each_byte: |
+ upcased = String.new
+ string.each_byte do |byte|
+ upcased << (byte ^ 32)
+ end
+ array.each: |
+ upcased = String.new
+ array.each do |byte|
+ upcased << (byte ^ 32)
+ end
+ buffer.each: |
+ upcased = String.new
+ buffer.each(:U8) do |offset, byte|
+ upcased << (byte ^ 32)
+ end
+ buffer.each_byte: |
+ upcased = String.new
+ buffer.each_byte do |byte|
+ upcased << (byte ^ 32)
+ end
diff --git a/benchmark/buffer_get.yml b/benchmark/buffer_get.yml
index e375dcf85d..9e1f99d64e 100644
--- a/benchmark/buffer_get.yml
+++ b/benchmark/buffer_get.yml
@@ -1,9 +1,25 @@
+prelude: |
+ # frozen_string_literal: true
+ Warning[:experimental] = false
+ string = "The quick brown fox jumped over the lazy dog."
+ buffer = IO::Buffer.for(string)
+ format = [:U32, :U32, :U32, :U32]
benchmark:
- - name: buffer.get
- prelude: buffer = IO::Buffer.new(32, IO::Buffer::MAPPED)
- script: buffer.get(:U32, 0)
- loop_count: 20000000
- - name: string.unpack
- prelude: string = "\0" * 32
- script: string.unpack("C")
- loop_count: 20000000
+ string.unpack1: |
+ [
+ string.unpack1("N"),
+ string.unpack1("N", offset: 4),
+ string.unpack1("N", offset: 8),
+ string.unpack1("N", offset: 12),
+ ]
+ buffer.get_value: |
+ [
+ buffer.get_value(:U32, 0),
+ buffer.get_value(:U32, 4),
+ buffer.get_value(:U32, 8),
+ buffer.get_value(:U32, 12),
+ ]
+ buffer.get_values: |
+ buffer.get_values(format, 0)
+ string.unpack: |
+ string.unpack("NNNN")
diff --git a/benchmark/cgi_escape_html.yml b/benchmark/cgi_escape_html.yml
index af6abd08ac..655be9d7d8 100644
--- a/benchmark/cgi_escape_html.yml
+++ b/benchmark/cgi_escape_html.yml
@@ -1,32 +1,23 @@
-prelude: require 'cgi/escape'
+prelude: |
+ # frozen_string_literal: true
+ require 'cgi/escape'
benchmark:
- - name: escape_html_blank
- prelude: str = ""
- script: CGI.escapeHTML(str)
+ - script: CGI.escapeHTML("")
loop_count: 20000000
- - name: escape_html_short_none
- prelude: str = "abcde"
- script: CGI.escapeHTML(str)
+ - script: CGI.escapeHTML("abcde")
loop_count: 20000000
- - name: escape_html_short_one
- prelude: str = "abcd<"
- script: CGI.escapeHTML(str)
+ - script: CGI.escapeHTML("abcd<")
loop_count: 20000000
- - name: escape_html_short_all
- prelude: str = "'&\"<>"
- script: CGI.escapeHTML(str)
+ - script: CGI.escapeHTML("'&\"<>")
loop_count: 5000000
- - name: escape_html_long_none
- prelude: str = "abcde" * 300
- script: CGI.escapeHTML(str)
+ - prelude: long_no_escape = "abcde" * 300
+ script: CGI.escapeHTML(long_no_escape)
loop_count: 1000000
- - name: escape_html_long_all
- prelude: str = "'&\"<>" * 10
- script: CGI.escapeHTML(str)
+ - prelude: long_all_escape = "'&\"<>" * 10
+ script: CGI.escapeHTML(long_all_escape)
loop_count: 1000000
- - name: escape_html_real
- prelude: | # http://example.com/
- str = <<~HTML
+ - prelude: | # http://example.com/
+ example_html = <<~HTML
<body>
<div>
<h1>Example Domain</h1>
@@ -36,5 +27,5 @@ benchmark:
</div>
</body>
HTML
- script: CGI.escapeHTML(str)
+ script: CGI.escapeHTML(example_html)
loop_count: 1000000
diff --git a/benchmark/enum_minmax.yml b/benchmark/enum_minmax.yml
new file mode 100644
index 0000000000..9d01731abb
--- /dev/null
+++ b/benchmark/enum_minmax.yml
@@ -0,0 +1,25 @@
+prelude: |
+ set2 = 2.times.to_a.shuffle.to_set
+ set10 = 10.times.to_a.shuffle.to_set
+ set100 = 100.times.to_a.shuffle.to_set
+ set1000 = 1000.times.to_a.shuffle.to_set
+ set10000 = 10000.times.to_a.shuffle.to_set
+
+benchmark:
+ set2.min: set2.min
+ set10.min: set10.min
+ set100.min: set100.min
+ set1000.min: set1000.min
+ set10000.min: set10000.min
+ set2.max: set2.max
+ set10.max: set10.max
+ set100.max: set100.max
+ set1000.max: set1000.max
+ set10000.max: set10000.max
+ set2.minmax: set2.minmax
+ set10.minmax: set10.minmax
+ set100.minmax: set100.minmax
+ set1000.minmax: set1000.minmax
+ set10000.minmax: set10000.minmax
+
+loop_count: 10000
diff --git a/benchmark/enum_sort.yml b/benchmark/enum_sort.yml
new file mode 100644
index 0000000000..6f26e748c6
--- /dev/null
+++ b/benchmark/enum_sort.yml
@@ -0,0 +1,15 @@
+prelude: |
+ set2 = 2.times.to_a.shuffle.to_set
+ set10 = 10.times.to_a.shuffle.to_set
+ set100 = 100.times.to_a.shuffle.to_set
+ set1000 = 1000.times.to_a.shuffle.to_set
+ set10000 = 10000.times.to_a.shuffle.to_set
+
+benchmark:
+ set2.sort_by: set2.sort_by { 0 }
+ set10.sort_by: set10.sort_by { 0 }
+ set100.sort_by: set100.sort_by { 0 }
+ set1000.sort_by: set1000.sort_by { 0 }
+ set10000.sort_by: set10000.sort_by { 0 }
+
+loop_count: 10000
diff --git a/benchmark/erb_escape_html.yml b/benchmark/erb_escape_html.yml
new file mode 100644
index 0000000000..ca28d756e7
--- /dev/null
+++ b/benchmark/erb_escape_html.yml
@@ -0,0 +1,31 @@
+prelude: |
+ # frozen_string_literal: true
+ require 'erb'
+benchmark:
+ - script: ERB::Util.html_escape("")
+ loop_count: 20000000
+ - script: ERB::Util.html_escape("abcde")
+ loop_count: 20000000
+ - script: ERB::Util.html_escape("abcd<")
+ loop_count: 20000000
+ - script: ERB::Util.html_escape("'&\"<>")
+ loop_count: 5000000
+ - prelude: long_no_escape = "abcde" * 300
+ script: ERB::Util.html_escape(long_no_escape)
+ loop_count: 1000000
+ - prelude: long_all_escape = "'&\"<>" * 10
+ script: ERB::Util.html_escape(long_all_escape)
+ loop_count: 1000000
+ - prelude: | # http://example.com/
+ example_html = <<~HTML
+ <body>
+ <div>
+ <h1>Example Domain</h1>
+ <p>This domain is established to be used for illustrative examples in documents. You may use this
+ domain in examples without prior coordination or asking for permission.</p>
+ <p><a href="http://www.iana.org/domains/example">More information...</a></p>
+ </div>
+ </body>
+ HTML
+ script: ERB::Util.html_escape(example_html)
+ loop_count: 1000000
diff --git a/benchmark/lib/benchmark_driver/runner/mjit.rb b/benchmark/lib/benchmark_driver/runner/mjit.rb
index 1d4693e8be..3a58a620de 100644
--- a/benchmark/lib/benchmark_driver/runner/mjit.rb
+++ b/benchmark/lib/benchmark_driver/runner/mjit.rb
@@ -16,7 +16,7 @@ class BenchmarkDriver::Runner::Mjit < BenchmarkDriver::Runner::Ips
job.prelude = "#{job.prelude}\n#{<<~EOS}"
if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
__bmdv_ruby_i = 0
- while __bmdv_ruby_i < 10000 # jit_min_calls
+ while __bmdv_ruby_i < 10000 # MJIT call threshold
#{job.script}
__bmdv_ruby_i += 1
end
diff --git a/benchmark/lib/benchmark_driver/runner/mjit_exec.rb b/benchmark/lib/benchmark_driver/runner/mjit_exec.rb
deleted file mode 100644
index eac3dfba84..0000000000
--- a/benchmark/lib/benchmark_driver/runner/mjit_exec.rb
+++ /dev/null
@@ -1,237 +0,0 @@
-require 'benchmark_driver/struct'
-require 'benchmark_driver/metric'
-require 'erb'
-
-# A special runner dedicated for measuring mjit_exec overhead.
-class BenchmarkDriver::Runner::MjitExec
- METRIC = BenchmarkDriver::Metric.new(name: 'Iteration per second', unit: 'i/s')
-
- # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job"
- Job = ::BenchmarkDriver::Struct.new(
- :name, # @param [String] name - This is mandatory for all runner
- :metrics, # @param [Array<BenchmarkDriver::Metric>]
- :num_methods, # @param [Integer] num_methods - The number of methods to be defined
- :loop_count, # @param [Integer] loop_count
- :from_jit, # @param [TrueClass,FalseClass] from_jit - Whether the mjit_exec() is from JIT or not
- :to_jit, # @param [TrueClass,FalseClass] to_jit - Whether the mjit_exec() is to JIT or not
- )
- # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
- class << JobParser = Module.new
- # @param [Array,String] num_methods
- # @param [Integer] loop_count
- # @param [TrueClass,FalseClass] from_jit
- # @param [TrueClass,FalseClass] to_jit
- def parse(num_methods:, loop_count:, from_jit:, to_jit:)
- if num_methods.is_a?(String)
- num_methods = eval(num_methods)
- end
-
- num_methods.map do |num|
- if num_methods.size > 1
- suffix = "[#{'%4d' % num}]"
- else
- suffix = "_#{num}"
- end
- Job.new(
- name: "mjit_exec_#{from_jit ? 'JT' : 'VM'}2#{to_jit ? 'JT' : 'VM'}#{suffix}",
- metrics: [METRIC],
- num_methods: num,
- loop_count: loop_count,
- from_jit: from_jit,
- to_jit: to_jit,
- )
- end
- end
- end
-
- # @param [BenchmarkDriver::Config::RunnerConfig] config
- # @param [BenchmarkDriver::Output] output
- # @param [BenchmarkDriver::Context] contexts
- def initialize(config:, output:, contexts:)
- @config = config
- @output = output
- @contexts = contexts
- end
-
- # This method is dynamically called by `BenchmarkDriver::JobRunner.run`
- # @param [Array<BenchmarkDriver::Runner::Peak::Job>] jobs
- def run(jobs)
- @output.with_benchmark do
- jobs.each do |job|
- @output.with_job(name: job.name) do
- @contexts.each do |context|
- result = BenchmarkDriver::Repeater.with_repeat(config: @config, larger_better: true, rest_on_average: :average) do
- run_benchmark(job, context: context)
- end
- value, duration = result.value
- @output.with_context(name: context.name, executable: context.executable, gems: context.gems, prelude: context.prelude) do
- @output.report(values: { METRIC => value }, duration: duration, loop_count: job.loop_count)
- end
- end
- end
- end
- end
- end
-
- private
-
- # @param [BenchmarkDriver::Runner::Ips::Job] job - loop_count is not nil
- # @param [BenchmarkDriver::Context] context
- # @return [BenchmarkDriver::Metrics]
- def run_benchmark(job, context:)
- if job.from_jit
- if job.to_jit
- benchmark = BenchmarkJT2JT.new(num_methods: job.num_methods, loop_count: job.loop_count)
- else
- raise NotImplementedError, "JT2VM is not implemented yet"
- end
- else
- if job.to_jit
- benchmark = BenchmarkVM2JT.new(num_methods: job.num_methods, loop_count: job.loop_count)
- else
- benchmark = BenchmarkVM2VM.new(num_methods: job.num_methods, loop_count: job.loop_count)
- end
- end
-
- duration = Tempfile.open(['benchmark_driver-result', '.txt']) do |f|
- with_script(benchmark.render(result: f.path)) do |path|
- opt = []
- if context.executable.command.any? { |c| c.start_with?('--jit') }
- opt << '--jit-min-calls=2'
- end
- IO.popen([*context.executable.command, '--disable-gems', *opt, path], &:read)
- if $?.success?
- Float(f.read)
- else
- BenchmarkDriver::Result::ERROR
- end
- end
- end
-
- [job.loop_count.to_f / duration, duration]
- end
-
- def with_script(script)
- if @config.verbose >= 2
- sep = '-' * 30
- $stdout.puts "\n\n#{sep}[Script begin]#{sep}\n#{script}#{sep}[Script end]#{sep}\n\n"
- end
-
- Tempfile.open(['benchmark_driver-', '.rb']) do |f|
- f.puts script
- f.close
- return yield(f.path)
- end
- end
-
- # @param [Integer] num_methods
- # @param [Integer] loop_count
- BenchmarkVM2VM = ::BenchmarkDriver::Struct.new(:num_methods, :loop_count) do
- # @param [String] result - A file to write result
- def render(result:)
- ERB.new(<<~EOS, trim_mode: '%').result(binding)
- % num_methods.times do |i|
- def a<%= i %>
- nil
- end
- % end
- RubyVM::MJIT.pause if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
-
- def vm
- t = Process.clock_gettime(Process::CLOCK_MONOTONIC)
- i = 0
- while i < <%= loop_count / 1000 %>
- % 1000.times do |i|
- a<%= i % num_methods %>
- % end
- i += 1
- end
- % (loop_count % 1000).times do |i|
- a<%= i % num_methods %>
- % end
- Process.clock_gettime(Process::CLOCK_MONOTONIC) - t
- end
-
- vm # warmup call cache
- File.write(<%= result.dump %>, vm)
- EOS
- end
- end
- private_constant :BenchmarkVM2VM
-
- # @param [Integer] num_methods
- # @param [Integer] loop_count
- BenchmarkVM2JT = ::BenchmarkDriver::Struct.new(:num_methods, :loop_count) do
- # @param [String] result - A file to write result
- def render(result:)
- ERB.new(<<~EOS, trim_mode: '%').result(binding)
- % num_methods.times do |i|
- def a<%= i %>
- nil
- end
- a<%= i %>
- a<%= i %> # --jit-min-calls=2
- % end
- RubyVM::MJIT.pause if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
-
- def vm
- t = Process.clock_gettime(Process::CLOCK_MONOTONIC)
- i = 0
- while i < <%= loop_count / 1000 %>
- % 1000.times do |i|
- a<%= i % num_methods %>
- % end
- i += 1
- end
- % (loop_count % 1000).times do |i|
- a<%= i % num_methods %>
- % end
- Process.clock_gettime(Process::CLOCK_MONOTONIC) - t
- end
-
- vm # warmup call cache
- File.write(<%= result.dump %>, vm)
- EOS
- end
- end
- private_constant :BenchmarkVM2JT
-
- # @param [Integer] num_methods
- # @param [Integer] loop_count
- BenchmarkJT2JT = ::BenchmarkDriver::Struct.new(:num_methods, :loop_count) do
- # @param [String] result - A file to write result
- def render(result:)
- ERB.new(<<~EOS, trim_mode: '%').result(binding)
- % num_methods.times do |i|
- def a<%= i %>
- nil
- end
- % end
-
- # You may need to:
- # * Increase `JIT_ISEQ_SIZE_THRESHOLD` to 10000000 in mjit.h
- # * Always return false in `inlinable_iseq_p()` of mjit_compile.c
- def jit
- t = Process.clock_gettime(Process::CLOCK_MONOTONIC)
- i = 0
- while i < <%= loop_count / 1000 %>
- % 1000.times do |i|
- a<%= i % num_methods %>
- % end
- i += 1
- end
- % (loop_count % 1000).times do |i|
- a<%= i % num_methods %>
- % end
- Process.clock_gettime(Process::CLOCK_MONOTONIC) - t
- end
-
- jit
- jit
- RubyVM::MJIT.pause if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
- File.write(<%= result.dump %>, jit)
- EOS
- end
- end
- private_constant :BenchmarkJT2JT
-end
diff --git a/benchmark/marshal_dump_load_integer.yml b/benchmark/marshal_dump_load_integer.yml
new file mode 100644
index 0000000000..78ebf823d2
--- /dev/null
+++ b/benchmark/marshal_dump_load_integer.yml
@@ -0,0 +1,22 @@
+prelude: |
+ smallint_array = 1000.times.map { |x| x }
+ bigint32_array = 1000.times.map { |x| x + 2**32 }
+ bigint64_array = 1000.times.map { |x| x + 2**64 }
+
+ smallint_dump = Marshal.dump(smallint_array)
+ bigint32_dump = Marshal.dump(bigint32_array)
+ bigint64_dump = Marshal.dump(bigint64_array)
+benchmark:
+ marshal_dump_integer_small: |
+ Marshal.dump(smallint_array)
+ marshal_dump_integer_over_32_bit: |
+ Marshal.dump(bigint32_array)
+ marshal_dump_integer_over_64_bit: |
+ Marshal.dump(bigint64_array)
+ marshal_load_integer_small: |
+ Marshal.load(smallint_dump)
+ marshal_load_integer_over_32_bit: |
+ Marshal.load(bigint32_dump)
+ marshal_load_integer_over_64_bit: |
+ Marshal.load(bigint64_dump)
+loop_count: 4000
diff --git a/benchmark/masgn.yml b/benchmark/masgn.yml
index 4be9333e23..31cb8ee4a3 100644
--- a/benchmark/masgn.yml
+++ b/benchmark/masgn.yml
@@ -1,7 +1,7 @@
prelude: |
a = [nil] * 3
b = Class.new{attr_writer :a, :b, :c}.new
- c, d, e, f = nil, nil, nil, nil
+ c = d = e = f = g = h = i = nil
benchmark:
array2_2: "c = (a[0], a[1] = 1, 2)"
array2_3: "c = (a[0], a[1] = 1, 2, 3)"
@@ -27,3 +27,27 @@ benchmark:
lvar2_3p: "(d, e = 1, 2, 3; nil)"
lvar3_2p: "(d, e, f = 1, 2; nil)"
lvar3_3p: "(d, e, f = 1, 2, 3; nil)"
+ array2_2lv: "c = (a[0], a[1] = g, h)"
+ array2_ilv: "c = (a[0], a[1] = g, h, i)"
+ arrayi_2lv: "c = (a[0], a[1], a[2] = g, h)"
+ arrayi_ilv: "c = (a[0], a[1], a[2] = g, h, i)"
+ attr2_2lv: "c = (b.a, b.b = g, h)"
+ attr2_ilv: "c = (b.a, b.b = g, h, i)"
+ attri_2lv: "c = (b.a, b.b, b.c = g, h)"
+ attri_ilv: "c = (b.a, b.b, b.c = g, h, i)"
+ lvar2_2lv: "c = (d, e = g, h)"
+ lvar2_ilv: "c = (d, e = g, h, i)"
+ lvari_2lv: "c = (d, e, f = g, h)"
+ lvari_ilv: "c = (d, e, f = g, h, i)"
+ array2_2plv: "(a[0], a[1] = g, h; nil)"
+ array2_iplv: "(a[0], a[1] = g, h, i; nil)"
+ arrayi_2plv: "(a[0], a[1], a[2] = g, h; nil)"
+ arrayi_iplv: "(a[0], a[1], a[2] = g, h, i; nil)"
+ attr2_2plv: "(b.a, b.b = g, h; nil)"
+ attr2_iplv: "(b.a, b.b = g, h, i; nil)"
+ attri_2plv: "(b.a, b.b, b.c = g, h; nil)"
+ attri_iplv: "(b.a, b.b, b.c = g, h, i; nil)"
+ lvar2_2plv: "(d, e = g, h; nil)"
+ lvar2_iplv: "(d, e = g, h, i; nil)"
+ lvari_2plv: "(d, e, f = g, h; nil)"
+ lvari_iplv: "(d, e, f = g, h, i; nil)"
diff --git a/benchmark/mjit_exec_jt2jt.yml b/benchmark/mjit_exec_jt2jt.yml
deleted file mode 100644
index 6c303c7a44..0000000000
--- a/benchmark/mjit_exec_jt2jt.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-type: lib/benchmark_driver/runner/mjit_exec
-num_methods: [1]
-#num_methods: (1..100).to_a + [200, 300, 400, 500, 600, 700, 800, 900, 1000]
-loop_count: 50000000
-from_jit: true
-to_jit: true
diff --git a/benchmark/mjit_exec_vm2jt.yml b/benchmark/mjit_exec_vm2jt.yml
deleted file mode 100644
index 764883f070..0000000000
--- a/benchmark/mjit_exec_vm2jt.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-type: lib/benchmark_driver/runner/mjit_exec
-num_methods: [1]
-#num_methods: (1..100).to_a + [200, 300, 400, 500, 600, 700, 800, 900, 1000]
-loop_count: 50000000
-from_jit: false
-to_jit: true
diff --git a/benchmark/mjit_exec_vm2vm.yml b/benchmark/mjit_exec_vm2vm.yml
deleted file mode 100644
index 030aa76c1c..0000000000
--- a/benchmark/mjit_exec_vm2vm.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-type: lib/benchmark_driver/runner/mjit_exec
-num_methods: [1]
-#num_methods: (1..100).to_a + [200, 300, 400, 500, 600, 700, 800, 900, 1000]
-loop_count: 50000000
-from_jit: false
-to_jit: false
diff --git a/benchmark/numeric_methods.yml b/benchmark/numeric_methods.yml
index 433c2268a3..1384902935 100644
--- a/benchmark/numeric_methods.yml
+++ b/benchmark/numeric_methods.yml
@@ -10,4 +10,20 @@ benchmark:
int.finite?
infinite?: |
int.infinite?
+ integer_real: |
+ int.real
+ float_real: |
+ flo.real
+ integr_imag: |
+ int.imag
+ float_imag: |
+ flo.imag
+ integer_conj: |
+ int.conj
+ float_conj: |
+ flo.conj
+ integer_numerator: |
+ int.numerator
+ integer_denominator: |
+ int.denominator
loop_count: 20000000
diff --git a/benchmark/range_min.yml b/benchmark/range_min.yml
new file mode 100644
index 0000000000..9e60dd7308
--- /dev/null
+++ b/benchmark/range_min.yml
@@ -0,0 +1,2 @@
+benchmark:
+ - (1..10).min
diff --git a/benchmark/so_nbody.rb b/benchmark/so_nbody.rb
index d6c5bb9e61..9884fc4edc 100644
--- a/benchmark/so_nbody.rb
+++ b/benchmark/so_nbody.rb
@@ -12,38 +12,38 @@ def _puts *args
end
class Planet
- attr_accessor :x, :y, :z, :vx, :vy, :vz, :mass
+ attr_accessor :x, :y, :z, :vx, :vy, :vz, :mass
- def initialize(x, y, z, vx, vy, vz, mass)
- @x, @y, @z = x, y, z
- @vx, @vy, @vz = vx * DAYS_PER_YEAR, vy * DAYS_PER_YEAR, vz * DAYS_PER_YEAR
- @mass = mass * SOLAR_MASS
- end
-
- def move_from_i(bodies, nbodies, dt, i)
- while i < nbodies
- b2 = bodies[i]
- dx = @x - b2.x
- dy = @y - b2.y
- dz = @z - b2.z
-
- distance = Math.sqrt(dx * dx + dy * dy + dz * dz)
- mag = dt / (distance * distance * distance)
- b_mass_mag, b2_mass_mag = @mass * mag, b2.mass * mag
-
- @vx -= dx * b2_mass_mag
- @vy -= dy * b2_mass_mag
- @vz -= dz * b2_mass_mag
- b2.vx += dx * b_mass_mag
- b2.vy += dy * b_mass_mag
- b2.vz += dz * b_mass_mag
- i += 1
+ def initialize(x, y, z, vx, vy, vz, mass)
+ @x, @y, @z = x, y, z
+ @vx, @vy, @vz = vx * DAYS_PER_YEAR, vy * DAYS_PER_YEAR, vz * DAYS_PER_YEAR
+ @mass = mass * SOLAR_MASS
end
- @x += dt * @vx
- @y += dt * @vy
- @z += dt * @vz
- end
+ def move_from_i(bodies, nbodies, dt, i)
+ while i < nbodies
+ b2 = bodies[i]
+ dx = @x - b2.x
+ dy = @y - b2.y
+ dz = @z - b2.z
+
+ distance = Math.sqrt(dx * dx + dy * dy + dz * dz)
+ mag = dt / (distance * distance * distance)
+ b_mass_mag, b2_mass_mag = @mass * mag, b2.mass * mag
+
+ @vx -= dx * b2_mass_mag
+ @vy -= dy * b2_mass_mag
+ @vz -= dz * b2_mass_mag
+ b2.vx += dx * b_mass_mag
+ b2.vy += dy * b_mass_mag
+ b2.vz += dz * b_mass_mag
+ i += 1
+ end
+
+ @x += dt * @vx
+ @y += dt * @vy
+ @z += dt * @vz
+ end
end
def energy(bodies)
diff --git a/benchmark/string_concat.yml b/benchmark/string_concat.yml
index 656bcd1cd7..e65c00cca9 100644
--- a/benchmark/string_concat.yml
+++ b/benchmark/string_concat.yml
@@ -1,8 +1,9 @@
prelude: |
CHUNK = "a" * 64
- BCHUNK = "a".b * 64
+ UCHUNK = "é" * 32
+ GC.disable # GC causes a lot of variance
benchmark:
- binary_concat_utf8: |
+ binary_concat_7bit: |
buffer = String.new(capacity: 4096, encoding: Encoding::BINARY)
buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
@@ -11,18 +12,8 @@ benchmark:
buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
- buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
- binary_concat_binary: |
- buffer = String.new(capacity: 4096, encoding: Encoding::BINARY)
- buffer << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK
- buffer << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK
- buffer << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK
- buffer << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK
- buffer << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK
- buffer << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK
- buffer << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK
- buffer << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK << BCHUNK
- utf8_concat_utf8: |
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ utf8_concat_7bit: |
buffer = String.new(capacity: 4096, encoding: Encoding::UTF_8)
buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
@@ -31,4 +22,24 @@ benchmark:
buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
- buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ utf8_concat_UTF8: |
+ buffer = String.new(capacity: 4096, encoding: Encoding::UTF_8)
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ interpolation: |
+ buffer = "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}"
diff --git a/benchmark/time_parse.yml b/benchmark/time_parse.yml
index a6d6948b9c..6060b58bc6 100644
--- a/benchmark/time_parse.yml
+++ b/benchmark/time_parse.yml
@@ -6,3 +6,5 @@ benchmark:
- Time.iso8601(iso8601)
- Time.parse(iso8601)
- Time.parse(inspect)
+ - Time.new(iso8601) rescue Time.iso8601(iso8601)
+ - Time.new(inspect) rescue Time.parse(inspect)
diff --git a/benchmark/vm_const.yml b/benchmark/vm_const.yml
index 6064d4eed0..8939ca0cd3 100644
--- a/benchmark/vm_const.yml
+++ b/benchmark/vm_const.yml
@@ -1,7 +1,13 @@
prelude: |
Const = 1
+ A = B = C = D = E = F = G = H = I = J = K = L = M = N = O = P = Q = R = S = T = U = V = W = X = Y = Z = 1
+ def foo
+ A; B; C; D; E; F; G; H; I; J; K; L; M; N; O; P; Q; R; S; T; U; V; W; X; Y; Z
+ end
benchmark:
vm_const: |
j = Const
k = Const
+ vm_const_many: |
+ foo
loop_count: 30000000
diff --git a/benchmark/vm_freezeobj.yml b/benchmark/vm_freezeobj.yml
new file mode 100644
index 0000000000..69a795a354
--- /dev/null
+++ b/benchmark/vm_freezeobj.yml
@@ -0,0 +1,6 @@
+prelude: |
+ objs = 100000.times.map { Object.new }
+benchmark:
+ vm_freeze_obj: |
+ objs.map(&:freeze)
+loop_count: 600
diff --git a/benchmark/vm_ivar_get.yml b/benchmark/vm_ivar_get.yml
new file mode 100644
index 0000000000..9174af6965
--- /dev/null
+++ b/benchmark/vm_ivar_get.yml
@@ -0,0 +1,37 @@
+prelude: |
+ class Example
+ def initialize
+ @v0 = 1
+ @v1 = 2
+ @v3 = 3
+ @levar = 1
+ end
+
+ def get_value_loop
+ sum = 0
+
+ i = 0
+ while i < 1000000
+ # 10 times to de-emphasize loop overhead
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ i += 1
+ end
+
+ return sum
+ end
+ end
+
+ obj = Example.new
+benchmark:
+ vm_ivar_get: |
+ obj.get_value_loop
+loop_count: 100
diff --git a/benchmark/vm_ivar_get_unintialized.yml b/benchmark/vm_ivar_get_unintialized.yml
new file mode 100644
index 0000000000..a1ccfb06ce
--- /dev/null
+++ b/benchmark/vm_ivar_get_unintialized.yml
@@ -0,0 +1,12 @@
+prelude: |
+ class Example
+ def read
+ @uninitialized
+ end
+ end
+
+ obj = Example.new
+benchmark:
+ vm_ivar_get_uninitialized: |
+ obj.read
+loop_count: 30000000
diff --git a/benchmark/vm_ivar_lazy_set.yml b/benchmark/vm_ivar_lazy_set.yml
new file mode 100644
index 0000000000..7372ffcfbc
--- /dev/null
+++ b/benchmark/vm_ivar_lazy_set.yml
@@ -0,0 +1,12 @@
+prelude: |
+ class Example
+ def lazy_set
+ @uninitialized ||= 123
+ end
+ end
+
+ objs = 10000000.times.map { Example.new }
+benchmark:
+ vm_ivar_lazy_set: |
+ objs.each(&:lazy_set)
+loop_count: 1
diff --git a/benchmark/vm_ivar_set_on_instance.yml b/benchmark/vm_ivar_set_on_instance.yml
new file mode 100644
index 0000000000..91857b7742
--- /dev/null
+++ b/benchmark/vm_ivar_set_on_instance.yml
@@ -0,0 +1,35 @@
+prelude: |
+ class TheClass
+ def initialize
+ @v0 = 1
+ @v1 = 2
+ @v3 = 3
+ @levar = 1
+ end
+
+ def set_value_loop
+ # 1M
+ i = 0
+ while i < 1000000
+ # 10 times to de-emphasize loop overhead
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ i += 1
+ end
+ end
+ end
+
+ obj = TheClass.new
+
+benchmark:
+ vm_ivar_set_on_instance: |
+ obj.set_value_loop
+loop_count: 100
diff --git a/benchmark/vm_lvar_cond_set.yml b/benchmark/vm_lvar_cond_set.yml
new file mode 100644
index 0000000000..1845f9d12e
--- /dev/null
+++ b/benchmark/vm_lvar_cond_set.yml
@@ -0,0 +1,8 @@
+benchmark:
+ vm_lvar_cond_set: |
+ a ||= 1
+ b ||= 1
+ c ||= 1
+ d ||= 1
+ nil
+loop_count: 30000000
diff --git a/bignum.c b/bignum.c
index 75651f9524..cb2c3b6f07 100644
--- a/bignum.c
+++ b/bignum.c
@@ -105,8 +105,8 @@ STATIC_ASSERT(sizeof_long_and_sizeof_bdigit, SIZEOF_BDIGIT % SIZEOF_LONG == 0);
#endif
#define BIGZEROP(x) (BIGNUM_LEN(x) == 0 || \
- (BDIGITS(x)[0] == 0 && \
- (BIGNUM_LEN(x) == 1 || bigzero_p(x))))
+ (BDIGITS(x)[0] == 0 && \
+ (BIGNUM_LEN(x) == 1 || bigzero_p(x))))
#define BIGSIZE(x) (BIGNUM_LEN(x) == 0 ? (size_t)0 : \
BDIGITS(x)[BIGNUM_LEN(x)-1] ? \
(size_t)(BIGNUM_LEN(x)*SIZEOF_BDIGIT - nlz(BDIGITS(x)[BIGNUM_LEN(x)-1])/CHAR_BIT) : \
@@ -419,9 +419,9 @@ bary_small_lshift(BDIGIT *zds, const BDIGIT *xds, size_t n, int shift)
assert(0 <= shift && shift < BITSPERDIG);
for (i=0; i<n; i++) {
- num = num | (BDIGIT_DBL)*xds++ << shift;
- *zds++ = BIGLO(num);
- num = BIGDN(num);
+ num = num | (BDIGIT_DBL)*xds++ << shift;
+ *zds++ = BIGLO(num);
+ num = BIGDN(num);
}
return BIGLO(num);
}
@@ -437,9 +437,9 @@ bary_small_rshift(BDIGIT *zds, const BDIGIT *xds, size_t n, int shift, BDIGIT hi
num = BIGUP(higher_bdigit);
for (i = 0; i < n; i++) {
BDIGIT x = xds[n - i - 1];
- num = (num | x) >> shift;
+ num = (num | x) >> shift;
zds[n - i - 1] = BIGLO(num);
- num = BIGUP(x);
+ num = BIGUP(x);
}
}
@@ -449,7 +449,7 @@ bary_zero_p(const BDIGIT *xds, size_t xn)
if (xn == 0)
return 1;
do {
- if (xds[--xn]) return 0;
+ if (xds[--xn]) return 0;
} while (xn);
return 1;
}
@@ -977,7 +977,7 @@ integer_unpack_num_bdigits_small(size_t numwords, size_t wordsize, size_t nails,
{
/* nlp_bits stands for number of leading padding bits */
size_t num_bits = (wordsize * CHAR_BIT - nails) * numwords;
- size_t num_bdigits = (num_bits + BITSPERDIG - 1) / BITSPERDIG;
+ size_t num_bdigits = roomof(num_bits, BITSPERDIG);
*nlp_bits_ret = (int)(num_bdigits * BITSPERDIG - num_bits);
return num_bdigits;
}
@@ -987,7 +987,7 @@ integer_unpack_num_bdigits_generic(size_t numwords, size_t wordsize, size_t nail
{
/* BITSPERDIG = SIZEOF_BDIGIT * CHAR_BIT */
/* num_bits = (wordsize * CHAR_BIT - nails) * numwords */
- /* num_bdigits = (num_bits + BITSPERDIG - 1) / BITSPERDIG */
+ /* num_bdigits = roomof(num_bits, BITSPERDIG) */
/* num_bits = CHAR_BIT * (wordsize * numwords) - nails * numwords = CHAR_BIT * num_bytes1 - nails * numwords */
size_t num_bytes1 = wordsize * numwords;
@@ -1350,9 +1350,9 @@ bary_subb(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yd
num = borrow ? -1 : 0;
for (i = 0; i < sn; i++) {
- num += (BDIGIT_DBL_SIGNED)xds[i] - yds[i];
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ num += (BDIGIT_DBL_SIGNED)xds[i] - yds[i];
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
if (yn <= xn) {
for (; i < xn; i++) {
@@ -1371,7 +1371,7 @@ bary_subb(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yd
}
if (num == 0) goto num_is_zero;
for (; i < zn; i++) {
- zds[i] = BDIGMAX;
+ zds[i] = BDIGMAX;
}
return 1;
@@ -1379,10 +1379,10 @@ bary_subb(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yd
if (xds == zds && xn == zn)
return 0;
for (; i < xn; i++) {
- zds[i] = xds[i];
+ zds[i] = xds[i];
}
for (; i < zn; i++) {
- zds[i] = 0;
+ zds[i] = 0;
}
return 0;
}
@@ -1409,27 +1409,27 @@ bary_addc(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yd
assert(yn <= zn);
if (xn > yn) {
- const BDIGIT *tds;
- tds = xds; xds = yds; yds = tds;
- i = xn; xn = yn; yn = i;
+ const BDIGIT *tds;
+ tds = xds; xds = yds; yds = tds;
+ i = xn; xn = yn; yn = i;
}
num = carry ? 1 : 0;
for (i = 0; i < xn; i++) {
- num += (BDIGIT_DBL)xds[i] + yds[i];
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ num += (BDIGIT_DBL)xds[i] + yds[i];
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
for (; i < yn; i++) {
if (num == 0) goto num_is_zero;
- num += yds[i];
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ num += yds[i];
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
for (; i < zn; i++) {
if (num == 0) goto num_is_zero;
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
return num != 0;
@@ -1437,10 +1437,10 @@ bary_addc(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yd
if (yds == zds && yn == zn)
return 0;
for (; i < yn; i++) {
- zds[i] = yds[i];
+ zds[i] = yds[i];
}
for (; i < zn; i++) {
- zds[i] = 0;
+ zds[i] = 0;
}
return 0;
}
@@ -1597,30 +1597,30 @@ bary_sq_fast(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn)
return;
for (i = 0; i < xn-1; i++) {
- v = (BDIGIT_DBL)xds[i];
- if (!v)
+ v = (BDIGIT_DBL)xds[i];
+ if (!v)
continue;
- c = (BDIGIT_DBL)zds[i + i] + v * v;
- zds[i + i] = BIGLO(c);
- c = BIGDN(c);
- v *= 2;
+ c = (BDIGIT_DBL)zds[i + i] + v * v;
+ zds[i + i] = BIGLO(c);
+ c = BIGDN(c);
+ v *= 2;
vl = BIGLO(v);
vh = (int)BIGDN(v);
- for (j = i + 1; j < xn; j++) {
- w = (BDIGIT_DBL)xds[j];
- c += (BDIGIT_DBL)zds[i + j] + vl * w;
- zds[i + j] = BIGLO(c);
- c = BIGDN(c);
- if (vh)
+ for (j = i + 1; j < xn; j++) {
+ w = (BDIGIT_DBL)xds[j];
+ c += (BDIGIT_DBL)zds[i + j] + vl * w;
+ zds[i + j] = BIGLO(c);
+ c = BIGDN(c);
+ if (vh)
c += w;
- }
- if (c) {
- c += (BDIGIT_DBL)zds[i + xn];
- zds[i + xn] = BIGLO(c);
- c = BIGDN(c);
+ }
+ if (c) {
+ c += (BDIGIT_DBL)zds[i + xn];
+ zds[i + xn] = BIGLO(c);
+ c = BIGDN(c);
if (c)
zds[i + xn + 1] += (BDIGIT)c;
- }
+ }
}
/* i == xn-1 */
@@ -1710,7 +1710,7 @@ bary_mul_balance_with_mulfunc(BDIGIT *const zds, const size_t zn,
zds + n, tn,
wds, xn);
}
- n += r;
+ n += r;
}
BDIGITS_ZERO(zds+xn+yn, zn - (xn+yn));
@@ -2102,21 +2102,21 @@ bary_mul_toom3(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGI
v3n = u3n; v3ds = u3ds; v3p = u3p;
}
else {
- /* v1 <- y0 + y2 */
+ /* v1 <- y0 + y2 */
bary_add(v1ds, v1n, y0ds, y0n, y2ds, y2n);
v1p = 1;
- /* y(-1) : v2 <- v1 - y1 = y0 - y1 + y2 */
+ /* y(-1) : v2 <- v1 - y1 = y0 - y1 + y2 */
v2p = 1;
if (bary_sub(v2ds, v2n, v1ds, v1n, y1ds, y1n)) {
bary_2comp(v2ds, v2n);
v2p = 0;
}
- /* y(1) : v1 <- v1 + y1 = y0 + y1 + y2 */
+ /* y(1) : v1 <- v1 + y1 = y0 + y1 + y2 */
bary_add(v1ds, v1n, v1ds, v1n, y1ds, y1n);
- /* y(-2) : v3 <- 2 * (v2 + y2) - y0 = y0 - 2 * (y1 - 2 * y2) */
+ /* y(-2) : v3 <- 2 * (v2 + y2) - y0 = y0 - 2 * (y1 - 2 * y2) */
v3p = 1;
if (v2p) {
bary_add(v3ds, v3n, v2ds, v2n, y2ds, y2n);
@@ -2447,8 +2447,8 @@ bary_mul_precheck(BDIGIT **zdsp, size_t *znp, const BDIGIT **xdsp, size_t *xnp,
if (xn > yn) {
const BDIGIT *tds;
size_t tn;
- tds = xds; xds = yds; yds = tds;
- tn = xn; xn = yn; yn = tn;
+ tds = xds; xds = yds; yds = tds;
+ tn = xn; xn = yn; yn = tn;
}
assert(xn <= yn);
@@ -2598,26 +2598,26 @@ bigdivrem1(void *ptr)
BDIGIT q;
do {
- if (bds->stop) {
- bds->zn = zn;
- return 0;
+ if (bds->stop) {
+ bds->zn = zn;
+ return 0;
}
- if (zds[zn-1] == yds[yn-1]) q = BDIGMAX;
- else q = (BDIGIT)((BIGUP(zds[zn-1]) + zds[zn-2])/yds[yn-1]);
- if (q) {
+ if (zds[zn-1] == yds[yn-1]) q = BDIGMAX;
+ else q = (BDIGIT)((BIGUP(zds[zn-1]) + zds[zn-2])/yds[yn-1]);
+ if (q) {
num = bigdivrem_mulsub(zds+zn-(yn+1), yn+1,
q,
yds, yn);
- while (num) { /* "add back" required */
- q--;
+ while (num) { /* "add back" required */
+ q--;
num = bary_add(zds+zn-(yn+1), yn,
zds+zn-(yn+1), yn,
yds, yn);
num--;
- }
- }
+ }
+ }
zn--;
- zds[zn] = q;
+ zds[zn] = q;
} while (zn > yn);
return 0;
}
@@ -2686,16 +2686,16 @@ bigdivrem_restoring(BDIGIT *zds, size_t zn, BDIGIT *yds, size_t yn)
bds.zn = zn - ynzero;
if (bds.zn > 10000 || bds.yn > 10000) {
retry:
- bds.stop = Qfalse;
+ bds.stop = Qfalse;
rb_nogvl(bigdivrem1, &bds, rb_big_stop, &bds, RB_NOGVL_UBF_ASYNC_SAFE);
- if (bds.stop == Qtrue) {
- /* execute trap handler, but exception was not raised. */
- goto retry;
- }
+ if (bds.stop == Qtrue) {
+ /* execute trap handler, but exception was not raised. */
+ goto retry;
+ }
}
else {
- bigdivrem1(&bds);
+ bigdivrem1(&bds);
}
}
@@ -2953,7 +2953,7 @@ int
rb_cmpint(VALUE val, VALUE a, VALUE b)
{
if (NIL_P(val)) {
- rb_cmperr(a, b);
+ rb_cmperr(a, b);
}
if (FIXNUM_P(val)) {
long l = FIX2LONG(val);
@@ -2962,9 +2962,9 @@ rb_cmpint(VALUE val, VALUE a, VALUE b)
return 0;
}
if (RB_BIGNUM_TYPE_P(val)) {
- if (BIGZEROP(val)) return 0;
- if (BIGNUM_SIGN(val)) return 1;
- return -1;
+ if (BIGZEROP(val)) return 0;
+ if (BIGNUM_SIGN(val)) return 1;
+ return -1;
}
if (RTEST(rb_funcall(val, '>', 1, INT2FIX(0)))) return 1;
if (RTEST(rb_funcall(val, '<', 1, INT2FIX(0)))) return -1;
@@ -2974,8 +2974,8 @@ rb_cmpint(VALUE val, VALUE a, VALUE b)
#define BIGNUM_SET_LEN(b,l) \
(BIGNUM_EMBED_P(b) ? \
(void)(RBASIC(b)->flags = \
- (RBASIC(b)->flags & ~BIGNUM_EMBED_LEN_MASK) | \
- ((l) << BIGNUM_EMBED_LEN_SHIFT)) : \
+ (RBASIC(b)->flags & ~BIGNUM_EMBED_LEN_MASK) | \
+ ((l) << BIGNUM_EMBED_LEN_SHIFT)) : \
(void)(RBIGNUM(b)->as.heap.len = (l)))
static void
@@ -2983,33 +2983,33 @@ rb_big_realloc(VALUE big, size_t len)
{
BDIGIT *ds;
if (BIGNUM_EMBED_P(big)) {
- if (BIGNUM_EMBED_LEN_MAX < len) {
- ds = ALLOC_N(BDIGIT, len);
- MEMCPY(ds, RBIGNUM(big)->as.ary, BDIGIT, BIGNUM_EMBED_LEN_MAX);
- RBIGNUM(big)->as.heap.len = BIGNUM_LEN(big);
- RBIGNUM(big)->as.heap.digits = ds;
+ if (BIGNUM_EMBED_LEN_MAX < len) {
+ ds = ALLOC_N(BDIGIT, len);
+ MEMCPY(ds, RBIGNUM(big)->as.ary, BDIGIT, BIGNUM_EMBED_LEN_MAX);
+ RBIGNUM(big)->as.heap.len = BIGNUM_LEN(big);
+ RBIGNUM(big)->as.heap.digits = ds;
FL_UNSET_RAW(big, BIGNUM_EMBED_FLAG);
- }
+ }
}
else {
- if (len <= BIGNUM_EMBED_LEN_MAX) {
- ds = RBIGNUM(big)->as.heap.digits;
+ if (len <= BIGNUM_EMBED_LEN_MAX) {
+ ds = RBIGNUM(big)->as.heap.digits;
FL_SET_RAW(big, BIGNUM_EMBED_FLAG);
- BIGNUM_SET_LEN(big, len);
+ BIGNUM_SET_LEN(big, len);
(void)VALGRIND_MAKE_MEM_UNDEFINED((void*)RBIGNUM(big)->as.ary, sizeof(RBIGNUM(big)->as.ary));
- if (ds) {
- MEMCPY(RBIGNUM(big)->as.ary, ds, BDIGIT, len);
- xfree(ds);
- }
- }
- else {
- if (BIGNUM_LEN(big) == 0) {
- RBIGNUM(big)->as.heap.digits = ALLOC_N(BDIGIT, len);
- }
- else {
- REALLOC_N(RBIGNUM(big)->as.heap.digits, BDIGIT, len);
- }
- }
+ if (ds) {
+ MEMCPY(RBIGNUM(big)->as.ary, ds, BDIGIT, len);
+ xfree(ds);
+ }
+ }
+ else {
+ if (BIGNUM_LEN(big) == 0) {
+ RBIGNUM(big)->as.heap.digits = ALLOC_N(BDIGIT, len);
+ }
+ else {
+ REALLOC_N(RBIGNUM(big)->as.heap.digits, BDIGIT, len);
+ }
+ }
}
}
@@ -3095,7 +3095,7 @@ abs2twocomp(VALUE *xp, long *n_ret)
MEMCPY(BDIGITS(z), ds, BDIGIT, n);
bary_2comp(BDIGITS(z), n);
hibits = BDIGMAX;
- *xp = z;
+ *xp = z;
}
*n_ret = n;
return hibits;
@@ -3119,7 +3119,7 @@ bigtrunc(VALUE x)
if (len == 0) return x;
while (--len && !ds[len]);
if (BIGNUM_LEN(x) > len+1) {
- rb_big_resize(x, len+1);
+ rb_big_resize(x, len+1);
}
return x;
}
@@ -3172,7 +3172,7 @@ static VALUE
bignorm(VALUE x)
{
if (RB_BIGNUM_TYPE_P(x)) {
- x = bigfixize(x);
+ x = bigfixize(x);
}
return x;
}
@@ -3194,8 +3194,8 @@ rb_uint2big(uintptr_t n)
digits[0] = n;
#else
for (i = 0; i < bdigit_roomof(SIZEOF_VALUE); i++) {
- digits[i] = BIGLO(n);
- n = BIGDN(n);
+ digits[i] = BIGLO(n);
+ n = BIGDN(n);
}
#endif
@@ -3214,14 +3214,14 @@ rb_int2big(intptr_t n)
if (n < 0) {
u = 1 + (VALUE)(-(n + 1)); /* u = -n avoiding overflow */
- neg = 1;
+ neg = 1;
}
else {
u = n;
}
big = rb_uint2big(u);
if (neg) {
- BIGNUM_SET_NEGATIVE_SIGN(big);
+ BIGNUM_SET_NEGATIVE_SIGN(big);
}
return big;
}
@@ -3380,7 +3380,7 @@ absint_numwords_generic(size_t numbytes, int nlz_bits_in_msbyte, size_t word_num
if (sign == 2) {
#if defined __GNUC__ && (__GNUC__ == 4 && __GNUC_MINOR__ == 4)
- *nlz_bits_ret = 0;
+ *nlz_bits_ret = 0;
#endif
return (size_t)-1;
}
@@ -3700,7 +3700,7 @@ rb_integer_unpack(const void *words, size_t numwords, size_t wordsize, size_t na
}
else if (num_bdigits == numberof(fixbuf)) {
val = bignew((long)num_bdigits+1, 0);
- MEMCPY(BDIGITS(val), fixbuf, BDIGIT, num_bdigits);
+ MEMCPY(BDIGITS(val), fixbuf, BDIGIT, num_bdigits);
BDIGITS(val)[num_bdigits++] = 1;
}
else {
@@ -3712,9 +3712,9 @@ rb_integer_unpack(const void *words, size_t numwords, size_t wordsize, size_t na
BDIGIT_DBL u = fixbuf[0] + BIGUP(fixbuf[1]);
if (u == 0)
return LONG2FIX(0);
- if (0 < sign && POSFIXABLE(u))
+ if (0 < sign && POSFIXABLE(u))
return LONG2FIX((long)u);
- if (sign < 0 && BDIGIT_MSB(fixbuf[1]) == 0 &&
+ if (sign < 0 && BDIGIT_MSB(fixbuf[1]) == 0 &&
NEGFIXABLE(-(BDIGIT_DBL_SIGNED)u))
return LONG2FIX((long)-(BDIGIT_DBL_SIGNED)u);
val = bignew((long)num_bdigits, 0 <= sign);
@@ -3766,41 +3766,41 @@ str2big_scan_digits(const char *s, const char *str, int base, int badcheck, size
int c;
if (!len) {
- *num_digits_p = 0;
- *len_p = 0;
- return TRUE;
+ *num_digits_p = 0;
+ *len_p = 0;
+ return TRUE;
}
if (badcheck && *str == '_') return FALSE;
while ((c = *str++) != 0) {
- if (c == '_') {
- if (nondigit) {
+ if (c == '_') {
+ if (nondigit) {
if (badcheck) return FALSE;
- break;
- }
- nondigit = (char) c;
- }
- else if ((c = conv_digit(c)) < 0 || c >= base) {
- break;
- }
- else {
- nondigit = 0;
- num_digits++;
- digits_end = str;
- }
- if (len > 0 && !--len) break;
+ break;
+ }
+ nondigit = (char) c;
+ }
+ else if ((c = conv_digit(c)) < 0 || c >= base) {
+ break;
+ }
+ else {
+ nondigit = 0;
+ num_digits++;
+ digits_end = str;
+ }
+ if (len > 0 && !--len) break;
}
if (badcheck && nondigit) return FALSE;
if (badcheck && len) {
- str--;
- while (*str && ISSPACE(*str)) {
- str++;
- if (len > 0 && !--len) break;
- }
- if (len && *str) {
- return FALSE;
- }
+ str--;
+ while (*str && ISSPACE(*str)) {
+ str++;
+ if (len > 0 && !--len) break;
+ }
+ if (len && *str) {
+ return FALSE;
+ }
}
*num_digits_p = num_digits;
*len_p = digits_end - digits_start;
@@ -4042,8 +4042,8 @@ rb_cstr_to_inum(const char *str, int base, int badcheck)
char *end;
VALUE ret = rb_cstr_parse_inum(str, -1, (badcheck ? NULL : &end), base);
if (NIL_P(ret)) {
- if (badcheck) rb_invalid_str(str, "Integer()");
- ret = INT2FIX(0);
+ if (badcheck) rb_invalid_str(str, "Integer()");
+ ret = INT2FIX(0);
}
return ret;
}
@@ -4067,7 +4067,7 @@ rb_cstr_to_inum(const char *str, int base, int badcheck)
VALUE
rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits,
- int base, int flags)
+ int base, int flags)
{
const char *const s = str;
char sign = 1;
@@ -4084,82 +4084,82 @@ rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits,
const int badcheck = !endp;
#define ADV(n) do {\
- if (len > 0 && len <= (n)) goto bad; \
- str += (n); \
- len -= (n); \
+ if (len > 0 && len <= (n)) goto bad; \
+ str += (n); \
+ len -= (n); \
} while (0)
#define ASSERT_LEN() do {\
- assert(len != 0); \
- if (len0 >= 0) assert(s + len0 == str + len); \
+ assert(len != 0); \
+ if (len0 >= 0) assert(s + len0 == str + len); \
} while (0)
if (!str) {
goto bad;
}
if (len && (flags & RB_INT_PARSE_SIGN)) {
- while (ISSPACE(*str)) ADV(1);
+ while (ISSPACE(*str)) ADV(1);
- if (str[0] == '+') {
- ADV(1);
- }
- else if (str[0] == '-') {
- ADV(1);
- sign = 0;
- }
- ASSERT_LEN();
+ if (str[0] == '+') {
+ ADV(1);
+ }
+ else if (str[0] == '-') {
+ ADV(1);
+ sign = 0;
+ }
+ ASSERT_LEN();
}
if (base <= 0) {
- if (str[0] == '0' && len > 1) {
- switch (str[1]) {
- case 'x': case 'X':
- base = 16;
- ADV(2);
- break;
- case 'b': case 'B':
- base = 2;
- ADV(2);
- break;
- case 'o': case 'O':
- base = 8;
- ADV(2);
- break;
- case 'd': case 'D':
- base = 10;
- ADV(2);
- break;
- default:
- base = 8;
- }
- }
- else if (base < -1) {
- base = -base;
- }
- else {
- base = 10;
- }
+ if (str[0] == '0' && len > 1) {
+ switch (str[1]) {
+ case 'x': case 'X':
+ base = 16;
+ ADV(2);
+ break;
+ case 'b': case 'B':
+ base = 2;
+ ADV(2);
+ break;
+ case 'o': case 'O':
+ base = 8;
+ ADV(2);
+ break;
+ case 'd': case 'D':
+ base = 10;
+ ADV(2);
+ break;
+ default:
+ base = 8;
+ }
+ }
+ else if (base < -1) {
+ base = -base;
+ }
+ else {
+ base = 10;
+ }
}
else if (len == 1 || !(flags & RB_INT_PARSE_PREFIX)) {
- /* no prefix */
+ /* no prefix */
}
else if (base == 2) {
- if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) {
- ADV(2);
- }
+ if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) {
+ ADV(2);
+ }
}
else if (base == 8) {
- if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) {
- ADV(2);
- }
+ if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) {
+ ADV(2);
+ }
}
else if (base == 10) {
- if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) {
- ADV(2);
- }
+ if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) {
+ ADV(2);
+ }
}
else if (base == 16) {
- if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) {
- ADV(2);
- }
+ if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) {
+ ADV(2);
+ }
}
if (!valid_radix_p(base)) {
invalid_radix(base);
@@ -4167,73 +4167,72 @@ rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits,
if (!len) goto bad;
num_digits = str - s;
if (*str == '0' && len != 1) { /* squeeze preceding 0s */
- int us = 0;
- const char *end = len < 0 ? NULL : str + len;
- ++num_digits;
- while ((c = *++str) == '0' ||
- ((flags & RB_INT_PARSE_UNDERSCORE) && c == '_')) {
- if (c == '_') {
- if (++us >= 2)
- break;
- }
- else {
- ++num_digits;
- us = 0;
- }
- if (str == end) break;
- }
- if (!c || ISSPACE(c)) --str;
- if (end) len = end - str;
- ASSERT_LEN();
+ int us = 0;
+ const char *end = len < 0 ? NULL : str + len;
+ ++num_digits;
+ while ((c = *++str) == '0' ||
+ ((flags & RB_INT_PARSE_UNDERSCORE) && c == '_')) {
+ if (c == '_') {
+ if (++us >= 2)
+ break;
+ }
+ else {
+ ++num_digits;
+ us = 0;
+ }
+ if (str == end) break;
+ }
+ if (!c || ISSPACE(c)) --str;
+ if (end) len = end - str;
}
c = *str;
c = conv_digit(c);
if (c < 0 || c >= base) {
- if (!badcheck && num_digits) z = INT2FIX(0);
- goto bad;
+ if (!badcheck && num_digits) z = INT2FIX(0);
+ goto bad;
}
if (ndigits) *ndigits = num_digits;
val = ruby_scan_digits(str, len, base, &num_digits, &ov);
if (!ov) {
- const char *end = &str[num_digits];
- if (num_digits > 0 && *end == '_' && (flags & RB_INT_PARSE_UNDERSCORE))
- goto bigparse;
- if (endp) *endp = (char *)end;
- if (ndigits) *ndigits += num_digits;
- if (badcheck) {
- if (num_digits == 0) return Qnil; /* no number */
- while (len < 0 ? *end : end < str + len) {
- if (!ISSPACE(*end)) return Qnil; /* trailing garbage */
- end++;
- }
- }
-
- if (POSFIXABLE(val)) {
- if (sign) return LONG2FIX(val);
- else {
- long result = -(long)val;
- return LONG2FIX(result);
- }
- }
- else {
- VALUE big = rb_uint2big(val);
- BIGNUM_SET_SIGN(big, sign);
- return bignorm(big);
- }
+ const char *end = &str[num_digits];
+ if (num_digits > 0 && *end == '_' && (flags & RB_INT_PARSE_UNDERSCORE))
+ goto bigparse;
+ if (endp) *endp = (char *)end;
+ if (ndigits) *ndigits += num_digits;
+ if (badcheck) {
+ if (num_digits == 0) return Qnil; /* no number */
+ while (len < 0 ? *end : end < str + len) {
+ if (!ISSPACE(*end)) return Qnil; /* trailing garbage */
+ end++;
+ }
+ }
+
+ if (POSFIXABLE(val)) {
+ if (sign) return LONG2FIX(val);
+ else {
+ long result = -(long)val;
+ return LONG2FIX(result);
+ }
+ }
+ else {
+ VALUE big = rb_uint2big(val);
+ BIGNUM_SET_SIGN(big, sign);
+ return bignorm(big);
+ }
}
bigparse:
digits_start = str;
if (!str2big_scan_digits(s, str, base, badcheck, &num_digits, &len))
- goto bad;
+ goto bad;
if (endp) *endp = (char *)(str + len);
if (ndigits) *ndigits += num_digits;
digits_end = digits_start + len;
if (POW2_P(base)) {
z = str2big_poweroftwo(sign, digits_start, digits_end, num_digits,
- bit_length(base-1));
+ bit_length(base-1));
}
else {
int digits_per_bdigits_dbl;
@@ -4269,7 +4268,7 @@ static VALUE
rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base)
{
return rb_int_parse_cstr(str, len, endp, NULL, base,
- RB_INT_PARSE_DEFAULT);
+ RB_INT_PARSE_DEFAULT);
}
VALUE
@@ -4318,14 +4317,14 @@ rb_str2big_poweroftwo(VALUE arg, int base, int badcheck)
s = str = StringValueCStr(arg);
len = RSTRING_LEN(arg);
if (*str == '-') {
- len--;
+ len--;
str++;
positive_p = 0;
}
digits_start = str;
if (!str2big_scan_digits(s, str, base, badcheck, &num_digits, &len))
- invalid_integer(arg);
+ invalid_integer(arg);
digits_end = digits_start + len;
z = str2big_poweroftwo(positive_p, digits_start, digits_end, num_digits,
@@ -4357,14 +4356,14 @@ rb_str2big_normal(VALUE arg, int base, int badcheck)
s = str = StringValuePtr(arg);
len = RSTRING_LEN(arg);
if (len > 0 && *str == '-') {
- len--;
+ len--;
str++;
positive_p = 0;
}
digits_start = str;
if (!str2big_scan_digits(s, str, base, badcheck, &num_digits, &len))
- invalid_integer(arg);
+ invalid_integer(arg);
digits_end = digits_start + len;
maxpow_in_bdigit_dbl(base, &digits_per_bdigits_dbl);
@@ -4399,14 +4398,14 @@ rb_str2big_karatsuba(VALUE arg, int base, int badcheck)
s = str = StringValuePtr(arg);
len = RSTRING_LEN(arg);
if (len > 0 && *str == '-') {
- len--;
+ len--;
str++;
positive_p = 0;
}
digits_start = str;
if (!str2big_scan_digits(s, str, base, badcheck, &num_digits, &len))
- invalid_integer(arg);
+ invalid_integer(arg);
digits_end = digits_start + len;
maxpow_in_bdigit_dbl(base, &digits_per_bdigits_dbl);
@@ -4442,14 +4441,14 @@ rb_str2big_gmp(VALUE arg, int base, int badcheck)
s = str = StringValuePtr(arg);
len = RSTRING_LEN(arg);
if (len > 0 && *str == '-') {
- len--;
+ len--;
str++;
positive_p = 0;
}
digits_start = str;
if (!str2big_scan_digits(s, str, base, badcheck, &num_digits, &len))
- invalid_integer(arg);
+ invalid_integer(arg);
digits_end = digits_start + len;
maxpow_in_bdigit_dbl(base, &digits_per_bdigits_dbl);
@@ -4476,8 +4475,8 @@ rb_ull2big(unsigned LONG_LONG n)
digits[0] = n;
#else
for (i = 0; i < bdigit_roomof(SIZEOF_LONG_LONG); i++) {
- digits[i] = BIGLO(n);
- n = BIGDN(n);
+ digits[i] = BIGLO(n);
+ n = BIGDN(n);
}
#endif
@@ -4496,14 +4495,14 @@ rb_ll2big(LONG_LONG n)
if (n < 0) {
u = 1 + (unsigned LONG_LONG)(-(n + 1)); /* u = -n avoiding overflow */
- neg = 1;
+ neg = 1;
}
else {
u = n;
}
big = rb_ull2big(u);
if (neg) {
- BIGNUM_SET_NEGATIVE_SIGN(big);
+ BIGNUM_SET_NEGATIVE_SIGN(big);
}
return big;
}
@@ -4533,7 +4532,7 @@ rb_uint128t2big(uint128_t n)
BDIGIT *digits = BDIGITS(big);
for (i = 0; i < bdigit_roomof(SIZEOF_INT128_T); i++) {
- digits[i] = BIGLO(RSHIFT(n ,BITSPERDIG*i));
+ digits[i] = BIGLO(RSHIFT(n ,BITSPERDIG*i));
}
i = bdigit_roomof(SIZEOF_INT128_T);
@@ -4551,14 +4550,14 @@ rb_int128t2big(int128_t n)
if (n < 0) {
u = 1 + (uint128_t)(-(n + 1)); /* u = -n avoiding overflow */
- neg = 1;
+ neg = 1;
}
else {
u = n;
}
big = rb_uint128t2big(u);
if (neg) {
- BIGNUM_SET_NEGATIVE_SIGN(big);
+ BIGNUM_SET_NEGATIVE_SIGN(big);
}
return big;
}
@@ -4587,11 +4586,14 @@ big_shift3(VALUE x, int lshift_p, size_t shift_numdigits, int shift_numbits)
if (lshift_p) {
if (LONG_MAX < shift_numdigits) {
- rb_raise(rb_eArgError, "too big number");
+ too_big:
+ rb_raise(rb_eRangeError, "shift width too big");
}
s1 = shift_numdigits;
s2 = shift_numbits;
+ if ((size_t)s1 != shift_numdigits) goto too_big;
xn = BIGNUM_LEN(x);
+ if (LONG_MAX/SIZEOF_BDIGIT <= xn+s1) goto too_big;
z = bignew(xn+s1+1, BIGNUM_SIGN(x));
zds = BDIGITS(z);
BDIGITS_ZERO(zds, s1);
@@ -4721,7 +4723,7 @@ power_cache_get_power(int base, int power_level, size_t *numdigits_ret)
rb_obj_hide(power);
base36_power_cache[base - 2][power_level] = power;
base36_numdigits_cache[base - 2][power_level] = numdigits;
- rb_gc_register_mark_object(power);
+ rb_gc_register_mark_object(power);
}
if (numdigits_ret)
*numdigits_ret = base36_numdigits_cache[base - 2][power_level];
@@ -4772,7 +4774,7 @@ big2str_2bdigits(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t tail
} while (num);
len = sizeof(buf) - j;
big2str_alloc(b2s, len + taillen);
- MEMCPY(b2s->ptr, buf + j, char, len);
+ MEMCPY(b2s->ptr, buf + j, char, len);
}
else {
p = b2s->ptr;
@@ -4789,7 +4791,7 @@ big2str_2bdigits(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t tail
static void
big2str_karatsuba(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t wn,
- int power_level, size_t taillen)
+ int power_level, size_t taillen)
{
VALUE b;
size_t half_numdigits, lower_numdigits;
@@ -4819,17 +4821,17 @@ big2str_karatsuba(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t wn,
*/
if (xn == 0 || bary_zero_p(xds, xn)) {
- if (b2s->ptr) {
+ if (b2s->ptr) {
/* When x is zero, power_cache_get_power(base, power_level) should be cached already. */
power_cache_get_power(b2s->base, power_level, &len);
- memset(b2s->ptr, '0', len);
+ memset(b2s->ptr, '0', len);
b2s->ptr += len;
- }
+ }
return;
}
if (power_level == 0) {
- big2str_2bdigits(b2s, xds, xn, taillen);
+ big2str_2bdigits(b2s, xds, xn, taillen);
return;
}
@@ -4857,7 +4859,7 @@ big2str_karatsuba(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t wn,
memset(b2s->ptr, '0', len);
b2s->ptr += len;
}
- big2str_2bdigits(b2s, xds, xn, taillen);
+ big2str_2bdigits(b2s, xds, xn, taillen);
}
else {
BDIGIT *qds, *rds;
@@ -4961,11 +4963,11 @@ big2str_generic(VALUE x, int base)
BARY_TRUNC(xds, xn);
if (xn == 0) {
- return rb_usascii_str_new2("0");
+ return rb_usascii_str_new2("0");
}
if (!valid_radix_p(base))
- invalid_radix(base);
+ invalid_radix(base);
if (xn >= LONG_MAX/BITSPERDIG) {
rb_raise(rb_eRangeError, "bignum too big to convert into `string'");
@@ -5002,7 +5004,7 @@ big2str_generic(VALUE x, int base)
b2s_data.ptr = NULL;
if (power_level == 0) {
- big2str_2bdigits(&b2s_data, xds, xn, 0);
+ big2str_2bdigits(&b2s_data, xds, xn, 0);
}
else {
VALUE tmpw = 0;
@@ -5011,7 +5013,7 @@ big2str_generic(VALUE x, int base)
wn = power_level * BIGDIVREM_EXTRA_WORDS + BIGNUM_LEN(power);
wds = ALLOCV_N(BDIGIT, tmpw, xn + wn);
MEMCPY(wds, xds, BDIGIT, xn);
- big2str_karatsuba(&b2s_data, wds, xn, wn, power_level, 0);
+ big2str_karatsuba(&b2s_data, wds, xn, wn, power_level, 0);
if (tmpw)
ALLOCV_END(tmpw);
}
@@ -5077,7 +5079,7 @@ rb_big2str1(VALUE x, int base)
size_t xn;
if (FIXNUM_P(x)) {
- return rb_fix2str(x, base);
+ return rb_fix2str(x, base);
}
bigtrunc(x);
@@ -5086,11 +5088,11 @@ rb_big2str1(VALUE x, int base)
BARY_TRUNC(xds, xn);
if (xn == 0) {
- return rb_usascii_str_new2("0");
+ return rb_usascii_str_new2("0");
}
if (!valid_radix_p(base))
- invalid_radix(base);
+ invalid_radix(base);
if (xn >= LONG_MAX/BITSPERDIG) {
rb_raise(rb_eRangeError, "bignum too big to convert into `string'");
@@ -5137,7 +5139,7 @@ big2ulong(VALUE x, const char *type)
#else
num = 0;
for (i = 0; i < len; i++) {
- num <<= BITSPERDIG;
+ num <<= BITSPERDIG;
num += (unsigned long)ds[len - i - 1]; /* overflow is already checked */
}
#endif
@@ -5190,13 +5192,13 @@ big2ull(VALUE x, const char *type)
if (len == 0)
return 0;
if (BIGSIZE(x) > SIZEOF_LONG_LONG)
- rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type);
+ rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type);
#if SIZEOF_LONG_LONG <= SIZEOF_BDIGIT
num = (unsigned LONG_LONG)ds[0];
#else
num = 0;
for (i = 0; i < len; i++) {
- num = BIGUP(num);
+ num = BIGUP(num);
num += ds[len - i - 1];
}
#endif
@@ -5246,23 +5248,23 @@ dbl2big(double d)
double u = (d < 0)?-d:d;
if (isinf(d)) {
- rb_raise(rb_eFloatDomainError, d < 0 ? "-Infinity" : "Infinity");
+ rb_raise(rb_eFloatDomainError, d < 0 ? "-Infinity" : "Infinity");
}
if (isnan(d)) {
- rb_raise(rb_eFloatDomainError, "NaN");
+ rb_raise(rb_eFloatDomainError, "NaN");
}
while (1.0 <= u) {
- u /= (double)(BIGRAD);
- i++;
+ u /= (double)(BIGRAD);
+ i++;
}
z = bignew(i, d>=0);
digits = BDIGITS(z);
while (i--) {
- u *= BIGRAD;
- c = (BDIGIT)u;
- u -= c;
- digits[i] = c;
+ u *= BIGRAD;
+ c = (BDIGIT)u;
+ u -= c;
+ digits[i] = c;
}
return z;
@@ -5282,28 +5284,28 @@ big2dbl(VALUE x)
BDIGIT *ds = BDIGITS(x), dl;
if (i) {
- bits = i * BITSPERDIG - nlz(ds[i-1]);
- if (bits > DBL_MANT_DIG+DBL_MAX_EXP) {
- d = HUGE_VAL;
- }
- else {
- if (bits > DBL_MANT_DIG+1)
- lo = (bits -= DBL_MANT_DIG+1) / BITSPERDIG;
- else
- bits = 0;
- while (--i > lo) {
- d = ds[i] + BIGRAD*d;
- }
- dl = ds[i];
- if (bits && (dl & ((BDIGIT)1 << (bits %= BITSPERDIG)))) {
- int carry = (dl & ~(BDIGMAX << bits)) != 0;
- if (!carry) {
- while (i-- > 0) {
- carry = ds[i] != 0;
- if (carry) break;
- }
- }
- if (carry) {
+ bits = i * BITSPERDIG - nlz(ds[i-1]);
+ if (bits > DBL_MANT_DIG+DBL_MAX_EXP) {
+ d = HUGE_VAL;
+ }
+ else {
+ if (bits > DBL_MANT_DIG+1)
+ lo = (bits -= DBL_MANT_DIG+1) / BITSPERDIG;
+ else
+ bits = 0;
+ while (--i > lo) {
+ d = ds[i] + BIGRAD*d;
+ }
+ dl = ds[i];
+ if (bits && (dl & ((BDIGIT)1 << (bits %= BITSPERDIG)))) {
+ int carry = (dl & ~(BDIGMAX << bits)) != 0;
+ if (!carry) {
+ while (i-- > 0) {
+ carry = ds[i] != 0;
+ if (carry) break;
+ }
+ }
+ if (carry) {
BDIGIT mask = BDIGMAX;
BDIGIT bit = 1;
mask <<= bits;
@@ -5311,19 +5313,19 @@ big2dbl(VALUE x)
dl &= mask;
dl += bit;
dl = BIGLO(dl);
- if (!dl) d += 1;
- }
- }
- d = dl + BIGRAD*d;
- if (lo) {
- if (lo > INT_MAX / BITSPERDIG)
- d = HUGE_VAL;
- else if (lo < INT_MIN / BITSPERDIG)
- d = 0.0;
- else
- d = ldexp(d, (int)(lo * BITSPERDIG));
- }
- }
+ if (!dl) d += 1;
+ }
+ }
+ d = dl + BIGRAD*d;
+ if (lo) {
+ if (lo > INT_MAX / BITSPERDIG)
+ d = HUGE_VAL;
+ else if (lo < INT_MIN / BITSPERDIG)
+ d = 0.0;
+ else
+ d = ldexp(d, (int)(lo * BITSPERDIG));
+ }
+ }
}
if (BIGNUM_NEGATIVE_P(x)) d = -d;
return d;
@@ -5335,11 +5337,11 @@ rb_big2dbl(VALUE x)
double d = big2dbl(x);
if (isinf(d)) {
- rb_warning("Integer out of Float range");
- if (d < 0.0)
- d = -HUGE_VAL;
- else
- d = HUGE_VAL;
+ rb_warning("Integer out of Float range");
+ if (d < 0.0)
+ d = -HUGE_VAL;
+ else
+ d = HUGE_VAL;
}
return d;
}
@@ -5436,26 +5438,26 @@ VALUE
rb_big_cmp(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- x = bigfixize(x);
+ x = bigfixize(x);
if (FIXNUM_P(x)) {
- /* SIGNED_VALUE and Fixnum have same sign-bits, same
- * order */
- SIGNED_VALUE sx = (SIGNED_VALUE)x, sy = (SIGNED_VALUE)y;
- if (sx < sy) return INT2FIX(-1);
- return INT2FIX(sx > sy);
+ /* SIGNED_VALUE and Fixnum have same sign-bits, same
+ * order */
+ SIGNED_VALUE sx = (SIGNED_VALUE)x, sy = (SIGNED_VALUE)y;
+ if (sx < sy) return INT2FIX(-1);
+ return INT2FIX(sx > sy);
}
}
else if (RB_BIGNUM_TYPE_P(y)) {
- if (BIGNUM_SIGN(x) == BIGNUM_SIGN(y)) {
- int cmp = bary_cmp(BDIGITS(x), BIGNUM_LEN(x), BDIGITS(y), BIGNUM_LEN(y));
- return INT2FIX(BIGNUM_SIGN(x) ? cmp : -cmp);
- }
+ if (BIGNUM_SIGN(x) == BIGNUM_SIGN(y)) {
+ int cmp = bary_cmp(BDIGITS(x), BIGNUM_LEN(x), BDIGITS(y), BIGNUM_LEN(y));
+ return INT2FIX(BIGNUM_SIGN(x) ? cmp : -cmp);
+ }
}
else if (RB_FLOAT_TYPE_P(y)) {
return rb_integer_float_cmp(x, y);
}
else {
- return rb_num_coerce_cmp(x, y, idCmp);
+ return rb_num_coerce_cmp(x, y, idCmp);
}
return INT2FIX(BIGNUM_SIGN(x) ? 1 : -1);
}
@@ -5474,30 +5476,30 @@ big_op(VALUE x, VALUE y, enum big_op_t op)
int n;
if (RB_INTEGER_TYPE_P(y)) {
- rel = rb_big_cmp(x, y);
+ rel = rb_big_cmp(x, y);
}
else if (RB_FLOAT_TYPE_P(y)) {
rel = rb_integer_float_cmp(x, y);
}
else {
- ID id = 0;
- switch (op) {
- case big_op_gt: id = '>'; break;
- case big_op_ge: id = idGE; break;
- case big_op_lt: id = '<'; break;
- case big_op_le: id = idLE; break;
- }
- return rb_num_coerce_relop(x, y, id);
+ ID id = 0;
+ switch (op) {
+ case big_op_gt: id = '>'; break;
+ case big_op_ge: id = idGE; break;
+ case big_op_lt: id = '<'; break;
+ case big_op_le: id = idLE; break;
+ }
+ return rb_num_coerce_relop(x, y, id);
}
if (NIL_P(rel)) return Qfalse;
n = FIX2INT(rel);
switch (op) {
- case big_op_gt: return RBOOL(n > 0);
- case big_op_ge: return RBOOL(n >= 0);
- case big_op_lt: return RBOOL(n < 0);
- case big_op_le: return RBOOL(n <= 0);
+ case big_op_gt: return RBOOL(n > 0);
+ case big_op_ge: return RBOOL(n >= 0);
+ case big_op_lt: return RBOOL(n < 0);
+ case big_op_le: return RBOOL(n <= 0);
}
return Qundef;
}
@@ -5541,7 +5543,7 @@ VALUE
rb_big_eq(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- return RBOOL(bignorm(x) == y);
+ return RBOOL(bignorm(x) == y);
}
else if (RB_BIGNUM_TYPE_P(y)) {
}
@@ -5549,7 +5551,7 @@ rb_big_eq(VALUE x, VALUE y)
return rb_integer_float_eq(x, y);
}
else {
- return rb_equal(y, x);
+ return rb_equal(y, x);
}
if (BIGNUM_SIGN(x) != BIGNUM_SIGN(y)) return Qfalse;
if (BIGNUM_LEN(x) != BIGNUM_LEN(y)) return Qfalse;
@@ -5656,10 +5658,10 @@ bigsub_int(VALUE x, long y0)
assert(xn == zn);
num = (BDIGIT_DBL_SIGNED)xds[0] - y;
if (xn == 1 && num < 0) {
- BIGNUM_NEGATE(z);
- zds[0] = (BDIGIT)-num;
- RB_GC_GUARD(x);
- return bignorm(z);
+ BIGNUM_NEGATE(z);
+ zds[0] = (BDIGIT)-num;
+ RB_GC_GUARD(x);
+ return bignorm(z);
}
zds[0] = BIGLO(num);
num = BIGDN(num);
@@ -5671,10 +5673,10 @@ bigsub_int(VALUE x, long y0)
num = 0;
for (i=0; i < xn; i++) {
if (y == 0) goto y_is_zero_x;
- num += (BDIGIT_DBL_SIGNED)xds[i] - BIGLO(y);
- zds[i] = BIGLO(num);
- num = BIGDN(num);
- y = BIGDN(y);
+ num += (BDIGIT_DBL_SIGNED)xds[i] - BIGLO(y);
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
+ y = BIGDN(y);
}
for (; i < zn; i++) {
if (y == 0) goto y_is_zero_z;
@@ -5689,9 +5691,9 @@ bigsub_int(VALUE x, long y0)
for (; i < xn; i++) {
y_is_zero_x:
if (num == 0) goto num_is_zero_x;
- num += xds[i];
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ num += xds[i];
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
#if SIZEOF_BDIGIT < SIZEOF_LONG
for (; i < zn; i++) {
@@ -5705,7 +5707,7 @@ bigsub_int(VALUE x, long y0)
for (; i < xn; i++) {
num_is_zero_x:
- zds[i] = xds[i];
+ zds[i] = xds[i];
}
#if SIZEOF_BDIGIT < SIZEOF_LONG
for (; i < zn; i++) {
@@ -5719,7 +5721,7 @@ bigsub_int(VALUE x, long y0)
assert(num == 0 || num == -1);
if (num < 0) {
get2comp(z);
- BIGNUM_NEGATE(z);
+ BIGNUM_NEGATE(z);
}
RB_GC_GUARD(x);
return bignorm(z);
@@ -5762,17 +5764,17 @@ bigadd_int(VALUE x, long y)
num = 0;
for (i=0; i < xn; i++) {
if (y == 0) goto y_is_zero_x;
- num += (BDIGIT_DBL)xds[i] + BIGLO(y);
- zds[i] = BIGLO(num);
- num = BIGDN(num);
- y = BIGDN(y);
+ num += (BDIGIT_DBL)xds[i] + BIGLO(y);
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
+ y = BIGDN(y);
}
for (; i < zn; i++) {
if (y == 0) goto y_is_zero_z;
- num += BIGLO(y);
- zds[i] = BIGLO(num);
- num = BIGDN(num);
- y = BIGDN(y);
+ num += BIGLO(y);
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
+ y = BIGDN(y);
}
goto finish;
@@ -5781,25 +5783,25 @@ bigadd_int(VALUE x, long y)
for (;i < xn; i++) {
y_is_zero_x:
if (num == 0) goto num_is_zero_x;
- num += (BDIGIT_DBL)xds[i];
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ num += (BDIGIT_DBL)xds[i];
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
for (; i < zn; i++) {
y_is_zero_z:
if (num == 0) goto num_is_zero_z;
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
goto finish;
for (;i < xn; i++) {
num_is_zero_x:
- zds[i] = xds[i];
+ zds[i] = xds[i];
}
for (; i < zn; i++) {
num_is_zero_z:
- zds[i] = 0;
+ zds[i] = 0;
}
goto finish;
@@ -5816,15 +5818,15 @@ bigadd(VALUE x, VALUE y, int sign)
sign = (sign == BIGNUM_SIGN(y));
if (BIGNUM_SIGN(x) != sign) {
- if (sign) return bigsub(y, x);
- return bigsub(x, y);
+ if (sign) return bigsub(y, x);
+ return bigsub(x, y);
}
if (BIGNUM_LEN(x) > BIGNUM_LEN(y)) {
- len = BIGNUM_LEN(x) + 1;
+ len = BIGNUM_LEN(x) + 1;
}
else {
- len = BIGNUM_LEN(y) + 1;
+ len = BIGNUM_LEN(y) + 1;
}
z = bignew(len, sign);
@@ -5841,26 +5843,26 @@ rb_big_plus(VALUE x, VALUE y)
long n;
if (FIXNUM_P(y)) {
- n = FIX2LONG(y);
- if ((n > 0) != BIGNUM_SIGN(x)) {
- if (n < 0) {
- n = -n;
- }
- return bigsub_int(x, n);
- }
- if (n < 0) {
- n = -n;
- }
- return bigadd_int(x, n);
+ n = FIX2LONG(y);
+ if ((n > 0) != BIGNUM_SIGN(x)) {
+ if (n < 0) {
+ n = -n;
+ }
+ return bigsub_int(x, n);
+ }
+ if (n < 0) {
+ n = -n;
+ }
+ return bigadd_int(x, n);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return bignorm(bigadd(x, y, 1));
+ return bignorm(bigadd(x, y, 1));
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM(rb_big2dbl(x) + RFLOAT_VALUE(y));
+ return DBL2NUM(rb_big2dbl(x) + RFLOAT_VALUE(y));
}
else {
- return rb_num_coerce_bin(x, y, '+');
+ return rb_num_coerce_bin(x, y, '+');
}
}
@@ -5870,26 +5872,26 @@ rb_big_minus(VALUE x, VALUE y)
long n;
if (FIXNUM_P(y)) {
- n = FIX2LONG(y);
- if ((n > 0) != BIGNUM_SIGN(x)) {
- if (n < 0) {
- n = -n;
- }
- return bigadd_int(x, n);
- }
- if (n < 0) {
- n = -n;
- }
- return bigsub_int(x, n);
+ n = FIX2LONG(y);
+ if ((n > 0) != BIGNUM_SIGN(x)) {
+ if (n < 0) {
+ n = -n;
+ }
+ return bigadd_int(x, n);
+ }
+ if (n < 0) {
+ n = -n;
+ }
+ return bigsub_int(x, n);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return bignorm(bigadd(x, y, 0));
+ return bignorm(bigadd(x, y, 0));
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM(rb_big2dbl(x) - RFLOAT_VALUE(y));
+ return DBL2NUM(rb_big2dbl(x) - RFLOAT_VALUE(y));
}
else {
- return rb_num_coerce_bin(x, y, '-');
+ return rb_num_coerce_bin(x, y, '-');
}
}
@@ -5948,15 +5950,15 @@ VALUE
rb_big_mul(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- y = rb_int2big(FIX2LONG(y));
+ y = rb_int2big(FIX2LONG(y));
}
else if (RB_BIGNUM_TYPE_P(y)) {
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM(rb_big2dbl(x) * RFLOAT_VALUE(y));
+ return DBL2NUM(rb_big2dbl(x) * RFLOAT_VALUE(y));
}
else {
- return rb_num_coerce_bin(x, y, '*');
+ return rb_num_coerce_bin(x, y, '*');
}
return bignorm(bigmul0(x, y));
@@ -5983,21 +5985,21 @@ bigdivrem(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp)
BARY_TRUNC(xds, xn);
if (xn < yn || (xn == yn && xds[xn - 1] < yds[yn - 1])) {
- if (divp) *divp = rb_int2big(0);
- if (modp) *modp = x;
- return Qnil;
+ if (divp) *divp = rb_int2big(0);
+ if (modp) *modp = x;
+ return Qnil;
}
if (yn == 1) {
- dd = yds[0];
- z = bignew(xn, BIGNUM_SIGN(x)==BIGNUM_SIGN(y));
- zds = BDIGITS(z);
+ dd = yds[0];
+ z = bignew(xn, BIGNUM_SIGN(x)==BIGNUM_SIGN(y));
+ zds = BDIGITS(z);
dd = bigdivrem_single(zds, xds, xn, dd);
- if (modp) {
- *modp = rb_uint2big((uintptr_t)dd);
- BIGNUM_SET_SIGN(*modp, BIGNUM_SIGN(x));
- }
- if (divp) *divp = z;
- return Qnil;
+ if (modp) {
+ *modp = rb_uint2big((uintptr_t)dd);
+ BIGNUM_SET_SIGN(*modp, BIGNUM_SIGN(x));
+ }
+ if (divp) *divp = z;
+ return Qnil;
}
if (xn == 2 && yn == 2) {
BDIGIT_DBL x0 = bary2bdigitdbl(xds, 2);
@@ -6062,11 +6064,11 @@ bigdivmod(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp)
bigdivrem(x, y, divp, &mod);
if (BIGNUM_SIGN(x) != BIGNUM_SIGN(y) && !BIGZEROP(mod)) {
- if (divp) *divp = bigadd(*divp, rb_int2big(1), 0);
- if (modp) *modp = bigadd(mod, y, 1);
+ if (divp) *divp = bigadd(*divp, rb_int2big(1), 0);
+ if (modp) *modp = bigadd(mod, y, 1);
}
else if (modp) {
- *modp = mod;
+ *modp = mod;
}
}
@@ -6077,25 +6079,25 @@ rb_big_divide(VALUE x, VALUE y, ID op)
VALUE z;
if (FIXNUM_P(y)) {
- y = rb_int2big(FIX2LONG(y));
+ y = rb_int2big(FIX2LONG(y));
}
else if (RB_BIGNUM_TYPE_P(y)) {
}
else if (RB_FLOAT_TYPE_P(y)) {
- if (op == '/') {
+ if (op == '/') {
double dx = rb_big2dbl(x);
return rb_flo_div_flo(DBL2NUM(dx), y);
- }
- else {
+ }
+ else {
VALUE v;
- double dy = RFLOAT_VALUE(y);
- if (dy == 0.0) rb_num_zerodiv();
+ double dy = RFLOAT_VALUE(y);
+ if (dy == 0.0) rb_num_zerodiv();
v = rb_big_divide(x, y, '/');
return rb_dbl2big(RFLOAT_VALUE(v));
- }
+ }
}
else {
- return rb_num_coerce_bin(x, y, op);
+ return rb_num_coerce_bin(x, y, op);
}
bigdivmod(x, y, &z, 0);
@@ -6120,10 +6122,10 @@ rb_big_modulo(VALUE x, VALUE y)
VALUE z;
if (FIXNUM_P(y)) {
- y = rb_int2big(FIX2LONG(y));
+ y = rb_int2big(FIX2LONG(y));
}
else if (!RB_BIGNUM_TYPE_P(y)) {
- return rb_num_coerce_bin(x, y, '%');
+ return rb_num_coerce_bin(x, y, '%');
}
bigdivmod(x, y, 0, &z);
@@ -6136,10 +6138,10 @@ rb_big_remainder(VALUE x, VALUE y)
VALUE z;
if (FIXNUM_P(y)) {
- y = rb_int2big(FIX2LONG(y));
+ y = rb_int2big(FIX2LONG(y));
}
else if (!RB_BIGNUM_TYPE_P(y)) {
- return rb_num_coerce_bin(x, y, rb_intern("remainder"));
+ return rb_num_coerce_bin(x, y, rb_intern("remainder"));
}
bigdivrem(x, y, 0, &z);
@@ -6152,7 +6154,7 @@ rb_big_divmod(VALUE x, VALUE y)
VALUE div, mod;
if (FIXNUM_P(y)) {
- y = rb_int2big(FIX2LONG(y));
+ y = rb_int2big(FIX2LONG(y));
}
else if (!RB_BIGNUM_TYPE_P(y)) {
return rb_num_coerce_bin(x, y, idDivmod);
@@ -6166,9 +6168,9 @@ static VALUE
big_shift(VALUE x, long n)
{
if (n < 0)
- return big_lshift(x, 1+(unsigned long)(-(n+1)));
+ return big_lshift(x, 1+(unsigned long)(-(n+1)));
else if (n > 0)
- return big_rshift(x, (unsigned long)n);
+ return big_rshift(x, (unsigned long)n);
return x;
}
@@ -6192,9 +6194,9 @@ big_fdiv(VALUE x, VALUE y, long ey)
l = ex - ey;
#if SIZEOF_LONG > SIZEOF_INT
{
- /* Visual C++ can't be here */
- if (l > INT_MAX) return HUGE_VAL;
- if (l < INT_MIN) return 0.0;
+ /* Visual C++ can't be here */
+ if (l > INT_MAX) return HUGE_VAL;
+ if (l < INT_MIN) return 0.0;
}
#endif
return ldexp(big2dbl(z), (int)l);
@@ -6228,19 +6230,19 @@ rb_big_fdiv_double(VALUE x, VALUE y)
dx = big2dbl(x);
if (FIXNUM_P(y)) {
- dy = (double)FIX2LONG(y);
- if (isinf(dx))
- return big_fdiv_int(x, rb_int2big(FIX2LONG(y)));
+ dy = (double)FIX2LONG(y);
+ if (isinf(dx))
+ return big_fdiv_int(x, rb_int2big(FIX2LONG(y)));
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return big_fdiv_int(x, y);
+ return big_fdiv_int(x, y);
}
else if (RB_FLOAT_TYPE_P(y)) {
- dy = RFLOAT_VALUE(y);
- if (isnan(dy))
- return dy;
- if (isinf(dx))
- return big_fdiv_float(x, y);
+ dy = RFLOAT_VALUE(y);
+ if (isnan(dy))
+ return dy;
+ if (isinf(dx))
+ return big_fdiv_float(x, y);
}
else {
return NUM2DBL(rb_num_coerce_bin(x, y, idFdiv));
@@ -6265,20 +6267,20 @@ rb_big_pow(VALUE x, VALUE y)
if (y == INT2FIX(0)) return INT2FIX(1);
if (y == INT2FIX(1)) return x;
if (RB_FLOAT_TYPE_P(y)) {
- d = RFLOAT_VALUE(y);
- if ((BIGNUM_NEGATIVE_P(x) && !BIGZEROP(x))) {
+ d = RFLOAT_VALUE(y);
+ if ((BIGNUM_NEGATIVE_P(x) && !BIGZEROP(x))) {
return rb_dbl_complex_new_polar_pi(pow(-rb_big2dbl(x), d), d);
- }
+ }
}
else if (RB_BIGNUM_TYPE_P(y)) {
- y = bignorm(y);
- if (FIXNUM_P(y))
- goto again;
- rb_warn("in a**b, b may be too big");
- d = rb_big2dbl(y);
+ y = bignorm(y);
+ if (FIXNUM_P(y))
+ goto again;
+ rb_warn("in a**b, b may be too big");
+ d = rb_big2dbl(y);
}
else if (FIXNUM_P(y)) {
- yy = FIX2LONG(y);
+ yy = FIX2LONG(y);
if (yy < 0) {
x = rb_big_pow(x, LONG2NUM(-yy));
@@ -6287,31 +6289,31 @@ rb_big_pow(VALUE x, VALUE y)
else
return DBL2NUM(1.0 / NUM2DBL(x));
}
- else {
- VALUE z = 0;
- SIGNED_VALUE mask;
+ else {
+ VALUE z = 0;
+ SIGNED_VALUE mask;
const size_t xbits = rb_absint_numwords(x, 1, NULL);
- const size_t BIGLEN_LIMIT = 32*1024*1024;
+ const size_t BIGLEN_LIMIT = 32*1024*1024;
- if (xbits == (size_t)-1 ||
+ if (xbits == (size_t)-1 ||
(xbits > BIGLEN_LIMIT) ||
(xbits * yy > BIGLEN_LIMIT)) {
- rb_warn("in a**b, b may be too big");
- d = (double)yy;
- }
- else {
- for (mask = FIXNUM_MAX + 1; mask; mask >>= 1) {
- if (z) z = bigsq(z);
- if (yy & mask) {
- z = z ? bigtrunc(bigmul0(z, x)) : x;
- }
- }
- return bignorm(z);
- }
- }
+ rb_warn("in a**b, b may be too big");
+ d = (double)yy;
+ }
+ else {
+ for (mask = FIXNUM_MAX + 1; mask; mask >>= 1) {
+ if (z) z = bigsq(z);
+ if (yy & mask) {
+ z = z ? bigtrunc(bigmul0(z, x)) : x;
+ }
+ }
+ return bignorm(z);
+ }
+ }
}
else {
- return rb_num_coerce_bin(x, y, idPow);
+ return rb_num_coerce_bin(x, y, idPow);
}
return DBL2NUM(pow(rb_big2dbl(x), d));
}
@@ -6331,8 +6333,8 @@ bigand_int(VALUE x, long xn, BDIGIT hibitsx, long y)
xds = BDIGITS(x);
#if SIZEOF_BDIGIT >= SIZEOF_LONG
if (!hibitsy) {
- y &= xds[0];
- return LONG2NUM(y);
+ y &= xds[0];
+ return LONG2NUM(y);
}
#endif
@@ -6361,10 +6363,10 @@ bigand_int(VALUE x, long xn, BDIGIT hibitsx, long y)
}
#endif
for (;i < xn; i++) {
- zds[i] = xds[i] & hibitsy;
+ zds[i] = xds[i] & hibitsy;
}
for (;i < zn; i++) {
- zds[i] = hibitsx & hibitsy;
+ zds[i] = hibitsx & hibitsy;
}
twocomp2abs_bang(z, hibitsx && hibitsy);
RB_GC_GUARD(x);
@@ -6384,12 +6386,12 @@ rb_big_and(VALUE x, VALUE y)
long tmpn;
if (!RB_INTEGER_TYPE_P(y)) {
- return rb_num_coerce_bit(x, y, '&');
+ return rb_num_coerce_bit(x, y, '&');
}
hibitsx = abs2twocomp(&x, &xn);
if (FIXNUM_P(y)) {
- return bigand_int(x, xn, hibitsx, FIX2LONG(y));
+ return bigand_int(x, xn, hibitsx, FIX2LONG(y));
}
hibitsy = abs2twocomp(&y, &yn);
if (xn > yn) {
@@ -6411,10 +6413,10 @@ rb_big_and(VALUE x, VALUE y)
zds = BDIGITS(z);
for (i=0; i<n1; i++) {
- zds[i] = ds1[i] & ds2[i];
+ zds[i] = ds1[i] & ds2[i];
}
for (; i<n2; i++) {
- zds[i] = hibits1 & ds2[i];
+ zds[i] = hibits1 & ds2[i];
}
twocomp2abs_bang(z, hibits1 && hibits2);
RB_GC_GUARD(x);
@@ -6503,12 +6505,12 @@ rb_big_or(VALUE x, VALUE y)
long tmpn;
if (!RB_INTEGER_TYPE_P(y)) {
- return rb_num_coerce_bit(x, y, '|');
+ return rb_num_coerce_bit(x, y, '|');
}
hibitsx = abs2twocomp(&x, &xn);
if (FIXNUM_P(y)) {
- return bigor_int(x, xn, hibitsx, FIX2LONG(y));
+ return bigor_int(x, xn, hibitsx, FIX2LONG(y));
}
hibitsy = abs2twocomp(&y, &yn);
if (xn > yn) {
@@ -6530,10 +6532,10 @@ rb_big_or(VALUE x, VALUE y)
zds = BDIGITS(z);
for (i=0; i<n1; i++) {
- zds[i] = ds1[i] | ds2[i];
+ zds[i] = ds1[i] | ds2[i];
}
for (; i<n2; i++) {
- zds[i] = hibits1 | ds2[i];
+ zds[i] = hibits1 | ds2[i];
}
twocomp2abs_bang(z, hibits1 || hibits2);
RB_GC_GUARD(x);
@@ -6597,12 +6599,12 @@ rb_big_xor(VALUE x, VALUE y)
long tmpn;
if (!RB_INTEGER_TYPE_P(y)) {
- return rb_num_coerce_bit(x, y, '^');
+ return rb_num_coerce_bit(x, y, '^');
}
hibitsx = abs2twocomp(&x, &xn);
if (FIXNUM_P(y)) {
- return bigxor_int(x, xn, hibitsx, FIX2LONG(y));
+ return bigxor_int(x, xn, hibitsx, FIX2LONG(y));
}
hibitsy = abs2twocomp(&y, &yn);
if (xn > yn) {
@@ -6621,10 +6623,10 @@ rb_big_xor(VALUE x, VALUE y)
zds = BDIGITS(z);
for (i=0; i<n1; i++) {
- zds[i] = ds1[i] ^ ds2[i];
+ zds[i] = ds1[i] ^ ds2[i];
}
for (; i<n2; i++) {
- zds[i] = hibitsx ^ ds2[i];
+ zds[i] = hibitsx ^ ds2[i];
}
twocomp2abs_bang(z, (hibits1 ^ hibits2) != 0);
RB_GC_GUARD(x);
@@ -6640,25 +6642,25 @@ rb_big_lshift(VALUE x, VALUE y)
int shift_numbits;
for (;;) {
- if (FIXNUM_P(y)) {
- long l = FIX2LONG(y);
+ if (FIXNUM_P(y)) {
+ long l = FIX2LONG(y);
unsigned long shift;
- if (0 <= l) {
- lshift_p = 1;
+ if (0 <= l) {
+ lshift_p = 1;
shift = l;
}
else {
- lshift_p = 0;
- shift = 1+(unsigned long)(-(l+1));
- }
+ lshift_p = 0;
+ shift = 1+(unsigned long)(-(l+1));
+ }
shift_numbits = (int)(shift & (BITSPERDIG-1));
shift_numdigits = shift >> bit_length(BITSPERDIG-1);
return bignorm(big_shift3(x, lshift_p, shift_numdigits, shift_numbits));
- }
- else if (RB_BIGNUM_TYPE_P(y)) {
+ }
+ else if (RB_BIGNUM_TYPE_P(y)) {
return bignorm(big_shift2(x, 1, y));
- }
- y = rb_to_int(y);
+ }
+ y = rb_to_int(y);
}
}
@@ -6670,8 +6672,8 @@ rb_big_rshift(VALUE x, VALUE y)
int shift_numbits;
for (;;) {
- if (FIXNUM_P(y)) {
- long l = FIX2LONG(y);
+ if (FIXNUM_P(y)) {
+ long l = FIX2LONG(y);
unsigned long shift;
if (0 <= l) {
lshift_p = 0;
@@ -6679,16 +6681,16 @@ rb_big_rshift(VALUE x, VALUE y)
}
else {
lshift_p = 1;
- shift = 1+(unsigned long)(-(l+1));
- }
+ shift = 1+(unsigned long)(-(l+1));
+ }
shift_numbits = (int)(shift & (BITSPERDIG-1));
shift_numdigits = shift >> bit_length(BITSPERDIG-1);
return bignorm(big_shift3(x, lshift_p, shift_numdigits, shift_numbits));
- }
- else if (RB_BIGNUM_TYPE_P(y)) {
+ }
+ else if (RB_BIGNUM_TYPE_P(y)) {
return bignorm(big_shift2(x, 0, y));
- }
- y = rb_to_int(y);
+ }
+ y = rb_to_int(y);
}
}
@@ -6702,22 +6704,22 @@ rb_big_aref(VALUE x, VALUE y)
BDIGIT bit;
if (RB_BIGNUM_TYPE_P(y)) {
- if (BIGNUM_NEGATIVE_P(y))
- return INT2FIX(0);
- bigtrunc(y);
- if (BIGSIZE(y) > sizeof(size_t)) {
- return BIGNUM_SIGN(x) ? INT2FIX(0) : INT2FIX(1);
- }
+ if (BIGNUM_NEGATIVE_P(y))
+ return INT2FIX(0);
+ bigtrunc(y);
+ if (BIGSIZE(y) > sizeof(size_t)) {
+ return BIGNUM_SIGN(x) ? INT2FIX(0) : INT2FIX(1);
+ }
#if SIZEOF_SIZE_T <= SIZEOF_LONG
- shift = big2ulong(y, "long");
+ shift = big2ulong(y, "long");
#else
- shift = big2ull(y, "long long");
+ shift = big2ull(y, "long long");
#endif
}
else {
- l = NUM2LONG(y);
- if (l < 0) return INT2FIX(0);
- shift = (size_t)l;
+ l = NUM2LONG(y);
+ if (l < 0) return INT2FIX(0);
+ shift = (size_t)l;
}
s1 = shift/BITSPERDIG;
s2 = shift%BITSPERDIG;
@@ -6778,8 +6780,8 @@ VALUE
rb_big_abs(VALUE x)
{
if (BIGNUM_NEGATIVE_P(x)) {
- x = rb_big_clone(x);
- BIGNUM_SET_POSITIVE_SIGN(x);
+ x = rb_big_clone(x);
+ BIGNUM_SET_POSITIVE_SIGN(x);
}
return x;
}
@@ -6853,7 +6855,7 @@ VALUE
rb_big_even_p(VALUE num)
{
if (BIGNUM_LEN(num) != 0 && BDIGITS(num)[0] & 1) {
- return Qfalse;
+ return Qfalse;
}
return Qtrue;
}
@@ -6884,21 +6886,21 @@ estimate_initial_sqrt(VALUE *xp, const size_t xn, const BDIGIT *nds, size_t len)
double f;
if (rshift > 0) {
- lowbits = (BDIGIT)d & ~(~(BDIGIT)1U << rshift);
- d >>= rshift;
+ lowbits = (BDIGIT)d & ~(~(BDIGIT)1U << rshift);
+ d >>= rshift;
}
else if (rshift < 0) {
- d <<= -rshift;
- d |= nds[len-dbl_per_bdig-1] >> (BITSPERDIG+rshift);
+ d <<= -rshift;
+ d |= nds[len-dbl_per_bdig-1] >> (BITSPERDIG+rshift);
}
f = sqrt(BDIGIT_DBL_TO_DOUBLE(d));
d = (BDIGIT_DBL)ceil(f);
if (BDIGIT_DBL_TO_DOUBLE(d) == f) {
- if (lowbits || (lowbits = !bary_zero_p(nds, len-dbl_per_bdig)))
- ++d;
+ if (lowbits || (lowbits = !bary_zero_p(nds, len-dbl_per_bdig)))
+ ++d;
}
else {
- lowbits = 1;
+ lowbits = 1;
}
rshift /= 2;
rshift += (2-(len&1))*BITSPERDIG/2;
@@ -6930,29 +6932,29 @@ rb_big_isqrt(VALUE n)
BDIGIT *xds;
if (len <= 2) {
- BDIGIT sq = rb_bdigit_dbl_isqrt(bary2bdigitdbl(nds, len));
+ BDIGIT sq = rb_bdigit_dbl_isqrt(bary2bdigitdbl(nds, len));
#if SIZEOF_BDIGIT > SIZEOF_LONG
- return ULL2NUM(sq);
+ return ULL2NUM(sq);
#else
- return ULONG2NUM(sq);
+ return ULONG2NUM(sq);
#endif
}
else if ((xds = estimate_initial_sqrt(&x, xn, nds, len)) != 0) {
- size_t tn = xn + BIGDIVREM_EXTRA_WORDS;
- VALUE t = bignew_1(0, tn, 1);
- BDIGIT *tds = BDIGITS(t);
- tn = BIGNUM_LEN(t);
-
- /* t = n/x */
- while (bary_divmod_branch(tds, tn, NULL, 0, nds, len, xds, xn),
- bary_cmp(tds, tn, xds, xn) < 0) {
- int carry;
- BARY_TRUNC(tds, tn);
- /* x = (x+t)/2 */
- carry = bary_add(xds, xn, xds, xn, tds, tn);
- bary_small_rshift(xds, xds, xn, 1, carry);
- tn = BIGNUM_LEN(t);
- }
+ size_t tn = xn + BIGDIVREM_EXTRA_WORDS;
+ VALUE t = bignew_1(0, tn, 1);
+ BDIGIT *tds = BDIGITS(t);
+ tn = BIGNUM_LEN(t);
+
+ /* t = n/x */
+ while (bary_divmod_branch(tds, tn, NULL, 0, nds, len, xds, xn),
+ bary_cmp(tds, tn, xds, xn) < 0) {
+ int carry;
+ BARY_TRUNC(tds, tn);
+ /* x = (x+t)/2 */
+ carry = bary_add(xds, xn, xds, xn, tds, tn);
+ bary_small_rshift(xds, xds, xn, 1, carry);
+ tn = BIGNUM_LEN(t);
+ }
}
RBASIC_SET_CLASS_RAW(x, rb_cInteger);
return x;
@@ -7160,7 +7162,7 @@ rb_int_powm(int const argc, VALUE * const argv, VALUE const num)
}
else {
if (rb_bigzero_p(m)) rb_num_zerodiv();
- if (bignorm(m) == INT2FIX(1)) return INT2FIX(0);
+ if (bignorm(m) == INT2FIX(1)) return INT2FIX(0);
return int_pow_tmp3(rb_int_modulo(a, m), b, m, nega_flg);
}
}
diff --git a/bin/gem b/bin/gem
index a4ec754abb..1c16ea7ddd 100755
--- a/bin/gem
+++ b/bin/gem
@@ -5,21 +5,6 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems'
-require 'rubygems/gem_runner'
-require 'rubygems/exceptions'
-
-required_version = Gem::Requirement.new ">= 1.8.7"
-
-unless required_version.satisfied_by? Gem.ruby_version then
- abort "Expected Ruby Version #{required_version}, is #{Gem.ruby_version}"
-end
-
-args = ARGV.clone
-
-begin
- Gem::GemRunner.new.run args
-rescue Gem::SystemExitException => e
- exit e.exit_code
-end
+require "rubygems/gem_runner"
+Gem::GemRunner.new.run ARGV.clone
diff --git a/bootstraptest/runner.rb b/bootstraptest/runner.rb
index 3d42390254..f9b3e919b8 100755
--- a/bootstraptest/runner.rb
+++ b/bootstraptest/runner.rb
@@ -61,7 +61,7 @@ if !Dir.respond_to?(:mktmpdir)
end
# Configuration
-BT = Struct.new(:ruby,
+bt = Struct.new(:ruby,
:verbose,
:color,
:tty,
@@ -73,9 +73,78 @@ BT = Struct.new(:ruby,
:failed,
:reset,
:columns,
+ :window_width,
:width,
+ :indent,
:platform,
- ).new
+ )
+BT = Class.new(bt) do
+ def indent=(n)
+ super
+ if (self.columns ||= 0) < n
+ $stderr.print(' ' * (n - self.columns))
+ end
+ self.columns = indent
+ end
+
+ def putc(c)
+ unless self.quiet
+ if self.window_width == nil
+ unless w = ENV["COLUMNS"] and (w = w.to_i) > 0
+ w = 80
+ end
+ w -= 1
+ self.window_width = w
+ end
+ if self.window_width and self.columns >= self.window_width
+ $stderr.print "\n", " " * (self.indent ||= 0)
+ self.columns = indent
+ end
+ $stderr.print c
+ $stderr.flush
+ self.columns += 1
+ end
+ end
+
+ def wn=(wn)
+ unless wn == 1
+ if /(?:\A|\s)--jobserver-(?:auth|fds)=(?:(\d+),(\d+)|fifo:((?:\\.|\S)+))/ =~ ENV.delete("MAKEFLAGS")
+ begin
+ if fifo = $3
+ fifo.gsub!(/\\(?=.)/, '')
+ r = File.open(fifo, IO::RDONLY|IO::NONBLOCK|IO::BINARY)
+ w = File.open(fifo, IO::WRONLY|IO::NONBLOCK|IO::BINARY)
+ else
+ r = IO.for_fd($1.to_i(10), "rb", autoclose: false)
+ w = IO.for_fd($2.to_i(10), "wb", autoclose: false)
+ end
+ rescue => e
+ r.close if r
+ else
+ r.close_on_exec = true
+ w.close_on_exec = true
+ tokens = r.read_nonblock(wn > 0 ? wn : 1024, exception: false)
+ r.close
+ if String === tokens
+ tokens.freeze
+ auth = w
+ w = nil
+ at_exit {auth << tokens; auth.close}
+ wn = tokens.size + 1
+ else
+ w.close
+ wn = 1
+ end
+ end
+ end
+ if wn <= 0
+ require 'etc'
+ wn = [Etc.nprocessors / 2, 1].max
+ end
+ end
+ super wn
+ end
+end.new
BT_STATE = Struct.new(:count, :error).new
@@ -87,7 +156,7 @@ def main
BT.color = nil
BT.tty = nil
BT.quiet = false
- BT.wn = 1
+ # BT.wn = 1
dir = nil
quiet = false
tests = nil
@@ -122,12 +191,7 @@ def main
BT.quiet = true
true
when /\A-j(\d+)?/
- wn = $1.to_i
- if wn <= 0
- require 'etc'
- wn = [Etc.nprocessors / 2, 1].max
- end
- BT.wn = wn
+ BT.wn = $1.to_i
true
when /\A(-v|--v(erbose))\z/
BT.verbose = true
@@ -154,8 +218,7 @@ End
end
}
if tests and not ARGV.empty?
- $stderr.puts "--tests and arguments are exclusive"
- exit false
+ abort "--sets and arguments are exclusive"
end
tests ||= ARGV
tests = Dir.glob("#{File.dirname($0)}/test_*.rb").sort if tests.empty?
@@ -164,6 +227,7 @@ End
BT.progress = %w[- \\ | /]
BT.progress_bs = "\b" * BT.progress[0].size
BT.tty = $stderr.tty? if BT.tty.nil?
+ BT.wn ||= /-j(\d+)?/ =~ (ENV["MAKEFLAGS"] || ENV["MFLAGS"]) ? $1.to_i : 1
case BT.color
when nil
@@ -241,7 +305,7 @@ def concurrent_exec_test
end
end
- $stderr.print ' ' unless BT.quiet
+ BT.indent = 1
aq.close
i = 1
term_wn = 0
@@ -253,7 +317,7 @@ def concurrent_exec_test
when BT.tty
$stderr.print "#{BT.progress_bs}#{BT.progress[(i+=1) % BT.progress.size]}"
else
- $stderr.print '.'
+ BT.putc '.'
end
else
term_wn += 1
@@ -275,7 +339,7 @@ def exec_test(pathes)
# execute tests
if BT.wn > 1
- concurrent_exec_test if BT.wn > 1
+ concurrent_exec_test
else
prev_basename = nil
Assertion.all.each do |basename, assertions|
@@ -428,7 +492,7 @@ class Assertion < Struct.new(:src, :path, :lineno, :proc)
elsif BT.verbose
$stderr.printf(". %.3f\n", t)
else
- $stderr.print '.'
+ BT.putc '.'
end
else
$stderr.print "#{BT.failed}F"
diff --git a/bootstraptest/test_attr.rb b/bootstraptest/test_attr.rb
index 721a847145..3cb9d3eb39 100644
--- a/bootstraptest/test_attr.rb
+++ b/bootstraptest/test_attr.rb
@@ -34,3 +34,19 @@ assert_equal %{ok}, %{
print "ok"
end
}, '[ruby-core:15120]'
+
+assert_equal %{ok}, %{
+ class Big
+ attr_reader :foo
+ def initialize
+ @foo = "ok"
+ end
+ end
+
+ obj = Big.new
+ 100.times do |i|
+ obj.instance_variable_set(:"@ivar_\#{i}", i)
+ end
+
+ Big.new.foo
+}
diff --git a/bootstraptest/test_io.rb b/bootstraptest/test_io.rb
index c6c3772d36..666e5a011b 100644
--- a/bootstraptest/test_io.rb
+++ b/bootstraptest/test_io.rb
@@ -1,3 +1,4 @@
+/freebsd/ =~ RUBY_PLATFORM or
assert_finish 5, %q{
r, w = IO.pipe
t1 = Thread.new { r.sysread(1) }
@@ -30,6 +31,7 @@ assert_finish 10, %q{
end
}, '[ruby-dev:32566]'
+/freebsd/ =~ RUBY_PLATFORM or
assert_finish 5, %q{
r, w = IO.pipe
Thread.new {
diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb
index b29db7ab0e..67e66b03ee 100644
--- a/bootstraptest/test_ractor.rb
+++ b/bootstraptest/test_ractor.rb
@@ -283,8 +283,9 @@ assert_equal 30.times.map { 'ok' }.to_s, %q{
30.times.map{|i|
test i
}
-} unless ENV['RUN_OPTS'] =~ /--jit-min-calls=5/ || # This always fails with --jit-wait --jit-min-calls=5
- (ENV.key?('TRAVIS') && ENV['TRAVIS_CPU_ARCH'] == 'arm64') # https://bugs.ruby-lang.org/issues/17878
+} unless ENV['RUN_OPTS'] =~ /--mjit-call-threshold=5/ || # This always fails with --mjit-wait --mjit-call-threshold=5
+ (ENV.key?('TRAVIS') && ENV['TRAVIS_CPU_ARCH'] == 'arm64') || # https://bugs.ruby-lang.org/issues/17878
+ true # too flaky everywhere http://ci.rvm.jp/results/trunk@ruby-sp1/4321096
# Exception for empty select
assert_match /specify at least one ractor/, %q{
@@ -501,7 +502,7 @@ assert_equal '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]', %q{
rs.delete r
n
}.sort
-}
+} unless /mswin/ =~ RUBY_PLATFORM # randomly hangs on mswin https://github.com/ruby/ruby/actions/runs/3753871445/jobs/6377551069#step:20:131
# Ractor.select also support multiple take, receive and yield
assert_equal '[true, true, true]', %q{
@@ -1472,7 +1473,7 @@ assert_equal "#{N}#{N}", %Q{
}
# enc_table
-assert_equal "#{N/10}", %Q{
+assert_equal "100", %Q{
Ractor.new do
loop do
Encoding.find("test-enc-#{rand(5_000)}").inspect
@@ -1481,7 +1482,7 @@ assert_equal "#{N/10}", %Q{
end
src = Encoding.find("UTF-8")
- #{N/10}.times{|i|
+ 100.times{|i|
src.replicate("test-enc-\#{i}")
}
}
@@ -1579,4 +1580,49 @@ assert_equal "ok", %q{
end
}
+assert_equal "ok", %q{
+ module M
+ def foo
+ @foo
+ end
+ end
+
+ class A
+ include M
+
+ def initialize
+ 100.times { |i| instance_variable_set(:"@var_#{i}", "bad: #{i}") }
+ @foo = 2
+ end
+ end
+
+ class B
+ include M
+
+ def initialize
+ @foo = 1
+ end
+ end
+
+ Ractor.new do
+ b = B.new
+ 100_000.times do
+ raise unless b.foo == 1
+ end
+ end
+
+ a = A.new
+ 100_000.times do
+ raise unless a.foo == 2
+ end
+
+ "ok"
+}
+
+assert_match /\Atest_ractor\.rb:1:\s+warning:\s+Ractor is experimental/, %q{
+ Warning[:experimental] = $VERBOSE = true
+ STDERR.reopen(STDOUT)
+ eval("Ractor.new{}.take", nil, "test_ractor.rb", 1)
+}
+
end # if !ENV['GITHUB_WORKFLOW']
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index 7158486d50..5c655b8f25 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -1,3 +1,33 @@
+# Regression test for yielding with autosplat to block with
+# optional parameters. https://github.com/Shopify/yjit/issues/313
+assert_equal '[:a, :b, :a, :b]', %q{
+ def yielder(arg) = yield(arg) + yield(arg)
+
+ yielder([:a, :b]) do |c = :c, d = :d|
+ [c, d]
+ end
+}
+
+# Regression test for GC mishap while doing shape transition
+assert_equal '[:ok]', %q{
+ # [Bug #19601]
+ class RegressionTest
+ def initialize
+ @a = @b = @fourth_ivar_does_shape_transition = nil
+ end
+
+ def extender
+ @first_extended_ivar = [:ok]
+ end
+ end
+
+ GC.stress = true
+
+ # Used to crash due to GC run in rb_ensure_iv_list_size()
+ # not marking the newly allocated [:ok].
+ RegressionTest.new.extender.itself
+}
+
assert_equal 'true', %q{
# regression test for tracking type of locals for too long
def local_setting_cmp(five)
@@ -45,6 +75,29 @@ assert_normal_exit %q{
Object.send(:remove_const, :Foo)
}
+assert_normal_exit %q{
+ # Test to ensure send on overriden c functions
+ # doesn't corrupt the stack
+ class Bar
+ def bar(x)
+ x
+ end
+ end
+
+ class Foo
+ def bar
+ Bar.new
+ end
+ end
+
+ foo = Foo.new
+ # before this change, this line would error
+ # because "s" would still be on the stack
+ # String.to_s is the overridden method here
+ p foo.bar.bar("s".__send__(:to_s))
+}
+
+
assert_equal '[nil, nil, nil, nil, nil, nil]', %q{
[NilClass, TrueClass, FalseClass, Integer, Float, Symbol].each do |klass|
klass.class_eval("def foo = @foo")
@@ -198,6 +251,8 @@ assert_equal 'string', %q{
# Check that exceptions work when getting global variable
assert_equal 'rescued', %q{
+ Warning[:deprecated] = true
+
module Warning
def warn(message)
raise
@@ -1118,6 +1173,38 @@ assert_equal '42', %q{
run
}
+# splatting an empty array on a specialized method
+assert_equal 'ok', %q{
+ def run
+ "ok".to_s(*[])
+ end
+
+ run
+ run
+}
+
+# splatting an single element array on a specialized method
+assert_equal '[1]', %q{
+ def run
+ [].<<(*[1])
+ end
+
+ run
+ run
+}
+
+# specialized method with wrong args
+assert_equal 'ok', %q{
+ def run(x)
+ "bad".to_s(123) if x
+ rescue
+ :ok
+ end
+
+ run(false)
+ run(true)
+}
+
# getinstancevariable on Symbol
assert_equal '[nil, nil]', %q{
# @foo to exercise the getinstancevariable instruction
@@ -1224,6 +1311,19 @@ assert_equal '[1, 2, 42]', %q{
[foo {1}, foo {2}, foo {42}]
}
+# test calling without block param
+assert_equal '[1, false, 2, false]', %q{
+ def bar
+ block_given? && yield
+ end
+
+ def foo(&block)
+ bar(&block)
+ end
+
+ [foo { 1 }, foo, foo { 2 }, foo]
+}
+
# test calling block param failing
assert_equal '42', %q{
def foo(&block)
@@ -2137,7 +2237,7 @@ assert_equal '[[:c_return, :String, :string_alias, "events_to_str"]]', %q{
events.compiled(events)
events
-}
+} unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # MJIT calls extra Ruby methods
# test enabling a TracePoint that targets a particular line in a C method call
assert_equal '[true]', %q{
@@ -2219,7 +2319,7 @@ assert_equal '[[:c_call, :itself]]', %q{
tp.enable { shouldnt_compile }
events
-}
+} unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # MJIT calls extra Ruby methods
# test enabling c_return tracing before compiling
assert_equal '[[:c_return, :itself, main]]', %q{
@@ -2234,6 +2334,26 @@ assert_equal '[[:c_return, :itself, main]]', %q{
tp.enable { shouldnt_compile }
events
+} unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # MJIT calls extra Ruby methods
+
+# test c_call invalidation
+assert_equal '[[:c_call, :itself]]', %q{
+ # enable the event once to make sure invalidation
+ # happens the second time we enable it
+ TracePoint.new(:c_call) {}.enable{}
+
+ def compiled
+ itself
+ end
+
+ # assume first call compiles
+ compiled
+
+ events = []
+ tp = TracePoint.new(:c_call) { |tp| events << [tp.event, tp.method_id] }
+ tp.enable { compiled }
+
+ events
}
# test enabling tracing for a suspended fiber
@@ -2888,11 +3008,20 @@ assert_equal 'new', %q{
foo
end
+ def bar
+ :bar
+ end
+
+
test
test
RubyVM::YJIT.simulate_oom! if defined?(RubyVM::YJIT)
+ # Old simulat_omm! leaves one byte of space and this fills it up
+ bar
+ bar
+
def foo
:new
end
@@ -3053,3 +3182,349 @@ assert_equal '10', %q{
a.length
}
+
+# checktype
+assert_equal 'false', %q{
+ def function()
+ [1, 2] in [Integer, String]
+ end
+ function()
+}
+
+# opt_send_without_block (VM_METHOD_TYPE_ATTRSET)
+assert_equal 'foo', %q{
+ class Foo
+ attr_writer :foo
+
+ def foo()
+ self.foo = "foo"
+ end
+ end
+ foo = Foo.new
+ foo.foo
+}
+
+# anytostring, intern
+assert_equal 'true', %q{
+ def foo()
+ :"#{true}"
+ end
+ foo()
+}
+
+# toregexp, objtostring
+assert_equal '/true/', %q{
+ def foo()
+ /#{true}/
+ end
+ foo().inspect
+}
+
+# concatstrings, objtostring
+assert_equal '9001', %q{
+ def foo()
+ "#{9001}"
+ end
+ foo()
+}
+
+# opt_send_without_block (VM_METHOD_TYPE_CFUNC)
+assert_equal 'nil', %q{
+ def foo
+ nil.inspect # argc: 0
+ end
+ foo
+}
+assert_equal '4', %q{
+ def foo
+ 2.pow(2) # argc: 1
+ end
+ foo
+}
+assert_equal 'aba', %q{
+ def foo
+ "abc".tr("c", "a") # argc: 2
+ end
+ foo
+}
+assert_equal 'true', %q{
+ def foo
+ respond_to?(:inspect) # argc: -1
+ end
+ foo
+}
+assert_equal '["a", "b"]', %q{
+ def foo
+ "a\nb".lines(chomp: true) # kwargs
+ end
+ foo
+}
+
+# invokebuiltin
+assert_equal '123', %q{
+ def foo(obj)
+ obj.foo = 123
+ end
+
+ struct = Struct.new(:foo)
+ obj = struct.new
+ foo(obj)
+}
+
+# invokebuiltin_delegate
+assert_equal '.', %q{
+ def foo(path)
+ Dir.open(path).path
+ end
+ foo(".")
+}
+
+# opt_invokebuiltin_delegate_leave
+assert_equal '[0]', %q{"\x00".unpack("c")}
+
+# opt_send_without_block (VM_METHOD_TYPE_ISEQ)
+assert_equal '1', %q{
+ def foo = 1
+ def bar = foo
+ bar
+}
+assert_equal '[1, 2, 3]', %q{
+ def foo(a, b) = [1, a, b]
+ def bar = foo(2, 3)
+ bar
+}
+assert_equal '[1, 2, 3, 4, 5, 6]', %q{
+ def foo(a, b, c:, d:, e: 0, f: 6) = [a, b, c, d, e, f]
+ def bar = foo(1, 2, c: 3, d: 4, e: 5)
+ bar
+}
+assert_equal '[1, 2, 3, 4]', %q{
+ def foo(a, b = 2) = [a, b]
+ def bar = foo(1) + foo(3, 4)
+ bar
+}
+
+assert_equal '1', %q{
+ def foo(a) = a
+ def bar = foo(1) { 2 }
+ bar
+}
+assert_equal '[1, 2]', %q{
+ def foo(a, &block) = [a, block.call]
+ def bar = foo(1) { 2 }
+ bar
+}
+
+# opt_send_without_block (VM_METHOD_TYPE_IVAR)
+assert_equal 'foo', %q{
+ class Foo
+ attr_reader :foo
+
+ def initialize
+ @foo = "foo"
+ end
+ end
+ Foo.new.foo
+}
+
+# opt_send_without_block (VM_METHOD_TYPE_OPTIMIZED)
+assert_equal 'foo', %q{
+ Foo = Struct.new(:bar)
+ Foo.new("bar").bar = "foo"
+}
+assert_equal 'foo', %q{
+ Foo = Struct.new(:bar)
+ Foo.new("foo").bar
+}
+
+# getblockparamproxy
+assert_equal 'foo', %q{
+ def foo(&block)
+ block.call
+ end
+ foo { "foo" }
+}
+
+# getblockparam
+assert_equal 'foo', %q{
+ def foo(&block)
+ block
+ end
+ foo { "foo" }.call
+}
+
+assert_equal '[1, 2]', %q{
+ def foo
+ x = [2]
+ [1, *x]
+ end
+
+ foo
+ foo
+}
+
+# respond_to? with changing symbol
+assert_equal 'false', %q{
+ def foo(name)
+ :sym.respond_to?(name)
+ end
+ foo(:to_s)
+ foo(:to_s)
+ foo(:not_exist)
+}
+
+# respond_to? with method being defined
+assert_equal 'true', %q{
+ def foo
+ :sym.respond_to?(:not_yet_defined)
+ end
+ foo
+ foo
+ module Kernel
+ def not_yet_defined = true
+ end
+ foo
+}
+
+# respond_to? with undef method
+assert_equal 'false', %q{
+ module Kernel
+ def to_be_removed = true
+ end
+ def foo
+ :sym.respond_to?(:to_be_removed)
+ end
+ foo
+ foo
+ class Object
+ undef_method :to_be_removed
+ end
+ foo
+}
+
+# respond_to? with respond_to_missing?
+assert_equal 'true', %q{
+ class Foo
+ end
+ def foo(x)
+ x.respond_to?(:bar)
+ end
+ foo(Foo.new)
+ foo(Foo.new)
+ class Foo
+ def respond_to_missing?(*) = true
+ end
+ foo(Foo.new)
+}
+
+# bmethod
+assert_equal '[1, 2, 3]', %q{
+ one = 1
+ define_method(:foo) do
+ one
+ end
+
+ 3.times.map { |i| foo + i }
+}
+
+# return inside bmethod
+assert_equal 'ok', %q{
+ define_method(:foo) do
+ 1.tap { return :ok }
+ end
+
+ foo
+}
+
+# bmethod optional and keywords
+assert_equal '[[1, nil, 2]]', %q{
+ define_method(:opt_and_kwargs) do |a = {}, b: nil, c: nil|
+ [a, b, c]
+ end
+
+ 5.times.map { opt_and_kwargs(1, c: 2) }.uniq
+}
+
+# bmethod with forwarded block
+assert_equal '2', %q{
+ define_method(:foo) do |&block|
+ block.call
+ end
+
+ def bar(&block)
+ foo(&block)
+ end
+
+ bar { 1 }
+ bar { 2 }
+}
+
+# bmethod with forwarded block and arguments
+assert_equal '5', %q{
+ define_method(:foo) do |n, &block|
+ n + block.call
+ end
+
+ def bar(n, &block)
+ foo(n, &block)
+ end
+
+ bar(0) { 1 }
+ bar(3) { 2 }
+}
+
+# bmethod with forwarded unwanted block
+assert_equal '1', %q{
+ one = 1
+ define_method(:foo) do
+ one
+ end
+
+ def bar(&block)
+ foo(&block)
+ end
+
+ bar { }
+ bar { }
+}
+
+# test for return stub lifetime issue
+assert_equal '1', %q{
+ def foo(n)
+ if n == 2
+ return 1.times { Object.define_method(:foo) {} }
+ end
+
+ foo(n + 1)
+ end
+
+ foo(1)
+}
+
+# case-when with redefined ===
+assert_equal 'ok', %q{
+ class Symbol
+ def ===(a)
+ true
+ end
+ end
+
+ def cw(arg)
+ case arg
+ when :b
+ :ok
+ when 4
+ :ng
+ end
+ end
+
+ cw(4)
+}
+
+assert_normal_exit %{
+ class Bug20997
+ def foo(&) = self.class.name(&)
+
+ new.foo
+ end
+}
diff --git a/builtin.h b/builtin.h
index b827b28928..38ad5a1629 100644
--- a/builtin.h
+++ b/builtin.h
@@ -13,11 +13,11 @@ struct rb_builtin_function {
const char * const name;
// for jit
- void (*compiler)(FILE *, long, unsigned, bool);
+ void (*compiler)(VALUE, long, unsigned, bool);
};
#define RB_BUILTIN_FUNCTION(_i, _name, _fname, _arity, _compiler) {\
- .name = #_name, \
+ .name = _i < 0 ? NULL : #_name, \
.func_ptr = (void *)_fname, \
.argc = _arity, \
.index = _i, \
diff --git a/ccan/check_type/check_type.h b/ccan/check_type/check_type.h
index e795ad71d0..659e1a5a83 100644
--- a/ccan/check_type/check_type.h
+++ b/ccan/check_type/check_type.h
@@ -44,7 +44,7 @@
* ((encl_type *) \
* ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr))))
*/
-#if HAVE_TYPEOF
+#if defined(HAVE_TYPEOF) && HAVE_TYPEOF
#define ccan_check_type(expr, type) \
((typeof(expr) *)0 != (type *)0)
diff --git a/ccan/container_of/container_of.h b/ccan/container_of/container_of.h
index b30c347d57..872bb6ea6e 100644
--- a/ccan/container_of/container_of.h
+++ b/ccan/container_of/container_of.h
@@ -112,7 +112,7 @@ static inline char *container_of_or_null_(void *member_ptr, size_t offset)
* return i;
* }
*/
-#if HAVE_TYPEOF
+#if defined(HAVE_TYPEOF) && HAVE_TYPEOF
#define ccan_container_of_var(member_ptr, container_var, member) \
ccan_container_of(member_ptr, typeof(*container_var), member)
#else
@@ -131,7 +131,7 @@ static inline char *container_of_or_null_(void *member_ptr, size_t offset)
* structure memory layout.
*
*/
-#if HAVE_TYPEOF
+#if defined(HAVE_TYPEOF) && HAVE_TYPEOF
#define ccan_container_off_var(var, member) \
ccan_container_off(typeof(*var), member)
#else
diff --git a/ccan/list/list.h b/ccan/list/list.h
index 91787bfdb3..30b2af04e9 100644
--- a/ccan/list/list.h
+++ b/ccan/list/list.h
@@ -770,7 +770,7 @@ static inline struct ccan_list_node *ccan_list_node_from_off_(void *ptr, size_t
(ccan_container_off_var(var, member) + \
ccan_check_type(var->member, struct ccan_list_node))
-#if HAVE_TYPEOF
+#if defined(HAVE_TYPEOF) && HAVE_TYPEOF
#define ccan_list_typeof(var) typeof(var)
#else
#define ccan_list_typeof(var) void *
diff --git a/class.c b/class.c
index 6db373edc1..cf0b7b821f 100644
--- a/class.c
+++ b/class.c
@@ -64,7 +64,7 @@ push_subclass_entry_to_list(VALUE super, VALUE klass)
void
rb_class_subclass_add(VALUE super, VALUE klass)
{
- if (super && super != Qundef) {
+ if (super && !UNDEF_P(super)) {
rb_subclass_entry_t *entry = push_subclass_entry_to_list(super, klass);
RCLASS_SUBCLASS_ENTRY(klass) = entry;
}
@@ -106,7 +106,7 @@ rb_class_remove_from_super_subclasses(VALUE klass)
next->prev = prev;
}
- xfree(entry);
+ xfree(entry);
}
RCLASS_SUBCLASS_ENTRY(klass) = NULL;
@@ -123,11 +123,11 @@ rb_class_remove_from_module_subclasses(VALUE klass)
if (prev) {
prev->next = next;
}
- if (next) {
+ if (next) {
next->prev = prev;
- }
+ }
- xfree(entry);
+ xfree(entry);
}
RCLASS_MODULE_SUBCLASS_ENTRY(klass) = NULL;
@@ -148,11 +148,11 @@ rb_class_foreach_subclass(VALUE klass, void (*f)(VALUE, VALUE), VALUE arg)
/* do not be tempted to simplify this loop into a for loop, the order of
operations is important here if `f` modifies the linked list */
while (cur) {
- VALUE curklass = cur->klass;
- cur = cur->next;
+ VALUE curklass = cur->klass;
+ cur = cur->next;
// do not trigger GC during f, otherwise the cur will become
// a dangling pointer if the subclass is collected
- f(curklass, arg);
+ f(curklass, arg);
}
}
@@ -197,7 +197,7 @@ class_alloc(VALUE flags, VALUE klass)
{
size_t alloc_size = sizeof(struct RClass);
-#if USE_RVARGC
+#if RCLASS_EXT_EMBEDDED
alloc_size += sizeof(rb_classext_t);
#endif
@@ -206,17 +206,13 @@ class_alloc(VALUE flags, VALUE klass)
if (RGENGC_WB_PROTECTED_CLASS) flags |= FL_WB_PROTECTED;
RVARGC_NEWOBJ_OF(obj, struct RClass, klass, flags, alloc_size);
-#if USE_RVARGC
+#if RCLASS_EXT_EMBEDDED
memset(RCLASS_EXT(obj), 0, sizeof(rb_classext_t));
-# if SIZEOF_SERIAL_T != SIZEOF_VALUE
- RCLASS(obj)->class_serial_ptr = ZALLOC(rb_serial_t);
-# endif
#else
obj->ptr = ZALLOC(rb_classext_t);
#endif
/* ZALLOC
- RCLASS_IV_TBL(obj) = 0;
RCLASS_CONST_TBL(obj) = 0;
RCLASS_M_TBL(obj) = 0;
RCLASS_IV_INDEX_TBL(obj) = 0;
@@ -226,7 +222,6 @@ class_alloc(VALUE flags, VALUE klass)
RCLASS_MODULE_SUBCLASSES(obj) = NULL;
*/
RCLASS_SET_ORIGIN((VALUE)obj, (VALUE)obj);
- RCLASS_SERIAL(obj) = rb_next_class_serial();
RB_OBJ_WRITE(obj, &RCLASS_REFINED_CLASS(obj), Qnil);
RCLASS_ALLOCATOR(obj) = 0;
@@ -282,7 +277,7 @@ rb_class_update_superclasses(VALUE klass)
VALUE super = RCLASS_SUPER(klass);
if (!RB_TYPE_P(klass, T_CLASS)) return;
- if (super == Qundef) return;
+ if (UNDEF_P(super)) return;
// If the superclass array is already built
if (RCLASS_SUPERCLASSES(klass))
@@ -316,13 +311,13 @@ rb_check_inheritable(VALUE super)
{
if (!RB_TYPE_P(super, T_CLASS)) {
rb_raise(rb_eTypeError, "superclass must be an instance of Class (given an instance of %"PRIsVALUE")",
- rb_obj_class(super));
+ rb_obj_class(super));
}
if (RBASIC(super)->flags & FL_SINGLETON) {
- rb_raise(rb_eTypeError, "can't make subclass of singleton class");
+ rb_raise(rb_eTypeError, "can't make subclass of singleton class");
}
if (super == rb_cClass) {
- rb_raise(rb_eTypeError, "can't make subclass of Class");
+ rb_raise(rb_eTypeError, "can't make subclass of Class");
}
}
@@ -331,7 +326,13 @@ rb_class_new(VALUE super)
{
Check_Type(super, T_CLASS);
rb_check_inheritable(super);
- return rb_class_boot(super);
+ VALUE klass = rb_class_boot(super);
+
+ if (super != rb_cObject && super != rb_cBasicObject) {
+ RCLASS_EXT(klass)->max_iv_count = RCLASS_EXT(super)->max_iv_count;
+ }
+
+ return klass;
}
VALUE
@@ -344,12 +345,12 @@ static void
clone_method(VALUE old_klass, VALUE new_klass, ID mid, const rb_method_entry_t *me)
{
if (me->def->type == VM_METHOD_TYPE_ISEQ) {
- rb_cref_t *new_cref;
- rb_vm_rewrite_cref(me->def->body.iseq.cref, old_klass, new_klass, &new_cref);
- rb_add_method_iseq(new_klass, mid, me->def->body.iseq.iseqptr, new_cref, METHOD_ENTRY_VISI(me));
+ rb_cref_t *new_cref;
+ rb_vm_rewrite_cref(me->def->body.iseq.cref, old_klass, new_klass, &new_cref);
+ rb_add_method_iseq(new_klass, mid, me->def->body.iseq.iseqptr, new_cref, METHOD_ENTRY_VISI(me));
}
else {
- rb_method_entry_set(new_klass, mid, me, METHOD_ENTRY_VISI(me));
+ rb_method_entry_set(new_klass, mid, me, METHOD_ENTRY_VISI(me));
}
}
@@ -393,45 +394,74 @@ static void
class_init_copy_check(VALUE clone, VALUE orig)
{
if (orig == rb_cBasicObject) {
- rb_raise(rb_eTypeError, "can't copy the root class");
+ rb_raise(rb_eTypeError, "can't copy the root class");
}
if (RCLASS_SUPER(clone) != 0 || clone == rb_cBasicObject) {
- rb_raise(rb_eTypeError, "already initialized class");
+ rb_raise(rb_eTypeError, "already initialized class");
}
if (FL_TEST(orig, FL_SINGLETON)) {
- rb_raise(rb_eTypeError, "can't copy singleton class");
+ rb_raise(rb_eTypeError, "can't copy singleton class");
}
}
+struct cvc_table_copy_ctx {
+ VALUE clone;
+ struct rb_id_table * new_table;
+};
+
+static enum rb_id_table_iterator_result
+cvc_table_copy(ID id, VALUE val, void *data) {
+ struct cvc_table_copy_ctx *ctx = (struct cvc_table_copy_ctx *)data;
+ struct rb_cvar_class_tbl_entry * orig_entry;
+ orig_entry = (struct rb_cvar_class_tbl_entry *)val;
+
+ struct rb_cvar_class_tbl_entry *ent;
+
+ ent = ALLOC(struct rb_cvar_class_tbl_entry);
+ ent->class_value = ctx->clone;
+ ent->cref = orig_entry->cref;
+ ent->global_cvar_state = orig_entry->global_cvar_state;
+ rb_id_table_insert(ctx->new_table, id, (VALUE)ent);
+
+ RB_OBJ_WRITTEN(ctx->clone, Qundef, ent->cref);
+
+ return ID_TABLE_CONTINUE;
+}
+
static void
copy_tables(VALUE clone, VALUE orig)
{
- if (RCLASS_IV_TBL(clone)) {
- st_free_table(RCLASS_IV_TBL(clone));
- RCLASS_IV_TBL(clone) = 0;
- }
if (RCLASS_CONST_TBL(clone)) {
- rb_free_const_table(RCLASS_CONST_TBL(clone));
- RCLASS_CONST_TBL(clone) = 0;
+ rb_free_const_table(RCLASS_CONST_TBL(clone));
+ RCLASS_CONST_TBL(clone) = 0;
+ }
+ if (RCLASS_CVC_TBL(orig)) {
+ struct rb_id_table *rb_cvc_tbl = RCLASS_CVC_TBL(orig);
+ struct rb_id_table *rb_cvc_tbl_dup = rb_id_table_create(rb_id_table_size(rb_cvc_tbl));
+
+ struct cvc_table_copy_ctx ctx;
+ ctx.clone = clone;
+ ctx.new_table = rb_cvc_tbl_dup;
+ rb_id_table_foreach(rb_cvc_tbl, cvc_table_copy, &ctx);
+ RCLASS_CVC_TBL(clone) = rb_cvc_tbl_dup;
}
+ rb_id_table_free(RCLASS_M_TBL(clone));
RCLASS_M_TBL(clone) = 0;
- if (RCLASS_IV_TBL(orig)) {
- st_data_t id;
+ if (!RB_TYPE_P(clone, T_ICLASS)) {
+ st_data_t id;
- rb_iv_tbl_copy(clone, orig);
- CONST_ID(id, "__tmp_classpath__");
- st_delete(RCLASS_IV_TBL(clone), &id, 0);
- CONST_ID(id, "__classpath__");
- st_delete(RCLASS_IV_TBL(clone), &id, 0);
- CONST_ID(id, "__classid__");
- st_delete(RCLASS_IV_TBL(clone), &id, 0);
+ rb_iv_tbl_copy(clone, orig);
+ CONST_ID(id, "__tmp_classpath__");
+ rb_attr_delete(clone, id);
+ CONST_ID(id, "__classpath__");
+ rb_attr_delete(clone, id);
}
if (RCLASS_CONST_TBL(orig)) {
- struct clone_const_arg arg;
+ struct clone_const_arg arg;
- arg.tbl = RCLASS_CONST_TBL(clone) = rb_id_table_create(0);
- arg.klass = clone;
- rb_id_table_foreach(RCLASS_CONST_TBL(orig), clone_const_i, &arg);
+ arg.tbl = RCLASS_CONST_TBL(clone) = rb_id_table_create(0);
+ arg.klass = clone;
+ rb_id_table_foreach(RCLASS_CONST_TBL(orig), clone_const_i, &arg);
}
}
@@ -493,11 +523,11 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
RCLASS_ALLOCATOR(clone) = RCLASS_ALLOCATOR(orig);
copy_tables(clone, orig);
if (RCLASS_M_TBL(orig)) {
- struct clone_method_arg arg;
- arg.old_klass = orig;
- arg.new_klass = clone;
- RCLASS_M_TBL_INIT(clone);
- rb_id_table_foreach(RCLASS_M_TBL(orig), clone_method_i, &arg);
+ struct clone_method_arg arg;
+ arg.old_klass = orig;
+ arg.new_klass = clone;
+ RCLASS_M_TBL_INIT(clone);
+ rb_id_table_foreach(RCLASS_M_TBL(orig), clone_method_i, &arg);
}
if (RCLASS_ORIGIN(orig) == orig) {
@@ -507,7 +537,7 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
VALUE p = RCLASS_SUPER(orig);
VALUE orig_origin = RCLASS_ORIGIN(orig);
VALUE prev_clone_p = clone;
- VALUE origin_stack = rb_ary_tmp_new(2);
+ VALUE origin_stack = rb_ary_hidden_new(2);
VALUE origin[2];
VALUE clone_p = 0;
long origin_len;
@@ -526,7 +556,6 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
prev_clone_p = clone_p;
RCLASS_M_TBL(clone_p) = RCLASS_M_TBL(p);
RCLASS_CONST_TBL(clone_p) = RCLASS_CONST_TBL(p);
- RCLASS_IV_TBL(clone_p) = RCLASS_IV_TBL(p);
RCLASS_ALLOCATOR(clone_p) = RCLASS_ALLOCATOR(p);
if (RB_TYPE_P(clone, T_CLASS)) {
RCLASS_SET_INCLUDER(clone_p, clone);
@@ -595,49 +624,47 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
return klass;
}
else {
- /* copy singleton(unnamed) class */
+ /* copy singleton(unnamed) class */
bool klass_of_clone_is_new;
- VALUE clone = class_alloc(RBASIC(klass)->flags, 0);
+ VALUE clone = class_alloc(RBASIC(klass)->flags, 0);
- if (BUILTIN_TYPE(obj) == T_CLASS) {
+ if (BUILTIN_TYPE(obj) == T_CLASS) {
klass_of_clone_is_new = true;
- RBASIC_SET_CLASS(clone, clone);
- }
- else {
+ RBASIC_SET_CLASS(clone, clone);
+ }
+ else {
VALUE klass_metaclass_clone = rb_singleton_class_clone(klass);
// When `METACLASS_OF(klass) == klass_metaclass_clone`, it means the
// recursive call did not clone `METACLASS_OF(klass)`.
klass_of_clone_is_new = (METACLASS_OF(klass) != klass_metaclass_clone);
RBASIC_SET_CLASS(clone, klass_metaclass_clone);
- }
-
- RCLASS_SET_SUPER(clone, RCLASS_SUPER(klass));
- RCLASS_ALLOCATOR(clone) = RCLASS_ALLOCATOR(klass);
- if (RCLASS_IV_TBL(klass)) {
- rb_iv_tbl_copy(clone, klass);
- }
- if (RCLASS_CONST_TBL(klass)) {
- struct clone_const_arg arg;
- arg.tbl = RCLASS_CONST_TBL(clone) = rb_id_table_create(0);
- arg.klass = clone;
- rb_id_table_foreach(RCLASS_CONST_TBL(klass), clone_const_i, &arg);
- }
- if (attach != Qundef) {
- rb_singleton_class_attached(clone, attach);
- }
- RCLASS_M_TBL_INIT(clone);
- {
- struct clone_method_arg arg;
- arg.old_klass = klass;
- arg.new_klass = clone;
- rb_id_table_foreach(RCLASS_M_TBL(klass), clone_method_i, &arg);
- }
+ }
+
+ RCLASS_SET_SUPER(clone, RCLASS_SUPER(klass));
+ RCLASS_ALLOCATOR(clone) = RCLASS_ALLOCATOR(klass);
+ rb_iv_tbl_copy(clone, klass);
+ if (RCLASS_CONST_TBL(klass)) {
+ struct clone_const_arg arg;
+ arg.tbl = RCLASS_CONST_TBL(clone) = rb_id_table_create(0);
+ arg.klass = clone;
+ rb_id_table_foreach(RCLASS_CONST_TBL(klass), clone_const_i, &arg);
+ }
+ if (!UNDEF_P(attach)) {
+ rb_singleton_class_attached(clone, attach);
+ }
+ RCLASS_M_TBL_INIT(clone);
+ {
+ struct clone_method_arg arg;
+ arg.old_klass = klass;
+ arg.new_klass = clone;
+ rb_id_table_foreach(RCLASS_M_TBL(klass), clone_method_i, &arg);
+ }
if (klass_of_clone_is_new) {
rb_singleton_class_attached(METACLASS_OF(clone), clone);
}
- FL_SET(clone, FL_SINGLETON);
+ FL_SET(clone, FL_SINGLETON);
- return clone;
+ return clone;
}
}
@@ -645,7 +672,7 @@ void
rb_singleton_class_attached(VALUE klass, VALUE obj)
{
if (FL_TEST(klass, FL_SINGLETON)) {
- rb_class_ivar_set(klass, id_attached, obj);
+ rb_class_ivar_set(klass, id_attached, obj);
}
}
@@ -666,7 +693,7 @@ int
rb_singleton_class_internal_p(VALUE sklass)
{
return (RB_TYPE_P(rb_attr_get(sklass, id_attached), T_CLASS) &&
- !rb_singleton_class_has_metaclass_p(sklass));
+ !rb_singleton_class_has_metaclass_p(sklass));
}
/*!
@@ -708,13 +735,13 @@ make_metaclass(VALUE klass)
rb_singleton_class_attached(metaclass, klass);
if (META_CLASS_OF_CLASS_CLASS_P(klass)) {
- SET_METACLASS_OF(klass, metaclass);
- SET_METACLASS_OF(metaclass, metaclass);
+ SET_METACLASS_OF(klass, metaclass);
+ SET_METACLASS_OF(metaclass, metaclass);
}
else {
- VALUE tmp = METACLASS_OF(klass); /* for a meta^(n)-class klass, tmp is meta^(n)-class of Class class */
- SET_METACLASS_OF(klass, metaclass);
- SET_METACLASS_OF(metaclass, ENSURE_EIGENCLASS(tmp));
+ VALUE tmp = METACLASS_OF(klass); /* for a meta^(n)-class klass, tmp is meta^(n)-class of Class class */
+ SET_METACLASS_OF(klass, metaclass);
+ SET_METACLASS_OF(metaclass, ENSURE_EIGENCLASS(tmp));
}
super = RCLASS_SUPER(klass);
@@ -855,10 +882,10 @@ VALUE
rb_make_metaclass(VALUE obj, VALUE unused)
{
if (BUILTIN_TYPE(obj) == T_CLASS) {
- return make_metaclass(obj);
+ return make_metaclass(obj);
}
else {
- return make_singleton_class(obj);
+ return make_singleton_class(obj);
}
}
@@ -900,21 +927,21 @@ rb_define_class(const char *name, VALUE super)
id = rb_intern(name);
if (rb_const_defined(rb_cObject, id)) {
- klass = rb_const_get(rb_cObject, id);
- if (!RB_TYPE_P(klass, T_CLASS)) {
- rb_raise(rb_eTypeError, "%s is not a class (%"PRIsVALUE")",
- name, rb_obj_class(klass));
- }
- if (rb_class_real(RCLASS_SUPER(klass)) != super) {
- rb_raise(rb_eTypeError, "superclass mismatch for class %s", name);
- }
+ klass = rb_const_get(rb_cObject, id);
+ if (!RB_TYPE_P(klass, T_CLASS)) {
+ rb_raise(rb_eTypeError, "%s is not a class (%"PRIsVALUE")",
+ name, rb_obj_class(klass));
+ }
+ if (rb_class_real(RCLASS_SUPER(klass)) != super) {
+ rb_raise(rb_eTypeError, "superclass mismatch for class %s", name);
+ }
/* Class may have been defined in Ruby and not pin-rooted */
rb_vm_add_root_module(klass);
- return klass;
+ return klass;
}
if (!super) {
- rb_raise(rb_eArgError, "no super class for `%s'", name);
+ rb_raise(rb_eArgError, "no super class for `%s'", name);
}
klass = rb_define_class_id(id, super);
rb_vm_add_root_module(klass);
@@ -931,42 +958,47 @@ rb_define_class_under(VALUE outer, const char *name, VALUE super)
}
VALUE
-rb_define_class_id_under(VALUE outer, ID id, VALUE super)
+rb_define_class_id_under_no_pin(VALUE outer, ID id, VALUE super)
{
VALUE klass;
if (rb_const_defined_at(outer, id)) {
- klass = rb_const_get_at(outer, id);
- if (!RB_TYPE_P(klass, T_CLASS)) {
- rb_raise(rb_eTypeError, "%"PRIsVALUE"::%"PRIsVALUE" is not a class"
- " (%"PRIsVALUE")",
- outer, rb_id2str(id), rb_obj_class(klass));
- }
- if (rb_class_real(RCLASS_SUPER(klass)) != super) {
- rb_raise(rb_eTypeError, "superclass mismatch for class "
- "%"PRIsVALUE"::%"PRIsVALUE""
- " (%"PRIsVALUE" is given but was %"PRIsVALUE")",
- outer, rb_id2str(id), RCLASS_SUPER(klass), super);
- }
- /* Class may have been defined in Ruby and not pin-rooted */
- rb_vm_add_root_module(klass);
+ klass = rb_const_get_at(outer, id);
+ if (!RB_TYPE_P(klass, T_CLASS)) {
+ rb_raise(rb_eTypeError, "%"PRIsVALUE"::%"PRIsVALUE" is not a class"
+ " (%"PRIsVALUE")",
+ outer, rb_id2str(id), rb_obj_class(klass));
+ }
+ if (rb_class_real(RCLASS_SUPER(klass)) != super) {
+ rb_raise(rb_eTypeError, "superclass mismatch for class "
+ "%"PRIsVALUE"::%"PRIsVALUE""
+ " (%"PRIsVALUE" is given but was %"PRIsVALUE")",
+ outer, rb_id2str(id), RCLASS_SUPER(klass), super);
+ }
- return klass;
+ return klass;
}
if (!super) {
- rb_raise(rb_eArgError, "no super class for `%"PRIsVALUE"::%"PRIsVALUE"'",
- rb_class_path(outer), rb_id2str(id));
+ rb_raise(rb_eArgError, "no super class for `%"PRIsVALUE"::%"PRIsVALUE"'",
+ rb_class_path(outer), rb_id2str(id));
}
klass = rb_define_class_id(id, super);
rb_set_class_path_string(klass, outer, rb_id2str(id));
rb_const_set(outer, id, klass);
rb_class_inherited(super, klass);
- rb_vm_add_root_module(klass);
return klass;
}
VALUE
+rb_define_class_id_under(VALUE outer, ID id, VALUE super)
+{
+ VALUE klass = rb_define_class_id_under_no_pin(outer, id, super);
+ rb_vm_add_root_module(klass);
+ return klass;
+}
+
+VALUE
rb_module_s_alloc(VALUE klass)
{
VALUE mod = class_alloc(T_MODULE, klass);
@@ -1010,14 +1042,14 @@ rb_define_module(const char *name)
id = rb_intern(name);
if (rb_const_defined(rb_cObject, id)) {
- module = rb_const_get(rb_cObject, id);
- if (!RB_TYPE_P(module, T_MODULE)) {
- rb_raise(rb_eTypeError, "%s is not a module (%"PRIsVALUE")",
- name, rb_obj_class(module));
- }
+ module = rb_const_get(rb_cObject, id);
+ if (!RB_TYPE_P(module, T_MODULE)) {
+ rb_raise(rb_eTypeError, "%s is not a module (%"PRIsVALUE")",
+ name, rb_obj_class(module));
+ }
/* Module may have been defined in Ruby and not pin-rooted */
rb_vm_add_root_module(module);
- return module;
+ return module;
}
module = rb_module_new();
rb_vm_add_root_module(module);
@@ -1038,15 +1070,15 @@ rb_define_module_id_under(VALUE outer, ID id)
VALUE module;
if (rb_const_defined_at(outer, id)) {
- module = rb_const_get_at(outer, id);
- if (!RB_TYPE_P(module, T_MODULE)) {
- rb_raise(rb_eTypeError, "%"PRIsVALUE"::%"PRIsVALUE" is not a module"
- " (%"PRIsVALUE")",
- outer, rb_id2str(id), rb_obj_class(module));
- }
+ module = rb_const_get_at(outer, id);
+ if (!RB_TYPE_P(module, T_MODULE)) {
+ rb_raise(rb_eTypeError, "%"PRIsVALUE"::%"PRIsVALUE" is not a module"
+ " (%"PRIsVALUE")",
+ outer, rb_id2str(id), rb_obj_class(module));
+ }
/* Module may have been defined in Ruby and not pin-rooted */
rb_gc_register_mark_object(module);
- return module;
+ return module;
}
module = rb_module_new();
rb_const_set(outer, id, module);
@@ -1065,16 +1097,13 @@ rb_include_class_new(VALUE module, VALUE super)
RCLASS_SET_ORIGIN(klass, klass);
if (BUILTIN_TYPE(module) == T_ICLASS) {
- module = METACLASS_OF(module);
+ module = METACLASS_OF(module);
}
RUBY_ASSERT(!RB_TYPE_P(module, T_ICLASS));
- if (!RCLASS_IV_TBL(module)) {
- RCLASS_IV_TBL(module) = st_init_numtable();
- }
if (!RCLASS_CONST_TBL(module)) {
- RCLASS_CONST_TBL(module) = rb_id_table_create(0);
+ RCLASS_CONST_TBL(module) = rb_id_table_create(0);
}
- RCLASS_IV_TBL(klass) = RCLASS_IV_TBL(module);
+
RCLASS_CVC_TBL(klass) = RCLASS_CVC_TBL(module);
RCLASS_CONST_TBL(klass) = RCLASS_CONST_TBL(module);
@@ -1093,7 +1122,7 @@ ensure_includable(VALUE klass, VALUE module)
Check_Type(module, T_MODULE);
rb_module_set_initialized(module);
if (!NIL_P(rb_refinement_module_get_refined_class(module))) {
- rb_raise(rb_eArgError, "refinement module is not allowed");
+ rb_raise(rb_eArgError, "refinement module is not allowed");
}
}
@@ -1106,7 +1135,7 @@ rb_include_module(VALUE klass, VALUE module)
changed = include_modules_at(klass, RCLASS_ORIGIN(klass), module, TRUE);
if (changed < 0)
- rb_raise(rb_eArgError, "cyclic include detected");
+ rb_raise(rb_eArgError, "cyclic include detected");
if (RB_TYPE_P(klass, T_MODULE)) {
rb_subclass_entry_t *iclass = RCLASS_SUBCLASSES(klass);
@@ -1116,8 +1145,8 @@ rb_include_module(VALUE klass, VALUE module)
iclass = iclass->next;
}
- int do_include = 1;
while (iclass) {
+ int do_include = 1;
VALUE check_class = iclass->klass;
/* During lazy sweeping, iclass->klass could be a dead object that
* has not yet been swept. */
@@ -1184,7 +1213,7 @@ static int
do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super, bool check_cyclic)
{
VALUE p, iclass, origin_stack = 0;
- int method_changed = 0, add_subclass;
+ int method_changed = 0;
long origin_len;
VALUE klass_origin = RCLASS_ORIGIN(klass);
VALUE original_klass = klass;
@@ -1194,8 +1223,8 @@ do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super
while (module) {
int c_seen = FALSE;
- int superclass_seen = FALSE;
- struct rb_id_table *tbl;
+ int superclass_seen = FALSE;
+ struct rb_id_table *tbl;
if (klass == c) {
c_seen = TRUE;
@@ -1245,12 +1274,11 @@ do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super
}
// setup T_ICLASS for the include/prepend module
- iclass = rb_include_class_new(module, super_class);
- c = RCLASS_SET_SUPER(c, iclass);
+ iclass = rb_include_class_new(module, super_class);
+ c = RCLASS_SET_SUPER(c, iclass);
RCLASS_SET_INCLUDER(iclass, klass);
- add_subclass = TRUE;
if (module != RCLASS_ORIGIN(module)) {
- if (!origin_stack) origin_stack = rb_ary_tmp_new(2);
+ if (!origin_stack) origin_stack = rb_ary_hidden_new(2);
VALUE origin[2] = {iclass, RCLASS_ORIGIN(module)};
rb_ary_cat(origin_stack, origin, 2);
}
@@ -1259,28 +1287,25 @@ do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super
RCLASS_SET_ORIGIN(RARRAY_AREF(origin_stack, (origin_len -= 2)), iclass);
RICLASS_SET_ORIGIN_SHARED_MTBL(iclass);
rb_ary_resize(origin_stack, origin_len);
- add_subclass = FALSE;
}
- if (add_subclass) {
- VALUE m = module;
- if (BUILTIN_TYPE(m) == T_ICLASS) m = METACLASS_OF(m);
- rb_module_add_to_subclasses_list(m, iclass);
- }
+ VALUE m = module;
+ if (BUILTIN_TYPE(m) == T_ICLASS) m = METACLASS_OF(m);
+ rb_module_add_to_subclasses_list(m, iclass);
- if (BUILTIN_TYPE(klass) == T_MODULE && FL_TEST(klass, RMODULE_IS_REFINEMENT)) {
- VALUE refined_class =
- rb_refinement_module_get_refined_class(klass);
+ if (BUILTIN_TYPE(klass) == T_MODULE && FL_TEST(klass, RMODULE_IS_REFINEMENT)) {
+ VALUE refined_class =
+ rb_refinement_module_get_refined_class(klass);
rb_id_table_foreach(RCLASS_M_TBL(module), add_refined_method_entry_i, (void *)refined_class);
RUBY_ASSERT(BUILTIN_TYPE(c) == T_MODULE);
- }
+ }
tbl = RCLASS_CONST_TBL(module);
- if (tbl && rb_id_table_size(tbl))
- rb_id_table_foreach(tbl, clear_constant_cache_i, NULL);
+ if (tbl && rb_id_table_size(tbl))
+ rb_id_table_foreach(tbl, clear_constant_cache_i, NULL);
skip:
- module = RCLASS_SUPER(module);
+ module = RCLASS_SUPER(module);
}
return method_changed;
@@ -1302,20 +1327,20 @@ move_refined_method(ID key, VALUE value, void *data)
struct rb_id_table *tbl = RCLASS_M_TBL(klass);
if (me->def->body.refined.orig_me) {
- const rb_method_entry_t *orig_me = me->def->body.refined.orig_me, *new_me;
- RB_OBJ_WRITE(me, &me->def->body.refined.orig_me, NULL);
- new_me = rb_method_entry_clone(me);
+ const rb_method_entry_t *orig_me = me->def->body.refined.orig_me, *new_me;
+ RB_OBJ_WRITE(me, &me->def->body.refined.orig_me, NULL);
+ new_me = rb_method_entry_clone(me);
rb_method_table_insert(klass, tbl, key, new_me);
- rb_method_entry_copy(me, orig_me);
- return ID_TABLE_CONTINUE;
- }
- else {
+ rb_method_entry_copy(me, orig_me);
+ return ID_TABLE_CONTINUE;
+ }
+ else {
rb_method_table_insert(klass, tbl, key, me);
- return ID_TABLE_DELETE;
- }
+ return ID_TABLE_DELETE;
+ }
}
else {
- return ID_TABLE_CONTINUE;
+ return ID_TABLE_CONTINUE;
}
}
@@ -1339,14 +1364,14 @@ ensure_origin(VALUE klass)
{
VALUE origin = RCLASS_ORIGIN(klass);
if (origin == klass) {
- origin = class_alloc(T_ICLASS, klass);
- RCLASS_SET_SUPER(origin, RCLASS_SUPER(klass));
- RCLASS_SET_SUPER(klass, origin);
- RCLASS_SET_ORIGIN(klass, origin);
- RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
- RCLASS_M_TBL_INIT(klass);
+ origin = class_alloc(T_ICLASS, klass);
+ RCLASS_SET_SUPER(origin, RCLASS_SUPER(klass));
+ RCLASS_SET_SUPER(klass, origin);
+ RCLASS_SET_ORIGIN(klass, origin);
+ RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
+ RCLASS_M_TBL_INIT(klass);
rb_id_table_foreach(RCLASS_M_TBL(origin), cache_clear_refined_method, (void *)klass);
- rb_id_table_foreach(RCLASS_M_TBL(origin), move_refined_method, (void *)klass);
+ rb_id_table_foreach(RCLASS_M_TBL(origin), move_refined_method, (void *)klass);
return true;
}
return false;
@@ -1366,7 +1391,7 @@ rb_prepend_module(VALUE klass, VALUE module)
changed = do_include_modules_at(klass, klass, module, FALSE, false);
RUBY_ASSERT(changed >= 0); // already checked for cyclic prepend above
if (changed) {
- rb_vm_check_redefinition_by_prepend(klass);
+ rb_vm_check_redefinition_by_prepend(klass);
}
if (RB_TYPE_P(klass, T_MODULE)) {
rb_subclass_entry_t *iclass = RCLASS_SUBCLASSES(klass);
@@ -1433,10 +1458,10 @@ rb_mod_included_modules(VALUE mod)
for (p = RCLASS_SUPER(mod); p; p = RCLASS_SUPER(p)) {
if (p != origin && RCLASS_ORIGIN(p) == p && BUILTIN_TYPE(p) == T_ICLASS) {
- VALUE m = METACLASS_OF(p);
- if (RB_TYPE_P(m, T_MODULE))
- rb_ary_push(ary, m);
- }
+ VALUE m = METACLASS_OF(p);
+ if (RB_TYPE_P(m, T_MODULE))
+ rb_ary_push(ary, m);
+ }
}
return ary;
}
@@ -1468,8 +1493,8 @@ rb_mod_include_p(VALUE mod, VALUE mod2)
Check_Type(mod2, T_MODULE);
for (p = RCLASS_SUPER(mod); p; p = RCLASS_SUPER(p)) {
if (BUILTIN_TYPE(p) == T_ICLASS && !FL_TEST(p, RICLASS_IS_ORIGIN)) {
- if (METACLASS_OF(p) == mod2) return Qtrue;
- }
+ if (METACLASS_OF(p) == mod2) return Qtrue;
+ }
}
return Qfalse;
}
@@ -1504,12 +1529,12 @@ rb_mod_ancestors(VALUE mod)
for (p = mod; p; p = RCLASS_SUPER(p)) {
if (p == refined_class) break;
if (p != RCLASS_ORIGIN(p)) continue;
- if (BUILTIN_TYPE(p) == T_ICLASS) {
- rb_ary_push(ary, METACLASS_OF(p));
- }
+ if (BUILTIN_TYPE(p) == T_ICLASS) {
+ rb_ary_push(ary, METACLASS_OF(p));
+ }
else {
- rb_ary_push(ary, p);
- }
+ rb_ary_push(ary, p);
+ }
}
return ary;
}
@@ -1583,6 +1608,27 @@ class_descendants(VALUE klass, bool immediate_only)
* A.subclasses #=> [D, B]
* B.subclasses #=> [C]
* C.subclasses #=> []
+ *
+ * Anonymous subclasses (not associated with a constant) are
+ * returned, too:
+ *
+ * c = Class.new(A)
+ * A.subclasses # => [#<Class:0x00007f003c77bd78>, D, B]
+ *
+ * Note that the parent does not hold references to subclasses
+ * and doesn't prevent them from being garbage collected. This
+ * means that the subclass might disappear when all references
+ * to it are dropped:
+ *
+ * # drop the reference to subclass, it can be garbage-collected now
+ * c = nil
+ *
+ * A.subclasses
+ * # It can be
+ * # => [#<Class:0x00007f003c77bd78>, D, B]
+ * # ...or just
+ * # => [D, B]
+ * # ...depending on whether garbage collector was run
*/
VALUE
@@ -1591,6 +1637,33 @@ rb_class_subclasses(VALUE klass)
return class_descendants(klass, true);
}
+/*
+ * call-seq:
+ * attached_object -> object
+ *
+ * Returns the object for which the receiver is the singleton class.
+ *
+ * Raises an TypeError if the class is not a singleton class.
+ *
+ * class Foo; end
+ *
+ * Foo.singleton_class.attached_object #=> Foo
+ * Foo.attached_object #=> TypeError: `Foo' is not a singleton class
+ * Foo.new.singleton_class.attached_object #=> #<Foo:0x000000010491a370>
+ * TrueClass.attached_object #=> TypeError: `TrueClass' is not a singleton class
+ * NilClass.attached_object #=> TypeError: `NilClass' is not a singleton class
+ */
+
+VALUE
+rb_class_attached_object(VALUE klass)
+{
+ if (!FL_TEST(klass, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "`%"PRIsVALUE"' is not a singleton class", klass);
+ }
+
+ return rb_attr_get(klass, id_attached);
+}
+
static void
ins_methods_push(st_data_t name, st_data_t ary)
{
@@ -1603,10 +1676,10 @@ ins_methods_i(st_data_t name, st_data_t type, st_data_t ary)
switch ((rb_method_visibility_t)type) {
case METHOD_VISI_UNDEF:
case METHOD_VISI_PRIVATE:
- break;
+ break;
default: /* everything but private */
- ins_methods_push(name, ary);
- break;
+ ins_methods_push(name, ary);
+ break;
}
return ST_CONTINUE;
}
@@ -1615,7 +1688,7 @@ static int
ins_methods_type_i(st_data_t name, st_data_t type, st_data_t ary, rb_method_visibility_t visi)
{
if ((rb_method_visibility_t)type == visi) {
- ins_methods_push(name, ary);
+ ins_methods_push(name, ary);
}
return ST_CONTINUE;
}
@@ -1641,10 +1714,7 @@ ins_methods_pub_i(st_data_t name, st_data_t type, st_data_t ary)
static int
ins_methods_undef_i(st_data_t name, st_data_t type, st_data_t ary)
{
- if ((rb_method_visibility_t)type == METHOD_VISI_UNDEF) {
- ins_methods_push(name, ary);
- }
- return ST_CONTINUE;
+ return ins_methods_type_i(name, type, ary, METHOD_VISI_UNDEF);
}
struct method_entry_arg {
@@ -1660,20 +1730,20 @@ method_entry_i(ID key, VALUE value, void *data)
rb_method_visibility_t type;
if (me->def->type == VM_METHOD_TYPE_REFINED) {
- VALUE owner = me->owner;
- me = rb_resolve_refined_method(Qnil, me);
- if (!me) return ID_TABLE_CONTINUE;
- if (!arg->recur && me->owner != owner) return ID_TABLE_CONTINUE;
+ VALUE owner = me->owner;
+ me = rb_resolve_refined_method(Qnil, me);
+ if (!me) return ID_TABLE_CONTINUE;
+ if (!arg->recur && me->owner != owner) return ID_TABLE_CONTINUE;
}
if (!st_is_member(arg->list, key)) {
- if (UNDEFINED_METHOD_ENTRY_P(me)) {
- type = METHOD_VISI_UNDEF; /* none */
- }
- else {
- type = METHOD_ENTRY_VISI(me);
- RUBY_ASSERT(type != METHOD_VISI_UNDEF);
- }
- st_add_direct(arg->list, key, (st_data_t)type);
+ if (UNDEFINED_METHOD_ENTRY_P(me)) {
+ type = METHOD_VISI_UNDEF; /* none */
+ }
+ else {
+ type = METHOD_ENTRY_VISI(me);
+ RUBY_ASSERT(type != METHOD_VISI_UNDEF);
+ }
+ st_add_direct(arg->list, key, (st_data_t)type);
}
return ID_TABLE_CONTINUE;
}
@@ -1714,14 +1784,14 @@ class_instance_method_list(int argc, const VALUE *argv, VALUE mod, int obj, int
}
if (!recur && RCLASS_ORIGIN(mod) != mod) {
- mod = RCLASS_ORIGIN(mod);
- prepended = 1;
+ mod = RCLASS_ORIGIN(mod);
+ prepended = 1;
}
for (; mod; mod = RCLASS_SUPER(mod)) {
add_instance_method_list(mod, &me_arg);
- if (BUILTIN_TYPE(mod) == T_ICLASS && !prepended) continue;
- if (!recur) break;
+ if (BUILTIN_TYPE(mod) == T_ICLASS && !prepended) continue;
+ if (!recur) break;
}
ary = rb_ary_new2(me_arg.list->num_entries);
st_foreach(me_arg.list, func, ary);
@@ -1755,6 +1825,15 @@ class_instance_method_list(int argc, const VALUE *argv, VALUE mod, int obj, int
* B.instance_methods(true).include?(:method1) #=> true
* C.instance_methods(false) #=> [:method3]
* C.instance_methods.include?(:method2) #=> true
+ *
+ * Note that method visibility changes in the current class, as well as aliases,
+ * are considered as methods of the current class by this method:
+ *
+ * class C < B
+ * alias method4 method2
+ * protected :method2
+ * end
+ * C.instance_methods(false).sort #=> [:method2, :method3, :method4]
*/
VALUE
@@ -1866,7 +1945,7 @@ rb_obj_methods(int argc, const VALUE *argv, VALUE obj)
{
rb_check_arity(argc, 0, 1);
if (argc > 0 && !RTEST(argv[0])) {
- return rb_obj_singleton_methods(argc, argv, obj);
+ return rb_obj_singleton_methods(argc, argv, obj);
}
return class_instance_method_list(argc, argv, CLASS_OF(obj), 1, ins_methods_i);
}
@@ -1966,14 +2045,14 @@ rb_obj_singleton_methods(int argc, const VALUE *argv, VALUE obj)
me_arg.list = st_init_numtable();
me_arg.recur = recur;
if (klass && FL_TEST(klass, FL_SINGLETON)) {
- if ((mtbl = RCLASS_M_TBL(origin)) != 0) rb_id_table_foreach(mtbl, method_entry_i, &me_arg);
- klass = RCLASS_SUPER(klass);
+ if ((mtbl = RCLASS_M_TBL(origin)) != 0) rb_id_table_foreach(mtbl, method_entry_i, &me_arg);
+ klass = RCLASS_SUPER(klass);
}
if (recur) {
- while (klass && (FL_TEST(klass, FL_SINGLETON) || RB_TYPE_P(klass, T_ICLASS))) {
- if (klass != origin && (mtbl = RCLASS_M_TBL(klass)) != 0) rb_id_table_foreach(mtbl, method_entry_i, &me_arg);
- klass = RCLASS_SUPER(klass);
- }
+ while (klass && (FL_TEST(klass, FL_SINGLETON) || RB_TYPE_P(klass, T_ICLASS))) {
+ if (klass != origin && (mtbl = RCLASS_M_TBL(klass)) != 0) rb_id_table_foreach(mtbl, method_entry_i, &me_arg);
+ klass = RCLASS_SUPER(klass);
+ }
}
ary = rb_ary_new2(me_arg.list->num_entries);
st_foreach(me_arg.list, ins_methods_i, ary);
@@ -2045,7 +2124,7 @@ rb_undef_methods_from(VALUE klass, VALUE super)
{
struct rb_id_table *mtbl = RCLASS_M_TBL(super);
if (mtbl) {
- rb_id_table_foreach(mtbl, undef_method_i, (void *)klass);
+ rb_id_table_foreach(mtbl, undef_method_i, (void *)klass);
}
}
@@ -2093,15 +2172,15 @@ singleton_class_of(VALUE obj)
case T_BIGNUM:
case T_FLOAT:
case T_SYMBOL:
- rb_raise(rb_eTypeError, "can't define singleton");
+ rb_raise(rb_eTypeError, "can't define singleton");
case T_FALSE:
case T_TRUE:
case T_NIL:
- klass = special_singleton_class_of(obj);
- if (NIL_P(klass))
- rb_bug("unknown immediate %p", (void *)obj);
- return klass;
+ klass = special_singleton_class_of(obj);
+ if (NIL_P(klass))
+ rb_bug("unknown immediate %p", (void *)obj);
+ return klass;
case T_STRING:
if (FL_TEST_RAW(obj, RSTRING_FSTR)) {
@@ -2112,9 +2191,7 @@ singleton_class_of(VALUE obj)
klass = METACLASS_OF(obj);
if (!(FL_TEST(klass, FL_SINGLETON) &&
rb_attr_get(klass, id_attached) == obj)) {
- rb_serial_t serial = RCLASS_SERIAL(klass);
- klass = rb_make_metaclass(obj, klass);
- RCLASS_SERIAL(klass) = serial;
+ klass = rb_make_metaclass(obj, klass);
}
RB_FL_SET_RAW(klass, RB_OBJ_FROZEN_RAW(obj));
@@ -2127,11 +2204,11 @@ rb_freeze_singleton_class(VALUE x)
{
/* should not propagate to meta-meta-class, and so on */
if (!(RBASIC(x)->flags & FL_SINGLETON)) {
- VALUE klass = RBASIC_CLASS(x);
- if (klass && (klass = RCLASS_ORIGIN(klass)) != 0 &&
- FL_TEST(klass, (FL_SINGLETON|FL_FREEZE)) == FL_SINGLETON) {
- OBJ_FREEZE_RAW(klass);
- }
+ VALUE klass = RBASIC_CLASS(x);
+ if (klass && // no class when hidden from ObjectSpace
+ FL_TEST(klass, (FL_SINGLETON|FL_FREEZE)) == FL_SINGLETON) {
+ OBJ_FREEZE_RAW(klass);
+ }
}
}
@@ -2148,7 +2225,7 @@ rb_singleton_class_get(VALUE obj)
VALUE klass;
if (SPECIAL_CONST_P(obj)) {
- return rb_special_singleton_class(obj);
+ return rb_special_singleton_class(obj);
}
klass = METACLASS_OF(obj);
if (!FL_TEST(klass, FL_SINGLETON)) return Qnil;
@@ -2223,13 +2300,13 @@ rb_keyword_error_new(const char *error, VALUE keys)
VALUE error_message = rb_sprintf("%s keyword%.*s", error, len > 1, "s");
if (len > 0) {
- rb_str_cat_cstr(error_message, ": ");
- while (1) {
+ rb_str_cat_cstr(error_message, ": ");
+ while (1) {
const VALUE k = RARRAY_AREF(keys, i);
- rb_str_append(error_message, rb_inspect(k));
- if (++i >= len) break;
- rb_str_cat_cstr(error_message, ", ");
- }
+ rb_str_append(error_message, rb_inspect(k));
+ if (++i >= len) break;
+ rb_str_cat_cstr(error_message, ", ");
+ }
}
return rb_exc_new_str(rb_eArgError, error_message);
@@ -2248,7 +2325,7 @@ unknown_keyword_error(VALUE hash, const ID *table, int keywords)
{
int i;
for (i = 0; i < keywords; i++) {
- st_data_t key = ID2SYM(table[i]);
+ st_data_t key = ID2SYM(table[i]);
rb_hash_stlike_delete(hash, &key, NULL);
}
rb_keyword_error("unknown", rb_hash_keys(hash));
@@ -2272,8 +2349,8 @@ rb_extract_keywords(VALUE *orighash)
VALUE hash = *orighash;
if (RHASH_EMPTY_P(hash)) {
- *orighash = 0;
- return hash;
+ *orighash = 0;
+ return hash;
}
rb_hash_foreach(hash, separate_symbol, (st_data_t)&parthash);
*orighash = parthash[1];
@@ -2299,36 +2376,36 @@ rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, V
if (NIL_P(keyword_hash)) keyword_hash = 0;
if (optional < 0) {
- rest = 1;
- optional = -1-optional;
+ rest = 1;
+ optional = -1-optional;
}
if (required) {
- for (; i < required; i++) {
- VALUE keyword = ID2SYM(table[i]);
- if (keyword_hash) {
+ for (; i < required; i++) {
+ VALUE keyword = ID2SYM(table[i]);
+ if (keyword_hash) {
if (extract_kwarg(keyword, values[i])) {
- continue;
- }
- }
- if (NIL_P(missing)) missing = rb_ary_tmp_new(1);
- rb_ary_push(missing, keyword);
- }
- if (!NIL_P(missing)) {
- rb_keyword_error("missing", missing);
- }
+ continue;
+ }
+ }
+ if (NIL_P(missing)) missing = rb_ary_hidden_new(1);
+ rb_ary_push(missing, keyword);
+ }
+ if (!NIL_P(missing)) {
+ rb_keyword_error("missing", missing);
+ }
}
j = i;
if (optional && keyword_hash) {
- for (i = 0; i < optional; i++) {
+ for (i = 0; i < optional; i++) {
if (extract_kwarg(ID2SYM(table[required+i]), values[required+i])) {
- j++;
- }
- }
+ j++;
+ }
+ }
}
if (!rest && keyword_hash) {
- if (RHASH_SIZE(keyword_hash) > (unsigned int)(values ? 0 : j)) {
- unknown_keyword_error(keyword_hash, table, required+optional);
- }
+ if (RHASH_SIZE(keyword_hash) > (unsigned int)(values ? 0 : j)) {
+ unknown_keyword_error(keyword_hash, table, required+optional);
+ }
}
if (values && !keyword_hash) {
for (i = 0; i < required + optional; i++) {
@@ -2359,30 +2436,30 @@ rb_scan_args_parse(int kw_flag, const char *fmt, struct rb_scan_args_t *arg)
if (ISDIGIT(*p)) {
arg->n_lead = *p - '0';
- p++;
- if (ISDIGIT(*p)) {
+ p++;
+ if (ISDIGIT(*p)) {
arg->n_opt = *p - '0';
- p++;
- }
+ p++;
+ }
}
if (*p == '*') {
arg->f_var = 1;
- p++;
+ p++;
}
if (ISDIGIT(*p)) {
arg->n_trail = *p - '0';
- p++;
+ p++;
}
if (*p == ':') {
arg->f_hash = 1;
- p++;
+ p++;
}
if (*p == '&') {
arg->f_block = 1;
- p++;
+ p++;
}
if (*p != '\0') {
- rb_fatal("bad scan arg format: %s", fmt);
+ rb_fatal("bad scan arg format: %s", fmt);
}
}
@@ -2418,37 +2495,37 @@ rb_scan_args_assign(const struct rb_scan_args_t *arg, int argc, const VALUE *con
for (i = 0; i < n_lead; i++) {
var = rb_scan_args_next_param();
if (var) *var = argv[argi];
- argi++;
+ argi++;
}
/* capture optional arguments */
for (i = 0; i < n_opt; i++) {
var = rb_scan_args_next_param();
if (argi < argc - n_trail) {
if (var) *var = argv[argi];
- argi++;
- }
- else {
- if (var) *var = Qnil;
- }
+ argi++;
+ }
+ else {
+ if (var) *var = Qnil;
+ }
}
/* capture variable length arguments */
if (f_var) {
int n_var = argc - argi - n_trail;
var = rb_scan_args_next_param();
- if (0 < n_var) {
+ if (0 < n_var) {
if (var) *var = rb_ary_new_from_values(n_var, &argv[argi]);
- argi += n_var;
- }
- else {
- if (var) *var = rb_ary_new();
- }
+ argi += n_var;
+ }
+ else {
+ if (var) *var = rb_ary_new();
+ }
}
/* capture trailing mandatory arguments */
for (i = 0; i < n_trail; i++) {
var = rb_scan_args_next_param();
if (var) *var = argv[argi];
- argi++;
+ argi++;
}
/* capture an option hash - phase 2: assignment */
if (f_hash) {
@@ -2458,12 +2535,12 @@ rb_scan_args_assign(const struct rb_scan_args_t *arg, int argc, const VALUE *con
/* capture iterator block */
if (f_block) {
var = rb_scan_args_next_param();
- if (rb_block_given_p()) {
- *var = rb_block_proc();
- }
- else {
- *var = Qnil;
- }
+ if (rb_block_given_p()) {
+ *var = rb_block_proc();
+ }
+ else {
+ *var = Qnil;
+ }
}
if (argi == argc) {
diff --git a/common.mk b/common.mk
index d3cb32ad85..126053c9c8 100644
--- a/common.mk
+++ b/common.mk
@@ -18,8 +18,10 @@ mflags = $(MFLAGS)
gnumake_recursive =
enable_shared = $(ENABLE_SHARED:no=)
-UNICODE_VERSION = 14.0.0
-UNICODE_EMOJI_VERSION = 14.0
+UNICODE_VERSION = 15.0.0
+UNICODE_EMOJI_VERSION_0 = $(UNICODE_VERSION)///
+UNICODE_EMOJI_VERSION_1 = $(UNICODE_EMOJI_VERSION_0:.0///=)
+UNICODE_EMOJI_VERSION = $(UNICODE_EMOJI_VERSION_1:///=)
UNICODE_BETA = NO
### set the following environment variable or uncomment the line if
@@ -41,14 +43,14 @@ RUN_OPTS = --disable-gems
# GITPULLOPTIONS = --no-tags
-INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(srcdir) -I$(UNICODE_HDR_DIR)
+INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(srcdir) -I$(UNICODE_HDR_DIR) $(incflags)
GEM_HOME =
GEM_PATH =
GEM_VENDOR =
BENCHMARK_DRIVER_GIT_URL = https://github.com/benchmark-driver/benchmark-driver
-BENCHMARK_DRIVER_GIT_REF = v0.15.18
+BENCHMARK_DRIVER_GIT_REF = v0.16.0
SIMPLECOV_GIT_URL = https://github.com/colszowka/simplecov.git
SIMPLECOV_GIT_REF = v0.17.0
SIMPLECOV_HTML_GIT_URL = https://github.com/colszowka/simplecov-html.git
@@ -82,7 +84,7 @@ EXTSOLIBS =
MINIOBJS = $(ARCHMINIOBJS) miniinit.$(OBJEXT) dmyext.$(OBJEXT)
ENC_MK = enc.mk
MAKE_ENC = -f $(ENC_MK) V="$(V)" UNICODE_HDR_DIR="$(UNICODE_HDR_DIR)" \
- RUBY="$(MINIRUBY)" MINIRUBY="$(MINIRUBY)" $(mflags)
+ RUBY="$(BOOTSTRAPRUBY)" MINIRUBY="$(BOOTSTRAPRUBY)" $(mflags)
COMMONOBJS = array.$(OBJEXT) \
ast.$(OBJEXT) \
@@ -113,7 +115,7 @@ COMMONOBJS = array.$(OBJEXT) \
math.$(OBJEXT) \
memory_view.$(OBJEXT) \
mjit.$(OBJEXT) \
- mjit_compile.$(OBJEXT) \
+ mjit_c.$(OBJEXT) \
node.$(OBJEXT) \
numeric.$(OBJEXT) \
object.$(OBJEXT) \
@@ -134,6 +136,7 @@ COMMONOBJS = array.$(OBJEXT) \
regsyntax.$(OBJEXT) \
ruby.$(OBJEXT) \
scheduler.$(OBJEXT) \
+ shape.$(OBJEXT) \
signal.$(OBJEXT) \
sprintf.$(OBJEXT) \
st.$(OBJEXT) \
@@ -195,6 +198,7 @@ INSTRUBY_ARGS = $(SCRIPT_ARGS) \
INSTALL_PROG_MODE = 0755
INSTALL_DATA_MODE = 0644
+BOOTSTRAPRUBY_COMMAND = $(BOOTSTRAPRUBY) $(BOOTSTRAPRUBY_OPT)
TESTSDIR = $(srcdir)/test
TOOL_TESTSDIR = $(tooldir)/test
TEST_EXCLUDES = --excludes-dir=$(TESTSDIR)/excludes --name=!/memory_leak/
@@ -216,12 +220,27 @@ MAKE_LINK = $(MINIRUBY) -rfileutils -e "include FileUtils::Verbose" \
-e "noraise {ln(src, dest)} or" \
-e "cp(src, dest)"
+# For release builds
+YJIT_RUSTC_ARGS = --crate-name=yjit \
+ --crate-type=staticlib \
+ --edition=2021 \
+ -g \
+ -C opt-level=3 \
+ -C overflow-checks=on \
+ '--out-dir=$(CARGO_TARGET_DIR)/release/' \
+ $(top_srcdir)/yjit/src/lib.rs
all: $(SHOWFLAGS) main docs
main: $(SHOWFLAGS) exts $(ENCSTATIC:static=lib)encs
@$(NULLCMD)
+main: $(srcdir)/lib/ruby_vm/mjit/instruction.rb
+srcs: $(srcdir)/lib/ruby_vm/mjit/instruction.rb
+$(srcdir)/lib/ruby_vm/mjit/instruction.rb: $(tooldir)/insns2vm.rb $(tooldir)/ruby_vm/views/lib/ruby_vm/mjit/instruction.rb.erb $(srcdir)/insns.def
+ $(ECHO) generating $@
+ $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@
+
mjit-headers: $(MJIT_SUPPORT)-mjit-headers
no-mjit-headers: PHONY
yes-mjit-headers: mjit_config.h PHONY
@@ -229,6 +248,9 @@ yes-mjit-headers: mjit_config.h PHONY
mjit.$(OBJEXT): mjit_config.h
mjit_config.h: Makefile
+.PHONY: mjit-bindgen
+mjit-bindgen:
+ $(Q) $(BASERUBY) -rrubygems -C $(srcdir)/tool/mjit bindgen.rb $(CURDIR)
# These rules using MJIT_HEADER_SUFFIX must be in common.mk, not
# Makefile.in, in order to override the macro in defs/universal.mk.
@@ -270,6 +292,8 @@ showflags:
" LC_ALL = $(LC_ALL)" \
" LC_CTYPE = $(LC_CTYPE)" \
" MFLAGS = $(MFLAGS)" \
+ " RUSTC = $(RUSTC)" \
+ " YJIT_RUSTC_ARGS = $(YJIT_RUSTC_ARGS)" \
$(MESSAGE_END)
-@$(CC_VERSION)
@@ -610,12 +634,13 @@ clear-installed-list: PHONY
clean: clean-ext clean-enc clean-golf clean-docs clean-extout clean-local clean-platform clean-spec
clean-local:: clean-runnable
- $(Q)$(RM) $(OBJS) $(MINIOBJS) $(MAINOBJ) $(LIBRUBY_A) $(LIBRUBY_SO) $(LIBRUBY) $(LIBRUBY_ALIASES)
+ $(Q)$(RM) $(OBJS) $(MINIOBJS) $(INITOBJS) $(MAINOBJ) $(LIBRUBY_A) $(LIBRUBY_SO) $(LIBRUBY) $(LIBRUBY_ALIASES)
$(Q)$(RM) $(PROGRAM) $(WPROGRAM) miniruby$(EXEEXT) dmyext.$(OBJEXT) dmyenc.$(OBJEXT) $(ARCHFILE) .*.time
$(Q)$(RM) y.tab.c y.output encdb.h transdb.h config.log rbconfig.rb $(ruby_pc) $(COROUTINE_H:/Context.h=/.time)
$(Q)$(RM) probes.h probes.$(OBJEXT) probes.stamp ruby-glommed.$(OBJEXT) ruby.imp ChangeLog $(STATIC_RUBY)$(EXEEXT)
$(Q)$(RM) GNUmakefile.old Makefile.old $(arch)-fake.rb bisect.sh $(ENC_TRANS_D) builtin_binary.inc
- -$(Q) $(RMDIR) enc/jis enc/trans enc $(COROUTINE_H:/Context.h=) coroutine 2> $(NULL) || $(NULLCMD)
+ -$(Q)$(RMALL) yjit/target
+ -$(Q) $(RMDIR) enc/jis enc/trans enc $(COROUTINE_H:/Context.h=) coroutine yjit 2> $(NULL) || $(NULLCMD)
bin/clean-runnable:: PHONY
$(Q)$(CHDIR) bin 2>$(NULL) && $(RM) $(PROGRAM) $(WPROGRAM) $(GORUBY)$(EXEEXT) bin/*.$(DLEXT) 2>$(NULL) || $(NULLCMD)
@@ -687,18 +712,18 @@ realclean-platform: distclean-platform
realclean-spec: distclean-spec
realclean-rubyspec: realclean-spec
-clean-ext:: ext/clean gems/clean timestamp/clean
-distclean-ext:: ext/distclean gems/distclean timestamp/distclean
-realclean-ext:: ext/realclean gems/realclean timestamp/realclean
+clean-ext:: ext/clean .bundle/clean timestamp/clean
+distclean-ext:: ext/distclean .bundle/distclean timestamp/distclean
+realclean-ext:: ext/realclean .bundle/realclean timestamp/realclean
ext/clean.mk ext/distclean.mk ext/realclean.mk::
ext/clean:: ext/clean.mk
ext/distclean:: ext/distclean.mk
ext/realclean:: ext/realclean.mk
-timestamp/clean:: ext/clean gems/clean
-timestamp/distclean:: ext/distclean gems/distclean
-timestamp/realclean:: ext/realclean gems/realclean
+timestamp/clean:: ext/clean .bundle/clean
+timestamp/distclean:: ext/distclean .bundle/distclean
+timestamp/realclean:: ext/realclean .bundle/realclean
timestamp/clean timestamp/distclean timestamp/realclean::
$(Q)$(RM) $(TIMESTAMPDIR)/.*.time $(TIMESTAMPDIR)/$(arch)/.time
@@ -741,7 +766,7 @@ clean-spec: PHONY
-$(Q) $(RMDIRS) $(RUBYSPEC_CAPIEXT) 2> $(NULL) || $(NULLCMD)
-$(Q) $(RMALL) rubyspec_temp
-check: main test test-tool test-all test-spec
+check: main $(DOT_WAIT) test $(DOT_WAIT) test-tool $(DOT_WAIT) test-all
$(ECHO) check succeeded
-$(Q) : : "run only on sh"; \
if [ x"$(GIT)" != x ] && $(CHDIR) "$(srcdir)" && \
@@ -755,26 +780,34 @@ fake: $(CROSS_COMPILING)-fake
yes-fake: $(arch)-fake.rb $(RBCONFIG) PHONY
no-fake -fake: PHONY
-# really doesn't depend on .o, just ensure newer than headers which
-# version.o depends on.
-$(arch)-fake.rb: $(srcdir)/template/fake.rb.in $(tooldir)/generic_erb.rb version.$(OBJEXT) miniruby$(EXEEXT)
+$(HAVE_BASERUBY:no=)$(arch)-fake.rb: miniruby$(EXEEXT)
+
+# actually depending on other headers more.
+$(arch:noarch=ignore)-fake.rb: $(top_srcdir)/revision.h $(top_srcdir)/version.h $(srcdir)/version.c
+$(arch:noarch=ignore)-fake.rb: {$(VPATH)}id.h {$(VPATH)}vm_opts.h
+
+$(arch:noarch=ignore)-fake.rb: $(srcdir)/template/fake.rb.in $(tooldir)/generic_erb.rb
$(ECHO) generating $@
$(Q) $(CPP) -DRUBY_EXPORT $(INCFLAGS) $(CPPFLAGS) "$(srcdir)/version.c" | \
$(BOOTSTRAPRUBY) "$(tooldir)/generic_erb.rb" -o $@ "$(srcdir)/template/fake.rb.in" \
- i=- srcdir="$(srcdir)" BASERUBY="$(BASERUBY)"
+ i=- srcdir="$(srcdir)" BASERUBY="$(BASERUBY)" \
+ LIBPATHENV="$(LIBPATHENV)" PRELOADENV="$(PRELOADENV)" LIBRUBY_SO="$(LIBRUBY_SO)"
+
+noarch-fake.rb: # prerequisite of yes-fake
+ $(Q) exit > $@
btest: $(TEST_RUNNABLE)-btest
no-btest: PHONY
yes-btest: yes-fake miniruby$(EXEEXT) PHONY
$(ACTIONS_GROUP)
- $(Q)$(exec) $(BOOTSTRAPRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(BTESTRUBY) $(RUN_OPTS)" $(OPTS) $(TESTOPTS) $(BTESTS)
+ $(Q)$(gnumake_recursive)$(exec) $(BOOTSTRAPRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(BTESTRUBY) $(RUN_OPTS)" $(OPTS) $(TESTOPTS) $(BTESTS)
$(ACTIONS_ENDGROUP)
btest-ruby: $(TEST_RUNNABLE)-btest-ruby
no-btest-ruby: PHONY
yes-btest-ruby: prog PHONY
$(ACTIONS_GROUP)
- $(Q)$(exec) $(RUNRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(PROGRAM) -I$(srcdir)/lib $(RUN_OPTS)" -q $(OPTS) $(TESTOPTS) $(BTESTS)
+ $(Q)$(gnumake_recursive)$(exec) $(RUNRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(PROGRAM) -I$(srcdir)/lib $(RUN_OPTS)" $(OPTS) $(TESTOPTS) $(BTESTS)
$(ACTIONS_ENDGROUP)
rtest: yes-fake miniruby$(EXEEXT) PHONY
@@ -812,7 +845,7 @@ yes-test-tool: prog PHONY
no-test-tool: PHONY
test-sample: test-basic # backward compatibility for mswin-build
-test-short: btest-ruby test-knownbug test-basic
+test-short: btest-ruby $(DOT_WAIT) test-knownbug $(DOT_WAIT) test-basic
test: test-short
# $ make test-all TESTOPTS="--help" displays more detail
@@ -839,7 +872,10 @@ extconf: $(PREP)
$(Q) $(MAKEDIRS) "$(EXTCONFDIR)"
$(RUNRUBY) -C "$(EXTCONFDIR)" $(EXTCONF) $(EXTCONFARGS)
-$(RBCONFIG): $(tooldir)/mkconfig.rb config.status $(srcdir)/version.h
+rbconfig.rb: $(RBCONFIG)
+
+$(HAVE_BASERUBY:no=)$(RBCONFIG)$(HAVE_BASERUBY:no=): $(PREP)
+$(RBCONFIG): $(tooldir)/mkconfig.rb config.status $(srcdir)/version.h $(srcdir)/common.mk
$(Q)$(BOOTSTRAPRUBY) -n \
-e 'BEGIN{version=ARGV.shift;mis=ARGV.dup}' \
-e 'END{abort "UNICODE version mismatch: #{mis}" unless mis.empty?}' \
@@ -857,7 +893,7 @@ $(RBCONFIG): $(tooldir)/mkconfig.rb config.status $(srcdir)/version.h
test-rubyspec: test-spec
yes-test-rubyspec: yes-test-spec
-test-spec-precheck: programs
+test-spec-precheck: programs yes-fake
test-spec: $(TEST_RUNNABLE)-test-spec
yes-test-spec: test-spec-precheck
@@ -867,6 +903,8 @@ yes-test-spec: test-spec-precheck
$(ACTIONS_ENDGROUP)
no-test-spec:
+check: $(DOT_WAIT) test-spec
+
RUNNABLE = $(LIBRUBY_RELATIVE:no=un)-runnable
runnable: $(RUNNABLE) prog $(tooldir)/mkrunnable.rb PHONY
$(Q) $(MINIRUBY) $(tooldir)/mkrunnable.rb -v $(EXTOUT)
@@ -885,9 +923,10 @@ libtrans trans: {$(VPATH)}transdb.h
ENC_HEADERS = $(srcdir)/enc/jis/props.h
# Use MINIRUBY which loads fake.rb for cross compiling
$(ENC_MK): $(srcdir)/enc/make_encmake.rb $(srcdir)/enc/Makefile.in $(srcdir)/enc/depend \
- $(srcdir)/enc/encinit.c.erb $(ENC_HEADERS) $(srcdir)/lib/mkmf.rb $(RBCONFIG) fake
+ $(srcdir)/enc/encinit.c.erb $(ENC_HEADERS) $(srcdir)/lib/mkmf.rb $(RBCONFIG) $(HAVE_BASERUBY)-fake
$(ECHO) generating $@
- $(Q) $(MINIRUBY) $(srcdir)/enc/make_encmake.rb --builtin-encs="$(BUILTIN_ENCOBJS)" --builtin-transes="$(BUILTIN_TRANSOBJS)" --module$(ENCSTATIC) $(ENCS) $@
+ $(Q) $(BOOTSTRAPRUBY_COMMAND) $(srcdir)/enc/make_encmake.rb \
+ --builtin-encs="$(BUILTIN_ENCOBJS)" --builtin-transes="$(BUILTIN_TRANSOBJS)" --module$(ENCSTATIC) $(ENCS) $@
.PRECIOUS: $(MKFILES)
@@ -909,9 +948,7 @@ PHONY:
{$(srcdir)}.y.c:
$(ECHO) generating $@
- $(Q)$(BASERUBY) $(tooldir)/id2token.rb --path-separator=.$(PATH_SEPARATOR)./ --vpath=$(VPATH) id.h $(SRC_FILE) > parse.tmp.y
- $(Q)$(BASERUBY) $(tooldir)/pure_parser.rb parse.tmp.y $(YACC)
- $(Q)$(RM) parse.tmp.y.bak
+ $(Q)$(BASERUBY) $(tooldir)/id2token.rb $(SRC_FILE) > parse.tmp.y
$(Q)$(YACC) -d $(YFLAGS) -o y.tab.c parse.tmp.y
$(Q)$(RM) parse.tmp.y
$(Q)sed -f $(tooldir)/ytab.sed -e "/^#/s|parse\.tmp\.[iy]|$(SRC_FILE)|" -e "/^#/s!y\.tab\.c!$@!" y.tab.c > $@.new
@@ -923,11 +960,11 @@ $(PLATFORM_D):
$(Q) $(MAKEDIRS) $(PLATFORM_DIR) $(@D)
@$(NULLCMD) > $@
-exe/$(PROGRAM): ruby-runner.c ruby-runner.h exe/.time miniruby$(EXEEXT) {$(VPATH)}config.h
+exe/$(PROGRAM): ruby-runner.c ruby-runner.h exe/.time $(PREP) {$(VPATH)}config.h
$(Q) $(CC) $(CFLAGS) $(INCFLAGS) $(CPPFLAGS) -DRUBY_INSTALL_NAME=$(@F) $(COUTFLAG)ruby-runner.$(OBJEXT) -c $(CSRCFLAG)$(srcdir)/ruby-runner.c
$(Q) $(PURIFY) $(CC) $(CFLAGS) $(LDFLAGS) $(OUTFLAG)$@ ruby-runner.$(OBJEXT) $(LIBS)
$(Q) $(POSTLINK)
- $(Q) ./miniruby$(EXEEXT) \
+ $(Q) $(BOOTSTRAPRUBY) \
-e 'prog, dest, inst = ARGV; dest += "/ruby"' \
-e 'exit unless prog==inst' \
-e 'unless prog=="ruby"' \
@@ -1041,11 +1078,7 @@ $(srcs_vpath)insns_info.inc: $(tooldir)/ruby_vm/views/insns_info.inc.erb $(inc_c
$(srcs_vpath)vmtc.inc: $(tooldir)/ruby_vm/views/vmtc.inc.erb $(inc_common_headers)
$(srcs_vpath)vm.inc: $(tooldir)/ruby_vm/views/vm.inc.erb $(inc_common_headers) \
$(tooldir)/ruby_vm/views/_insn_entry.erb $(tooldir)/ruby_vm/views/_trace_instruction.erb
-$(srcs_vpath)mjit_compile.inc: $(tooldir)/ruby_vm/views/mjit_compile.inc.erb $(inc_common_headers) \
- $(tooldir)/ruby_vm/views/_mjit_compile_insn.erb $(tooldir)/ruby_vm/views/_mjit_compile_send.erb \
- $(tooldir)/ruby_vm/views/_mjit_compile_ivar.erb \
- $(tooldir)/ruby_vm/views/_mjit_compile_insn_body.erb $(tooldir)/ruby_vm/views/_mjit_compile_pc_and_sp.erb \
- $(tooldir)/ruby_vm/views/_mjit_compile_invokebuiltin.erb $(tooldir)/ruby_vm/views/_mjit_compile_getinlinecache.erb
+$(srcs_vpath)mjit_sp_inc.inc: $(tooldir)/ruby_vm/views/mjit_sp_inc.inc.erb
BUILTIN_RB_SRCS = \
$(srcdir)/ast.rb \
@@ -1055,13 +1088,16 @@ BUILTIN_RB_SRCS = \
$(srcdir)/io.rb \
$(srcdir)/marshal.rb \
$(srcdir)/mjit.rb \
+ $(srcdir)/mjit_c.rb \
$(srcdir)/pack.rb \
$(srcdir)/trace_point.rb \
$(srcdir)/warning.rb \
$(srcdir)/array.rb \
$(srcdir)/kernel.rb \
$(srcdir)/ractor.rb \
+ $(srcdir)/symbol.rb \
$(srcdir)/timev.rb \
+ $(srcdir)/thread_sync.rb \
$(srcdir)/nilclass.rb \
$(srcdir)/prelude.rb \
$(srcdir)/gem_prelude.rb \
@@ -1071,7 +1107,7 @@ BUILTIN_RB_INCS = $(BUILTIN_RB_SRCS:.rb=.rbinc)
common-srcs: $(srcs_vpath)parse.c $(srcs_vpath)lex.c $(srcs_vpath)enc/trans/newline.c $(srcs_vpath)id.c \
$(BUILTIN_RB_INCS) \
- srcs-lib srcs-ext incs
+ srcs-lib srcs-ext incs preludes
missing-srcs: $(srcdir)/missing/des_tables.c
@@ -1127,13 +1163,13 @@ node_name.inc: $(tooldir)/node_name.rb $(srcdir)/node.h
$(ECHO) generating $@
$(Q) $(BASERUBY) -n $(tooldir)/node_name.rb < $(srcdir)/node.h > $@
-encdb.h: $(PREP) $(tooldir)/generic_erb.rb $(srcdir)/template/encdb.h.tmpl
+encdb.h: $(RBCONFIG) $(tooldir)/generic_erb.rb $(srcdir)/template/encdb.h.tmpl
$(ECHO) generating $@
- $(Q) $(MINIRUBY) $(tooldir)/generic_erb.rb -c -o $@ $(srcdir)/template/encdb.h.tmpl $(srcdir)/enc enc
+ $(Q) $(BOOTSTRAPRUBY) $(tooldir)/generic_erb.rb -c -o $@ $(srcdir)/template/encdb.h.tmpl $(srcdir)/enc enc
-transdb.h: $(PREP) srcs-enc $(tooldir)/generic_erb.rb $(srcdir)/template/transdb.h.tmpl
+transdb.h: $(RBCONFIG) srcs-enc $(tooldir)/generic_erb.rb $(srcdir)/template/transdb.h.tmpl
$(ECHO) generating $@
- $(Q) $(MINIRUBY) $(tooldir)/generic_erb.rb -c -o $@ $(srcdir)/template/transdb.h.tmpl $(srcdir)/enc/trans enc/trans
+ $(Q) $(BOOTSTRAPRUBY) $(tooldir)/generic_erb.rb -c -o $@ $(srcdir)/template/transdb.h.tmpl $(srcdir)/enc/trans enc/trans
enc/encinit.c: $(ENC_MK) $(srcdir)/enc/encinit.c.erb
@@ -1183,25 +1219,19 @@ builtin_binary.inc: $(PREP) $(BUILTIN_RB_SRCS) $(srcdir)/template/builtin_binary
$(BUILTIN_RB_INCS): $(top_srcdir)/tool/mk_builtin_loader.rb
-$(srcdir)/revision.h:
-$(srcdir)/revision.h$(gnumake:yes=-nongnumake):
- $(Q)$(RM) $(@F)
- $(Q)$(NULLCMD) > $@ || $(NULLCMD) > $(@F)
+$(srcdir)/revision.h$(no_baseruby:no=~disabled~): $(REVISION_H)
-revision.tmp::
- $(Q) $(NULLCMD) > $@
-revision.$(HAVE_BASERUBY:yes=tmp):: $(srcdir)/version.h $(tooldir)/file2lastrev.rb $(REVISION_FORCE)
- $(Q) $(BASERUBY) $(tooldir)/file2lastrev.rb -q --revision.h --srcdir="$(srcdir)" > $@
+$(REVISION_H)$(no_baseruby:no=~disabled~):
+ $(Q) $(BASERUBY) $(tooldir)/file2lastrev.rb -q --revision.h --srcdir="$(srcdir)" --output=revision.h --timestamp=$@
+$(REVISION_H)$(yes_baseruby:yes=~disabled~):
+ $(Q) exit > $@
-$(REVISION_H): revision.tmp
- $(Q)$(IFCHANGE) "--timestamp=$@" "$(srcdir)/revision.h" revision.tmp
-
-$(srcdir)/ext/ripper/ripper.c: $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/parse.y id.h $(srcdir)/ext/ripper/depend
+$(srcdir)/ext/ripper/ripper.c: $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/parse.y $(srcdir)/defs/id.def $(srcdir)/ext/ripper/depend
$(ECHO) generating $@
$(Q) $(CHDIR) $(@D) && \
- sed -e 's/{\$$([^(){}]*)[^{}]*}//g' -e /AUTOGENERATED/q depend | \
+ $(CAT_DEPEND) depend | \
$(exec) $(MAKE) -f - $(mflags) \
- Q=$(Q) ECHO=$(ECHO) RM="$(RM1)" BISON=$(YACC) top_srcdir=../.. srcdir=. VPATH=../.. \
+ Q=$(Q) ECHO=$(ECHO) RM="$(RM1)" BISON="$(YACC)" top_srcdir=../.. srcdir=. VPATH=../.. \
RUBY="$(BASERUBY)" PATH_SEPARATOR="$(PATH_SEPARATOR)" LANG=C
$(srcdir)/ext/json/parser/parser.c: $(srcdir)/ext/json/parser/parser.rl $(srcdir)/ext/json/parser/prereq.mk
@@ -1218,7 +1248,7 @@ $(srcdir)/ext/rbconfig/sizeof/sizes.c: $(srcdir)/ext/rbconfig/sizeof/depend \
$(tooldir)/generic_erb.rb $(srcdir)/template/sizes.c.tmpl $(srcdir)/configure.ac
$(ECHO) generating $@
$(Q) $(CHDIR) $(@D) && \
- sed '/AUTOGENERATED/q' depend | \
+ $(CAT_DEPEND) depend | \
$(exec) $(MAKE) -f - $(mflags) \
Q=$(Q) ECHO=$(ECHO) top_srcdir=../../.. srcdir=. VPATH=../../.. RUBY="$(BASERUBY)" $(@F)
@@ -1226,19 +1256,19 @@ $(srcdir)/ext/rbconfig/sizeof/limits.c: $(srcdir)/ext/rbconfig/sizeof/depend \
$(tooldir)/generic_erb.rb $(srcdir)/template/limits.c.tmpl
$(ECHO) generating $@
$(Q) $(CHDIR) $(@D) && \
- sed '/AUTOGENERATED/q' depend | \
+ $(CAT_DEPEND) depend | \
$(exec) $(MAKE) -f - $(mflags) \
Q=$(Q) ECHO=$(ECHO) top_srcdir=../../.. srcdir=. VPATH=../../.. RUBY="$(BASERUBY)" $(@F)
$(srcdir)/ext/socket/constdefs.c: $(srcdir)/ext/socket/depend
$(Q) $(CHDIR) $(@D) && \
- sed '/AUTOGENERATED/q' depend | \
+ $(CAT_DEPEND) depend | \
$(exec) $(MAKE) -f - $(mflags) \
Q=$(Q) ECHO=$(ECHO) top_srcdir=../.. srcdir=. VPATH=../.. RUBY="$(BASERUBY)"
$(srcdir)/ext/etc/constdefs.h: $(srcdir)/ext/etc/depend
$(Q) $(CHDIR) $(@D) && \
- sed '/AUTOGENERATED/q' depend | \
+ $(CAT_DEPEND) depend | \
$(exec) $(MAKE) -f - $(mflags) \
Q=$(Q) ECHO=$(ECHO) top_srcdir=../.. srcdir=. VPATH=../.. RUBY="$(BASERUBY)"
@@ -1310,7 +1340,7 @@ lldb-ruby: $(PROGRAM) PHONY
DISTPKGS = gzip,zip,all
PKGSDIR = tmp
dist:
- $(BASERUBY) $(tooldir)/make-snapshot \
+ $(BASERUBY) $(V0:1=-v) $(tooldir)/make-snapshot \
-srcdir=$(srcdir) -packages=$(DISTPKGS) \
-unicode-version=$(UNICODE_VERSION) \
$(DISTOPTS) $(PKGSDIR) $(RELNAME)
@@ -1323,10 +1353,15 @@ up::
yes::
no::
+after-update:: common-srcs
after-update:: $(REVISION_H)
after-update:: extract-extlibs
after-update:: extract-gems
+update-src::
+ $(Q) $(RM) $(REVISION_H) revision.h "$(srcdir)/$(REVISION_H)" "$(srcdir)/revision.h"
+ $(Q) exit > "$(srcdir)/revision.h"
+
update-remote:: update-src update-download
update-download:: $(ALWAYS_UPDATE_UNICODE:yes=update-unicode)
update-download:: update-gems
@@ -1341,8 +1376,9 @@ update-config_files: PHONY
refresh-gems: update-bundled_gems prepare-gems
prepare-gems: $(HAVE_BASERUBY:yes=update-gems) $(HAVE_BASERUBY:yes=extract-gems)
+extract-gems: $(HAVE_BASERUBY:yes=update-gems)
-update-gems$(gnumake:yes=-nongnumake): PHONY
+update-gems$(gnumake:yes=-sequential): PHONY
$(ECHO) Downloading bundled gem files...
$(Q) $(BASERUBY) -C "$(srcdir)" \
-I./tool -rdownloader -answ \
@@ -1356,24 +1392,32 @@ update-gems$(gnumake:yes=-nongnumake): PHONY
-e 'FileUtils.rm_rf(old.map{'"|n|"'n.chomp(".gem")})' \
gems/bundled_gems
-extract-gems$(gnumake:yes=-nongnumake): PHONY
+extract-gems$(gnumake:yes=-sequential): PHONY
$(ECHO) Extracting bundled gem files...
- $(Q) $(RUNRUBY) -C "$(srcdir)" \
- -Itool -rfileutils -rgem-unpack -answ \
+ $(Q) $(BASERUBY) -C "$(srcdir)" \
+ -Itool/lib -rfileutils -rbundled_gem -answ \
-e 'BEGIN {d = ".bundle/gems"}' \
- -e 'gem, ver = *$$F' \
+ -e 'gem, ver, _, rev = *$$F' \
-e 'next if !ver or /^#/=~gem' \
-e 'g = "#{gem}-#{ver}"' \
- -e 'File.directory?("#{d}/#{g}") or Gem.unpack("gems/#{g}.gem", ".bundle")' \
+ -e 'if File.directory?("#{d}/#{g}")' \
+ -e 'elsif rev and File.exist?(gs = "gems/src/#{gem}/#{gem}.gemspec")' \
+ -e 'BundledGem.copy(gs, ".bundle")' \
+ -e 'else' \
+ -e 'BundledGem.unpack("gems/#{g}.gem", ".bundle")' \
+ -e 'end' \
gems/bundled_gems
+outdate-bundled-gems: PHONY
+ $(Q) $(BASERUBY) $(tooldir)/$@.rb --make="$(MAKE)" --mflags="$(MFLAGS)" "$(srcdir)"
+
update-bundled_gems: PHONY
$(Q) $(RUNRUBY) -rrubygems \
$(tooldir)/update-bundled_gems.rb \
"$(srcdir)/gems/bundled_gems" | \
$(IFCHANGE) "$(srcdir)/gems/bundled_gems" -
- git -C "$(srcdir)" diff --no-ext-diff --ignore-submodules --exit-code || \
- git -C "$(srcdir)" commit -m "Update bundled_gems" gems/bundled_gems
+ $(GIT) -C "$(srcdir)" diff --no-ext-diff --ignore-submodules --exit-code || \
+ $(GIT) -C "$(srcdir)" commit -m "Update bundled_gems" gems/bundled_gems
PRECHECK_BUNDLED_GEMS = test-bundled-gems-precheck
test-bundled-gems-precheck: $(TEST_RUNNABLE)-test-bundled-gems-precheck
@@ -1381,7 +1425,7 @@ yes-test-bundled-gems-precheck: main
no-test-bundled-gems-precheck:
test-bundled-gems-fetch: yes-test-bundled-gems-fetch
-yes-test-bundled-gems-fetch: $(PREP)
+yes-test-bundled-gems-fetch:
$(ACTIONS_GROUP)
$(Q) $(BASERUBY) -C $(srcdir)/gems ../tool/fetch-bundled_gems.rb src bundled_gems
$(ACTIONS_ENDGROUP)
@@ -1393,7 +1437,7 @@ no-test-bundled-gems-prepare: no-test-bundled-gems-precheck
yes-test-bundled-gems-prepare: yes-test-bundled-gems-precheck
$(ACTIONS_GROUP)
$(XRUBY) -C "$(srcdir)" bin/gem install --no-document \
- --install-dir .bundle --conservative "bundler" "minitest:~> 5" "test-unit" "rake" "hoe" "yard" "pry" "packnga" "rexml" "json-schema" "test-unit-rr"
+ --install-dir .bundle --conservative "bundler" "minitest:~> 5" "test-unit" "rake" "hoe" "rexml" "json-schema:5.1.0" "test-unit-rr"
$(ACTIONS_ENDGROUP)
PREPARE_BUNDLED_GEMS = test-bundled-gems-prepare
@@ -1406,11 +1450,34 @@ no-test-bundled-gems:
BUNDLED_GEMS =
test-bundled-gems-run: $(PREPARE_BUNDLED_GEMS)
- $(Q) $(XRUBY) $(tooldir)/test-bundled-gems.rb $(BUNDLED_GEMS)
+ $(gnumake_recursive)$(Q) $(XRUBY) $(tooldir)/test-bundled-gems.rb $(BUNDLED_GEMS)
+
+test-syntax-suggest-precheck: $(TEST_RUNNABLE)-test-syntax-suggest-precheck
+no-test-syntax-suggest-precheck:
+yes-test-syntax-suggest-precheck: main
+
+test-syntax-suggest-prepare: $(TEST_RUNNABLE)-test-syntax-suggest-prepare
+no-test-syntax-suggest-prepare: no-test-syntax-suggest-precheck
+yes-test-syntax-suggest-prepare: yes-test-syntax-suggest-precheck
+ $(ACTIONS_GROUP)
+ $(XRUBY) -C "$(srcdir)" bin/gem install --no-document \
+ --install-dir .bundle --conservative "bundler" "rake" "rspec:~> 3" #"ruby-prof"
+ $(ACTIONS_ENDGROUP)
+
+RSPECOPTS =
+SYNTAX_SUGGEST_SPECS =
+PREPARE_SYNTAX_SUGGEST = test-syntax-suggest-prepare
+test-syntax-suggest: $(TEST_RUNNABLE)-test-syntax-suggest
+yes-test-syntax-suggest: yes-$(PREPARE_SYNTAX_SUGGEST)
+ $(XRUBY) -C $(srcdir) -Ispec/syntax_suggest .bundle/bin/rspec \
+ --require spec_helper $(RSPECOPTS) spec/syntax_suggest/$(SYNTAX_SUGGEST_SPECS)
+no-test-syntax-suggest:
+
+check: $(DOT_WAIT) $(TEST_RUNNABLE)-$(PREPARE_SYNTAX_SUGGEST) test-syntax-suggest
test-bundler-precheck: $(TEST_RUNNABLE)-test-bundler-precheck
no-test-bundler-precheck:
-yes-test-bundler-precheck: main
+yes-test-bundler-precheck: main $(arch)-fake.rb
no-test-bundler-prepare: no-test-bundler-precheck
yes-test-bundler-prepare: yes-test-bundler-precheck
@@ -1427,14 +1494,18 @@ RSPECOPTS =
BUNDLER_SPECS =
test-bundler: $(TEST_RUNNABLE)-test-bundler
yes-test-bundler: yes-test-bundler-prepare
- $(XRUBY) -C $(srcdir) -Ispec/bundler .bundle/bin/rspec \
+ $(gnumake_recursive)$(XRUBY) \
+ -r./$(arch)-fake \
+ -e "exec(*ARGV)" -- \
+ $(XRUBY) -C $(srcdir) -Ispec/bundler .bundle/bin/rspec \
--require spec_helper $(RSPECOPTS) spec/bundler/$(BUNDLER_SPECS)
no-test-bundler:
PARALLELRSPECOPTS = --runtime-log $(srcdir)/tmp/parallel_runtime_rspec.log
test-bundler-parallel: $(TEST_RUNNABLE)-test-bundler-parallel
yes-test-bundler-parallel: yes-test-bundler-prepare
- $(XRUBY) \
+ $(gnumake_recursive)$(XRUBY) \
+ -r./$(arch)-fake \
-e "ARGV[-1] = File.expand_path(ARGV[-1])" \
-e "exec(*ARGV)" -- \
$(XRUBY) -I$(srcdir)/spec/bundler \
@@ -1493,60 +1564,54 @@ update-unicode: $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES) \
$(UNICODE_AUXILIARY_FILES) $(UNICODE_UCD_EMOJI_FILES) $(UNICODE_EMOJI_FILES)
CACHE_DIR = $(srcdir)/.downloaded-cache
-UNICODE_DOWNLOAD = \
+UNICODE_DOWNLOADER_ALWAYS_UPDATE = $(ALWAYS_UPDATE_UNICODE:yes=--always)
+UNICODE_DOWNLOADER = \
$(BASERUBY) $(tooldir)/downloader.rb \
--cache-dir=$(CACHE_DIR) \
- --unicode-beta $(UNICODE_BETA) \
+ --exist $(UNICODE_DOWNLOADER_ALWAYS_UPDATE:no=) \
+ unicode --unicode-beta=$(UNICODE_BETA)
+UNICODE_DOWNLOAD = \
+ $(UNICODE_DOWNLOADER) \
-d $(UNICODE_SRC_DATA_DIR) \
- -p $(UNICODE_VERSION)/ucd \
- -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode
+ -p $(UNICODE_VERSION)/ucd
UNICODE_AUXILIARY_DOWNLOAD = \
- $(BASERUBY) $(tooldir)/downloader.rb \
- --cache-dir=$(CACHE_DIR) \
- --unicode-beta $(UNICODE_BETA) \
+ $(UNICODE_DOWNLOADER) \
-d $(UNICODE_SRC_DATA_DIR)/auxiliary \
- -p $(UNICODE_VERSION)/ucd/auxiliary \
- -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode
+ -p $(UNICODE_VERSION)/ucd/auxiliary
UNICODE_UCD_EMOJI_DOWNLOAD = \
- $(BASERUBY) $(tooldir)/downloader.rb \
- --cache-dir=$(CACHE_DIR) \
- --unicode-beta $(UNICODE_BETA) \
+ $(UNICODE_DOWNLOADER) \
-d $(UNICODE_SRC_DATA_DIR)/emoji \
- -p $(UNICODE_VERSION)/ucd/emoji \
- -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode
+ -p $(UNICODE_VERSION)/ucd/emoji
UNICODE_EMOJI_DOWNLOAD = \
- $(BASERUBY) $(tooldir)/downloader.rb \
- --cache-dir=$(CACHE_DIR) \
- --unicode-beta $(UNICODE_BETA) \
+ $(UNICODE_DOWNLOADER) \
-d $(UNICODE_SRC_EMOJI_DATA_DIR) \
- -p emoji/$(UNICODE_EMOJI_VERSION) \
- -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode
+ -p emoji/$(UNICODE_EMOJI_VERSION)
-$(UNICODE_FILES) $(UNICODE_PROPERTY_FILES): update-unicode-files
-update-unicode-files:
+update-unicode-files: $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES)
+$(UNICODE_FILES) $(UNICODE_PROPERTY_FILES):
$(ECHO) Downloading Unicode $(UNICODE_VERSION) data and property files...
$(Q) $(MAKEDIRS) "$(UNICODE_SRC_DATA_DIR)"
$(Q) $(UNICODE_DOWNLOAD) $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES)
-$(UNICODE_AUXILIARY_FILES): update-unicode-auxiliary-files
-update-unicode-auxiliary-files:
+update-unicode-auxiliary-files: $(UNICODE_AUXILIARY_FILES)
+$(UNICODE_AUXILIARY_FILES):
$(ECHO) Downloading Unicode $(UNICODE_VERSION) auxiliary files...
$(Q) $(MAKEDIRS) "$(UNICODE_SRC_DATA_DIR)/auxiliary"
$(Q) $(UNICODE_AUXILIARY_DOWNLOAD) $(UNICODE_AUXILIARY_FILES)
-$(UNICODE_UCD_EMOJI_FILES): update-unicode-ucd-emoji-files
-update-unicode-ucd-emoji-files:
+update-unicode-ucd-emoji-files: $(UNICODE_UCD_EMOJI_FILES)
+$(UNICODE_UCD_EMOJI_FILES):
$(ECHO) Downloading Unicode UCD emoji $(UNICODE_EMOJI_VERSION) files...
$(Q) $(MAKEDIRS) "$(UNICODE_SRC_DATA_DIR)/emoji"
$(Q) $(UNICODE_UCD_EMOJI_DOWNLOAD) $(UNICODE_UCD_EMOJI_FILES)
-$(UNICODE_EMOJI_FILES): update-unicode-emoji-files
-update-unicode-emoji-files:
+update-unicode-emoji-files: $(UNICODE_EMOJI_FILES)
+$(UNICODE_EMOJI_FILES):
$(ECHO) Downloading Unicode emoji $(UNICODE_EMOJI_VERSION) files...
$(Q) $(MAKEDIRS) "$(UNICODE_SRC_EMOJI_DATA_DIR)"
$(Q) $(UNICODE_EMOJI_DOWNLOAD) $(UNICODE_EMOJI_FILES)
-$(srcdir)/lib/unicode_normalize/$(ALWAYS_UPDATE_UNICODE:yes=tables.rb): \
+$(srcdir)/lib/unicode_normalize/tables.rb: \
$(UNICODE_SRC_DATA_DIR)/$(HAVE_BASERUBY:yes=.unicode-tables.time)
$(UNICODE_SRC_DATA_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=.unicode-tables.time): \
@@ -1555,13 +1620,25 @@ $(UNICODE_SRC_DATA_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=.unicode-tables.time): \
touch-unicode-files:
$(MAKEDIRS) $(UNICODE_SRC_DATA_DIR)
- touch $(UNICODE_SRC_DATA_DIR)/.unicode-tables.time $(UNICODE_DATA_HEADERS)
+ $(Q) $(TOUCH) $(UNICODE_SRC_DATA_DIR)/.unicode-tables.time $(UNICODE_DATA_HEADERS)
+UNICODE_TABLES_DATA_FILES = \
+ $(UNICODE_SRC_DATA_DIR)/UnicodeData.txt \
+ $(UNICODE_SRC_DATA_DIR)/CompositionExclusions.txt \
+ $(empty)
+
+UNICODE_TABLES_DEPENDENTS_1 = none$(ALWAYS_UPDATE_UNICODE)
+UNICODE_TABLES_DEPENDENTS = $(UNICODE_TABLES_DEPENDENTS_1:noneyes=force)
UNICODE_TABLES_TIMESTAMP = yes
-$(UNICODE_SRC_DATA_DIR)/.unicode-tables.time: $(tooldir)/generic_erb.rb \
+$(UNICODE_SRC_DATA_DIR)/.unicode-tables.$(UNICODE_TABLES_DEPENDENTS:none=time):
+ $(Q) $(MAKEDIRS) $(@D)
+ $(Q) exit > $(@) || $(NULLCMD)
+$(UNICODE_SRC_DATA_DIR)/.unicode-tables.$(UNICODE_TABLES_DEPENDENTS:force=time): \
+ $(tooldir)/generic_erb.rb \
$(srcdir)/template/unicode_norm_gen.tmpl \
- $(ALWAYS_UPDATE_UNICODE:yes=update-unicode)
- $(Q) $(MAKE) $(@D)
+ $(UNICODE_TABLES_DATA_FILES) \
+ $(order_only) \
+ $(UNICODE_SRC_DATA_DIR)
$(Q) $(BASERUBY) $(tooldir)/generic_erb.rb \
-c $(UNICODE_TABLES_TIMESTAMP:yes=-t$@) \
-o $(srcdir)/lib/unicode_normalize/tables.rb \
@@ -1587,19 +1664,19 @@ $(UNICODE_HDR_DIR)/name2ctype.h:
$(MV) $@.new $@
# the next non-comment line was:
-# $(UNICODE_HDR_DIR)/casefold.h: $(srcdir)/enc/unicode/case-folding.rb \
+# $(UNICODE_HDR_DIR)/casefold.h: $(tooldir)/enc-case-folding.rb \
# but was changed to make sure CI works on systems that don't have gperf
unicode-up: $(UNICODE_DATA_HEADERS)
$(UNICODE_HDR_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=casefold.h): \
- $(srcdir)/enc/unicode/case-folding.rb \
+ $(tooldir)/enc-case-folding.rb \
$(UNICODE_SRC_DATA_DIR)/UnicodeData.txt \
$(UNICODE_SRC_DATA_DIR)/SpecialCasing.txt \
$(UNICODE_SRC_DATA_DIR)/CaseFolding.txt
$(UNICODE_HDR_DIR)/casefold.h:
$(MAKEDIRS) $(@D)
- $(Q) $(BASERUBY) $(srcdir)/enc/unicode/case-folding.rb \
+ $(Q) $(BASERUBY) $(tooldir)/enc-case-folding.rb \
--output-file=$@ \
--mapping-data-directory=$(UNICODE_SRC_DATA_DIR)
@@ -1659,41 +1736,45 @@ help: PHONY
" Makefile of Ruby" \
"" \
"targets:" \
- " all (default): builds all of below" \
- " miniruby: builds only miniruby" \
- " encs: builds encodings" \
- " exts: builds extensions" \
- " main: builds encodings, extensions and ruby" \
- " docs: builds documents" \
- " install-capi: builds C API documents" \
- " run: runs test.rb by miniruby" \
- " runruby: runs test.rb by ruby you just built" \
- " gdb: runs test.rb by miniruby under gdb" \
- " gdb-ruby: runs test.rb by ruby under gdb" \
- " check: equals make test test-tool test-all test-spec" \
- " test: ruby core tests [BTESTS=<bootstraptest files>]" \
- " test-all: all ruby tests [TESTOPTS=-j4 TESTS=<test files>]" \
- " test-spec: run the Ruby spec suite [SPECOPTS=<specs, opts>]" \
- " test-bundler: run the Bundler spec" \
- " test-bundled-gems: run the test suite of bundled gems" \
- " test-tool: tests under the tool/test" \
- " update-gems: download files of the bundled gems" \
- " update-bundled_gems: update the latest version of bundled gems" \
- " sync-default-gems: sync default gems from upstream [GEM=<gem_name git_ref>]" \
- " up: update local copy and autogenerated files" \
- " benchmark: benchmark this ruby and COMPARE_RUBY." \
- " gcbench: gc benchmark [GCBENCH_ITEM=<item_name>]" \
- " install: install all ruby distributions" \
- " install-nodoc: install without rdoc" \
- " install-cross: install cross compiling stuff" \
- " clean: clean for tarball" \
- " distclean: clean for repository" \
- " golf: build goruby for golfers" \
+ " all (default): builds all of below" \
+ " miniruby: builds only miniruby" \
+ " encs: builds encodings" \
+ " exts: builds extensions" \
+ " main: builds encodings, extensions and ruby" \
+ " docs: builds documents" \
+ " install-capi: builds C API documents" \
+ " run: runs test.rb by miniruby" \
+ " runruby: runs test.rb by ruby you just built" \
+ " gdb: runs test.rb by miniruby under gdb" \
+ " gdb-ruby: runs test.rb by ruby under gdb" \
+ " check: equals make test test-tool test-all test-spec" \
+ " test: ruby core tests [BTESTS=<bootstraptest files>]" \
+ " test-all: all ruby tests [TESTOPTS=-j4 TESTS=<test files>]" \
+ " test-spec: run the Ruby spec suite [SPECOPTS=<specs, opts>]" \
+ " test-bundler: run the Bundler spec" \
+ " test-bundler-parallel: run the Bundler spec with parallel" \
+ " test-bundled-gems: run the test suite of bundled gems" \
+ " test-tool: tests under the tool/test" \
+ " update-gems: download files of the bundled gems" \
+ " update-bundled_gems: update the latest version of bundled gems" \
+ " sync-default-gems: sync default gems from upstream [GEM=<gem_name git_ref>]" \
+ " up: update local copy and autogenerated files" \
+ " benchmark: benchmark this ruby and COMPARE_RUBY." \
+ " gcbench: gc benchmark [GCBENCH_ITEM=<item_name>]" \
+ " install: install all ruby distributions" \
+ " install-nodoc: install without rdoc" \
+ " install-cross: install cross compiling stuff" \
+ " clean: clean for tarball" \
+ " distclean: clean for repository" \
+ " golf: build goruby for golfers" \
$(HELP_EXTRA_TASKS) \
"see DeveloperHowto for more detail: " \
" https://bugs.ruby-lang.org/projects/ruby/wiki/DeveloperHowto" \
$(MESSAGE_END)
+$(CROSS_COMPILING:yes=)builtin.$(OBJEXT): {$(VPATH)}mini_builtin.c
+$(CROSS_COMPILING:yes=)builtin.$(OBJEXT): {$(VPATH)}miniprelude.c
+
# AUTOGENERATED DEPENDENCIES START
addr2line.$(OBJEXT): {$(VPATH)}addr2line.c
addr2line.$(OBJEXT): {$(VPATH)}addr2line.h
@@ -1751,6 +1832,7 @@ addr2line.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
addr2line.$(OBJEXT): {$(VPATH)}missing.h
array.$(OBJEXT): $(hdrdir)/ruby/ruby.h
array.$(OBJEXT): $(top_srcdir)/internal/array.h
+array.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
array.$(OBJEXT): $(top_srcdir)/internal/bignum.h
array.$(OBJEXT): $(top_srcdir)/internal/bits.h
array.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -1766,6 +1848,7 @@ array.$(OBJEXT): $(top_srcdir)/internal/proc.h
array.$(OBJEXT): $(top_srcdir)/internal/rational.h
array.$(OBJEXT): $(top_srcdir)/internal/serial.h
array.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+array.$(OBJEXT): $(top_srcdir)/internal/variable.h
array.$(OBJEXT): $(top_srcdir)/internal/vm.h
array.$(OBJEXT): $(top_srcdir)/internal/warnings.h
array.$(OBJEXT): {$(VPATH)}array.c
@@ -1782,6 +1865,7 @@ array.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
array.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
array.$(OBJEXT): {$(VPATH)}builtin.h
array.$(OBJEXT): {$(VPATH)}config.h
+array.$(OBJEXT): {$(VPATH)}constant.h
array.$(OBJEXT): {$(VPATH)}debug_counter.h
array.$(OBJEXT): {$(VPATH)}defines.h
array.$(OBJEXT): {$(VPATH)}encoding.h
@@ -1944,6 +2028,7 @@ array.$(OBJEXT): {$(VPATH)}oniguruma.h
array.$(OBJEXT): {$(VPATH)}probes.dmyh
array.$(OBJEXT): {$(VPATH)}probes.h
array.$(OBJEXT): {$(VPATH)}ruby_assert.h
+array.$(OBJEXT): {$(VPATH)}shape.h
array.$(OBJEXT): {$(VPATH)}st.h
array.$(OBJEXT): {$(VPATH)}subst.h
array.$(OBJEXT): {$(VPATH)}transient_heap.h
@@ -1955,6 +2040,7 @@ ast.$(OBJEXT): $(CCAN_DIR)/str/str.h
ast.$(OBJEXT): $(hdrdir)/ruby.h
ast.$(OBJEXT): $(hdrdir)/ruby/ruby.h
ast.$(OBJEXT): $(top_srcdir)/internal/array.h
+ast.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
ast.$(OBJEXT): $(top_srcdir)/internal/compilers.h
ast.$(OBJEXT): $(top_srcdir)/internal/gc.h
ast.$(OBJEXT): $(top_srcdir)/internal/imemo.h
@@ -1962,6 +2048,7 @@ ast.$(OBJEXT): $(top_srcdir)/internal/parse.h
ast.$(OBJEXT): $(top_srcdir)/internal/serial.h
ast.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
ast.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+ast.$(OBJEXT): $(top_srcdir)/internal/variable.h
ast.$(OBJEXT): $(top_srcdir)/internal/vm.h
ast.$(OBJEXT): $(top_srcdir)/internal/warnings.h
ast.$(OBJEXT): {$(VPATH)}assert.h
@@ -1979,9 +2066,11 @@ ast.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
ast.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
ast.$(OBJEXT): {$(VPATH)}builtin.h
ast.$(OBJEXT): {$(VPATH)}config.h
+ast.$(OBJEXT): {$(VPATH)}constant.h
ast.$(OBJEXT): {$(VPATH)}defines.h
ast.$(OBJEXT): {$(VPATH)}encoding.h
ast.$(OBJEXT): {$(VPATH)}id.h
+ast.$(OBJEXT): {$(VPATH)}id_table.h
ast.$(OBJEXT): {$(VPATH)}intern.h
ast.$(OBJEXT): {$(VPATH)}internal.h
ast.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -2141,6 +2230,7 @@ ast.$(OBJEXT): {$(VPATH)}onigmo.h
ast.$(OBJEXT): {$(VPATH)}oniguruma.h
ast.$(OBJEXT): {$(VPATH)}ruby_assert.h
ast.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+ast.$(OBJEXT): {$(VPATH)}shape.h
ast.$(OBJEXT): {$(VPATH)}st.h
ast.$(OBJEXT): {$(VPATH)}subst.h
ast.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -2324,6 +2414,7 @@ bignum.$(OBJEXT): {$(VPATH)}internal/warning_push.h
bignum.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
bignum.$(OBJEXT): {$(VPATH)}missing.h
bignum.$(OBJEXT): {$(VPATH)}ruby_assert.h
+bignum.$(OBJEXT): {$(VPATH)}shape.h
bignum.$(OBJEXT): {$(VPATH)}st.h
bignum.$(OBJEXT): {$(VPATH)}subst.h
bignum.$(OBJEXT): {$(VPATH)}thread.h
@@ -2334,11 +2425,13 @@ builtin.$(OBJEXT): $(CCAN_DIR)/list/list.h
builtin.$(OBJEXT): $(CCAN_DIR)/str/str.h
builtin.$(OBJEXT): $(hdrdir)/ruby/ruby.h
builtin.$(OBJEXT): $(top_srcdir)/internal/array.h
+builtin.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
builtin.$(OBJEXT): $(top_srcdir)/internal/compilers.h
builtin.$(OBJEXT): $(top_srcdir)/internal/gc.h
builtin.$(OBJEXT): $(top_srcdir)/internal/imemo.h
builtin.$(OBJEXT): $(top_srcdir)/internal/serial.h
builtin.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+builtin.$(OBJEXT): $(top_srcdir)/internal/variable.h
builtin.$(OBJEXT): $(top_srcdir)/internal/vm.h
builtin.$(OBJEXT): $(top_srcdir)/internal/warnings.h
builtin.$(OBJEXT): {$(VPATH)}assert.h
@@ -2356,8 +2449,10 @@ builtin.$(OBJEXT): {$(VPATH)}builtin.c
builtin.$(OBJEXT): {$(VPATH)}builtin.h
builtin.$(OBJEXT): {$(VPATH)}builtin_binary.inc
builtin.$(OBJEXT): {$(VPATH)}config.h
+builtin.$(OBJEXT): {$(VPATH)}constant.h
builtin.$(OBJEXT): {$(VPATH)}defines.h
builtin.$(OBJEXT): {$(VPATH)}id.h
+builtin.$(OBJEXT): {$(VPATH)}id_table.h
builtin.$(OBJEXT): {$(VPATH)}intern.h
builtin.$(OBJEXT): {$(VPATH)}internal.h
builtin.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -2506,6 +2601,7 @@ builtin.$(OBJEXT): {$(VPATH)}missing.h
builtin.$(OBJEXT): {$(VPATH)}node.h
builtin.$(OBJEXT): {$(VPATH)}ruby_assert.h
builtin.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+builtin.$(OBJEXT): {$(VPATH)}shape.h
builtin.$(OBJEXT): {$(VPATH)}st.h
builtin.$(OBJEXT): {$(VPATH)}subst.h
builtin.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -2518,6 +2614,7 @@ class.$(OBJEXT): $(CCAN_DIR)/list/list.h
class.$(OBJEXT): $(CCAN_DIR)/str/str.h
class.$(OBJEXT): $(hdrdir)/ruby/ruby.h
class.$(OBJEXT): $(top_srcdir)/internal/array.h
+class.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
class.$(OBJEXT): $(top_srcdir)/internal/class.h
class.$(OBJEXT): $(top_srcdir)/internal/compilers.h
class.$(OBJEXT): $(top_srcdir)/internal/eval.h
@@ -2708,6 +2805,7 @@ class.$(OBJEXT): {$(VPATH)}onigmo.h
class.$(OBJEXT): {$(VPATH)}oniguruma.h
class.$(OBJEXT): {$(VPATH)}ruby_assert.h
class.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+class.$(OBJEXT): {$(VPATH)}shape.h
class.$(OBJEXT): {$(VPATH)}st.h
class.$(OBJEXT): {$(VPATH)}subst.h
class.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -2715,6 +2813,7 @@ class.$(OBJEXT): {$(VPATH)}thread_native.h
class.$(OBJEXT): {$(VPATH)}vm_core.h
class.$(OBJEXT): {$(VPATH)}vm_opts.h
compar.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+compar.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
compar.$(OBJEXT): $(top_srcdir)/internal/compar.h
compar.$(OBJEXT): $(top_srcdir)/internal/compilers.h
compar.$(OBJEXT): $(top_srcdir)/internal/error.h
@@ -2899,6 +2998,7 @@ compile.$(OBJEXT): $(CCAN_DIR)/list/list.h
compile.$(OBJEXT): $(CCAN_DIR)/str/str.h
compile.$(OBJEXT): $(hdrdir)/ruby/ruby.h
compile.$(OBJEXT): $(top_srcdir)/internal/array.h
+compile.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
compile.$(OBJEXT): $(top_srcdir)/internal/bignum.h
compile.$(OBJEXT): $(top_srcdir)/internal/bits.h
compile.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -3111,6 +3211,7 @@ compile.$(OBJEXT): {$(VPATH)}re.h
compile.$(OBJEXT): {$(VPATH)}regex.h
compile.$(OBJEXT): {$(VPATH)}ruby_assert.h
compile.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+compile.$(OBJEXT): {$(VPATH)}shape.h
compile.$(OBJEXT): {$(VPATH)}st.h
compile.$(OBJEXT): {$(VPATH)}subst.h
compile.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -3120,8 +3221,13 @@ compile.$(OBJEXT): {$(VPATH)}vm_callinfo.h
compile.$(OBJEXT): {$(VPATH)}vm_core.h
compile.$(OBJEXT): {$(VPATH)}vm_debug.h
compile.$(OBJEXT): {$(VPATH)}vm_opts.h
+complex.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+complex.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+complex.$(OBJEXT): $(CCAN_DIR)/list/list.h
+complex.$(OBJEXT): $(CCAN_DIR)/str/str.h
complex.$(OBJEXT): $(hdrdir)/ruby/ruby.h
complex.$(OBJEXT): $(top_srcdir)/internal/array.h
+complex.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
complex.$(OBJEXT): $(top_srcdir)/internal/bignum.h
complex.$(OBJEXT): $(top_srcdir)/internal/bits.h
complex.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -3129,15 +3235,18 @@ complex.$(OBJEXT): $(top_srcdir)/internal/compilers.h
complex.$(OBJEXT): $(top_srcdir)/internal/complex.h
complex.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
complex.$(OBJEXT): $(top_srcdir)/internal/gc.h
+complex.$(OBJEXT): $(top_srcdir)/internal/imemo.h
complex.$(OBJEXT): $(top_srcdir)/internal/math.h
complex.$(OBJEXT): $(top_srcdir)/internal/numeric.h
complex.$(OBJEXT): $(top_srcdir)/internal/object.h
complex.$(OBJEXT): $(top_srcdir)/internal/rational.h
complex.$(OBJEXT): $(top_srcdir)/internal/serial.h
complex.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+complex.$(OBJEXT): $(top_srcdir)/internal/variable.h
complex.$(OBJEXT): $(top_srcdir)/internal/vm.h
complex.$(OBJEXT): $(top_srcdir)/internal/warnings.h
complex.$(OBJEXT): {$(VPATH)}assert.h
+complex.$(OBJEXT): {$(VPATH)}atomic.h
complex.$(OBJEXT): {$(VPATH)}backward/2/assume.h
complex.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
complex.$(OBJEXT): {$(VPATH)}backward/2/bool.h
@@ -3149,6 +3258,7 @@ complex.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
complex.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
complex.$(OBJEXT): {$(VPATH)}complex.c
complex.$(OBJEXT): {$(VPATH)}config.h
+complex.$(OBJEXT): {$(VPATH)}constant.h
complex.$(OBJEXT): {$(VPATH)}defines.h
complex.$(OBJEXT): {$(VPATH)}id.h
complex.$(OBJEXT): {$(VPATH)}id_table.h
@@ -3294,10 +3404,18 @@ complex.$(OBJEXT): {$(VPATH)}internal/value_type.h
complex.$(OBJEXT): {$(VPATH)}internal/variable.h
complex.$(OBJEXT): {$(VPATH)}internal/warning_push.h
complex.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+complex.$(OBJEXT): {$(VPATH)}method.h
complex.$(OBJEXT): {$(VPATH)}missing.h
+complex.$(OBJEXT): {$(VPATH)}node.h
complex.$(OBJEXT): {$(VPATH)}ruby_assert.h
+complex.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+complex.$(OBJEXT): {$(VPATH)}shape.h
complex.$(OBJEXT): {$(VPATH)}st.h
complex.$(OBJEXT): {$(VPATH)}subst.h
+complex.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+complex.$(OBJEXT): {$(VPATH)}thread_native.h
+complex.$(OBJEXT): {$(VPATH)}vm_core.h
+complex.$(OBJEXT): {$(VPATH)}vm_opts.h
cont.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
cont.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
cont.$(OBJEXT): $(CCAN_DIR)/list/list.h
@@ -3305,13 +3423,18 @@ cont.$(OBJEXT): $(CCAN_DIR)/str/str.h
cont.$(OBJEXT): $(hdrdir)/ruby.h
cont.$(OBJEXT): $(hdrdir)/ruby/ruby.h
cont.$(OBJEXT): $(top_srcdir)/internal/array.h
+cont.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
cont.$(OBJEXT): $(top_srcdir)/internal/compilers.h
cont.$(OBJEXT): $(top_srcdir)/internal/cont.h
+cont.$(OBJEXT): $(top_srcdir)/internal/error.h
cont.$(OBJEXT): $(top_srcdir)/internal/gc.h
cont.$(OBJEXT): $(top_srcdir)/internal/imemo.h
cont.$(OBJEXT): $(top_srcdir)/internal/proc.h
+cont.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
cont.$(OBJEXT): $(top_srcdir)/internal/serial.h
cont.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+cont.$(OBJEXT): $(top_srcdir)/internal/string.h
+cont.$(OBJEXT): $(top_srcdir)/internal/variable.h
cont.$(OBJEXT): $(top_srcdir)/internal/vm.h
cont.$(OBJEXT): $(top_srcdir)/internal/warnings.h
cont.$(OBJEXT): {$(VPATH)}$(COROUTINE_H)
@@ -3327,9 +3450,11 @@ cont.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
cont.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
cont.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
cont.$(OBJEXT): {$(VPATH)}config.h
+cont.$(OBJEXT): {$(VPATH)}constant.h
cont.$(OBJEXT): {$(VPATH)}cont.c
cont.$(OBJEXT): {$(VPATH)}debug_counter.h
cont.$(OBJEXT): {$(VPATH)}defines.h
+cont.$(OBJEXT): {$(VPATH)}encoding.h
cont.$(OBJEXT): {$(VPATH)}eval_intern.h
cont.$(OBJEXT): {$(VPATH)}fiber/scheduler.h
cont.$(OBJEXT): {$(VPATH)}gc.h
@@ -3407,6 +3532,15 @@ cont.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
cont.$(OBJEXT): {$(VPATH)}internal/ctype.h
cont.$(OBJEXT): {$(VPATH)}internal/dllexport.h
cont.$(OBJEXT): {$(VPATH)}internal/dosish.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
cont.$(OBJEXT): {$(VPATH)}internal/error.h
cont.$(OBJEXT): {$(VPATH)}internal/eval.h
cont.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -3477,14 +3611,18 @@ cont.$(OBJEXT): {$(VPATH)}internal/value_type.h
cont.$(OBJEXT): {$(VPATH)}internal/variable.h
cont.$(OBJEXT): {$(VPATH)}internal/warning_push.h
cont.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+cont.$(OBJEXT): {$(VPATH)}iseq.h
cont.$(OBJEXT): {$(VPATH)}method.h
cont.$(OBJEXT): {$(VPATH)}missing.h
cont.$(OBJEXT): {$(VPATH)}mjit.h
cont.$(OBJEXT): {$(VPATH)}node.h
+cont.$(OBJEXT): {$(VPATH)}onigmo.h
+cont.$(OBJEXT): {$(VPATH)}oniguruma.h
cont.$(OBJEXT): {$(VPATH)}ractor.h
cont.$(OBJEXT): {$(VPATH)}ractor_core.h
cont.$(OBJEXT): {$(VPATH)}ruby_assert.h
cont.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+cont.$(OBJEXT): {$(VPATH)}shape.h
cont.$(OBJEXT): {$(VPATH)}st.h
cont.$(OBJEXT): {$(VPATH)}subst.h
cont.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -3492,6 +3630,7 @@ cont.$(OBJEXT): {$(VPATH)}thread_native.h
cont.$(OBJEXT): {$(VPATH)}vm_core.h
cont.$(OBJEXT): {$(VPATH)}vm_debug.h
cont.$(OBJEXT): {$(VPATH)}vm_opts.h
+cont.$(OBJEXT): {$(VPATH)}vm_sync.h
cont.$(OBJEXT): {$(VPATH)}yjit.h
debug.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
debug.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
@@ -3499,6 +3638,7 @@ debug.$(OBJEXT): $(CCAN_DIR)/list/list.h
debug.$(OBJEXT): $(CCAN_DIR)/str/str.h
debug.$(OBJEXT): $(hdrdir)/ruby/ruby.h
debug.$(OBJEXT): $(top_srcdir)/internal/array.h
+debug.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
debug.$(OBJEXT): $(top_srcdir)/internal/class.h
debug.$(OBJEXT): $(top_srcdir)/internal/compilers.h
debug.$(OBJEXT): $(top_srcdir)/internal/gc.h
@@ -3506,6 +3646,7 @@ debug.$(OBJEXT): $(top_srcdir)/internal/imemo.h
debug.$(OBJEXT): $(top_srcdir)/internal/serial.h
debug.$(OBJEXT): $(top_srcdir)/internal/signal.h
debug.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+debug.$(OBJEXT): $(top_srcdir)/internal/variable.h
debug.$(OBJEXT): $(top_srcdir)/internal/vm.h
debug.$(OBJEXT): $(top_srcdir)/internal/warnings.h
debug.$(OBJEXT): {$(VPATH)}assert.h
@@ -3520,6 +3661,7 @@ debug.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
debug.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
debug.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
debug.$(OBJEXT): {$(VPATH)}config.h
+debug.$(OBJEXT): {$(VPATH)}constant.h
debug.$(OBJEXT): {$(VPATH)}debug.c
debug.$(OBJEXT): {$(VPATH)}debug_counter.h
debug.$(OBJEXT): {$(VPATH)}defines.h
@@ -3690,6 +3832,7 @@ debug.$(OBJEXT): {$(VPATH)}ractor.h
debug.$(OBJEXT): {$(VPATH)}ractor_core.h
debug.$(OBJEXT): {$(VPATH)}ruby_assert.h
debug.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+debug.$(OBJEXT): {$(VPATH)}shape.h
debug.$(OBJEXT): {$(VPATH)}st.h
debug.$(OBJEXT): {$(VPATH)}subst.h
debug.$(OBJEXT): {$(VPATH)}symbol.h
@@ -3874,6 +4017,7 @@ dir.$(OBJEXT): $(top_srcdir)/internal/object.h
dir.$(OBJEXT): $(top_srcdir)/internal/serial.h
dir.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
dir.$(OBJEXT): $(top_srcdir)/internal/string.h
+dir.$(OBJEXT): $(top_srcdir)/internal/variable.h
dir.$(OBJEXT): $(top_srcdir)/internal/vm.h
dir.$(OBJEXT): $(top_srcdir)/internal/warnings.h
dir.$(OBJEXT): {$(VPATH)}assert.h
@@ -3888,6 +4032,7 @@ dir.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
dir.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
dir.$(OBJEXT): {$(VPATH)}builtin.h
dir.$(OBJEXT): {$(VPATH)}config.h
+dir.$(OBJEXT): {$(VPATH)}constant.h
dir.$(OBJEXT): {$(VPATH)}defines.h
dir.$(OBJEXT): {$(VPATH)}dir.c
dir.$(OBJEXT): {$(VPATH)}dir.rbinc
@@ -4050,6 +4195,7 @@ dir.$(OBJEXT): {$(VPATH)}io.h
dir.$(OBJEXT): {$(VPATH)}missing.h
dir.$(OBJEXT): {$(VPATH)}onigmo.h
dir.$(OBJEXT): {$(VPATH)}oniguruma.h
+dir.$(OBJEXT): {$(VPATH)}shape.h
dir.$(OBJEXT): {$(VPATH)}st.h
dir.$(OBJEXT): {$(VPATH)}subst.h
dir.$(OBJEXT): {$(VPATH)}thread.h
@@ -5367,6 +5513,7 @@ encoding.$(OBJEXT): $(top_srcdir)/internal/class.h
encoding.$(OBJEXT): $(top_srcdir)/internal/compilers.h
encoding.$(OBJEXT): $(top_srcdir)/internal/enc.h
encoding.$(OBJEXT): $(top_srcdir)/internal/encoding.h
+encoding.$(OBJEXT): $(top_srcdir)/internal/error.h
encoding.$(OBJEXT): $(top_srcdir)/internal/gc.h
encoding.$(OBJEXT): $(top_srcdir)/internal/inits.h
encoding.$(OBJEXT): $(top_srcdir)/internal/load.h
@@ -5374,6 +5521,7 @@ encoding.$(OBJEXT): $(top_srcdir)/internal/object.h
encoding.$(OBJEXT): $(top_srcdir)/internal/serial.h
encoding.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
encoding.$(OBJEXT): $(top_srcdir)/internal/string.h
+encoding.$(OBJEXT): $(top_srcdir)/internal/variable.h
encoding.$(OBJEXT): $(top_srcdir)/internal/vm.h
encoding.$(OBJEXT): $(top_srcdir)/internal/warnings.h
encoding.$(OBJEXT): {$(VPATH)}assert.h
@@ -5387,6 +5535,7 @@ encoding.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
encoding.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
encoding.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
encoding.$(OBJEXT): {$(VPATH)}config.h
+encoding.$(OBJEXT): {$(VPATH)}constant.h
encoding.$(OBJEXT): {$(VPATH)}debug_counter.h
encoding.$(OBJEXT): {$(VPATH)}defines.h
encoding.$(OBJEXT): {$(VPATH)}encindex.h
@@ -5549,6 +5698,7 @@ encoding.$(OBJEXT): {$(VPATH)}onigmo.h
encoding.$(OBJEXT): {$(VPATH)}oniguruma.h
encoding.$(OBJEXT): {$(VPATH)}regenc.h
encoding.$(OBJEXT): {$(VPATH)}ruby_assert.h
+encoding.$(OBJEXT): {$(VPATH)}shape.h
encoding.$(OBJEXT): {$(VPATH)}st.h
encoding.$(OBJEXT): {$(VPATH)}subst.h
encoding.$(OBJEXT): {$(VPATH)}util.h
@@ -5556,6 +5706,7 @@ encoding.$(OBJEXT): {$(VPATH)}vm_debug.h
encoding.$(OBJEXT): {$(VPATH)}vm_sync.h
enum.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enum.$(OBJEXT): $(top_srcdir)/internal/array.h
+enum.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
enum.$(OBJEXT): $(top_srcdir)/internal/bignum.h
enum.$(OBJEXT): $(top_srcdir)/internal/bits.h
enum.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -5573,6 +5724,7 @@ enum.$(OBJEXT): $(top_srcdir)/internal/rational.h
enum.$(OBJEXT): $(top_srcdir)/internal/re.h
enum.$(OBJEXT): $(top_srcdir)/internal/serial.h
enum.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+enum.$(OBJEXT): $(top_srcdir)/internal/variable.h
enum.$(OBJEXT): $(top_srcdir)/internal/vm.h
enum.$(OBJEXT): $(top_srcdir)/internal/warnings.h
enum.$(OBJEXT): {$(VPATH)}assert.h
@@ -5586,6 +5738,7 @@ enum.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
enum.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
enum.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
enum.$(OBJEXT): {$(VPATH)}config.h
+enum.$(OBJEXT): {$(VPATH)}constant.h
enum.$(OBJEXT): {$(VPATH)}defines.h
enum.$(OBJEXT): {$(VPATH)}encoding.h
enum.$(OBJEXT): {$(VPATH)}enum.c
@@ -5746,14 +5899,21 @@ enum.$(OBJEXT): {$(VPATH)}missing.h
enum.$(OBJEXT): {$(VPATH)}onigmo.h
enum.$(OBJEXT): {$(VPATH)}oniguruma.h
enum.$(OBJEXT): {$(VPATH)}ruby_assert.h
+enum.$(OBJEXT): {$(VPATH)}shape.h
enum.$(OBJEXT): {$(VPATH)}st.h
enum.$(OBJEXT): {$(VPATH)}subst.h
enum.$(OBJEXT): {$(VPATH)}symbol.h
enum.$(OBJEXT): {$(VPATH)}util.h
+enumerator.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+enumerator.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+enumerator.$(OBJEXT): $(CCAN_DIR)/list/list.h
+enumerator.$(OBJEXT): $(CCAN_DIR)/str/str.h
enumerator.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/array.h
+enumerator.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/bignum.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/bits.h
+enumerator.$(OBJEXT): $(top_srcdir)/internal/class.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/compilers.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/enumerator.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/error.h
@@ -5771,6 +5931,7 @@ enumerator.$(OBJEXT): $(top_srcdir)/internal/struct.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/vm.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/warnings.h
enumerator.$(OBJEXT): {$(VPATH)}assert.h
+enumerator.$(OBJEXT): {$(VPATH)}atomic.h
enumerator.$(OBJEXT): {$(VPATH)}backward/2/assume.h
enumerator.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
enumerator.$(OBJEXT): {$(VPATH)}backward/2/bool.h
@@ -5785,6 +5946,7 @@ enumerator.$(OBJEXT): {$(VPATH)}defines.h
enumerator.$(OBJEXT): {$(VPATH)}encoding.h
enumerator.$(OBJEXT): {$(VPATH)}enumerator.c
enumerator.$(OBJEXT): {$(VPATH)}id.h
+enumerator.$(OBJEXT): {$(VPATH)}id_table.h
enumerator.$(OBJEXT): {$(VPATH)}intern.h
enumerator.$(OBJEXT): {$(VPATH)}internal.h
enumerator.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -5936,18 +6098,27 @@ enumerator.$(OBJEXT): {$(VPATH)}internal/value_type.h
enumerator.$(OBJEXT): {$(VPATH)}internal/variable.h
enumerator.$(OBJEXT): {$(VPATH)}internal/warning_push.h
enumerator.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+enumerator.$(OBJEXT): {$(VPATH)}method.h
enumerator.$(OBJEXT): {$(VPATH)}missing.h
+enumerator.$(OBJEXT): {$(VPATH)}node.h
enumerator.$(OBJEXT): {$(VPATH)}onigmo.h
enumerator.$(OBJEXT): {$(VPATH)}oniguruma.h
enumerator.$(OBJEXT): {$(VPATH)}ruby_assert.h
+enumerator.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+enumerator.$(OBJEXT): {$(VPATH)}shape.h
enumerator.$(OBJEXT): {$(VPATH)}st.h
enumerator.$(OBJEXT): {$(VPATH)}subst.h
+enumerator.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+enumerator.$(OBJEXT): {$(VPATH)}thread_native.h
+enumerator.$(OBJEXT): {$(VPATH)}vm_core.h
+enumerator.$(OBJEXT): {$(VPATH)}vm_opts.h
error.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
error.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
error.$(OBJEXT): $(CCAN_DIR)/list/list.h
error.$(OBJEXT): $(CCAN_DIR)/str/str.h
error.$(OBJEXT): $(hdrdir)/ruby/ruby.h
error.$(OBJEXT): $(top_srcdir)/internal/array.h
+error.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
error.$(OBJEXT): $(top_srcdir)/internal/class.h
error.$(OBJEXT): $(top_srcdir)/internal/compilers.h
error.$(OBJEXT): $(top_srcdir)/internal/error.h
@@ -6145,6 +6316,7 @@ error.$(OBJEXT): {$(VPATH)}onigmo.h
error.$(OBJEXT): {$(VPATH)}oniguruma.h
error.$(OBJEXT): {$(VPATH)}ruby_assert.h
error.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+error.$(OBJEXT): {$(VPATH)}shape.h
error.$(OBJEXT): {$(VPATH)}st.h
error.$(OBJEXT): {$(VPATH)}subst.h
error.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -6159,8 +6331,10 @@ eval.$(OBJEXT): $(CCAN_DIR)/str/str.h
eval.$(OBJEXT): $(hdrdir)/ruby.h
eval.$(OBJEXT): $(hdrdir)/ruby/ruby.h
eval.$(OBJEXT): $(top_srcdir)/internal/array.h
+eval.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
eval.$(OBJEXT): $(top_srcdir)/internal/class.h
eval.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+eval.$(OBJEXT): $(top_srcdir)/internal/cont.h
eval.$(OBJEXT): $(top_srcdir)/internal/error.h
eval.$(OBJEXT): $(top_srcdir)/internal/eval.h
eval.$(OBJEXT): $(top_srcdir)/internal/gc.h
@@ -6366,6 +6540,7 @@ eval.$(OBJEXT): {$(VPATH)}ractor.h
eval.$(OBJEXT): {$(VPATH)}ractor_core.h
eval.$(OBJEXT): {$(VPATH)}ruby_assert.h
eval.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+eval.$(OBJEXT): {$(VPATH)}shape.h
eval.$(OBJEXT): {$(VPATH)}st.h
eval.$(OBJEXT): {$(VPATH)}subst.h
eval.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -6374,7 +6549,6 @@ eval.$(OBJEXT): {$(VPATH)}vm.h
eval.$(OBJEXT): {$(VPATH)}vm_core.h
eval.$(OBJEXT): {$(VPATH)}vm_debug.h
eval.$(OBJEXT): {$(VPATH)}vm_opts.h
-eval.$(OBJEXT): {$(VPATH)}yjit.h
explicit_bzero.$(OBJEXT): {$(VPATH)}config.h
explicit_bzero.$(OBJEXT): {$(VPATH)}explicit_bzero.c
explicit_bzero.$(OBJEXT): {$(VPATH)}internal/attr/format.h
@@ -6407,6 +6581,7 @@ file.$(OBJEXT): $(top_srcdir)/internal/serial.h
file.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
file.$(OBJEXT): $(top_srcdir)/internal/string.h
file.$(OBJEXT): $(top_srcdir)/internal/thread.h
+file.$(OBJEXT): $(top_srcdir)/internal/variable.h
file.$(OBJEXT): $(top_srcdir)/internal/vm.h
file.$(OBJEXT): $(top_srcdir)/internal/warnings.h
file.$(OBJEXT): {$(VPATH)}assert.h
@@ -6420,6 +6595,7 @@ file.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
file.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
file.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
file.$(OBJEXT): {$(VPATH)}config.h
+file.$(OBJEXT): {$(VPATH)}constant.h
file.$(OBJEXT): {$(VPATH)}defines.h
file.$(OBJEXT): {$(VPATH)}dln.h
file.$(OBJEXT): {$(VPATH)}encindex.h
@@ -6582,6 +6758,7 @@ file.$(OBJEXT): {$(VPATH)}io.h
file.$(OBJEXT): {$(VPATH)}missing.h
file.$(OBJEXT): {$(VPATH)}onigmo.h
file.$(OBJEXT): {$(VPATH)}oniguruma.h
+file.$(OBJEXT): {$(VPATH)}shape.h
file.$(OBJEXT): {$(VPATH)}st.h
file.$(OBJEXT): {$(VPATH)}subst.h
file.$(OBJEXT): {$(VPATH)}thread.h
@@ -6593,6 +6770,7 @@ gc.$(OBJEXT): $(CCAN_DIR)/str/str.h
gc.$(OBJEXT): $(hdrdir)/ruby.h
gc.$(OBJEXT): $(hdrdir)/ruby/ruby.h
gc.$(OBJEXT): $(top_srcdir)/internal/array.h
+gc.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
gc.$(OBJEXT): $(top_srcdir)/internal/bignum.h
gc.$(OBJEXT): $(top_srcdir)/internal/bits.h
gc.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -6641,7 +6819,6 @@ gc.$(OBJEXT): {$(VPATH)}encoding.h
gc.$(OBJEXT): {$(VPATH)}eval_intern.h
gc.$(OBJEXT): {$(VPATH)}gc.c
gc.$(OBJEXT): {$(VPATH)}gc.h
-gc.$(OBJEXT): {$(VPATH)}gc.rb
gc.$(OBJEXT): {$(VPATH)}gc.rbinc
gc.$(OBJEXT): {$(VPATH)}id.h
gc.$(OBJEXT): {$(VPATH)}id_table.h
@@ -6798,6 +6975,7 @@ gc.$(OBJEXT): {$(VPATH)}internal/variable.h
gc.$(OBJEXT): {$(VPATH)}internal/warning_push.h
gc.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
gc.$(OBJEXT): {$(VPATH)}io.h
+gc.$(OBJEXT): {$(VPATH)}iseq.h
gc.$(OBJEXT): {$(VPATH)}method.h
gc.$(OBJEXT): {$(VPATH)}missing.h
gc.$(OBJEXT): {$(VPATH)}mjit.h
@@ -6814,6 +6992,7 @@ gc.$(OBJEXT): {$(VPATH)}regex.h
gc.$(OBJEXT): {$(VPATH)}regint.h
gc.$(OBJEXT): {$(VPATH)}ruby_assert.h
gc.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+gc.$(OBJEXT): {$(VPATH)}shape.h
gc.$(OBJEXT): {$(VPATH)}st.h
gc.$(OBJEXT): {$(VPATH)}subst.h
gc.$(OBJEXT): {$(VPATH)}symbol.h
@@ -6827,7 +7006,6 @@ gc.$(OBJEXT): {$(VPATH)}vm_core.h
gc.$(OBJEXT): {$(VPATH)}vm_debug.h
gc.$(OBJEXT): {$(VPATH)}vm_opts.h
gc.$(OBJEXT): {$(VPATH)}vm_sync.h
-gc.$(OBJEXT): {$(VPATH)}yjit.h
goruby.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
goruby.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
goruby.$(OBJEXT): $(CCAN_DIR)/list/list.h
@@ -6835,11 +7013,13 @@ goruby.$(OBJEXT): $(CCAN_DIR)/str/str.h
goruby.$(OBJEXT): $(hdrdir)/ruby.h
goruby.$(OBJEXT): $(hdrdir)/ruby/ruby.h
goruby.$(OBJEXT): $(top_srcdir)/internal/array.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
goruby.$(OBJEXT): $(top_srcdir)/internal/compilers.h
goruby.$(OBJEXT): $(top_srcdir)/internal/gc.h
goruby.$(OBJEXT): $(top_srcdir)/internal/imemo.h
goruby.$(OBJEXT): $(top_srcdir)/internal/serial.h
goruby.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/variable.h
goruby.$(OBJEXT): $(top_srcdir)/internal/vm.h
goruby.$(OBJEXT): $(top_srcdir)/internal/warnings.h
goruby.$(OBJEXT): {$(VPATH)}assert.h
@@ -6855,11 +7035,12 @@ goruby.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
goruby.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
goruby.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
goruby.$(OBJEXT): {$(VPATH)}config.h
+goruby.$(OBJEXT): {$(VPATH)}constant.h
goruby.$(OBJEXT): {$(VPATH)}defines.h
goruby.$(OBJEXT): {$(VPATH)}golf_prelude.c
-goruby.$(OBJEXT): {$(VPATH)}golf_prelude.rb
goruby.$(OBJEXT): {$(VPATH)}goruby.c
goruby.$(OBJEXT): {$(VPATH)}id.h
+goruby.$(OBJEXT): {$(VPATH)}id_table.h
goruby.$(OBJEXT): {$(VPATH)}intern.h
goruby.$(OBJEXT): {$(VPATH)}internal.h
goruby.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -6899,6 +7080,7 @@ goruby.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+goruby.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
@@ -7009,6 +7191,7 @@ goruby.$(OBJEXT): {$(VPATH)}missing.h
goruby.$(OBJEXT): {$(VPATH)}node.h
goruby.$(OBJEXT): {$(VPATH)}ruby_assert.h
goruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+goruby.$(OBJEXT): {$(VPATH)}shape.h
goruby.$(OBJEXT): {$(VPATH)}st.h
goruby.$(OBJEXT): {$(VPATH)}subst.h
goruby.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -7016,8 +7199,13 @@ goruby.$(OBJEXT): {$(VPATH)}thread_native.h
goruby.$(OBJEXT): {$(VPATH)}vm_core.h
goruby.$(OBJEXT): {$(VPATH)}vm_debug.h
goruby.$(OBJEXT): {$(VPATH)}vm_opts.h
+hash.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+hash.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+hash.$(OBJEXT): $(CCAN_DIR)/list/list.h
+hash.$(OBJEXT): $(CCAN_DIR)/str/str.h
hash.$(OBJEXT): $(hdrdir)/ruby/ruby.h
hash.$(OBJEXT): $(top_srcdir)/internal/array.h
+hash.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
hash.$(OBJEXT): $(top_srcdir)/internal/bignum.h
hash.$(OBJEXT): $(top_srcdir)/internal/bits.h
hash.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -7026,6 +7214,7 @@ hash.$(OBJEXT): $(top_srcdir)/internal/cont.h
hash.$(OBJEXT): $(top_srcdir)/internal/error.h
hash.$(OBJEXT): $(top_srcdir)/internal/gc.h
hash.$(OBJEXT): $(top_srcdir)/internal/hash.h
+hash.$(OBJEXT): $(top_srcdir)/internal/imemo.h
hash.$(OBJEXT): $(top_srcdir)/internal/object.h
hash.$(OBJEXT): $(top_srcdir)/internal/proc.h
hash.$(OBJEXT): $(top_srcdir)/internal/serial.h
@@ -7034,9 +7223,11 @@ hash.$(OBJEXT): $(top_srcdir)/internal/string.h
hash.$(OBJEXT): $(top_srcdir)/internal/symbol.h
hash.$(OBJEXT): $(top_srcdir)/internal/thread.h
hash.$(OBJEXT): $(top_srcdir)/internal/time.h
+hash.$(OBJEXT): $(top_srcdir)/internal/variable.h
hash.$(OBJEXT): $(top_srcdir)/internal/vm.h
hash.$(OBJEXT): $(top_srcdir)/internal/warnings.h
hash.$(OBJEXT): {$(VPATH)}assert.h
+hash.$(OBJEXT): {$(VPATH)}atomic.h
hash.$(OBJEXT): {$(VPATH)}backward/2/assume.h
hash.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
hash.$(OBJEXT): {$(VPATH)}backward/2/bool.h
@@ -7047,6 +7238,7 @@ hash.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
hash.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
hash.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
hash.$(OBJEXT): {$(VPATH)}config.h
+hash.$(OBJEXT): {$(VPATH)}constant.h
hash.$(OBJEXT): {$(VPATH)}debug_counter.h
hash.$(OBJEXT): {$(VPATH)}defines.h
hash.$(OBJEXT): {$(VPATH)}encoding.h
@@ -7204,20 +7396,28 @@ hash.$(OBJEXT): {$(VPATH)}internal/value_type.h
hash.$(OBJEXT): {$(VPATH)}internal/variable.h
hash.$(OBJEXT): {$(VPATH)}internal/warning_push.h
hash.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+hash.$(OBJEXT): {$(VPATH)}iseq.h
+hash.$(OBJEXT): {$(VPATH)}method.h
hash.$(OBJEXT): {$(VPATH)}missing.h
+hash.$(OBJEXT): {$(VPATH)}node.h
hash.$(OBJEXT): {$(VPATH)}onigmo.h
hash.$(OBJEXT): {$(VPATH)}oniguruma.h
hash.$(OBJEXT): {$(VPATH)}probes.dmyh
hash.$(OBJEXT): {$(VPATH)}probes.h
hash.$(OBJEXT): {$(VPATH)}ractor.h
hash.$(OBJEXT): {$(VPATH)}ruby_assert.h
+hash.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+hash.$(OBJEXT): {$(VPATH)}shape.h
hash.$(OBJEXT): {$(VPATH)}st.h
hash.$(OBJEXT): {$(VPATH)}subst.h
hash.$(OBJEXT): {$(VPATH)}symbol.h
+hash.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
hash.$(OBJEXT): {$(VPATH)}thread_native.h
hash.$(OBJEXT): {$(VPATH)}transient_heap.h
hash.$(OBJEXT): {$(VPATH)}util.h
+hash.$(OBJEXT): {$(VPATH)}vm_core.h
hash.$(OBJEXT): {$(VPATH)}vm_debug.h
+hash.$(OBJEXT): {$(VPATH)}vm_opts.h
hash.$(OBJEXT): {$(VPATH)}vm_sync.h
inits.$(OBJEXT): $(hdrdir)/ruby.h
inits.$(OBJEXT): $(hdrdir)/ruby/ruby.h
@@ -7389,6 +7589,7 @@ io.$(OBJEXT): $(CCAN_DIR)/list/list.h
io.$(OBJEXT): $(CCAN_DIR)/str/str.h
io.$(OBJEXT): $(hdrdir)/ruby/ruby.h
io.$(OBJEXT): $(top_srcdir)/internal/array.h
+io.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
io.$(OBJEXT): $(top_srcdir)/internal/bignum.h
io.$(OBJEXT): $(top_srcdir)/internal/bits.h
io.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -7595,6 +7796,7 @@ io.$(OBJEXT): {$(VPATH)}oniguruma.h
io.$(OBJEXT): {$(VPATH)}ractor.h
io.$(OBJEXT): {$(VPATH)}ruby_assert.h
io.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+io.$(OBJEXT): {$(VPATH)}shape.h
io.$(OBJEXT): {$(VPATH)}st.h
io.$(OBJEXT): {$(VPATH)}subst.h
io.$(OBJEXT): {$(VPATH)}thread.h
@@ -7604,11 +7806,18 @@ io.$(OBJEXT): {$(VPATH)}util.h
io.$(OBJEXT): {$(VPATH)}vm_core.h
io.$(OBJEXT): {$(VPATH)}vm_opts.h
io_buffer.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/array.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/bignum.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/bits.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/compilers.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/error.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/numeric.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/serial.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/string.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/thread.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/vm.h
io_buffer.$(OBJEXT): {$(VPATH)}assert.h
io_buffer.$(OBJEXT): {$(VPATH)}backward/2/assume.h
io_buffer.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
@@ -7789,6 +7998,7 @@ iseq.$(OBJEXT): $(CCAN_DIR)/str/str.h
iseq.$(OBJEXT): $(hdrdir)/ruby.h
iseq.$(OBJEXT): $(hdrdir)/ruby/ruby.h
iseq.$(OBJEXT): $(top_srcdir)/internal/array.h
+iseq.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
iseq.$(OBJEXT): $(top_srcdir)/internal/bits.h
iseq.$(OBJEXT): $(top_srcdir)/internal/class.h
iseq.$(OBJEXT): $(top_srcdir)/internal/compile.h
@@ -7995,6 +8205,7 @@ iseq.$(OBJEXT): {$(VPATH)}oniguruma.h
iseq.$(OBJEXT): {$(VPATH)}ractor.h
iseq.$(OBJEXT): {$(VPATH)}ruby_assert.h
iseq.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+iseq.$(OBJEXT): {$(VPATH)}shape.h
iseq.$(OBJEXT): {$(VPATH)}st.h
iseq.$(OBJEXT): {$(VPATH)}subst.h
iseq.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -8010,6 +8221,7 @@ load.$(OBJEXT): $(CCAN_DIR)/list/list.h
load.$(OBJEXT): $(CCAN_DIR)/str/str.h
load.$(OBJEXT): $(hdrdir)/ruby/ruby.h
load.$(OBJEXT): $(top_srcdir)/internal/array.h
+load.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
load.$(OBJEXT): $(top_srcdir)/internal/compilers.h
load.$(OBJEXT): $(top_srcdir)/internal/dir.h
load.$(OBJEXT): $(top_srcdir)/internal/error.h
@@ -8207,6 +8419,7 @@ load.$(OBJEXT): {$(VPATH)}probes.dmyh
load.$(OBJEXT): {$(VPATH)}probes.h
load.$(OBJEXT): {$(VPATH)}ruby_assert.h
load.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+load.$(OBJEXT): {$(VPATH)}shape.h
load.$(OBJEXT): {$(VPATH)}st.h
load.$(OBJEXT): {$(VPATH)}subst.h
load.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -8705,15 +8918,24 @@ main.$(OBJEXT): {$(VPATH)}missing.h
main.$(OBJEXT): {$(VPATH)}st.h
main.$(OBJEXT): {$(VPATH)}subst.h
main.$(OBJEXT): {$(VPATH)}vm_debug.h
+marshal.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+marshal.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+marshal.$(OBJEXT): $(CCAN_DIR)/list/list.h
+marshal.$(OBJEXT): $(CCAN_DIR)/str/str.h
marshal.$(OBJEXT): $(hdrdir)/ruby/ruby.h
marshal.$(OBJEXT): $(top_srcdir)/internal/array.h
+marshal.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
marshal.$(OBJEXT): $(top_srcdir)/internal/bignum.h
+marshal.$(OBJEXT): $(top_srcdir)/internal/bits.h
marshal.$(OBJEXT): $(top_srcdir)/internal/class.h
marshal.$(OBJEXT): $(top_srcdir)/internal/compilers.h
marshal.$(OBJEXT): $(top_srcdir)/internal/encoding.h
marshal.$(OBJEXT): $(top_srcdir)/internal/error.h
+marshal.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
marshal.$(OBJEXT): $(top_srcdir)/internal/gc.h
marshal.$(OBJEXT): $(top_srcdir)/internal/hash.h
+marshal.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+marshal.$(OBJEXT): $(top_srcdir)/internal/numeric.h
marshal.$(OBJEXT): $(top_srcdir)/internal/object.h
marshal.$(OBJEXT): $(top_srcdir)/internal/serial.h
marshal.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
@@ -8721,9 +8943,11 @@ marshal.$(OBJEXT): $(top_srcdir)/internal/string.h
marshal.$(OBJEXT): $(top_srcdir)/internal/struct.h
marshal.$(OBJEXT): $(top_srcdir)/internal/symbol.h
marshal.$(OBJEXT): $(top_srcdir)/internal/util.h
+marshal.$(OBJEXT): $(top_srcdir)/internal/variable.h
marshal.$(OBJEXT): $(top_srcdir)/internal/vm.h
marshal.$(OBJEXT): $(top_srcdir)/internal/warnings.h
marshal.$(OBJEXT): {$(VPATH)}assert.h
+marshal.$(OBJEXT): {$(VPATH)}atomic.h
marshal.$(OBJEXT): {$(VPATH)}backward/2/assume.h
marshal.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
marshal.$(OBJEXT): {$(VPATH)}backward/2/bool.h
@@ -8735,9 +8959,11 @@ marshal.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
marshal.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
marshal.$(OBJEXT): {$(VPATH)}builtin.h
marshal.$(OBJEXT): {$(VPATH)}config.h
+marshal.$(OBJEXT): {$(VPATH)}constant.h
marshal.$(OBJEXT): {$(VPATH)}defines.h
marshal.$(OBJEXT): {$(VPATH)}encindex.h
marshal.$(OBJEXT): {$(VPATH)}encoding.h
+marshal.$(OBJEXT): {$(VPATH)}id.h
marshal.$(OBJEXT): {$(VPATH)}id_table.h
marshal.$(OBJEXT): {$(VPATH)}intern.h
marshal.$(OBJEXT): {$(VPATH)}internal.h
@@ -8778,6 +9004,7 @@ marshal.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+marshal.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
@@ -8893,12 +9120,21 @@ marshal.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
marshal.$(OBJEXT): {$(VPATH)}io.h
marshal.$(OBJEXT): {$(VPATH)}marshal.c
marshal.$(OBJEXT): {$(VPATH)}marshal.rbinc
+marshal.$(OBJEXT): {$(VPATH)}method.h
marshal.$(OBJEXT): {$(VPATH)}missing.h
+marshal.$(OBJEXT): {$(VPATH)}node.h
marshal.$(OBJEXT): {$(VPATH)}onigmo.h
marshal.$(OBJEXT): {$(VPATH)}oniguruma.h
+marshal.$(OBJEXT): {$(VPATH)}ruby_assert.h
+marshal.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+marshal.$(OBJEXT): {$(VPATH)}shape.h
marshal.$(OBJEXT): {$(VPATH)}st.h
marshal.$(OBJEXT): {$(VPATH)}subst.h
+marshal.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+marshal.$(OBJEXT): {$(VPATH)}thread_native.h
marshal.$(OBJEXT): {$(VPATH)}util.h
+marshal.$(OBJEXT): {$(VPATH)}vm_core.h
+marshal.$(OBJEXT): {$(VPATH)}vm_opts.h
math.$(OBJEXT): $(hdrdir)/ruby/ruby.h
math.$(OBJEXT): $(top_srcdir)/internal/bignum.h
math.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -8909,6 +9145,7 @@ math.$(OBJEXT): $(top_srcdir)/internal/math.h
math.$(OBJEXT): $(top_srcdir)/internal/object.h
math.$(OBJEXT): $(top_srcdir)/internal/serial.h
math.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+math.$(OBJEXT): $(top_srcdir)/internal/variable.h
math.$(OBJEXT): $(top_srcdir)/internal/vm.h
math.$(OBJEXT): $(top_srcdir)/internal/warnings.h
math.$(OBJEXT): {$(VPATH)}assert.h
@@ -8922,6 +9159,7 @@ math.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
math.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
math.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
math.$(OBJEXT): {$(VPATH)}config.h
+math.$(OBJEXT): {$(VPATH)}constant.h
math.$(OBJEXT): {$(VPATH)}defines.h
math.$(OBJEXT): {$(VPATH)}id_table.h
math.$(OBJEXT): {$(VPATH)}intern.h
@@ -9068,15 +9306,20 @@ math.$(OBJEXT): {$(VPATH)}internal/warning_push.h
math.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
math.$(OBJEXT): {$(VPATH)}math.c
math.$(OBJEXT): {$(VPATH)}missing.h
+math.$(OBJEXT): {$(VPATH)}shape.h
math.$(OBJEXT): {$(VPATH)}st.h
math.$(OBJEXT): {$(VPATH)}subst.h
memory_view.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+memory_view.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+memory_view.$(OBJEXT): $(top_srcdir)/internal/gc.h
memory_view.$(OBJEXT): $(top_srcdir)/internal/hash.h
memory_view.$(OBJEXT): $(top_srcdir)/internal/variable.h
+memory_view.$(OBJEXT): $(top_srcdir)/internal/warnings.h
memory_view.$(OBJEXT): {$(VPATH)}assert.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/assume.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+memory_view.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/limits.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
@@ -9232,6 +9475,7 @@ memory_view.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
memory_view.$(OBJEXT): {$(VPATH)}memory_view.c
memory_view.$(OBJEXT): {$(VPATH)}memory_view.h
memory_view.$(OBJEXT): {$(VPATH)}missing.h
+memory_view.$(OBJEXT): {$(VPATH)}shape.h
memory_view.$(OBJEXT): {$(VPATH)}st.h
memory_view.$(OBJEXT): {$(VPATH)}subst.h
memory_view.$(OBJEXT): {$(VPATH)}util.h
@@ -9242,12 +9486,15 @@ miniinit.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
miniinit.$(OBJEXT): $(CCAN_DIR)/list/list.h
miniinit.$(OBJEXT): $(CCAN_DIR)/str/str.h
miniinit.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+miniinit.$(OBJEXT): $(srcdir)/mjit_c.rb
miniinit.$(OBJEXT): $(top_srcdir)/internal/array.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/compilers.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/gc.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/imemo.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/serial.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/variable.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/vm.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/warnings.h
miniinit.$(OBJEXT): {$(VPATH)}array.rb
@@ -9265,12 +9512,14 @@ miniinit.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
miniinit.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
miniinit.$(OBJEXT): {$(VPATH)}builtin.h
miniinit.$(OBJEXT): {$(VPATH)}config.h
+miniinit.$(OBJEXT): {$(VPATH)}constant.h
miniinit.$(OBJEXT): {$(VPATH)}defines.h
miniinit.$(OBJEXT): {$(VPATH)}dir.rb
miniinit.$(OBJEXT): {$(VPATH)}encoding.h
miniinit.$(OBJEXT): {$(VPATH)}gc.rb
miniinit.$(OBJEXT): {$(VPATH)}gem_prelude.rb
miniinit.$(OBJEXT): {$(VPATH)}id.h
+miniinit.$(OBJEXT): {$(VPATH)}id_table.h
miniinit.$(OBJEXT): {$(VPATH)}intern.h
miniinit.$(OBJEXT): {$(VPATH)}internal.h
miniinit.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -9310,6 +9559,7 @@ miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
@@ -9432,6 +9682,7 @@ miniinit.$(OBJEXT): {$(VPATH)}miniinit.c
miniinit.$(OBJEXT): {$(VPATH)}miniprelude.c
miniinit.$(OBJEXT): {$(VPATH)}missing.h
miniinit.$(OBJEXT): {$(VPATH)}mjit.rb
+miniinit.$(OBJEXT): {$(VPATH)}mjit_c.rb
miniinit.$(OBJEXT): {$(VPATH)}nilclass.rb
miniinit.$(OBJEXT): {$(VPATH)}node.h
miniinit.$(OBJEXT): {$(VPATH)}numeric.rb
@@ -9442,10 +9693,13 @@ miniinit.$(OBJEXT): {$(VPATH)}prelude.rb
miniinit.$(OBJEXT): {$(VPATH)}ractor.rb
miniinit.$(OBJEXT): {$(VPATH)}ruby_assert.h
miniinit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+miniinit.$(OBJEXT): {$(VPATH)}shape.h
miniinit.$(OBJEXT): {$(VPATH)}st.h
miniinit.$(OBJEXT): {$(VPATH)}subst.h
+miniinit.$(OBJEXT): {$(VPATH)}symbol.rb
miniinit.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
miniinit.$(OBJEXT): {$(VPATH)}thread_native.h
+miniinit.$(OBJEXT): {$(VPATH)}thread_sync.rb
miniinit.$(OBJEXT): {$(VPATH)}timev.rb
miniinit.$(OBJEXT): {$(VPATH)}trace_point.rb
miniinit.$(OBJEXT): {$(VPATH)}vm_core.h
@@ -9460,6 +9714,7 @@ mjit.$(OBJEXT): $(hdrdir)/ruby.h
mjit.$(OBJEXT): $(hdrdir)/ruby/ruby.h
mjit.$(OBJEXT): $(hdrdir)/ruby/version.h
mjit.$(OBJEXT): $(top_srcdir)/internal/array.h
+mjit.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
mjit.$(OBJEXT): $(top_srcdir)/internal/class.h
mjit.$(OBJEXT): $(top_srcdir)/internal/cmdlineopt.h
mjit.$(OBJEXT): $(top_srcdir)/internal/compile.h
@@ -9469,8 +9724,10 @@ mjit.$(OBJEXT): $(top_srcdir)/internal/file.h
mjit.$(OBJEXT): $(top_srcdir)/internal/gc.h
mjit.$(OBJEXT): $(top_srcdir)/internal/hash.h
mjit.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+mjit.$(OBJEXT): $(top_srcdir)/internal/process.h
mjit.$(OBJEXT): $(top_srcdir)/internal/serial.h
mjit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+mjit.$(OBJEXT): $(top_srcdir)/internal/variable.h
mjit.$(OBJEXT): $(top_srcdir)/internal/vm.h
mjit.$(OBJEXT): $(top_srcdir)/internal/warnings.h
mjit.$(OBJEXT): {$(VPATH)}assert.h
@@ -9654,10 +9911,9 @@ mjit.$(OBJEXT): {$(VPATH)}method.h
mjit.$(OBJEXT): {$(VPATH)}missing.h
mjit.$(OBJEXT): {$(VPATH)}mjit.c
mjit.$(OBJEXT): {$(VPATH)}mjit.h
-mjit.$(OBJEXT): {$(VPATH)}mjit.rb
mjit.$(OBJEXT): {$(VPATH)}mjit.rbinc
+mjit.$(OBJEXT): {$(VPATH)}mjit_c.h
mjit.$(OBJEXT): {$(VPATH)}mjit_config.h
-mjit.$(OBJEXT): {$(VPATH)}mjit_unit.h
mjit.$(OBJEXT): {$(VPATH)}node.h
mjit.$(OBJEXT): {$(VPATH)}onigmo.h
mjit.$(OBJEXT): {$(VPATH)}oniguruma.h
@@ -9665,6 +9921,7 @@ mjit.$(OBJEXT): {$(VPATH)}ractor.h
mjit.$(OBJEXT): {$(VPATH)}ractor_core.h
mjit.$(OBJEXT): {$(VPATH)}ruby_assert.h
mjit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+mjit.$(OBJEXT): {$(VPATH)}shape.h
mjit.$(OBJEXT): {$(VPATH)}st.h
mjit.$(OBJEXT): {$(VPATH)}subst.h
mjit.$(OBJEXT): {$(VPATH)}thread.h
@@ -9677,214 +9934,220 @@ mjit.$(OBJEXT): {$(VPATH)}vm_debug.h
mjit.$(OBJEXT): {$(VPATH)}vm_opts.h
mjit.$(OBJEXT): {$(VPATH)}vm_sync.h
mjit.$(OBJEXT): {$(VPATH)}yjit.h
-mjit_compile.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
-mjit_compile.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
-mjit_compile.$(OBJEXT): $(CCAN_DIR)/list/list.h
-mjit_compile.$(OBJEXT): $(CCAN_DIR)/str/str.h
-mjit_compile.$(OBJEXT): $(hdrdir)/ruby.h
-mjit_compile.$(OBJEXT): $(hdrdir)/ruby/ruby.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/array.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/class.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/compile.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/compilers.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/gc.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/hash.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/imemo.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/object.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/serial.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/variable.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/vm.h
-mjit_compile.$(OBJEXT): $(top_srcdir)/internal/warnings.h
-mjit_compile.$(OBJEXT): {$(VPATH)}assert.h
-mjit_compile.$(OBJEXT): {$(VPATH)}atomic.h
-mjit_compile.$(OBJEXT): {$(VPATH)}backward/2/assume.h
-mjit_compile.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
-mjit_compile.$(OBJEXT): {$(VPATH)}backward/2/bool.h
-mjit_compile.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
-mjit_compile.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
-mjit_compile.$(OBJEXT): {$(VPATH)}backward/2/limits.h
-mjit_compile.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
-mjit_compile.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
-mjit_compile.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
-mjit_compile.$(OBJEXT): {$(VPATH)}builtin.h
-mjit_compile.$(OBJEXT): {$(VPATH)}config.h
-mjit_compile.$(OBJEXT): {$(VPATH)}constant.h
-mjit_compile.$(OBJEXT): {$(VPATH)}debug_counter.h
-mjit_compile.$(OBJEXT): {$(VPATH)}defines.h
-mjit_compile.$(OBJEXT): {$(VPATH)}id.h
-mjit_compile.$(OBJEXT): {$(VPATH)}id_table.h
-mjit_compile.$(OBJEXT): {$(VPATH)}insns.def
-mjit_compile.$(OBJEXT): {$(VPATH)}insns.inc
-mjit_compile.$(OBJEXT): {$(VPATH)}insns_info.inc
-mjit_compile.$(OBJEXT): {$(VPATH)}intern.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/abi.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/anyargs.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/assume.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/const.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/error.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/format.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/cast.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/config.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/constant_p.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core/robject.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/ctype.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/dllexport.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/dosish.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/error.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/eval.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/event.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/fl_type.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/gc.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/glob.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/globals.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/has/extension.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/has/feature.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/has/warning.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/array.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/class.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/error.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/file.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/gc.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/io.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/load.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/object.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/process.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/random.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/range.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/re.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/select.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/string.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/time.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/interpreter.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/iterator.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/memory.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/method.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/module.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/newobj.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/rgengc.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/scan_args.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/special_consts.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/static_assert.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/stdalign.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/stdbool.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/symbol.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/value.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/value_type.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/variable.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/warning_push.h
-mjit_compile.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
-mjit_compile.$(OBJEXT): {$(VPATH)}iseq.h
-mjit_compile.$(OBJEXT): {$(VPATH)}method.h
-mjit_compile.$(OBJEXT): {$(VPATH)}missing.h
-mjit_compile.$(OBJEXT): {$(VPATH)}mjit.h
-mjit_compile.$(OBJEXT): {$(VPATH)}mjit_compile.c
-mjit_compile.$(OBJEXT): {$(VPATH)}mjit_compile.inc
-mjit_compile.$(OBJEXT): {$(VPATH)}mjit_unit.h
-mjit_compile.$(OBJEXT): {$(VPATH)}node.h
-mjit_compile.$(OBJEXT): {$(VPATH)}ruby_assert.h
-mjit_compile.$(OBJEXT): {$(VPATH)}ruby_atomic.h
-mjit_compile.$(OBJEXT): {$(VPATH)}st.h
-mjit_compile.$(OBJEXT): {$(VPATH)}subst.h
-mjit_compile.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
-mjit_compile.$(OBJEXT): {$(VPATH)}thread_native.h
-mjit_compile.$(OBJEXT): {$(VPATH)}vm_callinfo.h
-mjit_compile.$(OBJEXT): {$(VPATH)}vm_core.h
-mjit_compile.$(OBJEXT): {$(VPATH)}vm_exec.h
-mjit_compile.$(OBJEXT): {$(VPATH)}vm_insnhelper.h
-mjit_compile.$(OBJEXT): {$(VPATH)}vm_opts.h
-mjit_compile.$(OBJEXT): {$(VPATH)}yjit.h
+mjit_c.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+mjit_c.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+mjit_c.$(OBJEXT): $(CCAN_DIR)/list/list.h
+mjit_c.$(OBJEXT): $(CCAN_DIR)/str/str.h
+mjit_c.$(OBJEXT): $(hdrdir)/ruby.h
+mjit_c.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+mjit_c.$(OBJEXT): $(srcdir)/mjit_c.rb
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/array.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/class.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/compile.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/gc.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/hash.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/object.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/serial.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/variable.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/vm.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+mjit_c.$(OBJEXT): {$(VPATH)}assert.h
+mjit_c.$(OBJEXT): {$(VPATH)}atomic.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/limits.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+mjit_c.$(OBJEXT): {$(VPATH)}builtin.h
+mjit_c.$(OBJEXT): {$(VPATH)}config.h
+mjit_c.$(OBJEXT): {$(VPATH)}constant.h
+mjit_c.$(OBJEXT): {$(VPATH)}debug_counter.h
+mjit_c.$(OBJEXT): {$(VPATH)}defines.h
+mjit_c.$(OBJEXT): {$(VPATH)}id.h
+mjit_c.$(OBJEXT): {$(VPATH)}id_table.h
+mjit_c.$(OBJEXT): {$(VPATH)}insns.def
+mjit_c.$(OBJEXT): {$(VPATH)}insns.inc
+mjit_c.$(OBJEXT): {$(VPATH)}insns_info.inc
+mjit_c.$(OBJEXT): {$(VPATH)}intern.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/abi.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/anyargs.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/assume.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/cast.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/config.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/constant_p.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/robject.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/ctype.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/dosish.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/error.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/eval.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/event.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/fl_type.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/gc.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/glob.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/globals.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/array.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/class.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/error.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/file.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/gc.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/io.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/load.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/object.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/process.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/random.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/range.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/re.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/select.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/string.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/time.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/interpreter.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/iterator.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/memory.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/method.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/module.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/newobj.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/rgengc.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/scan_args.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/symbol.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/value.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/value_type.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/variable.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+mjit_c.$(OBJEXT): {$(VPATH)}iseq.h
+mjit_c.$(OBJEXT): {$(VPATH)}method.h
+mjit_c.$(OBJEXT): {$(VPATH)}missing.h
+mjit_c.$(OBJEXT): {$(VPATH)}mjit.h
+mjit_c.$(OBJEXT): {$(VPATH)}mjit_c.c
+mjit_c.$(OBJEXT): {$(VPATH)}mjit_c.h
+mjit_c.$(OBJEXT): {$(VPATH)}mjit_c.rb
+mjit_c.$(OBJEXT): {$(VPATH)}mjit_c.rbinc
+mjit_c.$(OBJEXT): {$(VPATH)}mjit_sp_inc.inc
+mjit_c.$(OBJEXT): {$(VPATH)}node.h
+mjit_c.$(OBJEXT): {$(VPATH)}ruby_assert.h
+mjit_c.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+mjit_c.$(OBJEXT): {$(VPATH)}shape.h
+mjit_c.$(OBJEXT): {$(VPATH)}st.h
+mjit_c.$(OBJEXT): {$(VPATH)}subst.h
+mjit_c.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+mjit_c.$(OBJEXT): {$(VPATH)}thread_native.h
+mjit_c.$(OBJEXT): {$(VPATH)}vm_callinfo.h
+mjit_c.$(OBJEXT): {$(VPATH)}vm_core.h
+mjit_c.$(OBJEXT): {$(VPATH)}vm_exec.h
+mjit_c.$(OBJEXT): {$(VPATH)}vm_insnhelper.h
+mjit_c.$(OBJEXT): {$(VPATH)}vm_opts.h
+mjit_c.$(OBJEXT): {$(VPATH)}yjit.h
node.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
node.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
node.$(OBJEXT): $(CCAN_DIR)/list/list.h
node.$(OBJEXT): $(CCAN_DIR)/str/str.h
node.$(OBJEXT): $(hdrdir)/ruby/ruby.h
node.$(OBJEXT): $(top_srcdir)/internal/array.h
+node.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
node.$(OBJEXT): $(top_srcdir)/internal/compilers.h
node.$(OBJEXT): $(top_srcdir)/internal/gc.h
node.$(OBJEXT): $(top_srcdir)/internal/hash.h
@@ -10058,6 +10321,7 @@ node.$(OBJEXT): {$(VPATH)}node.c
node.$(OBJEXT): {$(VPATH)}node.h
node.$(OBJEXT): {$(VPATH)}ruby_assert.h
node.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+node.$(OBJEXT): {$(VPATH)}shape.h
node.$(OBJEXT): {$(VPATH)}st.h
node.$(OBJEXT): {$(VPATH)}subst.h
node.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -10255,16 +10519,21 @@ numeric.$(OBJEXT): {$(VPATH)}internal/warning_push.h
numeric.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
numeric.$(OBJEXT): {$(VPATH)}missing.h
numeric.$(OBJEXT): {$(VPATH)}numeric.c
-numeric.$(OBJEXT): {$(VPATH)}numeric.rb
numeric.$(OBJEXT): {$(VPATH)}numeric.rbinc
numeric.$(OBJEXT): {$(VPATH)}onigmo.h
numeric.$(OBJEXT): {$(VPATH)}oniguruma.h
numeric.$(OBJEXT): {$(VPATH)}ruby_assert.h
+numeric.$(OBJEXT): {$(VPATH)}shape.h
numeric.$(OBJEXT): {$(VPATH)}st.h
numeric.$(OBJEXT): {$(VPATH)}subst.h
numeric.$(OBJEXT): {$(VPATH)}util.h
+object.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+object.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+object.$(OBJEXT): $(CCAN_DIR)/list/list.h
+object.$(OBJEXT): $(CCAN_DIR)/str/str.h
object.$(OBJEXT): $(hdrdir)/ruby/ruby.h
object.$(OBJEXT): $(top_srcdir)/internal/array.h
+object.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
object.$(OBJEXT): $(top_srcdir)/internal/bignum.h
object.$(OBJEXT): $(top_srcdir)/internal/bits.h
object.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -10273,6 +10542,7 @@ object.$(OBJEXT): $(top_srcdir)/internal/error.h
object.$(OBJEXT): $(top_srcdir)/internal/eval.h
object.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
object.$(OBJEXT): $(top_srcdir)/internal/gc.h
+object.$(OBJEXT): $(top_srcdir)/internal/imemo.h
object.$(OBJEXT): $(top_srcdir)/internal/inits.h
object.$(OBJEXT): $(top_srcdir)/internal/numeric.h
object.$(OBJEXT): $(top_srcdir)/internal/object.h
@@ -10285,6 +10555,7 @@ object.$(OBJEXT): $(top_srcdir)/internal/variable.h
object.$(OBJEXT): $(top_srcdir)/internal/vm.h
object.$(OBJEXT): $(top_srcdir)/internal/warnings.h
object.$(OBJEXT): {$(VPATH)}assert.h
+object.$(OBJEXT): {$(VPATH)}atomic.h
object.$(OBJEXT): {$(VPATH)}backward/2/assume.h
object.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
object.$(OBJEXT): {$(VPATH)}backward/2/bool.h
@@ -10452,22 +10723,32 @@ object.$(OBJEXT): {$(VPATH)}internal/value_type.h
object.$(OBJEXT): {$(VPATH)}internal/variable.h
object.$(OBJEXT): {$(VPATH)}internal/warning_push.h
object.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
-object.$(OBJEXT): {$(VPATH)}kernel.rb
object.$(OBJEXT): {$(VPATH)}kernel.rbinc
+object.$(OBJEXT): {$(VPATH)}method.h
object.$(OBJEXT): {$(VPATH)}missing.h
object.$(OBJEXT): {$(VPATH)}nilclass.rbinc
+object.$(OBJEXT): {$(VPATH)}node.h
object.$(OBJEXT): {$(VPATH)}object.c
object.$(OBJEXT): {$(VPATH)}onigmo.h
object.$(OBJEXT): {$(VPATH)}oniguruma.h
object.$(OBJEXT): {$(VPATH)}probes.dmyh
object.$(OBJEXT): {$(VPATH)}probes.h
+object.$(OBJEXT): {$(VPATH)}ruby_assert.h
+object.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+object.$(OBJEXT): {$(VPATH)}shape.h
object.$(OBJEXT): {$(VPATH)}st.h
object.$(OBJEXT): {$(VPATH)}subst.h
+object.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+object.$(OBJEXT): {$(VPATH)}thread_native.h
object.$(OBJEXT): {$(VPATH)}util.h
+object.$(OBJEXT): {$(VPATH)}variable.h
+object.$(OBJEXT): {$(VPATH)}vm_core.h
+object.$(OBJEXT): {$(VPATH)}vm_opts.h
pack.$(OBJEXT): $(hdrdir)/ruby/ruby.h
pack.$(OBJEXT): $(top_srcdir)/internal/array.h
pack.$(OBJEXT): $(top_srcdir)/internal/bits.h
pack.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+pack.$(OBJEXT): $(top_srcdir)/internal/gc.h
pack.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
pack.$(OBJEXT): $(top_srcdir)/internal/string.h
pack.$(OBJEXT): $(top_srcdir)/internal/symbol.h
@@ -10645,6 +10926,7 @@ pack.$(OBJEXT): {$(VPATH)}onigmo.h
pack.$(OBJEXT): {$(VPATH)}oniguruma.h
pack.$(OBJEXT): {$(VPATH)}pack.c
pack.$(OBJEXT): {$(VPATH)}pack.rbinc
+pack.$(OBJEXT): {$(VPATH)}shape.h
pack.$(OBJEXT): {$(VPATH)}st.h
pack.$(OBJEXT): {$(VPATH)}subst.h
pack.$(OBJEXT): {$(VPATH)}util.h
@@ -10656,6 +10938,7 @@ parse.$(OBJEXT): $(top_srcdir)/internal/bits.h
parse.$(OBJEXT): $(top_srcdir)/internal/compile.h
parse.$(OBJEXT): $(top_srcdir)/internal/compilers.h
parse.$(OBJEXT): $(top_srcdir)/internal/complex.h
+parse.$(OBJEXT): $(top_srcdir)/internal/encoding.h
parse.$(OBJEXT): $(top_srcdir)/internal/error.h
parse.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
parse.$(OBJEXT): $(top_srcdir)/internal/gc.h
@@ -10857,6 +11140,7 @@ parse.$(OBJEXT): {$(VPATH)}ractor.h
parse.$(OBJEXT): {$(VPATH)}regenc.h
parse.$(OBJEXT): {$(VPATH)}regex.h
parse.$(OBJEXT): {$(VPATH)}ruby_assert.h
+parse.$(OBJEXT): {$(VPATH)}shape.h
parse.$(OBJEXT): {$(VPATH)}st.h
parse.$(OBJEXT): {$(VPATH)}subst.h
parse.$(OBJEXT): {$(VPATH)}symbol.h
@@ -10867,11 +11151,13 @@ proc.$(OBJEXT): $(CCAN_DIR)/list/list.h
proc.$(OBJEXT): $(CCAN_DIR)/str/str.h
proc.$(OBJEXT): $(hdrdir)/ruby/ruby.h
proc.$(OBJEXT): $(top_srcdir)/internal/array.h
+proc.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
proc.$(OBJEXT): $(top_srcdir)/internal/class.h
proc.$(OBJEXT): $(top_srcdir)/internal/compilers.h
proc.$(OBJEXT): $(top_srcdir)/internal/error.h
proc.$(OBJEXT): $(top_srcdir)/internal/eval.h
proc.$(OBJEXT): $(top_srcdir)/internal/gc.h
+proc.$(OBJEXT): $(top_srcdir)/internal/hash.h
proc.$(OBJEXT): $(top_srcdir)/internal/imemo.h
proc.$(OBJEXT): $(top_srcdir)/internal/object.h
proc.$(OBJEXT): $(top_srcdir)/internal/proc.h
@@ -10879,6 +11165,7 @@ proc.$(OBJEXT): $(top_srcdir)/internal/serial.h
proc.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
proc.$(OBJEXT): $(top_srcdir)/internal/string.h
proc.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+proc.$(OBJEXT): $(top_srcdir)/internal/variable.h
proc.$(OBJEXT): $(top_srcdir)/internal/vm.h
proc.$(OBJEXT): $(top_srcdir)/internal/warnings.h
proc.$(OBJEXT): {$(VPATH)}assert.h
@@ -10893,6 +11180,7 @@ proc.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
proc.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
proc.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
proc.$(OBJEXT): {$(VPATH)}config.h
+proc.$(OBJEXT): {$(VPATH)}constant.h
proc.$(OBJEXT): {$(VPATH)}defines.h
proc.$(OBJEXT): {$(VPATH)}encoding.h
proc.$(OBJEXT): {$(VPATH)}eval_intern.h
@@ -11059,6 +11347,7 @@ proc.$(OBJEXT): {$(VPATH)}oniguruma.h
proc.$(OBJEXT): {$(VPATH)}proc.c
proc.$(OBJEXT): {$(VPATH)}ruby_assert.h
proc.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+proc.$(OBJEXT): {$(VPATH)}shape.h
proc.$(OBJEXT): {$(VPATH)}st.h
proc.$(OBJEXT): {$(VPATH)}subst.h
proc.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -11073,6 +11362,7 @@ process.$(OBJEXT): $(CCAN_DIR)/str/str.h
process.$(OBJEXT): $(hdrdir)/ruby.h
process.$(OBJEXT): $(hdrdir)/ruby/ruby.h
process.$(OBJEXT): $(top_srcdir)/internal/array.h
+process.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
process.$(OBJEXT): $(top_srcdir)/internal/bignum.h
process.$(OBJEXT): $(top_srcdir)/internal/bits.h
process.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -11278,6 +11568,7 @@ process.$(OBJEXT): {$(VPATH)}process.c
process.$(OBJEXT): {$(VPATH)}ractor.h
process.$(OBJEXT): {$(VPATH)}ruby_assert.h
process.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+process.$(OBJEXT): {$(VPATH)}shape.h
process.$(OBJEXT): {$(VPATH)}st.h
process.$(OBJEXT): {$(VPATH)}subst.h
process.$(OBJEXT): {$(VPATH)}thread.h
@@ -11286,13 +11577,14 @@ process.$(OBJEXT): {$(VPATH)}thread_native.h
process.$(OBJEXT): {$(VPATH)}util.h
process.$(OBJEXT): {$(VPATH)}vm_core.h
process.$(OBJEXT): {$(VPATH)}vm_opts.h
-process.$(OBJEXT): {$(VPATH)}yjit.h
ractor.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
ractor.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
ractor.$(OBJEXT): $(CCAN_DIR)/list/list.h
ractor.$(OBJEXT): $(CCAN_DIR)/str/str.h
+ractor.$(OBJEXT): $(hdrdir)/ruby.h
ractor.$(OBJEXT): $(hdrdir)/ruby/ruby.h
ractor.$(OBJEXT): $(top_srcdir)/internal/array.h
+ractor.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
ractor.$(OBJEXT): $(top_srcdir)/internal/bignum.h
ractor.$(OBJEXT): $(top_srcdir)/internal/bits.h
ractor.$(OBJEXT): $(top_srcdir)/internal/compilers.h
@@ -11309,6 +11601,7 @@ ractor.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
ractor.$(OBJEXT): $(top_srcdir)/internal/string.h
ractor.$(OBJEXT): $(top_srcdir)/internal/struct.h
ractor.$(OBJEXT): $(top_srcdir)/internal/thread.h
+ractor.$(OBJEXT): $(top_srcdir)/internal/variable.h
ractor.$(OBJEXT): $(top_srcdir)/internal/vm.h
ractor.$(OBJEXT): $(top_srcdir)/internal/warnings.h
ractor.$(OBJEXT): {$(VPATH)}assert.h
@@ -11324,6 +11617,7 @@ ractor.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
ractor.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
ractor.$(OBJEXT): {$(VPATH)}builtin.h
ractor.$(OBJEXT): {$(VPATH)}config.h
+ractor.$(OBJEXT): {$(VPATH)}constant.h
ractor.$(OBJEXT): {$(VPATH)}debug_counter.h
ractor.$(OBJEXT): {$(VPATH)}defines.h
ractor.$(OBJEXT): {$(VPATH)}encoding.h
@@ -11483,16 +11777,17 @@ ractor.$(OBJEXT): {$(VPATH)}internal/warning_push.h
ractor.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
ractor.$(OBJEXT): {$(VPATH)}method.h
ractor.$(OBJEXT): {$(VPATH)}missing.h
+ractor.$(OBJEXT): {$(VPATH)}mjit.h
ractor.$(OBJEXT): {$(VPATH)}node.h
ractor.$(OBJEXT): {$(VPATH)}onigmo.h
ractor.$(OBJEXT): {$(VPATH)}oniguruma.h
ractor.$(OBJEXT): {$(VPATH)}ractor.c
ractor.$(OBJEXT): {$(VPATH)}ractor.h
-ractor.$(OBJEXT): {$(VPATH)}ractor.rb
ractor.$(OBJEXT): {$(VPATH)}ractor.rbinc
ractor.$(OBJEXT): {$(VPATH)}ractor_core.h
ractor.$(OBJEXT): {$(VPATH)}ruby_assert.h
ractor.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+ractor.$(OBJEXT): {$(VPATH)}shape.h
ractor.$(OBJEXT): {$(VPATH)}st.h
ractor.$(OBJEXT): {$(VPATH)}subst.h
ractor.$(OBJEXT): {$(VPATH)}thread.h
@@ -11511,6 +11806,7 @@ random.$(OBJEXT): $(top_srcdir)/internal/bignum.h
random.$(OBJEXT): $(top_srcdir)/internal/bits.h
random.$(OBJEXT): $(top_srcdir)/internal/compilers.h
random.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
+random.$(OBJEXT): $(top_srcdir)/internal/gc.h
random.$(OBJEXT): $(top_srcdir)/internal/numeric.h
random.$(OBJEXT): $(top_srcdir)/internal/random.h
random.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
@@ -11682,12 +11978,14 @@ random.$(OBJEXT): {$(VPATH)}ractor.h
random.$(OBJEXT): {$(VPATH)}random.c
random.$(OBJEXT): {$(VPATH)}random.h
random.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+random.$(OBJEXT): {$(VPATH)}shape.h
random.$(OBJEXT): {$(VPATH)}siphash.c
random.$(OBJEXT): {$(VPATH)}siphash.h
random.$(OBJEXT): {$(VPATH)}st.h
random.$(OBJEXT): {$(VPATH)}subst.h
range.$(OBJEXT): $(hdrdir)/ruby/ruby.h
range.$(OBJEXT): $(top_srcdir)/internal/array.h
+range.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
range.$(OBJEXT): $(top_srcdir)/internal/bignum.h
range.$(OBJEXT): $(top_srcdir)/internal/bits.h
range.$(OBJEXT): $(top_srcdir)/internal/compar.h
@@ -11874,6 +12172,7 @@ range.$(OBJEXT): {$(VPATH)}missing.h
range.$(OBJEXT): {$(VPATH)}onigmo.h
range.$(OBJEXT): {$(VPATH)}oniguruma.h
range.$(OBJEXT): {$(VPATH)}range.c
+range.$(OBJEXT): {$(VPATH)}shape.h
range.$(OBJEXT): {$(VPATH)}st.h
range.$(OBJEXT): {$(VPATH)}subst.h
rational.$(OBJEXT): $(hdrdir)/ruby/ruby.h
@@ -11890,6 +12189,7 @@ rational.$(OBJEXT): $(top_srcdir)/internal/object.h
rational.$(OBJEXT): $(top_srcdir)/internal/rational.h
rational.$(OBJEXT): $(top_srcdir)/internal/serial.h
rational.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+rational.$(OBJEXT): $(top_srcdir)/internal/variable.h
rational.$(OBJEXT): $(top_srcdir)/internal/vm.h
rational.$(OBJEXT): $(top_srcdir)/internal/warnings.h
rational.$(OBJEXT): {$(VPATH)}assert.h
@@ -11903,6 +12203,7 @@ rational.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
rational.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
rational.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
rational.$(OBJEXT): {$(VPATH)}config.h
+rational.$(OBJEXT): {$(VPATH)}constant.h
rational.$(OBJEXT): {$(VPATH)}defines.h
rational.$(OBJEXT): {$(VPATH)}id.h
rational.$(OBJEXT): {$(VPATH)}id_table.h
@@ -12051,6 +12352,7 @@ rational.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
rational.$(OBJEXT): {$(VPATH)}missing.h
rational.$(OBJEXT): {$(VPATH)}rational.c
rational.$(OBJEXT): {$(VPATH)}ruby_assert.h
+rational.$(OBJEXT): {$(VPATH)}shape.h
rational.$(OBJEXT): {$(VPATH)}st.h
rational.$(OBJEXT): {$(VPATH)}subst.h
re.$(OBJEXT): $(hdrdir)/ruby.h
@@ -12059,6 +12361,7 @@ re.$(OBJEXT): $(top_srcdir)/internal/array.h
re.$(OBJEXT): $(top_srcdir)/internal/bits.h
re.$(OBJEXT): $(top_srcdir)/internal/class.h
re.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+re.$(OBJEXT): $(top_srcdir)/internal/encoding.h
re.$(OBJEXT): $(top_srcdir)/internal/gc.h
re.$(OBJEXT): $(top_srcdir)/internal/hash.h
re.$(OBJEXT): $(top_srcdir)/internal/imemo.h
@@ -12248,6 +12551,7 @@ re.$(OBJEXT): {$(VPATH)}re.h
re.$(OBJEXT): {$(VPATH)}regenc.h
re.$(OBJEXT): {$(VPATH)}regex.h
re.$(OBJEXT): {$(VPATH)}regint.h
+re.$(OBJEXT): {$(VPATH)}shape.h
re.$(OBJEXT): {$(VPATH)}st.h
re.$(OBJEXT): {$(VPATH)}subst.h
re.$(OBJEXT): {$(VPATH)}util.h
@@ -13243,9 +13547,11 @@ ruby.$(OBJEXT): $(hdrdir)/ruby.h
ruby.$(OBJEXT): $(hdrdir)/ruby/ruby.h
ruby.$(OBJEXT): $(hdrdir)/ruby/version.h
ruby.$(OBJEXT): $(top_srcdir)/internal/array.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
ruby.$(OBJEXT): $(top_srcdir)/internal/class.h
ruby.$(OBJEXT): $(top_srcdir)/internal/cmdlineopt.h
ruby.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/cont.h
ruby.$(OBJEXT): $(top_srcdir)/internal/error.h
ruby.$(OBJEXT): $(top_srcdir)/internal/file.h
ruby.$(OBJEXT): $(top_srcdir)/internal/gc.h
@@ -13436,6 +13742,7 @@ ruby.$(OBJEXT): {$(VPATH)}internal/variable.h
ruby.$(OBJEXT): {$(VPATH)}internal/warning_push.h
ruby.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
ruby.$(OBJEXT): {$(VPATH)}io.h
+ruby.$(OBJEXT): {$(VPATH)}iseq.h
ruby.$(OBJEXT): {$(VPATH)}method.h
ruby.$(OBJEXT): {$(VPATH)}missing.h
ruby.$(OBJEXT): {$(VPATH)}mjit.h
@@ -13445,6 +13752,7 @@ ruby.$(OBJEXT): {$(VPATH)}oniguruma.h
ruby.$(OBJEXT): {$(VPATH)}ruby.c
ruby.$(OBJEXT): {$(VPATH)}ruby_assert.h
ruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+ruby.$(OBJEXT): {$(VPATH)}shape.h
ruby.$(OBJEXT): {$(VPATH)}st.h
ruby.$(OBJEXT): {$(VPATH)}subst.h
ruby.$(OBJEXT): {$(VPATH)}thread.h
@@ -13460,12 +13768,14 @@ scheduler.$(OBJEXT): $(CCAN_DIR)/list/list.h
scheduler.$(OBJEXT): $(CCAN_DIR)/str/str.h
scheduler.$(OBJEXT): $(hdrdir)/ruby/ruby.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/array.h
+scheduler.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/compilers.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/gc.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/imemo.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/serial.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/thread.h
+scheduler.$(OBJEXT): $(top_srcdir)/internal/variable.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/vm.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/warnings.h
scheduler.$(OBJEXT): {$(VPATH)}assert.h
@@ -13480,10 +13790,12 @@ scheduler.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
scheduler.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
scheduler.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
scheduler.$(OBJEXT): {$(VPATH)}config.h
+scheduler.$(OBJEXT): {$(VPATH)}constant.h
scheduler.$(OBJEXT): {$(VPATH)}defines.h
scheduler.$(OBJEXT): {$(VPATH)}encoding.h
scheduler.$(OBJEXT): {$(VPATH)}fiber/scheduler.h
scheduler.$(OBJEXT): {$(VPATH)}id.h
+scheduler.$(OBJEXT): {$(VPATH)}id_table.h
scheduler.$(OBJEXT): {$(VPATH)}intern.h
scheduler.$(OBJEXT): {$(VPATH)}internal.h
scheduler.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -13645,6 +13957,7 @@ scheduler.$(OBJEXT): {$(VPATH)}oniguruma.h
scheduler.$(OBJEXT): {$(VPATH)}ruby_assert.h
scheduler.$(OBJEXT): {$(VPATH)}ruby_atomic.h
scheduler.$(OBJEXT): {$(VPATH)}scheduler.c
+scheduler.$(OBJEXT): {$(VPATH)}shape.h
scheduler.$(OBJEXT): {$(VPATH)}st.h
scheduler.$(OBJEXT): {$(VPATH)}subst.h
scheduler.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -13810,13 +14123,219 @@ setproctitle.$(OBJEXT): {$(VPATH)}setproctitle.c
setproctitle.$(OBJEXT): {$(VPATH)}st.h
setproctitle.$(OBJEXT): {$(VPATH)}subst.h
setproctitle.$(OBJEXT): {$(VPATH)}util.h
+shape.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+shape.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+shape.$(OBJEXT): $(CCAN_DIR)/list/list.h
+shape.$(OBJEXT): $(CCAN_DIR)/str/str.h
+shape.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+shape.$(OBJEXT): $(top_srcdir)/internal/array.h
+shape.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+shape.$(OBJEXT): $(top_srcdir)/internal/class.h
+shape.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+shape.$(OBJEXT): $(top_srcdir)/internal/gc.h
+shape.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+shape.$(OBJEXT): $(top_srcdir)/internal/serial.h
+shape.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+shape.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+shape.$(OBJEXT): $(top_srcdir)/internal/variable.h
+shape.$(OBJEXT): $(top_srcdir)/internal/vm.h
+shape.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+shape.$(OBJEXT): {$(VPATH)}assert.h
+shape.$(OBJEXT): {$(VPATH)}atomic.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/limits.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+shape.$(OBJEXT): {$(VPATH)}config.h
+shape.$(OBJEXT): {$(VPATH)}constant.h
+shape.$(OBJEXT): {$(VPATH)}debug_counter.h
+shape.$(OBJEXT): {$(VPATH)}defines.h
+shape.$(OBJEXT): {$(VPATH)}encoding.h
+shape.$(OBJEXT): {$(VPATH)}gc.h
+shape.$(OBJEXT): {$(VPATH)}id.h
+shape.$(OBJEXT): {$(VPATH)}id_table.h
+shape.$(OBJEXT): {$(VPATH)}intern.h
+shape.$(OBJEXT): {$(VPATH)}internal.h
+shape.$(OBJEXT): {$(VPATH)}internal/abi.h
+shape.$(OBJEXT): {$(VPATH)}internal/anyargs.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/assume.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
+shape.$(OBJEXT): {$(VPATH)}internal/cast.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+shape.$(OBJEXT): {$(VPATH)}internal/config.h
+shape.$(OBJEXT): {$(VPATH)}internal/constant_p.h
+shape.$(OBJEXT): {$(VPATH)}internal/core.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/robject.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
+shape.$(OBJEXT): {$(VPATH)}internal/ctype.h
+shape.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+shape.$(OBJEXT): {$(VPATH)}internal/dosish.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
+shape.$(OBJEXT): {$(VPATH)}internal/error.h
+shape.$(OBJEXT): {$(VPATH)}internal/eval.h
+shape.$(OBJEXT): {$(VPATH)}internal/event.h
+shape.$(OBJEXT): {$(VPATH)}internal/fl_type.h
+shape.$(OBJEXT): {$(VPATH)}internal/gc.h
+shape.$(OBJEXT): {$(VPATH)}internal/glob.h
+shape.$(OBJEXT): {$(VPATH)}internal/globals.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/array.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/class.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/error.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/file.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/gc.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/io.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/load.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/object.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/process.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/random.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/range.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/re.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/select.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/string.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/time.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
+shape.$(OBJEXT): {$(VPATH)}internal/interpreter.h
+shape.$(OBJEXT): {$(VPATH)}internal/iterator.h
+shape.$(OBJEXT): {$(VPATH)}internal/memory.h
+shape.$(OBJEXT): {$(VPATH)}internal/method.h
+shape.$(OBJEXT): {$(VPATH)}internal/module.h
+shape.$(OBJEXT): {$(VPATH)}internal/newobj.h
+shape.$(OBJEXT): {$(VPATH)}internal/rgengc.h
+shape.$(OBJEXT): {$(VPATH)}internal/scan_args.h
+shape.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+shape.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+shape.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+shape.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+shape.$(OBJEXT): {$(VPATH)}internal/symbol.h
+shape.$(OBJEXT): {$(VPATH)}internal/value.h
+shape.$(OBJEXT): {$(VPATH)}internal/value_type.h
+shape.$(OBJEXT): {$(VPATH)}internal/variable.h
+shape.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+shape.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+shape.$(OBJEXT): {$(VPATH)}method.h
+shape.$(OBJEXT): {$(VPATH)}missing.h
+shape.$(OBJEXT): {$(VPATH)}node.h
+shape.$(OBJEXT): {$(VPATH)}onigmo.h
+shape.$(OBJEXT): {$(VPATH)}oniguruma.h
+shape.$(OBJEXT): {$(VPATH)}ruby_assert.h
+shape.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+shape.$(OBJEXT): {$(VPATH)}shape.c
+shape.$(OBJEXT): {$(VPATH)}shape.h
+shape.$(OBJEXT): {$(VPATH)}st.h
+shape.$(OBJEXT): {$(VPATH)}subst.h
+shape.$(OBJEXT): {$(VPATH)}symbol.h
+shape.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+shape.$(OBJEXT): {$(VPATH)}thread_native.h
+shape.$(OBJEXT): {$(VPATH)}variable.h
+shape.$(OBJEXT): {$(VPATH)}vm_core.h
+shape.$(OBJEXT): {$(VPATH)}vm_debug.h
+shape.$(OBJEXT): {$(VPATH)}vm_opts.h
+shape.$(OBJEXT): {$(VPATH)}vm_sync.h
signal.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
signal.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
signal.$(OBJEXT): $(CCAN_DIR)/list/list.h
signal.$(OBJEXT): $(CCAN_DIR)/str/str.h
-signal.$(OBJEXT): $(hdrdir)/ruby.h
signal.$(OBJEXT): $(hdrdir)/ruby/ruby.h
signal.$(OBJEXT): $(top_srcdir)/internal/array.h
+signal.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
signal.$(OBJEXT): $(top_srcdir)/internal/compilers.h
signal.$(OBJEXT): $(top_srcdir)/internal/eval.h
signal.$(OBJEXT): $(top_srcdir)/internal/gc.h
@@ -13827,6 +14346,7 @@ signal.$(OBJEXT): $(top_srcdir)/internal/signal.h
signal.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
signal.$(OBJEXT): $(top_srcdir)/internal/string.h
signal.$(OBJEXT): $(top_srcdir)/internal/thread.h
+signal.$(OBJEXT): $(top_srcdir)/internal/variable.h
signal.$(OBJEXT): $(top_srcdir)/internal/vm.h
signal.$(OBJEXT): $(top_srcdir)/internal/warnings.h
signal.$(OBJEXT): {$(VPATH)}assert.h
@@ -13841,6 +14361,7 @@ signal.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
signal.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
signal.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
signal.$(OBJEXT): {$(VPATH)}config.h
+signal.$(OBJEXT): {$(VPATH)}constant.h
signal.$(OBJEXT): {$(VPATH)}debug_counter.h
signal.$(OBJEXT): {$(VPATH)}defines.h
signal.$(OBJEXT): {$(VPATH)}encoding.h
@@ -13886,6 +14407,7 @@ signal.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+signal.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
@@ -14000,7 +14522,6 @@ signal.$(OBJEXT): {$(VPATH)}internal/warning_push.h
signal.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
signal.$(OBJEXT): {$(VPATH)}method.h
signal.$(OBJEXT): {$(VPATH)}missing.h
-signal.$(OBJEXT): {$(VPATH)}mjit.h
signal.$(OBJEXT): {$(VPATH)}node.h
signal.$(OBJEXT): {$(VPATH)}onigmo.h
signal.$(OBJEXT): {$(VPATH)}oniguruma.h
@@ -14008,6 +14529,7 @@ signal.$(OBJEXT): {$(VPATH)}ractor.h
signal.$(OBJEXT): {$(VPATH)}ractor_core.h
signal.$(OBJEXT): {$(VPATH)}ruby_assert.h
signal.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+signal.$(OBJEXT): {$(VPATH)}shape.h
signal.$(OBJEXT): {$(VPATH)}signal.c
signal.$(OBJEXT): {$(VPATH)}st.h
signal.$(OBJEXT): {$(VPATH)}subst.h
@@ -14016,7 +14538,6 @@ signal.$(OBJEXT): {$(VPATH)}thread_native.h
signal.$(OBJEXT): {$(VPATH)}vm_core.h
signal.$(OBJEXT): {$(VPATH)}vm_debug.h
signal.$(OBJEXT): {$(VPATH)}vm_opts.h
-signal.$(OBJEXT): {$(VPATH)}yjit.h
sprintf.$(OBJEXT): $(hdrdir)/ruby/ruby.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/bignum.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/bits.h
@@ -14033,6 +14554,7 @@ sprintf.$(OBJEXT): $(top_srcdir)/internal/serial.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/string.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+sprintf.$(OBJEXT): $(top_srcdir)/internal/variable.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/vm.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/warnings.h
sprintf.$(OBJEXT): {$(VPATH)}assert.h
@@ -14046,6 +14568,7 @@ sprintf.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
sprintf.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
sprintf.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
sprintf.$(OBJEXT): {$(VPATH)}config.h
+sprintf.$(OBJEXT): {$(VPATH)}constant.h
sprintf.$(OBJEXT): {$(VPATH)}defines.h
sprintf.$(OBJEXT): {$(VPATH)}encoding.h
sprintf.$(OBJEXT): {$(VPATH)}id.h
@@ -14207,6 +14730,7 @@ sprintf.$(OBJEXT): {$(VPATH)}onigmo.h
sprintf.$(OBJEXT): {$(VPATH)}oniguruma.h
sprintf.$(OBJEXT): {$(VPATH)}re.h
sprintf.$(OBJEXT): {$(VPATH)}regex.h
+sprintf.$(OBJEXT): {$(VPATH)}shape.h
sprintf.$(OBJEXT): {$(VPATH)}sprintf.c
sprintf.$(OBJEXT): {$(VPATH)}st.h
sprintf.$(OBJEXT): {$(VPATH)}subst.h
@@ -14374,11 +14898,13 @@ st.$(OBJEXT): {$(VPATH)}internal/variable.h
st.$(OBJEXT): {$(VPATH)}internal/warning_push.h
st.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
st.$(OBJEXT): {$(VPATH)}missing.h
+st.$(OBJEXT): {$(VPATH)}ruby_assert.h
st.$(OBJEXT): {$(VPATH)}st.c
st.$(OBJEXT): {$(VPATH)}st.h
st.$(OBJEXT): {$(VPATH)}subst.h
strftime.$(OBJEXT): $(hdrdir)/ruby/ruby.h
strftime.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+strftime.$(OBJEXT): $(top_srcdir)/internal/encoding.h
strftime.$(OBJEXT): $(top_srcdir)/internal/serial.h
strftime.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
strftime.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -14557,6 +15083,7 @@ strftime.$(OBJEXT): {$(VPATH)}timev.h
strftime.$(OBJEXT): {$(VPATH)}util.h
string.$(OBJEXT): $(hdrdir)/ruby/ruby.h
string.$(OBJEXT): $(top_srcdir)/internal/array.h
+string.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
string.$(OBJEXT): $(top_srcdir)/internal/bignum.h
string.$(OBJEXT): $(top_srcdir)/internal/bits.h
string.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -14575,6 +15102,7 @@ string.$(OBJEXT): $(top_srcdir)/internal/serial.h
string.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
string.$(OBJEXT): $(top_srcdir)/internal/string.h
string.$(OBJEXT): $(top_srcdir)/internal/transcode.h
+string.$(OBJEXT): $(top_srcdir)/internal/variable.h
string.$(OBJEXT): $(top_srcdir)/internal/vm.h
string.$(OBJEXT): $(top_srcdir)/internal/warnings.h
string.$(OBJEXT): {$(VPATH)}assert.h
@@ -14589,6 +15117,7 @@ string.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
string.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
string.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
string.$(OBJEXT): {$(VPATH)}config.h
+string.$(OBJEXT): {$(VPATH)}constant.h
string.$(OBJEXT): {$(VPATH)}debug_counter.h
string.$(OBJEXT): {$(VPATH)}defines.h
string.$(OBJEXT): {$(VPATH)}encindex.h
@@ -14635,6 +15164,7 @@ string.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
string.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
string.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
string.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+string.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
string.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
string.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
string.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
@@ -14756,6 +15286,7 @@ string.$(OBJEXT): {$(VPATH)}probes.h
string.$(OBJEXT): {$(VPATH)}re.h
string.$(OBJEXT): {$(VPATH)}regex.h
string.$(OBJEXT): {$(VPATH)}ruby_assert.h
+string.$(OBJEXT): {$(VPATH)}shape.h
string.$(OBJEXT): {$(VPATH)}st.h
string.$(OBJEXT): {$(VPATH)}string.c
string.$(OBJEXT): {$(VPATH)}subst.h
@@ -14799,6 +15330,7 @@ struct.$(OBJEXT): $(CCAN_DIR)/list/list.h
struct.$(OBJEXT): $(CCAN_DIR)/str/str.h
struct.$(OBJEXT): $(hdrdir)/ruby/ruby.h
struct.$(OBJEXT): $(top_srcdir)/internal/array.h
+struct.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
struct.$(OBJEXT): $(top_srcdir)/internal/class.h
struct.$(OBJEXT): $(top_srcdir)/internal/compilers.h
struct.$(OBJEXT): $(top_srcdir)/internal/error.h
@@ -14812,6 +15344,7 @@ struct.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
struct.$(OBJEXT): $(top_srcdir)/internal/string.h
struct.$(OBJEXT): $(top_srcdir)/internal/struct.h
struct.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+struct.$(OBJEXT): $(top_srcdir)/internal/variable.h
struct.$(OBJEXT): $(top_srcdir)/internal/vm.h
struct.$(OBJEXT): $(top_srcdir)/internal/warnings.h
struct.$(OBJEXT): {$(VPATH)}assert.h
@@ -14827,6 +15360,7 @@ struct.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
struct.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
struct.$(OBJEXT): {$(VPATH)}builtin.h
struct.$(OBJEXT): {$(VPATH)}config.h
+struct.$(OBJEXT): {$(VPATH)}constant.h
struct.$(OBJEXT): {$(VPATH)}defines.h
struct.$(OBJEXT): {$(VPATH)}encoding.h
struct.$(OBJEXT): {$(VPATH)}id.h
@@ -14989,6 +15523,7 @@ struct.$(OBJEXT): {$(VPATH)}onigmo.h
struct.$(OBJEXT): {$(VPATH)}oniguruma.h
struct.$(OBJEXT): {$(VPATH)}ruby_assert.h
struct.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+struct.$(OBJEXT): {$(VPATH)}shape.h
struct.$(OBJEXT): {$(VPATH)}st.h
struct.$(OBJEXT): {$(VPATH)}struct.c
struct.$(OBJEXT): {$(VPATH)}subst.h
@@ -15008,6 +15543,7 @@ symbol.$(OBJEXT): $(top_srcdir)/internal/serial.h
symbol.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
symbol.$(OBJEXT): $(top_srcdir)/internal/string.h
symbol.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+symbol.$(OBJEXT): $(top_srcdir)/internal/variable.h
symbol.$(OBJEXT): $(top_srcdir)/internal/vm.h
symbol.$(OBJEXT): $(top_srcdir)/internal/warnings.h
symbol.$(OBJEXT): {$(VPATH)}assert.h
@@ -15020,7 +15556,9 @@ symbol.$(OBJEXT): {$(VPATH)}backward/2/limits.h
symbol.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
symbol.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
symbol.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+symbol.$(OBJEXT): {$(VPATH)}builtin.h
symbol.$(OBJEXT): {$(VPATH)}config.h
+symbol.$(OBJEXT): {$(VPATH)}constant.h
symbol.$(OBJEXT): {$(VPATH)}debug_counter.h
symbol.$(OBJEXT): {$(VPATH)}defines.h
symbol.$(OBJEXT): {$(VPATH)}encoding.h
@@ -15068,6 +15606,7 @@ symbol.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+symbol.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
@@ -15186,10 +15725,13 @@ symbol.$(OBJEXT): {$(VPATH)}oniguruma.h
symbol.$(OBJEXT): {$(VPATH)}probes.dmyh
symbol.$(OBJEXT): {$(VPATH)}probes.h
symbol.$(OBJEXT): {$(VPATH)}ruby_assert.h
+symbol.$(OBJEXT): {$(VPATH)}shape.h
symbol.$(OBJEXT): {$(VPATH)}st.h
symbol.$(OBJEXT): {$(VPATH)}subst.h
symbol.$(OBJEXT): {$(VPATH)}symbol.c
symbol.$(OBJEXT): {$(VPATH)}symbol.h
+symbol.$(OBJEXT): {$(VPATH)}symbol.rb
+symbol.$(OBJEXT): {$(VPATH)}symbol.rbinc
symbol.$(OBJEXT): {$(VPATH)}vm_debug.h
symbol.$(OBJEXT): {$(VPATH)}vm_sync.h
thread.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
@@ -15199,6 +15741,7 @@ thread.$(OBJEXT): $(CCAN_DIR)/str/str.h
thread.$(OBJEXT): $(hdrdir)/ruby.h
thread.$(OBJEXT): $(hdrdir)/ruby/ruby.h
thread.$(OBJEXT): $(top_srcdir)/internal/array.h
+thread.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
thread.$(OBJEXT): $(top_srcdir)/internal/bits.h
thread.$(OBJEXT): $(top_srcdir)/internal/class.h
thread.$(OBJEXT): $(top_srcdir)/internal/compilers.h
@@ -15216,6 +15759,7 @@ thread.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
thread.$(OBJEXT): $(top_srcdir)/internal/string.h
thread.$(OBJEXT): $(top_srcdir)/internal/thread.h
thread.$(OBJEXT): $(top_srcdir)/internal/time.h
+thread.$(OBJEXT): $(top_srcdir)/internal/variable.h
thread.$(OBJEXT): $(top_srcdir)/internal/vm.h
thread.$(OBJEXT): $(top_srcdir)/internal/warnings.h
thread.$(OBJEXT): {$(VPATH)}assert.h
@@ -15229,7 +15773,9 @@ thread.$(OBJEXT): {$(VPATH)}backward/2/limits.h
thread.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
thread.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
thread.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+thread.$(OBJEXT): {$(VPATH)}builtin.h
thread.$(OBJEXT): {$(VPATH)}config.h
+thread.$(OBJEXT): {$(VPATH)}constant.h
thread.$(OBJEXT): {$(VPATH)}debug.h
thread.$(OBJEXT): {$(VPATH)}debug_counter.h
thread.$(OBJEXT): {$(VPATH)}defines.h
@@ -15403,6 +15949,7 @@ thread.$(OBJEXT): {$(VPATH)}ractor.h
thread.$(OBJEXT): {$(VPATH)}ractor_core.h
thread.$(OBJEXT): {$(VPATH)}ruby_assert.h
thread.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+thread.$(OBJEXT): {$(VPATH)}shape.h
thread.$(OBJEXT): {$(VPATH)}st.h
thread.$(OBJEXT): {$(VPATH)}subst.h
thread.$(OBJEXT): {$(VPATH)}thread.c
@@ -15411,20 +15958,22 @@ thread.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).c
thread.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
thread.$(OBJEXT): {$(VPATH)}thread_native.h
thread.$(OBJEXT): {$(VPATH)}thread_sync.c
+thread.$(OBJEXT): {$(VPATH)}thread_sync.rbinc
thread.$(OBJEXT): {$(VPATH)}timev.h
thread.$(OBJEXT): {$(VPATH)}vm_core.h
thread.$(OBJEXT): {$(VPATH)}vm_debug.h
thread.$(OBJEXT): {$(VPATH)}vm_opts.h
thread.$(OBJEXT): {$(VPATH)}vm_sync.h
-thread.$(OBJEXT): {$(VPATH)}yjit.h
time.$(OBJEXT): $(hdrdir)/ruby/ruby.h
time.$(OBJEXT): $(top_srcdir)/internal/array.h
+time.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
time.$(OBJEXT): $(top_srcdir)/internal/bignum.h
time.$(OBJEXT): $(top_srcdir)/internal/bits.h
time.$(OBJEXT): $(top_srcdir)/internal/compar.h
time.$(OBJEXT): $(top_srcdir)/internal/compilers.h
time.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
time.$(OBJEXT): $(top_srcdir)/internal/gc.h
+time.$(OBJEXT): $(top_srcdir)/internal/hash.h
time.$(OBJEXT): $(top_srcdir)/internal/numeric.h
time.$(OBJEXT): $(top_srcdir)/internal/rational.h
time.$(OBJEXT): $(top_srcdir)/internal/serial.h
@@ -15606,6 +16155,7 @@ time.$(OBJEXT): {$(VPATH)}missing.h
time.$(OBJEXT): {$(VPATH)}onigmo.h
time.$(OBJEXT): {$(VPATH)}oniguruma.h
time.$(OBJEXT): {$(VPATH)}ruby_assert.h
+time.$(OBJEXT): {$(VPATH)}shape.h
time.$(OBJEXT): {$(VPATH)}st.h
time.$(OBJEXT): {$(VPATH)}subst.h
time.$(OBJEXT): {$(VPATH)}time.c
@@ -15622,6 +16172,7 @@ transcode.$(OBJEXT): $(top_srcdir)/internal/serial.h
transcode.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
transcode.$(OBJEXT): $(top_srcdir)/internal/string.h
transcode.$(OBJEXT): $(top_srcdir)/internal/transcode.h
+transcode.$(OBJEXT): $(top_srcdir)/internal/variable.h
transcode.$(OBJEXT): $(top_srcdir)/internal/warnings.h
transcode.$(OBJEXT): {$(VPATH)}assert.h
transcode.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -15634,6 +16185,7 @@ transcode.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
transcode.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
transcode.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
transcode.$(OBJEXT): {$(VPATH)}config.h
+transcode.$(OBJEXT): {$(VPATH)}constant.h
transcode.$(OBJEXT): {$(VPATH)}defines.h
transcode.$(OBJEXT): {$(VPATH)}encoding.h
transcode.$(OBJEXT): {$(VPATH)}id.h
@@ -15792,6 +16344,7 @@ transcode.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
transcode.$(OBJEXT): {$(VPATH)}missing.h
transcode.$(OBJEXT): {$(VPATH)}onigmo.h
transcode.$(OBJEXT): {$(VPATH)}oniguruma.h
+transcode.$(OBJEXT): {$(VPATH)}shape.h
transcode.$(OBJEXT): {$(VPATH)}st.h
transcode.$(OBJEXT): {$(VPATH)}subst.h
transcode.$(OBJEXT): {$(VPATH)}transcode.c
@@ -15967,6 +16520,7 @@ transient_heap.$(OBJEXT): {$(VPATH)}internal/warning_push.h
transient_heap.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
transient_heap.$(OBJEXT): {$(VPATH)}missing.h
transient_heap.$(OBJEXT): {$(VPATH)}ruby_assert.h
+transient_heap.$(OBJEXT): {$(VPATH)}shape.h
transient_heap.$(OBJEXT): {$(VPATH)}st.h
transient_heap.$(OBJEXT): {$(VPATH)}subst.h
transient_heap.$(OBJEXT): {$(VPATH)}transient_heap.c
@@ -16146,6 +16700,7 @@ variable.$(OBJEXT): $(CCAN_DIR)/list/list.h
variable.$(OBJEXT): $(CCAN_DIR)/str/str.h
variable.$(OBJEXT): $(hdrdir)/ruby/ruby.h
variable.$(OBJEXT): $(top_srcdir)/internal/array.h
+variable.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
variable.$(OBJEXT): $(top_srcdir)/internal/class.h
variable.$(OBJEXT): $(top_srcdir)/internal/compilers.h
variable.$(OBJEXT): $(top_srcdir)/internal/error.h
@@ -16341,6 +16896,7 @@ variable.$(OBJEXT): {$(VPATH)}ractor.h
variable.$(OBJEXT): {$(VPATH)}ractor_core.h
variable.$(OBJEXT): {$(VPATH)}ruby_assert.h
variable.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+variable.$(OBJEXT): {$(VPATH)}shape.h
variable.$(OBJEXT): {$(VPATH)}st.h
variable.$(OBJEXT): {$(VPATH)}subst.h
variable.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -16361,14 +16917,16 @@ version.$(OBJEXT): $(hdrdir)/ruby.h
version.$(OBJEXT): $(hdrdir)/ruby/ruby.h
version.$(OBJEXT): $(hdrdir)/ruby/version.h
version.$(OBJEXT): $(top_srcdir)/internal/array.h
+version.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+version.$(OBJEXT): $(top_srcdir)/internal/cmdlineopt.h
version.$(OBJEXT): $(top_srcdir)/internal/compilers.h
version.$(OBJEXT): $(top_srcdir)/internal/gc.h
version.$(OBJEXT): $(top_srcdir)/internal/imemo.h
version.$(OBJEXT): $(top_srcdir)/internal/serial.h
version.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+version.$(OBJEXT): $(top_srcdir)/internal/variable.h
version.$(OBJEXT): $(top_srcdir)/internal/vm.h
version.$(OBJEXT): $(top_srcdir)/internal/warnings.h
-version.$(OBJEXT): $(top_srcdir)/revision.h
version.$(OBJEXT): $(top_srcdir)/version.h
version.$(OBJEXT): {$(VPATH)}assert.h
version.$(OBJEXT): {$(VPATH)}atomic.h
@@ -16382,9 +16940,11 @@ version.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
version.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
version.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
version.$(OBJEXT): {$(VPATH)}config.h
+version.$(OBJEXT): {$(VPATH)}constant.h
version.$(OBJEXT): {$(VPATH)}debug_counter.h
version.$(OBJEXT): {$(VPATH)}defines.h
version.$(OBJEXT): {$(VPATH)}id.h
+version.$(OBJEXT): {$(VPATH)}id_table.h
version.$(OBJEXT): {$(VPATH)}intern.h
version.$(OBJEXT): {$(VPATH)}internal.h
version.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -16531,8 +17091,10 @@ version.$(OBJEXT): {$(VPATH)}method.h
version.$(OBJEXT): {$(VPATH)}missing.h
version.$(OBJEXT): {$(VPATH)}mjit.h
version.$(OBJEXT): {$(VPATH)}node.h
+version.$(OBJEXT): {$(VPATH)}revision.h
version.$(OBJEXT): {$(VPATH)}ruby_assert.h
version.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+version.$(OBJEXT): {$(VPATH)}shape.h
version.$(OBJEXT): {$(VPATH)}st.h
version.$(OBJEXT): {$(VPATH)}subst.h
version.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -16548,6 +17110,7 @@ vm.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm.$(OBJEXT): $(hdrdir)/ruby.h
vm.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm.$(OBJEXT): $(top_srcdir)/internal/array.h
+vm.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm.$(OBJEXT): $(top_srcdir)/internal/bignum.h
vm.$(OBJEXT): $(top_srcdir)/internal/bits.h
vm.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -16768,6 +17331,7 @@ vm.$(OBJEXT): {$(VPATH)}ractor.h
vm.$(OBJEXT): {$(VPATH)}ractor_core.h
vm.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm.$(OBJEXT): {$(VPATH)}shape.h
vm.$(OBJEXT): {$(VPATH)}st.h
vm.$(OBJEXT): {$(VPATH)}subst.h
vm.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -16797,6 +17361,7 @@ vm_backtrace.$(OBJEXT): $(CCAN_DIR)/list/list.h
vm_backtrace.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm_backtrace.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/array.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/compilers.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/error.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/gc.h
@@ -16804,6 +17369,7 @@ vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/imemo.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/serial.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/string.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/variable.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/vm.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/warnings.h
vm_backtrace.$(OBJEXT): {$(VPATH)}assert.h
@@ -16818,11 +17384,13 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
vm_backtrace.$(OBJEXT): {$(VPATH)}config.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}constant.h
vm_backtrace.$(OBJEXT): {$(VPATH)}debug.h
vm_backtrace.$(OBJEXT): {$(VPATH)}defines.h
vm_backtrace.$(OBJEXT): {$(VPATH)}encoding.h
vm_backtrace.$(OBJEXT): {$(VPATH)}eval_intern.h
vm_backtrace.$(OBJEXT): {$(VPATH)}id.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}id_table.h
vm_backtrace.$(OBJEXT): {$(VPATH)}intern.h
vm_backtrace.$(OBJEXT): {$(VPATH)}internal.h
vm_backtrace.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -16982,6 +17550,7 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}onigmo.h
vm_backtrace.$(OBJEXT): {$(VPATH)}oniguruma.h
vm_backtrace.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_backtrace.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}shape.h
vm_backtrace.$(OBJEXT): {$(VPATH)}st.h
vm_backtrace.$(OBJEXT): {$(VPATH)}subst.h
vm_backtrace.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -16995,6 +17564,7 @@ vm_dump.$(OBJEXT): $(CCAN_DIR)/list/list.h
vm_dump.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm_dump.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/array.h
+vm_dump.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/compilers.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/gc.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/imemo.h
@@ -17172,6 +17742,7 @@ vm_dump.$(OBJEXT): {$(VPATH)}ractor.h
vm_dump.$(OBJEXT): {$(VPATH)}ractor_core.h
vm_dump.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_dump.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm_dump.$(OBJEXT): {$(VPATH)}shape.h
vm_dump.$(OBJEXT): {$(VPATH)}st.h
vm_dump.$(OBJEXT): {$(VPATH)}subst.h
vm_dump.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -17186,11 +17757,13 @@ vm_sync.$(OBJEXT): $(CCAN_DIR)/list/list.h
vm_sync.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm_sync.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/array.h
+vm_sync.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/compilers.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/gc.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/imemo.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/serial.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+vm_sync.$(OBJEXT): $(top_srcdir)/internal/variable.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/vm.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/warnings.h
vm_sync.$(OBJEXT): {$(VPATH)}assert.h
@@ -17205,6 +17778,7 @@ vm_sync.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
vm_sync.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
vm_sync.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
vm_sync.$(OBJEXT): {$(VPATH)}config.h
+vm_sync.$(OBJEXT): {$(VPATH)}constant.h
vm_sync.$(OBJEXT): {$(VPATH)}debug_counter.h
vm_sync.$(OBJEXT): {$(VPATH)}defines.h
vm_sync.$(OBJEXT): {$(VPATH)}gc.h
@@ -17359,6 +17933,7 @@ vm_sync.$(OBJEXT): {$(VPATH)}ractor.h
vm_sync.$(OBJEXT): {$(VPATH)}ractor_core.h
vm_sync.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_sync.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm_sync.$(OBJEXT): {$(VPATH)}shape.h
vm_sync.$(OBJEXT): {$(VPATH)}st.h
vm_sync.$(OBJEXT): {$(VPATH)}subst.h
vm_sync.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -17375,6 +17950,7 @@ vm_trace.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm_trace.$(OBJEXT): $(hdrdir)/ruby.h
vm_trace.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/array.h
+vm_trace.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/compilers.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/gc.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/hash.h
@@ -17382,6 +17958,7 @@ vm_trace.$(OBJEXT): $(top_srcdir)/internal/imemo.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/serial.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+vm_trace.$(OBJEXT): $(top_srcdir)/internal/variable.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/vm.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/warnings.h
vm_trace.$(OBJEXT): {$(VPATH)}assert.h
@@ -17397,12 +17974,14 @@ vm_trace.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
vm_trace.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
vm_trace.$(OBJEXT): {$(VPATH)}builtin.h
vm_trace.$(OBJEXT): {$(VPATH)}config.h
+vm_trace.$(OBJEXT): {$(VPATH)}constant.h
vm_trace.$(OBJEXT): {$(VPATH)}debug.h
vm_trace.$(OBJEXT): {$(VPATH)}debug_counter.h
vm_trace.$(OBJEXT): {$(VPATH)}defines.h
vm_trace.$(OBJEXT): {$(VPATH)}encoding.h
vm_trace.$(OBJEXT): {$(VPATH)}eval_intern.h
vm_trace.$(OBJEXT): {$(VPATH)}id.h
+vm_trace.$(OBJEXT): {$(VPATH)}id_table.h
vm_trace.$(OBJEXT): {$(VPATH)}intern.h
vm_trace.$(OBJEXT): {$(VPATH)}internal.h
vm_trace.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -17564,6 +18143,7 @@ vm_trace.$(OBJEXT): {$(VPATH)}oniguruma.h
vm_trace.$(OBJEXT): {$(VPATH)}ractor.h
vm_trace.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_trace.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm_trace.$(OBJEXT): {$(VPATH)}shape.h
vm_trace.$(OBJEXT): {$(VPATH)}st.h
vm_trace.$(OBJEXT): {$(VPATH)}subst.h
vm_trace.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -17579,19 +18159,19 @@ yjit.$(OBJEXT): $(CCAN_DIR)/list/list.h
yjit.$(OBJEXT): $(CCAN_DIR)/str/str.h
yjit.$(OBJEXT): $(hdrdir)/ruby/ruby.h
yjit.$(OBJEXT): $(top_srcdir)/internal/array.h
+yjit.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
yjit.$(OBJEXT): $(top_srcdir)/internal/class.h
yjit.$(OBJEXT): $(top_srcdir)/internal/compile.h
yjit.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+yjit.$(OBJEXT): $(top_srcdir)/internal/cont.h
+yjit.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
yjit.$(OBJEXT): $(top_srcdir)/internal/gc.h
yjit.$(OBJEXT): $(top_srcdir)/internal/hash.h
yjit.$(OBJEXT): $(top_srcdir)/internal/imemo.h
-yjit.$(OBJEXT): $(top_srcdir)/internal/object.h
-yjit.$(OBJEXT): $(top_srcdir)/internal/re.h
yjit.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
yjit.$(OBJEXT): $(top_srcdir)/internal/serial.h
yjit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
yjit.$(OBJEXT): $(top_srcdir)/internal/string.h
-yjit.$(OBJEXT): $(top_srcdir)/internal/struct.h
yjit.$(OBJEXT): $(top_srcdir)/internal/variable.h
yjit.$(OBJEXT): $(top_srcdir)/internal/vm.h
yjit.$(OBJEXT): $(top_srcdir)/internal/warnings.h
@@ -17609,6 +18189,7 @@ yjit.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
yjit.$(OBJEXT): {$(VPATH)}builtin.h
yjit.$(OBJEXT): {$(VPATH)}config.h
yjit.$(OBJEXT): {$(VPATH)}constant.h
+yjit.$(OBJEXT): {$(VPATH)}debug.h
yjit.$(OBJEXT): {$(VPATH)}debug_counter.h
yjit.$(OBJEXT): {$(VPATH)}defines.h
yjit.$(OBJEXT): {$(VPATH)}encoding.h
@@ -17780,6 +18361,7 @@ yjit.$(OBJEXT): {$(VPATH)}probes.h
yjit.$(OBJEXT): {$(VPATH)}probes_helper.h
yjit.$(OBJEXT): {$(VPATH)}ruby_assert.h
yjit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+yjit.$(OBJEXT): {$(VPATH)}shape.h
yjit.$(OBJEXT): {$(VPATH)}st.h
yjit.$(OBJEXT): {$(VPATH)}subst.h
yjit.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -17787,10 +18369,10 @@ yjit.$(OBJEXT): {$(VPATH)}thread_native.h
yjit.$(OBJEXT): {$(VPATH)}vm_callinfo.h
yjit.$(OBJEXT): {$(VPATH)}vm_core.h
yjit.$(OBJEXT): {$(VPATH)}vm_debug.h
+yjit.$(OBJEXT): {$(VPATH)}vm_insnhelper.h
yjit.$(OBJEXT): {$(VPATH)}vm_opts.h
yjit.$(OBJEXT): {$(VPATH)}vm_sync.h
yjit.$(OBJEXT): {$(VPATH)}yjit.c
yjit.$(OBJEXT): {$(VPATH)}yjit.h
-yjit.$(OBJEXT): {$(VPATH)}yjit.rb
yjit.$(OBJEXT): {$(VPATH)}yjit.rbinc
# AUTOGENERATED DEPENDENCIES END
diff --git a/compar.c b/compar.c
index bf8db53af7..040f77975e 100644
--- a/compar.c
+++ b/compar.c
@@ -30,13 +30,13 @@ rb_cmperr(VALUE x, VALUE y)
VALUE classname;
if (SPECIAL_CONST_P(y) || BUILTIN_TYPE(y) == T_FLOAT) {
- classname = rb_inspect(y);
+ classname = rb_inspect(y);
}
else {
- classname = rb_obj_class(y);
+ classname = rb_obj_class(y);
}
rb_raise(rb_eArgError, "comparison of %"PRIsVALUE" with %"PRIsVALUE" failed",
- rb_obj_class(x), classname);
+ rb_obj_class(x), classname);
}
static VALUE
@@ -50,12 +50,12 @@ VALUE
rb_invcmp(VALUE x, VALUE y)
{
VALUE invcmp = rb_exec_recursive(invcmp_recursive, x, y);
- if (invcmp == Qundef || NIL_P(invcmp)) {
- return Qnil;
+ if (NIL_OR_UNDEF_P(invcmp)) {
+ return Qnil;
}
else {
- int result = -rb_cmpint(invcmp, x, y);
- return INT2FIX(result);
+ int result = -rb_cmpint(invcmp, x, y);
+ return INT2FIX(result);
}
}
@@ -229,7 +229,7 @@ cmp_clamp(int argc, VALUE *argv, VALUE x)
}
}
if (!NIL_P(min) && !NIL_P(max) && cmpint(min, max) > 0) {
- rb_raise(rb_eArgError, "min argument must be smaller than max argument");
+ rb_raise(rb_eArgError, "min argument must be smaller than max argument");
}
if (!NIL_P(min)) {
diff --git a/compile.c b/compile.c
index 8c975782d7..0452305923 100644
--- a/compile.c
+++ b/compile.c
@@ -52,10 +52,10 @@
typedef struct iseq_link_element {
enum {
- ISEQ_ELEMENT_ANCHOR,
- ISEQ_ELEMENT_LABEL,
- ISEQ_ELEMENT_INSN,
- ISEQ_ELEMENT_ADJUST,
+ ISEQ_ELEMENT_ANCHOR,
+ ISEQ_ELEMENT_LABEL,
+ ISEQ_ELEMENT_INSN,
+ ISEQ_ELEMENT_ADJUST,
ISEQ_ELEMENT_TRACE,
} type;
struct iseq_link_element *next;
@@ -93,9 +93,9 @@ typedef struct iseq_insn_data {
int sc_state;
VALUE *operands;
struct {
- int line_no;
+ int line_no;
int node_id;
- rb_event_flag_t events;
+ rb_event_flag_t events;
} insn_info;
} INSN;
@@ -307,13 +307,13 @@ static void iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NOD
((label) ? (LABEL_REF(label), (label)->unremovable=1) : 0)
#define ADD_CATCH_ENTRY(type, ls, le, iseqv, lc) do { \
VALUE _e = rb_ary_new3(5, (type), \
- (VALUE)(ls) | 1, (VALUE)(le) | 1, \
- (VALUE)(iseqv), (VALUE)(lc) | 1); \
+ (VALUE)(ls) | 1, (VALUE)(le) | 1, \
+ (VALUE)(iseqv), (VALUE)(lc) | 1); \
LABEL_UNREMOVABLE(ls); \
LABEL_REF(le); \
LABEL_REF(lc); \
if (NIL_P(ISEQ_COMPILE_DATA(iseq)->catch_table_ary)) \
- RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, rb_ary_tmp_new(3)); \
+ RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, rb_ary_hidden_new(3)); \
rb_ary_push(ISEQ_COMPILE_DATA(iseq)->catch_table_ary, freeze_hide_obj(_e)); \
} while (0)
@@ -368,11 +368,11 @@ append_compile_error(const rb_iseq_t *iseq, int line, const char *fmt, ...)
err = rb_syntax_error_append(err, file, line, -1, NULL, fmt, args);
va_end(args);
if (NIL_P(err_info)) {
- RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, err);
- rb_set_errinfo(err);
+ RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, err);
+ rb_set_errinfo(err);
}
else if (!err_info) {
- RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, Qtrue);
+ RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, Qtrue);
}
if (compile_debug) {
if (SPECIAL_CONST_P(err)) err = rb_eSyntaxError;
@@ -402,17 +402,17 @@ do { \
const NODE *error_node = (node); \
enum node_type error_type = nd_type(error_node); \
if (error_type != (ndtype)) { \
- COMPILE_ERROR(ERROR_ARGS_AT(error_node) \
- prefix ": " #ndtype " is expected, but %s", \
- ruby_node_name(error_type)); \
- return errval; \
+ COMPILE_ERROR(ERROR_ARGS_AT(error_node) \
+ prefix ": " #ndtype " is expected, but %s", \
+ ruby_node_name(error_type)); \
+ return errval; \
} \
} while (0)
#define EXPECT_NODE_NONULL(prefix, parent, ndtype, errval) \
do { \
COMPILE_ERROR(ERROR_ARGS_AT(parent) \
- prefix ": must be " #ndtype ", but 0"); \
+ prefix ": must be " #ndtype ", but 0"); \
return errval; \
} while (0)
@@ -420,7 +420,7 @@ do { \
do { \
const NODE *error_node = (node); \
COMPILE_ERROR(ERROR_ARGS_AT(error_node) prefix ": unknown node (%s)", \
- ruby_node_name(nd_type(error_node))); \
+ ruby_node_name(nd_type(error_node))); \
return errval; \
} while (0)
@@ -510,19 +510,19 @@ verify_list(ISEQ_ARG_DECLARE const char *info, LINK_ANCHOR *const anchor)
list = anchor->anchor.next;
plist = &anchor->anchor;
while (list) {
- if (plist != list->prev) {
- flag += 1;
- }
- plist = list;
- list = list->next;
+ if (plist != list->prev) {
+ flag += 1;
+ }
+ plist = list;
+ list = list->next;
}
if (anchor->last != plist && anchor->last != 0) {
- flag |= 0x70000;
+ flag |= 0x70000;
}
if (flag != 0) {
- rb_bug("list verify error: %08x (%s)", flag, info);
+ rb_bug("list verify error: %08x (%s)", flag, info);
}
#endif
}
@@ -627,7 +627,7 @@ decl_branch_base(rb_iseq_t *iseq, const NODE *node, const char *type)
VALUE branches;
if (NIL_P(branch_base)) {
- branch_base = rb_ary_tmp_new(6);
+ branch_base = rb_ary_hidden_new(6);
rb_hash_aset(structure, key, branch_base);
rb_ary_push(branch_base, ID2SYM(rb_intern(type)));
rb_ary_push(branch_base, INT2FIX(first_lineno));
@@ -675,7 +675,7 @@ add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *n
long counter_idx;
if (NIL_P(branch)) {
- branch = rb_ary_tmp_new(6);
+ branch = rb_ary_hidden_new(6);
rb_hash_aset(branches, key, branch);
rb_ary_push(branch, ID2SYM(rb_intern(type)));
rb_ary_push(branch, INT2FIX(first_lineno));
@@ -705,11 +705,11 @@ validate_label(st_data_t name, st_data_t label, st_data_t arg)
rb_iseq_t *iseq = (rb_iseq_t *)arg;
LABEL *lobj = (LABEL *)label;
if (!lobj->link.next) {
- do {
- COMPILE_ERROR(iseq, lobj->position,
- "%"PRIsVALUE": undefined label",
- rb_sym2str((VALUE)name));
- } while (0);
+ do {
+ COMPILE_ERROR(iseq, lobj->position,
+ "%"PRIsVALUE": undefined label",
+ rb_sym2str((VALUE)name));
+ } while (0);
}
return ST_CONTINUE;
}
@@ -748,108 +748,108 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
if (node == 0) {
NO_CHECK(COMPILE(ret, "nil", node));
- iseq_set_local_table(iseq, 0);
+ iseq_set_local_table(iseq, 0);
}
/* assume node is T_NODE */
else if (nd_type_p(node, NODE_SCOPE)) {
- /* iseq type of top, method, class, block */
- iseq_set_local_table(iseq, node->nd_tbl);
- iseq_set_arguments(iseq, ret, node->nd_args);
+ /* iseq type of top, method, class, block */
+ iseq_set_local_table(iseq, node->nd_tbl);
+ iseq_set_arguments(iseq, ret, node->nd_args);
switch (ISEQ_BODY(iseq)->type) {
- case ISEQ_TYPE_BLOCK:
- {
- LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
- LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
-
- start->rescued = LABEL_RESCUE_BEG;
- end->rescued = LABEL_RESCUE_END;
-
- ADD_TRACE(ret, RUBY_EVENT_B_CALL);
- NODE dummy_line_node = generate_dummy_line_node(FIX2INT(ISEQ_BODY(iseq)->location.first_lineno), -1);
- ADD_INSN (ret, &dummy_line_node, nop);
- ADD_LABEL(ret, start);
- CHECK(COMPILE(ret, "block body", node->nd_body));
- ADD_LABEL(ret, end);
- ADD_TRACE(ret, RUBY_EVENT_B_RETURN);
+ case ISEQ_TYPE_BLOCK:
+ {
+ LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
+ LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
+
+ start->rescued = LABEL_RESCUE_BEG;
+ end->rescued = LABEL_RESCUE_END;
+
+ ADD_TRACE(ret, RUBY_EVENT_B_CALL);
+ NODE dummy_line_node = generate_dummy_line_node(ISEQ_BODY(iseq)->location.first_lineno, -1);
+ ADD_INSN (ret, &dummy_line_node, nop);
+ ADD_LABEL(ret, start);
+ CHECK(COMPILE(ret, "block body", node->nd_body));
+ ADD_LABEL(ret, end);
+ ADD_TRACE(ret, RUBY_EVENT_B_RETURN);
ISEQ_COMPILE_DATA(iseq)->last_line = ISEQ_BODY(iseq)->location.code_location.end_pos.lineno;
- /* wide range catch handler must put at last */
- ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
- ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
- break;
- }
- case ISEQ_TYPE_CLASS:
- {
- ADD_TRACE(ret, RUBY_EVENT_CLASS);
- CHECK(COMPILE(ret, "scoped node", node->nd_body));
- ADD_TRACE(ret, RUBY_EVENT_END);
- ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
- break;
- }
- case ISEQ_TYPE_METHOD:
- {
+ /* wide range catch handler must put at last */
+ ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
+ ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
+ break;
+ }
+ case ISEQ_TYPE_CLASS:
+ {
+ ADD_TRACE(ret, RUBY_EVENT_CLASS);
+ CHECK(COMPILE(ret, "scoped node", node->nd_body));
+ ADD_TRACE(ret, RUBY_EVENT_END);
+ ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
+ break;
+ }
+ case ISEQ_TYPE_METHOD:
+ {
ISEQ_COMPILE_DATA(iseq)->root_node = node->nd_body;
- ADD_TRACE(ret, RUBY_EVENT_CALL);
- CHECK(COMPILE(ret, "scoped node", node->nd_body));
+ ADD_TRACE(ret, RUBY_EVENT_CALL);
+ CHECK(COMPILE(ret, "scoped node", node->nd_body));
ISEQ_COMPILE_DATA(iseq)->root_node = node->nd_body;
- ADD_TRACE(ret, RUBY_EVENT_RETURN);
- ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
- break;
- }
- default: {
- CHECK(COMPILE(ret, "scoped node", node->nd_body));
- break;
- }
- }
+ ADD_TRACE(ret, RUBY_EVENT_RETURN);
+ ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
+ break;
+ }
+ default: {
+ CHECK(COMPILE(ret, "scoped node", node->nd_body));
+ break;
+ }
+ }
}
else {
- const char *m;
+ const char *m;
#define INVALID_ISEQ_TYPE(type) \
- ISEQ_TYPE_##type: m = #type; goto invalid_iseq_type
+ ISEQ_TYPE_##type: m = #type; goto invalid_iseq_type
switch (ISEQ_BODY(iseq)->type) {
- case INVALID_ISEQ_TYPE(METHOD);
- case INVALID_ISEQ_TYPE(CLASS);
- case INVALID_ISEQ_TYPE(BLOCK);
- case INVALID_ISEQ_TYPE(EVAL);
- case INVALID_ISEQ_TYPE(MAIN);
- case INVALID_ISEQ_TYPE(TOP);
+ case INVALID_ISEQ_TYPE(METHOD);
+ case INVALID_ISEQ_TYPE(CLASS);
+ case INVALID_ISEQ_TYPE(BLOCK);
+ case INVALID_ISEQ_TYPE(EVAL);
+ case INVALID_ISEQ_TYPE(MAIN);
+ case INVALID_ISEQ_TYPE(TOP);
#undef INVALID_ISEQ_TYPE /* invalid iseq types end */
- case ISEQ_TYPE_RESCUE:
- iseq_set_exception_local_table(iseq);
- CHECK(COMPILE(ret, "rescue", node));
- break;
- case ISEQ_TYPE_ENSURE:
- iseq_set_exception_local_table(iseq);
- CHECK(COMPILE_POPPED(ret, "ensure", node));
- break;
- case ISEQ_TYPE_PLAIN:
- CHECK(COMPILE(ret, "ensure", node));
- break;
- default:
+ case ISEQ_TYPE_RESCUE:
+ iseq_set_exception_local_table(iseq);
+ CHECK(COMPILE(ret, "rescue", node));
+ break;
+ case ISEQ_TYPE_ENSURE:
+ iseq_set_exception_local_table(iseq);
+ CHECK(COMPILE_POPPED(ret, "ensure", node));
+ break;
+ case ISEQ_TYPE_PLAIN:
+ CHECK(COMPILE(ret, "ensure", node));
+ break;
+ default:
COMPILE_ERROR(ERROR_ARGS "unknown scope: %d", ISEQ_BODY(iseq)->type);
- return COMPILE_NG;
- invalid_iseq_type:
- COMPILE_ERROR(ERROR_ARGS "compile/ISEQ_TYPE_%s should not be reached", m);
- return COMPILE_NG;
- }
+ return COMPILE_NG;
+ invalid_iseq_type:
+ COMPILE_ERROR(ERROR_ARGS "compile/ISEQ_TYPE_%s should not be reached", m);
+ return COMPILE_NG;
+ }
}
if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE || ISEQ_BODY(iseq)->type == ISEQ_TYPE_ENSURE) {
NODE dummy_line_node = generate_dummy_line_node(0, -1);
- ADD_GETLOCAL(ret, &dummy_line_node, LVAR_ERRINFO, 0);
- ADD_INSN1(ret, &dummy_line_node, throw, INT2FIX(0) /* continue throw */ );
+ ADD_GETLOCAL(ret, &dummy_line_node, LVAR_ERRINFO, 0);
+ ADD_INSN1(ret, &dummy_line_node, throw, INT2FIX(0) /* continue throw */ );
}
else {
NODE dummy_line_node = generate_dummy_line_node(ISEQ_COMPILE_DATA(iseq)->last_line, -1);
- ADD_INSN(ret, &dummy_line_node, leave);
+ ADD_INSN(ret, &dummy_line_node, leave);
}
#if OPT_SUPPORT_JOKE
if (ISEQ_COMPILE_DATA(iseq)->labels_table) {
- st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
- ISEQ_COMPILE_DATA(iseq)->labels_table = 0;
- validate_labels(iseq, labels_table);
+ st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
+ ISEQ_COMPILE_DATA(iseq)->labels_table = 0;
+ validate_labels(iseq, labels_table);
}
#endif
CHECK(iseq_setup_insn(iseq, ret));
@@ -866,9 +866,9 @@ rb_iseq_translate_threaded_code(rb_iseq_t *iseq)
for (i = 0; i < ISEQ_BODY(iseq)->iseq_size; /* */ ) {
int insn = (int)ISEQ_BODY(iseq)->iseq_encoded[i];
- int len = insn_len(insn);
- encoded[i] = (VALUE)table[insn];
- i += len;
+ int len = insn_len(insn);
+ encoded[i] = (VALUE)table[insn];
+ i += len;
}
FL_SET((VALUE)iseq, ISEQ_TRANSLATED);
#endif
@@ -886,15 +886,15 @@ rb_iseq_original_iseq(const rb_iseq_t *iseq) /* cold path */
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
{
- unsigned int i;
+ unsigned int i;
for (i = 0; i < ISEQ_BODY(iseq)->iseq_size; /* */ ) {
- const void *addr = (const void *)original_code[i];
- const int insn = rb_vm_insn_addr2insn(addr);
+ const void *addr = (const void *)original_code[i];
+ const int insn = rb_vm_insn_addr2insn(addr);
- original_code[i] = insn;
- i += insn_len(insn);
- }
+ original_code[i] = insn;
+ i += insn_len(insn);
+ }
}
#endif
return original_code;
@@ -976,18 +976,18 @@ compile_data_alloc_with_arena(struct iseq_compile_data_storage **arena, size_t s
if (size >= INT_MAX - padding) rb_memerror();
if (storage->pos + size + padding > storage->size) {
- unsigned int alloc_size = storage->size;
-
- while (alloc_size < size + PADDING_SIZE_MAX) {
- if (alloc_size >= INT_MAX / 2) rb_memerror();
- alloc_size *= 2;
- }
- storage->next = (void *)ALLOC_N(char, alloc_size +
- offsetof(struct iseq_compile_data_storage, buff));
- storage = *arena = storage->next;
- storage->next = 0;
- storage->pos = 0;
- storage->size = alloc_size;
+ unsigned int alloc_size = storage->size;
+
+ while (alloc_size < size + PADDING_SIZE_MAX) {
+ if (alloc_size >= INT_MAX / 2) rb_memerror();
+ alloc_size *= 2;
+ }
+ storage->next = (void *)ALLOC_N(char, alloc_size +
+ offsetof(struct iseq_compile_data_storage, buff));
+ storage = *arena = storage->next;
+ storage->next = 0;
+ storage->pos = 0;
+ storage->size = alloc_size;
#ifdef STRICT_ALIGNMENT
padding = calc_padding((void *)&storage->buff[storage->pos], size);
#endif /* STRICT_ALIGNMENT */
@@ -1060,7 +1060,7 @@ ELEM_INSERT_NEXT(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
elem2->prev = elem1;
elem1->next = elem2;
if (elem2->next) {
- elem2->next->prev = elem2;
+ elem2->next->prev = elem2;
}
}
@@ -1074,7 +1074,7 @@ ELEM_INSERT_PREV(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
elem2->next = elem1;
elem1->prev = elem2;
if (elem2->prev) {
- elem2->prev->next = elem2;
+ elem2->prev->next = elem2;
}
}
@@ -1087,10 +1087,10 @@ ELEM_REPLACE(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
elem2->prev = elem1->prev;
elem2->next = elem1->next;
if (elem1->prev) {
- elem1->prev->next = elem2;
+ elem1->prev->next = elem2;
}
if (elem1->next) {
- elem1->next->prev = elem2;
+ elem1->next->prev = elem2;
}
}
@@ -1099,7 +1099,7 @@ ELEM_REMOVE(LINK_ELEMENT *elem)
{
elem->prev->next = elem->next;
if (elem->next) {
- elem->next->prev = elem->prev;
+ elem->next->prev = elem->prev;
}
}
@@ -1119,13 +1119,13 @@ static LINK_ELEMENT *
ELEM_FIRST_INSN(LINK_ELEMENT *elem)
{
while (elem) {
- switch (elem->type) {
- case ISEQ_ELEMENT_INSN:
- case ISEQ_ELEMENT_ADJUST:
- return elem;
- default:
- elem = elem->next;
- }
+ switch (elem->type) {
+ case ISEQ_ELEMENT_INSN:
+ case ISEQ_ELEMENT_ADJUST:
+ return elem;
+ default:
+ elem = elem->next;
+ }
}
return NULL;
}
@@ -1135,11 +1135,11 @@ LIST_INSN_SIZE_ONE(const LINK_ANCHOR *const anchor)
{
LINK_ELEMENT *first_insn = ELEM_FIRST_INSN(FIRST_ELEMENT(anchor));
if (first_insn != NULL &&
- ELEM_FIRST_INSN(first_insn->next) == NULL) {
- return TRUE;
+ ELEM_FIRST_INSN(first_insn->next) == NULL) {
+ return TRUE;
}
else {
- return FALSE;
+ return FALSE;
}
}
@@ -1147,10 +1147,10 @@ static int
LIST_INSN_SIZE_ZERO(const LINK_ANCHOR *const anchor)
{
if (ELEM_FIRST_INSN(FIRST_ELEMENT(anchor)) == NULL) {
- return TRUE;
+ return TRUE;
}
else {
- return FALSE;
+ return FALSE;
}
}
@@ -1165,9 +1165,9 @@ static void
APPEND_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *const anc1, LINK_ANCHOR *const anc2)
{
if (anc2->anchor.next) {
- anc1->last->next = anc2->anchor.next;
- anc2->anchor.next->prev = anc1->last;
- anc1->last = anc2->last;
+ anc1->last->next = anc2->anchor.next;
+ anc2->anchor.next->prev = anc1->last;
+ anc1->last = anc2->last;
}
verify_list("append", anc1);
}
@@ -1182,11 +1182,11 @@ debug_list(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor, LINK_ELEMENT *cur)
LINK_ELEMENT *list = FIRST_ELEMENT(anchor);
printf("----\n");
printf("anch: %p, frst: %p, last: %p\n", (void *)&anchor->anchor,
- (void *)anchor->anchor.next, (void *)anchor->last);
+ (void *)anchor->anchor.next, (void *)anchor->last);
while (list) {
- printf("curr: %p, next: %p, prev: %p, type: %d\n", (void *)list, (void *)list->next,
- (void *)list->prev, (int)list->type);
- list = list->next;
+ printf("curr: %p, next: %p, prev: %p, type: %d\n", (void *)list, (void *)list->next,
+ (void *)list->prev, (int)list->type);
+ list = list->next;
}
printf("----\n");
@@ -1243,9 +1243,35 @@ new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line)
return adjust;
}
+static void
+iseq_insn_each_markable_object(INSN *insn, void (*func)(VALUE *, VALUE), VALUE data)
+{
+ const char *types = insn_op_types(insn->insn_id);
+ for (int j = 0; types[j]; j++) {
+ char type = types[j];
+ switch (type) {
+ case TS_CDHASH:
+ case TS_ISEQ:
+ case TS_VALUE:
+ case TS_IC: // constant path array
+ case TS_CALLDATA: // ci is stored.
+ func(&OPERAND_AT(insn, j), data);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+iseq_insn_each_object_write_barrier(VALUE *obj_ptr, VALUE iseq)
+{
+ RB_OBJ_WRITTEN(iseq, Qundef, *obj_ptr);
+}
+
static INSN *
new_insn_core(rb_iseq_t *iseq, const NODE *line_node,
- int insn_id, int argc, VALUE *argv)
+ int insn_id, int argc, VALUE *argv)
{
INSN *iobj = compile_data_alloc_insn(iseq);
@@ -1260,6 +1286,9 @@ new_insn_core(rb_iseq_t *iseq, const NODE *line_node,
iobj->operands = argv;
iobj->operand_size = argc;
iobj->sc_state = 0;
+
+ iseq_insn_each_markable_object(iobj, iseq_insn_each_object_write_barrier, (VALUE)iseq);
+
return iobj;
}
@@ -1269,14 +1298,14 @@ new_insn_body(rb_iseq_t *iseq, const NODE *const line_node, enum ruby_vminsn_typ
VALUE *operands = 0;
va_list argv;
if (argc > 0) {
- int i;
+ int i;
va_start(argv, argc);
operands = compile_data_alloc2(iseq, sizeof(VALUE), argc);
- for (i = 0; i < argc; i++) {
- VALUE v = va_arg(argv, VALUE);
- operands[i] = v;
- }
- va_end(argv);
+ for (i = 0; i < argc; i++) {
+ VALUE v = va_arg(argv, VALUE);
+ operands[i] = v;
+ }
+ va_end(argv);
}
return new_insn_core(iseq, line_node, insn_id, argc, operands);
}
@@ -1320,7 +1349,7 @@ new_insn_send(rb_iseq_t *iseq, const NODE *const line_node, ID id, VALUE argc, c
static rb_iseq_t *
new_child_iseq(rb_iseq_t *iseq, const NODE *const node,
- VALUE name, const rb_iseq_t *parent, enum iseq_type type, int line_no)
+ VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no)
{
rb_iseq_t *ret_iseq;
rb_ast_body_t ast;
@@ -1332,8 +1361,8 @@ new_child_iseq(rb_iseq_t *iseq, const NODE *const node,
debugs("[new_child_iseq]> ---------------------------------------\n");
int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
ret_iseq = rb_iseq_new_with_opt(&ast, name,
- rb_iseq_path(iseq), rb_iseq_realpath(iseq),
- INT2FIX(line_no), parent,
+ rb_iseq_path(iseq), rb_iseq_realpath(iseq),
+ line_no, parent,
isolated_depth ? isolated_depth + 1 : 0,
type, ISEQ_COMPILE_DATA(iseq)->option);
debugs("[new_child_iseq]< ---------------------------------------\n");
@@ -1342,14 +1371,14 @@ new_child_iseq(rb_iseq_t *iseq, const NODE *const node,
static rb_iseq_t *
new_child_iseq_with_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback_callback_func *ifunc,
- VALUE name, const rb_iseq_t *parent, enum iseq_type type, int line_no)
+ VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no)
{
rb_iseq_t *ret_iseq;
debugs("[new_child_iseq_with_callback]> ---------------------------------------\n");
ret_iseq = rb_iseq_new_with_callback(ifunc, name,
- rb_iseq_path(iseq), rb_iseq_realpath(iseq),
- INT2FIX(line_no), parent, type, ISEQ_COMPILE_DATA(iseq)->option);
+ rb_iseq_path(iseq), rb_iseq_realpath(iseq),
+ line_no, parent, type, ISEQ_COMPILE_DATA(iseq)->option);
debugs("[new_child_iseq_with_callback]< ---------------------------------------\n");
return ret_iseq;
}
@@ -1357,18 +1386,18 @@ new_child_iseq_with_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_call
static void
set_catch_except_p(struct rb_iseq_constant_body *body)
{
- body->catch_except_p = TRUE;
+ body->catch_except_p = true;
if (body->parent_iseq != NULL) {
set_catch_except_p(ISEQ_BODY(body->parent_iseq));
}
}
-/* Set body->catch_except_p to TRUE if the ISeq may catch an exception. If it is FALSE,
- JIT-ed code may be optimized. If we are extremely conservative, we should set TRUE
+/* Set body->catch_except_p to true if the ISeq may catch an exception. If it is false,
+ JIT-ed code may be optimized. If we are extremely conservative, we should set true
if catch table exists. But we want to optimize while loop, which always has catch
table entries for break/next/redo.
- So this function sets TRUE for limited ISeqs with break/next/redo catch table entries
+ So this function sets true for limited ISeqs with break/next/redo catch table entries
whose child ISeq would really raise an exception. */
static void
update_catch_except_flags(struct rb_iseq_constant_body *body)
@@ -1399,7 +1428,7 @@ update_catch_except_flags(struct rb_iseq_constant_body *body)
if (entry->type != CATCH_TYPE_BREAK
&& entry->type != CATCH_TYPE_NEXT
&& entry->type != CATCH_TYPE_REDO) {
- body->catch_except_p = TRUE;
+ body->catch_except_p = true;
break;
}
}
@@ -1418,7 +1447,7 @@ iseq_insert_nop_between_end_and_cont(rb_iseq_t *iseq)
LINK_ELEMENT *cont = (LINK_ELEMENT *)(ptr[4] & ~1);
LINK_ELEMENT *e;
- enum catch_type ct = (enum catch_type)(ptr[0] & 0xffff);
+ enum rb_catch_type ct = (enum rb_catch_type)(ptr[0] & 0xffff);
if (ct != CATCH_TYPE_BREAK
&& ct != CATCH_TYPE_NEXT
@@ -1440,31 +1469,31 @@ static int
iseq_setup_insn(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
{
if (RTEST(ISEQ_COMPILE_DATA(iseq)->err_info))
- return COMPILE_NG;
+ return COMPILE_NG;
/* debugs("[compile step 2] (iseq_array_to_linkedlist)\n"); */
if (compile_debug > 5)
- dump_disasm_list(FIRST_ELEMENT(anchor));
+ dump_disasm_list(FIRST_ELEMENT(anchor));
debugs("[compile step 3.1 (iseq_optimize)]\n");
iseq_optimize(iseq, anchor);
if (compile_debug > 5)
- dump_disasm_list(FIRST_ELEMENT(anchor));
+ dump_disasm_list(FIRST_ELEMENT(anchor));
if (ISEQ_COMPILE_DATA(iseq)->option->instructions_unification) {
- debugs("[compile step 3.2 (iseq_insns_unification)]\n");
- iseq_insns_unification(iseq, anchor);
- if (compile_debug > 5)
- dump_disasm_list(FIRST_ELEMENT(anchor));
+ debugs("[compile step 3.2 (iseq_insns_unification)]\n");
+ iseq_insns_unification(iseq, anchor);
+ if (compile_debug > 5)
+ dump_disasm_list(FIRST_ELEMENT(anchor));
}
if (ISEQ_COMPILE_DATA(iseq)->option->stack_caching) {
- debugs("[compile step 3.3 (iseq_set_sequence_stackcaching)]\n");
- iseq_set_sequence_stackcaching(iseq, anchor);
- if (compile_debug > 5)
- dump_disasm_list(FIRST_ELEMENT(anchor));
+ debugs("[compile step 3.3 (iseq_set_sequence_stackcaching)]\n");
+ iseq_set_sequence_stackcaching(iseq, anchor);
+ if (compile_debug > 5)
+ dump_disasm_list(FIRST_ELEMENT(anchor));
}
debugs("[compile step 3.4 (iseq_insert_nop_between_end_and_cont)]\n");
@@ -1484,7 +1513,7 @@ iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
debugs("[compile step 4.1 (iseq_set_sequence)]\n");
if (!iseq_set_sequence(iseq, anchor)) return COMPILE_NG;
if (compile_debug > 5)
- dump_disasm_list(FIRST_ELEMENT(anchor));
+ dump_disasm_list(FIRST_ELEMENT(anchor));
debugs("[compile step 4.2 (iseq_set_exception_table)]\n");
if (!iseq_set_exception_table(iseq)) return COMPILE_NG;
@@ -1512,8 +1541,8 @@ iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
#endif
if (compile_debug > 1) {
- VALUE str = rb_iseq_disasm(iseq);
- printf("%s\n", StringValueCStr(str));
+ VALUE str = rb_iseq_disasm(iseq);
+ printf("%s\n", StringValueCStr(str));
}
verify_call_cache(iseq);
debugs("[compile step: finish]\n");
@@ -1534,7 +1563,7 @@ get_lvar_level(const rb_iseq_t *iseq)
{
int lev = 0;
while (iseq != ISEQ_BODY(iseq)->local_iseq) {
- lev++;
+ lev++;
iseq = ISEQ_BODY(iseq)->parent_iseq;
}
return lev;
@@ -1547,8 +1576,8 @@ get_dyna_var_idx_at_raw(const rb_iseq_t *iseq, ID id)
for (i = 0; i < ISEQ_BODY(iseq)->local_table_size; i++) {
if (ISEQ_BODY(iseq)->local_table[i] == id) {
- return (int)i;
- }
+ return (int)i;
+ }
}
return -1;
}
@@ -1573,12 +1602,12 @@ get_dyna_var_idx(const rb_iseq_t *iseq, ID id, int *level, int *ls)
const rb_iseq_t *const topmost_iseq = iseq;
while (iseq) {
- idx = get_dyna_var_idx_at_raw(iseq, id);
- if (idx >= 0) {
- break;
- }
+ idx = get_dyna_var_idx_at_raw(iseq, id);
+ if (idx >= 0) {
+ break;
+ }
iseq = ISEQ_BODY(iseq)->parent_iseq;
- lv++;
+ lv++;
}
if (idx < 0) {
@@ -1597,16 +1626,16 @@ iseq_local_block_param_p(const rb_iseq_t *iseq, unsigned int idx, unsigned int l
const struct rb_iseq_constant_body *body;
while (level > 0) {
iseq = ISEQ_BODY(iseq)->parent_iseq;
- level--;
+ level--;
}
body = ISEQ_BODY(iseq);
if (body->local_iseq == iseq && /* local variables */
- body->param.flags.has_block &&
- body->local_table_size - body->param.block_start == idx) {
- return TRUE;
+ body->param.flags.has_block &&
+ body->local_table_size - body->param.block_start == idx) {
+ return TRUE;
}
else {
- return FALSE;
+ return FALSE;
}
}
@@ -1616,12 +1645,12 @@ iseq_block_param_id_p(const rb_iseq_t *iseq, ID id, int *pidx, int *plevel)
int level, ls;
int idx = get_dyna_var_idx(iseq, id, &level, &ls);
if (iseq_local_block_param_p(iseq, ls - idx, level)) {
- *pidx = ls - idx;
- *plevel = level;
- return TRUE;
+ *pidx = ls - idx;
+ *plevel = level;
+ return TRUE;
}
else {
- return FALSE;
+ return FALSE;
}
}
@@ -1676,10 +1705,10 @@ static void
iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level)
{
if (iseq_local_block_param_p(iseq, idx, level)) {
- ADD_INSN2(seq, line_node, getblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
+ ADD_INSN2(seq, line_node, getblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
}
else {
- ADD_INSN2(seq, line_node, getlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
+ ADD_INSN2(seq, line_node, getlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
}
if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse);
}
@@ -1688,10 +1717,10 @@ static void
iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level)
{
if (iseq_local_block_param_p(iseq, idx, level)) {
- ADD_INSN2(seq, line_node, setblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
+ ADD_INSN2(seq, line_node, setblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
}
else {
- ADD_INSN2(seq, line_node, setlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
+ ADD_INSN2(seq, line_node, setlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
}
if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue);
}
@@ -1703,47 +1732,47 @@ iseq_calc_param_size(rb_iseq_t *iseq)
{
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
if (body->param.flags.has_opt ||
- body->param.flags.has_post ||
- body->param.flags.has_rest ||
- body->param.flags.has_block ||
- body->param.flags.has_kw ||
- body->param.flags.has_kwrest) {
-
- if (body->param.flags.has_block) {
- body->param.size = body->param.block_start + 1;
- }
- else if (body->param.flags.has_kwrest) {
- body->param.size = body->param.keyword->rest_start + 1;
- }
- else if (body->param.flags.has_kw) {
- body->param.size = body->param.keyword->bits_start + 1;
- }
- else if (body->param.flags.has_post) {
- body->param.size = body->param.post_start + body->param.post_num;
- }
- else if (body->param.flags.has_rest) {
- body->param.size = body->param.rest_start + 1;
- }
- else if (body->param.flags.has_opt) {
- body->param.size = body->param.lead_num + body->param.opt_num;
- }
- else {
+ body->param.flags.has_post ||
+ body->param.flags.has_rest ||
+ body->param.flags.has_block ||
+ body->param.flags.has_kw ||
+ body->param.flags.has_kwrest) {
+
+ if (body->param.flags.has_block) {
+ body->param.size = body->param.block_start + 1;
+ }
+ else if (body->param.flags.has_kwrest) {
+ body->param.size = body->param.keyword->rest_start + 1;
+ }
+ else if (body->param.flags.has_kw) {
+ body->param.size = body->param.keyword->bits_start + 1;
+ }
+ else if (body->param.flags.has_post) {
+ body->param.size = body->param.post_start + body->param.post_num;
+ }
+ else if (body->param.flags.has_rest) {
+ body->param.size = body->param.rest_start + 1;
+ }
+ else if (body->param.flags.has_opt) {
+ body->param.size = body->param.lead_num + body->param.opt_num;
+ }
+ else {
UNREACHABLE;
- }
+ }
}
else {
- body->param.size = body->param.lead_num;
+ body->param.size = body->param.lead_num;
}
}
static int
iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
- const struct rb_args_info *args, int arg_size)
+ const struct rb_args_info *args, int arg_size)
{
const NODE *node = args->kw_args;
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
struct rb_iseq_param_keyword *keyword;
- const VALUE default_values = rb_ary_tmp_new(1);
+ const VALUE default_values = rb_ary_hidden_new(1);
const VALUE complex_mark = rb_str_tmp_new(0);
int kw = 0, rkw = 0, di = 0, i;
@@ -1751,68 +1780,68 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
while (node) {
- kw++;
- node = node->nd_next;
+ kw++;
+ node = node->nd_next;
}
arg_size += kw;
keyword->bits_start = arg_size++;
node = args->kw_args;
while (node) {
- const NODE *val_node = node->nd_body->nd_value;
- VALUE dv;
+ const NODE *val_node = node->nd_body->nd_value;
+ VALUE dv;
if (val_node == NODE_SPECIAL_REQUIRED_KEYWORD) {
- ++rkw;
- }
- else {
- switch (nd_type(val_node)) {
- case NODE_LIT:
- dv = val_node->nd_lit;
- break;
- case NODE_NIL:
- dv = Qnil;
- break;
- case NODE_TRUE:
- dv = Qtrue;
- break;
- case NODE_FALSE:
- dv = Qfalse;
- break;
- default:
+ ++rkw;
+ }
+ else {
+ switch (nd_type(val_node)) {
+ case NODE_LIT:
+ dv = val_node->nd_lit;
+ break;
+ case NODE_NIL:
+ dv = Qnil;
+ break;
+ case NODE_TRUE:
+ dv = Qtrue;
+ break;
+ case NODE_FALSE:
+ dv = Qfalse;
+ break;
+ default:
NO_CHECK(COMPILE_POPPED(optargs, "kwarg", node)); /* nd_type_p(node, NODE_KW_ARG) */
- dv = complex_mark;
- }
+ dv = complex_mark;
+ }
- keyword->num = ++di;
- rb_ary_push(default_values, dv);
- }
+ keyword->num = ++di;
+ rb_ary_push(default_values, dv);
+ }
- node = node->nd_next;
+ node = node->nd_next;
}
keyword->num = kw;
if (args->kw_rest_arg->nd_vid != 0) {
- keyword->rest_start = arg_size++;
- body->param.flags.has_kwrest = TRUE;
+ keyword->rest_start = arg_size++;
+ body->param.flags.has_kwrest = TRUE;
}
keyword->required_num = rkw;
keyword->table = &body->local_table[keyword->bits_start - keyword->num];
{
- VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
+ VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
- for (i = 0; i < RARRAY_LEN(default_values); i++) {
- VALUE dv = RARRAY_AREF(default_values, i);
- if (dv == complex_mark) dv = Qundef;
- if (!SPECIAL_CONST_P(dv)) {
- RB_OBJ_WRITTEN(iseq, Qundef, dv);
- }
- dvs[i] = dv;
- }
+ for (i = 0; i < RARRAY_LEN(default_values); i++) {
+ VALUE dv = RARRAY_AREF(default_values, i);
+ if (dv == complex_mark) dv = Qundef;
+ if (!SPECIAL_CONST_P(dv)) {
+ RB_OBJ_WRITTEN(iseq, Qundef, dv);
+ }
+ dvs[i] = dv;
+ }
- keyword->default_values = dvs;
+ keyword->default_values = dvs;
}
return arg_size;
}
@@ -1824,119 +1853,119 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons
if (node_args) {
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
- struct rb_args_info *args = node_args->nd_ainfo;
- ID rest_id = 0;
- int last_comma = 0;
- ID block_id = 0;
- int arg_size;
+ struct rb_args_info *args = node_args->nd_ainfo;
+ ID rest_id = 0;
+ int last_comma = 0;
+ ID block_id = 0;
+ int arg_size;
- EXPECT_NODE("iseq_set_arguments", node_args, NODE_ARGS, COMPILE_NG);
+ EXPECT_NODE("iseq_set_arguments", node_args, NODE_ARGS, COMPILE_NG);
body->param.flags.ruby2_keywords = args->ruby2_keywords;
- body->param.lead_num = arg_size = (int)args->pre_args_num;
- if (body->param.lead_num > 0) body->param.flags.has_lead = TRUE;
- debugs(" - argc: %d\n", body->param.lead_num);
+ body->param.lead_num = arg_size = (int)args->pre_args_num;
+ if (body->param.lead_num > 0) body->param.flags.has_lead = TRUE;
+ debugs(" - argc: %d\n", body->param.lead_num);
- rest_id = args->rest_arg;
+ rest_id = args->rest_arg;
if (rest_id == NODE_SPECIAL_EXCESSIVE_COMMA) {
- last_comma = 1;
- rest_id = 0;
- }
- block_id = args->block_arg;
-
- if (args->opt_args) {
- const NODE *node = args->opt_args;
- LABEL *label;
- VALUE labels = rb_ary_tmp_new(1);
- VALUE *opt_table;
- int i = 0, j;
-
- while (node) {
- label = NEW_LABEL(nd_line(node));
- rb_ary_push(labels, (VALUE)label | 1);
- ADD_LABEL(optargs, label);
+ last_comma = 1;
+ rest_id = 0;
+ }
+ block_id = args->block_arg;
+
+ if (args->opt_args) {
+ const NODE *node = args->opt_args;
+ LABEL *label;
+ VALUE labels = rb_ary_hidden_new(1);
+ VALUE *opt_table;
+ int i = 0, j;
+
+ while (node) {
+ label = NEW_LABEL(nd_line(node));
+ rb_ary_push(labels, (VALUE)label | 1);
+ ADD_LABEL(optargs, label);
NO_CHECK(COMPILE_POPPED(optargs, "optarg", node->nd_body));
- node = node->nd_next;
- i += 1;
- }
+ node = node->nd_next;
+ i += 1;
+ }
- /* last label */
- label = NEW_LABEL(nd_line(node_args));
- rb_ary_push(labels, (VALUE)label | 1);
- ADD_LABEL(optargs, label);
+ /* last label */
+ label = NEW_LABEL(nd_line(node_args));
+ rb_ary_push(labels, (VALUE)label | 1);
+ ADD_LABEL(optargs, label);
- opt_table = ALLOC_N(VALUE, i+1);
+ opt_table = ALLOC_N(VALUE, i+1);
MEMCPY(opt_table, RARRAY_CONST_PTR_TRANSIENT(labels), VALUE, i+1);
- for (j = 0; j < i+1; j++) {
- opt_table[j] &= ~1;
- }
- rb_ary_clear(labels);
-
- body->param.flags.has_opt = TRUE;
- body->param.opt_num = i;
- body->param.opt_table = opt_table;
- arg_size += i;
- }
-
- if (rest_id) {
- body->param.rest_start = arg_size++;
- body->param.flags.has_rest = TRUE;
- assert(body->param.rest_start != -1);
- }
-
- if (args->first_post_arg) {
- body->param.post_start = arg_size;
- body->param.post_num = args->post_args_num;
- body->param.flags.has_post = TRUE;
- arg_size += args->post_args_num;
-
- if (body->param.flags.has_rest) { /* TODO: why that? */
- body->param.post_start = body->param.rest_start + 1;
- }
- }
-
- if (args->kw_args) {
- arg_size = iseq_set_arguments_keywords(iseq, optargs, args, arg_size);
- }
- else if (args->kw_rest_arg) {
- struct rb_iseq_param_keyword *keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
- keyword->rest_start = arg_size++;
- body->param.keyword = keyword;
- body->param.flags.has_kwrest = TRUE;
- }
- else if (args->no_kwarg) {
- body->param.flags.accepts_no_kwarg = TRUE;
- }
-
- if (block_id) {
- body->param.block_start = arg_size++;
- body->param.flags.has_block = TRUE;
- }
-
- iseq_calc_param_size(iseq);
- body->param.size = arg_size;
-
- if (args->pre_init) { /* m_init */
+ for (j = 0; j < i+1; j++) {
+ opt_table[j] &= ~1;
+ }
+ rb_ary_clear(labels);
+
+ body->param.flags.has_opt = TRUE;
+ body->param.opt_num = i;
+ body->param.opt_table = opt_table;
+ arg_size += i;
+ }
+
+ if (rest_id) {
+ body->param.rest_start = arg_size++;
+ body->param.flags.has_rest = TRUE;
+ assert(body->param.rest_start != -1);
+ }
+
+ if (args->first_post_arg) {
+ body->param.post_start = arg_size;
+ body->param.post_num = args->post_args_num;
+ body->param.flags.has_post = TRUE;
+ arg_size += args->post_args_num;
+
+ if (body->param.flags.has_rest) { /* TODO: why that? */
+ body->param.post_start = body->param.rest_start + 1;
+ }
+ }
+
+ if (args->kw_args) {
+ arg_size = iseq_set_arguments_keywords(iseq, optargs, args, arg_size);
+ }
+ else if (args->kw_rest_arg) {
+ struct rb_iseq_param_keyword *keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
+ keyword->rest_start = arg_size++;
+ body->param.keyword = keyword;
+ body->param.flags.has_kwrest = TRUE;
+ }
+ else if (args->no_kwarg) {
+ body->param.flags.accepts_no_kwarg = TRUE;
+ }
+
+ if (block_id) {
+ body->param.block_start = arg_size++;
+ body->param.flags.has_block = TRUE;
+ }
+
+ iseq_calc_param_size(iseq);
+ body->param.size = arg_size;
+
+ if (args->pre_init) { /* m_init */
NO_CHECK(COMPILE_POPPED(optargs, "init arguments (m)", args->pre_init));
- }
- if (args->post_init) { /* p_init */
+ }
+ if (args->post_init) { /* p_init */
NO_CHECK(COMPILE_POPPED(optargs, "init arguments (p)", args->post_init));
- }
+ }
- if (body->type == ISEQ_TYPE_BLOCK) {
- if (body->param.flags.has_opt == FALSE &&
- body->param.flags.has_post == FALSE &&
- body->param.flags.has_rest == FALSE &&
- body->param.flags.has_kw == FALSE &&
- body->param.flags.has_kwrest == FALSE) {
+ if (body->type == ISEQ_TYPE_BLOCK) {
+ if (body->param.flags.has_opt == FALSE &&
+ body->param.flags.has_post == FALSE &&
+ body->param.flags.has_rest == FALSE &&
+ body->param.flags.has_kw == FALSE &&
+ body->param.flags.has_kwrest == FALSE) {
- if (body->param.lead_num == 1 && last_comma == 0) {
- /* {|a|} */
- body->param.flags.ambiguous_param0 = TRUE;
- }
- }
- }
+ if (body->param.lead_num == 1 && last_comma == 0) {
+ /* {|a|} */
+ body->param.flags.ambiguous_param0 = TRUE;
+ }
+ }
+ }
}
return COMPILE_OK;
@@ -1948,8 +1977,8 @@ iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl)
unsigned int size = tbl ? tbl->size : 0;
if (size > 0) {
- ID *ids = (ID *)ALLOC_N(ID, size);
- MEMCPY(ids, tbl->ids, ID, size);
+ ID *ids = (ID *)ALLOC_N(ID, size);
+ MEMCPY(ids, tbl->ids, ID, size);
ISEQ_BODY(iseq)->local_table = ids;
}
ISEQ_BODY(iseq)->local_table_size = size;
@@ -2058,20 +2087,7 @@ cdhash_set_label_i(VALUE key, VALUE val, VALUE ptr)
static inline VALUE
get_ivar_ic_value(rb_iseq_t *iseq,ID id)
{
- VALUE val;
- struct rb_id_table *tbl = ISEQ_COMPILE_DATA(iseq)->ivar_cache_table;
- if (tbl) {
- if (rb_id_table_lookup(tbl,id,&val)) {
- return val;
- }
- }
- else {
- tbl = rb_id_table_create(1);
- ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = tbl;
- }
- val = INT2FIX(ISEQ_BODY(iseq)->ivc_size++);
- rb_id_table_insert(tbl,id,val);
- return val;
+ return INT2FIX(ISEQ_BODY(iseq)->ivc_size++);
}
static inline VALUE
@@ -2080,13 +2096,13 @@ get_cvar_ic_value(rb_iseq_t *iseq,ID id)
VALUE val;
struct rb_id_table *tbl = ISEQ_COMPILE_DATA(iseq)->ivar_cache_table;
if (tbl) {
- if (rb_id_table_lookup(tbl,id,&val)) {
- return val;
- }
+ if (rb_id_table_lookup(tbl,id,&val)) {
+ return val;
+ }
}
else {
- tbl = rb_id_table_create(1);
- ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = tbl;
+ tbl = rb_id_table_create(1);
+ ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = tbl;
}
val = INT2FIX(ISEQ_BODY(iseq)->icvarc_size++);
rb_id_table_insert(tbl,id,val);
@@ -2109,113 +2125,113 @@ fix_sp_depth(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
LINK_ELEMENT *list;
for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
- if (IS_LABEL(list)) {
- LABEL *lobj = (LABEL *)list;
- lobj->set = TRUE;
- }
+ if (IS_LABEL(list)) {
+ LABEL *lobj = (LABEL *)list;
+ lobj->set = TRUE;
+ }
}
for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
- switch (list->type) {
- case ISEQ_ELEMENT_INSN:
- {
- int j, len, insn;
- const char *types;
- VALUE *operands;
- INSN *iobj = (INSN *)list;
-
- /* update sp */
- sp = calc_sp_depth(sp, iobj);
- if (sp < 0) {
- BADINSN_DUMP(anchor, list, NULL);
- COMPILE_ERROR(iseq, iobj->insn_info.line_no,
- "argument stack underflow (%d)", sp);
- return -1;
- }
- if (sp > stack_max) {
- stack_max = sp;
- }
-
- line = iobj->insn_info.line_no;
- /* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
- operands = iobj->operands;
- insn = iobj->insn_id;
- types = insn_op_types(insn);
- len = insn_len(insn);
-
- /* operand check */
- if (iobj->operand_size != len - 1) {
- /* printf("operand size miss! (%d, %d)\n", iobj->operand_size, len); */
- BADINSN_DUMP(anchor, list, NULL);
- COMPILE_ERROR(iseq, iobj->insn_info.line_no,
- "operand size miss! (%d for %d)",
- iobj->operand_size, len - 1);
- return -1;
- }
-
- for (j = 0; types[j]; j++) {
- if (types[j] == TS_OFFSET) {
- /* label(destination position) */
- LABEL *lobj = (LABEL *)operands[j];
- if (!lobj->set) {
- BADINSN_DUMP(anchor, list, NULL);
- COMPILE_ERROR(iseq, iobj->insn_info.line_no,
- "unknown label: "LABEL_FORMAT, lobj->label_no);
- return -1;
- }
- if (lobj->sp == -1) {
- lobj->sp = sp;
+ switch (list->type) {
+ case ISEQ_ELEMENT_INSN:
+ {
+ int j, len, insn;
+ const char *types;
+ VALUE *operands;
+ INSN *iobj = (INSN *)list;
+
+ /* update sp */
+ sp = calc_sp_depth(sp, iobj);
+ if (sp < 0) {
+ BADINSN_DUMP(anchor, list, NULL);
+ COMPILE_ERROR(iseq, iobj->insn_info.line_no,
+ "argument stack underflow (%d)", sp);
+ return -1;
+ }
+ if (sp > stack_max) {
+ stack_max = sp;
+ }
+
+ line = iobj->insn_info.line_no;
+ /* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
+ operands = iobj->operands;
+ insn = iobj->insn_id;
+ types = insn_op_types(insn);
+ len = insn_len(insn);
+
+ /* operand check */
+ if (iobj->operand_size != len - 1) {
+ /* printf("operand size miss! (%d, %d)\n", iobj->operand_size, len); */
+ BADINSN_DUMP(anchor, list, NULL);
+ COMPILE_ERROR(iseq, iobj->insn_info.line_no,
+ "operand size miss! (%d for %d)",
+ iobj->operand_size, len - 1);
+ return -1;
+ }
+
+ for (j = 0; types[j]; j++) {
+ if (types[j] == TS_OFFSET) {
+ /* label(destination position) */
+ LABEL *lobj = (LABEL *)operands[j];
+ if (!lobj->set) {
+ BADINSN_DUMP(anchor, list, NULL);
+ COMPILE_ERROR(iseq, iobj->insn_info.line_no,
+ "unknown label: "LABEL_FORMAT, lobj->label_no);
+ return -1;
+ }
+ if (lobj->sp == -1) {
+ lobj->sp = sp;
}
else if (lobj->sp != sp) {
debugs("%s:%d: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
RSTRING_PTR(rb_iseq_path(iseq)), line,
lobj->label_no, lobj->sp, sp);
}
- }
- }
- break;
- }
- case ISEQ_ELEMENT_LABEL:
- {
- LABEL *lobj = (LABEL *)list;
- if (lobj->sp == -1) {
- lobj->sp = sp;
- }
- else {
+ }
+ }
+ break;
+ }
+ case ISEQ_ELEMENT_LABEL:
+ {
+ LABEL *lobj = (LABEL *)list;
+ if (lobj->sp == -1) {
+ lobj->sp = sp;
+ }
+ else {
if (lobj->sp != sp) {
debugs("%s:%d: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
RSTRING_PTR(rb_iseq_path(iseq)), line,
lobj->label_no, lobj->sp, sp);
}
- sp = lobj->sp;
- }
- break;
- }
- case ISEQ_ELEMENT_TRACE:
- {
- /* ignore */
- break;
- }
- case ISEQ_ELEMENT_ADJUST:
- {
- ADJUST *adjust = (ADJUST *)list;
- int orig_sp = sp;
-
- sp = adjust->label ? adjust->label->sp : 0;
- if (adjust->line_no != -1 && orig_sp - sp < 0) {
- BADINSN_DUMP(anchor, list, NULL);
- COMPILE_ERROR(iseq, adjust->line_no,
- "iseq_set_sequence: adjust bug %d < %d",
- orig_sp, sp);
- return -1;
- }
- break;
- }
- default:
- BADINSN_DUMP(anchor, list, NULL);
- COMPILE_ERROR(iseq, line, "unknown list type: %d", list->type);
- return -1;
- }
+ sp = lobj->sp;
+ }
+ break;
+ }
+ case ISEQ_ELEMENT_TRACE:
+ {
+ /* ignore */
+ break;
+ }
+ case ISEQ_ELEMENT_ADJUST:
+ {
+ ADJUST *adjust = (ADJUST *)list;
+ int orig_sp = sp;
+
+ sp = adjust->label ? adjust->label->sp : 0;
+ if (adjust->line_no != -1 && orig_sp - sp < 0) {
+ BADINSN_DUMP(anchor, list, NULL);
+ COMPILE_ERROR(iseq, adjust->line_no,
+ "iseq_set_sequence: adjust bug %d < %d",
+ orig_sp, sp);
+ return -1;
+ }
+ break;
+ }
+ default:
+ BADINSN_DUMP(anchor, list, NULL);
+ COMPILE_ERROR(iseq, line, "unknown list type: %d", list->type);
+ return -1;
+ }
}
return stack_max;
}
@@ -2251,6 +2267,30 @@ add_adjust_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions
return TRUE;
}
+static ID *
+array_to_idlist(VALUE arr)
+{
+ RUBY_ASSERT(RB_TYPE_P(arr, T_ARRAY));
+ long size = RARRAY_LEN(arr);
+ ID *ids = (ID *)ALLOC_N(ID, size + 1);
+ for (int i = 0; i < size; i++) {
+ VALUE sym = RARRAY_AREF(arr, i);
+ ids[i] = SYM2ID(sym);
+ }
+ ids[size] = 0;
+ return ids;
+}
+
+static VALUE
+idlist_to_array(const ID *ids)
+{
+ VALUE arr = rb_ary_new();
+ while (*ids) {
+ rb_ary_push(arr, ID2SYM(*ids++));
+ }
+ return arr;
+}
+
/**
ruby insn object list -> raw instruction sequence
*/
@@ -2273,20 +2313,20 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
/* fix label position */
insn_num = code_index = 0;
for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
- switch (list->type) {
- case ISEQ_ELEMENT_INSN:
- {
- INSN *iobj = (INSN *)list;
- /* update sp */
- sp = calc_sp_depth(sp, iobj);
- insn_num++;
+ switch (list->type) {
+ case ISEQ_ELEMENT_INSN:
+ {
+ INSN *iobj = (INSN *)list;
+ /* update sp */
+ sp = calc_sp_depth(sp, iobj);
+ insn_num++;
events = iobj->insn_info.events |= events;
if (ISEQ_COVERAGE(iseq)) {
if (ISEQ_LINE_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_LINE) &&
!(rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES)) {
- int line = iobj->insn_info.line_no;
- if (line >= 1) {
- RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line - 1, INT2FIX(0));
+ int line = iobj->insn_info.line_no - 1;
+ if (line >= 0 && line < RARRAY_LEN(ISEQ_LINE_COVERAGE(iseq))) {
+ RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line, INT2FIX(0));
}
}
if (ISEQ_BRANCH_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_BRANCH)) {
@@ -2295,47 +2335,47 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
}
RARRAY_ASET(ISEQ_PC2BRANCHINDEX(iseq), code_index, INT2FIX(data));
}
- }
+ }
code_index += insn_data_length(iobj);
- events = 0;
+ events = 0;
data = 0;
- break;
- }
- case ISEQ_ELEMENT_LABEL:
- {
- LABEL *lobj = (LABEL *)list;
- lobj->position = code_index;
+ break;
+ }
+ case ISEQ_ELEMENT_LABEL:
+ {
+ LABEL *lobj = (LABEL *)list;
+ lobj->position = code_index;
if (lobj->sp != sp) {
debugs("%s: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
RSTRING_PTR(rb_iseq_path(iseq)),
lobj->label_no, lobj->sp, sp);
}
- sp = lobj->sp;
- break;
- }
- case ISEQ_ELEMENT_TRACE:
- {
- TRACE *trace = (TRACE *)list;
- events |= trace->event;
+ sp = lobj->sp;
+ break;
+ }
+ case ISEQ_ELEMENT_TRACE:
+ {
+ TRACE *trace = (TRACE *)list;
+ events |= trace->event;
if (trace->event & RUBY_EVENT_COVERAGE_BRANCH) data = trace->data;
- break;
- }
- case ISEQ_ELEMENT_ADJUST:
- {
- ADJUST *adjust = (ADJUST *)list;
- if (adjust->line_no != -1) {
- int orig_sp = sp;
- sp = adjust->label ? adjust->label->sp : 0;
- if (orig_sp - sp > 0) {
- if (orig_sp - sp > 1) code_index++; /* 1 operand */
- code_index++; /* insn */
- insn_num++;
- }
- }
- break;
- }
- default: break;
- }
+ break;
+ }
+ case ISEQ_ELEMENT_ADJUST:
+ {
+ ADJUST *adjust = (ADJUST *)list;
+ if (adjust->line_no != -1) {
+ int orig_sp = sp;
+ sp = adjust->label ? adjust->label->sp : 0;
+ if (orig_sp - sp > 0) {
+ if (orig_sp - sp > 1) code_index++; /* 1 operand */
+ code_index++; /* insn */
+ insn_num++;
+ }
+ }
+ break;
+ }
+ default: break;
+ }
}
/* make instruction sequence */
@@ -2368,92 +2408,116 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
insns_info_index = code_index = sp = 0;
while (list) {
- switch (list->type) {
- case ISEQ_ELEMENT_INSN:
- {
- int j, len, insn;
- const char *types;
- VALUE *operands;
- INSN *iobj = (INSN *)list;
-
- /* update sp */
- sp = calc_sp_depth(sp, iobj);
- /* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
- operands = iobj->operands;
- insn = iobj->insn_id;
- generated_iseq[code_index] = insn;
- types = insn_op_types(insn);
- len = insn_len(insn);
-
- for (j = 0; types[j]; j++) {
- char type = types[j];
-
- /* printf("--> [%c - (%d-%d)]\n", type, k, j); */
- switch (type) {
- case TS_OFFSET:
- {
- /* label(destination position) */
- LABEL *lobj = (LABEL *)operands[j];
- generated_iseq[code_index + 1 + j] = lobj->position - (code_index + len);
- break;
- }
- case TS_CDHASH:
- {
- VALUE map = operands[j];
- struct cdhash_set_label_struct data;
+ switch (list->type) {
+ case ISEQ_ELEMENT_INSN:
+ {
+ int j, len, insn;
+ const char *types;
+ VALUE *operands;
+ INSN *iobj = (INSN *)list;
+
+ /* update sp */
+ sp = calc_sp_depth(sp, iobj);
+ /* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
+ operands = iobj->operands;
+ insn = iobj->insn_id;
+ generated_iseq[code_index] = insn;
+ types = insn_op_types(insn);
+ len = insn_len(insn);
+
+ for (j = 0; types[j]; j++) {
+ char type = types[j];
+
+ /* printf("--> [%c - (%d-%d)]\n", type, k, j); */
+ switch (type) {
+ case TS_OFFSET:
+ {
+ /* label(destination position) */
+ LABEL *lobj = (LABEL *)operands[j];
+ generated_iseq[code_index + 1 + j] = lobj->position - (code_index + len);
+ break;
+ }
+ case TS_CDHASH:
+ {
+ VALUE map = operands[j];
+ struct cdhash_set_label_struct data;
data.hash = map;
data.pos = code_index;
data.len = len;
- rb_hash_foreach(map, cdhash_set_label_i, (VALUE)&data);
+ rb_hash_foreach(map, cdhash_set_label_i, (VALUE)&data);
- rb_hash_rehash(map);
- freeze_hide_obj(map);
- generated_iseq[code_index + 1 + j] = map;
+ rb_hash_rehash(map);
+ freeze_hide_obj(map);
+ generated_iseq[code_index + 1 + j] = map;
ISEQ_MBITS_SET(mark_offset_bits, code_index + 1 + j);
- RB_OBJ_WRITTEN(iseq, Qundef, map);
+ RB_OBJ_WRITTEN(iseq, Qundef, map);
needs_bitmap = true;
- break;
- }
- case TS_LINDEX:
- case TS_NUM: /* ulong */
- generated_iseq[code_index + 1 + j] = FIX2INT(operands[j]);
- break;
- case TS_ISEQ: /* iseq */
- case TS_VALUE: /* VALUE */
- {
- VALUE v = operands[j];
- generated_iseq[code_index + 1 + j] = v;
- /* to mark ruby object */
- if (!SPECIAL_CONST_P(v)) {
- RB_OBJ_WRITTEN(iseq, Qundef, v);
+ break;
+ }
+ case TS_LINDEX:
+ case TS_NUM: /* ulong */
+ generated_iseq[code_index + 1 + j] = FIX2INT(operands[j]);
+ break;
+ case TS_ISEQ: /* iseq */
+ case TS_VALUE: /* VALUE */
+ {
+ VALUE v = operands[j];
+ generated_iseq[code_index + 1 + j] = v;
+ /* to mark ruby object */
+ if (!SPECIAL_CONST_P(v)) {
+ RB_OBJ_WRITTEN(iseq, Qundef, v);
ISEQ_MBITS_SET(mark_offset_bits, code_index + 1 + j);
needs_bitmap = true;
- }
- break;
- }
+ }
+ break;
+ }
/* [ TS_IVC | TS_ICVARC | TS_ISE | TS_IC ] */
case TS_IC: /* inline cache: constants */
+ {
+ unsigned int ic_index = ISEQ_COMPILE_DATA(iseq)->ic_index++;
+ IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache;
+ if (UNLIKELY(ic_index >= body->ic_size)) {
+ BADINSN_DUMP(anchor, &iobj->link, 0);
+ COMPILE_ERROR(iseq, iobj->insn_info.line_no,
+ "iseq_set_sequence: ic_index overflow: index: %d, size: %d",
+ ic_index, ISEQ_IS_SIZE(body));
+ }
+
+ ic->segments = array_to_idlist(operands[j]);
+
+ generated_iseq[code_index + 1 + j] = (VALUE)ic;
+ }
+ break;
+ case TS_IVC: /* inline ivar cache */
+ {
+ unsigned int ic_index = FIX2UINT(operands[j]);
+
+ IVC cache = ((IVC)&body->is_entries[ic_index]);
+
+ if (insn == BIN(setinstancevariable)) {
+ cache->iv_set_name = SYM2ID(operands[j - 1]);
+ }
+ else {
+ cache->iv_set_name = 0;
+ }
+
+ vm_ic_attr_index_initialize(cache, INVALID_SHAPE_ID);
+ }
case TS_ISE: /* inline storage entry: `once` insn */
case TS_ICVARC: /* inline cvar cache */
- case TS_IVC: /* inline ivar cache */
- {
- unsigned int ic_index = FIX2UINT(operands[j]);
+ {
+ unsigned int ic_index = FIX2UINT(operands[j]);
IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache;
- if (UNLIKELY(ic_index >= ISEQ_IS_SIZE(body))) {
+ if (UNLIKELY(ic_index >= ISEQ_IS_SIZE(body))) {
BADINSN_DUMP(anchor, &iobj->link, 0);
COMPILE_ERROR(iseq, iobj->insn_info.line_no,
"iseq_set_sequence: ic_index overflow: index: %d, size: %d",
ic_index, ISEQ_IS_SIZE(body));
- }
- generated_iseq[code_index + 1 + j] = (VALUE)ic;
-
- if (insn == BIN(opt_getinlinecache) && type == TS_IC) {
- // Store the instruction index for opt_getinlinecache on the IC for
- // YJIT to invalidate code when opt_setinlinecache runs.
- ic->get_insn_idx = (unsigned int)code_index;
}
- break;
- }
+ generated_iseq[code_index + 1 + j] = (VALUE)ic;
+
+ break;
+ }
case TS_CALLDATA:
{
const struct rb_callinfo *source_ci = (const struct rb_callinfo *)operands[j];
@@ -2464,86 +2528,86 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
generated_iseq[code_index + 1 + j] = (VALUE)cd;
break;
}
- case TS_ID: /* ID */
- generated_iseq[code_index + 1 + j] = SYM2ID(operands[j]);
- break;
- case TS_FUNCPTR:
- generated_iseq[code_index + 1 + j] = operands[j];
- break;
+ case TS_ID: /* ID */
+ generated_iseq[code_index + 1 + j] = SYM2ID(operands[j]);
+ break;
+ case TS_FUNCPTR:
+ generated_iseq[code_index + 1 + j] = operands[j];
+ break;
case TS_BUILTIN:
generated_iseq[code_index + 1 + j] = operands[j];
break;
- default:
- BADINSN_ERROR(iseq, iobj->insn_info.line_no,
- "unknown operand type: %c", type);
- return COMPILE_NG;
- }
- }
- if (add_insn_info(insns_info, positions, insns_info_index, code_index, iobj)) insns_info_index++;
- code_index += len;
- break;
- }
- case ISEQ_ELEMENT_LABEL:
- {
- LABEL *lobj = (LABEL *)list;
+ default:
+ BADINSN_ERROR(iseq, iobj->insn_info.line_no,
+ "unknown operand type: %c", type);
+ return COMPILE_NG;
+ }
+ }
+ if (add_insn_info(insns_info, positions, insns_info_index, code_index, iobj)) insns_info_index++;
+ code_index += len;
+ break;
+ }
+ case ISEQ_ELEMENT_LABEL:
+ {
+ LABEL *lobj = (LABEL *)list;
if (lobj->sp != sp) {
debugs("%s: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
RSTRING_PTR(rb_iseq_path(iseq)),
lobj->label_no, lobj->sp, sp);
}
- sp = lobj->sp;
- break;
- }
- case ISEQ_ELEMENT_ADJUST:
- {
- ADJUST *adjust = (ADJUST *)list;
- int orig_sp = sp;
-
- if (adjust->label) {
- sp = adjust->label->sp;
- }
- else {
- sp = 0;
- }
-
- if (adjust->line_no != -1) {
- const int diff = orig_sp - sp;
- if (diff > 0) {
+ sp = lobj->sp;
+ break;
+ }
+ case ISEQ_ELEMENT_ADJUST:
+ {
+ ADJUST *adjust = (ADJUST *)list;
+ int orig_sp = sp;
+
+ if (adjust->label) {
+ sp = adjust->label->sp;
+ }
+ else {
+ sp = 0;
+ }
+
+ if (adjust->line_no != -1) {
+ const int diff = orig_sp - sp;
+ if (diff > 0) {
if (insns_info_index == 0) {
COMPILE_ERROR(iseq, adjust->line_no,
"iseq_set_sequence: adjust bug (ISEQ_ELEMENT_ADJUST must not be the first in iseq)");
}
- if (add_adjust_info(insns_info, positions, insns_info_index, code_index, adjust)) insns_info_index++;
- }
- if (diff > 1) {
- generated_iseq[code_index++] = BIN(adjuststack);
- generated_iseq[code_index++] = orig_sp - sp;
- }
- else if (diff == 1) {
- generated_iseq[code_index++] = BIN(pop);
- }
- else if (diff < 0) {
- int label_no = adjust->label ? adjust->label->label_no : -1;
- xfree(generated_iseq);
- xfree(insns_info);
- xfree(positions);
+ if (add_adjust_info(insns_info, positions, insns_info_index, code_index, adjust)) insns_info_index++;
+ }
+ if (diff > 1) {
+ generated_iseq[code_index++] = BIN(adjuststack);
+ generated_iseq[code_index++] = orig_sp - sp;
+ }
+ else if (diff == 1) {
+ generated_iseq[code_index++] = BIN(pop);
+ }
+ else if (diff < 0) {
+ int label_no = adjust->label ? adjust->label->label_no : -1;
+ xfree(generated_iseq);
+ xfree(insns_info);
+ xfree(positions);
if (ISEQ_MBITS_BUFLEN(code_size) > 1) {
xfree(mark_offset_bits);
}
- debug_list(anchor, list);
- COMPILE_ERROR(iseq, adjust->line_no,
- "iseq_set_sequence: adjust bug to %d %d < %d",
- label_no, orig_sp, sp);
- return COMPILE_NG;
- }
- }
- break;
- }
- default:
- /* ignore */
- break;
- }
- list = list->next;
+ debug_list(anchor, list);
+ COMPILE_ERROR(iseq, adjust->line_no,
+ "iseq_set_sequence: adjust bug to %d %d < %d",
+ label_no, orig_sp, sp);
+ return COMPILE_NG;
+ }
+ }
+ break;
+ }
+ default:
+ /* ignore */
+ break;
+ }
+ list = list->next;
}
body->iseq_encoded = (void *)generated_iseq;
@@ -2596,44 +2660,48 @@ iseq_set_exception_table(rb_iseq_t *iseq)
struct iseq_catch_table_entry *entry;
ISEQ_BODY(iseq)->catch_table = NULL;
- if (NIL_P(ISEQ_COMPILE_DATA(iseq)->catch_table_ary)) return COMPILE_OK;
- tlen = (int)RARRAY_LEN(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
- tptr = RARRAY_CONST_PTR_TRANSIENT(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
+
+ VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
+ if (NIL_P(catch_table_ary)) return COMPILE_OK;
+ tlen = (int)RARRAY_LEN(catch_table_ary);
+ tptr = RARRAY_CONST_PTR_TRANSIENT(catch_table_ary);
if (tlen > 0) {
- struct iseq_catch_table *table = xmalloc(iseq_catch_table_bytes(tlen));
- table->size = tlen;
+ struct iseq_catch_table *table = xmalloc(iseq_catch_table_bytes(tlen));
+ table->size = tlen;
- for (i = 0; i < table->size; i++) {
+ for (i = 0; i < table->size; i++) {
ptr = RARRAY_CONST_PTR_TRANSIENT(tptr[i]);
- entry = UNALIGNED_MEMBER_PTR(table, entries[i]);
- entry->type = (enum catch_type)(ptr[0] & 0xffff);
- entry->start = label_get_position((LABEL *)(ptr[1] & ~1));
- entry->end = label_get_position((LABEL *)(ptr[2] & ~1));
- entry->iseq = (rb_iseq_t *)ptr[3];
- RB_OBJ_WRITTEN(iseq, Qundef, entry->iseq);
-
- /* stack depth */
- if (ptr[4]) {
- LABEL *lobj = (LABEL *)(ptr[4] & ~1);
- entry->cont = label_get_position(lobj);
- entry->sp = label_get_sp(lobj);
-
- /* TODO: Dirty Hack! Fix me */
- if (entry->type == CATCH_TYPE_RESCUE ||
- entry->type == CATCH_TYPE_BREAK ||
- entry->type == CATCH_TYPE_NEXT) {
- entry->sp--;
- }
- }
- else {
- entry->cont = 0;
- }
- }
+ entry = UNALIGNED_MEMBER_PTR(table, entries[i]);
+ entry->type = (enum rb_catch_type)(ptr[0] & 0xffff);
+ entry->start = label_get_position((LABEL *)(ptr[1] & ~1));
+ entry->end = label_get_position((LABEL *)(ptr[2] & ~1));
+ entry->iseq = (rb_iseq_t *)ptr[3];
+ RB_OBJ_WRITTEN(iseq, Qundef, entry->iseq);
+
+ /* stack depth */
+ if (ptr[4]) {
+ LABEL *lobj = (LABEL *)(ptr[4] & ~1);
+ entry->cont = label_get_position(lobj);
+ entry->sp = label_get_sp(lobj);
+
+ /* TODO: Dirty Hack! Fix me */
+ if (entry->type == CATCH_TYPE_RESCUE ||
+ entry->type == CATCH_TYPE_BREAK ||
+ entry->type == CATCH_TYPE_NEXT) {
+ entry->sp--;
+ }
+ }
+ else {
+ entry->cont = 0;
+ }
+ }
ISEQ_BODY(iseq)->catch_table = table;
- RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, 0); /* free */
+ RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, 0); /* free */
}
+ RB_GC_GUARD(catch_table_ary);
+
return COMPILE_OK;
}
@@ -2654,8 +2722,8 @@ iseq_set_optargs_table(rb_iseq_t *iseq)
if (ISEQ_BODY(iseq)->param.flags.has_opt) {
for (i = 0; i < ISEQ_BODY(iseq)->param.opt_num + 1; i++) {
- opt_table[i] = label_get_position((LABEL *)opt_table[i]);
- }
+ opt_table[i] = label_get_position((LABEL *)opt_table[i]);
+ }
}
return COMPILE_OK;
}
@@ -2669,27 +2737,27 @@ get_destination_insn(INSN *iobj)
list = lobj->link.next;
while (list) {
- switch (list->type) {
- case ISEQ_ELEMENT_INSN:
- case ISEQ_ELEMENT_ADJUST:
- goto found;
- case ISEQ_ELEMENT_LABEL:
- /* ignore */
- break;
- case ISEQ_ELEMENT_TRACE:
- {
- TRACE *trace = (TRACE *)list;
- events |= trace->event;
- }
- break;
- default: break;
- }
- list = list->next;
+ switch (list->type) {
+ case ISEQ_ELEMENT_INSN:
+ case ISEQ_ELEMENT_ADJUST:
+ goto found;
+ case ISEQ_ELEMENT_LABEL:
+ /* ignore */
+ break;
+ case ISEQ_ELEMENT_TRACE:
+ {
+ TRACE *trace = (TRACE *)list;
+ events |= trace->event;
+ }
+ break;
+ default: break;
+ }
+ list = list->next;
}
found:
if (list && IS_INSN(list)) {
- INSN *iobj = (INSN *)list;
- iobj->insn_info.events |= events;
+ INSN *iobj = (INSN *)list;
+ iobj->insn_info.events |= events;
}
return list;
}
@@ -2700,10 +2768,10 @@ get_next_insn(INSN *iobj)
LINK_ELEMENT *list = iobj->link.next;
while (list) {
- if (IS_INSN(list) || IS_ADJUST(list)) {
- return list;
- }
- list = list->next;
+ if (IS_INSN(list) || IS_ADJUST(list)) {
+ return list;
+ }
+ list = list->next;
}
return 0;
}
@@ -2714,10 +2782,10 @@ get_prev_insn(INSN *iobj)
LINK_ELEMENT *list = iobj->link.prev;
while (list) {
- if (IS_INSN(list) || IS_ADJUST(list)) {
- return list;
- }
- list = list->prev;
+ if (IS_INSN(list) || IS_ADJUST(list)) {
+ return list;
+ }
+ list = list->prev;
}
return 0;
}
@@ -2747,9 +2815,9 @@ find_destination(INSN *i)
{
int pos, len = insn_len(i->insn_id);
for (pos = 0; pos < len; ++pos) {
- if (insn_op_types(i->insn_id)[pos] == TS_OFFSET) {
- return (LABEL *)OPERAND_AT(i, pos);
- }
+ if (insn_op_types(i->insn_id)[pos] == TS_OFFSET) {
+ return (LABEL *)OPERAND_AT(i, pos);
+ }
}
return 0;
}
@@ -2765,53 +2833,51 @@ remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
MEMZERO(unref_counts, int, nlabels);
end = i;
do {
- LABEL *lab;
- if (IS_INSN(i)) {
- if (IS_INSN_ID(i, leave)) {
- end = i;
- break;
- }
- else if ((lab = find_destination((INSN *)i)) != 0) {
- if (lab->unremovable) break;
- unref_counts[lab->label_no]++;
- }
- }
- else if (IS_LABEL(i)) {
- lab = (LABEL *)i;
- if (lab->unremovable) return 0;
- if (lab->refcnt > unref_counts[lab->label_no]) {
- if (i == first) return 0;
- break;
- }
- continue;
- }
- else if (IS_TRACE(i)) {
- /* do nothing */
- }
- else if (IS_ADJUST(i)) {
- LABEL *dest = ((ADJUST *)i)->label;
- if (dest && dest->unremovable) return 0;
- }
- end = i;
+ LABEL *lab;
+ if (IS_INSN(i)) {
+ if (IS_INSN_ID(i, leave)) {
+ end = i;
+ break;
+ }
+ else if ((lab = find_destination((INSN *)i)) != 0) {
+ unref_counts[lab->label_no]++;
+ }
+ }
+ else if (IS_LABEL(i)) {
+ lab = (LABEL *)i;
+ if (lab->unremovable) return 0;
+ if (lab->refcnt > unref_counts[lab->label_no]) {
+ if (i == first) return 0;
+ break;
+ }
+ continue;
+ }
+ else if (IS_TRACE(i)) {
+ /* do nothing */
+ }
+ else if (IS_ADJUST(i)) {
+ return 0;
+ }
+ end = i;
} while ((i = i->next) != 0);
i = first;
do {
- if (IS_INSN(i)) {
+ if (IS_INSN(i)) {
struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
- VALUE insn = INSN_OF(i);
- int pos, len = insn_len(insn);
- for (pos = 0; pos < len; ++pos) {
- switch (insn_op_types(insn)[pos]) {
- case TS_OFFSET:
- unref_destination((INSN *)i, pos);
- break;
+ VALUE insn = INSN_OF(i);
+ int pos, len = insn_len(insn);
+ for (pos = 0; pos < len; ++pos) {
+ switch (insn_op_types(insn)[pos]) {
+ case TS_OFFSET:
+ unref_destination((INSN *)i, pos);
+ break;
case TS_CALLDATA:
--(body->ci_size);
- break;
- }
- }
- }
- ELEM_REMOVE(i);
+ break;
+ }
+ }
+ }
+ ELEM_REMOVE(i);
} while ((i != end) && (i = i->next) != 0);
return 1;
}
@@ -2821,14 +2887,14 @@ iseq_pop_newarray(rb_iseq_t *iseq, INSN *iobj)
{
switch (OPERAND_AT(iobj, 0)) {
case INT2FIX(0): /* empty array */
- ELEM_REMOVE(&iobj->link);
- return TRUE;
+ ELEM_REMOVE(&iobj->link);
+ return TRUE;
case INT2FIX(1): /* single element array */
- ELEM_REMOVE(&iobj->link);
- return FALSE;
+ ELEM_REMOVE(&iobj->link);
+ return FALSE;
default:
- iobj->insn_id = BIN(adjuststack);
- return TRUE;
+ iobj->insn_id = BIN(adjuststack);
+ return TRUE;
}
}
@@ -2877,41 +2943,41 @@ optimize_checktype(rb_iseq_t *iseq, INSN *iobj)
switch (INSN_OF(iobj)) {
case BIN(putstring):
- type = INT2FIX(T_STRING);
- break;
+ type = INT2FIX(T_STRING);
+ break;
case BIN(putnil):
- type = INT2FIX(T_NIL);
- break;
+ type = INT2FIX(T_NIL);
+ break;
case BIN(putobject):
- type = INT2FIX(TYPE(OPERAND_AT(iobj, 0)));
- break;
+ type = INT2FIX(TYPE(OPERAND_AT(iobj, 0)));
+ break;
default: return FALSE;
}
ciobj = (INSN *)get_next_insn(iobj);
if (IS_INSN_ID(ciobj, jump)) {
- ciobj = (INSN *)get_next_insn((INSN*)OPERAND_AT(ciobj, 0));
+ ciobj = (INSN *)get_next_insn((INSN*)OPERAND_AT(ciobj, 0));
}
if (IS_INSN_ID(ciobj, dup)) {
- ciobj = (INSN *)get_next_insn(dup = ciobj);
+ ciobj = (INSN *)get_next_insn(dup = ciobj);
}
if (!ciobj || !IS_INSN_ID(ciobj, checktype)) return FALSE;
niobj = (INSN *)get_next_insn(ciobj);
if (!niobj) {
- /* TODO: putobject true/false */
- return FALSE;
+ /* TODO: putobject true/false */
+ return FALSE;
}
switch (INSN_OF(niobj)) {
case BIN(branchif):
- if (OPERAND_AT(ciobj, 0) == type) {
- dest = (LABEL *)OPERAND_AT(niobj, 0);
- }
- break;
+ if (OPERAND_AT(ciobj, 0) == type) {
+ dest = (LABEL *)OPERAND_AT(niobj, 0);
+ }
+ break;
case BIN(branchunless):
- if (OPERAND_AT(ciobj, 0) != type) {
- dest = (LABEL *)OPERAND_AT(niobj, 0);
- }
- break;
+ if (OPERAND_AT(ciobj, 0) != type) {
+ dest = (LABEL *)OPERAND_AT(niobj, 0);
+ }
+ break;
default:
return FALSE;
}
@@ -2919,13 +2985,13 @@ optimize_checktype(rb_iseq_t *iseq, INSN *iobj)
node_id = ciobj->insn_info.node_id;
NODE dummy_line_node = generate_dummy_line_node(line, node_id);
if (!dest) {
- if (niobj->link.next && IS_LABEL(niobj->link.next)) {
- dest = (LABEL *)niobj->link.next; /* reuse label */
- }
- else {
- dest = NEW_LABEL(line);
- ELEM_INSERT_NEXT(&niobj->link, &dest->link);
- }
+ if (niobj->link.next && IS_LABEL(niobj->link.next)) {
+ dest = (LABEL *)niobj->link.next; /* reuse label */
+ }
+ else {
+ dest = NEW_LABEL(line);
+ ELEM_INSERT_NEXT(&niobj->link, &dest->link);
+ }
}
INSERT_AFTER_INSN1(iobj, &dummy_line_node, jump, dest);
LABEL_REF(dest);
@@ -2965,112 +3031,112 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
if (IS_INSN_ID(iobj, jump)) {
INSN *niobj, *diobj, *piobj;
- diobj = (INSN *)get_destination_insn(iobj);
- niobj = (INSN *)get_next_insn(iobj);
-
- if (diobj == niobj) {
- /*
- * jump LABEL
- * LABEL:
- * =>
- * LABEL:
- */
- unref_destination(iobj, 0);
- ELEM_REMOVE(&iobj->link);
- return COMPILE_OK;
- }
+ diobj = (INSN *)get_destination_insn(iobj);
+ niobj = (INSN *)get_next_insn(iobj);
+
+ if (diobj == niobj) {
+ /*
+ * jump LABEL
+ * LABEL:
+ * =>
+ * LABEL:
+ */
+ unref_destination(iobj, 0);
+ ELEM_REMOVE(&iobj->link);
+ return COMPILE_OK;
+ }
else if (iobj != diobj && IS_INSN(&diobj->link) &&
IS_INSN_ID(diobj, jump) &&
- OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0) &&
+ OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0) &&
diobj->insn_info.events == 0) {
- /*
- * useless jump elimination:
- * jump LABEL1
- * ...
- * LABEL1:
- * jump LABEL2
- *
- * => in this case, first jump instruction should jump to
- * LABEL2 directly
- */
- replace_destination(iobj, diobj);
- remove_unreachable_chunk(iseq, iobj->link.next);
- goto again;
- }
+ /*
+ * useless jump elimination:
+ * jump LABEL1
+ * ...
+ * LABEL1:
+ * jump LABEL2
+ *
+ * => in this case, first jump instruction should jump to
+ * LABEL2 directly
+ */
+ replace_destination(iobj, diobj);
+ remove_unreachable_chunk(iseq, iobj->link.next);
+ goto again;
+ }
else if (IS_INSN_ID(diobj, leave)) {
- /*
- * jump LABEL
- * ...
- * LABEL:
- * leave
- * =>
- * leave
- * ...
- * LABEL:
- * leave
- */
- /* replace */
- unref_destination(iobj, 0);
+ /*
+ * jump LABEL
+ * ...
+ * LABEL:
+ * leave
+ * =>
+ * leave
+ * ...
+ * LABEL:
+ * leave
+ */
+ /* replace */
+ unref_destination(iobj, 0);
iobj->insn_id = BIN(leave);
- iobj->operand_size = 0;
- iobj->insn_info = diobj->insn_info;
- goto again;
- }
+ iobj->operand_size = 0;
+ iobj->insn_info = diobj->insn_info;
+ goto again;
+ }
else if (IS_INSN(iobj->link.prev) &&
(piobj = (INSN *)iobj->link.prev) &&
- (IS_INSN_ID(piobj, branchif) ||
- IS_INSN_ID(piobj, branchunless))) {
- INSN *pdiobj = (INSN *)get_destination_insn(piobj);
- if (niobj == pdiobj) {
- int refcnt = IS_LABEL(piobj->link.next) ?
- ((LABEL *)piobj->link.next)->refcnt : 0;
- /*
- * useless jump elimination (if/unless destination):
- * if L1
- * jump L2
- * L1:
- * ...
- * L2:
- *
- * ==>
- * unless L2
- * L1:
- * ...
- * L2:
- */
- piobj->insn_id = (IS_INSN_ID(piobj, branchif))
- ? BIN(branchunless) : BIN(branchif);
- replace_destination(piobj, iobj);
- if (refcnt <= 1) {
- ELEM_REMOVE(&iobj->link);
- }
- else {
- /* TODO: replace other branch destinations too */
- }
- return COMPILE_OK;
- }
- else if (diobj == pdiobj) {
- /*
- * useless jump elimination (if/unless before jump):
- * L1:
- * ...
- * if L1
- * jump L1
- *
- * ==>
- * L1:
- * ...
- * pop
- * jump L1
- */
+ (IS_INSN_ID(piobj, branchif) ||
+ IS_INSN_ID(piobj, branchunless))) {
+ INSN *pdiobj = (INSN *)get_destination_insn(piobj);
+ if (niobj == pdiobj) {
+ int refcnt = IS_LABEL(piobj->link.next) ?
+ ((LABEL *)piobj->link.next)->refcnt : 0;
+ /*
+ * useless jump elimination (if/unless destination):
+ * if L1
+ * jump L2
+ * L1:
+ * ...
+ * L2:
+ *
+ * ==>
+ * unless L2
+ * L1:
+ * ...
+ * L2:
+ */
+ piobj->insn_id = (IS_INSN_ID(piobj, branchif))
+ ? BIN(branchunless) : BIN(branchif);
+ replace_destination(piobj, iobj);
+ if (refcnt <= 1) {
+ ELEM_REMOVE(&iobj->link);
+ }
+ else {
+ /* TODO: replace other branch destinations too */
+ }
+ return COMPILE_OK;
+ }
+ else if (diobj == pdiobj) {
+ /*
+ * useless jump elimination (if/unless before jump):
+ * L1:
+ * ...
+ * if L1
+ * jump L1
+ *
+ * ==>
+ * L1:
+ * ...
+ * pop
+ * jump L1
+ */
NODE dummy_line_node = generate_dummy_line_node(iobj->insn_info.line_no, iobj->insn_info.node_id);
- INSN *popiobj = new_insn_core(iseq, &dummy_line_node, BIN(pop), 0, 0);
- ELEM_REPLACE(&piobj->link, &popiobj->link);
- }
- }
- if (remove_unreachable_chunk(iseq, iobj->link.next)) {
- goto again;
- }
+ INSN *popiobj = new_insn_core(iseq, &dummy_line_node, BIN(pop), 0, 0);
+ ELEM_REPLACE(&piobj->link, &popiobj->link);
+ }
+ }
+ if (remove_unreachable_chunk(iseq, iobj->link.next)) {
+ goto again;
+ }
}
/*
@@ -3091,19 +3157,19 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
is_frozen_putstring(end, &str_end) &&
(beg = (INSN *)get_prev_insn(end)) != 0 &&
is_frozen_putstring(beg, &str_beg)) {
- int excl = FIX2INT(OPERAND_AT(range, 0));
- VALUE lit_range = rb_range_new(str_beg, str_end, excl);
+ int excl = FIX2INT(OPERAND_AT(range, 0));
+ VALUE lit_range = rb_range_new(str_beg, str_end, excl);
- ELEM_REMOVE(&beg->link);
- ELEM_REMOVE(&end->link);
- range->insn_id = BIN(putobject);
- OPERAND_AT(range, 0) = lit_range;
- RB_OBJ_WRITTEN(iseq, Qundef, lit_range);
- }
+ ELEM_REMOVE(&beg->link);
+ ELEM_REMOVE(&end->link);
+ range->insn_id = BIN(putobject);
+ OPERAND_AT(range, 0) = lit_range;
+ RB_OBJ_WRITTEN(iseq, Qundef, lit_range);
+ }
}
if (IS_INSN_ID(iobj, leave)) {
- remove_unreachable_chunk(iseq, iobj->link.next);
+ remove_unreachable_chunk(iseq, iobj->link.next);
}
/*
@@ -3123,17 +3189,17 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
if (IS_INSN_ID(iobj, branchif) ||
- IS_INSN_ID(iobj, branchnil) ||
- IS_INSN_ID(iobj, branchunless)) {
- /*
- * if L1
- * ...
- * L1:
- * jump L2
- * =>
- * if L2
- */
- INSN *nobj = (INSN *)get_destination_insn(iobj);
+ IS_INSN_ID(iobj, branchnil) ||
+ IS_INSN_ID(iobj, branchunless)) {
+ /*
+ * if L1
+ * ...
+ * L1:
+ * jump L2
+ * =>
+ * if L2
+ */
+ INSN *nobj = (INSN *)get_destination_insn(iobj);
/* This is super nasty hack!!!
*
@@ -3156,10 +3222,10 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
* This should be fixed in future.
*/
int stop_optimization =
- ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq) &&
+ ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq) &&
nobj->link.type == ISEQ_ELEMENT_INSN &&
nobj->insn_info.events;
- if (!stop_optimization) {
+ if (!stop_optimization) {
INSN *pobj = (INSN *)iobj->link.prev;
int prev_dup = 0;
if (pobj) {
@@ -3269,231 +3335,344 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
if (IS_INSN_ID(iobj, pop)) {
- /*
- * putself / putnil / putobject obj / putstring "..."
- * pop
- * =>
- * # do nothing
- */
- LINK_ELEMENT *prev = iobj->link.prev;
- if (IS_INSN(prev)) {
- enum ruby_vminsn_type previ = ((INSN *)prev)->insn_id;
- if (previ == BIN(putobject) || previ == BIN(putnil) ||
- previ == BIN(putself) || previ == BIN(putstring) ||
- previ == BIN(dup) ||
- previ == BIN(getlocal) ||
- previ == BIN(getblockparam) ||
- previ == BIN(getblockparamproxy) ||
- /* getinstancevariable may issue a warning */
- previ == BIN(duparray)) {
- /* just push operand or static value and pop soon, no
- * side effects */
- ELEM_REMOVE(prev);
- ELEM_REMOVE(&iobj->link);
- }
- else if (previ == BIN(newarray) && iseq_pop_newarray(iseq, (INSN*)prev)) {
- ELEM_REMOVE(&iobj->link);
- }
- else if (previ == BIN(concatarray)) {
- INSN *piobj = (INSN *)prev;
+ /*
+ * putself / putnil / putobject obj / putstring "..."
+ * pop
+ * =>
+ * # do nothing
+ */
+ LINK_ELEMENT *prev = iobj->link.prev;
+ if (IS_INSN(prev)) {
+ enum ruby_vminsn_type previ = ((INSN *)prev)->insn_id;
+ if (previ == BIN(putobject) || previ == BIN(putnil) ||
+ previ == BIN(putself) || previ == BIN(putstring) ||
+ previ == BIN(dup) ||
+ previ == BIN(getlocal) ||
+ previ == BIN(getblockparam) ||
+ previ == BIN(getblockparamproxy) ||
+ /* getinstancevariable may issue a warning */
+ previ == BIN(duparray)) {
+ /* just push operand or static value and pop soon, no
+ * side effects */
+ ELEM_REMOVE(prev);
+ ELEM_REMOVE(&iobj->link);
+ }
+ else if (previ == BIN(newarray) && iseq_pop_newarray(iseq, (INSN*)prev)) {
+ ELEM_REMOVE(&iobj->link);
+ }
+ else if (previ == BIN(concatarray)) {
+ INSN *piobj = (INSN *)prev;
NODE dummy_line_node = generate_dummy_line_node(piobj->insn_info.line_no, piobj->insn_info.node_id);
- INSERT_BEFORE_INSN1(piobj, &dummy_line_node, splatarray, Qfalse);
- INSN_OF(piobj) = BIN(pop);
- }
- else if (previ == BIN(concatstrings)) {
- if (OPERAND_AT(prev, 0) == INT2FIX(1)) {
- ELEM_REMOVE(prev);
- }
- else {
- ELEM_REMOVE(&iobj->link);
- INSN_OF(prev) = BIN(adjuststack);
- }
- }
- }
+ INSERT_BEFORE_INSN1(piobj, &dummy_line_node, splatarray, Qfalse);
+ INSN_OF(piobj) = BIN(pop);
+ }
+ else if (previ == BIN(concatstrings)) {
+ if (OPERAND_AT(prev, 0) == INT2FIX(1)) {
+ ELEM_REMOVE(prev);
+ }
+ else {
+ ELEM_REMOVE(&iobj->link);
+ INSN_OF(prev) = BIN(adjuststack);
+ }
+ }
+ }
}
if (IS_INSN_ID(iobj, newarray) ||
- IS_INSN_ID(iobj, duparray) ||
- IS_INSN_ID(iobj, expandarray) ||
- IS_INSN_ID(iobj, concatarray) ||
- IS_INSN_ID(iobj, splatarray) ||
- 0) {
- /*
- * newarray N
- * splatarray
- * =>
- * newarray N
- * newarray always puts an array
- */
- LINK_ELEMENT *next = iobj->link.next;
- if (IS_INSN(next) && IS_INSN_ID(next, splatarray)) {
- /* remove splatarray following always-array insn */
- ELEM_REMOVE(next);
- }
+ IS_INSN_ID(iobj, duparray) ||
+ IS_INSN_ID(iobj, expandarray) ||
+ IS_INSN_ID(iobj, concatarray) ||
+ IS_INSN_ID(iobj, splatarray) ||
+ 0) {
+ /*
+ * newarray N
+ * splatarray
+ * =>
+ * newarray N
+ * newarray always puts an array
+ */
+ LINK_ELEMENT *next = iobj->link.next;
+ if (IS_INSN(next) && IS_INSN_ID(next, splatarray)) {
+ /* remove splatarray following always-array insn */
+ ELEM_REMOVE(next);
+ }
+ }
+
+ if (IS_INSN_ID(iobj, newarray)) {
+ LINK_ELEMENT *next = iobj->link.next;
+ if (IS_INSN(next) && IS_INSN_ID(next, expandarray) &&
+ OPERAND_AT(next, 1) == INT2FIX(0)) {
+ VALUE op1, op2;
+ op1 = OPERAND_AT(iobj, 0);
+ op2 = OPERAND_AT(next, 0);
+ ELEM_REMOVE(next);
+
+ if (op1 == op2) {
+ /*
+ * newarray 2
+ * expandarray 2, 0
+ * =>
+ * swap
+ */
+ if (op1 == INT2FIX(2)) {
+ INSN_OF(iobj) = BIN(swap);
+ iobj->operand_size = 0;
+ }
+ /*
+ * newarray X
+ * expandarray X, 0
+ * =>
+ * opt_reverse X
+ */
+ else {
+ INSN_OF(iobj) = BIN(opt_reverse);
+ }
+ }
+ else {
+ NODE dummy_line_node = generate_dummy_line_node(iobj->insn_info.line_no, iobj->insn_info.node_id);
+ long diff = FIX2LONG(op1) - FIX2LONG(op2);
+ INSN_OF(iobj) = BIN(opt_reverse);
+ OPERAND_AT(iobj, 0) = OPERAND_AT(next, 0);
+
+ if (op1 > op2) {
+ /* X > Y
+ * newarray X
+ * expandarray Y, 0
+ * =>
+ * pop * (Y-X)
+ * opt_reverse Y
+ */
+ for (; diff > 0; diff--) {
+ INSERT_BEFORE_INSN(iobj, &dummy_line_node, pop);
+ }
+ }
+ else { /* (op1 < op2) */
+ /* X < Y
+ * newarray X
+ * expandarray Y, 0
+ * =>
+ * putnil * (Y-X)
+ * opt_reverse Y
+ */
+ for (; diff < 0; diff++) {
+ INSERT_BEFORE_INSN(iobj, &dummy_line_node, putnil);
+ }
+ }
+ }
+ }
+ }
+
+ if (IS_INSN_ID(iobj, duparray)) {
+ LINK_ELEMENT *next = iobj->link.next;
+ /*
+ * duparray obj
+ * expandarray X, 0
+ * =>
+ * putobject obj
+ * expandarray X, 0
+ */
+ if (IS_INSN(next) && IS_INSN_ID(next, expandarray)) {
+ INSN_OF(iobj) = BIN(putobject);
+ }
}
if (IS_INSN_ID(iobj, anytostring)) {
- LINK_ELEMENT *next = iobj->link.next;
- /*
+ LINK_ELEMENT *next = iobj->link.next;
+ /*
* anytostring
- * concatstrings 1
- * =>
+ * concatstrings 1
+ * =>
* anytostring
- */
- if (IS_INSN(next) && IS_INSN_ID(next, concatstrings) &&
- OPERAND_AT(next, 0) == INT2FIX(1)) {
- ELEM_REMOVE(next);
- }
+ */
+ if (IS_INSN(next) && IS_INSN_ID(next, concatstrings) &&
+ OPERAND_AT(next, 0) == INT2FIX(1)) {
+ ELEM_REMOVE(next);
+ }
}
if (IS_INSN_ID(iobj, putstring) ||
- (IS_INSN_ID(iobj, putobject) && RB_TYPE_P(OPERAND_AT(iobj, 0), T_STRING))) {
- /*
- * putstring ""
- * concatstrings N
- * =>
- * concatstrings N-1
- */
- if (IS_NEXT_INSN_ID(&iobj->link, concatstrings) &&
- RSTRING_LEN(OPERAND_AT(iobj, 0)) == 0) {
- INSN *next = (INSN *)iobj->link.next;
- if ((OPERAND_AT(next, 0) = FIXNUM_INC(OPERAND_AT(next, 0), -1)) == INT2FIX(1)) {
- ELEM_REMOVE(&next->link);
- }
- ELEM_REMOVE(&iobj->link);
- }
+ (IS_INSN_ID(iobj, putobject) && RB_TYPE_P(OPERAND_AT(iobj, 0), T_STRING))) {
+ /*
+ * putstring ""
+ * concatstrings N
+ * =>
+ * concatstrings N-1
+ */
+ if (IS_NEXT_INSN_ID(&iobj->link, concatstrings) &&
+ RSTRING_LEN(OPERAND_AT(iobj, 0)) == 0) {
+ INSN *next = (INSN *)iobj->link.next;
+ if ((OPERAND_AT(next, 0) = FIXNUM_INC(OPERAND_AT(next, 0), -1)) == INT2FIX(1)) {
+ ELEM_REMOVE(&next->link);
+ }
+ ELEM_REMOVE(&iobj->link);
+ }
}
if (IS_INSN_ID(iobj, concatstrings)) {
- /*
- * concatstrings N
- * concatstrings M
- * =>
- * concatstrings N+M-1
- */
- LINK_ELEMENT *next = iobj->link.next;
- INSN *jump = 0;
- if (IS_INSN(next) && IS_INSN_ID(next, jump))
- next = get_destination_insn(jump = (INSN *)next);
- if (IS_INSN(next) && IS_INSN_ID(next, concatstrings)) {
- int n = FIX2INT(OPERAND_AT(iobj, 0)) + FIX2INT(OPERAND_AT(next, 0)) - 1;
- OPERAND_AT(iobj, 0) = INT2FIX(n);
- if (jump) {
- LABEL *label = ((LABEL *)OPERAND_AT(jump, 0));
- if (!--label->refcnt) {
- ELEM_REMOVE(&label->link);
- }
- else {
- label = NEW_LABEL(0);
- OPERAND_AT(jump, 0) = (VALUE)label;
- }
- label->refcnt++;
- ELEM_INSERT_NEXT(next, &label->link);
- CHECK(iseq_peephole_optimize(iseq, get_next_insn(jump), do_tailcallopt));
- }
- else {
- ELEM_REMOVE(next);
- }
- }
+ /*
+ * concatstrings N
+ * concatstrings M
+ * =>
+ * concatstrings N+M-1
+ */
+ LINK_ELEMENT *next = iobj->link.next;
+ INSN *jump = 0;
+ if (IS_INSN(next) && IS_INSN_ID(next, jump))
+ next = get_destination_insn(jump = (INSN *)next);
+ if (IS_INSN(next) && IS_INSN_ID(next, concatstrings)) {
+ int n = FIX2INT(OPERAND_AT(iobj, 0)) + FIX2INT(OPERAND_AT(next, 0)) - 1;
+ OPERAND_AT(iobj, 0) = INT2FIX(n);
+ if (jump) {
+ LABEL *label = ((LABEL *)OPERAND_AT(jump, 0));
+ if (!--label->refcnt) {
+ ELEM_REMOVE(&label->link);
+ }
+ else {
+ label = NEW_LABEL(0);
+ OPERAND_AT(jump, 0) = (VALUE)label;
+ }
+ label->refcnt++;
+ ELEM_INSERT_NEXT(next, &label->link);
+ CHECK(iseq_peephole_optimize(iseq, get_next_insn(jump), do_tailcallopt));
+ }
+ else {
+ ELEM_REMOVE(next);
+ }
+ }
}
if (do_tailcallopt &&
- (IS_INSN_ID(iobj, send) ||
- IS_INSN_ID(iobj, opt_aref_with) ||
- IS_INSN_ID(iobj, opt_aset_with) ||
- IS_INSN_ID(iobj, invokesuper))) {
- /*
- * send ...
- * leave
- * =>
- * send ..., ... | VM_CALL_TAILCALL, ...
- * leave # unreachable
- */
- INSN *piobj = NULL;
- if (iobj->link.next) {
- LINK_ELEMENT *next = iobj->link.next;
- do {
- if (!IS_INSN(next)) {
- next = next->next;
- continue;
- }
- switch (INSN_OF(next)) {
- case BIN(nop):
- next = next->next;
- break;
- case BIN(jump):
- /* if cond
- * return tailcall
- * end
- */
- next = get_destination_insn((INSN *)next);
- break;
- case BIN(leave):
- piobj = iobj;
+ (IS_INSN_ID(iobj, send) ||
+ IS_INSN_ID(iobj, opt_aref_with) ||
+ IS_INSN_ID(iobj, opt_aset_with) ||
+ IS_INSN_ID(iobj, invokesuper))) {
+ /*
+ * send ...
+ * leave
+ * =>
+ * send ..., ... | VM_CALL_TAILCALL, ...
+ * leave # unreachable
+ */
+ INSN *piobj = NULL;
+ if (iobj->link.next) {
+ LINK_ELEMENT *next = iobj->link.next;
+ do {
+ if (!IS_INSN(next)) {
+ next = next->next;
+ continue;
+ }
+ switch (INSN_OF(next)) {
+ case BIN(nop):
+ next = next->next;
+ break;
+ case BIN(jump):
+ /* if cond
+ * return tailcall
+ * end
+ */
+ next = get_destination_insn((INSN *)next);
+ break;
+ case BIN(leave):
+ piobj = iobj;
/* fall through */
- default:
- next = NULL;
- break;
- }
- } while (next);
- }
-
- if (piobj) {
+ default:
+ next = NULL;
+ break;
+ }
+ } while (next);
+ }
+
+ if (piobj) {
const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(piobj, 0);
- if (IS_INSN_ID(piobj, send) ||
+ if (IS_INSN_ID(piobj, send) ||
IS_INSN_ID(piobj, invokesuper)) {
if (OPERAND_AT(piobj, 1) == 0) { /* no blockiseq */
ci = ci_flag_set(iseq, ci, VM_CALL_TAILCALL);
OPERAND_AT(piobj, 0) = (VALUE)ci;
RB_OBJ_WRITTEN(iseq, Qundef, ci);
- }
- }
- else {
+ }
+ }
+ else {
ci = ci_flag_set(iseq, ci, VM_CALL_TAILCALL);
OPERAND_AT(piobj, 0) = (VALUE)ci;
RB_OBJ_WRITTEN(iseq, Qundef, ci);
- }
- }
+ }
+ }
}
if (IS_INSN_ID(iobj, dup)) {
- if (IS_NEXT_INSN_ID(&iobj->link, setlocal)) {
- LINK_ELEMENT *set1 = iobj->link.next, *set2 = NULL;
- if (IS_NEXT_INSN_ID(set1, setlocal)) {
- set2 = set1->next;
- if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
- OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
- ELEM_REMOVE(set1);
- ELEM_REMOVE(&iobj->link);
- }
- }
- else if (IS_NEXT_INSN_ID(set1, dup) &&
- IS_NEXT_INSN_ID(set1->next, setlocal)) {
- set2 = set1->next->next;
- if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
- OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
- ELEM_REMOVE(set1->next);
- ELEM_REMOVE(set2);
- }
- }
- }
+ if (IS_NEXT_INSN_ID(&iobj->link, setlocal)) {
+ LINK_ELEMENT *set1 = iobj->link.next, *set2 = NULL;
+
+ /*
+ * dup
+ * setlocal x, y
+ * setlocal x, y
+ * =>
+ * dup
+ * setlocal x, y
+ */
+ if (IS_NEXT_INSN_ID(set1, setlocal)) {
+ set2 = set1->next;
+ if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
+ OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
+ ELEM_REMOVE(set1);
+ ELEM_REMOVE(&iobj->link);
+ }
+ }
+
+ /*
+ * dup
+ * setlocal x, y
+ * dup
+ * setlocal x, y
+ * =>
+ * dup
+ * setlocal x, y
+ */
+ else if (IS_NEXT_INSN_ID(set1, dup) &&
+ IS_NEXT_INSN_ID(set1->next, setlocal)) {
+ set2 = set1->next->next;
+ if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
+ OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
+ ELEM_REMOVE(set1->next);
+ ELEM_REMOVE(set2);
+ }
+ }
+ }
}
+ /*
+ * getlocal x, y
+ * dup
+ * setlocal x, y
+ * =>
+ * dup
+ */
if (IS_INSN_ID(iobj, getlocal)) {
- LINK_ELEMENT *niobj = &iobj->link;
- if (IS_NEXT_INSN_ID(niobj, dup)) {
- niobj = niobj->next;
- }
- if (IS_NEXT_INSN_ID(niobj, setlocal)) {
- LINK_ELEMENT *set1 = niobj->next;
- if (OPERAND_AT(iobj, 0) == OPERAND_AT(set1, 0) &&
- OPERAND_AT(iobj, 1) == OPERAND_AT(set1, 1)) {
- ELEM_REMOVE(set1);
- ELEM_REMOVE(niobj);
- }
- }
+ LINK_ELEMENT *niobj = &iobj->link;
+ if (IS_NEXT_INSN_ID(niobj, dup)) {
+ niobj = niobj->next;
+ }
+ if (IS_NEXT_INSN_ID(niobj, setlocal)) {
+ LINK_ELEMENT *set1 = niobj->next;
+ if (OPERAND_AT(iobj, 0) == OPERAND_AT(set1, 0) &&
+ OPERAND_AT(iobj, 1) == OPERAND_AT(set1, 1)) {
+ ELEM_REMOVE(set1);
+ ELEM_REMOVE(niobj);
+ }
+ }
}
+ /*
+ * opt_invokebuiltin_delegate
+ * trace
+ * leave
+ * =>
+ * opt_invokebuiltin_delegate_leave
+ * trace
+ * leave
+ */
if (IS_INSN_ID(iobj, opt_invokebuiltin_delegate)) {
if (IS_TRACE(iobj->link.next)) {
if (IS_NEXT_INSN_ID(iobj->link.next, leave)) {
@@ -3502,6 +3681,19 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
}
+ /*
+ * getblockparam
+ * branchif / branchunless
+ * =>
+ * getblockparamproxy
+ * branchif / branchunless
+ */
+ if (IS_INSN_ID(iobj, getblockparam)) {
+ if (IS_NEXT_INSN_ID(&iobj->link, branchif) || IS_NEXT_INSN_ID(&iobj->link, branchunless)) {
+ iobj->insn_id = BIN(getblockparamproxy);
+ }
+ }
+
return COMPILE_OK;
}
@@ -3527,26 +3719,26 @@ static int
iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
{
if (IS_INSN_ID(iobj, newarray) && iobj->link.next &&
- IS_INSN(iobj->link.next)) {
- /*
- * [a, b, ...].max/min -> a, b, c, opt_newarray_max/min
- */
- INSN *niobj = (INSN *)iobj->link.next;
- if (IS_INSN_ID(niobj, send)) {
+ IS_INSN(iobj->link.next)) {
+ /*
+ * [a, b, ...].max/min -> a, b, c, opt_newarray_max/min
+ */
+ INSN *niobj = (INSN *)iobj->link.next;
+ if (IS_INSN_ID(niobj, send)) {
const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(niobj, 0);
if ((vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) && vm_ci_argc(ci) == 0) {
- switch (vm_ci_mid(ci)) {
- case idMax:
- iobj->insn_id = BIN(opt_newarray_max);
- ELEM_REMOVE(&niobj->link);
- return COMPILE_OK;
- case idMin:
- iobj->insn_id = BIN(opt_newarray_min);
- ELEM_REMOVE(&niobj->link);
- return COMPILE_OK;
- }
- }
- }
+ switch (vm_ci_mid(ci)) {
+ case idMax:
+ iobj->insn_id = BIN(opt_newarray_max);
+ ELEM_REMOVE(&niobj->link);
+ return COMPILE_OK;
+ case idMin:
+ iobj->insn_id = BIN(opt_newarray_min);
+ ELEM_REMOVE(&niobj->link);
+ return COMPILE_OK;
+ }
+ }
+ }
}
if (IS_INSN_ID(iobj, send)) {
@@ -3554,50 +3746,50 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(iobj, 1);
#define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt))
- if (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) {
- switch (vm_ci_argc(ci)) {
- case 0:
- switch (vm_ci_mid(ci)) {
- case idLength: SP_INSN(length); return COMPILE_OK;
- case idSize: SP_INSN(size); return COMPILE_OK;
- case idEmptyP: SP_INSN(empty_p);return COMPILE_OK;
+ if (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) {
+ switch (vm_ci_argc(ci)) {
+ case 0:
+ switch (vm_ci_mid(ci)) {
+ case idLength: SP_INSN(length); return COMPILE_OK;
+ case idSize: SP_INSN(size); return COMPILE_OK;
+ case idEmptyP: SP_INSN(empty_p);return COMPILE_OK;
case idNilP: SP_INSN(nil_p); return COMPILE_OK;
- case idSucc: SP_INSN(succ); return COMPILE_OK;
- case idNot: SP_INSN(not); return COMPILE_OK;
- }
- break;
- case 1:
- switch (vm_ci_mid(ci)) {
- case idPLUS: SP_INSN(plus); return COMPILE_OK;
- case idMINUS: SP_INSN(minus); return COMPILE_OK;
- case idMULT: SP_INSN(mult); return COMPILE_OK;
- case idDIV: SP_INSN(div); return COMPILE_OK;
- case idMOD: SP_INSN(mod); return COMPILE_OK;
- case idEq: SP_INSN(eq); return COMPILE_OK;
- case idNeq: SP_INSN(neq); return COMPILE_OK;
- case idEqTilde:SP_INSN(regexpmatch2);return COMPILE_OK;
- case idLT: SP_INSN(lt); return COMPILE_OK;
- case idLE: SP_INSN(le); return COMPILE_OK;
- case idGT: SP_INSN(gt); return COMPILE_OK;
- case idGE: SP_INSN(ge); return COMPILE_OK;
- case idLTLT: SP_INSN(ltlt); return COMPILE_OK;
- case idAREF: SP_INSN(aref); return COMPILE_OK;
+ case idSucc: SP_INSN(succ); return COMPILE_OK;
+ case idNot: SP_INSN(not); return COMPILE_OK;
+ }
+ break;
+ case 1:
+ switch (vm_ci_mid(ci)) {
+ case idPLUS: SP_INSN(plus); return COMPILE_OK;
+ case idMINUS: SP_INSN(minus); return COMPILE_OK;
+ case idMULT: SP_INSN(mult); return COMPILE_OK;
+ case idDIV: SP_INSN(div); return COMPILE_OK;
+ case idMOD: SP_INSN(mod); return COMPILE_OK;
+ case idEq: SP_INSN(eq); return COMPILE_OK;
+ case idNeq: SP_INSN(neq); return COMPILE_OK;
+ case idEqTilde:SP_INSN(regexpmatch2);return COMPILE_OK;
+ case idLT: SP_INSN(lt); return COMPILE_OK;
+ case idLE: SP_INSN(le); return COMPILE_OK;
+ case idGT: SP_INSN(gt); return COMPILE_OK;
+ case idGE: SP_INSN(ge); return COMPILE_OK;
+ case idLTLT: SP_INSN(ltlt); return COMPILE_OK;
+ case idAREF: SP_INSN(aref); return COMPILE_OK;
case idAnd: SP_INSN(and); return COMPILE_OK;
case idOr: SP_INSN(or); return COMPILE_OK;
- }
- break;
- case 2:
- switch (vm_ci_mid(ci)) {
- case idASET: SP_INSN(aset); return COMPILE_OK;
- }
- break;
- }
- }
-
- if ((vm_ci_flag(ci) & VM_CALL_ARGS_BLOCKARG) == 0 && blockiseq == NULL) {
- iobj->insn_id = BIN(opt_send_without_block);
- iobj->operand_size = insn_len(iobj->insn_id) - 1;
- }
+ }
+ break;
+ case 2:
+ switch (vm_ci_mid(ci)) {
+ case idASET: SP_INSN(aset); return COMPILE_OK;
+ }
+ break;
+ }
+ }
+
+ if ((vm_ci_flag(ci) & VM_CALL_ARGS_BLOCKARG) == 0 && blockiseq == NULL) {
+ iobj->insn_id = BIN(opt_send_without_block);
+ iobj->operand_size = insn_len(iobj->insn_id) - 1;
+ }
}
#undef SP_INSN
@@ -3611,13 +3803,13 @@ tailcallable_p(rb_iseq_t *iseq)
case ISEQ_TYPE_TOP:
case ISEQ_TYPE_EVAL:
case ISEQ_TYPE_MAIN:
- /* not tail callable because cfp will be over popped */
+ /* not tail callable because cfp will be over popped */
case ISEQ_TYPE_RESCUE:
case ISEQ_TYPE_ENSURE:
- /* rescue block can't tail call because of errinfo */
- return FALSE;
+ /* rescue block can't tail call because of errinfo */
+ return FALSE;
default:
- return TRUE;
+ return TRUE;
}
}
@@ -3627,7 +3819,7 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
LINK_ELEMENT *list;
const int do_peepholeopt = ISEQ_COMPILE_DATA(iseq)->option->peephole_optimization;
const int do_tailcallopt = tailcallable_p(iseq) &&
- ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization;
+ ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization;
const int do_si = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction;
const int do_ou = ISEQ_COMPILE_DATA(iseq)->option->operands_unification;
int rescue_level = 0;
@@ -3642,16 +3834,16 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
}
while (list) {
- if (IS_INSN(list)) {
- if (do_peepholeopt) {
- iseq_peephole_optimize(iseq, list, tailcallopt);
- }
- if (do_si) {
- iseq_specialized_instruction(iseq, (INSN *)list);
- }
- if (do_ou) {
- insn_operands_unification((INSN *)list);
- }
+ if (IS_INSN(list)) {
+ if (do_peepholeopt) {
+ iseq_peephole_optimize(iseq, list, tailcallopt);
+ }
+ if (do_si) {
+ iseq_specialized_instruction(iseq, (INSN *)list);
+ }
+ if (do_ou) {
+ insn_operands_unification((INSN *)list);
+ }
if (do_block_optimization) {
INSN * item = (INSN *)list;
@@ -3659,19 +3851,19 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
do_block_optimization = 0;
}
}
- }
- if (IS_LABEL(list)) {
- switch (((LABEL *)list)->rescued) {
- case LABEL_RESCUE_BEG:
- rescue_level++;
- tailcallopt = FALSE;
- break;
- case LABEL_RESCUE_END:
- if (!--rescue_level) tailcallopt = do_tailcallopt;
- break;
- }
- }
- list = list->next;
+ }
+ if (IS_LABEL(list)) {
+ switch (((LABEL *)list)->rescued) {
+ case LABEL_RESCUE_BEG:
+ rescue_level++;
+ tailcallopt = FALSE;
+ break;
+ case LABEL_RESCUE_END:
+ if (!--rescue_level) tailcallopt = do_tailcallopt;
+ break;
+ }
+ }
+ list = list->next;
}
if (do_block_optimization) {
@@ -3686,7 +3878,7 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
#if OPT_INSTRUCTIONS_UNIFICATION
static INSN *
new_unified_insn(rb_iseq_t *iseq,
- int insn_id, int size, LINK_ELEMENT *seq_list)
+ int insn_id, int size, LINK_ELEMENT *seq_list)
{
INSN *iobj = 0;
LINK_ELEMENT *list = seq_list;
@@ -3696,22 +3888,22 @@ new_unified_insn(rb_iseq_t *iseq,
/* count argc */
for (i = 0; i < size; i++) {
- iobj = (INSN *)list;
- argc += iobj->operand_size;
- list = list->next;
+ iobj = (INSN *)list;
+ argc += iobj->operand_size;
+ list = list->next;
}
if (argc > 0) {
- ptr = operands = compile_data_alloc2(iseq, sizeof(VALUE), argc);
+ ptr = operands = compile_data_alloc2(iseq, sizeof(VALUE), argc);
}
/* copy operands */
list = seq_list;
for (i = 0; i < size; i++) {
- iobj = (INSN *)list;
- MEMCPY(ptr, iobj->operands, VALUE, iobj->operand_size);
- ptr += iobj->operand_size;
- list = list->next;
+ iobj = (INSN *)list;
+ MEMCPY(ptr, iobj->operands, VALUE, iobj->operand_size);
+ ptr += iobj->operand_size;
+ list = list->next;
}
NODE dummy_line_node = generate_dummy_line_node(iobj->insn_info.line_no, iobj->insn_info.node_id);
@@ -3735,41 +3927,41 @@ iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
list = FIRST_ELEMENT(anchor);
while (list) {
- if (IS_INSN(list)) {
- iobj = (INSN *)list;
- id = iobj->insn_id;
- if (unified_insns_data[id] != 0) {
- const int *const *entry = unified_insns_data[id];
- for (j = 1; j < (intptr_t)entry[0]; j++) {
- const int *unified = entry[j];
- LINK_ELEMENT *li = list->next;
- for (k = 2; k < unified[1]; k++) {
- if (!IS_INSN(li) ||
- ((INSN *)li)->insn_id != unified[k]) {
- goto miss;
- }
- li = li->next;
- }
- /* matched */
- niobj =
- new_unified_insn(iseq, unified[0], unified[1] - 1,
- list);
-
- /* insert to list */
- niobj->link.prev = (LINK_ELEMENT *)iobj->link.prev;
- niobj->link.next = li;
- if (li) {
- li->prev = (LINK_ELEMENT *)niobj;
- }
-
- list->prev->next = (LINK_ELEMENT *)niobj;
- list = (LINK_ELEMENT *)niobj;
- break;
- miss:;
- }
- }
- }
- list = list->next;
+ if (IS_INSN(list)) {
+ iobj = (INSN *)list;
+ id = iobj->insn_id;
+ if (unified_insns_data[id] != 0) {
+ const int *const *entry = unified_insns_data[id];
+ for (j = 1; j < (intptr_t)entry[0]; j++) {
+ const int *unified = entry[j];
+ LINK_ELEMENT *li = list->next;
+ for (k = 2; k < unified[1]; k++) {
+ if (!IS_INSN(li) ||
+ ((INSN *)li)->insn_id != unified[k]) {
+ goto miss;
+ }
+ li = li->next;
+ }
+ /* matched */
+ niobj =
+ new_unified_insn(iseq, unified[0], unified[1] - 1,
+ list);
+
+ /* insert to list */
+ niobj->link.prev = (LINK_ELEMENT *)iobj->link.prev;
+ niobj->link.next = li;
+ if (li) {
+ li->prev = (LINK_ELEMENT *)niobj;
+ }
+
+ list->prev->next = (LINK_ELEMENT *)niobj;
+ list = (LINK_ELEMENT *)niobj;
+ break;
+ miss:;
+ }
+ }
+ }
+ list = list->next;
}
#endif
return COMPILE_OK;
@@ -3793,28 +3985,28 @@ insn_set_sc_state(rb_iseq_t *iseq, const LINK_ELEMENT *anchor, INSN *iobj, int s
nstate = SC_NEXT(iobj->insn_id);
if (insn_id == BIN(jump) ||
- insn_id == BIN(branchif) || insn_id == BIN(branchunless)) {
- LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
-
- if (lobj->sc_state != 0) {
- if (lobj->sc_state != nstate) {
- BADINSN_DUMP(anchor, iobj, lobj);
- COMPILE_ERROR(iseq, iobj->insn_info.line_no,
- "insn_set_sc_state error: %d at "LABEL_FORMAT
- ", %d expected\n",
- lobj->sc_state, lobj->label_no, nstate);
- return COMPILE_NG;
- }
- }
- else {
- lobj->sc_state = nstate;
- }
- if (insn_id == BIN(jump)) {
- nstate = SCS_XX;
- }
+ insn_id == BIN(branchif) || insn_id == BIN(branchunless)) {
+ LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
+
+ if (lobj->sc_state != 0) {
+ if (lobj->sc_state != nstate) {
+ BADINSN_DUMP(anchor, iobj, lobj);
+ COMPILE_ERROR(iseq, iobj->insn_info.line_no,
+ "insn_set_sc_state error: %d at "LABEL_FORMAT
+ ", %d expected\n",
+ lobj->sc_state, lobj->label_no, nstate);
+ return COMPILE_NG;
+ }
+ }
+ else {
+ lobj->sc_state = nstate;
+ }
+ if (insn_id == BIN(jump)) {
+ nstate = SCS_XX;
+ }
}
else if (insn_id == BIN(leave)) {
- nstate = SCS_XX;
+ nstate = SCS_XX;
}
return nstate;
@@ -3824,12 +4016,12 @@ static int
label_set_sc_state(LABEL *lobj, int state)
{
if (lobj->sc_state != 0) {
- if (lobj->sc_state != state) {
- state = lobj->sc_state;
- }
+ if (lobj->sc_state != state) {
+ state = lobj->sc_state;
+ }
}
else {
- lobj->sc_state = state;
+ lobj->sc_state = state;
}
return state;
@@ -3853,84 +4045,84 @@ iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
/* for each list element */
while (list) {
redo_point:
- switch (list->type) {
- case ISEQ_ELEMENT_INSN:
- {
- INSN *iobj = (INSN *)list;
- insn_id = iobj->insn_id;
-
- /* dump_disasm_list(list); */
-
- switch (insn_id) {
- case BIN(nop):
- {
- /* exception merge point */
- if (state != SCS_AX) {
+ switch (list->type) {
+ case ISEQ_ELEMENT_INSN:
+ {
+ INSN *iobj = (INSN *)list;
+ insn_id = iobj->insn_id;
+
+ /* dump_disasm_list(list); */
+
+ switch (insn_id) {
+ case BIN(nop):
+ {
+ /* exception merge point */
+ if (state != SCS_AX) {
NODE dummy_line_node = generate_dummy_line_node(0, -1);
- INSN *rpobj =
- new_insn_body(iseq, &dummy_line_node, BIN(reput), 0);
-
- /* replace this insn */
- ELEM_REPLACE(list, (LINK_ELEMENT *)rpobj);
- list = (LINK_ELEMENT *)rpobj;
- goto redo_point;
- }
- break;
- }
- case BIN(swap):
- {
- if (state == SCS_AB || state == SCS_BA) {
- state = (state == SCS_AB ? SCS_BA : SCS_AB);
-
- ELEM_REMOVE(list);
- list = list->next;
- goto redo_point;
- }
- break;
- }
- case BIN(pop):
- {
- switch (state) {
- case SCS_AX:
- case SCS_BX:
- state = SCS_XX;
- break;
- case SCS_AB:
- state = SCS_AX;
- break;
- case SCS_BA:
- state = SCS_BX;
- break;
- case SCS_XX:
- goto normal_insn;
- default:
- COMPILE_ERROR(iseq, iobj->insn_info.line_no,
- "unreachable");
- return COMPILE_NG;
- }
- /* remove useless pop */
- ELEM_REMOVE(list);
- list = list->next;
- goto redo_point;
- }
- default:;
- /* none */
- } /* end of switch */
- normal_insn:
- state = insn_set_sc_state(iseq, anchor, iobj, state);
- break;
- }
- case ISEQ_ELEMENT_LABEL:
- {
- LABEL *lobj;
- lobj = (LABEL *)list;
-
- state = label_set_sc_state(lobj, state);
- }
- default:
- break;
- }
- list = list->next;
+ INSN *rpobj =
+ new_insn_body(iseq, &dummy_line_node, BIN(reput), 0);
+
+ /* replace this insn */
+ ELEM_REPLACE(list, (LINK_ELEMENT *)rpobj);
+ list = (LINK_ELEMENT *)rpobj;
+ goto redo_point;
+ }
+ break;
+ }
+ case BIN(swap):
+ {
+ if (state == SCS_AB || state == SCS_BA) {
+ state = (state == SCS_AB ? SCS_BA : SCS_AB);
+
+ ELEM_REMOVE(list);
+ list = list->next;
+ goto redo_point;
+ }
+ break;
+ }
+ case BIN(pop):
+ {
+ switch (state) {
+ case SCS_AX:
+ case SCS_BX:
+ state = SCS_XX;
+ break;
+ case SCS_AB:
+ state = SCS_AX;
+ break;
+ case SCS_BA:
+ state = SCS_BX;
+ break;
+ case SCS_XX:
+ goto normal_insn;
+ default:
+ COMPILE_ERROR(iseq, iobj->insn_info.line_no,
+ "unreachable");
+ return COMPILE_NG;
+ }
+ /* remove useless pop */
+ ELEM_REMOVE(list);
+ list = list->next;
+ goto redo_point;
+ }
+ default:;
+ /* none */
+ } /* end of switch */
+ normal_insn:
+ state = insn_set_sc_state(iseq, anchor, iobj, state);
+ break;
+ }
+ case ISEQ_ELEMENT_LABEL:
+ {
+ LABEL *lobj;
+ lobj = (LABEL *)list;
+
+ state = label_set_sc_state(lobj, state);
+ }
+ default:
+ break;
+ }
+ list = list->next;
}
#endif
return COMPILE_OK;
@@ -3942,20 +4134,20 @@ all_string_result_p(const NODE *node)
if (!node) return FALSE;
switch (nd_type(node)) {
case NODE_STR: case NODE_DSTR:
- return TRUE;
+ return TRUE;
case NODE_IF: case NODE_UNLESS:
- if (!node->nd_body || !node->nd_else) return FALSE;
- if (all_string_result_p(node->nd_body))
- return all_string_result_p(node->nd_else);
- return FALSE;
+ if (!node->nd_body || !node->nd_else) return FALSE;
+ if (all_string_result_p(node->nd_body))
+ return all_string_result_p(node->nd_else);
+ return FALSE;
case NODE_AND: case NODE_OR:
- if (!node->nd_2nd)
- return all_string_result_p(node->nd_1st);
- if (!all_string_result_p(node->nd_1st))
- return FALSE;
- return all_string_result_p(node->nd_2nd);
+ if (!node->nd_2nd)
+ return all_string_result_p(node->nd_1st);
+ if (!all_string_result_p(node->nd_1st))
+ return FALSE;
+ return all_string_result_p(node->nd_2nd);
default:
- return FALSE;
+ return FALSE;
}
}
@@ -3969,35 +4161,35 @@ compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cons
debugp_param("nd_lit", lit);
if (!NIL_P(lit)) {
- cnt++;
- if (!RB_TYPE_P(lit, T_STRING)) {
- COMPILE_ERROR(ERROR_ARGS "dstr: must be string: %s",
- rb_builtin_type_name(TYPE(lit)));
- return COMPILE_NG;
- }
- lit = rb_fstring(lit);
- ADD_INSN1(ret, node, putobject, lit);
+ cnt++;
+ if (!RB_TYPE_P(lit, T_STRING)) {
+ COMPILE_ERROR(ERROR_ARGS "dstr: must be string: %s",
+ rb_builtin_type_name(TYPE(lit)));
+ return COMPILE_NG;
+ }
+ lit = rb_fstring(lit);
+ ADD_INSN1(ret, node, putobject, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit);
- if (RSTRING_LEN(lit) == 0) first_lit = LAST_ELEMENT(ret);
+ if (RSTRING_LEN(lit) == 0) first_lit = LAST_ELEMENT(ret);
}
while (list) {
- const NODE *const head = list->nd_head;
- if (nd_type_p(head, NODE_STR)) {
- lit = rb_fstring(head->nd_lit);
- ADD_INSN1(ret, head, putobject, lit);
+ const NODE *const head = list->nd_head;
+ if (nd_type_p(head, NODE_STR)) {
+ lit = rb_fstring(head->nd_lit);
+ ADD_INSN1(ret, head, putobject, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit);
- lit = Qnil;
- }
- else {
- CHECK(COMPILE(ret, "each string", head));
- }
- cnt++;
- list = list->nd_next;
+ lit = Qnil;
+ }
+ else {
+ CHECK(COMPILE(ret, "each string", head));
+ }
+ cnt++;
+ list = list->nd_next;
}
if (NIL_P(lit) && first_lit) {
- ELEM_REMOVE(first_lit);
- --cnt;
+ ELEM_REMOVE(first_lit);
+ --cnt;
}
*cntp = cnt;
@@ -4045,12 +4237,12 @@ compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
static int
compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int again,
- LABEL *then_label, LABEL *else_label)
+ LABEL *then_label, LABEL *else_label)
{
const int line = nd_line(node);
LABEL *lend = NEW_LABEL(line);
rb_num_t cnt = ISEQ_FLIP_CNT_INCREMENT(ISEQ_BODY(iseq)->local_iseq)
- + VM_SVAR_FLIPFLOP_START;
+ + VM_SVAR_FLIPFLOP_START;
VALUE key = INT2FIX(cnt);
ADD_INSN2(ret, node, getspecial, key, INT2FIX(0));
@@ -4062,7 +4254,7 @@ compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const nod
ADD_INSN1(ret, node, putobject, Qtrue);
ADD_INSN1(ret, node, setspecial, key);
if (!again) {
- ADD_INSNL(ret, node, jump, then_label);
+ ADD_INSNL(ret, node, jump, then_label);
}
/* *flip == 1 */
@@ -4078,67 +4270,67 @@ compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const nod
static int
compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond,
- LABEL *then_label, LABEL *else_label)
+ LABEL *then_label, LABEL *else_label)
{
again:
switch (nd_type(cond)) {
case NODE_AND:
- {
- LABEL *label = NEW_LABEL(nd_line(cond));
- CHECK(compile_branch_condition(iseq, ret, cond->nd_1st, label,
- else_label));
+ {
+ LABEL *label = NEW_LABEL(nd_line(cond));
+ CHECK(compile_branch_condition(iseq, ret, cond->nd_1st, label,
+ else_label));
if (!label->refcnt) {
ADD_INSN(ret, cond, putnil);
break;
}
- ADD_LABEL(ret, label);
- cond = cond->nd_2nd;
- goto again;
- }
+ ADD_LABEL(ret, label);
+ cond = cond->nd_2nd;
+ goto again;
+ }
case NODE_OR:
- {
- LABEL *label = NEW_LABEL(nd_line(cond));
- CHECK(compile_branch_condition(iseq, ret, cond->nd_1st, then_label,
- label));
+ {
+ LABEL *label = NEW_LABEL(nd_line(cond));
+ CHECK(compile_branch_condition(iseq, ret, cond->nd_1st, then_label,
+ label));
if (!label->refcnt) {
ADD_INSN(ret, cond, putnil);
break;
}
- ADD_LABEL(ret, label);
- cond = cond->nd_2nd;
- goto again;
- }
+ ADD_LABEL(ret, label);
+ cond = cond->nd_2nd;
+ goto again;
+ }
case NODE_LIT: /* NODE_LIT is always true */
case NODE_TRUE:
case NODE_STR:
case NODE_ZLIST:
case NODE_LAMBDA:
- /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
- ADD_INSNL(ret, cond, jump, then_label);
+ /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
+ ADD_INSNL(ret, cond, jump, then_label);
return COMPILE_OK;
case NODE_FALSE:
case NODE_NIL:
- /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
- ADD_INSNL(ret, cond, jump, else_label);
+ /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
+ ADD_INSNL(ret, cond, jump, else_label);
return COMPILE_OK;
case NODE_LIST:
case NODE_ARGSCAT:
case NODE_DREGX:
case NODE_DSTR:
- CHECK(COMPILE_POPPED(ret, "branch condition", cond));
- ADD_INSNL(ret, cond, jump, then_label);
+ CHECK(COMPILE_POPPED(ret, "branch condition", cond));
+ ADD_INSNL(ret, cond, jump, then_label);
return COMPILE_OK;
case NODE_FLIP2:
- CHECK(compile_flip_flop(iseq, ret, cond, TRUE, then_label, else_label));
+ CHECK(compile_flip_flop(iseq, ret, cond, TRUE, then_label, else_label));
return COMPILE_OK;
case NODE_FLIP3:
- CHECK(compile_flip_flop(iseq, ret, cond, FALSE, then_label, else_label));
+ CHECK(compile_flip_flop(iseq, ret, cond, FALSE, then_label, else_label));
return COMPILE_OK;
case NODE_DEFINED:
- CHECK(compile_defined_expr(iseq, ret, cond, Qfalse));
+ CHECK(compile_defined_expr(iseq, ret, cond, Qfalse));
break;
default:
- CHECK(COMPILE(ret, "branch condition", cond));
+ CHECK(COMPILE(ret, "branch condition", cond));
break;
}
@@ -4157,25 +4349,25 @@ keyword_node_p(const NODE *const node)
static int
compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
- const NODE *const root_node,
- struct rb_callinfo_kwarg **const kw_arg_ptr,
- unsigned int *flag)
+ const NODE *const root_node,
+ struct rb_callinfo_kwarg **const kw_arg_ptr,
+ unsigned int *flag)
{
if (kw_arg_ptr == NULL) return FALSE;
if (root_node->nd_head && nd_type_p(root_node->nd_head, NODE_LIST)) {
- const NODE *node = root_node->nd_head;
+ const NODE *node = root_node->nd_head;
int seen_nodes = 0;
- while (node) {
- const NODE *key_node = node->nd_head;
+ while (node) {
+ const NODE *key_node = node->nd_head;
seen_nodes++;
- assert(nd_type_p(node, NODE_LIST));
+ assert(nd_type_p(node, NODE_LIST));
if (key_node && nd_type_p(key_node, NODE_LIT) && SYMBOL_P(key_node->nd_lit)) {
- /* can be keywords */
- }
- else {
+ /* can be keywords */
+ }
+ else {
if (flag) {
*flag |= VM_CALL_KW_SPLAT;
if (seen_nodes > 1 || node->nd_next->nd_next) {
@@ -4186,33 +4378,33 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
*flag |= VM_CALL_KW_SPLAT_MUT;
}
}
- return FALSE;
- }
- node = node->nd_next; /* skip value node */
- node = node->nd_next;
- }
-
- /* may be keywords */
- node = root_node->nd_head;
- {
- int len = (int)node->nd_alen / 2;
+ return FALSE;
+ }
+ node = node->nd_next; /* skip value node */
+ node = node->nd_next;
+ }
+
+ /* may be keywords */
+ node = root_node->nd_head;
+ {
+ int len = (int)node->nd_alen / 2;
struct rb_callinfo_kwarg *kw_arg =
rb_xmalloc_mul_add(len, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
- VALUE *keywords = kw_arg->keywords;
- int i = 0;
- kw_arg->keyword_len = len;
+ VALUE *keywords = kw_arg->keywords;
+ int i = 0;
+ kw_arg->keyword_len = len;
- *kw_arg_ptr = kw_arg;
+ *kw_arg_ptr = kw_arg;
- for (i=0; node != NULL; i++, node = node->nd_next->nd_next) {
- const NODE *key_node = node->nd_head;
- const NODE *val_node = node->nd_next->nd_head;
- keywords[i] = key_node->nd_lit;
+ for (i=0; node != NULL; i++, node = node->nd_next->nd_next) {
+ const NODE *key_node = node->nd_head;
+ const NODE *val_node = node->nd_next->nd_head;
+ keywords[i] = key_node->nd_lit;
NO_CHECK(COMPILE(ret, "keyword values", val_node));
- }
- assert(i == len);
- return TRUE;
- }
+ }
+ assert(i == len);
+ return TRUE;
+ }
}
return FALSE;
}
@@ -4252,11 +4444,11 @@ static_literal_node_p(const NODE *node, const rb_iseq_t *iseq)
case NODE_NIL:
case NODE_TRUE:
case NODE_FALSE:
- return TRUE;
+ return TRUE;
case NODE_STR:
return ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal;
default:
- return FALSE;
+ return FALSE;
}
}
@@ -4265,11 +4457,11 @@ static_literal_value(const NODE *node, rb_iseq_t *iseq)
{
switch (nd_type(node)) {
case NODE_NIL:
- return Qnil;
+ return Qnil;
case NODE_TRUE:
- return Qtrue;
+ return Qtrue;
case NODE_FALSE:
- return Qfalse;
+ return Qfalse;
case NODE_STR:
if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
VALUE lit;
@@ -4282,7 +4474,7 @@ static_literal_value(const NODE *node, rb_iseq_t *iseq)
return rb_fstring(node->nd_lit);
}
default:
- return node->nd_lit;
+ return node->nd_lit;
}
}
@@ -4292,9 +4484,9 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop
const NODE *line_node = node;
if (nd_type_p(node, NODE_ZLIST)) {
- if (!popped) {
- ADD_INSN1(ret, line_node, newarray, INT2FIX(0));
- }
+ if (!popped) {
+ ADD_INSN1(ret, line_node, newarray, INT2FIX(0));
+ }
return 0;
}
@@ -4369,7 +4561,7 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop
if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_ary_len) {
/* The literal contains only optimizable elements, or the subarray is long enough */
- VALUE ary = rb_ary_literal_new(count);
+ VALUE ary = rb_ary_hidden_new(count);
/* Create a hidden array */
for (; count; count--, node = node->nd_next)
@@ -4420,7 +4612,7 @@ static int
compile_array_1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node)
{
if (static_literal_node_p(node, iseq)) {
- VALUE ary = rb_ary_tmp_new(1);
+ VALUE ary = rb_ary_hidden_new(1);
rb_ary_push(ary, static_literal_value(node, iseq));
OBJ_FREEZE(ary);
@@ -4428,7 +4620,12 @@ compile_array_1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node)
}
else {
CHECK(COMPILE_(ret, "array element", node, FALSE));
- ADD_INSN1(ret, node, newarray, INT2FIX(1));
+ if (keyword_node_p(node)) {
+ ADD_INSN1(ret, node, newarraykwsplat, INT2FIX(1));
+ }
+ else {
+ ADD_INSN1(ret, node, newarray, INT2FIX(1));
+ }
}
return 1;
@@ -4448,9 +4645,9 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth
node = node->nd_head;
if (!node || nd_type_p(node, NODE_ZLIST)) {
- if (!popped) {
- ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
- }
+ if (!popped) {
+ ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
+ }
return 0;
}
@@ -4517,7 +4714,7 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth
if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_hash_len) {
/* The literal contains only optimizable elements, or the subsequence is long enough */
- VALUE ary = rb_ary_tmp_new(count);
+ VALUE ary = rb_ary_hidden_new(count);
/* Create a hidden hash */
for (; count; count--, node = node->nd_next->nd_next) {
@@ -4635,62 +4832,62 @@ rb_node_case_when_optimizable_literal(const NODE *const node)
{
switch (nd_type(node)) {
case NODE_LIT: {
- VALUE v = node->nd_lit;
- double ival;
- if (RB_FLOAT_TYPE_P(v) &&
- modf(RFLOAT_VALUE(v), &ival) == 0.0) {
- return FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival);
- }
+ VALUE v = node->nd_lit;
+ double ival;
+ if (RB_FLOAT_TYPE_P(v) &&
+ modf(RFLOAT_VALUE(v), &ival) == 0.0) {
+ return FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival);
+ }
if (RB_TYPE_P(v, T_RATIONAL) || RB_TYPE_P(v, T_COMPLEX)) {
return Qundef;
}
- if (SYMBOL_P(v) || rb_obj_is_kind_of(v, rb_cNumeric)) {
- return v;
- }
- break;
+ if (SYMBOL_P(v) || rb_obj_is_kind_of(v, rb_cNumeric)) {
+ return v;
+ }
+ break;
}
case NODE_NIL:
- return Qnil;
+ return Qnil;
case NODE_TRUE:
- return Qtrue;
+ return Qtrue;
case NODE_FALSE:
- return Qfalse;
+ return Qfalse;
case NODE_STR:
- return rb_fstring(node->nd_lit);
+ return rb_fstring(node->nd_lit);
}
return Qundef;
}
static int
when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
- LABEL *l1, int only_special_literals, VALUE literals)
+ LABEL *l1, int only_special_literals, VALUE literals)
{
while (vals) {
- const NODE *val = vals->nd_head;
+ const NODE *val = vals->nd_head;
VALUE lit = rb_node_case_when_optimizable_literal(val);
- if (lit == Qundef) {
- only_special_literals = 0;
- }
+ if (UNDEF_P(lit)) {
+ only_special_literals = 0;
+ }
else if (NIL_P(rb_hash_lookup(literals, lit))) {
rb_hash_aset(literals, lit, (VALUE)(l1) | 1);
- }
+ }
- if (nd_type_p(val, NODE_STR)) {
- debugp_param("nd_lit", val->nd_lit);
- lit = rb_fstring(val->nd_lit);
- ADD_INSN1(cond_seq, val, putobject, lit);
+ if (nd_type_p(val, NODE_STR)) {
+ debugp_param("nd_lit", val->nd_lit);
+ lit = rb_fstring(val->nd_lit);
+ ADD_INSN1(cond_seq, val, putobject, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit);
- }
- else {
- if (!COMPILE(cond_seq, "when cond", val)) return -1;
- }
+ }
+ else {
+ if (!COMPILE(cond_seq, "when cond", val)) return -1;
+ }
// Emit patern === target
ADD_INSN1(cond_seq, vals, topn, INT2FIX(1));
ADD_CALL(cond_seq, vals, idEqq, INT2FIX(1));
- ADD_INSNL(cond_seq, val, branchif, l1);
- vals = vals->nd_next;
+ ADD_INSNL(cond_seq, val, branchif, l1);
+ vals = vals->nd_next;
}
return only_special_literals;
}
@@ -4837,7 +5034,8 @@ struct masgn_state {
};
static int
-add_masgn_lhs_node(struct masgn_state *state, int lhs_pos, const NODE *line_node, int argc, INSN *before_insn) {
+add_masgn_lhs_node(struct masgn_state *state, int lhs_pos, const NODE *line_node, int argc, INSN *before_insn)
+{
if (!state) {
rb_bug("no masgn_state");
}
@@ -4873,17 +5071,22 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
{
switch (nd_type(node)) {
case NODE_ATTRASGN: {
- INSN *iobj;
+ INSN *iobj;
const NODE *line_node = node;
CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_ATTRASGN)", node));
+ bool safenav_call = false;
LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
iobj = (INSN *)get_prev_insn((INSN *)insn_element); /* send insn */
- ASSUME(iobj);
- ELEM_REMOVE(LAST_ELEMENT(pre));
- ELEM_REMOVE((LINK_ELEMENT *)iobj);
- pre->last = iobj->link.prev;
+ ASSUME(iobj);
+ ELEM_REMOVE(insn_element);
+ if (!IS_INSN_ID(iobj, send)) {
+ safenav_call = true;
+ iobj = (INSN *)get_prev_insn(iobj);
+ ELEM_INSERT_NEXT(&iobj->link, insn_element);
+ }
+ (pre->last = iobj->link.prev)->next = 0;
const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
int argc = vm_ci_argc(ci) + 1;
@@ -4902,23 +5105,27 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
return COMPILE_NG;
}
- ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
- if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
+ iobj->link.prev = lhs->last;
+ lhs->last->next = &iobj->link;
+ for (lhs->last = &iobj->link; lhs->last->next; lhs->last = lhs->last->next);
+ if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
int argc = vm_ci_argc(ci);
ci = ci_argc_set(iseq, ci, argc - 1);
OPERAND_AT(iobj, 0) = (VALUE)ci;
RB_OBJ_WRITTEN(iseq, Qundef, iobj);
INSERT_BEFORE_INSN1(iobj, line_node, newarray, INT2FIX(1));
- INSERT_BEFORE_INSN(iobj, line_node, concatarray);
- }
- ADD_INSN(lhs, line_node, pop);
- if (argc != 1) {
+ INSERT_BEFORE_INSN(iobj, line_node, concatarray);
+ }
+ if (!safenav_call) {
ADD_INSN(lhs, line_node, pop);
+ if (argc != 1) {
+ ADD_INSN(lhs, line_node, pop);
+ }
}
for (int i=0; i < argc; i++) {
ADD_INSN(post, line_node, pop);
}
- break;
+ break;
}
case NODE_MASGN: {
DECL_ANCHOR(nest_rhs);
@@ -4936,7 +5143,7 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
ADD_SEQ(lhs, nest_rhs);
ADD_SEQ(lhs, nest_lhs);
- break;
+ break;
}
case NODE_CDECL:
if (!node->nd_vid) {
@@ -4962,10 +5169,10 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
}
/* Fallthrough */
default: {
- DECL_ANCHOR(anchor);
- INIT_ANCHOR(anchor);
- CHECK(COMPILE_POPPED(anchor, "masgn lhs", node));
- ELEM_REMOVE(FIRST_ELEMENT(anchor));
+ DECL_ANCHOR(anchor);
+ INIT_ANCHOR(anchor);
+ CHECK(COMPILE_POPPED(anchor, "masgn lhs", node));
+ ELEM_REMOVE(FIRST_ELEMENT(anchor));
ADD_SEQ(lhs, anchor);
}
}
@@ -4977,7 +5184,7 @@ static int
compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *lhsn)
{
if (lhsn) {
- CHECK(compile_massign_opt_lhs(iseq, ret, lhsn->nd_next));
+ CHECK(compile_massign_opt_lhs(iseq, ret, lhsn->nd_next));
CHECK(compile_massign_lhs(iseq, ret, ret, ret, ret, lhsn->nd_head, NULL, 0));
}
return COMPILE_OK;
@@ -4985,7 +5192,7 @@ compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *lhs
static int
compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
- const NODE *rhsn, const NODE *orig_lhsn)
+ const NODE *rhsn, const NODE *orig_lhsn)
{
VALUE mem[64];
const int memsize = numberof(mem);
@@ -4998,48 +5205,48 @@ compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
int i; \
if (memindex == memsize) return 0; \
for (i=0; i<memindex; i++) { \
- if (mem[i] == (v)) return 0; \
+ if (mem[i] == (v)) return 0; \
} \
mem[memindex++] = (v); \
}
if (rhsn == 0 || !nd_type_p(rhsn, NODE_LIST)) {
- return 0;
+ return 0;
}
while (lhsn) {
- const NODE *ln = lhsn->nd_head;
- switch (nd_type(ln)) {
- case NODE_LASGN:
- MEMORY(ln->nd_vid);
- break;
- case NODE_DASGN:
- case NODE_IASGN:
- case NODE_CVASGN:
- MEMORY(ln->nd_vid);
- break;
- default:
- return 0;
- }
- lhsn = lhsn->nd_next;
- llen++;
+ const NODE *ln = lhsn->nd_head;
+ switch (nd_type(ln)) {
+ case NODE_LASGN:
+ MEMORY(ln->nd_vid);
+ break;
+ case NODE_DASGN:
+ case NODE_IASGN:
+ case NODE_CVASGN:
+ MEMORY(ln->nd_vid);
+ break;
+ default:
+ return 0;
+ }
+ lhsn = lhsn->nd_next;
+ llen++;
}
while (rhsn) {
- if (llen <= rlen) {
+ if (llen <= rlen) {
NO_CHECK(COMPILE_POPPED(ret, "masgn val (popped)", rhsn->nd_head));
- }
- else {
+ }
+ else {
NO_CHECK(COMPILE(ret, "masgn val", rhsn->nd_head));
- }
- rhsn = rhsn->nd_next;
- rlen++;
+ }
+ rhsn = rhsn->nd_next;
+ rlen++;
}
if (llen > rlen) {
- for (i=0; i<llen-rlen; i++) {
- ADD_INSN(ret, orig_lhsn, putnil);
- }
+ for (i=0; i<llen-rlen; i++) {
+ ADD_INSN(ret, orig_lhsn, putnil);
+ }
}
compile_massign_opt_lhs(iseq, ret, orig_lhsn);
@@ -5154,32 +5361,55 @@ compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
return COMPILE_OK;
}
+static VALUE
+collect_const_segments(rb_iseq_t *iseq, const NODE *node)
+{
+ VALUE arr = rb_ary_new();
+ for (;;) {
+ switch (nd_type(node)) {
+ case NODE_CONST:
+ rb_ary_unshift(arr, ID2SYM(node->nd_vid));
+ return arr;
+ case NODE_COLON3:
+ rb_ary_unshift(arr, ID2SYM(node->nd_mid));
+ rb_ary_unshift(arr, ID2SYM(idNULL));
+ return arr;
+ case NODE_COLON2:
+ rb_ary_unshift(arr, ID2SYM(node->nd_mid));
+ node = node->nd_head;
+ break;
+ default:
+ return Qfalse;
+ }
+ }
+}
+
static int
compile_const_prefix(rb_iseq_t *iseq, const NODE *const node,
- LINK_ANCHOR *const pref, LINK_ANCHOR *const body)
+ LINK_ANCHOR *const pref, LINK_ANCHOR *const body)
{
switch (nd_type(node)) {
case NODE_CONST:
- debugi("compile_const_prefix - colon", node->nd_vid);
+ debugi("compile_const_prefix - colon", node->nd_vid);
ADD_INSN1(body, node, putobject, Qtrue);
ADD_INSN1(body, node, getconstant, ID2SYM(node->nd_vid));
- break;
+ break;
case NODE_COLON3:
- debugi("compile_const_prefix - colon3", node->nd_mid);
- ADD_INSN(body, node, pop);
- ADD_INSN1(body, node, putobject, rb_cObject);
+ debugi("compile_const_prefix - colon3", node->nd_mid);
+ ADD_INSN(body, node, pop);
+ ADD_INSN1(body, node, putobject, rb_cObject);
ADD_INSN1(body, node, putobject, Qtrue);
ADD_INSN1(body, node, getconstant, ID2SYM(node->nd_mid));
- break;
+ break;
case NODE_COLON2:
- CHECK(compile_const_prefix(iseq, node->nd_head, pref, body));
- debugi("compile_const_prefix - colon2", node->nd_mid);
+ CHECK(compile_const_prefix(iseq, node->nd_head, pref, body));
+ debugi("compile_const_prefix - colon2", node->nd_mid);
ADD_INSN1(body, node, putobject, Qfalse);
ADD_INSN1(body, node, getconstant, ID2SYM(node->nd_mid));
- break;
+ break;
default:
- CHECK(COMPILE(pref, "const colon2 prefix", node));
- break;
+ CHECK(COMPILE(pref, "const colon2 prefix", node));
+ break;
}
return COMPILE_OK;
}
@@ -5188,20 +5418,20 @@ static int
compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath)
{
if (nd_type_p(cpath, NODE_COLON3)) {
- /* toplevel class ::Foo */
- ADD_INSN1(ret, cpath, putobject, rb_cObject);
- return VM_DEFINECLASS_FLAG_SCOPED;
+ /* toplevel class ::Foo */
+ ADD_INSN1(ret, cpath, putobject, rb_cObject);
+ return VM_DEFINECLASS_FLAG_SCOPED;
}
else if (cpath->nd_head) {
- /* Bar::Foo */
+ /* Bar::Foo */
NO_CHECK(COMPILE(ret, "nd_else->nd_head", cpath->nd_head));
- return VM_DEFINECLASS_FLAG_SCOPED;
+ return VM_DEFINECLASS_FLAG_SCOPED;
}
else {
- /* class at cbase Foo */
- ADD_INSN1(ret, cpath, putspecialobject,
- INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
- return 0;
+ /* class at cbase Foo */
+ ADD_INSN1(ret, cpath, putspecialobject,
+ INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
+ return 0;
}
}
@@ -5217,7 +5447,7 @@ private_recv_p(const NODE *node)
static void
defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
- const NODE *const node, LABEL **lfinish, VALUE needstr);
+ const NODE *const node, LABEL **lfinish, VALUE needstr);
static int
compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const enum node_type type, const NODE *const line_node, int popped, bool assume_receiver);
@@ -5234,31 +5464,31 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
switch (type = nd_type(node)) {
- /* easy literals */
+ /* easy literals */
case NODE_NIL:
- expr_type = DEFINED_NIL;
- break;
+ expr_type = DEFINED_NIL;
+ break;
case NODE_SELF:
- expr_type = DEFINED_SELF;
- break;
+ expr_type = DEFINED_SELF;
+ break;
case NODE_TRUE:
- expr_type = DEFINED_TRUE;
- break;
+ expr_type = DEFINED_TRUE;
+ break;
case NODE_FALSE:
- expr_type = DEFINED_FALSE;
- break;
+ expr_type = DEFINED_FALSE;
+ break;
case NODE_LIST:{
- const NODE *vals = node;
+ const NODE *vals = node;
- do {
+ do {
defined_expr0(iseq, ret, vals->nd_head, lfinish, Qfalse, false);
- if (!lfinish[1]) {
+ if (!lfinish[1]) {
lfinish[1] = NEW_LABEL(line);
- }
+ }
ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
- } while ((vals = vals->nd_next) != NULL);
+ } while ((vals = vals->nd_next) != NULL);
}
/* fall through */
case NODE_STR:
@@ -5267,43 +5497,43 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
case NODE_AND:
case NODE_OR:
default:
- expr_type = DEFINED_EXPR;
- break;
+ expr_type = DEFINED_EXPR;
+ break;
- /* variables */
+ /* variables */
case NODE_LVAR:
case NODE_DVAR:
- expr_type = DEFINED_LVAR;
- break;
+ expr_type = DEFINED_LVAR;
+ break;
#define PUSH_VAL(type) (needstr == Qfalse ? Qtrue : rb_iseq_defined_string(type))
case NODE_IVAR:
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_IVAR),
- ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_IVAR));
+ ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_IVAR));
return;
case NODE_GVAR:
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_GVAR),
- ID2SYM(node->nd_entry), PUSH_VAL(DEFINED_GVAR));
+ ID2SYM(node->nd_entry), PUSH_VAL(DEFINED_GVAR));
return;
case NODE_CVAR:
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CVAR),
- ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_CVAR));
+ ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_CVAR));
return;
case NODE_CONST:
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST),
- ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_CONST));
+ ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_CONST));
return;
case NODE_COLON2:
- if (!lfinish[1]) {
+ if (!lfinish[1]) {
lfinish[1] = NEW_LABEL(line);
- }
+ }
defined_expr0(iseq, ret, node->nd_head, lfinish, Qfalse, false);
ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
NO_CHECK(COMPILE(ret, "defined/colon2#nd_head", node->nd_head));
@@ -5320,18 +5550,18 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
case NODE_COLON3:
ADD_INSN1(ret, line_node, putobject, rb_cObject);
ADD_INSN3(ret, line_node, defined,
- INT2FIX(DEFINED_CONST_FROM), ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_CONST));
+ INT2FIX(DEFINED_CONST_FROM), ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_CONST));
return;
- /* method dispatch */
+ /* method dispatch */
case NODE_CALL:
case NODE_OPCALL:
case NODE_VCALL:
case NODE_FCALL:
case NODE_ATTRASGN:{
- const int explicit_receiver =
- (type == NODE_CALL || type == NODE_OPCALL ||
- (type == NODE_ATTRASGN && !private_recv_p(node)));
+ const int explicit_receiver =
+ (type == NODE_CALL || type == NODE_OPCALL ||
+ (type == NODE_ATTRASGN && !private_recv_p(node)));
if (node->nd_args || explicit_receiver) {
if (!lfinish[1]) {
@@ -5341,11 +5571,11 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
lfinish[2] = NEW_LABEL(line);
}
}
- if (node->nd_args) {
+ if (node->nd_args) {
defined_expr0(iseq, ret, node->nd_args, lfinish, Qfalse, false);
ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
- }
- if (explicit_receiver) {
+ }
+ if (explicit_receiver) {
defined_expr0(iseq, ret, node->nd_recv, lfinish, Qfalse, true);
switch (nd_type(node->nd_recv)) {
case NODE_CALL:
@@ -5365,38 +5595,38 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
ADD_INSN(ret, line_node, dup);
}
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
- ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD));
- }
- else {
+ ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD));
+ }
+ else {
ADD_INSN(ret, line_node, putself);
if (keep_result) {
ADD_INSN(ret, line_node, dup);
}
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_FUNC),
- ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD));
- }
+ ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD));
+ }
return;
}
case NODE_YIELD:
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_YIELD), 0,
- PUSH_VAL(DEFINED_YIELD));
+ PUSH_VAL(DEFINED_YIELD));
return;
case NODE_BACK_REF:
case NODE_NTH_REF:
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_REF),
- INT2FIX((node->nd_nth << 1) | (type == NODE_BACK_REF)),
- PUSH_VAL(DEFINED_GVAR));
+ INT2FIX((node->nd_nth << 1) | (type == NODE_BACK_REF)),
+ PUSH_VAL(DEFINED_GVAR));
return;
case NODE_SUPER:
case NODE_ZSUPER:
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_ZSUPER), 0,
- PUSH_VAL(DEFINED_ZSUPER));
+ PUSH_VAL(DEFINED_ZSUPER));
return;
#undef PUSH_VAL
@@ -5411,8 +5641,8 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
case NODE_IASGN:
case NODE_CDECL:
case NODE_CVASGN:
- expr_type = DEFINED_ASGN;
- break;
+ expr_type = DEFINED_ASGN;
+ break;
}
assert(expr_type != DEFINED_NOT_DEFINED);
@@ -5436,26 +5666,26 @@ build_defined_rescue_iseq(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const void *u
static void
defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
- const NODE *const node, LABEL **lfinish, VALUE needstr)
+ const NODE *const node, LABEL **lfinish, VALUE needstr)
{
LINK_ELEMENT *lcur = ret->last;
defined_expr0(iseq, ret, node, lfinish, needstr, false);
if (lfinish[1]) {
- int line = nd_line(node);
- LABEL *lstart = NEW_LABEL(line);
- LABEL *lend = NEW_LABEL(line);
- const rb_iseq_t *rescue;
+ int line = nd_line(node);
+ LABEL *lstart = NEW_LABEL(line);
+ LABEL *lend = NEW_LABEL(line);
+ const rb_iseq_t *rescue;
struct rb_iseq_new_with_callback_callback_func *ifunc =
rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
rescue = new_child_iseq_with_callback(iseq, ifunc,
rb_str_concat(rb_str_new2("defined guard in "),
ISEQ_BODY(iseq)->location.label),
iseq, ISEQ_TYPE_RESCUE, 0);
- lstart->rescued = LABEL_RESCUE_BEG;
- lend->rescued = LABEL_RESCUE_END;
- APPEND_LABEL(ret, lcur, lstart);
- ADD_LABEL(ret, lend);
- ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
+ lstart->rescued = LABEL_RESCUE_BEG;
+ lend->rescued = LABEL_RESCUE_END;
+ APPEND_LABEL(ret, lcur, lstart);
+ ADD_LABEL(ret, lend);
+ ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
}
}
@@ -5465,26 +5695,26 @@ compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const
const int line = nd_line(node);
const NODE *line_node = node;
if (!node->nd_head) {
- VALUE str = rb_iseq_defined_string(DEFINED_NIL);
- ADD_INSN1(ret, line_node, putobject, str);
+ VALUE str = rb_iseq_defined_string(DEFINED_NIL);
+ ADD_INSN1(ret, line_node, putobject, str);
}
else {
LABEL *lfinish[3];
- LINK_ELEMENT *last = ret->last;
- lfinish[0] = NEW_LABEL(line);
- lfinish[1] = 0;
+ LINK_ELEMENT *last = ret->last;
+ lfinish[0] = NEW_LABEL(line);
+ lfinish[1] = 0;
lfinish[2] = 0;
- defined_expr(iseq, ret, node->nd_head, lfinish, needstr);
- if (lfinish[1]) {
- ELEM_INSERT_NEXT(last, &new_insn_body(iseq, line_node, BIN(putnil), 0)->link);
- ADD_INSN(ret, line_node, swap);
+ defined_expr(iseq, ret, node->nd_head, lfinish, needstr);
+ if (lfinish[1]) {
+ ELEM_INSERT_NEXT(last, &new_insn_body(iseq, line_node, BIN(putnil), 0)->link);
+ ADD_INSN(ret, line_node, swap);
if (lfinish[2]) {
ADD_LABEL(ret, lfinish[2]);
}
- ADD_INSN(ret, line_node, pop);
- ADD_LABEL(ret, lfinish[1]);
- }
- ADD_LABEL(ret, lfinish[0]);
+ ADD_INSN(ret, line_node, pop);
+ ADD_LABEL(ret, lfinish[1]);
+ }
+ ADD_LABEL(ret, lfinish[0]);
}
return COMPILE_OK;
}
@@ -5498,10 +5728,10 @@ make_name_for_block(const rb_iseq_t *orig_iseq)
if (ISEQ_BODY(orig_iseq)->parent_iseq != 0) {
while (ISEQ_BODY(orig_iseq)->local_iseq != iseq) {
if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK) {
- level++;
- }
+ level++;
+ }
iseq = ISEQ_BODY(iseq)->parent_iseq;
- }
+ }
}
if (level == 1) {
@@ -5514,8 +5744,8 @@ make_name_for_block(const rb_iseq_t *orig_iseq)
static void
push_ensure_entry(rb_iseq_t *iseq,
- struct iseq_compile_data_ensure_node_stack *enl,
- struct ensure_range *er, const NODE *const node)
+ struct iseq_compile_data_ensure_node_stack *enl,
+ struct ensure_range *er, const NODE *const node)
{
enl->ensure_node = node;
enl->prev = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack; /* prev */
@@ -5525,13 +5755,13 @@ push_ensure_entry(rb_iseq_t *iseq,
static void
add_ensure_range(rb_iseq_t *iseq, struct ensure_range *erange,
- LABEL *lstart, LABEL *lend)
+ LABEL *lstart, LABEL *lend)
{
struct ensure_range *ne =
- compile_data_alloc(iseq, sizeof(struct ensure_range));
+ compile_data_alloc(iseq, sizeof(struct ensure_range));
while (erange->next != 0) {
- erange = erange->next;
+ erange = erange->next;
}
ne->next = 0;
ne->begin = lend;
@@ -5560,32 +5790,32 @@ add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return)
assert(can_add_ensure_iseq(iseq));
struct iseq_compile_data_ensure_node_stack *enlp =
- ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
+ ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
DECL_ANCHOR(ensure);
INIT_ANCHOR(ensure);
while (enlp) {
- if (enlp->erange != NULL) {
- DECL_ANCHOR(ensure_part);
- LABEL *lstart = NEW_LABEL(0);
- LABEL *lend = NEW_LABEL(0);
- INIT_ANCHOR(ensure_part);
+ if (enlp->erange != NULL) {
+ DECL_ANCHOR(ensure_part);
+ LABEL *lstart = NEW_LABEL(0);
+ LABEL *lend = NEW_LABEL(0);
+ INIT_ANCHOR(ensure_part);
- add_ensure_range(iseq, enlp->erange, lstart, lend);
+ add_ensure_range(iseq, enlp->erange, lstart, lend);
- ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
- ADD_LABEL(ensure_part, lstart);
+ ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
+ ADD_LABEL(ensure_part, lstart);
NO_CHECK(COMPILE_POPPED(ensure_part, "ensure part", enlp->ensure_node));
- ADD_LABEL(ensure_part, lend);
- ADD_SEQ(ensure, ensure_part);
- }
- else {
- if (!is_return) {
- break;
- }
- }
- enlp = enlp->prev;
+ ADD_LABEL(ensure_part, lend);
+ ADD_SEQ(ensure, ensure_part);
+ }
+ else {
+ if (!is_return) {
+ break;
+ }
+ }
+ enlp = enlp->prev;
}
ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
ADD_SEQ(ret, ensure);
@@ -5668,7 +5898,7 @@ setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
static VALUE
setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
- unsigned int *flag, struct rb_callinfo_kwarg **keywords)
+ unsigned int *flag, struct rb_callinfo_kwarg **keywords)
{
VALUE ret;
if (argn && nd_type_p(argn, NODE_BLOCK_PASS)) {
@@ -5730,44 +5960,44 @@ compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE
ADD_INSNL(ret, line_node, branchunless, fail_label);
for (vars = node; vars; vars = vars->nd_next) {
- INSN *cap;
- if (vars->nd_next) {
- ADD_INSN(ret, line_node, dup);
- }
- last = ret->last;
+ INSN *cap;
+ if (vars->nd_next) {
+ ADD_INSN(ret, line_node, dup);
+ }
+ last = ret->last;
NO_CHECK(COMPILE_POPPED(ret, "capture", vars->nd_head));
- last = last->next; /* putobject :var */
- cap = new_insn_send(iseq, line_node, idAREF, INT2FIX(1),
- NULL, INT2FIX(0), NULL);
- ELEM_INSERT_PREV(last->next, (LINK_ELEMENT *)cap);
+ last = last->next; /* putobject :var */
+ cap = new_insn_send(iseq, line_node, idAREF, INT2FIX(1),
+ NULL, INT2FIX(0), NULL);
+ ELEM_INSERT_PREV(last->next, (LINK_ELEMENT *)cap);
#if !defined(NAMED_CAPTURE_SINGLE_OPT) || NAMED_CAPTURE_SINGLE_OPT-0
- if (!vars->nd_next && vars == node) {
- /* only one name */
- DECL_ANCHOR(nom);
+ if (!vars->nd_next && vars == node) {
+ /* only one name */
+ DECL_ANCHOR(nom);
- INIT_ANCHOR(nom);
- ADD_INSNL(nom, line_node, jump, end_label);
- ADD_LABEL(nom, fail_label);
+ INIT_ANCHOR(nom);
+ ADD_INSNL(nom, line_node, jump, end_label);
+ ADD_LABEL(nom, fail_label);
# if 0 /* $~ must be MatchData or nil */
- ADD_INSN(nom, line_node, pop);
- ADD_INSN(nom, line_node, putnil);
+ ADD_INSN(nom, line_node, pop);
+ ADD_INSN(nom, line_node, putnil);
# endif
- ADD_LABEL(nom, end_label);
- (nom->last->next = cap->link.next)->prev = nom->last;
- (cap->link.next = nom->anchor.next)->prev = &cap->link;
- return;
- }
+ ADD_LABEL(nom, end_label);
+ (nom->last->next = cap->link.next)->prev = nom->last;
+ (cap->link.next = nom->anchor.next)->prev = &cap->link;
+ return;
+ }
#endif
}
ADD_INSNL(ret, line_node, jump, end_label);
ADD_LABEL(ret, fail_label);
ADD_INSN(ret, line_node, pop);
for (vars = node; vars; vars = vars->nd_next) {
- last = ret->last;
+ last = ret->last;
NO_CHECK(COMPILE_POPPED(ret, "capture", vars->nd_head));
- last = last->next; /* putobject :var */
- ((INSN*)last)->insn_id = BIN(putnil);
- ((INSN*)last)->operand_size = 0;
+ last = last->next; /* putobject :var */
+ ((INSN*)last)->insn_id = BIN(putnil);
+ ((INSN*)last)->operand_size = 0;
}
ADD_LABEL(ret, end_label);
}
@@ -5812,7 +6042,7 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int
end_label = 0;
compile_branch_condition(iseq, cond_seq, node->nd_cond,
- then_label, else_label);
+ then_label, else_label);
ci_size = body->ci_size;
CHECK(COMPILE_(then_seq, "then", node_body, popped));
@@ -5839,44 +6069,44 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int
ADD_SEQ(ret, cond_seq);
if (then_label->refcnt && else_label->refcnt) {
- branches = decl_branch_base(iseq, node, type == NODE_IF ? "if" : "unless");
+ branches = decl_branch_base(iseq, node, type == NODE_IF ? "if" : "unless");
}
if (then_label->refcnt) {
- ADD_LABEL(ret, then_label);
- if (else_label->refcnt) {
- add_trace_branch_coverage(
+ ADD_LABEL(ret, then_label);
+ if (else_label->refcnt) {
+ add_trace_branch_coverage(
iseq,
- ret,
+ ret,
node_body ? node_body : node,
0,
- type == NODE_IF ? "then" : "else",
- branches);
- end_label = NEW_LABEL(line);
- ADD_INSNL(then_seq, line_node, jump, end_label);
+ type == NODE_IF ? "then" : "else",
+ branches);
+ end_label = NEW_LABEL(line);
+ ADD_INSNL(then_seq, line_node, jump, end_label);
if (!popped) {
ADD_INSN(then_seq, line_node, pop);
}
- }
- ADD_SEQ(ret, then_seq);
+ }
+ ADD_SEQ(ret, then_seq);
}
if (else_label->refcnt) {
- ADD_LABEL(ret, else_label);
- if (then_label->refcnt) {
- add_trace_branch_coverage(
+ ADD_LABEL(ret, else_label);
+ if (then_label->refcnt) {
+ add_trace_branch_coverage(
iseq,
- ret,
+ ret,
node_else ? node_else : node,
1,
- type == NODE_IF ? "else" : "then",
- branches);
- }
- ADD_SEQ(ret, else_seq);
+ type == NODE_IF ? "else" : "then",
+ branches);
+ }
+ ADD_SEQ(ret, else_seq);
}
if (end_label) {
- ADD_LABEL(ret, end_label);
+ ADD_LABEL(ret, end_label);
}
return COMPILE_OK;
@@ -5921,74 +6151,74 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod
ADD_SEQ(ret, head); /* case VAL */
while (type == NODE_WHEN) {
- LABEL *l1;
+ LABEL *l1;
- l1 = NEW_LABEL(line);
- ADD_LABEL(body_seq, l1);
- ADD_INSN(body_seq, line_node, pop);
- add_trace_branch_coverage(
+ l1 = NEW_LABEL(line);
+ ADD_LABEL(body_seq, l1);
+ ADD_INSN(body_seq, line_node, pop);
+ add_trace_branch_coverage(
iseq,
- body_seq,
+ body_seq,
node->nd_body ? node->nd_body : node,
branch_id++,
- "when",
- branches);
- CHECK(COMPILE_(body_seq, "when body", node->nd_body, popped));
- ADD_INSNL(body_seq, line_node, jump, endlabel);
-
- vals = node->nd_head;
- if (vals) {
- switch (nd_type(vals)) {
- case NODE_LIST:
- only_special_literals = when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals);
- if (only_special_literals < 0) return COMPILE_NG;
- break;
- case NODE_SPLAT:
- case NODE_ARGSCAT:
- case NODE_ARGSPUSH:
- only_special_literals = 0;
- CHECK(when_splat_vals(iseq, cond_seq, vals, l1, only_special_literals, literals));
- break;
- default:
- UNKNOWN_NODE("NODE_CASE", vals, COMPILE_NG);
- }
- }
- else {
- EXPECT_NODE_NONULL("NODE_CASE", node, NODE_LIST, COMPILE_NG);
- }
-
- node = node->nd_next;
- if (!node) {
- break;
- }
- type = nd_type(node);
- line = nd_line(node);
+ "when",
+ branches);
+ CHECK(COMPILE_(body_seq, "when body", node->nd_body, popped));
+ ADD_INSNL(body_seq, line_node, jump, endlabel);
+
+ vals = node->nd_head;
+ if (vals) {
+ switch (nd_type(vals)) {
+ case NODE_LIST:
+ only_special_literals = when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals);
+ if (only_special_literals < 0) return COMPILE_NG;
+ break;
+ case NODE_SPLAT:
+ case NODE_ARGSCAT:
+ case NODE_ARGSPUSH:
+ only_special_literals = 0;
+ CHECK(when_splat_vals(iseq, cond_seq, vals, l1, only_special_literals, literals));
+ break;
+ default:
+ UNKNOWN_NODE("NODE_CASE", vals, COMPILE_NG);
+ }
+ }
+ else {
+ EXPECT_NODE_NONULL("NODE_CASE", node, NODE_LIST, COMPILE_NG);
+ }
+
+ node = node->nd_next;
+ if (!node) {
+ break;
+ }
+ type = nd_type(node);
+ line = nd_line(node);
line_node = node;
}
/* else */
if (node) {
- ADD_LABEL(cond_seq, elselabel);
- ADD_INSN(cond_seq, line_node, pop);
- add_trace_branch_coverage(iseq, cond_seq, node, branch_id, "else", branches);
- CHECK(COMPILE_(cond_seq, "else", node, popped));
- ADD_INSNL(cond_seq, line_node, jump, endlabel);
+ ADD_LABEL(cond_seq, elselabel);
+ ADD_INSN(cond_seq, line_node, pop);
+ add_trace_branch_coverage(iseq, cond_seq, node, branch_id, "else", branches);
+ CHECK(COMPILE_(cond_seq, "else", node, popped));
+ ADD_INSNL(cond_seq, line_node, jump, endlabel);
}
else {
- debugs("== else (implicit)\n");
- ADD_LABEL(cond_seq, elselabel);
- ADD_INSN(cond_seq, orig_node, pop);
- add_trace_branch_coverage(iseq, cond_seq, orig_node, branch_id, "else", branches);
- if (!popped) {
- ADD_INSN(cond_seq, orig_node, putnil);
- }
- ADD_INSNL(cond_seq, orig_node, jump, endlabel);
+ debugs("== else (implicit)\n");
+ ADD_LABEL(cond_seq, elselabel);
+ ADD_INSN(cond_seq, orig_node, pop);
+ add_trace_branch_coverage(iseq, cond_seq, orig_node, branch_id, "else", branches);
+ if (!popped) {
+ ADD_INSN(cond_seq, orig_node, putnil);
+ }
+ ADD_INSNL(cond_seq, orig_node, jump, endlabel);
}
if (only_special_literals && ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
- ADD_INSN(ret, orig_node, dup);
- ADD_INSN2(ret, orig_node, opt_case_dispatch, literals, elselabel);
+ ADD_INSN(ret, orig_node, dup);
+ ADD_INSN2(ret, orig_node, opt_case_dispatch, literals, elselabel);
RB_OBJ_WRITTEN(iseq, Qundef, literals);
- LABEL_REF(elselabel);
+ LABEL_REF(elselabel);
}
ADD_SEQ(ret, cond_seq);
@@ -6014,56 +6244,56 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
endlabel = NEW_LABEL(nd_line(node));
while (node && nd_type_p(node, NODE_WHEN)) {
- const int line = nd_line(node);
- LABEL *l1 = NEW_LABEL(line);
- ADD_LABEL(body_seq, l1);
- add_trace_branch_coverage(
+ const int line = nd_line(node);
+ LABEL *l1 = NEW_LABEL(line);
+ ADD_LABEL(body_seq, l1);
+ add_trace_branch_coverage(
iseq,
- body_seq,
- node->nd_body ? node->nd_body : node,
+ body_seq,
+ node->nd_body ? node->nd_body : node,
branch_id++,
- "when",
- branches);
- CHECK(COMPILE_(body_seq, "when", node->nd_body, popped));
- ADD_INSNL(body_seq, node, jump, endlabel);
+ "when",
+ branches);
+ CHECK(COMPILE_(body_seq, "when", node->nd_body, popped));
+ ADD_INSNL(body_seq, node, jump, endlabel);
- vals = node->nd_head;
- if (!vals) {
+ vals = node->nd_head;
+ if (!vals) {
EXPECT_NODE_NONULL("NODE_WHEN", node, NODE_LIST, COMPILE_NG);
- }
- switch (nd_type(vals)) {
- case NODE_LIST:
- while (vals) {
- LABEL *lnext;
- val = vals->nd_head;
- lnext = NEW_LABEL(nd_line(val));
- debug_compile("== when2\n", (void)0);
- CHECK(compile_branch_condition(iseq, ret, val, l1, lnext));
- ADD_LABEL(ret, lnext);
- vals = vals->nd_next;
- }
- break;
- case NODE_SPLAT:
- case NODE_ARGSCAT:
- case NODE_ARGSPUSH:
- ADD_INSN(ret, vals, putnil);
- CHECK(COMPILE(ret, "when2/cond splat", vals));
- ADD_INSN1(ret, vals, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
- ADD_INSNL(ret, vals, branchif, l1);
- break;
- default:
- UNKNOWN_NODE("NODE_WHEN", vals, COMPILE_NG);
- }
- node = node->nd_next;
+ }
+ switch (nd_type(vals)) {
+ case NODE_LIST:
+ while (vals) {
+ LABEL *lnext;
+ val = vals->nd_head;
+ lnext = NEW_LABEL(nd_line(val));
+ debug_compile("== when2\n", (void)0);
+ CHECK(compile_branch_condition(iseq, ret, val, l1, lnext));
+ ADD_LABEL(ret, lnext);
+ vals = vals->nd_next;
+ }
+ break;
+ case NODE_SPLAT:
+ case NODE_ARGSCAT:
+ case NODE_ARGSPUSH:
+ ADD_INSN(ret, vals, putnil);
+ CHECK(COMPILE(ret, "when2/cond splat", vals));
+ ADD_INSN1(ret, vals, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
+ ADD_INSNL(ret, vals, branchif, l1);
+ break;
+ default:
+ UNKNOWN_NODE("NODE_WHEN", vals, COMPILE_NG);
+ }
+ node = node->nd_next;
}
/* else */
add_trace_branch_coverage(
iseq,
- ret,
+ ret,
node ? node : orig_node,
branch_id,
- "else",
- branches);
+ "else",
+ branches);
CHECK(COMPILE_(ret, "else", node, popped));
ADD_INSNL(ret, orig_node, jump, endlabel);
@@ -7181,11 +7411,11 @@ compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
push_ensure_entry(iseq, &enl, NULL, NULL);
if (node->nd_state == 1) {
- ADD_INSNL(ret, line_node, jump, next_label);
+ ADD_INSNL(ret, line_node, jump, next_label);
}
else {
- tmp_label = NEW_LABEL(line);
- ADD_INSNL(ret, line_node, jump, tmp_label);
+ tmp_label = NEW_LABEL(line);
+ ADD_INSNL(ret, line_node, jump, tmp_label);
}
ADD_LABEL(ret, adjust_label);
ADD_INSN(ret, line_node, putnil);
@@ -7198,48 +7428,48 @@ compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
branches = decl_branch_base(iseq, node, type == NODE_WHILE ? "while" : "until");
add_trace_branch_coverage(
iseq,
- ret,
+ ret,
node->nd_body ? node->nd_body : node,
0,
- "body",
- branches);
+ "body",
+ branches);
CHECK(COMPILE_POPPED(ret, "while body", node->nd_body));
ADD_LABEL(ret, next_label); /* next */
if (type == NODE_WHILE) {
- compile_branch_condition(iseq, ret, node->nd_cond,
- redo_label, end_label);
+ compile_branch_condition(iseq, ret, node->nd_cond,
+ redo_label, end_label);
}
else {
- /* until */
- compile_branch_condition(iseq, ret, node->nd_cond,
- end_label, redo_label);
+ /* until */
+ compile_branch_condition(iseq, ret, node->nd_cond,
+ end_label, redo_label);
}
ADD_LABEL(ret, end_label);
ADD_ADJUST_RESTORE(ret, adjust_label);
- if (node->nd_state == Qundef) {
- /* ADD_INSN(ret, line_node, putundef); */
- COMPILE_ERROR(ERROR_ARGS "unsupported: putundef");
- return COMPILE_NG;
+ if (UNDEF_P(node->nd_state)) {
+ /* ADD_INSN(ret, line_node, putundef); */
+ COMPILE_ERROR(ERROR_ARGS "unsupported: putundef");
+ return COMPILE_NG;
}
else {
- ADD_INSN(ret, line_node, putnil);
+ ADD_INSN(ret, line_node, putnil);
}
ADD_LABEL(ret, break_label); /* break */
if (popped) {
- ADD_INSN(ret, line_node, pop);
+ ADD_INSN(ret, line_node, pop);
}
ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL,
- break_label);
+ break_label);
ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL,
- next_catch_label);
+ next_catch_label);
ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL,
- ISEQ_COMPILE_DATA(iseq)->redo_label);
+ ISEQ_COMPILE_DATA(iseq)->redo_label);
ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
@@ -7261,23 +7491,46 @@ compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
ADD_LABEL(ret, retry_label);
if (nd_type_p(node, NODE_FOR)) {
- CHECK(COMPILE(ret, "iter caller (for)", node->nd_iter));
+ CHECK(COMPILE(ret, "iter caller (for)", node->nd_iter));
- ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
- NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
- ISEQ_TYPE_BLOCK, line);
- ADD_SEND_WITH_BLOCK(ret, line_node, idEach, INT2FIX(0), child_iseq);
+ ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
+ NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
+ ISEQ_TYPE_BLOCK, line);
+ ADD_SEND_WITH_BLOCK(ret, line_node, idEach, INT2FIX(0), child_iseq);
}
else {
- ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
- NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
- ISEQ_TYPE_BLOCK, line);
- CHECK(COMPILE(ret, "iter caller", node->nd_iter));
+ ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
+ NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
+ ISEQ_TYPE_BLOCK, line);
+ CHECK(COMPILE(ret, "iter caller", node->nd_iter));
+ }
+
+ {
+ // We need to put the label "retry_end_l" immediately after the last "send" instruction.
+ // This because vm_throw checks if the break cont is equal to the index of next insn of the "send".
+ // (Otherwise, it is considered "break from proc-closure". See "TAG_BREAK" handling in "vm_throw_start".)
+ //
+ // Normally, "send" instruction is at the last.
+ // However, qcall under branch coverage measurement adds some instructions after the "send".
+ //
+ // Note that "invokesuper" appears instead of "send".
+ INSN *iobj;
+ LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
+ iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
+ while (INSN_OF(iobj) != BIN(send) && INSN_OF(iobj) != BIN(invokesuper)) {
+ iobj = (INSN*) get_prev_insn(iobj);
+ }
+ ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
+
+ // LINK_ANCHOR has a pointer to the last element, but ELEM_INSERT_NEXT does not update it
+ // even if we add an insn to the last of LINK_ANCHOR. So this updates it manually.
+ if (&iobj->link == LAST_ELEMENT(ret)) {
+ ret->last = (LINK_ELEMENT*) retry_end_l;
+ }
}
- ADD_LABEL(ret, retry_end_l);
if (popped) {
- ADD_INSN(ret, line_node, pop);
+ ADD_INSN(ret, line_node, pop);
}
ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
@@ -7324,39 +7577,39 @@ compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
unsigned long throw_flag = 0;
if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
- /* while/until */
- LABEL *splabel = NEW_LABEL(0);
- ADD_LABEL(ret, splabel);
- ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
- CHECK(COMPILE_(ret, "break val (while/until)", node->nd_stts,
- ISEQ_COMPILE_DATA(iseq)->loopval_popped));
- add_ensure_iseq(ret, iseq, 0);
- ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
- ADD_ADJUST_RESTORE(ret, splabel);
-
- if (!popped) {
- ADD_INSN(ret, line_node, putnil);
- }
+ /* while/until */
+ LABEL *splabel = NEW_LABEL(0);
+ ADD_LABEL(ret, splabel);
+ ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
+ CHECK(COMPILE_(ret, "break val (while/until)", node->nd_stts,
+ ISEQ_COMPILE_DATA(iseq)->loopval_popped));
+ add_ensure_iseq(ret, iseq, 0);
+ ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
+ ADD_ADJUST_RESTORE(ret, splabel);
+
+ if (!popped) {
+ ADD_INSN(ret, line_node, putnil);
+ }
}
else {
const rb_iseq_t *ip = iseq;
- while (ip) {
- if (!ISEQ_COMPILE_DATA(ip)) {
- ip = 0;
- break;
- }
+ while (ip) {
+ if (!ISEQ_COMPILE_DATA(ip)) {
+ ip = 0;
+ break;
+ }
- if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
+ if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
throw_flag = VM_THROW_NO_ESCAPE_FLAG;
- }
+ }
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
throw_flag = 0;
- }
+ }
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
return COMPILE_NG;
- }
+ }
else {
ip = ISEQ_BODY(ip)->parent_iseq;
continue;
@@ -7369,9 +7622,9 @@ compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
ADD_INSN(ret, line_node, pop);
}
return COMPILE_OK;
- }
- COMPILE_ERROR(ERROR_ARGS "Invalid break");
- return COMPILE_NG;
+ }
+ COMPILE_ERROR(ERROR_ARGS "Invalid break");
+ return COMPILE_NG;
}
return COMPILE_OK;
}
@@ -7383,69 +7636,68 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
unsigned long throw_flag = 0;
if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
- LABEL *splabel = NEW_LABEL(0);
- debugs("next in while loop\n");
- ADD_LABEL(ret, splabel);
- CHECK(COMPILE(ret, "next val/valid syntax?", node->nd_stts));
- add_ensure_iseq(ret, iseq, 0);
- ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
- ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
- ADD_ADJUST_RESTORE(ret, splabel);
- if (!popped) {
- ADD_INSN(ret, line_node, putnil);
- }
+ LABEL *splabel = NEW_LABEL(0);
+ debugs("next in while loop\n");
+ ADD_LABEL(ret, splabel);
+ CHECK(COMPILE(ret, "next val/valid syntax?", node->nd_stts));
+ add_ensure_iseq(ret, iseq, 0);
+ ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
+ ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
+ ADD_ADJUST_RESTORE(ret, splabel);
+ if (!popped) {
+ ADD_INSN(ret, line_node, putnil);
+ }
}
else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
- LABEL *splabel = NEW_LABEL(0);
- debugs("next in block\n");
- ADD_LABEL(ret, splabel);
- ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
- CHECK(COMPILE(ret, "next val", node->nd_stts));
- add_ensure_iseq(ret, iseq, 0);
- ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
- ADD_ADJUST_RESTORE(ret, splabel);
- splabel->unremovable = FALSE;
-
- if (!popped) {
- ADD_INSN(ret, line_node, putnil);
- }
+ LABEL *splabel = NEW_LABEL(0);
+ debugs("next in block\n");
+ ADD_LABEL(ret, splabel);
+ ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
+ CHECK(COMPILE(ret, "next val", node->nd_stts));
+ add_ensure_iseq(ret, iseq, 0);
+ ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
+ ADD_ADJUST_RESTORE(ret, splabel);
+
+ if (!popped) {
+ ADD_INSN(ret, line_node, putnil);
+ }
}
else {
- const rb_iseq_t *ip = iseq;
+ const rb_iseq_t *ip = iseq;
- while (ip) {
- if (!ISEQ_COMPILE_DATA(ip)) {
- ip = 0;
- break;
- }
+ while (ip) {
+ if (!ISEQ_COMPILE_DATA(ip)) {
+ ip = 0;
+ break;
+ }
throw_flag = VM_THROW_NO_ESCAPE_FLAG;
- if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
- /* while loop */
- break;
- }
+ if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
+ /* while loop */
+ break;
+ }
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
- break;
- }
+ break;
+ }
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with next");
return COMPILE_NG;
- }
+ }
ip = ISEQ_BODY(ip)->parent_iseq;
- }
- if (ip != 0) {
- CHECK(COMPILE(ret, "next val", node->nd_stts));
+ }
+ if (ip != 0) {
+ CHECK(COMPILE(ret, "next val", node->nd_stts));
ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_NEXT));
- if (popped) {
- ADD_INSN(ret, line_node, pop);
- }
- }
- else {
- COMPILE_ERROR(ERROR_ARGS "Invalid next");
- return COMPILE_NG;
- }
+ if (popped) {
+ ADD_INSN(ret, line_node, pop);
+ }
+ }
+ else {
+ COMPILE_ERROR(ERROR_ARGS "Invalid next");
+ return COMPILE_NG;
+ }
}
return COMPILE_OK;
}
@@ -7456,65 +7708,65 @@ compile_redo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
const NODE *line_node = node;
if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
- LABEL *splabel = NEW_LABEL(0);
- debugs("redo in while");
- ADD_LABEL(ret, splabel);
- ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
- add_ensure_iseq(ret, iseq, 0);
- ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
- ADD_ADJUST_RESTORE(ret, splabel);
- if (!popped) {
- ADD_INSN(ret, line_node, putnil);
- }
+ LABEL *splabel = NEW_LABEL(0);
+ debugs("redo in while");
+ ADD_LABEL(ret, splabel);
+ ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
+ add_ensure_iseq(ret, iseq, 0);
+ ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
+ ADD_ADJUST_RESTORE(ret, splabel);
+ if (!popped) {
+ ADD_INSN(ret, line_node, putnil);
+ }
}
else if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
- LABEL *splabel = NEW_LABEL(0);
+ LABEL *splabel = NEW_LABEL(0);
- debugs("redo in block");
- ADD_LABEL(ret, splabel);
- add_ensure_iseq(ret, iseq, 0);
- ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
- ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
- ADD_ADJUST_RESTORE(ret, splabel);
+ debugs("redo in block");
+ ADD_LABEL(ret, splabel);
+ add_ensure_iseq(ret, iseq, 0);
+ ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
+ ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
+ ADD_ADJUST_RESTORE(ret, splabel);
- if (!popped) {
- ADD_INSN(ret, line_node, putnil);
- }
+ if (!popped) {
+ ADD_INSN(ret, line_node, putnil);
+ }
}
else {
- const rb_iseq_t *ip = iseq;
+ const rb_iseq_t *ip = iseq;
- while (ip) {
- if (!ISEQ_COMPILE_DATA(ip)) {
- ip = 0;
- break;
- }
+ while (ip) {
+ if (!ISEQ_COMPILE_DATA(ip)) {
+ ip = 0;
+ break;
+ }
- if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
- break;
- }
+ if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
+ break;
+ }
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
- break;
- }
+ break;
+ }
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with redo");
return COMPILE_NG;
- }
+ }
ip = ISEQ_BODY(ip)->parent_iseq;
- }
- if (ip != 0) {
- ADD_INSN(ret, line_node, putnil);
+ }
+ if (ip != 0) {
+ ADD_INSN(ret, line_node, putnil);
ADD_INSN1(ret, line_node, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
- if (popped) {
- ADD_INSN(ret, line_node, pop);
- }
- }
- else {
- COMPILE_ERROR(ERROR_ARGS "Invalid redo");
- return COMPILE_NG;
- }
+ if (popped) {
+ ADD_INSN(ret, line_node, pop);
+ }
+ }
+ else {
+ COMPILE_ERROR(ERROR_ARGS "Invalid redo");
+ return COMPILE_NG;
+ }
}
return COMPILE_OK;
}
@@ -7525,16 +7777,16 @@ compile_retry(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
const NODE *line_node = node;
if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
- ADD_INSN(ret, line_node, putnil);
- ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETRY));
+ ADD_INSN(ret, line_node, putnil);
+ ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETRY));
- if (popped) {
- ADD_INSN(ret, line_node, pop);
- }
+ if (popped) {
+ ADD_INSN(ret, line_node, pop);
+ }
}
else {
- COMPILE_ERROR(ERROR_ARGS "Invalid retry");
- return COMPILE_NG;
+ COMPILE_ERROR(ERROR_ARGS "Invalid retry");
+ return COMPILE_NG;
}
return COMPILE_OK;
}
@@ -7565,14 +7817,14 @@ compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
ADD_LABEL(ret, lend);
if (node->nd_else) {
- ADD_INSN(ret, line_node, pop);
- CHECK(COMPILE(ret, "rescue else", node->nd_else));
+ ADD_INSN(ret, line_node, pop);
+ CHECK(COMPILE(ret, "rescue else", node->nd_else));
}
ADD_INSN(ret, line_node, nop);
ADD_LABEL(ret, lcont);
if (popped) {
- ADD_INSN(ret, line_node, pop);
+ ADD_INSN(ret, line_node, pop);
}
/* register catch entry */
@@ -7591,48 +7843,48 @@ compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
LABEL *label_miss, *label_hit;
while (resq) {
- label_miss = NEW_LABEL(line);
- label_hit = NEW_LABEL(line);
-
- narg = resq->nd_args;
- if (narg) {
- switch (nd_type(narg)) {
- case NODE_LIST:
- while (narg) {
- ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
- CHECK(COMPILE(ret, "rescue arg", narg->nd_head));
- ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
- ADD_INSNL(ret, line_node, branchif, label_hit);
- narg = narg->nd_next;
- }
- break;
- case NODE_SPLAT:
- case NODE_ARGSCAT:
- case NODE_ARGSPUSH:
- ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
- CHECK(COMPILE(ret, "rescue/cond splat", narg));
- ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE | VM_CHECKMATCH_ARRAY));
- ADD_INSNL(ret, line_node, branchif, label_hit);
- break;
- default:
- UNKNOWN_NODE("NODE_RESBODY", narg, COMPILE_NG);
- }
- }
- else {
- ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
- ADD_INSN1(ret, line_node, putobject, rb_eStandardError);
- ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
- ADD_INSNL(ret, line_node, branchif, label_hit);
- }
- ADD_INSNL(ret, line_node, jump, label_miss);
- ADD_LABEL(ret, label_hit);
- CHECK(COMPILE(ret, "resbody body", resq->nd_body));
- if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
- ADD_INSN(ret, line_node, nop);
- }
- ADD_INSN(ret, line_node, leave);
- ADD_LABEL(ret, label_miss);
- resq = resq->nd_head;
+ label_miss = NEW_LABEL(line);
+ label_hit = NEW_LABEL(line);
+
+ narg = resq->nd_args;
+ if (narg) {
+ switch (nd_type(narg)) {
+ case NODE_LIST:
+ while (narg) {
+ ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
+ CHECK(COMPILE(ret, "rescue arg", narg->nd_head));
+ ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
+ ADD_INSNL(ret, line_node, branchif, label_hit);
+ narg = narg->nd_next;
+ }
+ break;
+ case NODE_SPLAT:
+ case NODE_ARGSCAT:
+ case NODE_ARGSPUSH:
+ ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
+ CHECK(COMPILE(ret, "rescue/cond splat", narg));
+ ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE | VM_CHECKMATCH_ARRAY));
+ ADD_INSNL(ret, line_node, branchif, label_hit);
+ break;
+ default:
+ UNKNOWN_NODE("NODE_RESBODY", narg, COMPILE_NG);
+ }
+ }
+ else {
+ ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
+ ADD_INSN1(ret, line_node, putobject, rb_eStandardError);
+ ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
+ ADD_INSNL(ret, line_node, branchif, label_hit);
+ }
+ ADD_INSNL(ret, line_node, jump, label_miss);
+ ADD_LABEL(ret, label_hit);
+ CHECK(COMPILE(ret, "resbody body", resq->nd_body));
+ if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
+ ADD_INSN(ret, line_node, nop);
+ }
+ ADD_INSN(ret, line_node, leave);
+ ADD_LABEL(ret, label_miss);
+ resq = resq->nd_head;
}
return COMPILE_OK;
}
@@ -7645,7 +7897,7 @@ compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
DECL_ANCHOR(ensr);
const rb_iseq_t *ensure = NEW_CHILD_ISEQ(node->nd_ensr,
rb_str_concat(rb_str_new2 ("ensure in "), ISEQ_BODY(iseq)->location.label),
- ISEQ_TYPE_ENSURE, line);
+ ISEQ_TYPE_ENSURE, line);
LABEL *lstart = NEW_LABEL(line);
LABEL *lend = NEW_LABEL(line);
LABEL *lcont = NEW_LABEL(line);
@@ -7675,11 +7927,11 @@ compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
if (lstart->link.next != &lend->link) {
- while (erange) {
- ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end,
- ensure, lcont);
- erange = erange->next;
- }
+ while (erange) {
+ ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end,
+ ensure, lcont);
+ erange = erange->next;
+ }
}
ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
@@ -7692,55 +7944,55 @@ compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
const NODE *line_node = node;
if (iseq) {
- enum iseq_type type = ISEQ_BODY(iseq)->type;
- const rb_iseq_t *is = iseq;
- enum iseq_type t = type;
- const NODE *retval = node->nd_stts;
- LABEL *splabel = 0;
+ enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
+ const rb_iseq_t *is = iseq;
+ enum rb_iseq_type t = type;
+ const NODE *retval = node->nd_stts;
+ LABEL *splabel = 0;
- while (t == ISEQ_TYPE_RESCUE || t == ISEQ_TYPE_ENSURE) {
+ while (t == ISEQ_TYPE_RESCUE || t == ISEQ_TYPE_ENSURE) {
if (!(is = ISEQ_BODY(is)->parent_iseq)) break;
t = ISEQ_BODY(is)->type;
- }
- switch (t) {
- case ISEQ_TYPE_TOP:
- case ISEQ_TYPE_MAIN:
+ }
+ switch (t) {
+ case ISEQ_TYPE_TOP:
+ case ISEQ_TYPE_MAIN:
if (retval) {
rb_warn("argument of top-level return is ignored");
}
- if (is == iseq) {
- /* plain top-level, leave directly */
- type = ISEQ_TYPE_METHOD;
- }
- break;
- default:
- break;
- }
-
- if (type == ISEQ_TYPE_METHOD) {
- splabel = NEW_LABEL(0);
- ADD_LABEL(ret, splabel);
- ADD_ADJUST(ret, line_node, 0);
- }
-
- CHECK(COMPILE(ret, "return nd_stts (return val)", retval));
-
- if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
- add_ensure_iseq(ret, iseq, 1);
- ADD_TRACE(ret, RUBY_EVENT_RETURN);
- ADD_INSN(ret, line_node, leave);
- ADD_ADJUST_RESTORE(ret, splabel);
-
- if (!popped) {
- ADD_INSN(ret, line_node, putnil);
- }
- }
- else {
- ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETURN));
- if (popped) {
- ADD_INSN(ret, line_node, pop);
- }
- }
+ if (is == iseq) {
+ /* plain top-level, leave directly */
+ type = ISEQ_TYPE_METHOD;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (type == ISEQ_TYPE_METHOD) {
+ splabel = NEW_LABEL(0);
+ ADD_LABEL(ret, splabel);
+ ADD_ADJUST(ret, line_node, 0);
+ }
+
+ CHECK(COMPILE(ret, "return nd_stts (return val)", retval));
+
+ if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
+ add_ensure_iseq(ret, iseq, 1);
+ ADD_TRACE(ret, RUBY_EVENT_RETURN);
+ ADD_INSN(ret, line_node, leave);
+ ADD_ADJUST_RESTORE(ret, splabel);
+
+ if (!popped) {
+ ADD_INSN(ret, line_node, putnil);
+ }
+ }
+ else {
+ ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETURN));
+ if (popped) {
+ ADD_INSN(ret, line_node, pop);
+ }
+ }
}
return COMPILE_OK;
}
@@ -7752,7 +8004,7 @@ compile_evstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
if (!popped && !all_string_result_p(node)) {
const NODE *line_node = node;
- const unsigned int flag = VM_CALL_FCALL;
+ const unsigned int flag = VM_CALL_FCALL;
// Note, this dup could be removed if we are willing to change anytostring. It pops
// two VALUEs off the stack when it could work by replacing the top most VALUE.
@@ -8047,7 +8299,7 @@ compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const N
ISEQ_BODY(iseq)->mandatory_only_iseq =
rb_iseq_new_with_opt(&ast, rb_iseq_base_label(iseq),
rb_iseq_path(iseq), rb_iseq_realpath(iseq),
- INT2FIX(nd_line(line_node)), NULL, 0,
+ nd_line(line_node), NULL, 0,
ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option);
GET_VM()->builtin_inline_index = prev_inline_index;
@@ -8340,106 +8592,106 @@ compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
*/
if (!popped) {
- ADD_INSN(ret, node, putnil);
+ ADD_INSN(ret, node, putnil);
}
asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN1 recv", node);
CHECK(asgnflag != -1);
switch (nd_type(node->nd_args->nd_head)) {
case NODE_ZLIST:
- argc = INT2FIX(0);
- break;
+ argc = INT2FIX(0);
+ break;
case NODE_BLOCK_PASS:
- boff = 1;
- /* fall through */
+ boff = 1;
+ /* fall through */
default:
- argc = setup_args(iseq, ret, node->nd_args->nd_head, &flag, NULL);
- CHECK(!NIL_P(argc));
+ argc = setup_args(iseq, ret, node->nd_args->nd_head, &flag, NULL);
+ CHECK(!NIL_P(argc));
}
ADD_INSN1(ret, node, dupn, FIXNUM_INC(argc, 1 + boff));
flag |= asgnflag;
ADD_SEND_WITH_FLAG(ret, node, idAREF, argc, INT2FIX(flag));
if (id == idOROP || id == idANDOP) {
- /* a[x] ||= y or a[x] &&= y
-
- unless/if a[x]
- a[x]= y
- else
- nil
- end
- */
- LABEL *label = NEW_LABEL(line);
- LABEL *lfin = NEW_LABEL(line);
-
- ADD_INSN(ret, node, dup);
- if (id == idOROP) {
- ADD_INSNL(ret, node, branchif, label);
- }
- else { /* idANDOP */
- ADD_INSNL(ret, node, branchunless, label);
- }
- ADD_INSN(ret, node, pop);
-
- CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body));
- if (!popped) {
- ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff));
- }
- if (flag & VM_CALL_ARGS_SPLAT) {
- ADD_INSN1(ret, node, newarray, INT2FIX(1));
- if (boff > 0) {
- ADD_INSN1(ret, node, dupn, INT2FIX(3));
- ADD_INSN(ret, node, swap);
- ADD_INSN(ret, node, pop);
- }
- ADD_INSN(ret, node, concatarray);
- if (boff > 0) {
- ADD_INSN1(ret, node, setn, INT2FIX(3));
- ADD_INSN(ret, node, pop);
- ADD_INSN(ret, node, pop);
- }
- ADD_SEND_WITH_FLAG(ret, node, idASET, argc, INT2FIX(flag));
- }
- else {
- if (boff > 0)
- ADD_INSN(ret, node, swap);
- ADD_SEND_WITH_FLAG(ret, node, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag));
- }
- ADD_INSN(ret, node, pop);
- ADD_INSNL(ret, node, jump, lfin);
- ADD_LABEL(ret, label);
- if (!popped) {
- ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff));
- }
- ADD_INSN1(ret, node, adjuststack, FIXNUM_INC(argc, 2+boff));
- ADD_LABEL(ret, lfin);
+ /* a[x] ||= y or a[x] &&= y
+
+ unless/if a[x]
+ a[x]= y
+ else
+ nil
+ end
+ */
+ LABEL *label = NEW_LABEL(line);
+ LABEL *lfin = NEW_LABEL(line);
+
+ ADD_INSN(ret, node, dup);
+ if (id == idOROP) {
+ ADD_INSNL(ret, node, branchif, label);
+ }
+ else { /* idANDOP */
+ ADD_INSNL(ret, node, branchunless, label);
+ }
+ ADD_INSN(ret, node, pop);
+
+ CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body));
+ if (!popped) {
+ ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff));
+ }
+ if (flag & VM_CALL_ARGS_SPLAT) {
+ ADD_INSN1(ret, node, newarray, INT2FIX(1));
+ if (boff > 0) {
+ ADD_INSN1(ret, node, dupn, INT2FIX(3));
+ ADD_INSN(ret, node, swap);
+ ADD_INSN(ret, node, pop);
+ }
+ ADD_INSN(ret, node, concatarray);
+ if (boff > 0) {
+ ADD_INSN1(ret, node, setn, INT2FIX(3));
+ ADD_INSN(ret, node, pop);
+ ADD_INSN(ret, node, pop);
+ }
+ ADD_SEND_WITH_FLAG(ret, node, idASET, argc, INT2FIX(flag));
+ }
+ else {
+ if (boff > 0)
+ ADD_INSN(ret, node, swap);
+ ADD_SEND_WITH_FLAG(ret, node, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag));
+ }
+ ADD_INSN(ret, node, pop);
+ ADD_INSNL(ret, node, jump, lfin);
+ ADD_LABEL(ret, label);
+ if (!popped) {
+ ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff));
+ }
+ ADD_INSN1(ret, node, adjuststack, FIXNUM_INC(argc, 2+boff));
+ ADD_LABEL(ret, lfin);
}
else {
- CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body));
- ADD_SEND(ret, node, id, INT2FIX(1));
- if (!popped) {
- ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff));
- }
- if (flag & VM_CALL_ARGS_SPLAT) {
- ADD_INSN1(ret, node, newarray, INT2FIX(1));
- if (boff > 0) {
- ADD_INSN1(ret, node, dupn, INT2FIX(3));
- ADD_INSN(ret, node, swap);
- ADD_INSN(ret, node, pop);
- }
- ADD_INSN(ret, node, concatarray);
- if (boff > 0) {
- ADD_INSN1(ret, node, setn, INT2FIX(3));
- ADD_INSN(ret, node, pop);
- ADD_INSN(ret, node, pop);
- }
- ADD_SEND_WITH_FLAG(ret, node, idASET, argc, INT2FIX(flag));
- }
- else {
- if (boff > 0)
- ADD_INSN(ret, node, swap);
- ADD_SEND_WITH_FLAG(ret, node, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag));
- }
- ADD_INSN(ret, node, pop);
+ CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body));
+ ADD_SEND(ret, node, id, INT2FIX(1));
+ if (!popped) {
+ ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff));
+ }
+ if (flag & VM_CALL_ARGS_SPLAT) {
+ ADD_INSN1(ret, node, newarray, INT2FIX(1));
+ if (boff > 0) {
+ ADD_INSN1(ret, node, dupn, INT2FIX(3));
+ ADD_INSN(ret, node, swap);
+ ADD_INSN(ret, node, pop);
+ }
+ ADD_INSN(ret, node, concatarray);
+ if (boff > 0) {
+ ADD_INSN1(ret, node, setn, INT2FIX(3));
+ ADD_INSN(ret, node, pop);
+ ADD_INSN(ret, node, pop);
+ }
+ ADD_SEND_WITH_FLAG(ret, node, idASET, argc, INT2FIX(flag));
+ }
+ else {
+ if (boff > 0)
+ ADD_INSN(ret, node, swap);
+ ADD_SEND_WITH_FLAG(ret, node, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag));
+ }
+ ADD_INSN(ret, node, pop);
}
return COMPILE_OK;
}
@@ -8479,6 +8731,17 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
lfin: # o ?
pop # o
+ # or (popped)
+ if lcfin # r
+ eval v # r v
+ send a= # ?
+ jump lfin # ?
+
+ lcfin: # r
+
+ lfin: # ?
+ pop #
+
# and
dup # r o o
unless lcfin
@@ -8499,56 +8762,56 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN2#recv", node);
CHECK(asgnflag != -1);
if (node->nd_next->nd_aid) {
- lskip = NEW_LABEL(line);
- ADD_INSN(ret, node, dup);
- ADD_INSNL(ret, node, branchnil, lskip);
+ lskip = NEW_LABEL(line);
+ ADD_INSN(ret, node, dup);
+ ADD_INSNL(ret, node, branchnil, lskip);
}
ADD_INSN(ret, node, dup);
ADD_SEND_WITH_FLAG(ret, node, vid, INT2FIX(0), INT2FIX(asgnflag));
if (atype == idOROP || atype == idANDOP) {
- ADD_INSN(ret, node, dup);
- if (atype == idOROP) {
- ADD_INSNL(ret, node, branchif, lcfin);
- }
- else { /* idANDOP */
- ADD_INSNL(ret, node, branchunless, lcfin);
- }
- ADD_INSN(ret, node, pop);
- CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
- ADD_INSN(ret, node, swap);
- ADD_INSN1(ret, node, topn, INT2FIX(1));
- ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
- ADD_INSNL(ret, node, jump, lfin);
-
- ADD_LABEL(ret, lcfin);
- ADD_INSN(ret, node, swap);
-
- ADD_LABEL(ret, lfin);
- ADD_INSN(ret, node, pop);
- if (lskip) {
- ADD_LABEL(ret, lskip);
- }
- if (popped) {
- /* we can apply more optimize */
- ADD_INSN(ret, node, pop);
- }
+ if (!popped) {
+ ADD_INSN(ret, node, dup);
+ }
+ if (atype == idOROP) {
+ ADD_INSNL(ret, node, branchif, lcfin);
+ }
+ else { /* idANDOP */
+ ADD_INSNL(ret, node, branchunless, lcfin);
+ }
+ if (!popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
+ if (!popped) {
+ ADD_INSN(ret, node, swap);
+ ADD_INSN1(ret, node, topn, INT2FIX(1));
+ }
+ ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
+ ADD_INSNL(ret, node, jump, lfin);
+
+ ADD_LABEL(ret, lcfin);
+ if (!popped) {
+ ADD_INSN(ret, node, swap);
+ }
+
+ ADD_LABEL(ret, lfin);
}
else {
- CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
- ADD_SEND(ret, node, atype, INT2FIX(1));
- if (!popped) {
- ADD_INSN(ret, node, swap);
- ADD_INSN1(ret, node, topn, INT2FIX(1));
- }
- ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
- if (lskip && popped) {
- ADD_LABEL(ret, lskip);
- }
- ADD_INSN(ret, node, pop);
- if (lskip && !popped) {
- ADD_LABEL(ret, lskip);
- }
+ CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
+ ADD_SEND(ret, node, atype, INT2FIX(1));
+ if (!popped) {
+ ADD_INSN(ret, node, swap);
+ ADD_INSN1(ret, node, topn, INT2FIX(1));
+ }
+ ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
+ }
+ if (lskip && popped) {
+ ADD_LABEL(ret, lskip);
+ }
+ ADD_INSN(ret, node, pop);
+ if (lskip && !popped) {
+ ADD_LABEL(ret, lskip);
}
return COMPILE_OK;
}
@@ -8563,63 +8826,63 @@ compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
switch (nd_type(node->nd_head)) {
case NODE_COLON3:
- ADD_INSN1(ret, node, putobject, rb_cObject);
- break;
+ ADD_INSN1(ret, node, putobject, rb_cObject);
+ break;
case NODE_COLON2:
- CHECK(COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", node->nd_head->nd_head));
- break;
+ CHECK(COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", node->nd_head->nd_head));
+ break;
default:
- COMPILE_ERROR(ERROR_ARGS "%s: invalid node in NODE_OP_CDECL",
- ruby_node_name(nd_type(node->nd_head)));
- return COMPILE_NG;
+ COMPILE_ERROR(ERROR_ARGS "%s: invalid node in NODE_OP_CDECL",
+ ruby_node_name(nd_type(node->nd_head)));
+ return COMPILE_NG;
}
mid = node->nd_head->nd_mid;
/* cref */
if (node->nd_aid == idOROP) {
- lassign = NEW_LABEL(line);
- ADD_INSN(ret, node, dup); /* cref cref */
- ADD_INSN3(ret, node, defined, INT2FIX(DEFINED_CONST_FROM),
- ID2SYM(mid), Qtrue); /* cref bool */
- ADD_INSNL(ret, node, branchunless, lassign); /* cref */
+ lassign = NEW_LABEL(line);
+ ADD_INSN(ret, node, dup); /* cref cref */
+ ADD_INSN3(ret, node, defined, INT2FIX(DEFINED_CONST_FROM),
+ ID2SYM(mid), Qtrue); /* cref bool */
+ ADD_INSNL(ret, node, branchunless, lassign); /* cref */
}
ADD_INSN(ret, node, dup); /* cref cref */
ADD_INSN1(ret, node, putobject, Qtrue);
ADD_INSN1(ret, node, getconstant, ID2SYM(mid)); /* cref obj */
if (node->nd_aid == idOROP || node->nd_aid == idANDOP) {
- lfin = NEW_LABEL(line);
- if (!popped) ADD_INSN(ret, node, dup); /* cref [obj] obj */
- if (node->nd_aid == idOROP)
- ADD_INSNL(ret, node, branchif, lfin);
- else /* idANDOP */
- ADD_INSNL(ret, node, branchunless, lfin);
- /* cref [obj] */
- if (!popped) ADD_INSN(ret, node, pop); /* cref */
- if (lassign) ADD_LABEL(ret, lassign);
- CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value));
- /* cref value */
- if (popped)
- ADD_INSN1(ret, node, topn, INT2FIX(1)); /* cref value cref */
- else {
- ADD_INSN1(ret, node, dupn, INT2FIX(2)); /* cref value cref value */
- ADD_INSN(ret, node, swap); /* cref value value cref */
- }
- ADD_INSN1(ret, node, setconstant, ID2SYM(mid)); /* cref [value] */
- ADD_LABEL(ret, lfin); /* cref [value] */
- if (!popped) ADD_INSN(ret, node, swap); /* [value] cref */
- ADD_INSN(ret, node, pop); /* [value] */
+ lfin = NEW_LABEL(line);
+ if (!popped) ADD_INSN(ret, node, dup); /* cref [obj] obj */
+ if (node->nd_aid == idOROP)
+ ADD_INSNL(ret, node, branchif, lfin);
+ else /* idANDOP */
+ ADD_INSNL(ret, node, branchunless, lfin);
+ /* cref [obj] */
+ if (!popped) ADD_INSN(ret, node, pop); /* cref */
+ if (lassign) ADD_LABEL(ret, lassign);
+ CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value));
+ /* cref value */
+ if (popped)
+ ADD_INSN1(ret, node, topn, INT2FIX(1)); /* cref value cref */
+ else {
+ ADD_INSN1(ret, node, dupn, INT2FIX(2)); /* cref value cref value */
+ ADD_INSN(ret, node, swap); /* cref value value cref */
+ }
+ ADD_INSN1(ret, node, setconstant, ID2SYM(mid)); /* cref [value] */
+ ADD_LABEL(ret, lfin); /* cref [value] */
+ if (!popped) ADD_INSN(ret, node, swap); /* [value] cref */
+ ADD_INSN(ret, node, pop); /* [value] */
}
else {
- CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value));
- /* cref obj value */
- ADD_CALL(ret, node, node->nd_aid, INT2FIX(1));
- /* cref value */
- ADD_INSN(ret, node, swap); /* value cref */
- if (!popped) {
- ADD_INSN1(ret, node, topn, INT2FIX(1)); /* value cref value */
- ADD_INSN(ret, node, swap); /* value value cref */
- }
- ADD_INSN1(ret, node, setconstant, ID2SYM(mid));
+ CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value));
+ /* cref obj value */
+ ADD_CALL(ret, node, node->nd_aid, INT2FIX(1));
+ /* cref value */
+ ADD_INSN(ret, node, swap); /* value cref */
+ if (!popped) {
+ ADD_INSN1(ret, node, topn, INT2FIX(1)); /* value cref value */
+ ADD_INSN(ret, node, swap); /* value value cref */
+ }
+ ADD_INSN1(ret, node, setconstant, ID2SYM(mid));
}
return COMPILE_OK;
}
@@ -8632,39 +8895,40 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
LABEL *lassign;
if (type == NODE_OP_ASGN_OR && !nd_type_p(node->nd_head, NODE_IVAR)) {
- LABEL *lfinish[2];
- lfinish[0] = lfin;
- lfinish[1] = 0;
- defined_expr(iseq, ret, node->nd_head, lfinish, Qfalse);
- lassign = lfinish[1];
- if (!lassign) {
- lassign = NEW_LABEL(line);
- }
- ADD_INSNL(ret, node, branchunless, lassign);
+ LABEL *lfinish[2];
+ lfinish[0] = lfin;
+ lfinish[1] = 0;
+ defined_expr(iseq, ret, node->nd_head, lfinish, Qfalse);
+ lassign = lfinish[1];
+ if (!lassign) {
+ lassign = NEW_LABEL(line);
+ }
+ ADD_INSNL(ret, node, branchunless, lassign);
}
else {
- lassign = NEW_LABEL(line);
+ lassign = NEW_LABEL(line);
}
CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", node->nd_head));
- ADD_INSN(ret, node, dup);
+
+ if (!popped) {
+ ADD_INSN(ret, node, dup);
+ }
if (type == NODE_OP_ASGN_AND) {
- ADD_INSNL(ret, node, branchunless, lfin);
+ ADD_INSNL(ret, node, branchunless, lfin);
}
else {
- ADD_INSNL(ret, node, branchif, lfin);
+ ADD_INSNL(ret, node, branchif, lfin);
+ }
+
+ if (!popped) {
+ ADD_INSN(ret, node, pop);
}
- ADD_INSN(ret, node, pop);
ADD_LABEL(ret, lassign);
- CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value));
+ CHECK(COMPILE_(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value, popped));
ADD_LABEL(ret, lfin);
-
- if (popped) {
- /* we can apply more optimize */
- ADD_INSN(ret, node, pop);
- }
return COMPILE_OK;
}
@@ -8681,115 +8945,115 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
INIT_ANCHOR(args);
ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
if (type == NODE_SUPER) {
- VALUE vargc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
- CHECK(!NIL_P(vargc));
- argc = FIX2INT(vargc);
+ VALUE vargc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
+ CHECK(!NIL_P(vargc));
+ argc = FIX2INT(vargc);
}
else {
- /* NODE_ZSUPER */
- int i;
- const rb_iseq_t *liseq = body->local_iseq;
+ /* NODE_ZSUPER */
+ int i;
+ const rb_iseq_t *liseq = body->local_iseq;
const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(liseq);
- const struct rb_iseq_param_keyword *const local_kwd = local_body->param.keyword;
- int lvar_level = get_lvar_level(iseq);
-
- argc = local_body->param.lead_num;
-
- /* normal arguments */
- for (i = 0; i < local_body->param.lead_num; i++) {
- int idx = local_body->local_table_size - i;
- ADD_GETLOCAL(args, node, idx, lvar_level);
- }
-
- if (local_body->param.flags.has_opt) {
- /* optional arguments */
- int j;
- for (j = 0; j < local_body->param.opt_num; j++) {
- int idx = local_body->local_table_size - (i + j);
- ADD_GETLOCAL(args, node, idx, lvar_level);
- }
- i += j;
- argc = i;
- }
- if (local_body->param.flags.has_rest) {
- /* rest argument */
- int idx = local_body->local_table_size - local_body->param.rest_start;
- ADD_GETLOCAL(args, node, idx, lvar_level);
- ADD_INSN1(args, node, splatarray, Qfalse);
-
- argc = local_body->param.rest_start + 1;
- flag |= VM_CALL_ARGS_SPLAT;
- }
- if (local_body->param.flags.has_post) {
- /* post arguments */
- int post_len = local_body->param.post_num;
- int post_start = local_body->param.post_start;
-
- if (local_body->param.flags.has_rest) {
- int j;
- for (j=0; j<post_len; j++) {
- int idx = local_body->local_table_size - (post_start + j);
- ADD_GETLOCAL(args, node, idx, lvar_level);
- }
- ADD_INSN1(args, node, newarray, INT2FIX(j));
- ADD_INSN (args, node, concatarray);
- /* argc is settled at above */
- }
- else {
- int j;
- for (j=0; j<post_len; j++) {
- int idx = local_body->local_table_size - (post_start + j);
- ADD_GETLOCAL(args, node, idx, lvar_level);
- }
- argc = post_len + post_start;
- }
- }
-
- if (local_body->param.flags.has_kw) { /* TODO: support keywords */
- int local_size = local_body->local_table_size;
- argc++;
-
- ADD_INSN1(args, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
-
- if (local_body->param.flags.has_kwrest) {
- int idx = local_body->local_table_size - local_kwd->rest_start;
- ADD_GETLOCAL(args, node, idx, lvar_level);
+ const struct rb_iseq_param_keyword *const local_kwd = local_body->param.keyword;
+ int lvar_level = get_lvar_level(iseq);
+
+ argc = local_body->param.lead_num;
+
+ /* normal arguments */
+ for (i = 0; i < local_body->param.lead_num; i++) {
+ int idx = local_body->local_table_size - i;
+ ADD_GETLOCAL(args, node, idx, lvar_level);
+ }
+
+ if (local_body->param.flags.has_opt) {
+ /* optional arguments */
+ int j;
+ for (j = 0; j < local_body->param.opt_num; j++) {
+ int idx = local_body->local_table_size - (i + j);
+ ADD_GETLOCAL(args, node, idx, lvar_level);
+ }
+ i += j;
+ argc = i;
+ }
+ if (local_body->param.flags.has_rest) {
+ /* rest argument */
+ int idx = local_body->local_table_size - local_body->param.rest_start;
+ ADD_GETLOCAL(args, node, idx, lvar_level);
+ ADD_INSN1(args, node, splatarray, Qfalse);
+
+ argc = local_body->param.rest_start + 1;
+ flag |= VM_CALL_ARGS_SPLAT;
+ }
+ if (local_body->param.flags.has_post) {
+ /* post arguments */
+ int post_len = local_body->param.post_num;
+ int post_start = local_body->param.post_start;
+
+ if (local_body->param.flags.has_rest) {
+ int j;
+ for (j=0; j<post_len; j++) {
+ int idx = local_body->local_table_size - (post_start + j);
+ ADD_GETLOCAL(args, node, idx, lvar_level);
+ }
+ ADD_INSN1(args, node, newarray, INT2FIX(j));
+ ADD_INSN (args, node, concatarray);
+ /* argc is settled at above */
+ }
+ else {
+ int j;
+ for (j=0; j<post_len; j++) {
+ int idx = local_body->local_table_size - (post_start + j);
+ ADD_GETLOCAL(args, node, idx, lvar_level);
+ }
+ argc = post_len + post_start;
+ }
+ }
+
+ if (local_body->param.flags.has_kw) { /* TODO: support keywords */
+ int local_size = local_body->local_table_size;
+ argc++;
+
+ ADD_INSN1(args, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+
+ if (local_body->param.flags.has_kwrest) {
+ int idx = local_body->local_table_size - local_kwd->rest_start;
+ ADD_GETLOCAL(args, node, idx, lvar_level);
if (local_kwd->num > 0) {
ADD_SEND (args, node, rb_intern("dup"), INT2FIX(0));
flag |= VM_CALL_KW_SPLAT_MUT;
}
- }
- else {
- ADD_INSN1(args, node, newhash, INT2FIX(0));
+ }
+ else {
+ ADD_INSN1(args, node, newhash, INT2FIX(0));
flag |= VM_CALL_KW_SPLAT_MUT;
- }
- for (i = 0; i < local_kwd->num; ++i) {
- ID id = local_kwd->table[i];
- int idx = local_size - get_local_var_idx(liseq, id);
- ADD_INSN1(args, node, putobject, ID2SYM(id));
- ADD_GETLOCAL(args, node, idx, lvar_level);
- }
- ADD_SEND(args, node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
- if (local_body->param.flags.has_rest) {
- ADD_INSN1(args, node, newarray, INT2FIX(1));
- ADD_INSN (args, node, concatarray);
- --argc;
- }
+ }
+ for (i = 0; i < local_kwd->num; ++i) {
+ ID id = local_kwd->table[i];
+ int idx = local_size - get_local_var_idx(liseq, id);
+ ADD_INSN1(args, node, putobject, ID2SYM(id));
+ ADD_GETLOCAL(args, node, idx, lvar_level);
+ }
+ ADD_SEND(args, node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
+ if (local_body->param.flags.has_rest) {
+ ADD_INSN1(args, node, newarray, INT2FIX(1));
+ ADD_INSN (args, node, concatarray);
+ --argc;
+ }
flag |= VM_CALL_KW_SPLAT;
- }
- else if (local_body->param.flags.has_kwrest) {
- int idx = local_body->local_table_size - local_kwd->rest_start;
- ADD_GETLOCAL(args, node, idx, lvar_level);
-
- if (local_body->param.flags.has_rest) {
- ADD_INSN1(args, node, newarray, INT2FIX(1));
- ADD_INSN (args, node, concatarray);
- }
- else {
- argc++;
- }
+ }
+ else if (local_body->param.flags.has_kwrest) {
+ int idx = local_body->local_table_size - local_kwd->rest_start;
+ ADD_GETLOCAL(args, node, idx, lvar_level);
+
+ if (local_body->param.flags.has_rest) {
+ ADD_INSN1(args, node, newarray, INT2FIX(1));
+ ADD_INSN (args, node, concatarray);
+ }
+ else {
+ argc++;
+ }
flag |= VM_CALL_KW_SPLAT;
- }
+ }
}
flag |= VM_CALL_SUPER | VM_CALL_FCALL;
@@ -8797,11 +9061,11 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
ADD_INSN(ret, node, putself);
ADD_SEQ(ret, args);
ADD_INSN2(ret, node, invokesuper,
- new_callinfo(iseq, 0, argc, flag, keywords, parent_block != NULL),
- parent_block);
+ new_callinfo(iseq, 0, argc, flag, keywords, parent_block != NULL),
+ parent_block);
if (popped) {
- ADD_INSN(ret, node, pop);
+ ADD_INSN(ret, node, pop);
}
return COMPILE_OK;
}
@@ -8820,24 +9084,24 @@ compile_yield(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
case ISEQ_TYPE_TOP:
case ISEQ_TYPE_MAIN:
case ISEQ_TYPE_CLASS:
- COMPILE_ERROR(ERROR_ARGS "Invalid yield");
- return COMPILE_NG;
+ COMPILE_ERROR(ERROR_ARGS "Invalid yield");
+ return COMPILE_NG;
default: /* valid */;
}
if (node->nd_head) {
- argc = setup_args(iseq, args, node->nd_head, &flag, &keywords);
- CHECK(!NIL_P(argc));
+ argc = setup_args(iseq, args, node->nd_head, &flag, &keywords);
+ CHECK(!NIL_P(argc));
}
else {
- argc = INT2FIX(0);
+ argc = INT2FIX(0);
}
ADD_SEQ(ret, args);
ADD_INSN1(ret, node, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), flag, keywords, FALSE));
if (popped) {
- ADD_INSN(ret, node, pop);
+ ADD_INSN(ret, node, pop);
}
int level = 0;
@@ -8860,18 +9124,18 @@ compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
INIT_ANCHOR(val);
switch ((int)type) {
case NODE_MATCH:
- ADD_INSN1(recv, node, putobject, node->nd_lit);
- ADD_INSN2(val, node, getspecial, INT2FIX(0),
- INT2FIX(0));
- break;
+ ADD_INSN1(recv, node, putobject, node->nd_lit);
+ ADD_INSN2(val, node, getspecial, INT2FIX(0),
+ INT2FIX(0));
+ break;
case NODE_MATCH2:
- CHECK(COMPILE(recv, "receiver", node->nd_recv));
- CHECK(COMPILE(val, "value", node->nd_value));
- break;
+ CHECK(COMPILE(recv, "receiver", node->nd_recv));
+ CHECK(COMPILE(val, "value", node->nd_value));
+ break;
case NODE_MATCH3:
- CHECK(COMPILE(recv, "receiver", node->nd_value));
- CHECK(COMPILE(val, "value", node->nd_recv));
- break;
+ CHECK(COMPILE(recv, "receiver", node->nd_value));
+ CHECK(COMPILE(val, "value", node->nd_recv));
+ break;
}
ADD_SEQ(ret, recv);
@@ -8879,11 +9143,11 @@ compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
ADD_SEND(ret, node, idEqTilde, INT2FIX(1));
if (node->nd_args) {
- compile_named_capture_assign(iseq, ret, node->nd_args);
+ compile_named_capture_assign(iseq, ret, node->nd_args);
}
if (popped) {
- ADD_INSN(ret, node, pop);
+ ADD_INSN(ret, node, pop);
}
return COMPILE_OK;
}
@@ -8891,46 +9155,41 @@ compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
static int
compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
- const int line = nd_line(node);
if (rb_is_const_id(node->nd_mid)) {
- /* constant */
- LABEL *lend = NEW_LABEL(line);
- int ic_index = ISEQ_BODY(iseq)->ic_size++;
-
- DECL_ANCHOR(pref);
- DECL_ANCHOR(body);
-
- INIT_ANCHOR(pref);
- INIT_ANCHOR(body);
- CHECK(compile_const_prefix(iseq, node, pref, body));
- if (LIST_INSN_SIZE_ZERO(pref)) {
- if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- ADD_INSN2(ret, node, opt_getinlinecache, lend, INT2FIX(ic_index));
- }
- else {
- ADD_INSN(ret, node, putnil);
- }
-
- ADD_SEQ(ret, body);
-
- if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- ADD_INSN1(ret, node, opt_setinlinecache, INT2FIX(ic_index));
- ADD_LABEL(ret, lend);
- }
- }
- else {
- ADD_SEQ(ret, pref);
- ADD_SEQ(ret, body);
- }
+ /* constant */
+ VALUE segments;
+ if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache &&
+ (segments = collect_const_segments(iseq, node))) {
+ ISEQ_BODY(iseq)->ic_size++;
+ ADD_INSN1(ret, node, opt_getconstant_path, segments);
+ RB_OBJ_WRITTEN(iseq, Qundef, segments);
+ }
+ else {
+ /* constant */
+ DECL_ANCHOR(pref);
+ DECL_ANCHOR(body);
+
+ INIT_ANCHOR(pref);
+ INIT_ANCHOR(body);
+ CHECK(compile_const_prefix(iseq, node, pref, body));
+ if (LIST_INSN_SIZE_ZERO(pref)) {
+ ADD_INSN(ret, node, putnil);
+ ADD_SEQ(ret, body);
+ }
+ else {
+ ADD_SEQ(ret, pref);
+ ADD_SEQ(ret, body);
+ }
+ }
}
else {
- /* function call */
- ADD_CALL_RECEIVER(ret, node);
- CHECK(COMPILE(ret, "colon2#nd_head", node->nd_head));
- ADD_CALL(ret, node, node->nd_mid, INT2FIX(1));
+ /* function call */
+ ADD_CALL_RECEIVER(ret, node);
+ CHECK(COMPILE(ret, "colon2#nd_head", node->nd_head));
+ ADD_CALL(ret, node, node->nd_mid, INT2FIX(1));
}
if (popped) {
- ADD_INSN(ret, node, pop);
+ ADD_INSN(ret, node, pop);
}
return COMPILE_OK;
}
@@ -8938,29 +9197,23 @@ compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
static int
compile_colon3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
- const int line = nd_line(node);
- LABEL *lend = NEW_LABEL(line);
- int ic_index = ISEQ_BODY(iseq)->ic_size++;
-
debugi("colon3#nd_mid", node->nd_mid);
/* add cache insn */
if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- ADD_INSN2(ret, node, opt_getinlinecache, lend, INT2FIX(ic_index));
- ADD_INSN(ret, node, pop);
+ ISEQ_BODY(iseq)->ic_size++;
+ VALUE segments = rb_ary_new_from_args(2, ID2SYM(idNULL), ID2SYM(node->nd_mid));
+ ADD_INSN1(ret, node, opt_getconstant_path, segments);
+ RB_OBJ_WRITTEN(iseq, Qundef, segments);
}
-
- ADD_INSN1(ret, node, putobject, rb_cObject);
- ADD_INSN1(ret, node, putobject, Qtrue);
- ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_mid));
-
- if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- ADD_INSN1(ret, node, opt_setinlinecache, INT2FIX(ic_index));
- ADD_LABEL(ret, lend);
+ else {
+ ADD_INSN1(ret, node, putobject, rb_cObject);
+ ADD_INSN1(ret, node, putobject, Qtrue);
+ ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_mid));
}
if (popped) {
- ADD_INSN(ret, node, pop);
+ ADD_INSN(ret, node, pop);
}
return COMPILE_OK;
}
@@ -8973,20 +9226,20 @@ compile_dots(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
const NODE *e = node->nd_end;
if (optimizable_range_item_p(b) && optimizable_range_item_p(e)) {
- if (!popped) {
+ if (!popped) {
VALUE bv = nd_type_p(b, NODE_LIT) ? b->nd_lit : Qnil;
VALUE ev = nd_type_p(e, NODE_LIT) ? e->nd_lit : Qnil;
- VALUE val = rb_range_new(bv, ev, excl);
- ADD_INSN1(ret, node, putobject, val);
- RB_OBJ_WRITTEN(iseq, Qundef, val);
- }
+ VALUE val = rb_range_new(bv, ev, excl);
+ ADD_INSN1(ret, node, putobject, val);
+ RB_OBJ_WRITTEN(iseq, Qundef, val);
+ }
}
else {
- CHECK(COMPILE_(ret, "min", b, popped));
- CHECK(COMPILE_(ret, "max", e, popped));
- if (!popped) {
- ADD_INSN1(ret, node, newrange, flag);
- }
+ CHECK(COMPILE_(ret, "min", b, popped));
+ CHECK(COMPILE_(ret, "max", e, popped));
+ if (!popped) {
+ ADD_INSN1(ret, node, newrange, flag);
+ }
}
return COMPILE_OK;
}
@@ -8996,25 +9249,25 @@ compile_errinfo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
{
if (!popped) {
if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
- ADD_GETLOCAL(ret, node, LVAR_ERRINFO, 0);
- }
- else {
- const rb_iseq_t *ip = iseq;
- int level = 0;
- while (ip) {
+ ADD_GETLOCAL(ret, node, LVAR_ERRINFO, 0);
+ }
+ else {
+ const rb_iseq_t *ip = iseq;
+ int level = 0;
+ while (ip) {
if (ISEQ_BODY(ip)->type == ISEQ_TYPE_RESCUE) {
- break;
- }
+ break;
+ }
ip = ISEQ_BODY(ip)->parent_iseq;
- level++;
- }
- if (ip) {
- ADD_GETLOCAL(ret, node, LVAR_ERRINFO, level);
- }
- else {
- ADD_INSN(ret, node, putnil);
- }
- }
+ level++;
+ }
+ if (ip) {
+ ADD_GETLOCAL(ret, node, LVAR_ERRINFO, level);
+ }
+ else {
+ ADD_INSN(ret, node, putnil);
+ }
+ }
}
return COMPILE_OK;
}
@@ -9027,29 +9280,29 @@ compile_kw_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
const NODE *default_value = node->nd_body->nd_value;
if (default_value == NODE_SPECIAL_REQUIRED_KEYWORD) {
- /* required argument. do nothing */
- COMPILE_ERROR(ERROR_ARGS "unreachable");
- return COMPILE_NG;
+ /* required argument. do nothing */
+ COMPILE_ERROR(ERROR_ARGS "unreachable");
+ return COMPILE_NG;
}
else if (nd_type_p(default_value, NODE_LIT) ||
- nd_type_p(default_value, NODE_NIL) ||
- nd_type_p(default_value, NODE_TRUE) ||
- nd_type_p(default_value, NODE_FALSE)) {
- COMPILE_ERROR(ERROR_ARGS "unreachable");
- return COMPILE_NG;
+ nd_type_p(default_value, NODE_NIL) ||
+ nd_type_p(default_value, NODE_TRUE) ||
+ nd_type_p(default_value, NODE_FALSE)) {
+ COMPILE_ERROR(ERROR_ARGS "unreachable");
+ return COMPILE_NG;
}
else {
- /* if keywordcheck(_kw_bits, nth_keyword)
- * kw = default_value
- * end
- */
- int kw_bits_idx = body->local_table_size - body->param.keyword->bits_start;
- int keyword_idx = body->param.keyword->num;
-
- ADD_INSN2(ret, node, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(keyword_idx));
- ADD_INSNL(ret, node, branchif, end_label);
- CHECK(COMPILE_POPPED(ret, "keyword default argument", node->nd_body));
- ADD_LABEL(ret, end_label);
+ /* if keywordcheck(_kw_bits, nth_keyword)
+ * kw = default_value
+ * end
+ */
+ int kw_bits_idx = body->local_table_size - body->param.keyword->bits_start;
+ int keyword_idx = body->param.keyword->num;
+
+ ADD_INSN2(ret, node, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(keyword_idx));
+ ADD_INSNL(ret, node, branchif, end_label);
+ CHECK(COMPILE_POPPED(ret, "keyword default argument", node->nd_body));
+ ADD_LABEL(ret, end_label);
}
return COMPILE_OK;
}
@@ -9068,25 +9321,26 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
/* optimization shortcut
* obj["literal"] = value -> opt_aset_with(obj, "literal", value)
*/
- if (mid == idASET && !private_recv_p(node) && node->nd_args &&
- nd_type_p(node->nd_args, NODE_LIST) && node->nd_args->nd_alen == 2 &&
- nd_type_p(node->nd_args->nd_head, NODE_STR) &&
- ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
- !ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal &&
- ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction)
+ if (!ISEQ_COMPILE_DATA(iseq)->in_masgn &&
+ mid == idASET && !private_recv_p(node) && node->nd_args &&
+ nd_type_p(node->nd_args, NODE_LIST) && node->nd_args->nd_alen == 2 &&
+ nd_type_p(node->nd_args->nd_head, NODE_STR) &&
+ ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
+ !ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal &&
+ ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction)
{
- VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit);
- CHECK(COMPILE(ret, "recv", node->nd_recv));
- CHECK(COMPILE(ret, "value", node->nd_args->nd_next->nd_head));
- if (!popped) {
- ADD_INSN(ret, node, swap);
- ADD_INSN1(ret, node, topn, INT2FIX(1));
- }
- ADD_INSN2(ret, node, opt_aset_with, str,
- new_callinfo(iseq, idASET, 2, 0, NULL, FALSE));
- RB_OBJ_WRITTEN(iseq, Qundef, str);
- ADD_INSN(ret, node, pop);
- return COMPILE_OK;
+ VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit);
+ CHECK(COMPILE(ret, "recv", node->nd_recv));
+ CHECK(COMPILE(ret, "value", node->nd_args->nd_next->nd_head));
+ if (!popped) {
+ ADD_INSN(ret, node, swap);
+ ADD_INSN1(ret, node, topn, INT2FIX(1));
+ }
+ ADD_INSN2(ret, node, opt_aset_with, str,
+ new_callinfo(iseq, idASET, 2, 0, NULL, FALSE));
+ RB_OBJ_WRITTEN(iseq, Qundef, str);
+ ADD_INSN(ret, node, pop);
+ return COMPILE_OK;
}
INIT_ANCHOR(recv);
@@ -9102,38 +9356,38 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
debugp_param("nd_mid", ID2SYM(mid));
if (!rb_is_attrset_id(mid)) {
- /* safe nav attr */
- mid = rb_id_attrset(mid);
- else_label = qcall_branch_start(iseq, recv, &branches, node, node);
+ /* safe nav attr */
+ mid = rb_id_attrset(mid);
+ else_label = qcall_branch_start(iseq, recv, &branches, node, node);
}
if (!popped) {
- ADD_INSN(ret, node, putnil);
- ADD_SEQ(ret, recv);
- ADD_SEQ(ret, args);
-
- if (flag & VM_CALL_ARGS_BLOCKARG) {
- ADD_INSN1(ret, node, topn, INT2FIX(1));
- if (flag & VM_CALL_ARGS_SPLAT) {
- ADD_INSN1(ret, node, putobject, INT2FIX(-1));
- ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag));
- }
- ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 3));
- ADD_INSN (ret, node, pop);
- }
- else if (flag & VM_CALL_ARGS_SPLAT) {
- ADD_INSN(ret, node, dup);
- ADD_INSN1(ret, node, putobject, INT2FIX(-1));
- ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag));
- ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2));
- ADD_INSN (ret, node, pop);
- }
- else {
- ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 1));
- }
+ ADD_INSN(ret, node, putnil);
+ ADD_SEQ(ret, recv);
+ ADD_SEQ(ret, args);
+
+ if (flag & VM_CALL_ARGS_BLOCKARG) {
+ ADD_INSN1(ret, node, topn, INT2FIX(1));
+ if (flag & VM_CALL_ARGS_SPLAT) {
+ ADD_INSN1(ret, node, putobject, INT2FIX(-1));
+ ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag));
+ }
+ ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 3));
+ ADD_INSN (ret, node, pop);
+ }
+ else if (flag & VM_CALL_ARGS_SPLAT) {
+ ADD_INSN(ret, node, dup);
+ ADD_INSN1(ret, node, putobject, INT2FIX(-1));
+ ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag));
+ ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2));
+ ADD_INSN (ret, node, pop);
+ }
+ else {
+ ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 1));
+ }
}
else {
- ADD_SEQ(ret, recv);
- ADD_SEQ(ret, args);
+ ADD_SEQ(ret, recv);
+ ADD_SEQ(ret, args);
}
ADD_SEND_WITH_FLAG(ret, node, mid, argc, INT2FIX(flag));
qcall_branch_end(iseq, ret, else_label, branches, node, node);
@@ -9173,17 +9427,17 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
if (ISEQ_COMPILE_DATA(iseq)->last_line == line) {
- /* ignore */
+ /* ignore */
}
else {
- if (node->flags & NODE_FL_NEWLINE) {
- int event = RUBY_EVENT_LINE;
- ISEQ_COMPILE_DATA(iseq)->last_line = line;
- if (ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
- event |= RUBY_EVENT_COVERAGE_LINE;
- }
- ADD_TRACE(ret, event);
- }
+ if (node->flags & NODE_FL_NEWLINE) {
+ int event = RUBY_EVENT_LINE;
+ ISEQ_COMPILE_DATA(iseq)->last_line = line;
+ if (ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
+ event |= RUBY_EVENT_COVERAGE_LINE;
+ }
+ ADD_TRACE(ret, event);
+ }
}
debug_node_start(node);
@@ -9193,134 +9447,137 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
switch (type) {
case NODE_BLOCK:
CHECK(compile_block(iseq, ret, node, popped));
- break;
+ break;
case NODE_IF:
case NODE_UNLESS:
- CHECK(compile_if(iseq, ret, node, popped, type));
- break;
+ CHECK(compile_if(iseq, ret, node, popped, type));
+ break;
case NODE_CASE:
- CHECK(compile_case(iseq, ret, node, popped));
- break;
+ CHECK(compile_case(iseq, ret, node, popped));
+ break;
case NODE_CASE2:
- CHECK(compile_case2(iseq, ret, node, popped));
- break;
+ CHECK(compile_case2(iseq, ret, node, popped));
+ break;
case NODE_CASE3:
CHECK(compile_case3(iseq, ret, node, popped));
break;
case NODE_WHILE:
case NODE_UNTIL:
- CHECK(compile_loop(iseq, ret, node, popped, type));
- break;
+ CHECK(compile_loop(iseq, ret, node, popped, type));
+ break;
case NODE_FOR:
case NODE_ITER:
- CHECK(compile_iter(iseq, ret, node, popped));
- break;
+ CHECK(compile_iter(iseq, ret, node, popped));
+ break;
case NODE_FOR_MASGN:
- CHECK(compile_for_masgn(iseq, ret, node, popped));
- break;
+ CHECK(compile_for_masgn(iseq, ret, node, popped));
+ break;
case NODE_BREAK:
- CHECK(compile_break(iseq, ret, node, popped));
- break;
+ CHECK(compile_break(iseq, ret, node, popped));
+ break;
case NODE_NEXT:
- CHECK(compile_next(iseq, ret, node, popped));
- break;
+ CHECK(compile_next(iseq, ret, node, popped));
+ break;
case NODE_REDO:
- CHECK(compile_redo(iseq, ret, node, popped));
- break;
+ CHECK(compile_redo(iseq, ret, node, popped));
+ break;
case NODE_RETRY:
- CHECK(compile_retry(iseq, ret, node, popped));
- break;
+ CHECK(compile_retry(iseq, ret, node, popped));
+ break;
case NODE_BEGIN:{
- CHECK(COMPILE_(ret, "NODE_BEGIN", node->nd_body, popped));
- break;
+ CHECK(COMPILE_(ret, "NODE_BEGIN", node->nd_body, popped));
+ break;
}
case NODE_RESCUE:
- CHECK(compile_rescue(iseq, ret, node, popped));
- break;
+ CHECK(compile_rescue(iseq, ret, node, popped));
+ break;
case NODE_RESBODY:
- CHECK(compile_resbody(iseq, ret, node, popped));
- break;
+ CHECK(compile_resbody(iseq, ret, node, popped));
+ break;
case NODE_ENSURE:
- CHECK(compile_ensure(iseq, ret, node, popped));
- break;
+ CHECK(compile_ensure(iseq, ret, node, popped));
+ break;
case NODE_AND:
case NODE_OR:{
- LABEL *end_label = NEW_LABEL(line);
- CHECK(COMPILE(ret, "nd_1st", node->nd_1st));
- if (!popped) {
- ADD_INSN(ret, node, dup);
- }
- if (type == NODE_AND) {
- ADD_INSNL(ret, node, branchunless, end_label);
- }
- else {
- ADD_INSNL(ret, node, branchif, end_label);
- }
- if (!popped) {
- ADD_INSN(ret, node, pop);
- }
- CHECK(COMPILE_(ret, "nd_2nd", node->nd_2nd, popped));
- ADD_LABEL(ret, end_label);
- break;
+ LABEL *end_label = NEW_LABEL(line);
+ CHECK(COMPILE(ret, "nd_1st", node->nd_1st));
+ if (!popped) {
+ ADD_INSN(ret, node, dup);
+ }
+ if (type == NODE_AND) {
+ ADD_INSNL(ret, node, branchunless, end_label);
+ }
+ else {
+ ADD_INSNL(ret, node, branchif, end_label);
+ }
+ if (!popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ CHECK(COMPILE_(ret, "nd_2nd", node->nd_2nd, popped));
+ ADD_LABEL(ret, end_label);
+ break;
}
case NODE_MASGN:{
- compile_massign(iseq, ret, node, popped);
- break;
+ bool prev_in_masgn = ISEQ_COMPILE_DATA(iseq)->in_masgn;
+ ISEQ_COMPILE_DATA(iseq)->in_masgn = true;
+ compile_massign(iseq, ret, node, popped);
+ ISEQ_COMPILE_DATA(iseq)->in_masgn = prev_in_masgn;
+ break;
}
case NODE_LASGN:{
- ID id = node->nd_vid;
+ ID id = node->nd_vid;
int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
- debugs("lvar: %s idx: %d\n", rb_id2name(id), idx);
- CHECK(COMPILE(ret, "rvalue", node->nd_value));
+ debugs("lvar: %s idx: %d\n", rb_id2name(id), idx);
+ CHECK(COMPILE(ret, "rvalue", node->nd_value));
- if (!popped) {
- ADD_INSN(ret, node, dup);
- }
- ADD_SETLOCAL(ret, node, idx, get_lvar_level(iseq));
- break;
+ if (!popped) {
+ ADD_INSN(ret, node, dup);
+ }
+ ADD_SETLOCAL(ret, node, idx, get_lvar_level(iseq));
+ break;
}
case NODE_DASGN: {
- int idx, lv, ls;
- ID id = node->nd_vid;
- CHECK(COMPILE(ret, "dvalue", node->nd_value));
- debugi("dassn id", rb_id2str(id) ? id : '*');
-
- if (!popped) {
- ADD_INSN(ret, node, dup);
- }
-
- idx = get_dyna_var_idx(iseq, id, &lv, &ls);
-
- if (idx < 0) {
- COMPILE_ERROR(ERROR_ARGS "NODE_DASGN: unknown id (%"PRIsVALUE")",
- rb_id2str(id));
- goto ng;
- }
- ADD_SETLOCAL(ret, node, ls - idx, lv);
- break;
+ int idx, lv, ls;
+ ID id = node->nd_vid;
+ CHECK(COMPILE(ret, "dvalue", node->nd_value));
+ debugi("dassn id", rb_id2str(id) ? id : '*');
+
+ if (!popped) {
+ ADD_INSN(ret, node, dup);
+ }
+
+ idx = get_dyna_var_idx(iseq, id, &lv, &ls);
+
+ if (idx < 0) {
+ COMPILE_ERROR(ERROR_ARGS "NODE_DASGN: unknown id (%"PRIsVALUE")",
+ rb_id2str(id));
+ goto ng;
+ }
+ ADD_SETLOCAL(ret, node, ls - idx, lv);
+ break;
}
case NODE_GASGN:{
- CHECK(COMPILE(ret, "lvalue", node->nd_value));
+ CHECK(COMPILE(ret, "lvalue", node->nd_value));
- if (!popped) {
- ADD_INSN(ret, node, dup);
- }
- ADD_INSN1(ret, node, setglobal, ID2SYM(node->nd_entry));
- break;
+ if (!popped) {
+ ADD_INSN(ret, node, dup);
+ }
+ ADD_INSN1(ret, node, setglobal, ID2SYM(node->nd_entry));
+ break;
}
case NODE_IASGN:{
- CHECK(COMPILE(ret, "lvalue", node->nd_value));
- if (!popped) {
- ADD_INSN(ret, node, dup);
- }
- ADD_INSN2(ret, node, setinstancevariable,
- ID2SYM(node->nd_vid),
- get_ivar_ic_value(iseq,node->nd_vid));
- break;
+ CHECK(COMPILE(ret, "lvalue", node->nd_value));
+ if (!popped) {
+ ADD_INSN(ret, node, dup);
+ }
+ ADD_INSN2(ret, node, setinstancevariable,
+ ID2SYM(node->nd_vid),
+ get_ivar_ic_value(iseq,node->nd_vid));
+ break;
}
case NODE_CDECL:{
if (node->nd_vid) {
@@ -9330,12 +9587,12 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
ADD_INSN(ret, node, dup);
}
- ADD_INSN1(ret, node, putspecialobject,
- INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
+ ADD_INSN1(ret, node, putspecialobject,
+ INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
ADD_INSN1(ret, node, setconstant, ID2SYM(node->nd_vid));
- }
- else {
- compile_cpath(ret, iseq, node->nd_else);
+ }
+ else {
+ compile_cpath(ret, iseq, node->nd_else);
CHECK(COMPILE(ret, "lvalue", node->nd_value));
ADD_INSN(ret, node, swap);
@@ -9345,32 +9602,32 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
ADD_INSN1(ret, node, setconstant, ID2SYM(node->nd_else->nd_mid));
- }
- break;
+ }
+ break;
}
case NODE_CVASGN:{
- CHECK(COMPILE(ret, "cvasgn val", node->nd_value));
- if (!popped) {
- ADD_INSN(ret, node, dup);
- }
+ CHECK(COMPILE(ret, "cvasgn val", node->nd_value));
+ if (!popped) {
+ ADD_INSN(ret, node, dup);
+ }
ADD_INSN2(ret, node, setclassvariable,
ID2SYM(node->nd_vid),
get_cvar_ic_value(iseq,node->nd_vid));
- break;
+ break;
}
case NODE_OP_ASGN1:
- CHECK(compile_op_asgn1(iseq, ret, node, popped));
- break;
+ CHECK(compile_op_asgn1(iseq, ret, node, popped));
+ break;
case NODE_OP_ASGN2:
- CHECK(compile_op_asgn2(iseq, ret, node, popped));
- break;
+ CHECK(compile_op_asgn2(iseq, ret, node, popped));
+ break;
case NODE_OP_CDECL:
- CHECK(compile_op_cdecl(iseq, ret, node, popped));
- break;
+ CHECK(compile_op_cdecl(iseq, ret, node, popped));
+ break;
case NODE_OP_ASGN_AND:
case NODE_OP_ASGN_OR:
- CHECK(compile_op_log(iseq, ret, node, popped, type));
- break;
+ CHECK(compile_op_log(iseq, ret, node, popped, type));
+ break;
case NODE_CALL: /* obj.foo */
case NODE_OPCALL: /* foo[] */
if (compile_call_precheck_freeze(iseq, ret, node, node, popped) == TRUE) {
@@ -9385,271 +9642,272 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
break;
case NODE_SUPER:
case NODE_ZSUPER:
- CHECK(compile_super(iseq, ret, node, popped, type));
- break;
+ CHECK(compile_super(iseq, ret, node, popped, type));
+ break;
case NODE_LIST:{
CHECK(compile_array(iseq, ret, node, popped) >= 0);
- break;
+ break;
}
case NODE_ZLIST:{
- if (!popped) {
- ADD_INSN1(ret, node, newarray, INT2FIX(0));
- }
- break;
+ if (!popped) {
+ ADD_INSN1(ret, node, newarray, INT2FIX(0));
+ }
+ break;
}
case NODE_VALUES:{
- const NODE *n = node;
- if (popped) {
- COMPILE_ERROR(ERROR_ARGS "NODE_VALUES: must not be popped");
- }
- while (n) {
- CHECK(COMPILE(ret, "values item", n->nd_head));
- n = n->nd_next;
- }
- ADD_INSN1(ret, node, newarray, INT2FIX(node->nd_alen));
- break;
+ const NODE *n = node;
+ if (popped) {
+ COMPILE_ERROR(ERROR_ARGS "NODE_VALUES: must not be popped");
+ }
+ while (n) {
+ CHECK(COMPILE(ret, "values item", n->nd_head));
+ n = n->nd_next;
+ }
+ ADD_INSN1(ret, node, newarray, INT2FIX(node->nd_alen));
+ break;
}
case NODE_HASH:
CHECK(compile_hash(iseq, ret, node, FALSE, popped) >= 0);
break;
case NODE_RETURN:
- CHECK(compile_return(iseq, ret, node, popped));
- break;
+ CHECK(compile_return(iseq, ret, node, popped));
+ break;
case NODE_YIELD:
- CHECK(compile_yield(iseq, ret, node, popped));
- break;
+ CHECK(compile_yield(iseq, ret, node, popped));
+ break;
case NODE_LVAR:{
- if (!popped) {
- compile_lvar(iseq, ret, node, node->nd_vid);
- }
- break;
+ if (!popped) {
+ compile_lvar(iseq, ret, node, node->nd_vid);
+ }
+ break;
}
case NODE_DVAR:{
- int lv, idx, ls;
- debugi("nd_vid", node->nd_vid);
- if (!popped) {
- idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls);
- if (idx < 0) {
- COMPILE_ERROR(ERROR_ARGS "unknown dvar (%"PRIsVALUE")",
- rb_id2str(node->nd_vid));
- goto ng;
- }
- ADD_GETLOCAL(ret, node, ls - idx, lv);
- }
- break;
+ int lv, idx, ls;
+ debugi("nd_vid", node->nd_vid);
+ if (!popped) {
+ idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls);
+ if (idx < 0) {
+ COMPILE_ERROR(ERROR_ARGS "unknown dvar (%"PRIsVALUE")",
+ rb_id2str(node->nd_vid));
+ goto ng;
+ }
+ ADD_GETLOCAL(ret, node, ls - idx, lv);
+ }
+ break;
}
case NODE_GVAR:{
- ADD_INSN1(ret, node, getglobal, ID2SYM(node->nd_entry));
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ ADD_INSN1(ret, node, getglobal, ID2SYM(node->nd_entry));
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_IVAR:{
- debugi("nd_vid", node->nd_vid);
- if (!popped) {
- ADD_INSN2(ret, node, getinstancevariable,
- ID2SYM(node->nd_vid),
- get_ivar_ic_value(iseq,node->nd_vid));
- }
- break;
+ debugi("nd_vid", node->nd_vid);
+ if (!popped) {
+ ADD_INSN2(ret, node, getinstancevariable,
+ ID2SYM(node->nd_vid),
+ get_ivar_ic_value(iseq,node->nd_vid));
+ }
+ break;
}
case NODE_CONST:{
- debugi("nd_vid", node->nd_vid);
-
- if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- LABEL *lend = NEW_LABEL(line);
- int ic_index = body->ic_size++;
+ debugi("nd_vid", node->nd_vid);
- ADD_INSN2(ret, node, opt_getinlinecache, lend, INT2FIX(ic_index));
- ADD_INSN1(ret, node, putobject, Qtrue);
- ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_vid));
- ADD_INSN1(ret, node, opt_setinlinecache, INT2FIX(ic_index));
- ADD_LABEL(ret, lend);
- }
- else {
- ADD_INSN(ret, node, putnil);
+ if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
+ body->ic_size++;
+ VALUE segments = rb_ary_new_from_args(1, ID2SYM(node->nd_vid));
+ ADD_INSN1(ret, node, opt_getconstant_path, segments);
+ RB_OBJ_WRITTEN(iseq, Qundef, segments);
+ }
+ else {
+ ADD_INSN(ret, node, putnil);
ADD_INSN1(ret, node, putobject, Qtrue);
ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_vid));
- }
+ }
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_CVAR:{
- if (!popped) {
- ADD_INSN2(ret, node, getclassvariable,
- ID2SYM(node->nd_vid),
- get_cvar_ic_value(iseq,node->nd_vid));
- }
- break;
+ if (!popped) {
+ ADD_INSN2(ret, node, getclassvariable,
+ ID2SYM(node->nd_vid),
+ get_cvar_ic_value(iseq,node->nd_vid));
+ }
+ break;
}
case NODE_NTH_REF:{
if (!popped) {
- if (!node->nd_nth) {
- ADD_INSN(ret, node, putnil);
- break;
- }
- ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
- INT2FIX(node->nd_nth << 1));
- }
- break;
+ if (!node->nd_nth) {
+ ADD_INSN(ret, node, putnil);
+ break;
+ }
+ ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
+ INT2FIX(node->nd_nth << 1));
+ }
+ break;
}
case NODE_BACK_REF:{
- if (!popped) {
- ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
- INT2FIX(0x01 | (node->nd_nth << 1)));
- }
- break;
+ if (!popped) {
+ ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
+ INT2FIX(0x01 | (node->nd_nth << 1)));
+ }
+ break;
}
case NODE_MATCH:
case NODE_MATCH2:
case NODE_MATCH3:
- CHECK(compile_match(iseq, ret, node, popped, type));
- break;
+ CHECK(compile_match(iseq, ret, node, popped, type));
+ break;
case NODE_LIT:{
- debugp_param("lit", node->nd_lit);
- if (!popped) {
- ADD_INSN1(ret, node, putobject, node->nd_lit);
+ debugp_param("lit", node->nd_lit);
+ if (!popped) {
+ if (UNLIKELY(node->nd_lit == rb_mRubyVMFrozenCore)) {
+ ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); // [Bug #20569]
+ }
+ else {
+ ADD_INSN1(ret, node, putobject, node->nd_lit);
+ }
RB_OBJ_WRITTEN(iseq, Qundef, node->nd_lit);
- }
- break;
+ }
+ break;
}
case NODE_STR:{
- debugp_param("nd_lit", node->nd_lit);
- if (!popped) {
- VALUE lit = node->nd_lit;
- if (!ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
- lit = rb_fstring(lit);
- ADD_INSN1(ret, node, putstring, lit);
+ debugp_param("nd_lit", node->nd_lit);
+ if (!popped) {
+ VALUE lit = node->nd_lit;
+ if (!ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
+ lit = rb_fstring(lit);
+ ADD_INSN1(ret, node, putstring, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit);
- }
- else {
- if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
- VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line));
- lit = rb_str_dup(lit);
- rb_ivar_set(lit, id_debug_created_info, rb_obj_freeze(debug_info));
- lit = rb_str_freeze(lit);
- }
- else {
- lit = rb_fstring(lit);
- }
- ADD_INSN1(ret, node, putobject, lit);
+ }
+ else {
+ if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
+ VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line));
+ lit = rb_str_dup(lit);
+ rb_ivar_set(lit, id_debug_created_info, rb_obj_freeze(debug_info));
+ lit = rb_str_freeze(lit);
+ }
+ else {
+ lit = rb_fstring(lit);
+ }
+ ADD_INSN1(ret, node, putobject, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit);
- }
- }
- break;
+ }
+ }
+ break;
}
case NODE_DSTR:{
- compile_dstr(iseq, ret, node);
+ compile_dstr(iseq, ret, node);
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_XSTR:{
- ADD_CALL_RECEIVER(ret, node);
+ ADD_CALL_RECEIVER(ret, node);
VALUE str = rb_fstring(node->nd_lit);
- ADD_INSN1(ret, node, putobject, str);
+ ADD_INSN1(ret, node, putobject, str);
RB_OBJ_WRITTEN(iseq, Qundef, str);
- ADD_CALL(ret, node, idBackquote, INT2FIX(1));
+ ADD_CALL(ret, node, idBackquote, INT2FIX(1));
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_DXSTR:{
- ADD_CALL_RECEIVER(ret, node);
- compile_dstr(iseq, ret, node);
- ADD_CALL(ret, node, idBackquote, INT2FIX(1));
-
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ ADD_CALL_RECEIVER(ret, node);
+ compile_dstr(iseq, ret, node);
+ ADD_CALL(ret, node, idBackquote, INT2FIX(1));
+
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_EVSTR:
- CHECK(compile_evstr(iseq, ret, node->nd_body, popped));
- break;
+ CHECK(compile_evstr(iseq, ret, node->nd_body, popped));
+ break;
case NODE_DREGX:{
- compile_dregx(iseq, ret, node);
+ compile_dregx(iseq, ret, node);
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_ONCE:{
- int ic_index = body->ise_size++;
- const rb_iseq_t *block_iseq;
- block_iseq = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, line);
+ int ic_index = body->ise_size++;
+ const rb_iseq_t *block_iseq;
+ block_iseq = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, line);
- ADD_INSN2(ret, node, once, block_iseq, INT2FIX(ic_index));
+ ADD_INSN2(ret, node, once, block_iseq, INT2FIX(ic_index));
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block_iseq);
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_ARGSCAT:{
- if (popped) {
- CHECK(COMPILE(ret, "argscat head", node->nd_head));
- ADD_INSN1(ret, node, splatarray, Qfalse);
- ADD_INSN(ret, node, pop);
- CHECK(COMPILE(ret, "argscat body", node->nd_body));
- ADD_INSN1(ret, node, splatarray, Qfalse);
- ADD_INSN(ret, node, pop);
- }
- else {
- CHECK(COMPILE(ret, "argscat head", node->nd_head));
- CHECK(COMPILE(ret, "argscat body", node->nd_body));
- ADD_INSN(ret, node, concatarray);
- }
- break;
+ if (popped) {
+ CHECK(COMPILE(ret, "argscat head", node->nd_head));
+ ADD_INSN1(ret, node, splatarray, Qfalse);
+ ADD_INSN(ret, node, pop);
+ CHECK(COMPILE(ret, "argscat body", node->nd_body));
+ ADD_INSN1(ret, node, splatarray, Qfalse);
+ ADD_INSN(ret, node, pop);
+ }
+ else {
+ CHECK(COMPILE(ret, "argscat head", node->nd_head));
+ CHECK(COMPILE(ret, "argscat body", node->nd_body));
+ ADD_INSN(ret, node, concatarray);
+ }
+ break;
}
case NODE_ARGSPUSH:{
- if (popped) {
- CHECK(COMPILE(ret, "argspush head", node->nd_head));
- ADD_INSN1(ret, node, splatarray, Qfalse);
- ADD_INSN(ret, node, pop);
- CHECK(COMPILE_(ret, "argspush body", node->nd_body, popped));
- }
- else {
- CHECK(COMPILE(ret, "argspush head", node->nd_head));
- CHECK(compile_array_1(iseq, ret, node->nd_body));
- ADD_INSN(ret, node, concatarray);
- }
- break;
+ if (popped) {
+ CHECK(COMPILE(ret, "argspush head", node->nd_head));
+ ADD_INSN1(ret, node, splatarray, Qfalse);
+ ADD_INSN(ret, node, pop);
+ CHECK(COMPILE_(ret, "argspush body", node->nd_body, popped));
+ }
+ else {
+ CHECK(COMPILE(ret, "argspush head", node->nd_head));
+ CHECK(compile_array_1(iseq, ret, node->nd_body));
+ ADD_INSN(ret, node, concatarray);
+ }
+ break;
}
case NODE_SPLAT:{
- CHECK(COMPILE(ret, "splat", node->nd_head));
- ADD_INSN1(ret, node, splatarray, Qtrue);
+ CHECK(COMPILE(ret, "splat", node->nd_head));
+ ADD_INSN1(ret, node, splatarray, Qtrue);
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_DEFN:{
ID mid = node->nd_mid;
- const rb_iseq_t *method_iseq = NEW_ISEQ(node->nd_defn,
+ const rb_iseq_t *method_iseq = NEW_ISEQ(node->nd_defn,
rb_id2str(mid),
- ISEQ_TYPE_METHOD, line);
+ ISEQ_TYPE_METHOD, line);
- debugp_param("defn/iseq", rb_iseqw_new(method_iseq));
+ debugp_param("defn/iseq", rb_iseqw_new(method_iseq));
ADD_INSN2(ret, node, definemethod, ID2SYM(mid), method_iseq);
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)method_iseq);
if (!popped) {
ADD_INSN1(ret, node, putobject, ID2SYM(mid));
- }
+ }
- break;
+ break;
}
case NODE_DEFS:{
ID mid = node->nd_mid;
@@ -9665,206 +9923,206 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
if (!popped) {
ADD_INSN1(ret, node, putobject, ID2SYM(mid));
}
- break;
+ break;
}
case NODE_ALIAS:{
- ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
- CHECK(COMPILE(ret, "alias arg1", node->nd_1st));
- CHECK(COMPILE(ret, "alias arg2", node->nd_2nd));
- ADD_SEND(ret, node, id_core_set_method_alias, INT2FIX(3));
-
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
+ CHECK(COMPILE(ret, "alias arg1", node->nd_1st));
+ CHECK(COMPILE(ret, "alias arg2", node->nd_2nd));
+ ADD_SEND(ret, node, id_core_set_method_alias, INT2FIX(3));
+
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_VALIAS:{
- ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- ADD_INSN1(ret, node, putobject, ID2SYM(node->nd_alias));
- ADD_INSN1(ret, node, putobject, ID2SYM(node->nd_orig));
- ADD_SEND(ret, node, id_core_set_variable_alias, INT2FIX(2));
-
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ ADD_INSN1(ret, node, putobject, ID2SYM(node->nd_alias));
+ ADD_INSN1(ret, node, putobject, ID2SYM(node->nd_orig));
+ ADD_SEND(ret, node, id_core_set_variable_alias, INT2FIX(2));
+
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_UNDEF:{
- ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
- CHECK(COMPILE(ret, "undef arg", node->nd_undef));
- ADD_SEND(ret, node, id_core_undef_method, INT2FIX(2));
-
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
+ CHECK(COMPILE(ret, "undef arg", node->nd_undef));
+ ADD_SEND(ret, node, id_core_undef_method, INT2FIX(2));
+
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_CLASS:{
- const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(node->nd_body,
- rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid))),
- ISEQ_TYPE_CLASS, line);
- const int flags = VM_DEFINECLASS_TYPE_CLASS |
- (node->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
- compile_cpath(ret, iseq, node->nd_cpath);
-
- CHECK(COMPILE(ret, "super", node->nd_super));
- ADD_INSN3(ret, node, defineclass, ID2SYM(node->nd_cpath->nd_mid), class_iseq, INT2FIX(flags));
+ const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(node->nd_body,
+ rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid))),
+ ISEQ_TYPE_CLASS, line);
+ const int flags = VM_DEFINECLASS_TYPE_CLASS |
+ (node->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
+ compile_cpath(ret, iseq, node->nd_cpath);
+
+ CHECK(COMPILE(ret, "super", node->nd_super));
+ ADD_INSN3(ret, node, defineclass, ID2SYM(node->nd_cpath->nd_mid), class_iseq, INT2FIX(flags));
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_MODULE:{
const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(node->nd_body,
- rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid))),
- ISEQ_TYPE_CLASS, line);
- const int flags = VM_DEFINECLASS_TYPE_MODULE |
- compile_cpath(ret, iseq, node->nd_cpath);
+ rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid))),
+ ISEQ_TYPE_CLASS, line);
+ const int flags = VM_DEFINECLASS_TYPE_MODULE |
+ compile_cpath(ret, iseq, node->nd_cpath);
- ADD_INSN (ret, node, putnil); /* dummy */
- ADD_INSN3(ret, node, defineclass, ID2SYM(node->nd_cpath->nd_mid), module_iseq, INT2FIX(flags));
+ ADD_INSN (ret, node, putnil); /* dummy */
+ ADD_INSN3(ret, node, defineclass, ID2SYM(node->nd_cpath->nd_mid), module_iseq, INT2FIX(flags));
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)module_iseq);
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_SCLASS:{
- ID singletonclass;
- const rb_iseq_t *singleton_class = NEW_ISEQ(node->nd_body, rb_fstring_lit("singleton class"),
- ISEQ_TYPE_CLASS, line);
-
- CHECK(COMPILE(ret, "sclass#recv", node->nd_recv));
- ADD_INSN (ret, node, putnil);
- CONST_ID(singletonclass, "singletonclass");
- ADD_INSN3(ret, node, defineclass,
- ID2SYM(singletonclass), singleton_class,
- INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS));
+ ID singletonclass;
+ const rb_iseq_t *singleton_class = NEW_ISEQ(node->nd_body, rb_fstring_lit("singleton class"),
+ ISEQ_TYPE_CLASS, line);
+
+ CHECK(COMPILE(ret, "sclass#recv", node->nd_recv));
+ ADD_INSN (ret, node, putnil);
+ CONST_ID(singletonclass, "singletonclass");
+ ADD_INSN3(ret, node, defineclass,
+ ID2SYM(singletonclass), singleton_class,
+ INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS));
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_class);
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_COLON2:
- CHECK(compile_colon2(iseq, ret, node, popped));
- break;
+ CHECK(compile_colon2(iseq, ret, node, popped));
+ break;
case NODE_COLON3:
- CHECK(compile_colon3(iseq, ret, node, popped));
- break;
+ CHECK(compile_colon3(iseq, ret, node, popped));
+ break;
case NODE_DOT2:
- CHECK(compile_dots(iseq, ret, node, popped, FALSE));
- break;
+ CHECK(compile_dots(iseq, ret, node, popped, FALSE));
+ break;
case NODE_DOT3:
- CHECK(compile_dots(iseq, ret, node, popped, TRUE));
- break;
+ CHECK(compile_dots(iseq, ret, node, popped, TRUE));
+ break;
case NODE_FLIP2:
case NODE_FLIP3:{
- LABEL *lend = NEW_LABEL(line);
- LABEL *ltrue = NEW_LABEL(line);
- LABEL *lfalse = NEW_LABEL(line);
- CHECK(compile_flip_flop(iseq, ret, node, type == NODE_FLIP2,
- ltrue, lfalse));
- ADD_LABEL(ret, ltrue);
- ADD_INSN1(ret, node, putobject, Qtrue);
- ADD_INSNL(ret, node, jump, lend);
- ADD_LABEL(ret, lfalse);
- ADD_INSN1(ret, node, putobject, Qfalse);
- ADD_LABEL(ret, lend);
- break;
+ LABEL *lend = NEW_LABEL(line);
+ LABEL *ltrue = NEW_LABEL(line);
+ LABEL *lfalse = NEW_LABEL(line);
+ CHECK(compile_flip_flop(iseq, ret, node, type == NODE_FLIP2,
+ ltrue, lfalse));
+ ADD_LABEL(ret, ltrue);
+ ADD_INSN1(ret, node, putobject, Qtrue);
+ ADD_INSNL(ret, node, jump, lend);
+ ADD_LABEL(ret, lfalse);
+ ADD_INSN1(ret, node, putobject, Qfalse);
+ ADD_LABEL(ret, lend);
+ break;
}
case NODE_SELF:{
- if (!popped) {
- ADD_INSN(ret, node, putself);
- }
- break;
+ if (!popped) {
+ ADD_INSN(ret, node, putself);
+ }
+ break;
}
case NODE_NIL:{
- if (!popped) {
- ADD_INSN(ret, node, putnil);
- }
- break;
+ if (!popped) {
+ ADD_INSN(ret, node, putnil);
+ }
+ break;
}
case NODE_TRUE:{
- if (!popped) {
- ADD_INSN1(ret, node, putobject, Qtrue);
- }
- break;
+ if (!popped) {
+ ADD_INSN1(ret, node, putobject, Qtrue);
+ }
+ break;
}
case NODE_FALSE:{
- if (!popped) {
- ADD_INSN1(ret, node, putobject, Qfalse);
- }
- break;
+ if (!popped) {
+ ADD_INSN1(ret, node, putobject, Qfalse);
+ }
+ break;
}
case NODE_ERRINFO:
- CHECK(compile_errinfo(iseq, ret, node, popped));
- break;
+ CHECK(compile_errinfo(iseq, ret, node, popped));
+ break;
case NODE_DEFINED:
- if (!popped) {
- CHECK(compile_defined_expr(iseq, ret, node, Qtrue));
- }
- break;
+ if (!popped) {
+ CHECK(compile_defined_expr(iseq, ret, node, Qtrue));
+ }
+ break;
case NODE_POSTEXE:{
- /* compiled to:
- * ONCE{ rb_mRubyVMFrozenCore::core#set_postexe{ ... } }
- */
- int is_index = body->ise_size++;
+ /* compiled to:
+ * ONCE{ rb_mRubyVMFrozenCore::core#set_postexe{ ... } }
+ */
+ int is_index = body->ise_size++;
struct rb_iseq_new_with_callback_callback_func *ifunc =
rb_iseq_new_with_callback_new_callback(build_postexe_iseq, node->nd_body);
- const rb_iseq_t *once_iseq =
+ const rb_iseq_t *once_iseq =
new_child_iseq_with_callback(iseq, ifunc,
- rb_fstring(make_name_for_block(iseq)), iseq, ISEQ_TYPE_BLOCK, line);
+ rb_fstring(make_name_for_block(iseq)), iseq, ISEQ_TYPE_BLOCK, line);
- ADD_INSN2(ret, node, once, once_iseq, INT2FIX(is_index));
+ ADD_INSN2(ret, node, once, once_iseq, INT2FIX(is_index));
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)once_iseq);
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_KW_ARG:
- CHECK(compile_kw_arg(iseq, ret, node, popped));
- break;
+ CHECK(compile_kw_arg(iseq, ret, node, popped));
+ break;
case NODE_DSYM:{
- compile_dstr(iseq, ret, node);
- if (!popped) {
- ADD_INSN(ret, node, intern);
- }
- else {
- ADD_INSN(ret, node, pop);
- }
- break;
+ compile_dstr(iseq, ret, node);
+ if (!popped) {
+ ADD_INSN(ret, node, intern);
+ }
+ else {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
case NODE_ATTRASGN:
- CHECK(compile_attrasgn(iseq, ret, node, popped));
- break;
+ CHECK(compile_attrasgn(iseq, ret, node, popped));
+ break;
case NODE_LAMBDA:{
- /* compile same as lambda{...} */
- const rb_iseq_t *block = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
- VALUE argc = INT2FIX(0);
+ /* compile same as lambda{...} */
+ const rb_iseq_t *block = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
+ VALUE argc = INT2FIX(0);
- ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- ADD_CALL_WITH_BLOCK(ret, node, idLambda, argc, block);
+ ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ ADD_CALL_WITH_BLOCK(ret, node, idLambda, argc, block);
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block);
- if (popped) {
- ADD_INSN(ret, node, pop);
- }
- break;
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+ break;
}
default:
- UNKNOWN_NODE("iseq_compile_each", node, COMPILE_NG);
+ UNKNOWN_NODE("iseq_compile_each", node, COMPILE_NG);
ng:
- debug_node_end();
- return COMPILE_NG;
+ debug_node_end();
+ return COMPILE_NG;
}
debug_node_end();
@@ -9892,15 +10150,15 @@ opobj_inspect(VALUE obj)
{
if (!SPECIAL_CONST_P(obj) && !RBASIC_CLASS(obj)) {
switch (BUILTIN_TYPE(obj)) {
- case T_STRING:
- obj = rb_str_new_cstr(RSTRING_PTR(obj));
- break;
- case T_ARRAY:
- obj = rb_ary_dup(obj);
- break;
+ case T_STRING:
+ obj = rb_str_new_cstr(RSTRING_PTR(obj));
+ break;
+ case T_ARRAY:
+ obj = rb_ary_dup(obj);
+ break;
default:
break;
- }
+ }
}
return rb_inspect(obj);
}
@@ -9913,86 +10171,92 @@ insn_data_to_s_detail(INSN *iobj)
VALUE str = rb_sprintf("%-20s ", insn_name(iobj->insn_id));
if (iobj->operands) {
- const char *types = insn_op_types(iobj->insn_id);
- int j;
-
- for (j = 0; types[j]; j++) {
- char type = types[j];
-
- switch (type) {
- case TS_OFFSET: /* label(destination position) */
- {
- LABEL *lobj = (LABEL *)OPERAND_AT(iobj, j);
- rb_str_catf(str, LABEL_FORMAT, lobj->label_no);
- break;
- }
- break;
- case TS_ISEQ: /* iseq */
- {
- rb_iseq_t *iseq = (rb_iseq_t *)OPERAND_AT(iobj, j);
- VALUE val = Qnil;
- if (0 && iseq) { /* TODO: invalidate now */
- val = (VALUE)iseq;
- }
- rb_str_concat(str, opobj_inspect(val));
- }
- break;
- case TS_LINDEX:
- case TS_NUM: /* ulong */
- case TS_VALUE: /* VALUE */
- {
- VALUE v = OPERAND_AT(iobj, j);
+ const char *types = insn_op_types(iobj->insn_id);
+ int j;
+
+ for (j = 0; types[j]; j++) {
+ char type = types[j];
+
+ switch (type) {
+ case TS_OFFSET: /* label(destination position) */
+ {
+ LABEL *lobj = (LABEL *)OPERAND_AT(iobj, j);
+ rb_str_catf(str, LABEL_FORMAT, lobj->label_no);
+ break;
+ }
+ break;
+ case TS_ISEQ: /* iseq */
+ {
+ rb_iseq_t *iseq = (rb_iseq_t *)OPERAND_AT(iobj, j);
+ VALUE val = Qnil;
+ if (0 && iseq) { /* TODO: invalidate now */
+ val = (VALUE)iseq;
+ }
+ rb_str_concat(str, opobj_inspect(val));
+ }
+ break;
+ case TS_LINDEX:
+ case TS_NUM: /* ulong */
+ case TS_VALUE: /* VALUE */
+ {
+ VALUE v = OPERAND_AT(iobj, j);
if (!CLASS_OF(v))
rb_str_cat2(str, "<hidden>");
else {
rb_str_concat(str, opobj_inspect(v));
}
- break;
- }
- case TS_ID: /* ID */
- rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
- break;
- case TS_IC: /* inline cache */
- case TS_IVC: /* inline ivar cache */
- case TS_ICVARC: /* inline cvar cache */
- case TS_ISE: /* inline storage entry */
- rb_str_catf(str, "<ic:%d>", FIX2INT(OPERAND_AT(iobj, j)));
- break;
+ break;
+ }
+ case TS_ID: /* ID */
+ rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
+ break;
+ case TS_IC: /* inline cache */
+ rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
+ break;
+ case TS_IVC: /* inline ivar cache */
+ rb_str_catf(str, "<ivc:%d>", FIX2INT(OPERAND_AT(iobj, j)));
+ break;
+ case TS_ICVARC: /* inline cvar cache */
+ rb_str_catf(str, "<icvarc:%d>", FIX2INT(OPERAND_AT(iobj, j)));
+ break;
+ case TS_ISE: /* inline storage entry */
+ rb_str_catf(str, "<ise:%d>", FIX2INT(OPERAND_AT(iobj, j)));
+ break;
case TS_CALLDATA: /* we store these as call infos at compile time */
- {
+ {
const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, j);
rb_str_cat2(str, "<calldata:");
if (vm_ci_mid(ci)) rb_str_catf(str, "%"PRIsVALUE, rb_id2str(vm_ci_mid(ci)));
rb_str_catf(str, ", %d>", vm_ci_argc(ci));
- break;
- }
- case TS_CDHASH: /* case/when condition cache */
- rb_str_cat2(str, "<ch>");
- break;
- case TS_FUNCPTR:
- {
- void *func = (void *)OPERAND_AT(iobj, j);
+ break;
+ }
+ case TS_CDHASH: /* case/when condition cache */
+ rb_str_cat2(str, "<ch>");
+ break;
+ case TS_FUNCPTR:
+ {
+ void *func = (void *)OPERAND_AT(iobj, j);
#ifdef HAVE_DLADDR
- Dl_info info;
- if (dladdr(func, &info) && info.dli_sname) {
- rb_str_cat2(str, info.dli_sname);
- break;
- }
+ Dl_info info;
+ if (dladdr(func, &info) && info.dli_sname) {
+ rb_str_cat2(str, info.dli_sname);
+ break;
+ }
#endif
- rb_str_catf(str, "<%p>", func);
- }
- break;
+ rb_str_catf(str, "<%p>", func);
+ }
+ break;
case TS_BUILTIN:
rb_str_cat2(str, "<TS_BUILTIN>");
break;
- default:{
- rb_raise(rb_eSyntaxError, "unknown operand type: %c", type);
- }
- }
- if (types[j + 1]) {
- rb_str_cat2(str, ", ");
- }
- }
+ default:{
+ rb_raise(rb_eSyntaxError, "unknown operand type: %c", type);
+ }
+ }
+ if (types[j + 1]) {
+ rb_str_cat2(str, ", ");
+ }
+ }
}
return str;
}
@@ -10014,40 +10278,40 @@ dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr,
printf("-- raw disasm--------\n");
while (link) {
- if (curr) printf(curr == link ? "*" : " ");
- switch (link->type) {
- case ISEQ_ELEMENT_INSN:
- {
- iobj = (INSN *)link;
- str = insn_data_to_s_detail(iobj);
- printf(" %04d %-65s(%4u)\n", pos, StringValueCStr(str), iobj->insn_info.line_no);
- pos += insn_data_length(iobj);
- break;
- }
- case ISEQ_ELEMENT_LABEL:
- {
- lobj = (LABEL *)link;
- printf(LABEL_FORMAT" [sp: %d]%s\n", lobj->label_no, lobj->sp,
- dest == lobj ? " <---" : "");
- break;
- }
- case ISEQ_ELEMENT_TRACE:
- {
- TRACE *trace = (TRACE *)link;
- printf(" trace: %0x\n", trace->event);
- break;
- }
- case ISEQ_ELEMENT_ADJUST:
- {
- ADJUST *adjust = (ADJUST *)link;
- printf(" adjust: [label: %d]\n", adjust->label ? adjust->label->label_no : -1);
- break;
- }
- default:
- /* ignore */
- rb_raise(rb_eSyntaxError, "dump_disasm_list error: %ld\n", FIX2LONG(link->type));
- }
- link = link->next;
+ if (curr) printf(curr == link ? "*" : " ");
+ switch (link->type) {
+ case ISEQ_ELEMENT_INSN:
+ {
+ iobj = (INSN *)link;
+ str = insn_data_to_s_detail(iobj);
+ printf(" %04d %-65s(%4u)\n", pos, StringValueCStr(str), iobj->insn_info.line_no);
+ pos += insn_data_length(iobj);
+ break;
+ }
+ case ISEQ_ELEMENT_LABEL:
+ {
+ lobj = (LABEL *)link;
+ printf(LABEL_FORMAT" [sp: %d]%s\n", lobj->label_no, lobj->sp,
+ dest == lobj ? " <---" : "");
+ break;
+ }
+ case ISEQ_ELEMENT_TRACE:
+ {
+ TRACE *trace = (TRACE *)link;
+ printf(" trace: %0x\n", trace->event);
+ break;
+ }
+ case ISEQ_ELEMENT_ADJUST:
+ {
+ ADJUST *adjust = (ADJUST *)link;
+ printf(" adjust: [label: %d]\n", adjust->label ? adjust->label->label_no : -1);
+ break;
+ }
+ default:
+ /* ignore */
+ rb_raise(rb_eSyntaxError, "dump_disasm_list error: %ld\n", FIX2LONG(link->type));
+ }
+ link = link->next;
}
printf("---------------------\n");
fflush(stdout);
@@ -10065,7 +10329,7 @@ rb_insns_name_array(void)
VALUE ary = rb_ary_new_capa(VM_INSTRUCTION_SIZE);
int i;
for (i = 0; i < VM_INSTRUCTION_SIZE; i++) {
- rb_ary_push(ary, rb_fstring_cstr(insn_name(i)));
+ rb_ary_push(ary, rb_fstring_cstr(insn_name(i)));
}
return rb_obj_freeze(ary);
}
@@ -10078,11 +10342,11 @@ register_label(rb_iseq_t *iseq, struct st_table *labels_table, VALUE obj)
obj = rb_to_symbol_type(obj);
if (st_lookup(labels_table, obj, &tmp) == 0) {
- label = NEW_LABEL(0);
- st_insert(labels_table, obj, (st_data_t)label);
+ label = NEW_LABEL(0);
+ st_insert(labels_table, obj, (st_data_t)label);
}
else {
- label = (LABEL *)tmp;
+ label = (LABEL *)tmp;
}
LABEL_REF(label);
return label;
@@ -10095,12 +10359,12 @@ get_exception_sym2type(VALUE sym)
static VALUE symBreak, symRedo, symNext;
if (symRescue == 0) {
- symRescue = ID2SYM(rb_intern_const("rescue"));
- symEnsure = ID2SYM(rb_intern_const("ensure"));
- symRetry = ID2SYM(rb_intern_const("retry"));
- symBreak = ID2SYM(rb_intern_const("break"));
- symRedo = ID2SYM(rb_intern_const("redo"));
- symNext = ID2SYM(rb_intern_const("next"));
+ symRescue = ID2SYM(rb_intern_const("rescue"));
+ symEnsure = ID2SYM(rb_intern_const("ensure"));
+ symRetry = ID2SYM(rb_intern_const("retry"));
+ symBreak = ID2SYM(rb_intern_const("break"));
+ symRedo = ID2SYM(rb_intern_const("redo"));
+ symNext = ID2SYM(rb_intern_const("next"));
}
if (sym == symRescue) return CATCH_TYPE_RESCUE;
@@ -10115,25 +10379,25 @@ get_exception_sym2type(VALUE sym)
static int
iseq_build_from_ary_exception(rb_iseq_t *iseq, struct st_table *labels_table,
- VALUE exception)
+ VALUE exception)
{
int i;
for (i=0; i<RARRAY_LEN(exception); i++) {
- const rb_iseq_t *eiseq;
- VALUE v, type;
- LABEL *lstart, *lend, *lcont;
- unsigned int sp;
-
- v = rb_to_array_type(RARRAY_AREF(exception, i));
- if (RARRAY_LEN(v) != 6) {
- rb_raise(rb_eSyntaxError, "wrong exception entry");
- }
+ const rb_iseq_t *eiseq;
+ VALUE v, type;
+ LABEL *lstart, *lend, *lcont;
+ unsigned int sp;
+
+ v = rb_to_array_type(RARRAY_AREF(exception, i));
+ if (RARRAY_LEN(v) != 6) {
+ rb_raise(rb_eSyntaxError, "wrong exception entry");
+ }
type = get_exception_sym2type(RARRAY_AREF(v, 0));
if (NIL_P(RARRAY_AREF(v, 1))) {
- eiseq = NULL;
- }
- else {
+ eiseq = NULL;
+ }
+ else {
eiseq = rb_iseqw_to_iseq(rb_iseq_load(RARRAY_AREF(v, 1), (VALUE)iseq, Qnil));
}
@@ -10142,18 +10406,18 @@ iseq_build_from_ary_exception(rb_iseq_t *iseq, struct st_table *labels_table,
lcont = register_label(iseq, labels_table, RARRAY_AREF(v, 4));
sp = NUM2UINT(RARRAY_AREF(v, 5));
- /* TODO: Dirty Hack! Fix me */
- if (type == CATCH_TYPE_RESCUE ||
- type == CATCH_TYPE_BREAK ||
- type == CATCH_TYPE_NEXT) {
- ++sp;
- }
+ /* TODO: Dirty Hack! Fix me */
+ if (type == CATCH_TYPE_RESCUE ||
+ type == CATCH_TYPE_BREAK ||
+ type == CATCH_TYPE_NEXT) {
+ ++sp;
+ }
- lcont->sp = sp;
+ lcont->sp = sp;
- ADD_CATCH_ENTRY(type, lstart, lend, eiseq, lcont);
+ ADD_CATCH_ENTRY(type, lstart, lend, eiseq, lcont);
- RB_GC_GUARD(v);
+ RB_GC_GUARD(v);
}
return COMPILE_OK;
}
@@ -10166,7 +10430,7 @@ insn_make_insn_table(void)
table = st_init_numtable_with_size(VM_INSTRUCTION_SIZE);
for (i=0; i<VM_INSTRUCTION_SIZE; i++) {
- st_insert(table, ID2SYM(rb_intern_const(insn_name(i))), i);
+ st_insert(table, ID2SYM(rb_intern_const(insn_name(i))), i);
}
return table;
@@ -10179,13 +10443,13 @@ iseq_build_load_iseq(const rb_iseq_t *iseq, VALUE op)
const rb_iseq_t *loaded_iseq;
if (RB_TYPE_P(op, T_ARRAY)) {
- iseqw = rb_iseq_load(op, (VALUE)iseq, Qnil);
+ iseqw = rb_iseq_load(op, (VALUE)iseq, Qnil);
}
else if (CLASS_OF(op) == rb_cISeq) {
- iseqw = op;
+ iseqw = op;
}
else {
- rb_raise(rb_eSyntaxError, "ISEQ is required");
+ rb_raise(rb_eSyntaxError, "ISEQ is required");
}
loaded_iseq = rb_iseqw_to_iseq(iseqw);
@@ -10201,28 +10465,28 @@ iseq_build_callinfo_from_hash(rb_iseq_t *iseq, VALUE op)
struct rb_callinfo_kwarg *kw_arg = 0;
if (!NIL_P(op)) {
- VALUE vmid = rb_hash_aref(op, ID2SYM(rb_intern_const("mid")));
- VALUE vflag = rb_hash_aref(op, ID2SYM(rb_intern_const("flag")));
- VALUE vorig_argc = rb_hash_aref(op, ID2SYM(rb_intern_const("orig_argc")));
- VALUE vkw_arg = rb_hash_aref(op, ID2SYM(rb_intern_const("kw_arg")));
-
- if (!NIL_P(vmid)) mid = SYM2ID(vmid);
- if (!NIL_P(vflag)) flag = NUM2UINT(vflag);
- if (!NIL_P(vorig_argc)) orig_argc = FIX2INT(vorig_argc);
-
- if (!NIL_P(vkw_arg)) {
- int i;
- int len = RARRAY_LENINT(vkw_arg);
- size_t n = rb_callinfo_kwarg_bytes(len);
-
- kw_arg = xmalloc(n);
- kw_arg->keyword_len = len;
- for (i = 0; i < len; i++) {
- VALUE kw = RARRAY_AREF(vkw_arg, i);
- SYM2ID(kw); /* make immortal */
- kw_arg->keywords[i] = kw;
- }
- }
+ VALUE vmid = rb_hash_aref(op, ID2SYM(rb_intern_const("mid")));
+ VALUE vflag = rb_hash_aref(op, ID2SYM(rb_intern_const("flag")));
+ VALUE vorig_argc = rb_hash_aref(op, ID2SYM(rb_intern_const("orig_argc")));
+ VALUE vkw_arg = rb_hash_aref(op, ID2SYM(rb_intern_const("kw_arg")));
+
+ if (!NIL_P(vmid)) mid = SYM2ID(vmid);
+ if (!NIL_P(vflag)) flag = NUM2UINT(vflag);
+ if (!NIL_P(vorig_argc)) orig_argc = FIX2INT(vorig_argc);
+
+ if (!NIL_P(vkw_arg)) {
+ int i;
+ int len = RARRAY_LENINT(vkw_arg);
+ size_t n = rb_callinfo_kwarg_bytes(len);
+
+ kw_arg = xmalloc(n);
+ kw_arg->keyword_len = len;
+ for (i = 0; i < len; i++) {
+ VALUE kw = RARRAY_AREF(vkw_arg, i);
+ SYM2ID(kw); /* make immortal */
+ kw_arg->keywords[i] = kw;
+ }
+ }
}
const struct rb_callinfo *ci = new_callinfo(iseq, mid, orig_argc, flag, kw_arg, (flag & VM_CALL_ARGS_SIMPLE) == 0);
@@ -10234,20 +10498,20 @@ static rb_event_flag_t
event_name_to_flag(VALUE sym)
{
#define CHECK_EVENT(ev) if (sym == ID2SYM(rb_intern_const(#ev))) return ev;
- CHECK_EVENT(RUBY_EVENT_LINE);
- CHECK_EVENT(RUBY_EVENT_CLASS);
- CHECK_EVENT(RUBY_EVENT_END);
- CHECK_EVENT(RUBY_EVENT_CALL);
- CHECK_EVENT(RUBY_EVENT_RETURN);
- CHECK_EVENT(RUBY_EVENT_B_CALL);
- CHECK_EVENT(RUBY_EVENT_B_RETURN);
+ CHECK_EVENT(RUBY_EVENT_LINE);
+ CHECK_EVENT(RUBY_EVENT_CLASS);
+ CHECK_EVENT(RUBY_EVENT_END);
+ CHECK_EVENT(RUBY_EVENT_CALL);
+ CHECK_EVENT(RUBY_EVENT_RETURN);
+ CHECK_EVENT(RUBY_EVENT_B_CALL);
+ CHECK_EVENT(RUBY_EVENT_B_RETURN);
#undef CHECK_EVENT
return RUBY_EVENT_NONE;
}
static int
iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
- VALUE body, VALUE node_ids, VALUE labels_wrapper)
+ VALUE body, VALUE node_ids, VALUE labels_wrapper)
{
/* TODO: body should be frozen */
long i, len = RARRAY_LEN(body);
@@ -10262,52 +10526,52 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
static struct st_table *insn_table;
if (insn_table == 0) {
- insn_table = insn_make_insn_table();
+ insn_table = insn_make_insn_table();
}
for (i=0; i<len; i++) {
VALUE obj = RARRAY_AREF(body, i);
- if (SYMBOL_P(obj)) {
- rb_event_flag_t event;
- if ((event = event_name_to_flag(obj)) != RUBY_EVENT_NONE) {
- ADD_TRACE(anchor, event);
- }
- else {
- LABEL *label = register_label(iseq, labels_table, obj);
- ADD_LABEL(anchor, label);
- }
- }
- else if (FIXNUM_P(obj)) {
- line_no = NUM2INT(obj);
- }
- else if (RB_TYPE_P(obj, T_ARRAY)) {
- VALUE *argv = 0;
- int argc = RARRAY_LENINT(obj) - 1;
- st_data_t insn_id;
- VALUE insn;
+ if (SYMBOL_P(obj)) {
+ rb_event_flag_t event;
+ if ((event = event_name_to_flag(obj)) != RUBY_EVENT_NONE) {
+ ADD_TRACE(anchor, event);
+ }
+ else {
+ LABEL *label = register_label(iseq, labels_table, obj);
+ ADD_LABEL(anchor, label);
+ }
+ }
+ else if (FIXNUM_P(obj)) {
+ line_no = NUM2INT(obj);
+ }
+ else if (RB_TYPE_P(obj, T_ARRAY)) {
+ VALUE *argv = 0;
+ int argc = RARRAY_LENINT(obj) - 1;
+ st_data_t insn_id;
+ VALUE insn;
if (node_ids) {
node_id = NUM2INT(rb_ary_entry(node_ids, insn_idx++));
}
- insn = (argc < 0) ? Qnil : RARRAY_AREF(obj, 0);
- if (st_lookup(insn_table, (st_data_t)insn, &insn_id) == 0) {
- /* TODO: exception */
- COMPILE_ERROR(iseq, line_no,
- "unknown instruction: %+"PRIsVALUE, insn);
- ret = COMPILE_NG;
- break;
- }
+ insn = (argc < 0) ? Qnil : RARRAY_AREF(obj, 0);
+ if (st_lookup(insn_table, (st_data_t)insn, &insn_id) == 0) {
+ /* TODO: exception */
+ COMPILE_ERROR(iseq, line_no,
+ "unknown instruction: %+"PRIsVALUE, insn);
+ ret = COMPILE_NG;
+ break;
+ }
- if (argc != insn_len((VALUE)insn_id)-1) {
- COMPILE_ERROR(iseq, line_no,
- "operand size mismatch");
- ret = COMPILE_NG;
- break;
- }
+ if (argc != insn_len((VALUE)insn_id)-1) {
+ COMPILE_ERROR(iseq, line_no,
+ "operand size mismatch");
+ ret = COMPILE_NG;
+ break;
+ }
- if (argc > 0) {
+ if (argc > 0) {
argv = compile_data_calloc2(iseq, sizeof(VALUE), argc);
// add element before operand setup to make GC root
@@ -10316,109 +10580,120 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
(LINK_ELEMENT*)new_insn_core(iseq, &dummy_line_node,
(enum ruby_vminsn_type)insn_id, argc, argv));
- for (j=0; j<argc; j++) {
- VALUE op = rb_ary_entry(obj, j+1);
- switch (insn_op_type((VALUE)insn_id, j)) {
- case TS_OFFSET: {
- LABEL *label = register_label(iseq, labels_table, op);
- argv[j] = (VALUE)label;
- break;
- }
- case TS_LINDEX:
- case TS_NUM:
- (void)NUM2INT(op);
- argv[j] = op;
- break;
- case TS_VALUE:
- argv[j] = op;
- RB_OBJ_WRITTEN(iseq, Qundef, op);
- break;
- case TS_ISEQ:
- {
- if (op != Qnil) {
- VALUE v = (VALUE)iseq_build_load_iseq(iseq, op);
- argv[j] = v;
- RB_OBJ_WRITTEN(iseq, Qundef, v);
- }
- else {
- argv[j] = 0;
- }
- }
- break;
- case TS_ISE:
- argv[j] = op;
+ for (j=0; j<argc; j++) {
+ VALUE op = rb_ary_entry(obj, j+1);
+ switch (insn_op_type((VALUE)insn_id, j)) {
+ case TS_OFFSET: {
+ LABEL *label = register_label(iseq, labels_table, op);
+ argv[j] = (VALUE)label;
+ break;
+ }
+ case TS_LINDEX:
+ case TS_NUM:
+ (void)NUM2INT(op);
+ argv[j] = op;
+ break;
+ case TS_VALUE:
+ argv[j] = op;
+ RB_OBJ_WRITTEN(iseq, Qundef, op);
+ break;
+ case TS_ISEQ:
+ {
+ if (op != Qnil) {
+ VALUE v = (VALUE)iseq_build_load_iseq(iseq, op);
+ argv[j] = v;
+ RB_OBJ_WRITTEN(iseq, Qundef, v);
+ }
+ else {
+ argv[j] = 0;
+ }
+ }
+ break;
+ case TS_ISE:
+ argv[j] = op;
if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ise_size) {
ISEQ_BODY(iseq)->ise_size = NUM2INT(op) + 1;
}
break;
- case TS_IC:
- argv[j] = op;
- if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ic_size) {
- ISEQ_BODY(iseq)->ic_size = NUM2INT(op) + 1;
+ case TS_IC:
+ {
+ VALUE segments = rb_ary_new();
+ op = rb_to_array_type(op);
+
+ for (int i = 0; i < RARRAY_LEN(op); i++) {
+ VALUE sym = RARRAY_AREF(op, i);
+ sym = rb_to_symbol_type(sym);
+ rb_ary_push(segments, sym);
+ }
+
+ RB_GC_GUARD(op);
+ argv[j] = segments;
+ RB_OBJ_WRITTEN(iseq, Qundef, segments);
+ ISEQ_BODY(iseq)->ic_size++;
}
break;
case TS_IVC: /* inline ivar cache */
- argv[j] = op;
+ argv[j] = op;
if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ivc_size) {
ISEQ_BODY(iseq)->ivc_size = NUM2INT(op) + 1;
}
- break;
+ break;
case TS_ICVARC: /* inline cvar cache */
- argv[j] = op;
+ argv[j] = op;
if (NUM2UINT(op) >= ISEQ_BODY(iseq)->icvarc_size) {
ISEQ_BODY(iseq)->icvarc_size = NUM2INT(op) + 1;
}
- break;
+ break;
case TS_CALLDATA:
- argv[j] = iseq_build_callinfo_from_hash(iseq, op);
- break;
- case TS_ID:
- argv[j] = rb_to_symbol_type(op);
- break;
- case TS_CDHASH:
- {
- int i;
- VALUE map = rb_hash_new_with_size(RARRAY_LEN(op)/2);
+ argv[j] = iseq_build_callinfo_from_hash(iseq, op);
+ break;
+ case TS_ID:
+ argv[j] = rb_to_symbol_type(op);
+ break;
+ case TS_CDHASH:
+ {
+ int i;
+ VALUE map = rb_hash_new_with_size(RARRAY_LEN(op)/2);
RHASH_TBL_RAW(map)->type = &cdhash_type;
- op = rb_to_array_type(op);
- for (i=0; i<RARRAY_LEN(op); i+=2) {
- VALUE key = RARRAY_AREF(op, i);
- VALUE sym = RARRAY_AREF(op, i+1);
- LABEL *label =
- register_label(iseq, labels_table, sym);
- rb_hash_aset(map, key, (VALUE)label | 1);
- }
- RB_GC_GUARD(op);
- argv[j] = map;
- RB_OBJ_WRITTEN(iseq, Qundef, map);
- }
- break;
- case TS_FUNCPTR:
- {
+ op = rb_to_array_type(op);
+ for (i=0; i<RARRAY_LEN(op); i+=2) {
+ VALUE key = RARRAY_AREF(op, i);
+ VALUE sym = RARRAY_AREF(op, i+1);
+ LABEL *label =
+ register_label(iseq, labels_table, sym);
+ rb_hash_aset(map, key, (VALUE)label | 1);
+ }
+ RB_GC_GUARD(op);
+ argv[j] = map;
+ RB_OBJ_WRITTEN(iseq, Qundef, map);
+ }
+ break;
+ case TS_FUNCPTR:
+ {
#if SIZEOF_VALUE <= SIZEOF_LONG
- long funcptr = NUM2LONG(op);
+ long funcptr = NUM2LONG(op);
#else
- LONG_LONG funcptr = NUM2LL(op);
+ LONG_LONG funcptr = NUM2LL(op);
#endif
- argv[j] = (VALUE)funcptr;
- }
- break;
- default:
- rb_raise(rb_eSyntaxError, "unknown operand: %c", insn_op_type((VALUE)insn_id, j));
- }
- }
- }
+ argv[j] = (VALUE)funcptr;
+ }
+ break;
+ default:
+ rb_raise(rb_eSyntaxError, "unknown operand: %c", insn_op_type((VALUE)insn_id, j));
+ }
+ }
+ }
else {
NODE dummy_line_node = generate_dummy_line_node(line_no, node_id);
ADD_ELEM(anchor,
(LINK_ELEMENT*)new_insn_core(iseq, &dummy_line_node,
(enum ruby_vminsn_type)insn_id, argc, NULL));
}
- }
- else {
- rb_raise(rb_eTypeError, "unexpected object for instruction");
- }
+ }
+ else {
+ rb_raise(rb_eTypeError, "unexpected object for instruction");
+ }
}
DATA_PTR(labels_wrapper) = 0;
validate_labels(iseq, labels_table);
@@ -10434,12 +10709,12 @@ int_param(int *dst, VALUE param, VALUE sym)
{
VALUE val = rb_hash_aref(param, sym);
if (FIXNUM_P(val)) {
- *dst = FIX2INT(val);
- return TRUE;
+ *dst = FIX2INT(val);
+ return TRUE;
}
else if (!NIL_P(val)) {
- rb_raise(rb_eTypeError, "invalid %+"PRIsVALUE" Fixnum: %+"PRIsVALUE,
- sym, val);
+ rb_raise(rb_eTypeError, "invalid %+"PRIsVALUE" Fixnum: %+"PRIsVALUE,
+ sym, val);
}
return FALSE;
}
@@ -10466,20 +10741,20 @@ iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
/* required args */
for (i = 0; i < len; i++) {
- VALUE val = RARRAY_AREF(keywords, i);
+ VALUE val = RARRAY_AREF(keywords, i);
- if (!SYMBOL_P(val)) {
- goto default_values;
- }
- ids[i] = SYM2ID(val);
- keyword->required_num++;
+ if (!SYMBOL_P(val)) {
+ goto default_values;
+ }
+ ids[i] = SYM2ID(val);
+ keyword->required_num++;
}
default_values: /* note: we intentionally preserve `i' from previous loop */
default_len = len - i;
if (default_len == 0) {
- keyword->table = ids;
- return keyword;
+ keyword->table = ids;
+ return keyword;
}
else if (default_len < 0) {
UNREACHABLE;
@@ -10488,23 +10763,23 @@ iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
dvs = ALLOC_N(VALUE, (unsigned int)default_len);
for (j = 0; i < len; i++, j++) {
- key = RARRAY_AREF(keywords, i);
- CHECK_ARRAY(key);
-
- switch (RARRAY_LEN(key)) {
- case 1:
- sym = RARRAY_AREF(key, 0);
- default_val = Qundef;
- break;
- case 2:
- sym = RARRAY_AREF(key, 0);
- default_val = RARRAY_AREF(key, 1);
- break;
- default:
- rb_raise(rb_eTypeError, "keyword default has unsupported len %+"PRIsVALUE, key);
- }
- ids[i] = SYM2ID(sym);
- dvs[j] = default_val;
+ key = RARRAY_AREF(keywords, i);
+ CHECK_ARRAY(key);
+
+ switch (RARRAY_LEN(key)) {
+ case 1:
+ sym = RARRAY_AREF(key, 0);
+ default_val = Qundef;
+ break;
+ case 2:
+ sym = RARRAY_AREF(key, 0);
+ default_val = RARRAY_AREF(key, 1);
+ break;
+ default:
+ rb_raise(rb_eTypeError, "keyword default has unsupported len %+"PRIsVALUE, key);
+ }
+ ids[i] = SYM2ID(sym);
+ dvs[j] = default_val;
}
keyword->table = ids;
@@ -10513,6 +10788,12 @@ iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
return keyword;
}
+static void
+iseq_insn_each_object_mark(VALUE *obj_ptr, VALUE _)
+{
+ rb_gc_mark(*obj_ptr);
+}
+
void
rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *storage)
{
@@ -10539,28 +10820,7 @@ rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *storage)
iobj = (INSN *)&storage->buff[pos];
if (iobj->operands) {
- int j;
- const char *types = insn_op_types(iobj->insn_id);
-
- for (j = 0; types[j]; j++) {
- char type = types[j];
- switch (type) {
- case TS_CDHASH:
- case TS_ISEQ:
- case TS_VALUE:
- case TS_CALLDATA: // ci is stored.
- {
- VALUE op = OPERAND_AT(iobj, j);
-
- if (!SPECIAL_CONST_P(op)) {
- rb_gc_mark(op);
- }
- }
- break;
- default:
- break;
- }
- }
+ iseq_insn_each_markable_object(iobj, iseq_insn_each_object_mark, (VALUE)0);
}
pos += (int)size;
}
@@ -10569,7 +10829,7 @@ rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *storage)
void
rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
- VALUE exception, VALUE body)
+ VALUE exception, VALUE body)
{
#define SYM(s) ID2SYM(rb_intern_const(#s))
int i, len;
@@ -10588,14 +10848,14 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
ISEQ_BODY(iseq)->local_table = tbl = len > 0 ? (ID *)ALLOC_N(ID, ISEQ_BODY(iseq)->local_table_size) : NULL;
for (i = 0; i < len; i++) {
- VALUE lv = RARRAY_AREF(locals, i);
+ VALUE lv = RARRAY_AREF(locals, i);
- if (sym_arg_rest == lv) {
- tbl[i] = 0;
- }
- else {
- tbl[i] = FIXNUM_P(lv) ? (ID)FIX2LONG(lv) : SYM2ID(CHECK_SYMBOL(lv));
- }
+ if (sym_arg_rest == lv) {
+ tbl[i] = 0;
+ }
+ else {
+ tbl[i] = FIXNUM_P(lv) ? (ID)FIX2LONG(lv) : SYM2ID(CHECK_SYMBOL(lv));
+ }
}
#define INT_PARAM(F) int_param(&ISEQ_BODY(iseq)->param.F, params, SYM(F))
@@ -10609,10 +10869,10 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
#undef INT_PARAM
{
#define INT_PARAM(F) F = (int_param(&x, misc, SYM(F)) ? (unsigned int)x : 0)
- int x;
- INT_PARAM(arg_size);
- INT_PARAM(local_size);
- INT_PARAM(stack_max);
+ int x;
+ INT_PARAM(arg_size);
+ INT_PARAM(local_size);
+ INT_PARAM(stack_max);
#undef INT_PARAM
}
@@ -10620,38 +10880,38 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
#ifdef USE_ISEQ_NODE_ID
node_ids = rb_hash_aref(misc, ID2SYM(rb_intern("node_ids")));
if (!RB_TYPE_P(node_ids, T_ARRAY)) {
- rb_raise(rb_eTypeError, "node_ids is not an array");
+ rb_raise(rb_eTypeError, "node_ids is not an array");
}
#endif
if (RB_TYPE_P(arg_opt_labels, T_ARRAY)) {
- len = RARRAY_LENINT(arg_opt_labels);
+ len = RARRAY_LENINT(arg_opt_labels);
ISEQ_BODY(iseq)->param.flags.has_opt = !!(len - 1 >= 0);
if (ISEQ_BODY(iseq)->param.flags.has_opt) {
- VALUE *opt_table = ALLOC_N(VALUE, len);
+ VALUE *opt_table = ALLOC_N(VALUE, len);
- for (i = 0; i < len; i++) {
- VALUE ent = RARRAY_AREF(arg_opt_labels, i);
- LABEL *label = register_label(iseq, labels_table, ent);
- opt_table[i] = (VALUE)label;
- }
+ for (i = 0; i < len; i++) {
+ VALUE ent = RARRAY_AREF(arg_opt_labels, i);
+ LABEL *label = register_label(iseq, labels_table, ent);
+ opt_table[i] = (VALUE)label;
+ }
ISEQ_BODY(iseq)->param.opt_num = len - 1;
ISEQ_BODY(iseq)->param.opt_table = opt_table;
- }
+ }
}
else if (!NIL_P(arg_opt_labels)) {
- rb_raise(rb_eTypeError, ":opt param is not an array: %+"PRIsVALUE,
- arg_opt_labels);
+ rb_raise(rb_eTypeError, ":opt param is not an array: %+"PRIsVALUE,
+ arg_opt_labels);
}
if (RB_TYPE_P(keywords, T_ARRAY)) {
ISEQ_BODY(iseq)->param.keyword = iseq_build_kw(iseq, params, keywords);
}
else if (!NIL_P(keywords)) {
- rb_raise(rb_eTypeError, ":keywords param is not an array: %+"PRIsVALUE,
- keywords);
+ rb_raise(rb_eTypeError, ":keywords param is not an array: %+"PRIsVALUE,
+ keywords);
}
if (Qtrue == rb_hash_aref(params, SYM(ambiguous_param0))) {
@@ -10660,10 +10920,10 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
if (int_param(&i, params, SYM(kwrest))) {
struct rb_iseq_param_keyword *keyword = (struct rb_iseq_param_keyword *)ISEQ_BODY(iseq)->param.keyword;
- if (keyword == NULL) {
+ if (keyword == NULL) {
ISEQ_BODY(iseq)->param.keyword = keyword = ZALLOC(struct rb_iseq_param_keyword);
- }
- keyword->rest_start = i;
+ }
+ keyword->rest_start = i;
ISEQ_BODY(iseq)->param.flags.has_kwrest = TRUE;
}
#undef SYM
@@ -10687,22 +10947,22 @@ rb_dvar_defined(ID id, const rb_iseq_t *iseq)
{
if (iseq) {
const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
- while (body->type == ISEQ_TYPE_BLOCK ||
- body->type == ISEQ_TYPE_RESCUE ||
- body->type == ISEQ_TYPE_ENSURE ||
- body->type == ISEQ_TYPE_EVAL ||
- body->type == ISEQ_TYPE_MAIN
- ) {
- unsigned int i;
-
- for (i = 0; i < body->local_table_size; i++) {
- if (body->local_table[i] == id) {
- return 1;
- }
- }
- iseq = body->parent_iseq;
+ while (body->type == ISEQ_TYPE_BLOCK ||
+ body->type == ISEQ_TYPE_RESCUE ||
+ body->type == ISEQ_TYPE_ENSURE ||
+ body->type == ISEQ_TYPE_EVAL ||
+ body->type == ISEQ_TYPE_MAIN
+ ) {
+ unsigned int i;
+
+ for (i = 0; i < body->local_table_size; i++) {
+ if (body->local_table[i] == id) {
+ return 1;
+ }
+ }
+ iseq = body->parent_iseq;
body = ISEQ_BODY(iseq);
- }
+ }
}
return 0;
}
@@ -10711,14 +10971,14 @@ int
rb_local_defined(ID id, const rb_iseq_t *iseq)
{
if (iseq) {
- unsigned int i;
+ unsigned int i;
const struct rb_iseq_constant_body *const body = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq);
- for (i=0; i<body->local_table_size; i++) {
- if (body->local_table[i] == id) {
- return 1;
- }
- }
+ for (i=0; i<body->local_table_size; i++) {
+ if (body->local_table[i] == id) {
+ return 1;
+ }
+ }
}
return 0;
}
@@ -11176,11 +11436,17 @@ ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
wv = (VALUE)ibf_dump_iseq(dump, (const rb_iseq_t *)op);
break;
case TS_IC:
+ {
+ IC ic = (IC)op;
+ VALUE arr = idlist_to_array(ic->segments);
+ wv = ibf_dump_object(dump, arr);
+ }
+ break;
case TS_ISE:
case TS_IVC:
case TS_ICVARC:
{
- union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)op;
+ union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)op;
wv = is - ISEQ_IS_ENTRY_START(body, types[op_index]);
}
break;
@@ -11220,6 +11486,7 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod
struct rb_iseq_constant_body *load_body = ISEQ_BODY(iseq);
struct rb_call_data *cd_entries = load_body->call_data;
+ int ic_index = 0;
iseq_bits_t * mark_offset_bits;
@@ -11236,7 +11503,6 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod
for (code_index=0; code_index<iseq_size;) {
/* opcode */
const VALUE insn = code[code_index] = ibf_load_small_value(load, &reading_pos);
- const unsigned int insn_index = code_index;
const char *types = insn_op_types(insn);
int op_index;
@@ -11291,6 +11557,16 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod
break;
}
case TS_IC:
+ {
+ VALUE op = ibf_load_small_value(load, &reading_pos);
+ VALUE arr = ibf_load_object(load, op);
+
+ IC ic = &ISEQ_IS_IC_ENTRY(load_body, ic_index++);
+ ic->segments = array_to_idlist(arr);
+
+ code[code_index] = (VALUE)ic;
+ }
+ break;
case TS_ISE:
case TS_ICVARC:
case TS_IVC:
@@ -11300,11 +11576,20 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod
ISE ic = ISEQ_IS_ENTRY_START(load_body, operand_type) + op;
code[code_index] = (VALUE)ic;
- if (insn == BIN(opt_getinlinecache) && operand_type == TS_IC) {
- // Store the instruction index for opt_getinlinecache on the IC for
- // YJIT to invalidate code when opt_setinlinecache runs.
- ic->ic_cache.get_insn_idx = insn_index;
+ if (operand_type == TS_IVC) {
+ IVC cache = (IVC)ic;
+
+ if (insn == BIN(setinstancevariable)) {
+ ID iv_name = (ID)code[code_index - 1];
+ cache->iv_set_name = iv_name;
+ }
+ else {
+ cache->iv_set_name = 0;
+ }
+
+ vm_ic_attr_index_initialize(cache, INVALID_SHAPE_ID);
}
+
}
break;
case TS_CALLDATA:
@@ -11575,7 +11860,7 @@ ibf_load_catch_table(const struct ibf_load *load, ibf_offset_t catch_table_offse
unsigned int i;
for (i=0; i<table->size; i++) {
int iseq_index = (int)ibf_load_small_value(load, &reading_pos);
- table->entries[i].type = (enum catch_type)ibf_load_small_value(load, &reading_pos);
+ table->entries[i].type = (enum rb_catch_type)ibf_load_small_value(load, &reading_pos);
table->entries[i].start = (unsigned int)ibf_load_small_value(load, &reading_pos);
table->entries[i].end = (unsigned int)ibf_load_small_value(load, &reading_pos);
table->entries[i].cont = (unsigned int)ibf_load_small_value(load, &reading_pos);
@@ -11922,7 +12207,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
const VALUE location_pathobj_index = ibf_load_small_value(load, &reading_pos);
const VALUE location_base_label_index = ibf_load_small_value(load, &reading_pos);
const VALUE location_label_index = ibf_load_small_value(load, &reading_pos);
- const VALUE location_first_lineno = ibf_load_small_value(load, &reading_pos);
+ const int location_first_lineno = (int)ibf_load_small_value(load, &reading_pos);
const int location_node_id = (int)ibf_load_small_value(load, &reading_pos);
const int location_code_location_beg_pos_lineno = (int)ibf_load_small_value(load, &reading_pos);
const int location_code_location_beg_pos_column = (int)ibf_load_small_value(load, &reading_pos);
@@ -11952,6 +12237,40 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
const char catch_except_p = (char)ibf_load_small_value(load, &reading_pos);
const bool builtin_inline_p = (bool)ibf_load_small_value(load, &reading_pos);
+ // setup fname and dummy frame
+ VALUE path = ibf_load_object(load, location_pathobj_index);
+ {
+ VALUE realpath = Qnil;
+
+ if (RB_TYPE_P(path, T_STRING)) {
+ realpath = path = rb_fstring(path);
+ }
+ else if (RB_TYPE_P(path, T_ARRAY)) {
+ VALUE pathobj = path;
+ if (RARRAY_LEN(pathobj) != 2) {
+ rb_raise(rb_eRuntimeError, "path object size mismatch");
+ }
+ path = rb_fstring(RARRAY_AREF(pathobj, 0));
+ realpath = RARRAY_AREF(pathobj, 1);
+ if (!NIL_P(realpath)) {
+ if (!RB_TYPE_P(realpath, T_STRING)) {
+ rb_raise(rb_eArgError, "unexpected realpath %"PRIxVALUE
+ "(%x), path=%+"PRIsVALUE,
+ realpath, TYPE(realpath), path);
+ }
+ realpath = rb_fstring(realpath);
+ }
+ }
+ else {
+ rb_raise(rb_eRuntimeError, "unexpected path object");
+ }
+ rb_iseq_pathobj_set(iseq, path, realpath);
+ }
+
+ // push dummy frame
+ rb_execution_context_t *ec = GET_EC();
+ VALUE dummy_frame = rb_vm_push_frame_fname(ec, path);
+
#undef IBF_BODY_OFFSET
load_body->type = type;
@@ -12020,33 +12339,6 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
load->current_buffer = &load->global_buffer;
#endif
- {
- VALUE realpath = Qnil, path = ibf_load_object(load, location_pathobj_index);
- if (RB_TYPE_P(path, T_STRING)) {
- realpath = path = rb_fstring(path);
- }
- else if (RB_TYPE_P(path, T_ARRAY)) {
- VALUE pathobj = path;
- if (RARRAY_LEN(pathobj) != 2) {
- rb_raise(rb_eRuntimeError, "path object size mismatch");
- }
- path = rb_fstring(RARRAY_AREF(pathobj, 0));
- realpath = RARRAY_AREF(pathobj, 1);
- if (!NIL_P(realpath)) {
- if (!RB_TYPE_P(realpath, T_STRING)) {
- rb_raise(rb_eArgError, "unexpected realpath %"PRIxVALUE
- "(%x), path=%+"PRIsVALUE,
- realpath, TYPE(realpath), path);
- }
- realpath = rb_fstring(realpath);
- }
- }
- else {
- rb_raise(rb_eRuntimeError, "unexpected path object");
- }
- rb_iseq_pathobj_set(iseq, path, realpath);
- }
-
RB_OBJ_WRITE(iseq, &load_body->location.base_label, ibf_load_location_str(load, location_base_label_index));
RB_OBJ_WRITE(iseq, &load_body->location.label, ibf_load_location_str(load, location_label_index));
@@ -12054,6 +12346,9 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
load->current_buffer = saved_buffer;
#endif
verify_call_cache(iseq);
+
+ RB_GC_GUARD(dummy_frame);
+ rb_vm_pop_frame_no_int(ec);
}
struct ibf_dump_iseq_list_arg
@@ -12077,7 +12372,7 @@ ibf_dump_iseq_list_i(st_data_t key, st_data_t val, st_data_t ptr)
static void
ibf_dump_iseq_list(struct ibf_dump *dump, struct ibf_header *header)
{
- VALUE offset_list = rb_ary_tmp_new(dump->iseq_table->num_entries);
+ VALUE offset_list = rb_ary_hidden_new(dump->iseq_table->num_entries);
struct ibf_dump_iseq_list_arg args;
args.dump = dump;
@@ -12166,7 +12461,7 @@ static const void *
ibf_load_check_offset(const struct ibf_load *load, size_t offset)
{
if (offset >= load->current_buffer->size) {
- rb_raise(rb_eIndexError, "object offset out of range: %"PRIdSIZE, offset);
+ rb_raise(rb_eIndexError, "object offset out of range: %"PRIdSIZE, offset);
}
return load->current_buffer->buff + offset;
}
@@ -12227,11 +12522,11 @@ ibf_load_object_class(const struct ibf_load *load, const struct ibf_object_heade
switch (cindex) {
case IBF_OBJECT_CLASS_OBJECT:
- return rb_cObject;
+ return rb_cObject;
case IBF_OBJECT_CLASS_ARRAY:
- return rb_cArray;
+ return rb_cArray;
case IBF_OBJECT_CLASS_STANDARD_ERROR:
- return rb_eStandardError;
+ return rb_eStandardError;
case IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR:
return rb_eNoMatchingPatternError;
case IBF_OBJECT_CLASS_TYPE_ERROR:
@@ -12349,7 +12644,7 @@ ibf_load_object_array(const struct ibf_load *load, const struct ibf_object_heade
const long len = (long)ibf_load_small_value(load, &reading_pos);
- VALUE ary = header->internal ? rb_ary_literal_new(len) : rb_ary_new_capa(len);
+ VALUE ary = header->internal ? rb_ary_hidden_new(len) : rb_ary_new_capa(len);
int i;
for (i=0; i<len; i++) {
@@ -12357,8 +12652,7 @@ ibf_load_object_array(const struct ibf_load *load, const struct ibf_object_heade
rb_ary_push(ary, ibf_load_object(load, index));
}
- if (header->internal) rb_obj_hide(ary);
- if (header->frozen) rb_obj_freeze(ary);
+ if (header->frozen) rb_obj_freeze(ary);
return ary;
}
@@ -12461,7 +12755,7 @@ ibf_load_object_bignum(const struct ibf_load *load, const struct ibf_object_head
int sign = bignum->slen > 0;
ssize_t len = sign > 0 ? bignum->slen : -1 * bignum->slen;
VALUE obj = rb_integer_unpack(bignum->digits, len * 2, 2, 0,
- INTEGER_PACK_LITTLE_ENDIAN | (sign == 0 ? INTEGER_PACK_NEGATIVE : 0));
+ INTEGER_PACK_LITTLE_ENDIAN | (sign == 0 ? INTEGER_PACK_NEGATIVE : 0));
if (header->internal) rb_obj_hide(obj);
if (header->frozen) rb_obj_freeze(obj);
return obj;
@@ -12745,7 +13039,7 @@ static void
ibf_dump_object_list(struct ibf_dump *dump, ibf_offset_t *obj_list_offset, unsigned int *obj_list_size)
{
st_table *obj_table = dump->current_buffer->obj_table;
- VALUE offset_list = rb_ary_tmp_new(obj_table->num_entries);
+ VALUE offset_list = rb_ary_hidden_new(obj_table->num_entries);
struct ibf_dump_object_list_arg args;
args.dump = dump;
@@ -12805,7 +13099,7 @@ ibf_dump_memsize(const void *ptr)
static const rb_data_type_t ibf_dump_type = {
"ibf_dump",
{ibf_dump_mark, ibf_dump_free, ibf_dump_memsize,},
- 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
static void
@@ -12888,8 +13182,8 @@ rb_ibf_load_iseq_complete(rb_iseq_t *iseq)
load->iseq = iseq;
#if IBF_ISEQ_DEBUG
fprintf(stderr, "rb_ibf_load_iseq_complete: index=%#x offset=%#x size=%#x\n",
- iseq->aux.loader.index, offset,
- load->header->size);
+ iseq->aux.loader.index, offset,
+ load->header->size);
#endif
ibf_load_iseq_each(load, iseq, offset);
ISEQ_COMPILE_DATA_CLEAR(iseq);
@@ -12914,37 +13208,37 @@ ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq)
#if IBF_ISEQ_DEBUG
fprintf(stderr, "ibf_load_iseq: index_iseq=%p iseq_list=%p\n",
- (void *)index_iseq, (void *)load->iseq_list);
+ (void *)index_iseq, (void *)load->iseq_list);
#endif
if (iseq_index == -1) {
- return NULL;
+ return NULL;
}
else {
- VALUE iseqv = pinned_list_fetch(load->iseq_list, iseq_index);
+ VALUE iseqv = pinned_list_fetch(load->iseq_list, iseq_index);
#if IBF_ISEQ_DEBUG
- fprintf(stderr, "ibf_load_iseq: iseqv=%p\n", (void *)iseqv);
+ fprintf(stderr, "ibf_load_iseq: iseqv=%p\n", (void *)iseqv);
#endif
- if (iseqv) {
- return (rb_iseq_t *)iseqv;
- }
- else {
- rb_iseq_t *iseq = iseq_imemo_alloc();
+ if (iseqv) {
+ return (rb_iseq_t *)iseqv;
+ }
+ else {
+ rb_iseq_t *iseq = iseq_imemo_alloc();
#if IBF_ISEQ_DEBUG
- fprintf(stderr, "ibf_load_iseq: new iseq=%p\n", (void *)iseq);
+ fprintf(stderr, "ibf_load_iseq: new iseq=%p\n", (void *)iseq);
#endif
- FL_SET((VALUE)iseq, ISEQ_NOT_LOADED_YET);
- iseq->aux.loader.obj = load->loader_obj;
- iseq->aux.loader.index = iseq_index;
+ FL_SET((VALUE)iseq, ISEQ_NOT_LOADED_YET);
+ iseq->aux.loader.obj = load->loader_obj;
+ iseq->aux.loader.index = iseq_index;
#if IBF_ISEQ_DEBUG
- fprintf(stderr, "ibf_load_iseq: iseq=%p loader_obj=%p index=%d\n",
- (void *)iseq, (void *)load->loader_obj, iseq_index);
+ fprintf(stderr, "ibf_load_iseq: iseq=%p loader_obj=%p index=%d\n",
+ (void *)iseq, (void *)load->loader_obj, iseq_index);
#endif
- pinned_list_store(load->iseq_list, iseq_index, (VALUE)iseq);
+ pinned_list_store(load->iseq_list, iseq_index, (VALUE)iseq);
#if !USE_LAZY_LOAD
#if IBF_ISEQ_DEBUG
- fprintf(stderr, "ibf_load_iseq: loading iseq=%p\n", (void *)iseq);
+ fprintf(stderr, "ibf_load_iseq: loading iseq=%p\n", (void *)iseq);
#endif
rb_ibf_load_iseq_complete(iseq);
#else
@@ -12954,11 +13248,11 @@ ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq)
#endif /* !USE_LAZY_LOAD */
#if IBF_ISEQ_DEBUG
- fprintf(stderr, "ibf_load_iseq: iseq=%p loaded %p\n",
- (void *)iseq, (void *)load->iseq);
+ fprintf(stderr, "ibf_load_iseq: iseq=%p loaded %p\n",
+ (void *)iseq, (void *)load->iseq);
#endif
- return iseq;
- }
+ return iseq;
+ }
}
}
@@ -12978,18 +13272,18 @@ ibf_load_setup_bytes(struct ibf_load *load, VALUE loader_obj, const char *bytes,
load->current_buffer = &load->global_buffer;
if (size < load->header->size) {
- rb_raise(rb_eRuntimeError, "broken binary format");
+ rb_raise(rb_eRuntimeError, "broken binary format");
}
if (strncmp(load->header->magic, "YARB", 4) != 0) {
- rb_raise(rb_eRuntimeError, "unknown binary format");
+ rb_raise(rb_eRuntimeError, "unknown binary format");
}
if (load->header->major_version != IBF_MAJOR_VERSION ||
- load->header->minor_version != IBF_MINOR_VERSION) {
- rb_raise(rb_eRuntimeError, "unmatched version file (%u.%u for %u.%u)",
- load->header->major_version, load->header->minor_version, IBF_MAJOR_VERSION, IBF_MINOR_VERSION);
+ load->header->minor_version != IBF_MINOR_VERSION) {
+ rb_raise(rb_eRuntimeError, "unmatched version file (%u.%u for %u.%u)",
+ load->header->major_version, load->header->minor_version, IBF_MAJOR_VERSION, IBF_MINOR_VERSION);
}
if (strcmp(load->global_buffer.buff + sizeof(struct ibf_header), RUBY_PLATFORM) != 0) {
- rb_raise(rb_eRuntimeError, "unmatched platform");
+ rb_raise(rb_eRuntimeError, "unmatched platform");
}
if (load->header->iseq_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
rb_raise(rb_eArgError, "unaligned iseq list offset: %u",
diff --git a/complex.c b/complex.c
index 92f4e812d6..a227cb0a58 100644
--- a/complex.c
+++ b/complex.c
@@ -97,7 +97,7 @@ inline static VALUE
f_div(VALUE x, VALUE y)
{
if (FIXNUM_P(y) && FIX2LONG(y) == 1)
- return x;
+ return x;
return rb_funcall(x, '/', 1, y);
}
@@ -152,7 +152,7 @@ f_sub(VALUE x, VALUE y)
{
if (FIXNUM_ZERO_P(y) &&
LIKELY(rb_method_basic_definition_p(CLASS_OF(x), idMINUS))) {
- return x;
+ return x;
}
return rb_funcall(x, '-', 1, y);
}
@@ -262,7 +262,7 @@ inline static VALUE
f_to_i(VALUE x)
{
if (RB_TYPE_P(x, T_STRING))
- return rb_str_to_inum(x, 10, 0);
+ return rb_str_to_inum(x, 10, 0);
return rb_funcall(x, id_to_i, 0);
}
@@ -270,7 +270,7 @@ inline static VALUE
f_to_f(VALUE x)
{
if (RB_TYPE_P(x, T_STRING))
- return DBL2NUM(rb_str_to_dbl(x, 0));
+ return DBL2NUM(rb_str_to_dbl(x, 0));
return rb_funcall(x, id_to_f, 0);
}
@@ -280,9 +280,9 @@ inline static int
f_eqeq_p(VALUE x, VALUE y)
{
if (FIXNUM_P(x) && FIXNUM_P(y))
- return x == y;
+ return x == y;
else if (RB_FLOAT_TYPE_P(x) || RB_FLOAT_TYPE_P(y))
- return NUM2DBL(x) == NUM2DBL(y);
+ return NUM2DBL(x) == NUM2DBL(y);
return (int)rb_equal(x, y);
}
@@ -316,7 +316,7 @@ f_negative_p(VALUE x)
#define f_positive_p(x) (!f_negative_p(x))
-inline static int
+inline static bool
f_zero_p(VALUE x)
{
if (RB_FLOAT_TYPE_P(x)) {
@@ -329,7 +329,7 @@ f_zero_p(VALUE x)
const VALUE num = RRATIONAL(x)->num;
return FIXNUM_ZERO_P(num);
}
- return (int)rb_equal(x, ZERO);
+ return rb_equal(x, ZERO) != 0;
}
#define f_nonzero_p(x) (!f_zero_p(x))
@@ -349,7 +349,7 @@ f_finite_p(VALUE x)
return TRUE;
}
else if (RB_FLOAT_TYPE_P(x)) {
- return isfinite(RFLOAT_VALUE(x));
+ return isfinite(RFLOAT_VALUE(x));
}
return RTEST(rb_funcallv(x, id_finite_p, 0, 0));
}
@@ -361,7 +361,7 @@ f_infinite_p(VALUE x)
return FALSE;
}
else if (RB_FLOAT_TYPE_P(x)) {
- return isinf(RFLOAT_VALUE(x));
+ return isinf(RFLOAT_VALUE(x));
}
return RTEST(rb_funcallv(x, id_infinite_p, 0, 0));
}
@@ -421,15 +421,22 @@ f_complex_new_bang2(VALUE klass, VALUE x, VALUE y)
return nucomp_s_new_internal(klass, x, y);
}
-inline static void
+WARN_UNUSED_RESULT(inline static VALUE nucomp_real_check(VALUE num));
+inline static VALUE
nucomp_real_check(VALUE num)
{
if (!RB_INTEGER_TYPE_P(num) &&
- !RB_FLOAT_TYPE_P(num) &&
- !RB_TYPE_P(num, T_RATIONAL)) {
- if (!k_numeric_p(num) || !f_real_p(num))
- rb_raise(rb_eTypeError, "not a real");
+ !RB_FLOAT_TYPE_P(num) &&
+ !RB_TYPE_P(num, T_RATIONAL)) {
+ if (RB_TYPE_P(num, T_COMPLEX) && nucomp_real_p(num)) {
+ VALUE real = RCOMPLEX(num)->real;
+ assert(!RB_TYPE_P(real, T_COMPLEX));
+ return real;
+ }
+ if (!k_numeric_p(num) || !f_real_p(num))
+ rb_raise(rb_eTypeError, "not a real");
}
+ return num;
}
inline static VALUE
@@ -439,28 +446,28 @@ nucomp_s_canonicalize_internal(VALUE klass, VALUE real, VALUE imag)
complex_r = RB_TYPE_P(real, T_COMPLEX);
complex_i = RB_TYPE_P(imag, T_COMPLEX);
if (!complex_r && !complex_i) {
- return nucomp_s_new_internal(klass, real, imag);
+ return nucomp_s_new_internal(klass, real, imag);
}
else if (!complex_r) {
- get_dat1(imag);
+ get_dat1(imag);
- return nucomp_s_new_internal(klass,
- f_sub(real, dat->imag),
- f_add(ZERO, dat->real));
+ return nucomp_s_new_internal(klass,
+ f_sub(real, dat->imag),
+ f_add(ZERO, dat->real));
}
else if (!complex_i) {
- get_dat1(real);
+ get_dat1(real);
- return nucomp_s_new_internal(klass,
- dat->real,
- f_add(dat->imag, imag));
+ return nucomp_s_new_internal(klass,
+ dat->real,
+ f_add(dat->imag, imag));
}
else {
- get_dat2(real, imag);
+ get_dat2(real, imag);
- return nucomp_s_new_internal(klass,
- f_sub(adat->real, bdat->imag),
- f_add(adat->imag, bdat->real));
+ return nucomp_s_new_internal(klass,
+ f_sub(adat->real, bdat->imag),
+ f_add(adat->imag, bdat->real));
}
}
@@ -480,22 +487,26 @@ nucomp_s_new(int argc, VALUE *argv, VALUE klass)
switch (rb_scan_args(argc, argv, "11", &real, &imag)) {
case 1:
- nucomp_real_check(real);
- imag = ZERO;
- break;
+ real = nucomp_real_check(real);
+ imag = ZERO;
+ break;
default:
- nucomp_real_check(real);
- nucomp_real_check(imag);
- break;
+ real = nucomp_real_check(real);
+ imag = nucomp_real_check(imag);
+ break;
}
- return nucomp_s_canonicalize_internal(klass, real, imag);
+ return nucomp_s_new_internal(klass, real, imag);
}
inline static VALUE
f_complex_new2(VALUE klass, VALUE x, VALUE y)
{
- assert(!RB_TYPE_P(x, T_COMPLEX));
+ if (RB_TYPE_P(x, T_COMPLEX)) {
+ get_dat1(x);
+ x = dat->real;
+ y = f_add(dat->imag, y);
+ }
return nucomp_s_canonicalize_internal(klass, x, y);
}
@@ -550,7 +561,7 @@ nucomp_f_complex(int argc, VALUE *argv, VALUE klass)
if (!NIL_P(opts)) {
raise = rb_opts_exception_p(opts, raise);
}
- if (argc > 0 && CLASS_OF(a1) == rb_cComplex && a2 == Qundef) {
+ if (argc > 0 && CLASS_OF(a1) == rb_cComplex && UNDEF_P(a2)) {
return a1;
}
return nucomp_convert(rb_cComplex, a1, a2, raise);
@@ -580,14 +591,14 @@ static VALUE
m_cos(VALUE x)
{
if (!RB_TYPE_P(x, T_COMPLEX))
- return m_cos_bang(x);
+ return m_cos_bang(x);
{
- get_dat1(x);
- return f_complex_new2(rb_cComplex,
- f_mul(m_cos_bang(dat->real),
- m_cosh_bang(dat->imag)),
- f_mul(f_negate(m_sin_bang(dat->real)),
- m_sinh_bang(dat->imag)));
+ get_dat1(x);
+ return f_complex_new2(rb_cComplex,
+ f_mul(m_cos_bang(dat->real),
+ m_cosh_bang(dat->imag)),
+ f_mul(f_negate(m_sin_bang(dat->real)),
+ m_sinh_bang(dat->imag)));
}
}
@@ -595,55 +606,61 @@ static VALUE
m_sin(VALUE x)
{
if (!RB_TYPE_P(x, T_COMPLEX))
- return m_sin_bang(x);
+ return m_sin_bang(x);
{
- get_dat1(x);
- return f_complex_new2(rb_cComplex,
- f_mul(m_sin_bang(dat->real),
- m_cosh_bang(dat->imag)),
- f_mul(m_cos_bang(dat->real),
- m_sinh_bang(dat->imag)));
+ get_dat1(x);
+ return f_complex_new2(rb_cComplex,
+ f_mul(m_sin_bang(dat->real),
+ m_cosh_bang(dat->imag)),
+ f_mul(m_cos_bang(dat->real),
+ m_sinh_bang(dat->imag)));
}
}
static VALUE
-f_complex_polar(VALUE klass, VALUE x, VALUE y)
+f_complex_polar_real(VALUE klass, VALUE x, VALUE y)
{
- assert(!RB_TYPE_P(x, T_COMPLEX));
- assert(!RB_TYPE_P(y, T_COMPLEX));
if (f_zero_p(x) || f_zero_p(y)) {
- return nucomp_s_new_internal(klass, x, RFLOAT_0);
+ return nucomp_s_new_internal(klass, x, RFLOAT_0);
}
if (RB_FLOAT_TYPE_P(y)) {
- const double arg = RFLOAT_VALUE(y);
- if (arg == M_PI) {
- x = f_negate(x);
- y = RFLOAT_0;
- }
- else if (arg == M_PI_2) {
- y = x;
- x = RFLOAT_0;
- }
- else if (arg == M_PI_2+M_PI) {
- y = f_negate(x);
- x = RFLOAT_0;
- }
- else if (RB_FLOAT_TYPE_P(x)) {
- const double abs = RFLOAT_VALUE(x);
- const double real = abs * cos(arg), imag = abs * sin(arg);
- x = DBL2NUM(real);
- y = DBL2NUM(imag);
- }
- else {
+ const double arg = RFLOAT_VALUE(y);
+ if (arg == M_PI) {
+ x = f_negate(x);
+ y = RFLOAT_0;
+ }
+ else if (arg == M_PI_2) {
+ y = x;
+ x = RFLOAT_0;
+ }
+ else if (arg == M_PI_2+M_PI) {
+ y = f_negate(x);
+ x = RFLOAT_0;
+ }
+ else if (RB_FLOAT_TYPE_P(x)) {
+ const double abs = RFLOAT_VALUE(x);
+ const double real = abs * cos(arg), imag = abs * sin(arg);
+ x = DBL2NUM(real);
+ y = DBL2NUM(imag);
+ }
+ else {
const double ax = sin(arg), ay = cos(arg);
y = f_mul(x, DBL2NUM(ax));
x = f_mul(x, DBL2NUM(ay));
- }
- return nucomp_s_new_internal(klass, x, y);
+ }
+ return nucomp_s_new_internal(klass, x, y);
}
return nucomp_s_canonicalize_internal(klass,
- f_mul(x, m_cos(y)),
- f_mul(x, m_sin(y)));
+ f_mul(x, m_cos(y)),
+ f_mul(x, m_sin(y)));
+}
+
+static VALUE
+f_complex_polar(VALUE klass, VALUE x, VALUE y)
+{
+ x = nucomp_real_check(x);
+ y = nucomp_real_check(y);
+ return f_complex_polar_real(klass, x, y);
}
#ifdef HAVE___COSPI
@@ -665,12 +682,12 @@ rb_dbl_complex_new_polar_pi(double abs, double ang)
int pos = fr == +0.5;
if (pos || fr == -0.5) {
- if ((modf(fi / 2.0, &fi) != fr) ^ pos) abs = -abs;
- return rb_complex_new(RFLOAT_0, DBL2NUM(abs));
+ if ((modf(fi / 2.0, &fi) != fr) ^ pos) abs = -abs;
+ return rb_complex_new(RFLOAT_0, DBL2NUM(abs));
}
else if (fr == 0.0) {
- if (modf(fi / 2.0, &fi) != 0.0) abs = -abs;
- return DBL2NUM(abs);
+ if (modf(fi / 2.0, &fi) != 0.0) abs = -abs;
+ return DBL2NUM(abs);
}
else {
const double real = abs * cospi(ang), imag = abs * sinpi(ang);
@@ -694,24 +711,15 @@ nucomp_s_polar(int argc, VALUE *argv, VALUE klass)
{
VALUE abs, arg;
- switch (rb_scan_args(argc, argv, "11", &abs, &arg)) {
- case 1:
- nucomp_real_check(abs);
- return nucomp_s_new_internal(klass, abs, ZERO);
- default:
- nucomp_real_check(abs);
- nucomp_real_check(arg);
- break;
- }
- if (RB_TYPE_P(abs, T_COMPLEX)) {
- get_dat1(abs);
- abs = dat->real;
+ argc = rb_scan_args(argc, argv, "11", &abs, &arg);
+ abs = nucomp_real_check(abs);
+ if (argc == 2) {
+ arg = nucomp_real_check(arg);
}
- if (RB_TYPE_P(arg, T_COMPLEX)) {
- get_dat1(arg);
- arg = dat->real;
+ else {
+ arg = ZERO;
}
- return f_complex_polar(klass, abs, arg);
+ return f_complex_polar_real(klass, abs, arg);
}
/*
@@ -760,7 +768,7 @@ rb_complex_uminus(VALUE self)
{
get_dat1(self);
return f_complex_new2(CLASS_OF(self),
- f_negate(dat->real), f_negate(dat->imag));
+ f_negate(dat->real), f_negate(dat->imag));
}
/*
@@ -779,20 +787,20 @@ VALUE
rb_complex_plus(VALUE self, VALUE other)
{
if (RB_TYPE_P(other, T_COMPLEX)) {
- VALUE real, imag;
+ VALUE real, imag;
- get_dat2(self, other);
+ get_dat2(self, other);
- real = f_add(adat->real, bdat->real);
- imag = f_add(adat->imag, bdat->imag);
+ real = f_add(adat->real, bdat->real);
+ imag = f_add(adat->imag, bdat->imag);
- return f_complex_new2(CLASS_OF(self), real, imag);
+ return f_complex_new2(CLASS_OF(self), real, imag);
}
if (k_numeric_p(other) && f_real_p(other)) {
- get_dat1(self);
+ get_dat1(self);
- return f_complex_new2(CLASS_OF(self),
- f_add(dat->real, other), dat->imag);
+ return f_complex_new2(CLASS_OF(self),
+ f_add(dat->real, other), dat->imag);
}
return rb_num_coerce_bin(self, other, '+');
}
@@ -813,33 +821,33 @@ VALUE
rb_complex_minus(VALUE self, VALUE other)
{
if (RB_TYPE_P(other, T_COMPLEX)) {
- VALUE real, imag;
+ VALUE real, imag;
- get_dat2(self, other);
+ get_dat2(self, other);
- real = f_sub(adat->real, bdat->real);
- imag = f_sub(adat->imag, bdat->imag);
+ real = f_sub(adat->real, bdat->real);
+ imag = f_sub(adat->imag, bdat->imag);
- return f_complex_new2(CLASS_OF(self), real, imag);
+ return f_complex_new2(CLASS_OF(self), real, imag);
}
if (k_numeric_p(other) && f_real_p(other)) {
- get_dat1(self);
+ get_dat1(self);
- return f_complex_new2(CLASS_OF(self),
- f_sub(dat->real, other), dat->imag);
+ return f_complex_new2(CLASS_OF(self),
+ f_sub(dat->real, other), dat->imag);
}
return rb_num_coerce_bin(self, other, '-');
}
static VALUE
-safe_mul(VALUE a, VALUE b, int az, int bz)
+safe_mul(VALUE a, VALUE b, bool az, bool bz)
{
double v;
if (!az && bz && RB_FLOAT_TYPE_P(a) && (v = RFLOAT_VALUE(a), !isnan(v))) {
- a = signbit(v) ? DBL2NUM(-1.0) : DBL2NUM(1.0);
+ a = signbit(v) ? DBL2NUM(-1.0) : DBL2NUM(1.0);
}
if (!bz && az && RB_FLOAT_TYPE_P(b) && (v = RFLOAT_VALUE(b), !isnan(v))) {
- b = signbit(v) ? DBL2NUM(-1.0) : DBL2NUM(1.0);
+ b = signbit(v) ? DBL2NUM(-1.0) : DBL2NUM(1.0);
}
return f_mul(a, b);
}
@@ -847,10 +855,10 @@ safe_mul(VALUE a, VALUE b, int az, int bz)
static void
comp_mul(VALUE areal, VALUE aimag, VALUE breal, VALUE bimag, VALUE *real, VALUE *imag)
{
- int arzero = f_zero_p(areal);
- int aizero = f_zero_p(aimag);
- int brzero = f_zero_p(breal);
- int bizero = f_zero_p(bimag);
+ bool arzero = f_zero_p(areal);
+ bool aizero = f_zero_p(aimag);
+ bool brzero = f_zero_p(breal);
+ bool bizero = f_zero_p(bimag);
*real = f_sub(safe_mul(areal, breal, arzero, brzero),
safe_mul(aimag, bimag, aizero, bizero));
*imag = f_add(safe_mul(areal, bimag, arzero, bizero),
@@ -873,47 +881,47 @@ VALUE
rb_complex_mul(VALUE self, VALUE other)
{
if (RB_TYPE_P(other, T_COMPLEX)) {
- VALUE real, imag;
- get_dat2(self, other);
+ VALUE real, imag;
+ get_dat2(self, other);
comp_mul(adat->real, adat->imag, bdat->real, bdat->imag, &real, &imag);
- return f_complex_new2(CLASS_OF(self), real, imag);
+ return f_complex_new2(CLASS_OF(self), real, imag);
}
if (k_numeric_p(other) && f_real_p(other)) {
- get_dat1(self);
+ get_dat1(self);
- return f_complex_new2(CLASS_OF(self),
- f_mul(dat->real, other),
- f_mul(dat->imag, other));
+ return f_complex_new2(CLASS_OF(self),
+ f_mul(dat->real, other),
+ f_mul(dat->imag, other));
}
return rb_num_coerce_bin(self, other, '*');
}
inline static VALUE
f_divide(VALUE self, VALUE other,
- VALUE (*func)(VALUE, VALUE), ID id)
+ VALUE (*func)(VALUE, VALUE), ID id)
{
if (RB_TYPE_P(other, T_COMPLEX)) {
VALUE r, n, x, y;
- int flo;
- get_dat2(self, other);
+ int flo;
+ get_dat2(self, other);
- flo = (RB_FLOAT_TYPE_P(adat->real) || RB_FLOAT_TYPE_P(adat->imag) ||
- RB_FLOAT_TYPE_P(bdat->real) || RB_FLOAT_TYPE_P(bdat->imag));
+ flo = (RB_FLOAT_TYPE_P(adat->real) || RB_FLOAT_TYPE_P(adat->imag) ||
+ RB_FLOAT_TYPE_P(bdat->real) || RB_FLOAT_TYPE_P(bdat->imag));
- if (f_gt_p(f_abs(bdat->real), f_abs(bdat->imag))) {
- r = (*func)(bdat->imag, bdat->real);
- n = f_mul(bdat->real, f_add(ONE, f_mul(r, r)));
+ if (f_gt_p(f_abs(bdat->real), f_abs(bdat->imag))) {
+ r = (*func)(bdat->imag, bdat->real);
+ n = f_mul(bdat->real, f_add(ONE, f_mul(r, r)));
x = (*func)(f_add(adat->real, f_mul(adat->imag, r)), n);
y = (*func)(f_sub(adat->imag, f_mul(adat->real, r)), n);
- }
- else {
- r = (*func)(bdat->real, bdat->imag);
- n = f_mul(bdat->imag, f_add(ONE, f_mul(r, r)));
+ }
+ else {
+ r = (*func)(bdat->real, bdat->imag);
+ n = f_mul(bdat->imag, f_add(ONE, f_mul(r, r)));
x = (*func)(f_add(f_mul(adat->real, r), adat->imag), n);
y = (*func)(f_sub(f_mul(adat->imag, r), adat->real), n);
- }
+ }
if (!flo) {
x = rb_rational_canonicalize(x);
y = rb_rational_canonicalize(y);
@@ -922,7 +930,7 @@ f_divide(VALUE self, VALUE other,
}
if (k_numeric_p(other) && f_real_p(other)) {
VALUE x, y;
- get_dat1(self);
+ get_dat1(self);
x = rb_rational_canonicalize((*func)(dat->real, other));
y = rb_rational_canonicalize((*func)(dat->imag, other));
return f_complex_new2(CLASS_OF(self), x, y);
@@ -986,31 +994,31 @@ VALUE
rb_complex_pow(VALUE self, VALUE other)
{
if (k_numeric_p(other) && k_exact_zero_p(other))
- return f_complex_new_bang1(CLASS_OF(self), ONE);
+ return f_complex_new_bang1(CLASS_OF(self), ONE);
if (RB_TYPE_P(other, T_RATIONAL) && RRATIONAL(other)->den == LONG2FIX(1))
- other = RRATIONAL(other)->num; /* c14n */
+ other = RRATIONAL(other)->num; /* c14n */
if (RB_TYPE_P(other, T_COMPLEX)) {
- get_dat1(other);
+ get_dat1(other);
- if (k_exact_zero_p(dat->imag))
- other = dat->real; /* c14n */
+ if (k_exact_zero_p(dat->imag))
+ other = dat->real; /* c14n */
}
if (RB_TYPE_P(other, T_COMPLEX)) {
- VALUE r, theta, nr, ntheta;
+ VALUE r, theta, nr, ntheta;
- get_dat1(other);
+ get_dat1(other);
- r = f_abs(self);
- theta = f_arg(self);
+ r = f_abs(self);
+ theta = f_arg(self);
- nr = m_exp_bang(f_sub(f_mul(dat->real, m_log_bang(r)),
- f_mul(dat->imag, theta)));
- ntheta = f_add(f_mul(theta, dat->real),
- f_mul(dat->imag, m_log_bang(r)));
- return f_complex_polar(CLASS_OF(self), nr, ntheta);
+ nr = m_exp_bang(f_sub(f_mul(dat->real, m_log_bang(r)),
+ f_mul(dat->imag, theta)));
+ ntheta = f_add(f_mul(theta, dat->real),
+ f_mul(dat->imag, m_log_bang(r)));
+ return f_complex_polar(CLASS_OF(self), nr, ntheta);
}
if (FIXNUM_P(other)) {
long n = FIX2LONG(other);
@@ -1051,19 +1059,19 @@ rb_complex_pow(VALUE self, VALUE other)
}
}
return nucomp_s_new_internal(CLASS_OF(self), zr, zi);
- }
+ }
}
if (k_numeric_p(other) && f_real_p(other)) {
- VALUE r, theta;
+ VALUE r, theta;
- if (RB_BIGNUM_TYPE_P(other))
- rb_warn("in a**b, b may be too big");
+ if (RB_BIGNUM_TYPE_P(other))
+ rb_warn("in a**b, b may be too big");
- r = f_abs(self);
- theta = f_arg(self);
+ r = f_abs(self);
+ theta = f_arg(self);
- return f_complex_polar(CLASS_OF(self), f_expt(r, other),
- f_mul(theta, other));
+ return f_complex_polar(CLASS_OF(self), f_expt(r, other),
+ f_mul(theta, other));
}
return rb_num_coerce_bin(self, other, id_expt);
}
@@ -1084,15 +1092,15 @@ static VALUE
nucomp_eqeq_p(VALUE self, VALUE other)
{
if (RB_TYPE_P(other, T_COMPLEX)) {
- get_dat2(self, other);
+ get_dat2(self, other);
- return RBOOL(f_eqeq_p(adat->real, bdat->real) &&
- f_eqeq_p(adat->imag, bdat->imag));
+ return RBOOL(f_eqeq_p(adat->real, bdat->real) &&
+ f_eqeq_p(adat->imag, bdat->imag));
}
if (k_numeric_p(other) && f_real_p(other)) {
- get_dat1(self);
+ get_dat1(self);
- return RBOOL(f_eqeq_p(dat->real, other) && f_zero_p(dat->imag));
+ return RBOOL(f_eqeq_p(dat->real, other) && f_zero_p(dat->imag));
}
return RBOOL(f_eqeq_p(other, self));
}
@@ -1101,7 +1109,7 @@ static bool
nucomp_real_p(VALUE self)
{
get_dat1(self);
- return(f_zero_p(dat->imag) ? true : false);
+ return f_zero_p(dat->imag);
}
/*
@@ -1121,15 +1129,26 @@ nucomp_real_p(VALUE self)
static VALUE
nucomp_cmp(VALUE self, VALUE other)
{
- if (nucomp_real_p(self) && k_numeric_p(other)) {
- if (RB_TYPE_P(other, T_COMPLEX) && nucomp_real_p(other)) {
+ if (!k_numeric_p(other)) {
+ return rb_num_coerce_cmp(self, other, idCmp);
+ }
+ if (!nucomp_real_p(self)) {
+ return Qnil;
+ }
+ if (RB_TYPE_P(other, T_COMPLEX)) {
+ if (nucomp_real_p(other)) {
get_dat2(self, other);
return rb_funcall(adat->real, idCmp, 1, bdat->real);
}
- else if (f_real_p(other)) {
- get_dat1(self);
+ }
+ else {
+ get_dat1(self);
+ if (f_real_p(other)) {
return rb_funcall(dat->real, idCmp, 1, other);
}
+ else {
+ return rb_num_coerce_cmp(dat->real, other, idCmp);
+ }
}
return Qnil;
}
@@ -1139,12 +1158,12 @@ static VALUE
nucomp_coerce(VALUE self, VALUE other)
{
if (RB_TYPE_P(other, T_COMPLEX))
- return rb_assoc_new(other, self);
+ return rb_assoc_new(other, self);
if (k_numeric_p(other) && f_real_p(other))
return rb_assoc_new(f_complex_new_bang1(CLASS_OF(self), other), self);
rb_raise(rb_eTypeError, "%"PRIsVALUE" can't be coerced into %"PRIsVALUE,
- rb_obj_class(other), rb_obj_class(self));
+ rb_obj_class(other), rb_obj_class(self));
return Qnil;
}
@@ -1164,16 +1183,16 @@ rb_complex_abs(VALUE self)
get_dat1(self);
if (f_zero_p(dat->real)) {
- VALUE a = f_abs(dat->imag);
- if (RB_FLOAT_TYPE_P(dat->real) && !RB_FLOAT_TYPE_P(dat->imag))
- a = f_to_f(a);
- return a;
+ VALUE a = f_abs(dat->imag);
+ if (RB_FLOAT_TYPE_P(dat->real) && !RB_FLOAT_TYPE_P(dat->imag))
+ a = f_to_f(a);
+ return a;
}
if (f_zero_p(dat->imag)) {
- VALUE a = f_abs(dat->real);
- if (!RB_FLOAT_TYPE_P(dat->real) && RB_FLOAT_TYPE_P(dat->imag))
- a = f_to_f(a);
- return a;
+ VALUE a = f_abs(dat->real);
+ if (!RB_FLOAT_TYPE_P(dat->real) && RB_FLOAT_TYPE_P(dat->imag))
+ a = f_to_f(a);
+ return a;
}
return rb_math_hypot(dat->real, dat->imag);
}
@@ -1192,7 +1211,7 @@ nucomp_abs2(VALUE self)
{
get_dat1(self);
return f_add(f_mul(dat->real, dat->real),
- f_mul(dat->imag, dat->imag));
+ f_mul(dat->imag, dat->imag));
}
/*
@@ -1313,10 +1332,10 @@ nucomp_numerator(VALUE self)
cd = nucomp_denominator(self);
return f_complex_new2(CLASS_OF(self),
- f_mul(f_numerator(dat->real),
- f_div(cd, f_denominator(dat->real))),
- f_mul(f_numerator(dat->imag),
- f_div(cd, f_denominator(dat->imag))));
+ f_mul(f_numerator(dat->real),
+ f_div(cd, f_denominator(dat->real))),
+ f_mul(f_numerator(dat->imag),
+ f_div(cd, f_denominator(dat->imag))));
}
/* :nodoc: */
@@ -1346,11 +1365,11 @@ static VALUE
nucomp_eql_p(VALUE self, VALUE other)
{
if (RB_TYPE_P(other, T_COMPLEX)) {
- get_dat2(self, other);
+ get_dat2(self, other);
- return RBOOL((CLASS_OF(adat->real) == CLASS_OF(bdat->real)) &&
- (CLASS_OF(adat->imag) == CLASS_OF(bdat->imag)) &&
- f_eqeq_p(self, other));
+ return RBOOL((CLASS_OF(adat->real) == CLASS_OF(bdat->real)) &&
+ (CLASS_OF(adat->imag) == CLASS_OF(bdat->imag)) &&
+ f_eqeq_p(self, other));
}
return Qfalse;
@@ -1360,8 +1379,8 @@ inline static int
f_signbit(VALUE x)
{
if (RB_FLOAT_TYPE_P(x)) {
- double f = RFLOAT_VALUE(x);
- return !isnan(f) && signbit(f);
+ double f = RFLOAT_VALUE(x);
+ return !isnan(f) && signbit(f);
}
return f_negative_p(x);
}
@@ -1387,7 +1406,7 @@ f_format(VALUE self, VALUE (*func)(VALUE))
rb_str_concat(s, (*func)(f_abs(dat->imag)));
if (!rb_isdigit(RSTRING_PTR(s)[RSTRING_LEN(s) - 1]))
- rb_str_cat2(s, "*");
+ rb_str_cat2(s, "*");
rb_str_cat2(s, "i");
return s;
@@ -1470,7 +1489,7 @@ rb_complex_infinite_p(VALUE self)
get_dat1(self);
if (!f_infinite_p(dat->real) && !f_infinite_p(dat->imag)) {
- return Qnil;
+ return Qnil;
}
return ONE;
}
@@ -1513,7 +1532,7 @@ nucomp_marshal_load(VALUE self, VALUE a)
{
Check_Type(a, T_ARRAY);
if (RARRAY_LEN(a) != 2)
- rb_raise(rb_eArgError, "marshaled complex must have an array whose length is 2 but %ld", RARRAY_LEN(a));
+ rb_raise(rb_eArgError, "marshaled complex must have an array whose length is 2 but %ld", RARRAY_LEN(a));
rb_ivar_set(self, id_i_real, RARRAY_AREF(a, 0));
rb_ivar_set(self, id_i_imag, RARRAY_AREF(a, 1));
return self;
@@ -1575,8 +1594,8 @@ nucomp_to_i(VALUE self)
get_dat1(self);
if (!k_exact_zero_p(dat->imag)) {
- rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Integer",
- self);
+ rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Integer",
+ self);
}
return f_to_i(dat->real);
}
@@ -1598,8 +1617,8 @@ nucomp_to_f(VALUE self)
get_dat1(self);
if (!k_exact_zero_p(dat->imag)) {
- rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Float",
- self);
+ rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Float",
+ self);
}
return f_to_f(dat->real);
}
@@ -1623,8 +1642,8 @@ nucomp_to_r(VALUE self)
get_dat1(self);
if (!k_exact_zero_p(dat->imag)) {
- rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Rational",
- self);
+ rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Rational",
+ self);
}
return f_to_r(dat->real);
}
@@ -1703,14 +1722,14 @@ issign(int c)
static int
read_sign(const char **s,
- char **b)
+ char **b)
{
int sign = '?';
if (issign(**s)) {
- sign = **b = **s;
- (*s)++;
- (*b)++;
+ sign = **b = **s;
+ (*s)++;
+ (*b)++;
}
return sign;
}
@@ -1723,32 +1742,32 @@ isdecimal(int c)
static int
read_digits(const char **s, int strict,
- char **b)
+ char **b)
{
int us = 1;
if (!isdecimal(**s))
- return 0;
+ return 0;
while (isdecimal(**s) || **s == '_') {
- if (**s == '_') {
- if (strict) {
- if (us)
- return 0;
- }
- us = 1;
- }
- else {
- **b = **s;
- (*b)++;
- us = 0;
- }
- (*s)++;
+ if (**s == '_') {
+ if (us) {
+ if (strict) return 0;
+ break;
+ }
+ us = 1;
+ }
+ else {
+ **b = **s;
+ (*b)++;
+ us = 0;
+ }
+ (*s)++;
}
if (us)
- do {
- (*s)--;
- } while (**s == '_');
+ do {
+ (*s)--;
+ } while (**s == '_');
return 1;
}
@@ -1760,70 +1779,70 @@ islettere(int c)
static int
read_num(const char **s, int strict,
- char **b)
+ char **b)
{
if (**s != '.') {
- if (!read_digits(s, strict, b))
- return 0;
+ if (!read_digits(s, strict, b))
+ return 0;
}
if (**s == '.') {
- **b = **s;
- (*s)++;
- (*b)++;
- if (!read_digits(s, strict, b)) {
- (*b)--;
- return 0;
- }
+ **b = **s;
+ (*s)++;
+ (*b)++;
+ if (!read_digits(s, strict, b)) {
+ (*b)--;
+ return 0;
+ }
}
if (islettere(**s)) {
- **b = **s;
- (*s)++;
- (*b)++;
- read_sign(s, b);
- if (!read_digits(s, strict, b)) {
- (*b)--;
- return 0;
- }
+ **b = **s;
+ (*s)++;
+ (*b)++;
+ read_sign(s, b);
+ if (!read_digits(s, strict, b)) {
+ (*b)--;
+ return 0;
+ }
}
return 1;
}
inline static int
read_den(const char **s, int strict,
- char **b)
+ char **b)
{
if (!read_digits(s, strict, b))
- return 0;
+ return 0;
return 1;
}
static int
read_rat_nos(const char **s, int strict,
- char **b)
+ char **b)
{
if (!read_num(s, strict, b))
- return 0;
+ return 0;
if (**s == '/') {
- **b = **s;
- (*s)++;
- (*b)++;
- if (!read_den(s, strict, b)) {
- (*b)--;
- return 0;
- }
+ **b = **s;
+ (*s)++;
+ (*b)++;
+ if (!read_den(s, strict, b)) {
+ (*b)--;
+ return 0;
+ }
}
return 1;
}
static int
read_rat(const char **s, int strict,
- char **b)
+ char **b)
{
read_sign(s, b);
if (!read_rat_nos(s, strict, b))
- return 0;
+ return 0;
return 1;
}
@@ -1831,22 +1850,22 @@ inline static int
isimagunit(int c)
{
return (c == 'i' || c == 'I' ||
- c == 'j' || c == 'J');
+ c == 'j' || c == 'J');
}
static VALUE
str2num(char *s)
{
if (strchr(s, '/'))
- return rb_cstr_to_rat(s, 0);
+ return rb_cstr_to_rat(s, 0);
if (strpbrk(s, ".eE"))
- return DBL2NUM(rb_cstr_to_dbl(s, 0));
+ return DBL2NUM(rb_cstr_to_dbl(s, 0));
return rb_cstr_to_inum(s, 10, 0);
}
static int
read_comp(const char **s, int strict,
- VALUE *ret, char **b)
+ VALUE *ret, char **b)
{
char *bb;
int sign;
@@ -1857,72 +1876,72 @@ read_comp(const char **s, int strict,
sign = read_sign(s, b);
if (isimagunit(**s)) {
- (*s)++;
- num = INT2FIX((sign == '-') ? -1 : + 1);
- *ret = rb_complex_new2(ZERO, num);
- return 1; /* e.g. "i" */
+ (*s)++;
+ num = INT2FIX((sign == '-') ? -1 : + 1);
+ *ret = rb_complex_new2(ZERO, num);
+ return 1; /* e.g. "i" */
}
if (!read_rat_nos(s, strict, b)) {
- **b = '\0';
- num = str2num(bb);
- *ret = rb_complex_new2(num, ZERO);
- return 0; /* e.g. "-" */
+ **b = '\0';
+ num = str2num(bb);
+ *ret = rb_complex_new2(num, ZERO);
+ return 0; /* e.g. "-" */
}
**b = '\0';
num = str2num(bb);
if (isimagunit(**s)) {
- (*s)++;
- *ret = rb_complex_new2(ZERO, num);
- return 1; /* e.g. "3i" */
+ (*s)++;
+ *ret = rb_complex_new2(ZERO, num);
+ return 1; /* e.g. "3i" */
}
if (**s == '@') {
- int st;
-
- (*s)++;
- bb = *b;
- st = read_rat(s, strict, b);
- **b = '\0';
- if (strlen(bb) < 1 ||
- !isdecimal(*(bb + strlen(bb) - 1))) {
- *ret = rb_complex_new2(num, ZERO);
- return 0; /* e.g. "1@-" */
- }
- num2 = str2num(bb);
- *ret = rb_complex_new_polar(num, num2);
- if (!st)
- return 0; /* e.g. "1@2." */
- else
- return 1; /* e.g. "1@2" */
+ int st;
+
+ (*s)++;
+ bb = *b;
+ st = read_rat(s, strict, b);
+ **b = '\0';
+ if (strlen(bb) < 1 ||
+ !isdecimal(*(bb + strlen(bb) - 1))) {
+ *ret = rb_complex_new2(num, ZERO);
+ return 0; /* e.g. "1@-" */
+ }
+ num2 = str2num(bb);
+ *ret = rb_complex_new_polar(num, num2);
+ if (!st)
+ return 0; /* e.g. "1@2." */
+ else
+ return 1; /* e.g. "1@2" */
}
if (issign(**s)) {
- bb = *b;
- sign = read_sign(s, b);
- if (isimagunit(**s))
- num2 = INT2FIX((sign == '-') ? -1 : + 1);
- else {
- if (!read_rat_nos(s, strict, b)) {
- *ret = rb_complex_new2(num, ZERO);
- return 0; /* e.g. "1+xi" */
- }
- **b = '\0';
- num2 = str2num(bb);
- }
- if (!isimagunit(**s)) {
- *ret = rb_complex_new2(num, ZERO);
- return 0; /* e.g. "1+3x" */
- }
- (*s)++;
- *ret = rb_complex_new2(num, num2);
- return 1; /* e.g. "1+2i" */
+ bb = *b;
+ sign = read_sign(s, b);
+ if (isimagunit(**s))
+ num2 = INT2FIX((sign == '-') ? -1 : + 1);
+ else {
+ if (!read_rat_nos(s, strict, b)) {
+ *ret = rb_complex_new2(num, ZERO);
+ return 0; /* e.g. "1+xi" */
+ }
+ **b = '\0';
+ num2 = str2num(bb);
+ }
+ if (!isimagunit(**s)) {
+ *ret = rb_complex_new2(num, ZERO);
+ return 0; /* e.g. "1+3x" */
+ }
+ (*s)++;
+ *ret = rb_complex_new2(num, num2);
+ return 1; /* e.g. "1+2i" */
}
/* !(@, - or +) */
{
- *ret = rb_complex_new2(num, ZERO);
- return 1; /* e.g. "3" */
+ *ret = rb_complex_new2(num, ZERO);
+ return 1; /* e.g. "3" */
}
}
@@ -1930,7 +1949,7 @@ inline static void
skip_ws(const char **s)
{
while (isspace((unsigned char)**s))
- (*s)++;
+ (*s)++;
}
static int
@@ -1971,22 +1990,22 @@ string_to_c_strict(VALUE self, int raise)
if (!s || memchr(s, '\0', RSTRING_LEN(self))) {
if (!raise) return Qnil;
- rb_raise(rb_eArgError, "string contains null byte");
+ rb_raise(rb_eArgError, "string contains null byte");
}
if (s && s[RSTRING_LEN(self)]) {
- rb_str_modify(self);
- s = RSTRING_PTR(self);
- s[RSTRING_LEN(self)] = '\0';
+ rb_str_modify(self);
+ s = RSTRING_PTR(self);
+ s[RSTRING_LEN(self)] = '\0';
}
if (!s)
- s = (char *)"";
+ s = (char *)"";
if (!parse_comp(s, 1, &num)) {
if (!raise) return Qnil;
- rb_raise(rb_eArgError, "invalid value for convert(): %+"PRIsVALUE,
- self);
+ rb_raise(rb_eArgError, "invalid value for convert(): %+"PRIsVALUE,
+ self);
}
return num;
@@ -2013,6 +2032,12 @@ string_to_c_strict(VALUE self, int raise)
* '1/2+3/4i'.to_c #=> ((1/2)+(3/4)*i)
* 'ruby'.to_c #=> (0+0i)
*
+ * Polar form:
+ * include Math
+ * "1.0@0".to_c #=> (1+0.0i)
+ * "1.0@#{PI/2}".to_c #=> (0.0+1i)
+ * "1.0@#{PI}".to_c #=> (-1+0.0i)
+ *
* See Kernel.Complex.
*/
static VALUE
@@ -2026,13 +2051,13 @@ string_to_c(VALUE self)
s = RSTRING_PTR(self);
if (s && s[RSTRING_LEN(self)]) {
- rb_str_modify(self);
- s = RSTRING_PTR(self);
- s[RSTRING_LEN(self)] = '\0';
+ rb_str_modify(self);
+ s = RSTRING_PTR(self);
+ s[RSTRING_LEN(self)] = '\0';
}
if (!s)
- s = (char *)"";
+ s = (char *)"";
(void)parse_comp(s, 0, &num);
@@ -2050,65 +2075,68 @@ nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise)
{
if (NIL_P(a1) || NIL_P(a2)) {
if (!raise) return Qnil;
- rb_raise(rb_eTypeError, "can't convert nil into Complex");
+ rb_raise(rb_eTypeError, "can't convert nil into Complex");
}
if (RB_TYPE_P(a1, T_STRING)) {
- a1 = string_to_c_strict(a1, raise);
+ a1 = string_to_c_strict(a1, raise);
if (NIL_P(a1)) return Qnil;
}
if (RB_TYPE_P(a2, T_STRING)) {
- a2 = string_to_c_strict(a2, raise);
+ a2 = string_to_c_strict(a2, raise);
if (NIL_P(a2)) return Qnil;
}
if (RB_TYPE_P(a1, T_COMPLEX)) {
- {
- get_dat1(a1);
+ {
+ get_dat1(a1);
- if (k_exact_zero_p(dat->imag))
- a1 = dat->real;
- }
+ if (k_exact_zero_p(dat->imag))
+ a1 = dat->real;
+ }
}
if (RB_TYPE_P(a2, T_COMPLEX)) {
- {
- get_dat1(a2);
+ {
+ get_dat1(a2);
- if (k_exact_zero_p(dat->imag))
- a2 = dat->real;
- }
+ if (k_exact_zero_p(dat->imag))
+ a2 = dat->real;
+ }
}
if (RB_TYPE_P(a1, T_COMPLEX)) {
- if (a2 == Qundef || (k_exact_zero_p(a2)))
- return a1;
- }
-
- if (a2 == Qundef) {
- if (k_numeric_p(a1) && !f_real_p(a1))
- return a1;
- /* should raise exception for consistency */
- if (!k_numeric_p(a1)) {
- if (!raise)
- return rb_protect(to_complex, a1, NULL);
- return to_complex(a1);
+ if (UNDEF_P(a2) || (k_exact_zero_p(a2)))
+ return a1;
+ }
+
+ if (UNDEF_P(a2)) {
+ if (k_numeric_p(a1) && !f_real_p(a1))
+ return a1;
+ /* should raise exception for consistency */
+ if (!k_numeric_p(a1)) {
+ if (!raise) {
+ a1 = rb_protect(to_complex, a1, NULL);
+ rb_set_errinfo(Qnil);
+ return a1;
+ }
+ return to_complex(a1);
}
}
else {
- if ((k_numeric_p(a1) && k_numeric_p(a2)) &&
- (!f_real_p(a1) || !f_real_p(a2)))
- return f_add(a1,
- f_mul(a2,
- f_complex_new_bang2(rb_cComplex, ZERO, ONE)));
+ if ((k_numeric_p(a1) && k_numeric_p(a2)) &&
+ (!f_real_p(a1) || !f_real_p(a2)))
+ return f_add(a1,
+ f_mul(a2,
+ f_complex_new_bang2(rb_cComplex, ZERO, ONE)));
}
{
int argc;
- VALUE argv2[2];
- argv2[0] = a1;
- if (a2 == Qundef) {
+ VALUE argv2[2];
+ argv2[0] = a1;
+ if (UNDEF_P(a2)) {
argv2[1] = Qnil;
argc = 1;
}
@@ -2118,7 +2146,7 @@ nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise)
argv2[1] = a2;
argc = 2;
}
- return nucomp_s_new(argc, argv2, klass);
+ return nucomp_s_new(argc, argv2, klass);
}
}
@@ -2136,31 +2164,6 @@ nucomp_s_convert(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * num.real -> self
- *
- * Returns self.
- */
-static VALUE
-numeric_real(VALUE self)
-{
- return self;
-}
-
-/*
- * call-seq:
- * num.imag -> 0
- * num.imaginary -> 0
- *
- * Returns zero.
- */
-static VALUE
-numeric_imag(VALUE self)
-{
- return INT2FIX(0);
-}
-
-/*
- * call-seq:
* num.abs2 -> real
*
* Returns square of self.
@@ -2232,19 +2235,6 @@ numeric_polar(VALUE self)
/*
* call-seq:
- * num.conj -> self
- * num.conjugate -> self
- *
- * Returns self.
- */
-static VALUE
-numeric_conj(VALUE self)
-{
- return self;
-}
-
-/*
- * call-seq:
* flo.arg -> 0 or float
* flo.angle -> 0 or float
* flo.phase -> 0 or float
@@ -2255,9 +2245,9 @@ static VALUE
float_arg(VALUE self)
{
if (isnan(RFLOAT_VALUE(self)))
- return self;
+ return self;
if (f_tpositive_p(self))
- return INT2FIX(0);
+ return INT2FIX(0);
return rb_const_get(rb_mMath, id_PI);
}
@@ -2408,9 +2398,6 @@ Init_Complex(void)
rb_define_private_method(CLASS_OF(rb_cComplex), "convert", nucomp_s_convert, -1);
- rb_define_method(rb_cNumeric, "real", numeric_real, 0);
- rb_define_method(rb_cNumeric, "imaginary", numeric_imag, 0);
- rb_define_method(rb_cNumeric, "imag", numeric_imag, 0);
rb_define_method(rb_cNumeric, "abs2", numeric_abs2, 0);
rb_define_method(rb_cNumeric, "arg", numeric_arg, 0);
rb_define_method(rb_cNumeric, "angle", numeric_arg, 0);
@@ -2418,8 +2405,6 @@ Init_Complex(void)
rb_define_method(rb_cNumeric, "rectangular", numeric_rect, 0);
rb_define_method(rb_cNumeric, "rect", numeric_rect, 0);
rb_define_method(rb_cNumeric, "polar", numeric_polar, 0);
- rb_define_method(rb_cNumeric, "conjugate", numeric_conj, 0);
- rb_define_method(rb_cNumeric, "conj", numeric_conj, 0);
rb_define_method(rb_cFloat, "arg", float_arg, 0);
rb_define_method(rb_cFloat, "angle", float_arg, 0);
@@ -2429,7 +2414,7 @@ Init_Complex(void)
* The imaginary unit.
*/
rb_define_const(rb_cComplex, "I",
- f_complex_new_bang2(rb_cComplex, ZERO, ONE));
+ f_complex_new_bang2(rb_cComplex, ZERO, ONE));
#if !USE_FLONUM
rb_gc_register_mark_object(RFLOAT_0 = DBL2NUM(0.0));
diff --git a/configure.ac b/configure.ac
index 4fe1623966..220392d120 100644
--- a/configure.ac
+++ b/configure.ac
@@ -64,14 +64,22 @@ AC_ARG_WITH(baseruby,
[
AC_PATH_PROG([BASERUBY], [ruby], [false])
])
+# BASERUBY must be >= 2.2.0. Note that `"2.2.0" > "2.2"` is true.
AS_IF([test "$HAVE_BASERUBY" != no -a "`RUBYOPT=- $BASERUBY --disable=gems -e 'print 42 if RUBY_VERSION > "2.2"' 2>/dev/null`" = 42], [
+ AS_CASE(["$build_os"], [mingw*], [
+ # Can MSys shell run a command with a drive letter?
+ RUBYOPT=- `cygpath -ma "$BASERUBY"` --disable=gems -e exit 2>/dev/null || HAVE_BASERUBY=no
+ ])
BASERUBY="$BASERUBY --disable=gems"
BASERUBY_VERSION=`$BASERUBY -v`
$BASERUBY -C "$srcdir" tool/downloader.rb -d tool -e gnu config.guess config.sub >&AS_MESSAGE_FD
], [
- BASERUBY="echo executable host ruby is required. use --with-baseruby option.; false"
HAVE_BASERUBY=no
])
+AS_IF([test "$HAVE_BASERUBY" = no], [
+ AS_IF([test "$cross_compiling" = yes], [AC_MSG_ERROR([executable host ruby is required for cross-compiling])])
+ BASERUBY="echo executable host ruby is required. use --with-baseruby option.; false"
+])
AC_SUBST(BASERUBY)
AC_SUBST(HAVE_BASERUBY)
@@ -88,7 +96,9 @@ AC_SUBST(GIT)
AC_SUBST(HAVE_GIT)
eval `sed -n -e ['s/^@%:@define RUBY_[A-Z_]*VERSION_\([A-Z][A-Z][A-Z_0-9]*\) \([0-9][0-9]*\)$/\1=\2/p'] \
+ -e ['s/^@%:@define \(RUBY_ABI_VERSION\) \([0-9][0-9]*\).*/\1=\2/p'] \
-e ['s/^@%:@define \(RUBY_PATCHLEVEL\) \(.*\)/\1=\2/p'] \
+ $srcdir/include/ruby/internal/abi.h \
$srcdir/include/ruby/version.h $srcdir/version.h`
for v in MAJOR MINOR TEENY; do
AS_IF([eval "test \"\$$v\" = ''"], [
@@ -100,6 +110,9 @@ AC_SUBST(MINOR)
AC_SUBST(TEENY)
AC_SUBST(RUBY_API_VERSION, '$(MAJOR).$(MINOR)')
AC_SUBST(RUBY_PROGRAM_VERSION, '$(MAJOR).$(MINOR).$(TEENY)')
+AS_CASE([$RUBY_PATCHLEVEL], [-*], [
+ AC_DEFINE_UNQUOTED(RUBY_ABI_VERSION, [${RUBY_ABI_VERSION}])
+], [RUBY_ABI_VERSION=])
AS_IF([test "$program_prefix" = NONE], [
program_prefix=
@@ -120,9 +133,12 @@ AC_CANONICAL_TARGET
AS_CASE(["$target_cpu-$target_os"],
[aarch64-darwin*], [
target_cpu=arm64
- AS_CASE(["$target_vendor"], [unknown], [target_vendor=apple target=${target/-unknown-/-apple-}])
- target="${target/aarch64/arm64}"
- target_alias="${target_alias/aarch64/arm64}"
+ AS_CASE(["$target_vendor"], [unknown], [
+ target_vendor=apple
+ target=${target%%-unknown-*}-apple-${target@%:@*-unknown-}
+ ])
+ target="arm64-${target@%:@aarch64-}"
+ AS_IF([test -n "$target_alias"], [target_alias="arm64-${target_alias@%:@aarch64-}"])
])
AC_ARG_PROGRAM
@@ -198,15 +214,23 @@ AS_CASE(["/${rb_CC} "],
[*clang*], [
# Ditto for LLVM. Note however that llvm-as is a LLVM-IR to LLVM bitcode
# assembler that does not target your machine native binary.
- : ${LD:="${CC}"} # ... try -fuse-ld=lld ?
- RUBY_CHECK_PROG_FOR_CC([AR], [s/clang/llvm-ar/])
-# RUBY_CHECK_PROG_FOR_CC([AS], [s/clang/llvm-as/])
+
+ # Xcode has its own version tools that may be incompatible with
+ # genuine LLVM tools, use the tools in the same directory.
+
+ AS_IF([$rb_CC -E -dM -xc - < /dev/null | grep -F __apple_build_version__ > /dev/null],
+ [llvm_prefix=], [llvm_prefix=llvm-])
+ # AC_PREPROC_IFELSE cannot be used before AC_USE_SYSTEM_EXTENSIONS
+
+ RUBY_CHECK_PROG_FOR_CC([LD], [s/clang/ld/]) # ... maybe try lld ?
+ RUBY_CHECK_PROG_FOR_CC([AR], [s/clang/${llvm_prefix}ar/])
+# RUBY_CHECK_PROG_FOR_CC([AS], [s/clang/${llvm_prefix}as/])
RUBY_CHECK_PROG_FOR_CC([CXX], [s/clang/clang++/])
- RUBY_CHECK_PROG_FOR_CC([NM], [s/clang/llvm-nm/])
- RUBY_CHECK_PROG_FOR_CC([OBJCOPY], [s/clang/llvm-objcopy/])
- RUBY_CHECK_PROG_FOR_CC([OBJDUMP], [s/clang/llvm-objdump/])
- RUBY_CHECK_PROG_FOR_CC([RANLIB], [s/clang/llvm-ranlib/])
- RUBY_CHECK_PROG_FOR_CC([STRIP], [s/clang/llvm-strip/])
+ RUBY_CHECK_PROG_FOR_CC([NM], [s/clang/${llvm_prefix}nm/])
+ RUBY_CHECK_PROG_FOR_CC([OBJCOPY], [s/clang/${llvm_prefix}objcopy/])
+ RUBY_CHECK_PROG_FOR_CC([OBJDUMP], [s/clang/${llvm_prefix}objdump/])
+ RUBY_CHECK_PROG_FOR_CC([RANLIB], [s/clang/${llvm_prefix}ranlib/])
+ RUBY_CHECK_PROG_FOR_CC([STRIP], [s/clang/${llvm_prefix}strip/])
])
AS_UNSET(rb_CC)
AS_UNSET(rb_dummy)
@@ -350,12 +374,6 @@ AS_CASE(["$target_os"],
[AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)
AC_MSG_ERROR([Unsupported OS X version is required])])
- AC_CACHE_CHECK([if thread-local storage is supported], [rb_cv_tls_supported],
- [AC_LINK_IFELSE([AC_LANG_PROGRAM([[int __thread conftest;]])],
- [rb_cv_tls_supported=yes],
- [rb_cv_tls_supported=no])])
- AS_IF([test x"$rb_cv_tls_supported" != xyes],
- [AC_DEFINE(RB_THREAD_LOCAL_SPECIFIER_IS_UNSUPPORTED)])
])
RUBY_MINGW32
@@ -375,6 +393,13 @@ AS_IF([test "$GCC" = yes], [
AS_IF([test "$gcc_major" -lt 4], [
AC_MSG_ERROR([too old GCC: $gcc_major.$gcc_minor])
])
+
+ AC_CACHE_CHECK([if thread-local storage is supported], [rb_cv_tls_supported],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[int __thread conftest;]])],
+ [rb_cv_tls_supported=yes],
+ [rb_cv_tls_supported=no])])
+ AS_IF([test x"$rb_cv_tls_supported" != xyes],
+ [AC_DEFINE(RB_THREAD_LOCAL_SPECIFIER_IS_UNSUPPORTED)])
], [
linker_flag=
])
@@ -485,6 +510,7 @@ AS_CASE(["$target_os"],
AC_DEFINE_UNQUOTED(RUBY_MSVCRT_VERSION, $RT_VER)
sysconfdir=
])
+ rb_cv_binary_elf=no
: ${enable_shared=yes}
],
[hiuxmpp*], [AC_DEFINE(__HIUX_MPP__)]) # by TOYODA Eizi <toyoda@npd.kishou.go.jp>
@@ -507,11 +533,16 @@ AS_IF([test "$cross_compiling:$ac_cv_prog_DTRACE" = no: -a -n "$ac_tool_prefix"]
AC_CHECK_PROGS(DOT, dot)
AC_CHECK_PROGS(DOXYGEN, doxygen)
-for prog in ${ac_tool_prefix:+${ac_tool_prefix}pkg-config} pkg-config; do
- AC_CHECK_PROG(PKG_CONFIG, $prog, [$prog], [], [],
- [`"$as_dir/$ac_word$ac_exec_ext" --print-errors --version > /dev/null 2>&1 || echo "$as_dir/$ac_word$ac_exec_ext"`])
- test -z "${PKG_CONFIG}" || break
-done
+tool_warned=$ac_tool_warned ac_tool_warned=no
+AC_CHECK_TOOL(PKG_CONFIG, pkg-config)
+ac_tool_warned=$tool_warned
+AS_IF([test -z "$PKG_CONFIG"], [],
+["$PKG_CONFIG" --print-errors --version > /dev/null 2>&1], [],
+[
+ unset ac_cv_prog_PKG_CONFIG
+ PKG_CONFIG=
+ AC_MSG_WARN([$PKG_CONFIG does not work; ignore])
+])
AC_MSG_CHECKING([whether it is Android])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@@ -594,22 +625,39 @@ RUBY_WERROR_FLAG([
cd .. && rm -fr tmp.$$.try_link
])
-: ${RPATHFLAG=''}
-rpathflag=''
-AS_IF([test x"${RPATHFLAG}" = x], [
- AS_CASE(["$target_os"],
+: "rpath" && {
+ AC_CACHE_CHECK(whether ELF binaries are produced, rb_cv_binary_elf,
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[
+ AS_CASE(["`head -1 conftest$EXEEXT | tr -dc '\177ELF' | tr '\177' .`"],
+ [.ELF*], [rb_cv_binary_elf=yes], [rb_cv_binary_elf=no])],
+ [rb_cv_binary_elf=no])])
+
+ rpathflag=''
+ AS_IF([test x"${RPATHFLAG=}" = x], [
+ AS_CASE(["$target_os"],
[aix*], [rpathflag='-blibpath:'],
- [for rpathflag in -R "-rpath "; do
+ [for rpathflag in "-rpath " -R; do
AS_CASE("$rpathflag",
[*" "], [AS_CASE(["${linker_flag}"],
[*,], [rpathflag=`echo "$rpathflag" | tr ' ' ,`])])
rpathflag="${linker_flag}${rpathflag}"
RUBY_TRY_LDFLAGS([${rpathflag}.], [], [rpathflag=])
- AS_IF([test "x${rpathflag}" != x], [])
+ AS_IF([test "x${rpathflag}" != x], [break])
done])
-], [
- rpathflag=`echo "$RPATHFLAG" | sed 's/%.*//'`
-])
+ ], [
+ rpathflag=`echo "$RPATHFLAG" | sed 's/%.*//'`
+ ])
+
+ AC_ARG_ENABLE(rpath,
+ AS_HELP_STRING([--enable-rpath], [embed run path into extension libraries.
+ enabled by default on ELF platforms]),
+ [enable_rpath=$enableval], [enable_rpath="$rb_cv_binary_elf"])
+
+ AS_IF([test "$enable_rpath:${RPATHFLAG}" = yes:], [
+ RPATHFLAG="${rpathflag:+ ${rpathflag}%1\$-s}"
+ ])
+ AS_CASE([${RPATHFLAG}],[*'%1$'*],[: ${LIBPATHFLAG=' -L%1$-s'}],[: ${LIBPATHFLAG=' -L%s'}])
+}
RUBY_TRY_LDFLAGS(-fdeclspec, [fdeclspec=yes], [fdeclspec=no])
AS_IF([test "$fdeclspec" = yes], [
@@ -622,9 +670,13 @@ AS_IF([test "$fdeclspec" = yes], [
RUBY_APPEND_OPTIONS(CXXFLAGS, -fdeclspec)
])
-AS_CASE([$RUBY_PATCHLEVEL], [-*],
- [RUBY_DEVEL=yes], [RUBY_DEVEL=no])
-particular_werror_flags=$RUBY_DEVEL
+AC_ARG_ENABLE(devel,
+ AS_HELP_STRING([--enable-devel], [enable development build]),
+ [RUBY_DEVEL=$enableval],
+ [AS_IF([test "x${RUBY_DEVEL-no}" != xyes], [RUBY_DEVEL=])]
+)dnl
+AC_SUBST(RUBY_DEVEL)
+particular_werror_flags=${RUBY_DEVEL:-no}
AC_ARG_ENABLE(werror,
AS_HELP_STRING([--disable-werror],
[don't make warnings into errors
@@ -748,7 +800,8 @@ AS_IF([test "$GCC" = yes], [
[disable -D_FORTIFY_SOURCE=2 option, which causes link error on mingw]),
[fortify_source=$enableval])
AS_IF([test "x$fortify_source" != xno], [
- RUBY_TRY_CFLAGS([$optflags -D_FORTIFY_SOURCE=2], [RUBY_APPEND_OPTION(XCFLAGS, -D_FORTIFY_SOURCE=2)], [],
+ RUBY_TRY_CFLAGS([$optflags -D_FORTIFY_SOURCE=2],
+ [RUBY_APPEND_OPTION(XCFLAGS, -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2)], [],
[@%:@include <stdio.h>])
])
@@ -756,7 +809,7 @@ AS_IF([test "$GCC" = yes], [
# -fstack-protector
AS_CASE(["$target_os"],
- [mingw*|emscripten*|wasi*], [
+ [emscripten*|wasi*], [
stack_protector=no
])
AS_IF([test -z "${stack_protector+set}"], [
@@ -768,6 +821,8 @@ AS_IF([test "$GCC" = yes], [
AS_IF([test "x$stack_protector" = xyes], [stack_protector=option; break])
])
])
+ AC_MSG_CHECKING([for -fstack-protector])
+ AC_MSG_RESULT(["$stack_protector"])
AS_CASE(["$stack_protector"], [-*], [
RUBY_APPEND_OPTION(XCFLAGS, $stack_protector)
RUBY_APPEND_OPTION(XLDFLAGS, $stack_protector)
@@ -833,33 +888,6 @@ AS_IF([test "$GCC" = yes], [
# need lgamma_r()
])
- # ANSI (no XCFLAGS because this is C only)
- AS_CASE(["$target_os"],
- [solaris*], [
- # Because "-std=gnu99" affects existence of functions on Solaris,
- # "-std=gnu99" will be appended to CPPFLAGS.
- for ansi_options in -std=gnu99; do
- RUBY_TRY_CFLAGS(${ansi_options}, [
- RUBY_APPEND_OPTIONS(CPPFLAGS, ${ansi_options})
- ], [ansi_options=])
- test "x${ansi_options}" = x || break
- done
- ],
- [
- # ANSI (no XCFLAGS because this is C only)
- rb_tmp_std_check=`echo $CC $CFLAGS $optflags $warnflags $debugflags | fgrep std= | tr -d '\015'`
- AS_IF([test "x$rb_tmp_std_check" = "x"],
- [
- for ansi_options in -std=gnu99; do
- RUBY_TRY_CFLAGS(${ansi_options}, [
- RUBY_APPEND_OPTIONS(warnflags, ${ansi_options})
- RUBY_APPEND_OPTIONS(strict_warnflags, ${ansi_options})
- ], [ansi_options=])
- test "x${ansi_options}" = x || break
- done
- ])
- ])
-
# suppress annoying -Wstrict-overflow warnings
RUBY_TRY_CFLAGS(-fno-strict-overflow, [RUBY_APPEND_OPTION(XCFLAGS, -fno-strict-overflow)])
@@ -868,7 +896,6 @@ AS_IF([test "$GCC" = yes], [
test "${debugflags+set}" || {RUBY_TRY_CFLAGS(-g3, [debugflags=-g3])}
])
test $ac_cv_prog_cc_g = yes && : ${debugflags=-g}
-AS_IF([test "x$RUBY_DEVEL" = xyes], [RUBY_APPEND_OPTION(XCFLAGS, -DRUBY_DEVEL=1)])
AS_IF([test "$GCC" = ""], [
AS_CASE(["$target_os"],[aix*],[warnflags="$warnflags -qinfo=por" rb_cv_warnflags="$rb_cv_warnflags -qinfo=por"])
@@ -923,17 +950,35 @@ AS_CASE(["$target_cpu"], [[i[3-6]86*]], [
AS_IF([test "$rb_cv_gcc_compiler_cas" = i486], [ARCH_FLAG="-march=i486"])
])
+OPT_DIR=
+AC_ARG_WITH([gmp-dir],
+ AS_HELP_STRING([--with-gmp-dir=DIR],
+ [specify the prefix directory where gmp is installed]),
+ [OPT_DIR="${OPT_DIR:+$OPT_DIR$PATH_SEPARATOR}$withval"], [])
+AC_ARG_WITH([gmp],
+ [AS_HELP_STRING([--without-gmp],
+ [disable GNU GMP to accelerate Bignum operations])],
+ [], [with_gmp=yes])
+
AC_ARG_WITH(opt-dir,
AS_HELP_STRING([--with-opt-dir=DIR-LIST],
[add optional headers and libraries directories separated by $PATH_SEPARATOR]),
- [
- val=`echo "$PATH_SEPARATOR$withval" | sed "s|$PATH_SEPARATOR\([[^$PATH_SEPARATOR]*]\)| -I\1/include|g;s/^ //"`
- CPPFLAGS="$CPPFLAGS $val"
- val=`echo "$PATH_SEPARATOR$withval" | sed "s|$PATH_SEPARATOR\([[^$PATH_SEPARATOR]*]\)| -L\1/lib${rpathflag:+ $rpathflag\\\\1/lib}|g;s/^ //"`
- LDFLAGS="$LDFLAGS $val"
- LDFLAGS_OPTDIR="$val"
- OPT_DIR="$withval"
- ], [OPT_DIR=])
+ [OPT_DIR="${OPT_DIR:+$OPT_DIR$PATH_SEPARATOR}$withval"], [])
+
+AS_IF([test "x$OPT_DIR" != x], [
+ val=`IFS="$PATH_SEPARATOR"
+ for dir in $OPT_DIR; do
+ test -z "$dir" && continue
+ echo x ${LIBPATHFLAG} ${RPATHFLAG} |
+ sed "s/^x *//;s${IFS}"'%1\\$-s'"${IFS}${dir}/lib${IFS}g;s${IFS}%s${IFS}${dir}/lib${IFS}g"
+ done | tr '\012' ' ' | sed 's/ *$//'`
+ LDFLAGS="${LDFLAGS:+$LDFLAGS }$val"
+ DLDFLAGS="${DLDFLAGS:+$DLDFLAGS }$val"
+ LDFLAGS_OPTDIR="$val"
+ INCFLAGS="${INCFLAGS:+$INCFLAGS }"`echo "$OPT_DIR" | tr "${PATH_SEPARATOR}" '\012' |
+ sed '/^$/d;s|^|-I|;s|$|/include|' | tr '\012' ' ' | sed 's/ *$//'`
+])
+AC_SUBST(incflags, "$INCFLAGS")
test -z "${ac_env_CFLAGS_set}" -a -n "${cflags+set}" && eval CFLAGS="\"$cflags $ARCH_FLAG\""
test -z "${ac_env_CXXFLAGS_set}" -a -n "${cxxflags+set}" && eval CXXFLAGS="\"$cxxflags $ARCH_FLAG\""
@@ -1152,15 +1197,12 @@ main()
ac_cv_func_getpgrp_void=no
ac_cv_func_memcmp_working=yes
ac_cv_lib_dl_dlopen=no
- rb_cv_binary_elf=no
rb_cv_negative_time_t=yes
ac_cv_func_fcntl=yes
ac_cv_func_flock=yes
ac_cv_func_gmtime_r=yes
rb_cv_large_fd_select=yes
ac_cv_type_struct_timeval=yes
- ac_cv_func_clock_gettime=yes
- ac_cv_func_clock_getres=yes
ac_cv_func_malloc_usable_size=no
ac_cv_type_off_t=yes
ac_cv_sizeof_off_t=8
@@ -1255,6 +1297,11 @@ dnl AC_HEADER_STDC has been checked in AC_USE_SYSTEM_EXTENSIONS
AC_HEADER_STDBOOL
AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS([afunix.h], [], [],
+[#ifdef _WIN32
+# include <winsock2.h>
+#endif
+])
AC_CHECK_HEADERS(atomic.h)
AC_CHECK_HEADERS(copyfile.h)
AC_CHECK_HEADERS(direct.h)
@@ -1301,16 +1348,13 @@ AC_CHECK_HEADERS(syscall.h)
AC_CHECK_HEADERS(time.h)
AC_CHECK_HEADERS(ucontext.h)
AC_CHECK_HEADERS(utime.h)
+AC_CHECK_HEADERS(stdatomic.h)
+
AS_CASE("$target_cpu", [x64|x86_64|i[3-6]86*], [
AC_CHECK_HEADERS(x86intrin.h)
])
RUBY_UNIVERSAL_CHECK_HEADER([x86_64, i386], x86intrin.h)
-AC_ARG_WITH([gmp],
- [AS_HELP_STRING([--without-gmp],
- [disable GNU GMP to accelerate Bignum operations])],
- [],
- [with_gmp=yes])
AS_IF([test "x$with_gmp" != xno],
[AC_CHECK_HEADERS(gmp.h)
AS_IF([test "x$ac_cv_header_gmp_h" != xno],
@@ -1322,6 +1366,8 @@ AC_ARG_WITH([jemalloc],
[with_jemalloc=$withval], [with_jemalloc=no])
AS_IF([test "x$with_jemalloc" != xno],[
# find jemalloc header first
+ save_CPPFLAGS="${CPPFLAGS}"
+ CPPFLAGS="${INCFLAGS} ${CPPFLAGS}"
malloc_header=
AC_CHECK_HEADER(jemalloc/jemalloc.h, [malloc_header=jemalloc/jemalloc.h], [
AC_CHECK_HEADER(jemalloc.h, [malloc_header=jemalloc.h])
@@ -1353,6 +1399,8 @@ AS_IF([test "x$with_jemalloc" != xno],[
done
done
])
+ CPPFLAGS="${save_CPPFLAGS}"
+ unset save_CPPFLAGS
with_jemalloc=${rb_cv_jemalloc_library}
AS_CASE(["$with_jemalloc"],
[no],
@@ -1882,8 +1930,8 @@ AS_CASE(["${target_cpu}-${target_os}:${target_archs}"],
[universal-darwin*:*ppc*], [
AC_LIBSOURCES(alloca.c)
AC_SUBST([ALLOCA], [\${LIBOBJDIR}alloca.${ac_objext}])
- RUBY_DEFINE_IF([defined __powerpc__], C_ALLOCA, 1)
- RUBY_DEFINE_IF([defined __powerpc__], alloca, alloca)
+ RUBY_DEFINE_IF([defined __POWERPC__], C_ALLOCA, 1) # Darwin defines __POWERPC__ for ppc and ppc64 both
+ RUBY_DEFINE_IF([defined __POWERPC__], alloca, alloca)
],
[
AC_FUNC_ALLOCA
@@ -1963,6 +2011,7 @@ AC_CHECK_FUNCS(_longjmp) # used for AC_ARG_WITH(setjmp-type)
test x$ac_cv_func__longjmp = xno && ac_cv_func__setjmp=no
AC_CHECK_FUNCS(arc4random_buf)
AC_CHECK_FUNCS(atan2l atan2f)
+AC_CHECK_DECLS(atomic_signal_fence, [], [], [#include <stdatomic.h>])
AC_CHECK_FUNCS(chmod)
AC_CHECK_FUNCS(chown)
AC_CHECK_FUNCS(chroot)
@@ -2553,10 +2602,13 @@ AS_CASE([$coroutine_type], [yes|''], [
[arm64-darwin*], [
coroutine_type=arm64
],
- [powerpc-darwin*], [
+ # Correct target name is powerpc*-, but Ruby seems to prefer ppc*-.
+ # Notice that Darwin PPC ABI differs from AIX and ELF.
+ # Adding PPC targets for AIX, *BSD and *Linux will require separate implementations.
+ [powerpc-darwin*|ppc-darwin*], [
coroutine_type=ppc
],
- [powerpc64-darwin*], [
+ [powerpc64-darwin*|ppc64-darwin*], [
coroutine_type=ppc64
],
[x*64-linux*], [
@@ -2862,12 +2914,6 @@ AC_ARG_WITH(dln-a-out,
])
])
-AC_CACHE_CHECK(whether ELF binaries are produced, rb_cv_binary_elf,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[
-AS_CASE(["`head -1 conftest$EXEEXT | tr -dc '\177ELF' | tr '\177' .`"],
-[.ELF*], [rb_cv_binary_elf=yes], [rb_cv_binary_elf=no])],
-rb_cv_binary_elf=no)])
-
AS_IF([test "$rb_cv_binary_elf" = yes], [
AC_DEFINE(USE_ELF)
AC_CHECK_HEADERS([elf.h elf_abi.h])
@@ -2944,13 +2990,25 @@ STATIC=
])
}
-: "rpath" && {
- AC_ARG_ENABLE(rpath,
- AS_HELP_STRING([--enable-rpath], [embed run path into extension libraries.
- enabled by default on ELF platforms]),
- [enable_rpath=$enableval], [enable_rpath="$rb_cv_binary_elf"])
+EXTSTATIC=
+AC_SUBST(EXTSTATIC)dnl
+AC_ARG_WITH(static-linked-ext,
+ AS_HELP_STRING([--with-static-linked-ext], [link external modules statically]),
+ [AS_CASE([$withval],[yes],[STATIC=;EXTSTATIC=static],[no],[],[EXTSTATIC="$withval"])])
+AS_CASE([",$EXTSTATIC,"], [,static,|*,enc,*], [
+ ENCOBJS='enc/encinit.$(OBJEXT) enc/libenc.$(LIBEXT) enc/libtrans.$(LIBEXT)'
+ EXTOBJS='ext/extinit.$(OBJEXT)'
+ AC_DEFINE_UNQUOTED(EXTSTATIC, 1)
+ AC_SUBST(ENCSTATIC, static)
+], [
+ ENCOBJS='dmyenc.$(OBJEXT)'
+ EXTOBJS='dmyext.$(OBJEXT)'
+])
+AC_SUBST(ENCOBJS)
+AC_SUBST(EXTOBJS)
- AS_CASE(["$target_os"],
+: "rpath" && {
+ AS_CASE(["$target_os"],
[solaris*], [ AS_IF([test "$GCC" = yes], [
: ${LDSHARED='$(CC) -shared'}
AS_IF([test "$rb_cv_prog_gnu_ld" = yes], [
@@ -2990,7 +3048,6 @@ STATIC=
rb_cv_dlopen=yes],
[interix*], [ : ${LDSHARED='$(CC) -shared'}
XLDFLAGS="$XLDFLAGS -Wl,-E"
- LIBPATHFLAG=" -L%1\$-s"
rb_cv_dlopen=yes],
[freebsd*|dragonfly*], [
: ${LDSHARED='$(CC) -shared'}
@@ -3009,8 +3066,16 @@ STATIC=
[darwin*], [ : ${LDSHARED='$(CC) -dynamic -bundle'}
: ${DLDSHARED='$(CC) -dynamiclib'}
: ${LDFLAGS=""}
- : ${LIBPATHENV=DYLD_FALLBACK_LIBRARY_PATH}
+ : ${LIBPATHENV=DYLD_LIBRARY_PATH}
: ${PRELOADENV=DYLD_INSERT_LIBRARIES}
+ AS_IF([test x"$enable_shared" = xyes], [
+ # Resolve symbols from libruby.dylib when --enable-shared
+ EXTDLDFLAGS='$(LIBRUBYARG_SHARED)'
+ ], [test "x$EXTSTATIC" = x], [
+ # When building exts as bundles, a mach-o bundle needs to know its loader
+ # program to bind symbols from the ruby executable
+ EXTDLDFLAGS="-bundle_loader '\$(BUILTRUBY)'"
+ ])
rb_cv_dlopen=yes],
[aix*], [ : ${LDSHARED='$(CC)'}
AS_IF([test "$GCC" = yes], [
@@ -3042,30 +3107,35 @@ STATIC=
[atheos*], [ : ${LDSHARED='$(CC) -shared'}
rb_cv_dlopen=yes],
[ : ${LDSHARED='$(LD)'}])
- AC_MSG_RESULT($rb_cv_dlopen)
+ AC_MSG_RESULT($rb_cv_dlopen)
+}
- AS_IF([test "$rb_cv_dlopen" = yes], [
+AS_IF([test "$rb_cv_dlopen" = yes], [
AS_CASE(["$target_os"],
- [darwin*], [
+ [darwin*], [
+ AC_SUBST(ADDITIONAL_DLDFLAGS, "")
for flag in \
- "-undefined dynamic_lookup" \
"-multiply_defined suppress" \
+ "-undefined dynamic_lookup" \
; do
- test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`"
- RUBY_TRY_LDFLAGS([$flag], [], [flag=])
- AS_IF([test "x$flag" != x], [
- RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag])
- ])
+ test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`"
+ RUBY_TRY_LDFLAGS([$flag], [], [flag=])
+ AS_IF([test x"$flag" = x], [continue])
+
+ AC_MSG_CHECKING([whether $flag is accepted for bundle])
+ : > conftest.c
+ AS_IF([${LDSHARED%%'$(CC)'*}$CC${LDSHARED@%:@*'$(CC)'} -o conftest.bundle $flag conftest.c >/dev/null 2>conftest.err &&
+ test ! -s conftest.err], [
+ AC_MSG_RESULT([yes])
+ RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag])
+ ], [
+ AC_MSG_RESULT([no])
+ RUBY_APPEND_OPTIONS(ADDITIONAL_DLDFLAGS, [$flag])
+ ])
+ rm -fr conftest.*
done
- ])
- ])
-
- AS_IF([test "$enable_rpath:${RPATHFLAG}" = yes:], [
- AS_IF([test "x$rpathflag" != x], [
- RPATHFLAG=" ${rpathflag}%1\$-s"
- ])
- ])
-}
+ ])
+])
AS_IF([test "${LDSHAREDXX}" = ""], [
AS_CASE(["${LDSHARED}"],
@@ -3081,7 +3151,6 @@ AS_IF([test "${LDSHAREDXX}" = ""], [
[ld" "*], [
])
])
-AS_CASE([${RPATHFLAG}],[*'%1$'*],[: ${LIBPATHFLAG=' -L%1$-s'}],[: ${LIBPATHFLAG=' -L%s'}])
AC_SUBST(LINK_SO)
AC_SUBST(LIBPATHFLAG)
@@ -3090,23 +3159,6 @@ AC_SUBST(LIBPATHENV, "${LIBPATHENV-LD_LIBRARY_PATH}")
AC_SUBST(PRELOADENV, "${PRELOADENV-LD_PRELOAD}")
AC_SUBST(TRY_LINK)
-AS_IF([test "x$OPT_DIR" != x], [
- pat=`echo "${LDFLAGS_OPTDIR}" | sed ['s/[][\\.*|]/\\\\&/']`
- LDFLAGS=`echo "${LDFLAGS}" | sed "s| ${pat}||"`
- val=`IFS="$PATH_SEPARATOR"
- for dir in $OPT_DIR; do
- echo x ${LIBPATHFLAG} ${RPATHFLAG} |
- sed "s/^x *//;s${IFS}"'%1\\$-s'"${IFS}${dir}/lib${IFS}g;s${IFS}%s${IFS}${dir}/lib${IFS}g"
- done | tr '\012' ' ' | sed 's/ *$//'`
- AS_IF([test x"$val" != x], [
- test x"${LDFLAGS}" = x || LDFLAGS="$LDFLAGS "
- LDFLAGS="$LDFLAGS$val"
- test x"${DLDFLAGS}" = x || DLDFLAGS="$DLDFLAGS "
- DLDFLAGS="$DLDFLAGS$val"
- ])
- LDFLAGS_OPTDIR="$val"
-])
-
AS_CASE(["$target_os"],
[freebsd*], [
AC_CHECK_LIB([procstat], [procstat_open_sysctl])
@@ -3259,23 +3311,6 @@ AC_ARG_WITH(ext,
AC_ARG_WITH(out-ext,
AS_HELP_STRING([--with-out-ext=EXTS],
[pass to --without-ext option of extmk.rb]))
-EXTSTATIC=
-AC_SUBST(EXTSTATIC)dnl
-AC_ARG_WITH(static-linked-ext,
- AS_HELP_STRING([--with-static-linked-ext], [link external modules statically]),
- [AS_CASE([$withval],[yes],[STATIC=;EXTSTATIC=static],[no],[],[EXTSTATIC="$withval"])])
-AS_CASE([",$EXTSTATIC,"], [,static,|*,enc,*], [
- ENCOBJS='enc/encinit.$(OBJEXT) enc/libenc.$(LIBEXT) enc/libtrans.$(LIBEXT)'
- EXTOBJS='ext/extinit.$(OBJEXT)'
- AC_DEFINE_UNQUOTED(EXTSTATIC, 1)
- AC_SUBST(ENCSTATIC, static)
-], [
- ENCOBJS='dmyenc.$(OBJEXT)'
- EXTOBJS='dmyext.$(OBJEXT)'
-])
-AC_SUBST(ENCOBJS)
-AC_SUBST(EXTOBJS)
-
AC_ARG_WITH(setup,
AS_HELP_STRING([--with-setup=SETUP], [use extension libraries setup]),
[setup=$withval])
@@ -3325,7 +3360,6 @@ for var in bindir includedir libdir rubylibprefix; do
done
BTESTRUBY='$(MINIRUBY)'
-BOOTSTRAPRUBY='$(BASERUBY)'
AS_IF([test x"$cross_compiling" = xyes], [
test x"$MINIRUBY" = x && MINIRUBY="${RUBY-$BASERUBY} -I`$CHDIR .; pwd` "-r'$(arch)-fake'
XRUBY_LIBDIR=`${RUBY-$BASERUBY} -rrbconfig -e ['puts RbConfig::CONFIG["libdir"]']`
@@ -3335,6 +3369,10 @@ AS_IF([test x"$cross_compiling" = xyes], [
AC_SUBST(XRUBY_RUBYLIBDIR)
AC_SUBST(XRUBY_RUBYHDRDIR)
PREP='$(arch)-fake.rb'
+ AS_CASE(["$enable_shared:$EXTSTATIC:$target_os"], [no::darwin*], [
+ # darwin target requires miniruby for linking ext bundles
+ PREP="$PREP"' miniruby$(EXEEXT)'
+ ])
RUNRUBY_COMMAND='$(MINIRUBY) -I`cd $(srcdir)/lib; pwd`'
RUNRUBY='$(RUNRUBY_COMMAND)'
XRUBY='$(MINIRUBY)'
@@ -3348,7 +3386,6 @@ AS_IF([test x"$cross_compiling" = xyes], [
RUNRUBY_COMMAND='$(MINIRUBY) $(tooldir)/runruby.rb --extout=$(EXTOUT) $(RUNRUBYOPT)'
RUNRUBY='$(RUNRUBY_COMMAND) --'
XRUBY='$(RUNRUBY)'
- AS_CASE(["$HAVE_BASERUBY:$build_os"], [no:*|*:mingw*], [BOOTSTRAPRUBY='$(MINIRUBY)'])
TEST_RUNNABLE=yes
CROSS_COMPILING=no
])
@@ -3360,7 +3397,6 @@ AC_SUBST(PREP)
AC_SUBST(RUNRUBY_COMMAND)
AC_SUBST(RUNRUBY)
AC_SUBST(XRUBY)
-AC_SUBST(BOOTSTRAPRUBY)
AC_SUBST(EXTOUT, [${EXTOUT=.ext}])
FIRSTMAKEFILE=""
@@ -3599,7 +3635,7 @@ AS_CASE("$cross_compiling:${LIBPATHENV}", [yes:* | no:], [], [
AC_MSG_CHECKING(whether wrapper for $LIBPATHENV is needed)
AS_IF([env ${LIBPATHENV}=/lib /bin/sh -c ': ${'${LIBPATHENV}'?}' 2>/dev/null],
[AC_MSG_RESULT(no)],
- [PREP="$PREP"' exe/$(PROGRAM)'
+ [AC_SUBST(XRUBY_LIBPATHENV_WRAPPER, 'exe/$(PROGRAM)')
AC_MSG_RESULT(yes)]
)
])
@@ -3717,7 +3753,7 @@ AC_ARG_ENABLE(jit-support,
AS_HELP_STRING([--disable-jit-support], [disable JIT features]),
[MJIT_SUPPORT=$enableval],
[AS_CASE(["$target_os"],
- [wasi | mingw*], [MJIT_SUPPORT=no],
+ [wasi | mingw* | solaris*], [MJIT_SUPPORT=no],
[MJIT_SUPPORT=yes]
)])
@@ -3727,39 +3763,108 @@ AS_IF([test x"$MJIT_SUPPORT" = "xyes"],
AC_SUBST(MJIT_SUPPORT)
+AC_CHECK_PROG(RUSTC, [rustc], [rustc], [no]) dnl no ac_tool_prefix
+
+dnl check if rustc is recent enough to build YJIT (rustc >= 1.58.0)
+YJIT_RUSTC_OK=no
+AS_IF([test "$RUSTC" != "no"],
+ AC_MSG_CHECKING([whether ${RUSTC} works for YJIT])
+ YJIT_TARGET_ARCH=
+ AS_CASE(["$target_cpu"],
+ [arm64|aarch64], [YJIT_TARGET_ARCH=aarch64],
+ [x86_64], [YJIT_TARGET_ARCH=x86_64],
+ )
+ dnl Fails in case rustc target doesn't match ruby target.
+ dnl Can happen on Rosetta, for example.
+ AS_IF([echo "#[cfg(target_arch = \"$YJIT_TARGET_ARCH\")] fn main() { let x = 1; format!(\"{x}\"); }" |
+ $RUSTC - --emit asm=/dev/null 2>/dev/null],
+ [YJIT_RUSTC_OK=yes]
+ )
+ AC_MSG_RESULT($YJIT_RUSTC_OK)
+)
+
+dnl check if we can build YJIT on this target platform
+dnl we can't easily cross-compile with rustc so we don't support that
+YJIT_TARGET_OK=no
+AS_IF([test "$cross_compiling" = no],
+ AS_CASE(["$target_cpu-$target_os"],
+ [*android*], [
+ YJIT_TARGET_OK=no
+ ],
+ [arm64-darwin*|aarch64-darwin*|x86_64-darwin*], [
+ YJIT_TARGET_OK=yes
+ ],
+ [arm64-*linux*|aarch64-*linux*|x86_64-*linux*], [
+ YJIT_TARGET_OK=yes
+ ],
+ [arm64-*bsd*|aarch64-*bsd*|x86_64-*bsd*], [
+ YJIT_TARGET_OK=yes
+ ]
+ )
+)
+
+dnl build YJIT in release mode if rustc >= 1.58.0 is present and we are on a supported platform
AC_ARG_ENABLE(yjit,
- AS_HELP_STRING([--enable-yjit],
- [enable experimental in-process JIT compiler that requires Rust build tools [default=no]]),
- [YJIT_SUPPORT=$enableval], [YJIT_SUPPORT=no])
+ AS_HELP_STRING([--enable-yjit],
+ [enable in-process JIT compiler that requires Rust build tools. enabled by default on supported platforms if rustc 1.58.0+ is available]),
+ [YJIT_SUPPORT=$enableval],
+ [AS_CASE(["$enable_jit_support:$YJIT_TARGET_OK:$YJIT_RUSTC_OK"],
+ [yes:yes:yes|:yes:yes], [
+ YJIT_SUPPORT=yes
+ ],
+ [YJIT_SUPPORT=no]
+ )]
+)
CARGO=
CARGO_BUILD_ARGS=
YJIT_LIBS=
AS_CASE(["${YJIT_SUPPORT}"],
-[yes|dev], [
+[yes|dev|stats|dev_nodebug], [
AS_IF([test x"$enable_jit_support" = "xno"],
AC_MSG_ERROR([--disable-jit-support but --enable-yjit. YJIT requires JIT support])
)
- AC_CHECK_TOOL(RUSTC, [rustc], [no])
AS_IF([test x"$RUSTC" = "xno"],
AC_MSG_ERROR([rustc is required. Installation instructions available at https://www.rust-lang.org/tools/install])
)
- AS_IF([test x"$YJIT_SUPPORT" = "xyes"],
- [rb_rust_target_subdir=release
- CARGO_BUILD_ARGS='--release'],
- [rb_rust_target_subdir=debug
- CARGO_BUILD_ARGS='--features stats,disasm,asm_comments'
+
+ AS_CASE(["${YJIT_SUPPORT}"],
+ [yes], [
+ rb_rust_target_subdir=release
+ ],
+ [dev], [
+ rb_rust_target_subdir=debug
+ CARGO_BUILD_ARGS='--features stats,disasm'
+ AC_DEFINE(RUBY_DEBUG, 1)
+ ],
+ [dev_nodebug], [
+ rb_rust_target_subdir=dev_nodebug
+ CARGO_BUILD_ARGS='--profile dev_nodebug --features stats,disasm'
+ ],
+ [stats], [
+ rb_rust_target_subdir=stats
+ CARGO_BUILD_ARGS='--profile stats --features stats'
+ AC_DEFINE(YJIT_STATS, 1)
+ ])
+
+ AS_IF([test -n "${CARGO_BUILD_ARGS}"], [
AC_CHECK_TOOL(CARGO, [cargo], [no])
AS_IF([test x"$CARGO" = "xno"],
AC_MSG_ERROR([cargo is required. Installation instructions available at https://www.rust-lang.org/tools/install])
- )
- AC_DEFINE(RUBY_DEBUG, 1)])
+ ]))
+
YJIT_LIBS="yjit/target/${rb_rust_target_subdir}/libyjit.a"
+ AS_CASE(["$target_os"],[openbsd*],[
+ # Link libc++abi (which requires libpthread) for _Unwind_* functions needed by yjit
+ LDFLAGS="$LDFLAGS -lpthread -lc++abi"
+ ])
YJIT_OBJ='yjit.$(OBJEXT)'
+ AS_IF([test x"$YJIT_SUPPORT" != "xyes" ], [
+ AC_DEFINE_UNQUOTED(YJIT_SUPPORT, [$YJIT_SUPPORT])
+ ])
AC_DEFINE(USE_YJIT, 1)
], [AC_DEFINE(USE_YJIT, 0)])
-
dnl These variables end up in ::RbConfig::CONFIG
AC_SUBST(YJIT_SUPPORT)dnl what flavor of YJIT the Ruby build includes
AC_SUBST(RUSTC)dnl Rust compiler command
@@ -4092,6 +4197,7 @@ AS_CASE(["$ruby_version"],
AS_IF([test ${RUBY_LIB_VERSION_STYLE+set}], [
{
echo "#define RUBY_LIB_VERSION_STYLE $RUBY_LIB_VERSION_STYLE"
+ echo '@%:@include "confdefs.h"'
echo '#define STRINGIZE(x) x'
test -f revision.h -o -f "${srcdir}/revision.h" || echo '#define RUBY_REVISION 0'
echo '#include "version.h"'
@@ -4330,6 +4436,14 @@ AC_SUBST(XCC_WRAPPER)
AS_CASE([" $CPP "], [*" $CC "*], [CPP=`echo " $CPP " | sed "s| $CC |"' $(CC) |;s/^ *//;s/ *$//'`])
+AS_IF([test ! -f "$srcdir/revision.h"], [
+ AS_IF([test "x$HAVE_BASERUBY" = xyes], [
+ ${BASERUBY} -C "$srcdir" tool/file2lastrev.rb -q --revision.h > "$srcdir/revision.h"
+ ], [
+ touch "$srcdir/revision.h"
+ ])
+])
+
AS_IF([test x"$firstmf" != x], [
AC_CONFIG_FILES($firstmf:$firsttmpl, [], [firstmf="$firstmf" firsttmpl="$firsttmpl"])
])
@@ -4346,17 +4460,24 @@ AC_CONFIG_FILES(Makefile:template/Makefile.in, [
AS_CASE("$VCS",
['$(GIT)'|git], [VCSUP='$(VCS) pull --rebase $(GITPULLOPTIONS)'],
[VCSUP='$(VCS)'])
- sed -n \
- -e '[/^@%:@define \(RUBY_RELEASE_[A-Z]*\) \([0-9][0-9]*\)/]{' \
- -e 's//\1 = \2/' \
- -e '[s/ \([0-9]\)$/ 0\1/]' \
- -e p \
- -e '}' "$srcdir/version.h"
+ for f in "$srcdir/version.h" "$srcdir/revision.h"; do
+ test -f "$f" || continue
+ sed -n \
+ -e '[/^@%:@define \(RUBY_RELEASE_[A-Z]*\) \([0-9][0-9]*\)/]{' \
+ -e 's//\1 = \2/' \
+ -e '[s/ \([0-9]\)$/ 0\1/]' \
+ -e p \
+ -e '}' "$f"
+ done
sed '/^MISSING/s/\$U\././g;/^VCS *=/s#@VCS@#'"$VCS"'#;/^VCSUP *=/s#@VCSUP@#'"$VCSUP"'#' Makefile
echo; test x"$EXEEXT" = x || echo 'miniruby: miniruby$(EXEEXT)'
AS_IF([test "$gnumake" != yes], [
echo ['$(MKFILES): $(srcdir)/common.mk']
sed ['s/{\$([^(){}]*)[^{}]*}//g'] ${srcdir}/common.mk
+ AS_IF([test "$YJIT_SUPPORT" = yes], [
+ cat ${srcdir}/yjit/not_gmake.mk
+ echo ['$(MKFILES): ${srcdir}/yjit/not_gmake.mk']
+ ])
], [
echo 'distclean-local::; @$(RM) GNUmakefile uncommon.mk'
])
@@ -4374,7 +4495,7 @@ AC_CONFIG_FILES(Makefile:template/Makefile.in, [
echo 'ruby: $(PROGRAM);' >> $tmpmk
test "$tmpmk" = "$tmpgmk" || rm -f "$tmpgmk"
]) && mv -f $tmpmk Makefile],
-[EXEEXT='$EXEEXT' MAKE='${MAKE-make}' gnumake='$gnumake' GIT='$GIT'])
+[EXEEXT='$EXEEXT' MAKE='${MAKE-make}' gnumake='$gnumake' GIT='$GIT' YJIT_SUPPORT='$YJIT_SUPPORT'])
AC_ARG_WITH([ruby-pc],
AS_HELP_STRING([--with-ruby-pc=FILENAME], [pc file basename]),
diff --git a/cont.c b/cont.c
index 590d6c250f..5375d1945b 100644
--- a/cont.c
+++ b/cont.c
@@ -29,11 +29,15 @@ extern int madvise(caddr_t, size_t, int);
#include "gc.h"
#include "internal.h"
#include "internal/cont.h"
+#include "internal/error.h"
#include "internal/proc.h"
+#include "internal/sanitizers.h"
#include "internal/warnings.h"
#include "ruby/fiber/scheduler.h"
#include "mjit.h"
+#include "yjit.h"
#include "vm_core.h"
+#include "vm_sync.h"
#include "id_table.h"
#include "ractor_core.h"
@@ -66,6 +70,8 @@ static VALUE rb_cFiberPool;
#define FIBER_POOL_ALLOCATION_FREE
#endif
+#define jit_cont_enabled (mjit_enabled || rb_yjit_enabled_p())
+
enum context_type {
CONTINUATION_CONTEXT = 0,
FIBER_CONTEXT = 1
@@ -194,6 +200,15 @@ struct fiber_pool {
size_t vm_stack_size;
};
+// Continuation contexts used by JITs
+struct rb_jit_cont {
+ rb_execution_context_t *ec; // continuation ec
+ struct rb_jit_cont *prev, *next; // used to form lists
+};
+
+// Doubly linked list for enumerating all on-stack ISEQs.
+static struct rb_jit_cont *first_jit_cont;
+
typedef struct rb_context_struct {
enum context_type type;
int argc;
@@ -211,8 +226,7 @@ typedef struct rb_context_struct {
rb_execution_context_t saved_ec;
rb_jmpbuf_t jmpbuf;
rb_ensure_entry_t *ensure_array;
- /* Pointer to MJIT info about the continuation. */
- struct mjit_cont *mjit_cont;
+ struct rb_jit_cont *jit_cont; // Continuation contexts for JITs
} rb_context_t;
@@ -258,7 +272,7 @@ struct rb_fiber_struct {
static struct fiber_pool shared_fiber_pool = {NULL, NULL, 0, 0, 0, 0};
-static ID fiber_initialize_keywords[2] = {0};
+static ID fiber_initialize_keywords[3] = {0};
/*
* FreeBSD require a first (i.e. addr) argument of mmap(2) is not NULL
@@ -680,21 +694,36 @@ fiber_pool_stack_free(struct fiber_pool_stack * stack)
if (DEBUG) fprintf(stderr, "fiber_pool_stack_free: %p+%"PRIuSIZE" [base=%p, size=%"PRIuSIZE"]\n", base, size, stack->base, stack->size);
-#if VM_CHECK_MODE > 0 && defined(MADV_DONTNEED)
+ // The pages being used by the stack can be returned back to the system.
+ // That doesn't change the page mapping, but it does allow the system to
+ // reclaim the physical memory.
+ // Since we no longer care about the data itself, we don't need to page
+ // out to disk, since that is costly. Not all systems support that, so
+ // we try our best to select the most efficient implementation.
+ // In addition, it's actually slightly desirable to not do anything here,
+ // but that results in higher memory usage.
+
+#ifdef __wasi__
+ // WebAssembly doesn't support madvise, so we just don't do anything.
+#elif VM_CHECK_MODE > 0 && defined(MADV_DONTNEED)
// This immediately discards the pages and the memory is reset to zero.
madvise(base, size, MADV_DONTNEED);
-#elif defined(POSIX_MADV_DONTNEED)
- posix_madvise(base, size, POSIX_MADV_DONTNEED);
#elif defined(MADV_FREE_REUSABLE)
+ // Darwin / macOS / iOS.
// Acknowledge the kernel down to the task info api we make this
// page reusable for future use.
// As for MADV_FREE_REUSE below we ensure in the rare occasions the task was not
// completed at the time of the call to re-iterate.
while (madvise(base, size, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN);
#elif defined(MADV_FREE)
+ // Recent Linux.
madvise(base, size, MADV_FREE);
#elif defined(MADV_DONTNEED)
+ // Old Linux.
madvise(base, size, MADV_DONTNEED);
+#elif defined(POSIX_MADV_DONTNEED)
+ // Solaris?
+ posix_madvise(base, size, POSIX_MADV_DONTNEED);
#elif defined(_WIN32)
VirtualAlloc(base, size, MEM_RESET, PAGE_READWRITE);
// Not available in all versions of Windows.
@@ -999,6 +1028,8 @@ fiber_is_root_p(const rb_fiber_t *fiber)
}
#endif
+static void jit_cont_free(struct rb_jit_cont *cont);
+
static void
cont_free(void *ptr)
{
@@ -1019,9 +1050,9 @@ cont_free(void *ptr)
RUBY_FREE_UNLESS_NULL(cont->saved_vm_stack.ptr);
- if (mjit_enabled) {
- VM_ASSERT(cont->mjit_cont != NULL);
- mjit_cont_free(cont->mjit_cont);
+ if (jit_cont_enabled) {
+ VM_ASSERT(cont->jit_cont != NULL);
+ jit_cont_free(cont->jit_cont);
}
/* free rb_cont_t or rb_fiber_t */
ruby_xfree(ptr);
@@ -1126,7 +1157,9 @@ fiber_memsize(const void *ptr)
*/
if (saved_ec->local_storage && fiber != th->root_fiber) {
size += rb_id_table_memsize(saved_ec->local_storage);
+ size += rb_obj_memsize_of(saved_ec->storage);
}
+
size += cont_memsize(&fiber->cont);
return size;
}
@@ -1161,6 +1194,7 @@ cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont)
}
FLUSH_REGISTER_WINDOWS;
+ asan_unpoison_memory_region(cont->machine.stack_src, size, false);
MEMCPY(cont->machine.stack, cont->machine.stack_src, VALUE, size);
}
@@ -1185,12 +1219,103 @@ cont_save_thread(rb_context_t *cont, rb_thread_t *th)
sec->machine.stack_end = NULL;
}
+static rb_nativethread_lock_t jit_cont_lock;
+
+// Register a new continuation with execution context `ec`. Return JIT info about
+// the continuation.
+static struct rb_jit_cont *
+jit_cont_new(rb_execution_context_t *ec)
+{
+ struct rb_jit_cont *cont;
+
+ // We need to use calloc instead of something like ZALLOC to avoid triggering GC here.
+ // When this function is called from rb_thread_alloc through rb_threadptr_root_fiber_setup,
+ // the thread is still being prepared and marking it causes SEGV.
+ cont = calloc(1, sizeof(struct rb_jit_cont));
+ if (cont == NULL)
+ rb_memerror();
+ cont->ec = ec;
+
+ rb_native_mutex_lock(&jit_cont_lock);
+ if (first_jit_cont == NULL) {
+ cont->next = cont->prev = NULL;
+ }
+ else {
+ cont->prev = NULL;
+ cont->next = first_jit_cont;
+ first_jit_cont->prev = cont;
+ }
+ first_jit_cont = cont;
+ rb_native_mutex_unlock(&jit_cont_lock);
+
+ return cont;
+}
+
+// Unregister continuation `cont`.
+static void
+jit_cont_free(struct rb_jit_cont *cont)
+{
+ if (!cont) return;
+
+ rb_native_mutex_lock(&jit_cont_lock);
+ if (cont == first_jit_cont) {
+ first_jit_cont = cont->next;
+ if (first_jit_cont != NULL)
+ first_jit_cont->prev = NULL;
+ }
+ else {
+ cont->prev->next = cont->next;
+ if (cont->next != NULL)
+ cont->next->prev = cont->prev;
+ }
+ rb_native_mutex_unlock(&jit_cont_lock);
+
+ free(cont);
+}
+
+// Call a given callback against all on-stack ISEQs.
+void
+rb_jit_cont_each_iseq(rb_iseq_callback callback, void *data)
+{
+ struct rb_jit_cont *cont;
+ for (cont = first_jit_cont; cont != NULL; cont = cont->next) {
+ if (cont->ec->vm_stack == NULL)
+ continue;
+
+ const rb_control_frame_t *cfp;
+ for (cfp = RUBY_VM_END_CONTROL_FRAME(cont->ec) - 1; ; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
+ const rb_iseq_t *iseq;
+ if (cfp->pc && (iseq = cfp->iseq) != NULL && imemo_type((VALUE)iseq) == imemo_iseq) {
+ callback(iseq, data);
+ }
+
+ if (cfp == cont->ec->cfp)
+ break; // reached the most recent cfp
+ }
+ }
+}
+
+// Finish working with jit_cont.
+void
+rb_jit_cont_finish(void)
+{
+ if (!jit_cont_enabled)
+ return;
+
+ struct rb_jit_cont *cont, *next;
+ for (cont = first_jit_cont; cont != NULL; cont = next) {
+ next = cont->next;
+ free(cont); // Don't use xfree because it's allocated by calloc.
+ }
+ rb_native_mutex_destroy(&jit_cont_lock);
+}
+
static void
-cont_init_mjit_cont(rb_context_t *cont)
+cont_init_jit_cont(rb_context_t *cont)
{
- VM_ASSERT(cont->mjit_cont == NULL);
- if (mjit_enabled) {
- cont->mjit_cont = mjit_cont_new(&(cont->saved_ec));
+ VM_ASSERT(cont->jit_cont == NULL);
+ if (jit_cont_enabled) {
+ cont->jit_cont = jit_cont_new(&(cont->saved_ec));
}
}
@@ -1209,7 +1334,7 @@ cont_init(rb_context_t *cont, rb_thread_t *th)
cont->saved_ec.local_storage = NULL;
cont->saved_ec.local_storage_recursive_hash = Qnil;
cont->saved_ec.local_storage_recursive_hash_for_trace = Qnil;
- cont_init_mjit_cont(cont);
+ cont_init_jit_cont(cont);
}
static rb_context_t *
@@ -1238,11 +1363,15 @@ rb_fiberptr_blocking(struct rb_fiber_struct *fiber)
return fiber->blocking;
}
-// This is used for root_fiber because other fibers call cont_init_mjit_cont through cont_new.
+// Start working with jit_cont.
void
-rb_fiber_init_mjit_cont(struct rb_fiber_struct *fiber)
+rb_jit_cont_init(void)
{
- cont_init_mjit_cont(&fiber->cont);
+ if (!jit_cont_enabled)
+ return;
+
+ rb_native_mutex_initialize(&jit_cont_lock);
+ cont_init_jit_cont(&GET_EC()->fiber_ptr->cont);
}
#if 0
@@ -1316,7 +1445,7 @@ cont_capture(volatile int *volatile stat)
entry = cont->ensure_array = ALLOC_N(rb_ensure_entry_t,size+1);
for (p=th->ec->ensure_list; p; p=p->next) {
if (!p->entry.marker)
- p->entry.marker = rb_ary_tmp_new(0); /* dummy object */
+ p->entry.marker = rb_ary_hidden_new(0); /* dummy object */
*entry++ = p->entry;
}
entry->marker = 0;
@@ -1452,7 +1581,7 @@ cont_restore_1(rb_context_t *cont)
cont_restore_thread(cont);
/* restore machine stack */
-#ifdef _M_AMD64
+#if defined(_M_AMD64) && !defined(__MINGW64__)
{
/* workaround for x64 SEH */
jmp_buf buf;
@@ -1707,7 +1836,7 @@ rollback_ensure_stack(VALUE self,rb_ensure_list_t *current,rb_ensure_entry_t *ta
/* push ensure stack */
for (j = 0; j < i; j++) {
func = lookup_rollback_func(target[i - j - 1].e_proc);
- if ((VALUE)func != Qundef) {
+ if (!UNDEF_P((VALUE)func)) {
(*func)(target[i - j - 1].data2);
}
}
@@ -1832,7 +1961,7 @@ rb_cont_call(int argc, VALUE *argv, VALUE contval)
* the current thread, blocking and non-blocking fibers' behavior is identical.
*
* Ruby doesn't provide a scheduler class: it is expected to be implemented by
- * the user and correspond to Fiber::SchedulerInterface.
+ * the user and correspond to Fiber::Scheduler.
*
* There is also Fiber.schedule method, which is expected to immediately perform
* the given block in a non-blocking manner. Its actual implementation is up to
@@ -1883,11 +2012,207 @@ fiber_t_alloc(VALUE fiber_value, unsigned int blocking)
return fiber;
}
+static rb_fiber_t *
+root_fiber_alloc(rb_thread_t *th)
+{
+ VALUE fiber_value = fiber_alloc(rb_cFiber);
+ rb_fiber_t *fiber = th->ec->fiber_ptr;
+
+ VM_ASSERT(DATA_PTR(fiber_value) == NULL);
+ VM_ASSERT(fiber->cont.type == FIBER_CONTEXT);
+ VM_ASSERT(FIBER_RESUMED_P(fiber));
+
+ th->root_fiber = fiber;
+ DATA_PTR(fiber_value) = fiber;
+ fiber->cont.self = fiber_value;
+
+ coroutine_initialize_main(&fiber->context);
+
+ return fiber;
+}
+
+static inline rb_fiber_t*
+fiber_current(void)
+{
+ rb_execution_context_t *ec = GET_EC();
+ if (ec->fiber_ptr->cont.self == 0) {
+ root_fiber_alloc(rb_ec_thread_ptr(ec));
+ }
+ return ec->fiber_ptr;
+}
+
+static inline VALUE
+current_fiber_storage(void)
+{
+ rb_execution_context_t *ec = GET_EC();
+ return ec->storage;
+}
+
+static inline VALUE
+inherit_fiber_storage(void)
+{
+ return rb_obj_dup(current_fiber_storage());
+}
+
+static inline void
+fiber_storage_set(struct rb_fiber_struct *fiber, VALUE storage)
+{
+ fiber->cont.saved_ec.storage = storage;
+}
+
+static inline VALUE
+fiber_storage_get(rb_fiber_t *fiber)
+{
+ VALUE storage = fiber->cont.saved_ec.storage;
+ if (storage == Qnil) {
+ storage = rb_hash_new();
+ fiber_storage_set(fiber, storage);
+ }
+ return storage;
+}
+
+static void
+storage_access_must_be_from_same_fiber(VALUE self)
+{
+ rb_fiber_t *fiber = fiber_ptr(self);
+ rb_fiber_t *current = fiber_current();
+ if (fiber != current) {
+ rb_raise(rb_eArgError, "Fiber storage can only be accessed from the Fiber it belongs to");
+ }
+}
+
+/**
+ * call-seq: fiber.storage -> hash (dup)
+ *
+ * Returns a copy of the storage hash for the fiber. The method can only be called on the
+ * Fiber.current.
+ */
+static VALUE
+rb_fiber_storage_get(VALUE self)
+{
+ storage_access_must_be_from_same_fiber(self);
+ return rb_obj_dup(fiber_storage_get(fiber_ptr(self)));
+}
+
+static int
+fiber_storage_validate_each(VALUE key, VALUE value, VALUE _argument)
+{
+ Check_Type(key, T_SYMBOL);
+
+ return ST_CONTINUE;
+}
+
+static void
+fiber_storage_validate(VALUE value)
+{
+ // nil is an allowed value and will be lazily initialized.
+ if (value == Qnil) return;
+
+ if (!RB_TYPE_P(value, T_HASH)) {
+ rb_raise(rb_eTypeError, "storage must be a hash");
+ }
+
+ if (RB_OBJ_FROZEN(value)) {
+ rb_raise(rb_eFrozenError, "storage must not be frozen");
+ }
+
+ rb_hash_foreach(value, fiber_storage_validate_each, Qundef);
+}
+
+/**
+ * call-seq: fiber.storage = hash
+ *
+ * Sets the storage hash for the fiber. This feature is experimental
+ * and may change in the future. The method can only be called on the
+ * Fiber.current.
+ *
+ * You should be careful about using this method as you may inadvertently clear
+ * important fiber-storage state. You should mostly prefer to assign specific
+ * keys in the storage using Fiber::[]=.
+ *
+ * You can also use <tt>Fiber.new(storage: nil)</tt> to create a fiber with an empty
+ * storage.
+ *
+ * Example:
+ *
+ * while request = request_queue.pop
+ * # Reset the per-request state:
+ * Fiber.current.storage = nil
+ * handle_request(request)
+ * end
+ */
static VALUE
-fiber_initialize(VALUE self, VALUE proc, struct fiber_pool * fiber_pool, unsigned int blocking)
+rb_fiber_storage_set(VALUE self, VALUE value)
{
+ if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
+ rb_category_warn(RB_WARN_CATEGORY_EXPERIMENTAL,
+ "Fiber#storage= is experimental and may be removed in the future!");
+ }
+
+ storage_access_must_be_from_same_fiber(self);
+ fiber_storage_validate(value);
+
+ fiber_ptr(self)->cont.saved_ec.storage = rb_obj_dup(value);
+ return value;
+}
+
+/**
+ * call-seq: Fiber[key] -> value
+ *
+ * Returns the value of the fiber storage variable identified by +key+.
+ *
+ * The +key+ must be a symbol, and the value is set by Fiber#[]= or
+ * Fiber#store.
+ *
+ * See also Fiber::[]=.
+ */
+static VALUE
+rb_fiber_storage_aref(VALUE class, VALUE key)
+{
+ Check_Type(key, T_SYMBOL);
+
+ VALUE storage = fiber_storage_get(fiber_current());
+
+ if (storage == Qnil) return Qnil;
+
+ return rb_hash_aref(storage, key);
+}
+
+/**
+ * call-seq: Fiber[key] = value
+ *
+ * Assign +value+ to the fiber storage variable identified by +key+.
+ * The variable is created if it doesn't exist.
+ *
+ * +key+ must be a Symbol, otherwise a TypeError is raised.
+ *
+ * See also Fiber::[].
+ */
+static VALUE
+rb_fiber_storage_aset(VALUE class, VALUE key, VALUE value)
+{
+ Check_Type(key, T_SYMBOL);
+
+ VALUE storage = fiber_storage_get(fiber_current());
+
+ return rb_hash_aset(storage, key, value);
+}
+
+static VALUE
+fiber_initialize(VALUE self, VALUE proc, struct fiber_pool * fiber_pool, unsigned int blocking, VALUE storage)
+{
+ if (storage == Qundef || storage == Qtrue) {
+ // The default, inherit storage (dup) from the current fiber:
+ storage = inherit_fiber_storage();
+ }
+ else /* nil, hash, etc. */ {
+ fiber_storage_validate(storage);
+ storage = rb_obj_dup(storage);
+ }
+
rb_fiber_t *fiber = fiber_t_alloc(self, blocking);
+ fiber->cont.saved_ec.storage = storage;
fiber->first_proc = proc;
fiber->stack.base = NULL;
fiber->stack.pool = fiber_pool;
@@ -1920,54 +2245,90 @@ rb_fiber_pool_default(VALUE pool)
return &shared_fiber_pool;
}
+VALUE rb_fiber_inherit_storage(struct rb_execution_context_struct *ec, struct rb_fiber_struct *fiber)
+{
+ VALUE storage = rb_obj_dup(ec->storage);
+ fiber->cont.saved_ec.storage = storage;
+ return storage;
+}
+
/* :nodoc: */
static VALUE
rb_fiber_initialize_kw(int argc, VALUE* argv, VALUE self, int kw_splat)
{
VALUE pool = Qnil;
VALUE blocking = Qfalse;
+ VALUE storage = Qundef;
if (kw_splat != RB_NO_KEYWORDS) {
VALUE options = Qnil;
- VALUE arguments[2] = {Qundef};
+ VALUE arguments[3] = {Qundef};
argc = rb_scan_args_kw(kw_splat, argc, argv, ":", &options);
- rb_get_kwargs(options, fiber_initialize_keywords, 0, 2, arguments);
+ rb_get_kwargs(options, fiber_initialize_keywords, 0, 3, arguments);
- if (arguments[0] != Qundef) {
+ if (!UNDEF_P(arguments[0])) {
blocking = arguments[0];
}
- if (arguments[1] != Qundef) {
+ if (!UNDEF_P(arguments[1])) {
pool = arguments[1];
}
+
+ storage = arguments[2];
}
- return fiber_initialize(self, rb_block_proc(), rb_fiber_pool_default(pool), RTEST(blocking));
+ return fiber_initialize(self, rb_block_proc(), rb_fiber_pool_default(pool), RTEST(blocking), storage);
}
/*
* call-seq:
- * Fiber.new(blocking: false) { |*args| ... } -> fiber
+ * Fiber.new(blocking: false, storage: true) { |*args| ... } -> fiber
*
- * Creates new Fiber. Initially, the fiber is not running and can be resumed with
- * #resume. Arguments to the first #resume call will be passed to the block:
+ * Creates new Fiber. Initially, the fiber is not running and can be resumed
+ * with #resume. Arguments to the first #resume call will be passed to the
+ * block:
*
- * f = Fiber.new do |initial|
- * current = initial
- * loop do
- * puts "current: #{current.inspect}"
- * current = Fiber.yield
- * end
- * end
- * f.resume(100) # prints: current: 100
- * f.resume(1, 2, 3) # prints: current: [1, 2, 3]
- * f.resume # prints: current: nil
- * # ... and so on ...
- *
- * If <tt>blocking: false</tt> is passed to <tt>Fiber.new</tt>, _and_ current thread
- * has a Fiber.scheduler defined, the Fiber becomes non-blocking (see "Non-blocking
- * Fibers" section in class docs).
+ * f = Fiber.new do |initial|
+ * current = initial
+ * loop do
+ * puts "current: #{current.inspect}"
+ * current = Fiber.yield
+ * end
+ * end
+ * f.resume(100) # prints: current: 100
+ * f.resume(1, 2, 3) # prints: current: [1, 2, 3]
+ * f.resume # prints: current: nil
+ * # ... and so on ...
+ *
+ * If <tt>blocking: false</tt> is passed to <tt>Fiber.new</tt>, _and_ current
+ * thread has a Fiber.scheduler defined, the Fiber becomes non-blocking (see
+ * "Non-blocking Fibers" section in class docs).
+ *
+ * If the <tt>storage</tt> is unspecified, the default is to inherit a copy of
+ * the storage from the current fiber. This is the same as specifying
+ * <tt>storage: true</tt>.
+ *
+ * Fiber[:x] = 1
+ * Fiber.new do
+ * Fiber[:x] # => 1
+ * Fiber[:x] = 2
+ * end.resume
+ * Fiber[:x] # => 1
+ *
+ * If the given <tt>storage</tt> is <tt>nil</tt>, this function will lazy
+ * initialize the internal storage, which starts as an empty hash.
+ *
+ * Fiber[:x] = "Hello World"
+ * Fiber.new(storage: nil) do
+ * Fiber[:x] # nil
+ * end
+ *
+ * Otherwise, the given <tt>storage</tt> is used as the new fiber's storage,
+ * and it must be an instance of Hash.
+ *
+ * Explicitly using <tt>storage: true</tt> is currently experimental and may
+ * change in the future.
*/
static VALUE
rb_fiber_initialize(int argc, VALUE* argv, VALUE self)
@@ -1976,9 +2337,15 @@ rb_fiber_initialize(int argc, VALUE* argv, VALUE self)
}
VALUE
+rb_fiber_new_storage(rb_block_call_func_t func, VALUE obj, VALUE storage)
+{
+ return fiber_initialize(fiber_alloc(rb_cFiber), rb_proc_new(func, obj), rb_fiber_pool_default(Qnil), 0, storage);
+}
+
+VALUE
rb_fiber_new(rb_block_call_func_t func, VALUE obj)
{
- return fiber_initialize(fiber_alloc(rb_cFiber), rb_proc_new(func, obj), rb_fiber_pool_default(Qnil), 1);
+ return rb_fiber_new_storage(func, obj, Qtrue);
}
static VALUE
@@ -1989,7 +2356,7 @@ rb_fiber_s_schedule_kw(int argc, VALUE* argv, int kw_splat)
VALUE fiber = Qnil;
if (scheduler != Qnil) {
- fiber = rb_funcall_passing_block_kw(scheduler, rb_intern("fiber"), argc, argv, kw_splat);
+ fiber = rb_fiber_scheduler_fiber(scheduler, argc, argv, kw_splat);
}
else {
rb_raise(rb_eRuntimeError, "No scheduler is available!");
@@ -2032,7 +2399,7 @@ rb_fiber_s_schedule_kw(int argc, VALUE* argv, int kw_splat)
*
* Note that the behavior described above is how the method is <em>expected</em>
* to behave, actual behavior is up to the current scheduler's implementation of
- * Fiber::SchedulerInterface#fiber method. Ruby doesn't enforce this method to
+ * Fiber::Scheduler#fiber method. Ruby doesn't enforce this method to
* behave in any particular way.
*
* If the scheduler is not set, the method raises
@@ -2051,7 +2418,7 @@ rb_fiber_s_schedule(int argc, VALUE *argv, VALUE obj)
*
* Returns the Fiber scheduler, that was last set for the current thread with Fiber.set_scheduler.
* Returns +nil+ if no scheduler is set (which is the default), and non-blocking fibers'
- # behavior is the same as blocking.
+ * behavior is the same as blocking.
* (see "Non-blocking fibers" section in class docs for details about the scheduler concept).
*
*/
@@ -2085,7 +2452,7 @@ rb_fiber_current_scheduler(VALUE klass)
* thread will call scheduler's +close+ method on finalization (allowing the scheduler to
* properly manage all non-finished fibers).
*
- * +scheduler+ can be an object of any class corresponding to Fiber::SchedulerInterface. Its
+ * +scheduler+ can be an object of any class corresponding to Fiber::Scheduler. Its
* implementation is up to the user.
*
* See also the "Non-blocking fibers" section in class docs.
@@ -2152,25 +2519,7 @@ rb_fiber_start(rb_fiber_t *fiber)
rb_fiber_terminate(fiber, need_interrupt, err);
}
-static rb_fiber_t *
-root_fiber_alloc(rb_thread_t *th)
-{
- VALUE fiber_value = fiber_alloc(rb_cFiber);
- rb_fiber_t *fiber = th->ec->fiber_ptr;
-
- VM_ASSERT(DATA_PTR(fiber_value) == NULL);
- VM_ASSERT(fiber->cont.type == FIBER_CONTEXT);
- VM_ASSERT(fiber->status == FIBER_RESUMED);
-
- th->root_fiber = fiber;
- DATA_PTR(fiber_value) = fiber;
- fiber->cont.self = fiber_value;
-
- coroutine_initialize_main(&fiber->context);
-
- return fiber;
-}
-
+// Set up a "root fiber", which is the fiber that every Ractor has.
void
rb_threadptr_root_fiber_setup(rb_thread_t *th)
{
@@ -2185,9 +2534,11 @@ rb_threadptr_root_fiber_setup(rb_thread_t *th)
fiber->blocking = 1;
fiber_status_set(fiber, FIBER_RESUMED); /* skip CREATED */
th->ec = &fiber->cont.saved_ec;
- // This skips mjit_cont_new for the initial thread because mjit_enabled is always false
- // at this point. mjit_init calls rb_fiber_init_mjit_cont again for this root_fiber.
- rb_fiber_init_mjit_cont(fiber);
+ // When rb_threadptr_root_fiber_setup is called for the first time, mjit_enabled and
+ // rb_yjit_enabled_p() are still false. So this does nothing and rb_jit_cont_init() that is
+ // called later will take care of it. However, you still have to call cont_init_jit_cont()
+ // here for other Ractors, which are not initialized by rb_jit_cont_init().
+ cont_init_jit_cont(&fiber->cont);
}
void
@@ -2222,16 +2573,6 @@ rb_threadptr_root_fiber_terminate(rb_thread_t *th)
}
static inline rb_fiber_t*
-fiber_current(void)
-{
- rb_execution_context_t *ec = GET_EC();
- if (ec->fiber_ptr->cont.self == 0) {
- root_fiber_alloc(rb_ec_thread_ptr(ec));
- }
- return ec->fiber_ptr;
-}
-
-static inline rb_fiber_t*
return_fiber(bool terminate)
{
rb_fiber_t *fiber = fiber_current();
@@ -2408,7 +2749,60 @@ rb_fiber_transfer(VALUE fiber_value, int argc, const VALUE *argv)
VALUE
rb_fiber_blocking_p(VALUE fiber)
{
- return RBOOL(fiber_ptr(fiber)->blocking != 0);
+ return RBOOL(fiber_ptr(fiber)->blocking);
+}
+
+static VALUE
+fiber_blocking_yield(VALUE fiber_value)
+{
+ rb_fiber_t *fiber = fiber_ptr(fiber_value);
+ rb_thread_t * volatile th = fiber->cont.saved_ec.thread_ptr;
+
+ // fiber->blocking is `unsigned int : 1`, so we use it as a boolean:
+ fiber->blocking = 1;
+
+ // Once the fiber is blocking, and current, we increment the thread blocking state:
+ th->blocking += 1;
+
+ return rb_yield(fiber_value);
+}
+
+static VALUE
+fiber_blocking_ensure(VALUE fiber_value)
+{
+ rb_fiber_t *fiber = fiber_ptr(fiber_value);
+ rb_thread_t * volatile th = fiber->cont.saved_ec.thread_ptr;
+
+ // We are no longer blocking:
+ fiber->blocking = 0;
+ th->blocking -= 1;
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * Fiber.blocking{|fiber| ...} -> result
+ *
+ * Forces the fiber to be blocking for the duration of the block. Returns the
+ * result of the block.
+ *
+ * See the "Non-blocking fibers" section in class docs for details.
+ *
+ */
+VALUE
+rb_fiber_blocking(VALUE class)
+{
+ VALUE fiber_value = rb_fiber_current();
+ rb_fiber_t *fiber = fiber_ptr(fiber_value);
+
+ // If we are already blocking, this is essentially a no-op:
+ if (fiber->blocking) {
+ return rb_yield(fiber_value);
+ }
+ else {
+ return rb_ensure(fiber_blocking_yield, fiber_value, fiber_blocking_ensure, fiber_value);
+ }
}
/*
@@ -2946,329 +3340,6 @@ rb_fiber_pool_initialize(int argc, VALUE* argv, VALUE self)
* fiber.resume #=> FiberError: dead fiber called
*/
-/*
- * Document-class: Fiber::SchedulerInterface
- *
- * This is not an existing class, but documentation of the interface that Scheduler
- * object should comply to in order to be used as argument to Fiber.scheduler and handle non-blocking
- * fibers. See also the "Non-blocking fibers" section in Fiber class docs for explanations
- * of some concepts.
- *
- * Scheduler's behavior and usage are expected to be as follows:
- *
- * * When the execution in the non-blocking Fiber reaches some blocking operation (like
- * sleep, wait for a process, or a non-ready I/O), it calls some of the scheduler's
- * hook methods, listed below.
- * * Scheduler somehow registers what the current fiber is waiting on, and yields control
- * to other fibers with Fiber.yield (so the fiber would be suspended while expecting its
- * wait to end, and other fibers in the same thread can perform)
- * * At the end of the current thread execution, the scheduler's method #close is called
- * * The scheduler runs into a wait loop, checking all the blocked fibers (which it has
- * registered on hook calls) and resuming them when the awaited resource is ready
- * (e.g. I/O ready or sleep time elapsed).
- *
- * A typical implementation would probably rely for this closing loop on a gem like
- * EventMachine[https://github.com/eventmachine/eventmachine] or
- * Async[https://github.com/socketry/async].
- *
- * This way concurrent execution will be achieved transparently for every
- * individual Fiber's code.
- *
- * Hook methods are:
- *
- * * #io_wait, #io_read, and #io_write
- * * #process_wait
- * * #kernel_sleep
- * * #timeout_after
- * * #address_resolve
- * * #block and #unblock
- * * (the list is expanded as Ruby developers make more methods having non-blocking calls)
- *
- * When not specified otherwise, the hook implementations are mandatory: if they are not
- * implemented, the methods trying to call hook will fail. To provide backward compatibility,
- * in the future hooks will be optional (if they are not implemented, due to the scheduler
- * being created for the older Ruby version, the code which needs this hook will not fail,
- * and will just behave in a blocking fashion).
- *
- * It is also strongly recommended that the scheduler implements the #fiber method, which is
- * delegated to by Fiber.schedule.
- *
- * Sample _toy_ implementation of the scheduler can be found in Ruby's code, in
- * <tt>test/fiber/scheduler.rb</tt>
- *
- */
-
-#if 0 /* for RDoc */
-/*
- *
- * Document-method: Fiber::SchedulerInterface#close
- *
- * Called when the current thread exits. The scheduler is expected to implement this
- * method in order to allow all waiting fibers to finalize their execution.
- *
- * The suggested pattern is to implement the main event loop in the #close method.
- *
- */
-static VALUE
-rb_fiber_scheduler_interface_close(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#process_wait
- * call-seq: process_wait(pid, flags)
- *
- * Invoked by Process::Status.wait in order to wait for a specified process.
- * See that method description for arguments description.
- *
- * Suggested minimal implementation:
- *
- * Thread.new do
- * Process::Status.wait(pid, flags)
- * end.value
- *
- * This hook is optional: if it is not present in the current scheduler,
- * Process::Status.wait will behave as a blocking method.
- *
- * Expected to return a Process::Status instance.
- */
-static VALUE
-rb_fiber_scheduler_interface_process_wait(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#io_wait
- * call-seq: io_wait(io, events, timeout)
- *
- * Invoked by IO#wait, IO#wait_readable, IO#wait_writable to ask whether the
- * specified descriptor is ready for specified events within
- * the specified +timeout+.
- *
- * +events+ is a bit mask of <tt>IO::READABLE</tt>, <tt>IO::WRITABLE</tt>, and
- * <tt>IO::PRIORITY</tt>.
- *
- * Suggested implementation should register which Fiber is waiting for which
- * resources and immediately calling Fiber.yield to pass control to other
- * fibers. Then, in the #close method, the scheduler might dispatch all the
- * I/O resources to fibers waiting for it.
- *
- * Expected to return the subset of events that are ready immediately.
- *
- */
-static VALUE
-rb_fiber_scheduler_interface_io_wait(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#io_read
- * call-seq: io_read(io, buffer, length) -> read length or -errno
- *
- * Invoked by IO#read to read +length+ bytes from +io+ into a specified
- * +buffer+ (see IO::Buffer).
- *
- * The +length+ argument is the "minimum length to be read".
- * If the IO buffer size is 8KiB, but the +length+ is +1024+ (1KiB), up to
- * 8KiB might be read, but at least 1KiB will be.
- * Generally, the only case where less data than +length+ will be read is if
- * there is an error reading the data.
- *
- * Specifying a +length+ of 0 is valid and means try reading at least once
- * and return any available data.
- *
- * Suggested implementation should try to read from +io+ in a non-blocking
- * manner and call #io_wait if the +io+ is not ready (which will yield control
- * to other fibers).
- *
- * See IO::Buffer for an interface available to return data.
- *
- * Expected to return number of bytes read, or, in case of an error, <tt>-errno</tt>
- * (negated number corresponding to system's error code).
- *
- * The method should be considered _experimental_.
- */
-static VALUE
-rb_fiber_scheduler_interface_io_read(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#io_write
- * call-seq: io_write(io, buffer, length) -> written length or -errno
- *
- * Invoked by IO#write to write +length+ bytes to +io+ from
- * from a specified +buffer+ (see IO::Buffer).
- *
- * The +length+ argument is the "(minimum) length to be written".
- * If the IO buffer size is 8KiB, but the +length+ specified is 1024 (1KiB),
- * at most 8KiB will be written, but at least 1KiB will be.
- * Generally, the only case where less data than +length+ will be written is if
- * there is an error writing the data.
- *
- * Specifying a +length+ of 0 is valid and means try writing at least once,
- * as much data as possible.
- *
- * Suggested implementation should try to write to +io+ in a non-blocking
- * manner and call #io_wait if the +io+ is not ready (which will yield control
- * to other fibers).
- *
- * See IO::Buffer for an interface available to get data from buffer efficiently.
- *
- * Expected to return number of bytes written, or, in case of an error, <tt>-errno</tt>
- * (negated number corresponding to system's error code).
- *
- * The method should be considered _experimental_.
- */
-static VALUE
-rb_fiber_scheduler_interface_io_write(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#kernel_sleep
- * call-seq: kernel_sleep(duration = nil)
- *
- * Invoked by Kernel#sleep and Mutex#sleep and is expected to provide
- * an implementation of sleeping in a non-blocking way. Implementation might
- * register the current fiber in some list of "which fiber wait until what
- * moment", call Fiber.yield to pass control, and then in #close resume
- * the fibers whose wait period has elapsed.
- *
- */
-static VALUE
-rb_fiber_scheduler_interface_kernel_sleep(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#address_resolve
- * call-seq: address_resolve(hostname) -> array_of_strings or nil
- *
- * Invoked by any method that performs a non-reverse DNS lookup. The most
- * notable method is Addrinfo.getaddrinfo, but there are many other.
- *
- * The method is expected to return an array of strings corresponding to ip
- * addresses the +hostname+ is resolved to, or +nil+ if it can not be resolved.
- *
- * Fairly exhaustive list of all possible call-sites:
- *
- * - Addrinfo.getaddrinfo
- * - Addrinfo.tcp
- * - Addrinfo.udp
- * - Addrinfo.ip
- * - Addrinfo.new
- * - Addrinfo.marshal_load
- * - SOCKSSocket.new
- * - TCPServer.new
- * - TCPSocket.new
- * - IPSocket.getaddress
- * - TCPSocket.gethostbyname
- * - UDPSocket#connect
- * - UDPSocket#bind
- * - UDPSocket#send
- * - Socket.getaddrinfo
- * - Socket.gethostbyname
- * - Socket.pack_sockaddr_in
- * - Socket.sockaddr_in
- * - Socket.unpack_sockaddr_in
- */
-static VALUE
-rb_fiber_scheduler_interface_address_resolve(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#timeout_after
- * call-seq: timeout_after(duration, exception_class, *exception_arguments, &block) -> result of block
- *
- * Invoked by Timeout.timeout to execute the given +block+ within the given
- * +duration+. It can also be invoked directly by the scheduler or user code.
- *
- * Attempt to limit the execution time of a given +block+ to the given
- * +duration+ if possible. When a non-blocking operation causes the +block+'s
- * execution time to exceed the specified +duration+, that non-blocking
- * operation should be interrupted by raising the specified +exception_class+
- * constructed with the given +exception_arguments+.
- *
- * General execution timeouts are often considered risky. This implementation
- * will only interrupt non-blocking operations. This is by design because it's
- * expected that non-blocking operations can fail for a variety of
- * unpredictable reasons, so applications should already be robust in handling
- * these conditions and by implication timeouts.
- *
- * However, as a result of this design, if the +block+ does not invoke any
- * non-blocking operations, it will be impossible to interrupt it. If you
- * desire to provide predictable points for timeouts, consider adding
- * +sleep(0)+.
- *
- * If the block is executed successfully, its result will be returned.
- *
- * The exception will typically be raised using Fiber#raise.
- */
-static VALUE
-rb_fiber_scheduler_interface_timeout_after(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#block
- * call-seq: block(blocker, timeout = nil)
- *
- * Invoked by methods like Thread.join, and by Mutex, to signify that current
- * Fiber is blocked until further notice (e.g. #unblock) or until +timeout+ has
- * elapsed.
- *
- * +blocker+ is what we are waiting on, informational only (for debugging and
- * logging). There are no guarantee about its value.
- *
- * Expected to return boolean, specifying whether the blocking operation was
- * successful or not.
- */
-static VALUE
-rb_fiber_scheduler_interface_block(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#unblock
- * call-seq: unblock(blocker, fiber)
- *
- * Invoked to wake up Fiber previously blocked with #block (for example, Mutex#lock
- * calls #block and Mutex#unlock calls #unblock). The scheduler should use
- * the +fiber+ parameter to understand which fiber is unblocked.
- *
- * +blocker+ is what was awaited for, but it is informational only (for debugging
- * and logging), and it is not guaranteed to be the same value as the +blocker+ for
- * #block.
- *
- */
-static VALUE
-rb_fiber_scheduler_interface_unblock(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#fiber
- * call-seq: fiber(&block)
- *
- * Implementation of the Fiber.schedule. The method is <em>expected</em> to immediately
- * run the given block of code in a separate non-blocking fiber, and to return that Fiber.
- *
- * Minimal suggested implementation is:
- *
- * def fiber(&block)
- * fiber = Fiber.new(blocking: false, &block)
- * fiber.resume
- * fiber
- * end
- */
-static VALUE
-rb_fiber_scheduler_interface_fiber(VALUE self)
-{
-}
-#endif
-
void
Init_Cont(void)
{
@@ -3290,6 +3361,7 @@ Init_Cont(void)
fiber_initialize_keywords[0] = rb_intern_const("blocking");
fiber_initialize_keywords[1] = rb_intern_const("pool");
+ fiber_initialize_keywords[2] = rb_intern_const("storage");
const char *fiber_shared_fiber_pool_free_stacks = getenv("RUBY_SHARED_FIBER_POOL_FREE_STACKS");
if (fiber_shared_fiber_pool_free_stacks) {
@@ -3301,8 +3373,14 @@ Init_Cont(void)
rb_eFiberError = rb_define_class("FiberError", rb_eStandardError);
rb_define_singleton_method(rb_cFiber, "yield", rb_fiber_s_yield, -1);
rb_define_singleton_method(rb_cFiber, "current", rb_fiber_s_current, 0);
+ rb_define_singleton_method(rb_cFiber, "blocking", rb_fiber_blocking, 0);
+ rb_define_singleton_method(rb_cFiber, "[]", rb_fiber_storage_aref, 1);
+ rb_define_singleton_method(rb_cFiber, "[]=", rb_fiber_storage_aset, 2);
+
rb_define_method(rb_cFiber, "initialize", rb_fiber_initialize, -1);
rb_define_method(rb_cFiber, "blocking?", rb_fiber_blocking_p, 0);
+ rb_define_method(rb_cFiber, "storage", rb_fiber_storage_get, 0);
+ rb_define_method(rb_cFiber, "storage=", rb_fiber_storage_set, 1);
rb_define_method(rb_cFiber, "resume", rb_fiber_m_resume, -1);
rb_define_method(rb_cFiber, "raise", rb_fiber_m_raise, -1);
rb_define_method(rb_cFiber, "backtrace", rb_fiber_backtrace, -1);
@@ -3319,21 +3397,6 @@ Init_Cont(void)
rb_define_singleton_method(rb_cFiber, "schedule", rb_fiber_s_schedule, -1);
-#if 0 /* for RDoc */
- rb_cFiberScheduler = rb_define_class_under(rb_cFiber, "SchedulerInterface", rb_cObject);
- rb_define_method(rb_cFiberScheduler, "close", rb_fiber_scheduler_interface_close, 0);
- rb_define_method(rb_cFiberScheduler, "process_wait", rb_fiber_scheduler_interface_process_wait, 0);
- rb_define_method(rb_cFiberScheduler, "io_wait", rb_fiber_scheduler_interface_io_wait, 0);
- rb_define_method(rb_cFiberScheduler, "io_read", rb_fiber_scheduler_interface_io_read, 0);
- rb_define_method(rb_cFiberScheduler, "io_write", rb_fiber_scheduler_interface_io_write, 0);
- rb_define_method(rb_cFiberScheduler, "kernel_sleep", rb_fiber_scheduler_interface_kernel_sleep, 0);
- rb_define_method(rb_cFiberScheduler, "address_resolve", rb_fiber_scheduler_interface_address_resolve, 0);
- rb_define_method(rb_cFiberScheduler, "timeout_after", rb_fiber_scheduler_interface_timeout_after, 0);
- rb_define_method(rb_cFiberScheduler, "block", rb_fiber_scheduler_interface_block, 0);
- rb_define_method(rb_cFiberScheduler, "unblock", rb_fiber_scheduler_interface_unblock, 0);
- rb_define_method(rb_cFiberScheduler, "fiber", rb_fiber_scheduler_interface_fiber, 0);
-#endif
-
#ifdef RB_EXPERIMENTAL_FIBER_POOL
rb_cFiberPool = rb_define_class_under(rb_cFiber, "Pool", rb_cObject);
rb_define_alloc_func(rb_cFiberPool, fiber_pool_alloc);
diff --git a/coroutine/asyncify/Context.h b/coroutine/asyncify/Context.h
index 7dba829a1d..71791a4004 100644
--- a/coroutine/asyncify/Context.h
+++ b/coroutine/asyncify/Context.h
@@ -13,6 +13,7 @@
#include <stddef.h>
#include <stdio.h>
+#include <stdint.h>
#include "wasm/asyncify.h"
#include "wasm/machine.h"
#include "wasm/fiber.h"
@@ -47,10 +48,13 @@ static inline void coroutine_initialize_main(struct coroutine_context * context)
static inline void coroutine_initialize(struct coroutine_context *context, coroutine_start start, void *stack, size_t size)
{
- if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)stack + size);
+ // Linear stack pointer must be always aligned down to 16 bytes.
+ // https://github.com/WebAssembly/tool-conventions/blob/c74267a5897c1bdc9aa60adeaf41816387d3cd12/BasicCABI.md#the-linear-stack
+ uintptr_t sp = ((uintptr_t)stack + size) & ~0xF;
+ if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)sp);
rb_wasm_init_context(&context->fc, coroutine_trampoline, start, context);
// record the initial stack pointer position to restore it after resumption
- context->current_sp = (char *)stack + size;
+ context->current_sp = (char *)sp;
context->stack_base = stack;
context->size = size;
}
diff --git a/coroutine/ppc/Context.S b/coroutine/ppc/Context.S
index fe28390df0..cdda93e179 100644
--- a/coroutine/ppc/Context.S
+++ b/coroutine/ppc/Context.S
@@ -1,73 +1,90 @@
+; Based on the code by Samuel Williams. Created by Sergey Fedorov on 04/06/2022.
+; Credits to Samuel Williams, Rei Odaira and Iain Sandoe. Errors, if any, are mine.
+; Some relevant examples: https://github.com/gcc-mirror/gcc/blob/master/libphobos/libdruntime/config/powerpc/switchcontext.S
+; https://github.com/gcc-mirror/gcc/blob/master/libgcc/config/rs6000/darwin-gpsave.S
+; https://www.ibm.com/docs/en/aix/7.2?topic=epilogs-saving-gprs-only
+; ppc32 version may be re-written compactly with stmw/lwm, but the code wonʼt be faster, see: https://github.com/ruby/ruby/pull/5927#issuecomment-1139730541
+
+; Notice that this code is only for Darwin (macOS). Darwin ABI differs from AIX and ELF.
+; To add support for AIX, *BSD or *Linux, please make separate implementations.
+
#define TOKEN_PASTE(x,y) x##y
#define PREFIXED_SYMBOL(prefix,name) TOKEN_PASTE(prefix,name)
+.machine ppc7400 ; = G4, Rosetta
.text
-.align 2
.globl PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer)
+.align 2
+
PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer):
- # Make space on the stack for caller registers
- addi r1,r1,-80
+ ; Make space on the stack for caller registers
+ ; (Should we rather use red zone? See libphobos example.)
+ subi r1,r1,80
+
+ ; Get LR
+ mflr r0
- # Save caller registers
- stw r13,0(r1)
- stw r14,4(r1)
- stw r15,8(r1)
- stw r16,12(r1)
- stw r17,16(r1)
- stw r18,20(r1)
- stw r19,24(r1)
- stw r20,28(r1)
- stw r21,32(r1)
+ ; Save caller registers
+ stw r31,0(r1)
+ stw r30,4(r1)
+ stw r29,8(r1)
+ stw r28,12(r1)
+ stw r27,16(r1)
+ stw r26,20(r1)
+ stw r25,24(r1)
+ stw r24,28(r1)
+ stw r23,32(r1)
stw r22,36(r1)
- stw r23,40(r1)
- stw r24,44(r1)
- stw r25,48(r1)
- stw r26,52(r1)
- stw r27,56(r1)
- stw r28,60(r1)
- stw r29,64(r1)
- stw r30,68(r1)
- stw r31,72(r1)
+ stw r21,40(r1)
+ stw r20,44(r1)
+ stw r19,48(r1)
+ stw r18,52(r1)
+ stw r17,56(r1)
+ stw r16,60(r1)
+ stw r15,64(r1)
+ stw r14,68(r1)
+ stw r13,72(r1)
- # Save return address
- mflr r0
+ ; Save return address
+ ; Possibly should rather be saved into linkage area, see libphobos and IBM docs
stw r0,76(r1)
- # Save stack pointer to first argument
+ ; Save stack pointer to first argument
stw r1,0(r3)
- # Load stack pointer from second argument
+ ; Load stack pointer from second argument
lwz r1,0(r4)
- # Restore caller registers
- lwz r13,0(r1)
- lwz r14,4(r1)
- lwz r15,8(r1)
- lwz r16,12(r1)
- lwz r17,16(r1)
- lwz r18,20(r1)
- lwz r19,24(r1)
- lwz r20,28(r1)
- lwz r21,32(r1)
+ ; Load return address
+ lwz r0,76(r1)
+
+ ; Restore caller registers
+ lwz r13,72(r1)
+ lwz r14,68(r1)
+ lwz r15,64(r1)
+ lwz r16,60(r1)
+ lwz r17,56(r1)
+ lwz r18,52(r1)
+ lwz r19,48(r1)
+ lwz r20,44(r1)
+ lwz r21,40(r1)
lwz r22,36(r1)
- lwz r23,40(r1)
- lwz r24,44(r1)
- lwz r25,48(r1)
- lwz r26,52(r1)
- lwz r27,56(r1)
- lwz r28,60(r1)
- lwz r29,64(r1)
- lwz r30,68(r1)
- lwz r31,72(r1)
+ lwz r23,32(r1)
+ lwz r24,28(r1)
+ lwz r25,24(r1)
+ lwz r26,20(r1)
+ lwz r27,16(r1)
+ lwz r28,12(r1)
+ lwz r29,8(r1)
+ lwz r30,4(r1)
+ lwz r31,0(r1)
- # Load return address
- lwz r0,76(r1)
+ ; Set LR
mtlr r0
- # Pop stack frame
+ ; Pop stack frame
addi r1,r1,80
- # Jump to return address
+ ; Jump to return address
blr
-
diff --git a/coroutine/ppc/Context.h b/coroutine/ppc/Context.h
index 9f69390388..1fce112579 100644
--- a/coroutine/ppc/Context.h
+++ b/coroutine/ppc/Context.h
@@ -9,6 +9,7 @@
#include <string.h>
#define COROUTINE __attribute__((noreturn)) void
+#define COROUTINE_LIMITED_ADDRESS_SPACE
enum {
COROUTINE_REGISTERS =
diff --git a/coroutine/ppc64/Context.S b/coroutine/ppc64/Context.S
index 1bd9268f93..f8561e0e7d 100644
--- a/coroutine/ppc64/Context.S
+++ b/coroutine/ppc64/Context.S
@@ -1,70 +1,89 @@
+; Based on the code by Samuel Williams. Created by Sergey Fedorov on 04/06/2022.
+; Credits to Samuel Williams, Rei Odaira and Iain Sandoe. Errors, if any, are mine.
+; Some relevant examples: https://github.com/gcc-mirror/gcc/blob/master/libphobos/libdruntime/config/powerpc/switchcontext.S
+; https://github.com/gcc-mirror/gcc/blob/master/libgcc/config/rs6000/darwin-gpsave.S
+; https://www.ibm.com/docs/en/aix/7.2?topic=epilogs-saving-gprs-only
+
+; Notice that this code is only for Darwin (macOS). Darwin ABI differs from AIX and ELF.
+; To add support for AIX, *BSD or *Linux, please make separate implementations.
+
#define TOKEN_PASTE(x,y) x##y
#define PREFIXED_SYMBOL(prefix,name) TOKEN_PASTE(prefix,name)
+.machine ppc64 ; = G5
.text
-.align 3
.globl PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer)
-PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer):
- # Make space on the stack for caller registers
- addi r1,r1,-152
+.align 2
- # Save caller registers
- std r14,0(r1)
- std r15,8(r1)
- std r16,16(r1)
- std r17,24(r1)
- std r18,32(r1)
- std r19,40(r1)
- std r20,48(r1)
- std r21,56(r1)
- std r22,64(r1)
- std r23,72(r1)
- std r24,80(r1)
- std r25,88(r1)
- std r26,96(r1)
- std r27,104(r1)
- std r28,112(r1)
- std r29,120(r1)
- std r30,128(r1)
- std r31,136(r1)
+PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer):
+ ; Make space on the stack for caller registers
+ ; (Should we rather use red zone? See libphobos example.)
+ subi r1,r1,160
- # Save return address
+ ; Get LR
mflr r0
- std r0,144(r1)
- # Save stack pointer to first argument
+ ; Save caller registers
+ std r31,0(r1)
+ std r30,8(r1)
+ std r29,16(r1)
+ std r28,24(r1)
+ std r27,32(r1)
+ std r26,40(r1)
+ std r25,48(r1)
+ std r24,56(r1)
+ std r23,64(r1)
+ std r22,72(r1)
+ std r21,80(r1)
+ std r20,88(r1)
+ std r19,96(r1)
+ std r18,104(r1)
+ std r17,112(r1)
+ std r16,120(r1)
+ std r15,128(r1)
+ std r14,136(r1)
+ std r13,144(r1)
+
+ ; Save return address
+ ; Possibly should rather be saved into linkage area, see libphobos and IBM docs
+ std r0,152(r1)
+
+ ; Save stack pointer to first argument
std r1,0(r3)
- # Load stack pointer from second argument
+ ; Load stack pointer from second argument
ld r1,0(r4)
- # Restore caller registers
- ld r14,0(r1)
- ld r15,8(r1)
- ld r16,16(r1)
- ld r17,24(r1)
- ld r18,32(r1)
- ld r19,40(r1)
- ld r20,48(r1)
- ld r21,56(r1)
- ld r22,64(r1)
- ld r23,72(r1)
- ld r24,80(r1)
- ld r25,88(r1)
- ld r26,96(r1)
- ld r27,104(r1)
- ld r28,112(r1)
- ld r29,120(r1)
- ld r30,128(r1)
- ld r31,136(r1)
+ ; Load return address
+ ld r0,152(r1)
+
+ ; Restore caller registers
+ ld r13,144(r1)
+ ld r14,136(r1)
+ ld r15,128(r1)
+ ld r16,120(r1)
+ ld r17,112(r1)
+ ld r18,104(r1)
+ ld r19,96(r1)
+ ld r20,88(r1)
+ ld r21,80(r1)
+ ld r22,72(r1)
+ ld r23,64(r1)
+ ld r24,56(r1)
+ ld r25,48(r1)
+ ld r26,40(r1)
+ ld r27,32(r1)
+ ld r28,24(r1)
+ ld r29,16(r1)
+ ld r30,8(r1)
+ ld r31,0(r1)
- # Load return address
- ld r0,144(r1)
+ ; Set LR
mtlr r0
- # Pop stack frame
- addi r1,r1,152
+ ; Pop stack frame
+ addi r1,r1,160
- # Jump to return address
+ ; Jump to return address
blr
diff --git a/coroutine/ppc64/Context.h b/coroutine/ppc64/Context.h
index 5b47511b9c..3e6f77f55a 100644
--- a/coroutine/ppc64/Context.h
+++ b/coroutine/ppc64/Context.h
@@ -12,7 +12,7 @@
enum {
COROUTINE_REGISTERS =
- 19 /* 18 general purpose registers (r14–r31) and 1 return address */
+ 20 /* 19 general purpose registers (r13–r31) and 1 return address */
+ 4 /* space for fiber_entry() to store the link register */
};
@@ -44,7 +44,7 @@ static inline void coroutine_initialize(
memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
/* Skip a global prologue that sets the TOC register */
- context->stack_pointer[18] = ((char*)start) + 8;
+ context->stack_pointer[19] = ((char*)start) + 8;
}
struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target);
diff --git a/cygwin/GNUmakefile.in b/cygwin/GNUmakefile.in
index da00b8aa3c..f342d2fcf7 100644
--- a/cygwin/GNUmakefile.in
+++ b/cygwin/GNUmakefile.in
@@ -2,15 +2,17 @@ gnumake = yes
include Makefile
-ENABLE_SHARED=@ENABLE_SHARED@
-DLLWRAP = @DLLWRAP@ --target=@target_os@ --driver-name="$(CC)"
+MUNICODE_FLAG := $(if $(filter mingw%,$(target_os)),-municode)
+override EXE_LDFLAGS += $(MUNICODE_FLAG)
+
+DLLWRAP = @DLLWRAP@ --target=$(target_os) --driver-name="$(CC)"
windres-cpp := $(CPP) -xc
windres-cpp := --preprocessor=$(firstword $(windres-cpp)) \
$(addprefix --preprocessor-arg=,$(wordlist 2,$(words $(windres-cpp)),$(windres-cpp)))
WINDRES = @WINDRES@ $(windres-cpp) -DRC_INVOKED
STRIP = @STRIP@
-ifeq (@target_os@,cygwin)
+ifeq ($(target_os),cygwin)
DLL_BASE_NAME := $(LIBRUBY_SO:.dll=)
else
DLL_BASE_NAME := $(RUBY_SO_NAME)
@@ -38,7 +40,7 @@ WPROGRAM = $(RUBYW_INSTALL_NAME)$(EXEEXT)
include $(srcdir)/template/GNUmakefile.in
-SOLIBS := $(DLL_BASE_NAME).res.@OBJEXT@ $(SOLIBS)
+SOLIBS := $(DLL_BASE_NAME).res.$(OBJEXT) $(SOLIBS)
override EXTOBJS += $(if $(filter-out $(RUBYW_INSTALL_NAME),$(@:$(EXEEXT)=)),$(RUBY_INSTALL_NAME),$(@:$(EXEEXT)=)).res.$(OBJEXT)
RCFILES = $(RUBY_INSTALL_NAME).rc $(RUBYW_INSTALL_NAME).rc $(DLL_BASE_NAME).rc
RUBYDEF = $(DLL_BASE_NAME).def
@@ -47,26 +49,26 @@ ruby: $(PROGRAM)
rubyw: $(WPROGRAM)
$(LIBRUBY): $(RUBY_EXP) $(LIBRUBY_SO)
-$(RUBY_EXP) $(LIBRUBY_SO): $(DLL_BASE_NAME).res.@OBJEXT@
+$(RUBY_EXP) $(LIBRUBY_SO): $(DLL_BASE_NAME).res.$(OBJEXT)
-%.res.@OBJEXT@: %.rc
+%.res.$(OBJEXT): %.rc
$(ECHO) compiling $@
$(Q) $(WINDRES) --include-dir . --include-dir $(<D) --include-dir $(srcdir)/win32 $< $@
-%.rc: $(RBCONFIG) $(srcdir)/revision.h $(srcdir)/win32/resource.rb
+%.rc: $(BOOTSTRAPRUBY_FAKE) $(RBCONFIG) $(srcdir)/revision.h $(srcdir)/win32/resource.rb
$(ECHO) generating $@
- $(Q) $(MINIRUBY) $(srcdir)/win32/resource.rb \
+ $(Q) $(BOOTSTRAPRUBY_COMMAND) $(srcdir)/win32/resource.rb \
-ruby_name=$(RUBY_INSTALL_NAME) -rubyw_name=$(RUBYW_INSTALL_NAME) \
-so_name=$(DLL_BASE_NAME) -output=$(*F) \
. $(icondirs) $(srcdir)/win32
-$(PROGRAM): $(RUBY_INSTALL_NAME).res.@OBJEXT@
-$(WPROGRAM): $(RUBYW_INSTALL_NAME).res.@OBJEXT@
+$(PROGRAM): $(RUBY_INSTALL_NAME).res.$(OBJEXT)
+$(WPROGRAM): $(RUBYW_INSTALL_NAME).res.$(OBJEXT)
@rm -f $@
$(ECHO) linking $@
- $(Q) $(PURIFY) $(CC) -mwindows -e $(SYMBOL_PREFIX)mainCRTStartup $(LDFLAGS) $(XLDFLAGS) \
+ $(Q) $(PURIFY) $(CC) $(MUNICODE_FLAG) -mwindows -e $(SYMBOL_PREFIX)mainCRTStartup $(LDFLAGS) $(XLDFLAGS) \
$(MAINOBJ) $(EXTOBJS) $(LIBRUBYARG) $(LIBS) -o $@
-$(STUBPROGRAM): $(RUBY_INSTALL_NAME).res.@OBJEXT@
+$(STUBPROGRAM): $(RUBY_INSTALL_NAME).res.$(OBJEXT)
$(RUBY_EXP): $(LIBRUBY_A)
$(ECHO) creating $@
@@ -78,7 +80,7 @@ $(RUBY_EXP): $(LIBRUBY_A)
GNUmakefile: $(srcdir)/cygwin/GNUmakefile.in
-ifeq (@target_os@,mingw32)
+ifeq ($(target_os),mingw32)
$(OBJS) $(MAINOBJ): win32.h
dir.$(OBJEXT) win32/win32.$(OBJEXT): win32/dir.h
@@ -95,11 +97,11 @@ endif
$(LIBRUBY_SO): $(RUBYDEF)
-$(RUBYDEF): $(LIBRUBY_A) $(PREP) $(RBCONFIG)
+$(RUBYDEF): $(LIBRUBY_A) $(PREP) $(BOOTSTRAPRUBY_FAKE) $(RBCONFIG)
$(ECHO) generating $@
- $(Q) $(MINIRUBY) $(srcdir)/win32/mkexports.rb -output=$@ $(LIBRUBY_A)
+ $(Q) $(BOOTSTRAPRUBY_COMMAND) $(srcdir)/win32/mkexports.rb -output=$@ $(LIBRUBY_A)
clean-local::
@$(RM) $(RUBYDEF)
- @$(RM) $(RUBY_EXP) $(RCFILES:.rc=.res.@OBJEXT@)
+ @$(RM) $(RUBY_EXP) $(RCFILES:.rc=.res.$(OBJEXT))
@$(RM) $(RCFILES)
diff --git a/debug.c b/debug.c
index d927f72231..3dd0f71906 100644
--- a/debug.c
+++ b/debug.c
@@ -64,23 +64,23 @@ const union {
enum ruby_rarray_flags rarray_flags;
enum ruby_rarray_consts rarray_consts;
enum {
- RUBY_FMODE_READABLE = FMODE_READABLE,
- RUBY_FMODE_WRITABLE = FMODE_WRITABLE,
- RUBY_FMODE_READWRITE = FMODE_READWRITE,
- RUBY_FMODE_BINMODE = FMODE_BINMODE,
- RUBY_FMODE_SYNC = FMODE_SYNC,
- RUBY_FMODE_TTY = FMODE_TTY,
- RUBY_FMODE_DUPLEX = FMODE_DUPLEX,
- RUBY_FMODE_APPEND = FMODE_APPEND,
- RUBY_FMODE_CREATE = FMODE_CREATE,
- RUBY_FMODE_NOREVLOOKUP = 0x00000100,
- RUBY_FMODE_TRUNC = FMODE_TRUNC,
- RUBY_FMODE_TEXTMODE = FMODE_TEXTMODE,
- RUBY_FMODE_PREP = 0x00010000,
- RUBY_FMODE_SETENC_BY_BOM = FMODE_SETENC_BY_BOM,
- RUBY_FMODE_UNIX = 0x00200000,
- RUBY_FMODE_INET = 0x00400000,
- RUBY_FMODE_INET6 = 0x00800000,
+ RUBY_FMODE_READABLE = FMODE_READABLE,
+ RUBY_FMODE_WRITABLE = FMODE_WRITABLE,
+ RUBY_FMODE_READWRITE = FMODE_READWRITE,
+ RUBY_FMODE_BINMODE = FMODE_BINMODE,
+ RUBY_FMODE_SYNC = FMODE_SYNC,
+ RUBY_FMODE_TTY = FMODE_TTY,
+ RUBY_FMODE_DUPLEX = FMODE_DUPLEX,
+ RUBY_FMODE_APPEND = FMODE_APPEND,
+ RUBY_FMODE_CREATE = FMODE_CREATE,
+ RUBY_FMODE_NOREVLOOKUP = 0x00000100,
+ RUBY_FMODE_TRUNC = FMODE_TRUNC,
+ RUBY_FMODE_TEXTMODE = FMODE_TEXTMODE,
+ RUBY_FMODE_PREP = 0x00010000,
+ RUBY_FMODE_SETENC_BY_BOM = FMODE_SETENC_BY_BOM,
+ RUBY_FMODE_UNIX = 0x00200000,
+ RUBY_FMODE_INET = 0x00400000,
+ RUBY_FMODE_INET6 = 0x00800000,
RUBY_NODE_TYPESHIFT = NODE_TYPESHIFT,
RUBY_NODE_TYPEMASK = NODE_TYPEMASK,
@@ -88,9 +88,9 @@ const union {
RUBY_NODE_FL_NEWLINE = NODE_FL_NEWLINE
} various;
union {
- enum imemo_type types;
- enum {RUBY_IMEMO_MASK = IMEMO_MASK} mask;
- struct RIMemo *ptr;
+ enum imemo_type types;
+ enum {RUBY_IMEMO_MASK = IMEMO_MASK} mask;
+ struct RIMemo *ptr;
} imemo;
struct RSymbol *symbol_ptr;
enum vm_call_flag_bits vm_call_flags;
@@ -102,9 +102,9 @@ int
ruby_debug_print_indent(int level, int debug_level, int indent_level)
{
if (level < debug_level) {
- fprintf(stderr, "%*s", indent_level, "");
- fflush(stderr);
- return TRUE;
+ fprintf(stderr, "%*s", indent_level, "");
+ fflush(stderr);
+ return TRUE;
}
return FALSE;
}
@@ -124,11 +124,11 @@ VALUE
ruby_debug_print_value(int level, int debug_level, const char *header, VALUE obj)
{
if (level < debug_level) {
- char buff[0x100];
- rb_raw_obj_info(buff, 0x100, obj);
+ char buff[0x100];
+ rb_raw_obj_info(buff, 0x100, obj);
- fprintf(stderr, "DBG> %s: %s\n", header, buff);
- fflush(stderr);
+ fprintf(stderr, "DBG> %s: %s\n", header, buff);
+ fflush(stderr);
}
return obj;
}
@@ -143,8 +143,8 @@ ID
ruby_debug_print_id(int level, int debug_level, const char *header, ID id)
{
if (level < debug_level) {
- fprintf(stderr, "DBG> %s: %s\n", header, rb_id2name(id));
- fflush(stderr);
+ fprintf(stderr, "DBG> %s: %s\n", header, rb_id2name(id));
+ fflush(stderr);
}
return id;
}
@@ -153,8 +153,8 @@ NODE *
ruby_debug_print_node(int level, int debug_level, const char *header, const NODE *node)
{
if (level < debug_level) {
- fprintf(stderr, "DBG> %s: %s (%u)\n", header,
- ruby_node_name(nd_type(node)), nd_line(node));
+ fprintf(stderr, "DBG> %s: %s (%u)\n", header,
+ ruby_node_name(nd_type(node)), nd_line(node));
}
return (NODE *)node;
}
@@ -184,11 +184,11 @@ ruby_env_debug_option(const char *str, int len, void *arg)
size_t retlen;
unsigned long n;
#define SET_WHEN(name, var, val) do { \
- if (len == sizeof(name) - 1 && \
- strncmp(str, (name), len) == 0) { \
- (var) = (val); \
- return 1; \
- } \
+ if (len == sizeof(name) - 1 && \
+ strncmp(str, (name), len) == 0) { \
+ (var) = (val); \
+ return 1; \
+ } \
} while (0)
#define NAME_MATCH_VALUE(name) \
((size_t)len >= sizeof(name)-1 && \
@@ -197,24 +197,24 @@ ruby_env_debug_option(const char *str, int len, void *arg)
(str[sizeof(name)-1] == '=' && \
(str += sizeof(name), len -= sizeof(name), 1))))
#define SET_UINT(val) do { \
- n = ruby_scan_digits(str, len, 10, &retlen, &ov); \
- if (!ov && retlen) { \
- val = (unsigned int)n; \
- } \
- str += retlen; \
- len -= retlen; \
+ n = ruby_scan_digits(str, len, 10, &retlen, &ov); \
+ if (!ov && retlen) { \
+ val = (unsigned int)n; \
+ } \
+ str += retlen; \
+ len -= retlen; \
} while (0)
#define SET_UINT_LIST(name, vals, num) do { \
- int i; \
- for (i = 0; i < (num); ++i) { \
- SET_UINT((vals)[i]); \
- if (!len || *str != ':') break; \
- ++str; \
- --len; \
- } \
- if (len > 0) { \
- fprintf(stderr, "ignored "name" option: `%.*s'\n", len, str); \
- } \
+ int i; \
+ for (i = 0; i < (num); ++i) { \
+ SET_UINT((vals)[i]); \
+ if (!len || *str != ':') break; \
+ ++str; \
+ --len; \
+ } \
+ if (len > 0) { \
+ fprintf(stderr, "ignored "name" option: `%.*s'\n", len, str); \
+ } \
} while (0)
#define SET_WHEN_UINT(name, vals, num, req) \
if (NAME_MATCH_VALUE(name)) SET_UINT_LIST(name, vals, num);
@@ -223,9 +223,9 @@ ruby_env_debug_option(const char *str, int len, void *arg)
SET_WHEN("core", ruby_enable_coredump, 1);
SET_WHEN("ci", ruby_on_ci, 1);
if (NAME_MATCH_VALUE("rgengc")) {
- if (!len) ruby_rgengc_debug = 1;
- else SET_UINT_LIST("rgengc", &ruby_rgengc_debug, 1);
- return 1;
+ if (!len) ruby_rgengc_debug = 1;
+ else SET_UINT_LIST("rgengc", &ruby_rgengc_debug, 1);
+ return 1;
}
#if defined _WIN32
# if RUBY_MSVCRT_VERSION >= 80
@@ -234,9 +234,9 @@ ruby_env_debug_option(const char *str, int len, void *arg)
#endif
#if defined _WIN32 || defined __CYGWIN__
if (NAME_MATCH_VALUE("codepage")) {
- if (!len) fprintf(stderr, "missing codepage argument");
- else SET_UINT_LIST("codepage", ruby_w32_codepage, numberof(ruby_w32_codepage));
- return 1;
+ if (!len) fprintf(stderr, "missing codepage argument");
+ else SET_UINT_LIST("codepage", ruby_w32_codepage, numberof(ruby_w32_codepage));
+ return 1;
}
#endif
return 0;
@@ -246,7 +246,7 @@ static void
set_debug_option(const char *str, int len, void *arg)
{
if (!ruby_env_debug_option(str, len, arg)) {
- fprintf(stderr, "unexpected debug option: %.*s\n", len, str);
+ fprintf(stderr, "unexpected debug option: %.*s\n", len, str);
}
}
@@ -348,7 +348,8 @@ setup_debug_log_filter(void)
if (*str == '-') {
debug_log.filters[i].negative = true;
str++;
- } else if (*str == '+') {
+ }
+ else if (*str == '+') {
// negative is false on default.
str++;
}
@@ -498,6 +499,7 @@ pretty_filename(const char *path)
return path;
}
+#undef ruby_debug_log
void
ruby_debug_log(const char *file, int line, const char *func_name, const char *fmt, ...)
{
diff --git a/debug_counter.c b/debug_counter.c
index 1569b0e9fb..463bebf849 100644
--- a/debug_counter.c
+++ b/debug_counter.c
@@ -16,7 +16,7 @@
#if USE_DEBUG_COUNTER
-static const char *const debug_counter_names[] = {
+const char *const rb_debug_counter_names[] = {
#define DEBUG_COUNTER_NAME_EMPTY "" /* Suppress -Wstring-concatenation */
DEBUG_COUNTER_NAME_EMPTY
#undef DEBUG_COUNTER_NAME_EMPTY
@@ -26,7 +26,7 @@ static const char *const debug_counter_names[] = {
};
MJIT_SYMBOL_EXPORT_BEGIN
-size_t rb_debug_counter[numberof(debug_counter_names)];
+size_t rb_debug_counter[numberof(rb_debug_counter_names)];
void rb_debug_counter_add_atomic(enum rb_debug_counter_type type, int add);
MJIT_SYMBOL_EXPORT_END
@@ -56,17 +56,7 @@ void
ruby_debug_counter_reset(void)
{
for (int i = 0; i < RB_DEBUG_COUNTER_MAX; i++) {
- switch (i) {
- case RB_DEBUG_COUNTER_mjit_length_unit_queue:
- case RB_DEBUG_COUNTER_mjit_length_active_units:
- case RB_DEBUG_COUNTER_mjit_length_compact_units:
- case RB_DEBUG_COUNTER_mjit_length_stale_units:
- // These counters may be decreased and should not be reset.
- break;
- default:
- rb_debug_counter[i] = 0;
- break;
- }
+ rb_debug_counter[i] = 0;
}
}
@@ -77,7 +67,7 @@ ruby_debug_counter_get(const char **names_ptr, size_t *counters_ptr)
int i;
if (names_ptr != NULL) {
for (i=0; i<RB_DEBUG_COUNTER_MAX; i++) {
- names_ptr[i] = debug_counter_names[i];
+ names_ptr[i] = rb_debug_counter_names[i];
}
}
if (counters_ptr != NULL) {
@@ -103,13 +93,13 @@ rb_debug_counter_show_results(const char *msg)
setlocale(LC_NUMERIC, "");
if (env == NULL || strcmp("1", env) != 0) {
- int i;
+ int i;
fprintf(stderr, "[RUBY_DEBUG_COUNTER]\t%d %s\n", getpid(), msg);
- for (i=0; i<RB_DEBUG_COUNTER_MAX; i++) {
+ for (i=0; i<RB_DEBUG_COUNTER_MAX; i++) {
fprintf(stderr, "[RUBY_DEBUG_COUNTER]\t%-30s\t%'14"PRIuSIZE"\n",
- debug_counter_names[i],
- rb_debug_counter[i]);
- }
+ rb_debug_counter_names[i],
+ rb_debug_counter[i]);
+ }
}
}
diff --git a/debug_counter.h b/debug_counter.h
index 3f0dec948f..6e0b8dee60 100644
--- a/debug_counter.h
+++ b/debug_counter.h
@@ -130,7 +130,6 @@ RB_DEBUG_COUNTER(frame_C2R)
/* instance variable counts
*
* * ivar_get_ic_hit/miss: ivar_get inline cache (ic) hit/miss counts (VM insn)
- * * ivar_get_ic_miss_serial: ivar_get ic miss reason by serial (VM insn)
* * ivar_get_ic_miss_unset: ... by unset (VM insn)
* * ivar_get_ic_miss_noobject: ... by "not T_OBJECT" (VM insn)
* * ivar_set_...: same counts with ivar_set (VM insn)
@@ -140,17 +139,17 @@ RB_DEBUG_COUNTER(frame_C2R)
*/
RB_DEBUG_COUNTER(ivar_get_ic_hit)
RB_DEBUG_COUNTER(ivar_get_ic_miss)
-RB_DEBUG_COUNTER(ivar_get_ic_miss_serial)
-RB_DEBUG_COUNTER(ivar_get_ic_miss_unset)
RB_DEBUG_COUNTER(ivar_get_ic_miss_noobject)
RB_DEBUG_COUNTER(ivar_set_ic_hit)
RB_DEBUG_COUNTER(ivar_set_ic_miss)
-RB_DEBUG_COUNTER(ivar_set_ic_miss_serial)
-RB_DEBUG_COUNTER(ivar_set_ic_miss_unset)
RB_DEBUG_COUNTER(ivar_set_ic_miss_iv_hit)
RB_DEBUG_COUNTER(ivar_set_ic_miss_noobject)
RB_DEBUG_COUNTER(ivar_get_base)
RB_DEBUG_COUNTER(ivar_set_base)
+RB_DEBUG_COUNTER(ivar_get_ic_miss_set)
+RB_DEBUG_COUNTER(ivar_get_cc_miss_set)
+RB_DEBUG_COUNTER(ivar_get_ic_miss_unset)
+RB_DEBUG_COUNTER(ivar_get_cc_miss_unset)
/* local variable counts
*
@@ -244,6 +243,7 @@ RB_DEBUG_COUNTER(obj_wb_unprotect)
RB_DEBUG_COUNTER(obj_obj_embed)
RB_DEBUG_COUNTER(obj_obj_transient)
RB_DEBUG_COUNTER(obj_obj_ptr)
+RB_DEBUG_COUNTER(obj_obj_too_complex)
RB_DEBUG_COUNTER(obj_str_ptr)
RB_DEBUG_COUNTER(obj_str_embed)
@@ -347,41 +347,6 @@ RB_DEBUG_COUNTER(vm_sync_lock_enter_nb)
RB_DEBUG_COUNTER(vm_sync_lock_enter_cr)
RB_DEBUG_COUNTER(vm_sync_barrier)
-/* mjit_exec() counts */
-RB_DEBUG_COUNTER(mjit_exec)
-RB_DEBUG_COUNTER(mjit_exec_not_added)
-RB_DEBUG_COUNTER(mjit_exec_not_ready)
-RB_DEBUG_COUNTER(mjit_exec_not_compiled)
-RB_DEBUG_COUNTER(mjit_exec_call_func)
-
-/* MJIT enqueue / unload */
-RB_DEBUG_COUNTER(mjit_add_iseq_to_process)
-RB_DEBUG_COUNTER(mjit_unload_units)
-
-/* MJIT <-> VM frame push counts */
-RB_DEBUG_COUNTER(mjit_frame_VM2VM)
-RB_DEBUG_COUNTER(mjit_frame_VM2JT)
-RB_DEBUG_COUNTER(mjit_frame_JT2JT)
-RB_DEBUG_COUNTER(mjit_frame_JT2VM)
-
-/* MJIT cancel counters */
-RB_DEBUG_COUNTER(mjit_cancel)
-RB_DEBUG_COUNTER(mjit_cancel_ivar_inline)
-RB_DEBUG_COUNTER(mjit_cancel_exivar_inline)
-RB_DEBUG_COUNTER(mjit_cancel_send_inline)
-RB_DEBUG_COUNTER(mjit_cancel_opt_insn) /* CALL_SIMPLE_METHOD */
-RB_DEBUG_COUNTER(mjit_cancel_invalidate_all)
-RB_DEBUG_COUNTER(mjit_cancel_leave)
-
-/* rb_mjit_unit_list length */
-RB_DEBUG_COUNTER(mjit_length_unit_queue)
-RB_DEBUG_COUNTER(mjit_length_active_units)
-RB_DEBUG_COUNTER(mjit_length_compact_units)
-RB_DEBUG_COUNTER(mjit_length_stale_units)
-
-/* Other MJIT counters */
-RB_DEBUG_COUNTER(mjit_compile_failures)
-
/* load (not implemented yet) */
/*
RB_DEBUG_COUNTER(load_files)
diff --git a/defs/gmake.mk b/defs/gmake.mk
index 4019eb3854..54fef6685f 100644
--- a/defs/gmake.mk
+++ b/defs/gmake.mk
@@ -19,6 +19,7 @@ INSTRUBY_ENV += SDKROOT=
endif
INSTRUBY_ARGS += --gnumake
+ifeq ($(DOT_WAIT),)
CHECK_TARGETS := great exam love check test check% test% btest%
# expand test targets, and those dependents
TEST_TARGETS := $(filter $(CHECK_TARGETS),$(MAKECMDGOALS))
@@ -26,7 +27,7 @@ TEST_DEPENDS := $(filter-out commit $(TEST_TARGETS),$(MAKECMDGOALS))
TEST_TARGETS := $(patsubst great,exam,$(TEST_TARGETS))
TEST_DEPENDS := $(filter-out great $(TEST_TARGETS),$(TEST_DEPENDS))
TEST_TARGETS := $(patsubst exam,check,$(TEST_TARGETS))
-TEST_TARGETS := $(patsubst check,test-spec test-all test-tool test-short,$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst check,test-syntax-suggest test-spec test-all test-tool test-short,$(TEST_TARGETS))
TEST_TARGETS := $(patsubst test-rubyspec,test-spec,$(TEST_TARGETS))
TEST_DEPENDS := $(filter-out exam check test-spec $(TEST_TARGETS),$(TEST_DEPENDS))
TEST_TARGETS := $(patsubst love,check,$(TEST_TARGETS))
@@ -39,15 +40,13 @@ TEST_TARGETS := $(patsubst test-short,btest-ruby test-knownbug test-basic,$(TEST
TEST_TARGETS := $(patsubst test-bundled-gems,test-bundled-gems-run,$(TEST_TARGETS))
TEST_TARGETS := $(patsubst test-bundled-gems-run,test-bundled-gems-run $(PREPARE_BUNDLED_GEMS),$(TEST_TARGETS))
TEST_TARGETS := $(patsubst test-bundled-gems-prepare,test-bundled-gems-prepare $(PRECHECK_BUNDLED_GEMS) test-bundled-gems-fetch,$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst test-syntax-suggest,test-syntax-suggest $(PREPARE_SYNTAX_SUGGEST),$(TEST_TARGETS))
TEST_DEPENDS := $(filter-out test-short $(TEST_TARGETS),$(TEST_DEPENDS))
TEST_DEPENDS += $(if $(filter great exam love check,$(MAKECMDGOALS)),all exts)
+endif
in-srcdir := $(if $(filter-out .,$(srcdir)),$(CHDIR) $(srcdir) &&)
-ifneq ($(filter -O0 -Od,$(optflags)),)
-override XCFLAGS := $(filter-out -D_FORTIFY_SOURCE=%,$(XCFLAGS))
-endif
-
ifeq ($(if $(filter all main exts enc trans libencs libenc libtrans \
prog program ruby ruby$(EXEEXT) \
wprogram rubyw rubyw$(EXEEXT) \
@@ -75,6 +74,7 @@ $(foreach arch,$(arch_flags),\
$(eval $(call archcmd,$(patsubst -arch=%,%,$(value arch)),$(patsubst -arch=%,-arch %,$(value arch)))))
endif
+ifeq ($(DOT_WAIT),)
.PHONY: $(addprefix yes-,$(TEST_TARGETS))
ifneq ($(filter-out btest%,$(TEST_TARGETS)),)
@@ -84,7 +84,8 @@ endif
ORDERED_TEST_TARGETS := $(filter $(TEST_TARGETS), \
btest-ruby test-knownbug test-basic \
test-testframework test-tool test-ruby test-all \
- test-spec test-bundler-prepare test-bundler test-bundler-parallel \
+ test-spec test-syntax-suggest-prepare test-syntax-suggest \
+ test-bundler-prepare test-bundler test-bundler-parallel \
test-bundled-gems-precheck test-bundled-gems-fetch \
test-bundled-gems-prepare test-bundled-gems-run \
)
@@ -92,6 +93,7 @@ prev_test := $(if $(filter test-spec,$(ORDERED_TEST_TARGETS)),test-spec-precheck
$(foreach test,$(ORDERED_TEST_TARGETS), \
$(eval yes-$(value test) no-$(value test): $(value prev_test)); \
$(eval prev_test := $(value test)))
+endif
ifneq ($(if $(filter install,$(MAKECMDGOALS)),$(filter uninstall,$(MAKECMDGOALS))),)
install-targets := $(filter install uninstall,$(MAKECMDGOALS))
@@ -137,7 +139,7 @@ config.status: $(wildcard config.cache)
STUBPROGRAM = rubystub$(EXEEXT)
IGNOREDPATTERNS = %~ .% %.orig %.rej \#%\#
SCRIPTBINDIR := $(if $(EXEEXT),,exec/)
-SCRIPTPROGRAMS = $(addprefix $(SCRIPTBINDIR),$(addsuffix $(EXEEXT),$(filter-out $(IGNOREDPATTERNS),$(notdir $(wildcard $(srcdir)/libexec/*)))))
+SCRIPTPROGRAMS = $(addprefix $(SCRIPTBINDIR),$(addsuffix $(EXEEXT),$(filter-out $(IGNOREDPATTERNS),$(notdir $(wildcard $(srcdir)/bin/*)))))
stub: $(STUBPROGRAM)
scriptbin: $(SCRIPTPROGRAMS)
@@ -163,9 +165,8 @@ $(SCRIPTBINDIR)%$(EXEEXT): bin/% $(STUBPROGRAM) \
$(Q) chmod +x $@
$(Q) $(POSTLINK)
-$(TIMESTAMPDIR)/.exec.time:
- $(Q) mkdir exec
- $(Q) exit > $@
+$(SCRIPTBINDIR):
+ $(Q) mkdir $@
.PHONY: commit
commit: $(if $(filter commit,$(MAKECMDGOALS)),$(filter-out commit,$(MAKECMDGOALS))) up
@@ -183,8 +184,8 @@ commit: $(if $(filter commit,$(MAKECMDGOALS)),$(filter-out commit,$(MAKECMDGOALS
GITHUB_RUBY_URL = https://github.com/ruby/ruby
PR =
-COMMIT_GPG_SIGN = $(shell git -C "$(srcdir)" config commit.gpgsign)
-REMOTE_GITHUB_URL = $(shell git -C "$(srcdir)" config remote.github.url)
+COMMIT_GPG_SIGN = $(shell $(GIT) -C "$(srcdir)" config commit.gpgsign)
+REMOTE_GITHUB_URL = $(shell $(GIT) -C "$(srcdir)" config remote.github.url)
COMMITS_NOTES = commits
.PHONY: fetch-github
@@ -197,21 +198,21 @@ define fetch-github
exit 1; \
)
$(eval REMOTE_GITHUB_URL := $(REMOTE_GITHUB_URL))
- $(if $(REMOTE_GITHUB_URL),, \
- echo adding $(GITHUB_RUBY_URL) as remote github; \
- git -C "$(srcdir)" remote add github $(GITHUB_RUBY_URL); \
- git -C "$(srcdir)" config --add remote.github.fetch +refs/notes/$(COMMITS_NOTES):refs/notes/$(COMMITS_NOTES)
- $(eval REMOTE_GITHUB_URL := $(GITHUB_RUBY_URL)) \
+ $(if $(REMOTE_GITHUB_URL),,
+ echo adding $(GITHUB_RUBY_URL) as remote github
+ $(GIT) -C "$(srcdir)" remote add github $(GITHUB_RUBY_URL)
+ $(GIT) -C "$(srcdir)" config --add remote.github.fetch +refs/notes/$(COMMITS_NOTES):refs/notes/$(COMMITS_NOTES)
+ $(eval REMOTE_GITHUB_URL := $(GITHUB_RUBY_URL))
)
- $(if $(git -C "$(srcdir)" rev-parse "github/pull/$(1)/head" -- 2> /dev/null), \
- git -C "$(srcdir)" branch -f "gh-$(1)" "github/pull/$(1)/head", \
- git -C "$(srcdir)" fetch -f github "pull/$(1)/head:gh-$(1)" \
+ $(if $(shell $(GIT) -C "$(srcdir)" rev-parse "github/pull/$(1)/head" -- 2> /dev/null),
+ $(GIT) -C "$(srcdir)" branch -f "gh-$(1)" "github/pull/$(1)/head",
+ $(GIT) -C "$(srcdir)" fetch -f github "pull/$(1)/head:gh-$(1)"
)
endef
.PHONY: checkout-github
checkout-github: fetch-github
- git -C "$(srcdir)" checkout "gh-$(PR)"
+ $(GIT) -C "$(srcdir)" checkout "gh-$(PR)"
.PHONY: update-github
update-github: fetch-github
@@ -224,31 +225,31 @@ update-github: fetch-github
$(eval PR_BRANCH := $(word 2,$(PULL_REQUEST_FORK_BRANCH)))
$(eval GITHUB_UPDATE_WORKTREE := $(shell mktemp -d "$(srcdir)/gh-$(PR)-XXXXXX"))
- git -C "$(srcdir)" worktree add $(notdir $(GITHUB_UPDATE_WORKTREE)) "gh-$(PR)"
- git -C "$(GITHUB_UPDATE_WORKTREE)" merge master --no-edit
+ $(GIT) -C "$(srcdir)" worktree add $(notdir $(GITHUB_UPDATE_WORKTREE)) "gh-$(PR)"
+ $(GIT) -C "$(GITHUB_UPDATE_WORKTREE)" merge master --no-edit
@$(BASERUBY) -e 'print "Are you sure to push this to PR=$(PR)? [Y/n]: "; exit(gets.chomp != "n")'
- git -C "$(srcdir)" remote add fork-$(PR) git@github.com:$(FORK_REPO).git
- git -C "$(GITHUB_UPDATE_WORKTREE)" push fork-$(PR) gh-$(PR):$(PR_BRANCH)
- git -C "$(srcdir)" remote rm fork-$(PR)
- git -C "$(srcdir)" worktree remove $(notdir $(GITHUB_UPDATE_WORKTREE))
- git -C "$(srcdir)" branch -D gh-$(PR)
+ $(GIT) -C "$(srcdir)" remote add fork-$(PR) git@github.com:$(FORK_REPO).git
+ $(GIT) -C "$(GITHUB_UPDATE_WORKTREE)" push fork-$(PR) gh-$(PR):$(PR_BRANCH)
+ $(GIT) -C "$(srcdir)" remote rm fork-$(PR)
+ $(GIT) -C "$(srcdir)" worktree remove $(notdir $(GITHUB_UPDATE_WORKTREE))
+ $(GIT) -C "$(srcdir)" branch -D gh-$(PR)
.PHONY: pull-github
pull-github: fetch-github
$(call pull-github,$(PR))
define pull-github
- $(eval GITHUB_MERGE_BASE := $(shell git -C "$(srcdir)" log -1 --format=format:%H))
- $(eval GITHUB_MERGE_BRANCH := $(shell git -C "$(srcdir)" symbolic-ref --short HEAD))
+ $(eval GITHUB_MERGE_BASE := $(shell $(GIT) -C "$(srcdir)" log -1 --format=format:%H))
+ $(eval GITHUB_MERGE_BRANCH := $(shell $(GIT) -C "$(srcdir)" symbolic-ref --short HEAD))
$(eval GITHUB_MERGE_WORKTREE := $(shell mktemp -d "$(srcdir)/gh-$(1)-XXXXXX"))
- git -C "$(srcdir)" worktree prune
- git -C "$(srcdir)" worktree add $(notdir $(GITHUB_MERGE_WORKTREE)) "gh-$(1)"
- git -C "$(GITHUB_MERGE_WORKTREE)" rebase $(GITHUB_MERGE_BRANCH)
+ $(GIT) -C "$(srcdir)" worktree prune
+ $(GIT) -C "$(srcdir)" worktree add $(notdir $(GITHUB_MERGE_WORKTREE)) "gh-$(1)"
+ $(GIT) -C "$(GITHUB_MERGE_WORKTREE)" rebase $(GITHUB_MERGE_BRANCH)
$(eval COMMIT_GPG_SIGN := $(COMMIT_GPG_SIGN))
$(if $(filter true,$(COMMIT_GPG_SIGN)), \
- git -C "$(GITHUB_MERGE_WORKTREE)" rebase --exec "git commit --amend --no-edit -S" "$(GITHUB_MERGE_BASE)"; \
+ $(GIT) -C "$(GITHUB_MERGE_WORKTREE)" rebase --exec "$(GIT) commit --amend --no-edit -S" "$(GITHUB_MERGE_BASE)"; \
)
- git -C "$(GITHUB_MERGE_WORKTREE)" rebase --exec "git notes add --message 'Merged: $(GITHUB_RUBY_URL)/pull/$(1)'" "$(GITHUB_MERGE_BASE)"
+ $(GIT) -C "$(GITHUB_MERGE_WORKTREE)" rebase --exec "$(GIT) notes add --message 'Merged: $(GITHUB_RUBY_URL)/pull/$(1)'" "$(GITHUB_MERGE_BASE)"
endef
.PHONY: fetch-github-%
@@ -257,23 +258,46 @@ fetch-github-%:
.PHONY: checkout-github-%
checkout-github-%: fetch-github-%
- git -C "$(srcdir)" checkout "gh-$*"
+ $(GIT) -C "$(srcdir)" checkout "gh-$*"
.PHONY: pr-% pull-github-%
pr-% pull-github-%: fetch-github-%
$(call pull-github,$*)
HELP_EXTRA_TASKS = \
- " checkout-github: checkout GitHub Pull Request [PR=1234]" \
- " pull-github: rebase GitHub Pull Request to new worktree [PR=1234]" \
- " update-github: merge master branch and push it to Pull Request [PR=1234]" \
+ " checkout-github: checkout GitHub Pull Request [PR=1234]" \
+ " pull-github: rebase GitHub Pull Request to new worktree [PR=1234]" \
+ " update-github: merge master branch and push it to Pull Request [PR=1234]" \
""
-extract-gems: $(HAVE_BASERUBY:yes=update-gems)
-
-bundled-gems := $(shell sed '/^[ ]*\#/d;/^[ ]*$$/d;s/[ ][ ]*/-/;s/[ ].*//' $(srcdir)/gems/bundled_gems)
-
-update-gems: | $(patsubst %,gems/%.gem,$(bundled-gems))
+# 1. squeeze spaces
+# 2. strip and skip comment/empty lines
+# 3. "gem x.y.z URL xxxxxx" -> "gem|x.y.z|xxxxxx|URL"
+# 4. "gem x.y.z URL" -> "gem-x.y.z"
+bundled-gems := $(shell sed \
+ -e 's/[ ][ ]*/ /g' \
+ -e 's/^ //;/\#/d;s/ *$$//;/^$$/d' \
+ $(if $(filter yes,$(HAVE_GIT)), \
+ -e 's/^\(.*\) \(.*\) \(.*\) \(.*\)/\1|\2|\4|\3/' \
+ ) \
+ -e 's/ /-/;s/ .*//' \
+ $(srcdir)/gems/bundled_gems)
+
+bundled-gems-rev := $(filter-out $(subst |,,$(bundled-gems)),$(bundled-gems))
+bundled-gems := $(filter-out $(bundled-gems-rev),$(bundled-gems))
+
+# calls $(1) with name, version, revision, URL
+foreach-bundled-gems-rev = \
+ $(foreach g,$(bundled-gems-rev),$(call foreach-bundled-gems-rev-0,$(1),$(subst |, ,$(value g))))
+foreach-bundled-gems-rev-0 = \
+ $(call $(1),$(word 1,$(2)),$(word 2,$(2)),$(word 3,$(2)),$(word 4,$(2)))
+bundled-gem-gemfile = $(srcdir)/gems/$(1)-$(2).gem
+bundled-gem-srcdir = $(srcdir)/gems/src/$(1)
+bundled-gem-extracted = $(srcdir)/.bundle/gems/$(1)-$(2)
+
+update-gems: | $(patsubst %,$(srcdir)/gems/%.gem,$(bundled-gems))
+update-gems: | $(call foreach-bundled-gems-rev,bundled-gem-gemfile)
+update-gems: | $(call foreach-bundled-gems-rev,bundled-gem-srcdir)
test-bundler-precheck: | $(srcdir)/.bundle/cache
@@ -281,7 +305,7 @@ $(srcdir)/.bundle/cache:
$(MAKEDIRS) $(@D) $(CACHE_DIR)
$(LN_S) ../.downloaded-cache $@
-gems/%.gem:
+$(srcdir)/gems/%.gem:
$(ECHO) Downloading bundled gem $*...
$(Q) $(BASERUBY) -C "$(srcdir)" \
-I./tool -rdownloader \
@@ -292,13 +316,39 @@ gems/%.gem:
-e 'File.unlink(*old) and' \
-e 'FileUtils.rm_rf(old.map{'"|n|"'n.chomp(".gem")})'
-extract-gems: | $(patsubst %,.bundle/gems/%,$(bundled-gems))
+extract-gems: | $(patsubst %,$(srcdir)/.bundle/gems/%,$(bundled-gems))
+extract-gems: | $(call foreach-bundled-gems-rev,bundled-gem-extracted)
-.bundle/gems/%: gems/%.gem | .bundle/gems
+$(srcdir)/.bundle/gems/%: $(srcdir)/gems/%.gem | .bundle/gems
$(ECHO) Extracting bundle gem $*...
$(Q) $(BASERUBY) -C "$(srcdir)" \
- -Itool -rgem-unpack \
- -e 'Gem.unpack("gems/$(@F).gem", ".bundle")'
+ -Itool/lib -rbundled_gem \
+ -e 'BundledGem.unpack("gems/$(@F).gem", ".bundle")'
+
+define copy-gem
+$(srcdir)/gems/src/$(1): | $(srcdir)/gems/src
+ $(ECHO) Cloning $(4)
+ $(Q) $(GIT) clone $(4) $$(@)
+
+$(srcdir)/.bundle/gems/$(1)-$(2): | $(srcdir)/gems/src/$(1) .bundle/gems
+ $(ECHO) Copying $(1)@$(3) to $$(@F)
+ $(Q) $(CHDIR) "$(srcdir)/gems/src/$(1)" && \
+ $(GIT) fetch origin $(3) && \
+ $(GIT) checkout --detach $(3) && \
+ :
+ $(Q) $(BASERUBY) -C "$(srcdir)" \
+ -Itool/lib -rbundled_gem \
+ -e 'BundledGem.copy("gems/src/$(1)/$(1).gemspec", ".bundle")'
+
+endef
+define copy-gem-0
+$(eval $(call copy-gem,$(1),$(2),$(3),$(4)))
+endef
+
+$(call foreach-bundled-gems-rev,copy-gem-0)
+
+$(srcdir)/gems/src:
+ $(MAKEDIRS) $@
$(srcdir)/.bundle/gems:
$(MAKEDIRS) $@
@@ -335,28 +385,22 @@ $(MJIT_MIN_HEADER): $(mjit_min_headers) $(PREP)
endif
-ifeq ($(if $(wildcard $(filter-out .,$(UNICODE_FILES) $(UNICODE_PROPERTY_FILES))),,\
- $(wildcard $(srcdir)/lib/unicode_normalize/tables.rb)),)
-# Needs the dependency when any Unicode data file exists, or
-# normalization tables script doesn't. Otherwise, when the target
-# only exists, use it as-is.
-.PHONY: $(UNICODE_SRC_DATA_DIR)/.unicode-tables.time
-UNICODE_TABLES_TIMESTAMP =
-$(UNICODE_SRC_DATA_DIR)/.unicode-tables.time: \
- $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES)
-endif
+.SECONDARY: update-unicode-files
+.SECONDARY: update-unicode-auxiliary-files
+.SECONDARY: update-unicode-ucd-emoji-files
+.SECONDARY: update-unicode-emoji-files
-ifeq ($(wildcard $(srcdir)/revision.h),)
-REVISION_IN_HEADER := none
-REVISION_LATEST := update
+ifeq ($(HAVE_GIT),yes)
+REVISION_LATEST := $(shell $(CHDIR) $(srcdir) && $(GIT) log -1 --format=%H 2>/dev/null)
else
-REVISION_IN_HEADER := $(shell sed -n 's/^\#define RUBY_FULL_REVISION "\(.*\)"/\1/p' $(srcdir)/revision.h 2>/dev/null)
-REVISION_LATEST := $(shell $(CHDIR) $(srcdir) && git log -1 --format=%H 2>/dev/null)
+REVISION_LATEST := update
+endif
+REVISION_IN_HEADER := $(shell sed -n 's/^\#define RUBY_FULL_REVISION "\(.*\)"/\1/p' $(wildcard $(srcdir)/revision.h revision.h) /dev/null 2>/dev/null)
+ifeq ($(REVISION_IN_HEADER),)
+REVISION_IN_HEADER := none
endif
ifneq ($(REVISION_IN_HEADER),$(REVISION_LATEST))
-# GNU make treat the target as unmodified when its dependents get
-# updated but it is not updated, while others may not.
-$(srcdir)/revision.h: $(REVISION_H)
+$(REVISION_H): PHONY
endif
include $(top_srcdir)/yjit/yjit.mk
@@ -403,19 +447,19 @@ endif
update-deps:
$(eval update_deps := $(shell date +update-deps-%Y%m%d))
$(eval deps_dir := $(shell mktemp -d)/$(update_deps))
- $(eval GIT_DIR := $(shell git -C $(srcdir) rev-parse --absolute-git-dir))
- git --git-dir=$(GIT_DIR) worktree add $(deps_dir)
+ $(eval GIT_DIR := $(shell $(GIT) -C $(srcdir) rev-parse --absolute-git-dir))
+ $(GIT) --git-dir=$(GIT_DIR) worktree add $(deps_dir)
cp $(tooldir)/config.guess $(tooldir)/config.sub $(deps_dir)/tool
[ -f config.status ] && cp config.status $(deps_dir)
cd $(deps_dir) && autoconf && \
exec ./configure -q -C --enable-load-relative --disable-install-doc --disable-rubygems 'optflags=-O0' 'debugflags=-save-temps=obj -g'
$(RUNRUBY) -C $(deps_dir) tool/update-deps --fix
- git -C $(deps_dir) diff --no-ext-diff --ignore-submodules --exit-code || \
- git -C $(deps_dir) commit --all --message='Update dependencies'
- git --git-dir=$(GIT_DIR) worktree remove $(deps_dir)
+ $(GIT) -C $(deps_dir) diff --no-ext-diff --ignore-submodules --exit-code || \
+ $(GIT) -C $(deps_dir) commit --all --message='Update dependencies'
+ $(GIT) --git-dir=$(GIT_DIR) worktree remove $(deps_dir)
$(RMDIR) $(dir $(deps_dir))
- git --git-dir=$(GIT_DIR) merge --no-edit --ff-only $(update_deps)
- git --git-dir=$(GIT_DIR) branch --delete $(update_deps)
+ $(GIT) --git-dir=$(GIT_DIR) merge --no-edit --ff-only $(update_deps)
+ $(GIT) --git-dir=$(GIT_DIR) branch --delete $(update_deps)
# order-only-prerequisites doesn't work for $(RUBYSPEC_CAPIEXT)
# because the same named directory exists in the source tree.
diff --git a/defs/id.def b/defs/id.def
index 097e34e405..ebf00506ea 100644
--- a/defs/id.def
+++ b/defs/id.def
@@ -58,6 +58,7 @@ firstline, predefined = __LINE__+1, %[\
quo
name
nil
+ path
_ UScore
@@ -75,6 +76,7 @@ firstline, predefined = __LINE__+1, %[\
"/*NULL*/" NULL
empty?
eql?
+ default
respond_to? Respond_to
respond_to_missing? Respond_to_missing
<IFUNC>
@@ -194,13 +196,14 @@ predefined.split(/^/).each_with_index do |line, num|
end << token
predefined_ids[token] = name
end
+index = 127
token_ops.split(/^/).each do |line|
next if /^#/ =~ line
line.sub!(/\s+#.*/, '')
id, op, token = line.split
next unless id and op
token ||= (id unless /\A\W\z/ =~ op)
- token_op_ids << [id, op, token]
+ token_op_ids << [id, op, token, (index += 1 if token)]
end
{
"LOCAL" => local_ids,
@@ -212,4 +215,5 @@ end
:preserved => preserved_ids,
:predefined => predefined_ids,
:token_op => token_op_ids,
+ :last_token => index,
}
diff --git a/defs/keywords b/defs/keywords
index fc30ec2d15..a1b1f4f60f 100644
--- a/defs/keywords
+++ b/defs/keywords
@@ -2,7 +2,7 @@
struct kwtable {short name, id[2], state;};
const struct kwtable *rb_reserved_word(const char *, unsigned int);
#ifndef RIPPER
-static const struct kwtable *reserved_word(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/);
+static const struct kwtable *reserved_word(register const char *str, register size_t len);
#define rb_reserved_word(str, len) reserved_word(str, len)
%}
diff --git a/defs/lex.c.src b/defs/lex.c.src
index fc30ec2d15..a1b1f4f60f 100644
--- a/defs/lex.c.src
+++ b/defs/lex.c.src
@@ -2,7 +2,7 @@
struct kwtable {short name, id[2], state;};
const struct kwtable *rb_reserved_word(const char *, unsigned int);
#ifndef RIPPER
-static const struct kwtable *reserved_word(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/);
+static const struct kwtable *reserved_word(register const char *str, register size_t len);
#define rb_reserved_word(str, len) reserved_word(str, len)
%}
diff --git a/dir.c b/dir.c
index 77487c8426..3f73f83fc5 100644
--- a/dir.c
+++ b/dir.c
@@ -77,9 +77,9 @@ char *strchr(char*,char);
#endif
#define USE_NAME_ON_FS_REAL_BASENAME 1 /* platform dependent APIs to
- * get real basenames */
+ * get real basenames */
#define USE_NAME_ON_FS_BY_FNMATCH 2 /* select the matching
- * basename by fnmatch */
+ * basename by fnmatch */
#ifdef HAVE_GETATTRLIST
# define USE_NAME_ON_FS USE_NAME_ON_FS_REAL_BASENAME
@@ -160,12 +160,12 @@ need_normalization(DIR *dirp, const char *path)
int ret = getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0);
# endif
if (!ret) {
- const fsobj_tag_t *tag = (void *)(attrbuf+1);
- switch (*tag) {
- case VT_HFS:
- case VT_CIFS:
- return TRUE;
- }
+ const fsobj_tag_t *tag = (void *)(attrbuf+1);
+ switch (*tag) {
+ case VT_HFS:
+ case VT_CIFS:
+ return TRUE;
+ }
}
# endif
return FALSE;
@@ -175,9 +175,9 @@ static inline int
has_nonascii(const char *ptr, size_t len)
{
while (len > 0) {
- if (!ISASCII(*ptr)) return 1;
- ptr++;
- --len;
+ if (!ISASCII(*ptr)) return 1;
+ ptr++;
+ --len;
}
return 0;
}
@@ -254,53 +254,53 @@ bracket(
if (p >= pend) return NULL;
if (*p == '!' || *p == '^') {
- not = 1;
- p++;
+ not = 1;
+ p++;
}
while (*p != ']') {
- const char *t1 = p;
- if (escape && *t1 == '\\')
- t1++;
- if (!*t1)
- return NULL;
- p = t1 + (r = rb_enc_mbclen(t1, pend, enc));
- if (p >= pend) return NULL;
- if (p[0] == '-' && p[1] != ']') {
- const char *t2 = p + 1;
- int r2;
- if (escape && *t2 == '\\')
- t2++;
- if (!*t2)
- return NULL;
- p = t2 + (r2 = rb_enc_mbclen(t2, pend, enc));
- if (ok) continue;
- if ((r <= (send-s) && memcmp(t1, s, r) == 0) ||
- (r2 <= (send-s) && memcmp(t2, s, r2) == 0)) {
- ok = 1;
- continue;
- }
- c1 = rb_enc_codepoint(s, send, enc);
- if (nocase) c1 = rb_enc_toupper(c1, enc);
- c2 = rb_enc_codepoint(t1, pend, enc);
- if (nocase) c2 = rb_enc_toupper(c2, enc);
- if (c1 < c2) continue;
- c2 = rb_enc_codepoint(t2, pend, enc);
- if (nocase) c2 = rb_enc_toupper(c2, enc);
- if (c1 > c2) continue;
- }
- else {
- if (ok) continue;
- if (r <= (send-s) && memcmp(t1, s, r) == 0) {
- ok = 1;
- continue;
- }
- if (!nocase) continue;
- c1 = rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc);
- c2 = rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc);
- if (c1 != c2) continue;
- }
- ok = 1;
+ const char *t1 = p;
+ if (escape && *t1 == '\\')
+ t1++;
+ if (!*t1)
+ return NULL;
+ p = t1 + (r = rb_enc_mbclen(t1, pend, enc));
+ if (p >= pend) return NULL;
+ if (p[0] == '-' && p[1] != ']') {
+ const char *t2 = p + 1;
+ int r2;
+ if (escape && *t2 == '\\')
+ t2++;
+ if (!*t2)
+ return NULL;
+ p = t2 + (r2 = rb_enc_mbclen(t2, pend, enc));
+ if (ok) continue;
+ if ((r <= (send-s) && memcmp(t1, s, r) == 0) ||
+ (r2 <= (send-s) && memcmp(t2, s, r2) == 0)) {
+ ok = 1;
+ continue;
+ }
+ c1 = rb_enc_codepoint(s, send, enc);
+ if (nocase) c1 = rb_enc_toupper(c1, enc);
+ c2 = rb_enc_codepoint(t1, pend, enc);
+ if (nocase) c2 = rb_enc_toupper(c2, enc);
+ if (c1 < c2) continue;
+ c2 = rb_enc_codepoint(t2, pend, enc);
+ if (nocase) c2 = rb_enc_toupper(c2, enc);
+ if (c1 > c2) continue;
+ }
+ else {
+ if (ok) continue;
+ if (r <= (send-s) && memcmp(t1, s, r) == 0) {
+ ok = 1;
+ continue;
+ }
+ if (!nocase) continue;
+ c1 = rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc);
+ c2 = rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc);
+ if (c1 != c2) continue;
+ }
+ ok = 1;
}
return ok == not ? NULL : (char *)p + 1;
@@ -338,72 +338,72 @@ fnmatch_helper(
int r;
if (period && *s == '.' && *UNESCAPE(p) != '.') /* leading period */
- RETURN(FNM_NOMATCH);
+ RETURN(FNM_NOMATCH);
while (1) {
- switch (*p) {
- case '*':
- do { p++; } while (*p == '*');
- if (ISEND(UNESCAPE(p))) {
- p = UNESCAPE(p);
- RETURN(0);
- }
- if (ISEND(s))
- RETURN(FNM_NOMATCH);
- ptmp = p;
- stmp = s;
- continue;
-
- case '?':
- if (ISEND(s))
- RETURN(FNM_NOMATCH);
- p++;
- Inc(s, send, enc);
- continue;
-
- case '[': {
- const char *t;
- if (ISEND(s))
- RETURN(FNM_NOMATCH);
- if ((t = bracket(p + 1, pend, s, send, flags, enc)) != 0) {
- p = t;
- Inc(s, send, enc);
- continue;
- }
- goto failed;
- }
- }
-
- /* ordinary */
- p = UNESCAPE(p);
- if (ISEND(s))
- RETURN(ISEND(p) ? 0 : FNM_NOMATCH);
- if (ISEND(p))
- goto failed;
- r = rb_enc_precise_mbclen(p, pend, enc);
- if (!MBCLEN_CHARFOUND_P(r))
- goto failed;
- if (r <= (send-s) && memcmp(p, s, r) == 0) {
- p += r;
- s += r;
- continue;
- }
- if (!nocase) goto failed;
- if (rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc) !=
- rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc))
- goto failed;
- p += r;
- Inc(s, send, enc);
- continue;
+ switch (*p) {
+ case '*':
+ do { p++; } while (*p == '*');
+ if (ISEND(UNESCAPE(p))) {
+ p = UNESCAPE(p);
+ RETURN(0);
+ }
+ if (ISEND(s))
+ RETURN(FNM_NOMATCH);
+ ptmp = p;
+ stmp = s;
+ continue;
+
+ case '?':
+ if (ISEND(s))
+ RETURN(FNM_NOMATCH);
+ p++;
+ Inc(s, send, enc);
+ continue;
+
+ case '[': {
+ const char *t;
+ if (ISEND(s))
+ RETURN(FNM_NOMATCH);
+ if ((t = bracket(p + 1, pend, s, send, flags, enc)) != 0) {
+ p = t;
+ Inc(s, send, enc);
+ continue;
+ }
+ goto failed;
+ }
+ }
+
+ /* ordinary */
+ p = UNESCAPE(p);
+ if (ISEND(s))
+ RETURN(ISEND(p) ? 0 : FNM_NOMATCH);
+ if (ISEND(p))
+ goto failed;
+ r = rb_enc_precise_mbclen(p, pend, enc);
+ if (!MBCLEN_CHARFOUND_P(r))
+ goto failed;
+ if (r <= (send-s) && memcmp(p, s, r) == 0) {
+ p += r;
+ s += r;
+ continue;
+ }
+ if (!nocase) goto failed;
+ if (rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc) !=
+ rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc))
+ goto failed;
+ p += r;
+ Inc(s, send, enc);
+ continue;
failed: /* try next '*' position */
- if (ptmp && stmp) {
- p = ptmp;
- Inc(stmp, send, enc); /* !ISEND(*stmp) */
- s = stmp;
- continue;
- }
- RETURN(FNM_NOMATCH);
+ if (ptmp && stmp) {
+ p = ptmp;
+ Inc(stmp, send, enc); /* !ISEND(*stmp) */
+ s = stmp;
+ continue;
+ }
+ RETURN(FNM_NOMATCH);
}
}
@@ -424,37 +424,37 @@ fnmatch(
const char *stmp = 0;
if (pathname) {
- while (1) {
- if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
- do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
- ptmp = p;
- stmp = s;
- }
- if (fnmatch_helper(&p, &s, flags, enc) == 0) {
- while (*s && *s != '/') Inc(s, send, enc);
- if (*p && *s) {
- p++;
- s++;
- continue;
- }
- if (!*p && !*s)
- return 0;
- }
- /* failed : try next recursion */
- if (ptmp && stmp && !(period && *stmp == '.')) {
- while (*stmp && *stmp != '/') Inc(stmp, send, enc);
- if (*stmp) {
- p = ptmp;
- stmp++;
- s = stmp;
- continue;
- }
- }
- return FNM_NOMATCH;
- }
+ while (1) {
+ if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
+ do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
+ ptmp = p;
+ stmp = s;
+ }
+ if (fnmatch_helper(&p, &s, flags, enc) == 0) {
+ while (*s && *s != '/') Inc(s, send, enc);
+ if (*p && *s) {
+ p++;
+ s++;
+ continue;
+ }
+ if (!*p && !*s)
+ return 0;
+ }
+ /* failed : try next recursion */
+ if (ptmp && stmp && !(period && *stmp == '.')) {
+ while (*stmp && *stmp != '/') Inc(stmp, send, enc);
+ if (*stmp) {
+ p = ptmp;
+ stmp++;
+ s = stmp;
+ continue;
+ }
+ }
+ return FNM_NOMATCH;
+ }
}
else
- return fnmatch_helper(&p, &s, flags, enc);
+ return fnmatch_helper(&p, &s, flags, enc);
}
VALUE rb_cDir;
@@ -520,14 +520,14 @@ static DIR *
opendir_without_gvl(const char *path)
{
if (vm_initialized) {
- union { const void *in; void *out; } u;
+ union { const void *in; void *out; } u;
- u.in = path;
+ u.in = path;
- return rb_thread_call_without_gvl(nogvl_opendir, u.out, RUBY_UBF_IO, 0);
+ return rb_thread_call_without_gvl(nogvl_opendir, u.out, RUBY_UBF_IO, 0);
}
else
- return opendir(path);
+ return opendir(path);
}
static VALUE
@@ -551,23 +551,23 @@ dir_initialize(rb_execution_context_t *ec, VALUE dir, VALUE dirname, VALUE enc)
path = RSTRING_PTR(dirname);
dp->dir = opendir_without_gvl(path);
if (dp->dir == NULL) {
- int e = errno;
- if (rb_gc_for_fd(e)) {
- dp->dir = opendir_without_gvl(path);
- }
+ int e = errno;
+ if (rb_gc_for_fd(e)) {
+ dp->dir = opendir_without_gvl(path);
+ }
#ifdef HAVE_GETATTRLIST
- else if (e == EIO) {
- u_int32_t attrbuf[1];
- struct attrlist al = {ATTR_BIT_MAP_COUNT, 0};
- if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW) == 0) {
- dp->dir = opendir_without_gvl(path);
- }
- }
-#endif
- if (dp->dir == NULL) {
- RB_GC_GUARD(dirname);
- rb_syserr_fail_path(e, orig);
- }
+ else if (e == EIO) {
+ u_int32_t attrbuf[1];
+ struct attrlist al = {ATTR_BIT_MAP_COUNT, 0};
+ if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW) == 0) {
+ dp->dir = opendir_without_gvl(path);
+ }
+ }
+#endif
+ if (dp->dir == NULL) {
+ RB_GC_GUARD(dirname);
+ rb_syserr_fail_path(e, orig);
+ }
}
RB_OBJ_WRITE(dir, &dp->path, orig);
@@ -630,12 +630,12 @@ dir_inspect(VALUE dir)
TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dirp);
if (!NIL_P(dirp->path)) {
- VALUE str = rb_str_new_cstr("#<");
- rb_str_append(str, rb_class_name(CLASS_OF(dir)));
- rb_str_cat2(str, ":");
- rb_str_append(str, dirp->path);
- rb_str_cat2(str, ">");
- return str;
+ VALUE str = rb_str_new_cstr("#<");
+ rb_str_append(str, rb_class_name(CLASS_OF(dir)));
+ rb_str_cat2(str, ":");
+ rb_str_append(str, dirp->path);
+ rb_str_cat2(str, ">");
+ return str;
}
return rb_funcallv(dir, idTo_s, 0, 0);
}
@@ -677,7 +677,7 @@ dir_fileno(VALUE dir)
GetDIR(dir, dirp);
fd = dirfd(dirp->dir);
if (fd == -1)
- rb_sys_fail("dirfd");
+ rb_sys_fail("dirfd");
return INT2NUM(fd);
}
#else
@@ -712,9 +712,9 @@ fundamental_encoding_p(rb_encoding *enc)
case ENCINDEX_ASCII_8BIT:
case ENCINDEX_US_ASCII:
case ENCINDEX_UTF_8:
- return TRUE;
+ return TRUE;
default:
- return FALSE;
+ return FALSE;
}
}
# define READDIR(dir, enc) rb_w32_readdir((dir), (enc))
@@ -731,11 +731,11 @@ to_be_skipped(const struct dirent *dp)
#ifdef HAVE_DIRENT_NAMLEN
switch (NAMLEN(dp)) {
case 2:
- if (name[1] != '.') return FALSE;
+ if (name[1] != '.') return FALSE;
case 1:
- return TRUE;
+ return TRUE;
default:
- break;
+ break;
}
#else
if (!name[1]) return TRUE;
@@ -766,12 +766,12 @@ dir_read(VALUE dir)
GetDIR(dir, dirp);
errno = 0;
if ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
- return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc);
+ return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc);
}
else {
- int e = errno;
- if (e != 0) rb_syserr_fail(e, 0);
- return Qnil; /* end of stream */
+ int e = errno;
+ if (e != 0) rb_syserr_fail(e, 0);
+ return Qnil; /* end of stream */
}
}
@@ -821,23 +821,23 @@ dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_o
rewinddir(dirp->dir);
IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp->dir, RSTRING_PTR(dirp->path)));
while ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
- const char *name = dp->d_name;
- size_t namlen = NAMLEN(dp);
- VALUE path;
-
- if (children_only && name[0] == '.') {
- if (namlen == 1) continue; /* current directory */
- if (namlen == 2 && name[1] == '.') continue; /* parent directory */
- }
+ const char *name = dp->d_name;
+ size_t namlen = NAMLEN(dp);
+ VALUE path;
+
+ if (children_only && name[0] == '.') {
+ if (namlen == 1) continue; /* current directory */
+ if (namlen == 2 && name[1] == '.') continue; /* parent directory */
+ }
#if NORMALIZE_UTF8PATH
- if (norm_p && has_nonascii(name, namlen) &&
- !NIL_P(path = rb_str_normalize_ospath(name, namlen))) {
- path = rb_external_str_with_enc(path, dirp->enc);
- }
- else
+ if (norm_p && has_nonascii(name, namlen) &&
+ !NIL_P(path = rb_str_normalize_ospath(name, namlen))) {
+ path = rb_external_str_with_enc(path, dirp->enc);
+ }
+ else
#endif
- path = rb_external_str_new_with_enc(name, namlen, dirp->enc);
- (*each)(arg, path);
+ path = rb_external_str_new_with_enc(name, namlen, dirp->enc);
+ (*each)(arg, path);
}
return dir;
}
@@ -978,7 +978,7 @@ static void
dir_chdir(VALUE path)
{
if (chdir(RSTRING_PTR(path)) < 0)
- rb_sys_fail_path(path);
+ rb_sys_fail_path(path);
}
static int chdir_blocking = 0;
@@ -997,7 +997,7 @@ chdir_yield(VALUE v)
args->done = TRUE;
chdir_blocking++;
if (NIL_P(chdir_thread))
- chdir_thread = rb_thread_current();
+ chdir_thread = rb_thread_current();
return rb_yield(args->new_path);
}
@@ -1006,10 +1006,10 @@ chdir_restore(VALUE v)
{
struct chdir_data *args = (void *)v;
if (args->done) {
- chdir_blocking--;
- if (chdir_blocking == 0)
- chdir_thread = Qnil;
- dir_chdir(args->old_path);
+ chdir_blocking--;
+ if (chdir_blocking == 0)
+ chdir_thread = Qnil;
+ dir_chdir(args->old_path);
}
return Qnil;
}
@@ -1063,35 +1063,35 @@ dir_s_chdir(int argc, VALUE *argv, VALUE obj)
path = rb_str_encode_ospath(rb_get_path(argv[0]));
}
else {
- const char *dist = getenv("HOME");
- if (!dist) {
- dist = getenv("LOGDIR");
- if (!dist) rb_raise(rb_eArgError, "HOME/LOGDIR not set");
- }
- path = rb_str_new2(dist);
+ const char *dist = getenv("HOME");
+ if (!dist) {
+ dist = getenv("LOGDIR");
+ if (!dist) rb_raise(rb_eArgError, "HOME/LOGDIR not set");
+ }
+ path = rb_str_new2(dist);
}
if (chdir_blocking > 0) {
- if (rb_thread_current() != chdir_thread)
+ if (rb_thread_current() != chdir_thread)
rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block");
if (!rb_block_given_p())
rb_warn("conflicting chdir during another chdir block");
}
if (rb_block_given_p()) {
- struct chdir_data args;
+ struct chdir_data args;
- args.old_path = rb_str_encode_ospath(rb_dir_getwd());
- args.new_path = path;
- args.done = FALSE;
- return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
+ args.old_path = rb_str_encode_ospath(rb_dir_getwd());
+ args.new_path = path;
+ args.done = FALSE;
+ return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
}
else {
- char *p = RSTRING_PTR(path);
- int r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_chdir, p,
- RUBY_UBF_IO, 0);
- if (r < 0)
- rb_sys_fail_path(path);
+ char *p = RSTRING_PTR(path);
+ int r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_chdir, p,
+ RUBY_UBF_IO, 0);
+ if (r < 0)
+ rb_sys_fail_path(path);
}
return INT2FIX(0);
@@ -1131,12 +1131,12 @@ rb_dir_getwd(void)
switch (fsenc) {
case ENCINDEX_US_ASCII:
- fsenc = ENCINDEX_ASCII_8BIT;
+ fsenc = ENCINDEX_ASCII_8BIT;
case ENCINDEX_ASCII_8BIT:
- break;
+ break;
#if defined _WIN32 || defined __APPLE__
default:
- return rb_str_conv_enc(cwd, NULL, fs);
+ return rb_str_conv_enc(cwd, NULL, fs);
#endif
}
return rb_enc_associate_index(cwd, fsenc);
@@ -1174,8 +1174,8 @@ check_dirname(VALUE dir)
pend = path + len;
pend = rb_enc_path_end(rb_enc_path_skip_prefix(path, pend, enc), pend, enc);
if (pend - path < len) {
- d = rb_str_subseq(d, 0, pend - path);
- StringValueCStr(d);
+ d = rb_str_subseq(d, 0, pend - path);
+ StringValueCStr(d);
}
return rb_str_encode_ospath(d);
}
@@ -1195,7 +1195,7 @@ dir_s_chroot(VALUE dir, VALUE path)
{
path = check_dirname(path);
if (chroot(RSTRING_PTR(path)) == -1)
- rb_sys_fail_path(path);
+ rb_sys_fail_path(path);
return INT2FIX(0);
}
@@ -1238,17 +1238,17 @@ dir_s_mkdir(int argc, VALUE *argv, VALUE obj)
int r;
if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) {
- m.mode = NUM2MODET(vmode);
+ m.mode = NUM2MODET(vmode);
}
else {
- m.mode = 0777;
+ m.mode = 0777;
}
path = check_dirname(path);
m.path = RSTRING_PTR(path);
r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_mkdir, &m, RUBY_UBF_IO, 0);
if (r < 0)
- rb_sys_fail_path(path);
+ rb_sys_fail_path(path);
return INT2FIX(0);
}
@@ -1280,7 +1280,7 @@ dir_s_rmdir(VALUE obj, VALUE dir)
p = RSTRING_PTR(dir);
r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_rmdir, (void *)p, RUBY_UBF_IO, 0);
if (r < 0)
- rb_sys_fail_path(dir);
+ rb_sys_fail_path(dir);
return INT2FIX(0);
}
@@ -1386,8 +1386,8 @@ at_subpath(int fd, size_t baselen, const char *path)
{
#if USE_OPENDIR_AT
if (fd != (int)AT_FDCWD && baselen > 0) {
- path += baselen;
- if (*path == '/') ++path;
+ path += baselen;
+ if (*path == '/') ++path;
}
#endif
return *path ? path : ".";
@@ -1403,7 +1403,7 @@ do_stat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, r
int ret = STAT(path, pst);
#endif
if (ret < 0 && !to_be_ignored(errno))
- sys_warning(path, enc);
+ sys_warning(path, enc);
return ret;
}
@@ -1418,7 +1418,7 @@ do_lstat(int fd, size_t baselen, const char *path, struct stat *pst, int flags,
int ret = lstat(path, pst);
#endif
if (ret < 0 && !to_be_ignored(errno))
- sys_warning(path, enc);
+ sys_warning(path, enc);
return ret;
}
@@ -1443,9 +1443,9 @@ static int
gc_for_fd_with_gvl(int e)
{
if (vm_initialized)
- return (int)(VALUE)rb_thread_call_with_gvl(with_gvl_gc_for_fd, &e);
+ return (int)(VALUE)rb_thread_call_with_gvl(with_gvl_gc_for_fd, &e);
else
- return RBOOL(rb_gc_for_fd(e));
+ return RBOOL(rb_gc_for_fd(e));
}
static void *
@@ -1457,32 +1457,32 @@ nogvl_opendir_at(void *ptr)
#if USE_OPENDIR_AT
const int opendir_flags = (O_RDONLY|O_CLOEXEC|
# ifdef O_DIRECTORY
- O_DIRECTORY|
+ O_DIRECTORY|
# endif /* O_DIRECTORY */
- 0);
+ 0);
int fd = openat(oaa->basefd, oaa->path, opendir_flags);
dirp = fd >= 0 ? fdopendir(fd) : 0;
if (!dirp) {
- int e = errno;
-
- switch (gc_for_fd_with_gvl(e)) {
- default:
- if (fd < 0) fd = openat(oaa->basefd, oaa->path, opendir_flags);
- if (fd >= 0) dirp = fdopendir(fd);
- if (dirp) return dirp;
-
- e = errno;
- /* fallthrough*/
- case 0:
- if (fd >= 0) close(fd);
- errno = e;
- }
+ int e = errno;
+
+ switch (gc_for_fd_with_gvl(e)) {
+ default:
+ if (fd < 0) fd = openat(oaa->basefd, oaa->path, opendir_flags);
+ if (fd >= 0) dirp = fdopendir(fd);
+ if (dirp) return dirp;
+
+ e = errno;
+ /* fallthrough*/
+ case 0:
+ if (fd >= 0) close(fd);
+ errno = e;
+ }
}
#else /* !USE_OPENDIR_AT */
dirp = opendir(oaa->path);
if (!dirp && gc_for_fd_with_gvl(errno))
- dirp = opendir(oaa->path);
+ dirp = opendir(oaa->path);
#endif /* !USE_OPENDIR_AT */
return dirp;
@@ -1497,37 +1497,37 @@ opendir_at(int basefd, const char *path)
oaa.path = path;
if (vm_initialized)
- return rb_thread_call_without_gvl(nogvl_opendir_at, &oaa, RUBY_UBF_IO, 0);
+ return rb_thread_call_without_gvl(nogvl_opendir_at, &oaa, RUBY_UBF_IO, 0);
else
- return nogvl_opendir_at(&oaa);
+ return nogvl_opendir_at(&oaa);
}
static DIR *
do_opendir(const int basefd, size_t baselen, const char *path, int flags, rb_encoding *enc,
- ruby_glob_errfunc *errfunc, VALUE arg, int *status)
+ ruby_glob_errfunc *errfunc, VALUE arg, int *status)
{
DIR *dirp;
#ifdef _WIN32
VALUE tmp = 0;
if (!fundamental_encoding_p(enc)) {
- tmp = rb_enc_str_new(path, strlen(path), enc);
- tmp = rb_str_encode_ospath(tmp);
- path = RSTRING_PTR(tmp);
+ tmp = rb_enc_str_new(path, strlen(path), enc);
+ tmp = rb_str_encode_ospath(tmp);
+ path = RSTRING_PTR(tmp);
}
#endif
dirp = opendir_at(basefd, at_subpath(basefd, baselen, path));
if (!dirp) {
- int e = errno;
-
- *status = 0;
- if (!to_be_ignored(e)) {
- if (errfunc) {
- *status = (*errfunc)(path, arg, enc, e);
- }
- else {
- sys_warning(path, enc);
- }
- }
+ int e = errno;
+
+ *status = 0;
+ if (!to_be_ignored(e)) {
+ if (errfunc) {
+ *status = (*errfunc)(path, arg, enc, e);
+ }
+ else {
+ sys_warning(path, enc);
+ }
+ }
}
#ifdef _WIN32
if (tmp) rb_str_resize(tmp, 0); /* GC guard */
@@ -1550,37 +1550,37 @@ has_magic(const char *p, const char *pend, int flags, rb_encoding *enc)
register char c;
while (p < pend && (c = *p++) != 0) {
- switch (c) {
- case '{':
- return BRACE;
+ switch (c) {
+ case '{':
+ return BRACE;
- case '*':
- case '?':
- case '[':
- hasmagical = 1;
- break;
+ case '*':
+ case '?':
+ case '[':
+ hasmagical = 1;
+ break;
- case '\\':
- if (escape && p++ >= pend)
- continue;
- break;
+ case '\\':
+ if (escape && p++ >= pend)
+ continue;
+ break;
#ifdef _WIN32
- case '.':
- break;
+ case '.':
+ break;
- case '~':
- hasalpha = 1;
- break;
+ case '~':
+ hasalpha = 1;
+ break;
#endif
- default:
- if (IS_WIN32 || ISALPHA(c)) {
- hasalpha = 1;
- }
- break;
- }
+ default:
+ if (IS_WIN32 || ISALPHA(c)) {
+ hasalpha = 1;
+ }
+ break;
+ }
- p = Next(p-1, pend, enc);
+ p = Next(p-1, pend, enc);
}
return hasmagical ? MAGICAL : hasalpha ? ALPHA : PLAIN;
@@ -1596,33 +1596,33 @@ find_dirsep(const char *p, const char *pend, int flags, rb_encoding *enc)
int open = 0;
while ((c = *p++) != 0) {
- switch (c) {
- case '[':
- open = 1;
- continue;
- case ']':
- open = 0;
- continue;
-
- case '{':
- open = 1;
- continue;
- case '}':
- open = 0;
- continue;
-
- case '/':
- if (!open)
- return (char *)p-1;
- continue;
-
- case '\\':
- if (escape && !(c = *p++))
- return (char *)p-1;
- continue;
- }
-
- p = Next(p-1, pend, enc);
+ switch (c) {
+ case '[':
+ open = 1;
+ continue;
+ case ']':
+ open = 0;
+ continue;
+
+ case '{':
+ open = 1;
+ continue;
+ case '}':
+ open = 0;
+ continue;
+
+ case '/':
+ if (!open)
+ return (char *)p-1;
+ continue;
+
+ case '\\':
+ if (escape && !(c = *p++))
+ return (char *)p-1;
+ continue;
+ }
+
+ p = Next(p-1, pend, enc);
}
return (char *)p-1;
@@ -1636,20 +1636,20 @@ remove_backslashes(char *p, register const char *pend, rb_encoding *enc)
char *s = p;
while (*p) {
- if (*p == '\\') {
- if (t != s)
- memmove(t, s, p - s);
- t += p - s;
- s = ++p;
- if (!*p) break;
- }
- Inc(p, pend, enc);
+ if (*p == '\\') {
+ if (t != s)
+ memmove(t, s, p - s);
+ t += p - s;
+ s = ++p;
+ if (!*p) break;
+ }
+ Inc(p, pend, enc);
}
while (*p++);
if (t != s)
- memmove(t, s, p - s); /* move '\0' too */
+ memmove(t, s, p - s); /* move '\0' too */
return p;
}
@@ -1670,49 +1670,49 @@ glob_make_pattern(const char *p, const char *e, int flags, rb_encoding *enc)
int recursive = 0;
while (p < e && *p) {
- tmp = GLOB_ALLOC(struct glob_pattern);
- if (!tmp) goto error;
- if (p + 2 < e && p[0] == '*' && p[1] == '*' && p[2] == '/') {
- /* fold continuous RECURSIVEs (needed in glob_helper) */
- do { p += 3; while (*p == '/') p++; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
- tmp->type = RECURSIVE;
- tmp->str = 0;
- dirsep = 1;
- recursive = 1;
- }
- else {
- const char *m = find_dirsep(p, e, flags, enc);
- const enum glob_pattern_type magic = has_magic(p, m, flags, enc);
- const enum glob_pattern_type non_magic = (USE_NAME_ON_FS || FNM_SYSCASE) ? PLAIN : ALPHA;
- char *buf;
-
- if (!(FNM_SYSCASE || magic > non_magic) && !recursive && *m) {
- const char *m2;
- while (has_magic(m+1, m2 = find_dirsep(m+1, e, flags, enc), flags, enc) <= non_magic &&
- *m2) {
- m = m2;
- }
- }
- buf = GLOB_ALLOC_N(char, m-p+1);
- if (!buf) {
- GLOB_FREE(tmp);
- goto error;
- }
- memcpy(buf, p, m-p);
- buf[m-p] = '\0';
- tmp->type = magic > MAGICAL ? MAGICAL : magic > non_magic ? magic : PLAIN;
- tmp->str = buf;
- if (*m) {
- dirsep = 1;
- p = m + 1;
- }
- else {
- dirsep = 0;
- p = m;
- }
- }
- *tail = tmp;
- tail = &tmp->next;
+ tmp = GLOB_ALLOC(struct glob_pattern);
+ if (!tmp) goto error;
+ if (p + 2 < e && p[0] == '*' && p[1] == '*' && p[2] == '/') {
+ /* fold continuous RECURSIVEs (needed in glob_helper) */
+ do { p += 3; while (*p == '/') p++; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
+ tmp->type = RECURSIVE;
+ tmp->str = 0;
+ dirsep = 1;
+ recursive = 1;
+ }
+ else {
+ const char *m = find_dirsep(p, e, flags, enc);
+ const enum glob_pattern_type magic = has_magic(p, m, flags, enc);
+ const enum glob_pattern_type non_magic = (USE_NAME_ON_FS || FNM_SYSCASE) ? PLAIN : ALPHA;
+ char *buf;
+
+ if (!(FNM_SYSCASE || magic > non_magic) && !recursive && *m) {
+ const char *m2;
+ while (has_magic(m+1, m2 = find_dirsep(m+1, e, flags, enc), flags, enc) <= non_magic &&
+ *m2) {
+ m = m2;
+ }
+ }
+ buf = GLOB_ALLOC_N(char, m-p+1);
+ if (!buf) {
+ GLOB_FREE(tmp);
+ goto error;
+ }
+ memcpy(buf, p, m-p);
+ buf[m-p] = '\0';
+ tmp->type = magic > MAGICAL ? MAGICAL : magic > non_magic ? magic : PLAIN;
+ tmp->str = buf;
+ if (*m) {
+ dirsep = 1;
+ p = m + 1;
+ }
+ else {
+ dirsep = 0;
+ p = m;
+ }
+ }
+ *tail = tmp;
+ tail = &tmp->next;
}
tmp = GLOB_ALLOC(struct glob_pattern);
@@ -1736,11 +1736,11 @@ static void
glob_free_pattern(struct glob_pattern *list)
{
while (list) {
- struct glob_pattern *tmp = list;
- list = list->next;
- if (tmp->str)
- GLOB_FREE(tmp->str);
- GLOB_FREE(tmp);
+ struct glob_pattern *tmp = list;
+ list = list->next;
+ if (tmp->str)
+ GLOB_FREE(tmp->str);
+ GLOB_FREE(tmp);
}
}
@@ -1752,7 +1752,7 @@ join_path(const char *path, size_t len, int dirsep, const char *name, size_t nam
if (!buf) return 0;
memcpy(buf, path, len);
if (dirsep) {
- buf[len++] = '/';
+ buf[len++] = '/';
}
memcpy(buf+len, name, namlen);
buf[len+namlen] = '\0';
@@ -1769,8 +1769,8 @@ static int
is_case_sensitive(DIR *dirp, const char *path)
{
struct {
- u_int32_t length;
- vol_capabilities_attr_t cap[1];
+ u_int32_t length;
+ vol_capabilities_attr_t cap[1];
} __attribute__((aligned(4), packed)) attrbuf[1];
struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, 0, ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES};
const vol_capabilities_attr_t *const cap = attrbuf[0].cap;
@@ -1779,13 +1779,13 @@ is_case_sensitive(DIR *dirp, const char *path)
# if defined HAVE_FGETATTRLIST
if (fgetattrlist(dirfd(dirp), &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW))
- return -1;
+ return -1;
# else
if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW))
- return -1;
+ return -1;
# endif
if (!(cap->valid[idx] & mask))
- return -1;
+ return -1;
return (cap->capabilities[idx] & mask) != 0;
}
@@ -1793,10 +1793,10 @@ static char *
replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int flags, rb_pathtype_t *type)
{
struct {
- u_int32_t length;
- attrreference_t ref[1];
- fsobj_type_t objtype;
- char path[MAXPATHLEN * 3];
+ u_int32_t length;
+ attrreference_t ref[1];
+ fsobj_type_t objtype;
+ char path[MAXPATHLEN * 3];
} __attribute__((aligned(4), packed)) attrbuf[1];
struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_NAME|ATTR_CMN_OBJTYPE};
const attrreference_t *const ar = attrbuf[0].ref;
@@ -1807,9 +1807,9 @@ replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int f
*type = path_noent;
if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) {
- if (!to_be_ignored(errno))
- sys_warning(path, enc);
- return path;
+ if (!to_be_ignored(errno))
+ sys_warning(path, enc);
+ return path;
}
switch (attrbuf[0].objtype) {
@@ -1821,21 +1821,21 @@ replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int f
name = (char *)ar + ar->attr_dataoffset;
len = (long)ar->attr_length - 1;
if (name + len > (char *)attrbuf + sizeof(attrbuf))
- return path;
+ return path;
# if NORMALIZE_UTF8PATH
if (norm_p && has_nonascii(name, len)) {
- if (!NIL_P(utf8str = rb_str_normalize_ospath(name, len))) {
- RSTRING_GETMEM(utf8str, name, len);
- }
+ if (!NIL_P(utf8str = rb_str_normalize_ospath(name, len))) {
+ RSTRING_GETMEM(utf8str, name, len);
+ }
}
# endif
tmp = GLOB_REALLOC(path, base + len + 1);
if (tmp) {
- path = tmp;
- memcpy(path + base, name, len);
- path[base + len] = '\0';
+ path = tmp;
+ memcpy(path + base, name, len);
+ path[base + len] = '\0';
}
IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0));
return path;
@@ -1856,62 +1856,62 @@ replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int f
long wlen;
int e = 0;
if (!fundamental_encoding_p(enc)) {
- tmp = rb_enc_str_new_cstr(plainname, enc);
- tmp = rb_str_encode_ospath(tmp);
- plainname = RSTRING_PTR(tmp);
+ tmp = rb_enc_str_new_cstr(plainname, enc);
+ tmp = rb_str_encode_ospath(tmp);
+ plainname = RSTRING_PTR(tmp);
}
wplain = rb_w32_mbstr_to_wstr(CP_UTF8, plainname, -1, &wlen);
if (tmp) rb_str_resize(tmp, 0);
if (!wplain) return path;
if (GetFileAttributesExW(wplain, GetFileExInfoStandard, &fa)) {
- h = FindFirstFileW(wplain, &fd);
- e = rb_w32_map_errno(GetLastError());
+ h = FindFirstFileW(wplain, &fd);
+ e = rb_w32_map_errno(GetLastError());
}
if (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
- if (!rb_w32_reparse_symlink_p(wplain))
- fa.dwFileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
+ if (!rb_w32_reparse_symlink_p(wplain))
+ fa.dwFileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
}
free(wplain);
if (h == INVALID_HANDLE_VALUE) {
- *type = path_noent;
- if (e && !to_be_ignored(e)) {
- errno = e;
- sys_warning(path, enc);
- }
- return path;
+ *type = path_noent;
+ if (e && !to_be_ignored(e)) {
+ errno = e;
+ sys_warning(path, enc);
+ }
+ return path;
}
FindClose(h);
*type =
- (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? path_symlink :
- (fa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? path_directory :
- path_regular;
+ (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? path_symlink :
+ (fa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? path_directory :
+ path_regular;
if (tmp) {
- char *buf;
- tmp = rb_w32_conv_from_wchar(fd.cFileName, enc);
- wlen = RSTRING_LEN(tmp);
- buf = GLOB_REALLOC(path, base + wlen + 1);
- if (buf) {
- path = buf;
- memcpy(path + base, RSTRING_PTR(tmp), wlen);
- path[base + wlen] = 0;
- }
- rb_str_resize(tmp, 0);
+ char *buf;
+ tmp = rb_w32_conv_from_wchar(fd.cFileName, enc);
+ wlen = RSTRING_LEN(tmp);
+ buf = GLOB_REALLOC(path, base + wlen + 1);
+ if (buf) {
+ path = buf;
+ memcpy(path + base, RSTRING_PTR(tmp), wlen);
+ path[base + wlen] = 0;
+ }
+ rb_str_resize(tmp, 0);
}
else {
- char *utf8filename;
- wlen = WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, NULL, 0, NULL, NULL);
- utf8filename = GLOB_REALLOC(0, wlen);
- if (utf8filename) {
- char *buf;
- WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, utf8filename, wlen, NULL, NULL);
- buf = GLOB_REALLOC(path, base + wlen + 1);
- if (buf) {
- path = buf;
- memcpy(path + base, utf8filename, wlen);
- path[base + wlen] = 0;
- }
- GLOB_FREE(utf8filename);
- }
+ char *utf8filename;
+ wlen = WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, NULL, 0, NULL, NULL);
+ utf8filename = GLOB_REALLOC(0, wlen);
+ if (utf8filename) {
+ char *buf;
+ WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, utf8filename, wlen, NULL, NULL);
+ buf = GLOB_REALLOC(path, base + wlen + 1);
+ if (buf) {
+ path = buf;
+ memcpy(path + base, utf8filename, wlen);
+ path[base + wlen] = 0;
+ }
+ GLOB_FREE(utf8filename);
+ }
}
return path;
}
@@ -2003,7 +2003,7 @@ rb_glob_error(const char *path, VALUE a, const void *enc, int error)
#ifdef ENOTCAPABLE
case ENOTCAPABLE:
#endif
- errfunc = glob_func_warning;
+ errfunc = glob_func_warning;
}
args.path = path;
args.enc = enc;
@@ -2027,7 +2027,7 @@ dirent_match(const char *pat, rb_encoding *enc, const char *name, const rb_diren
if (fnmatch(pat, enc, name, flags) == 0) return 1;
#ifdef _WIN32
if (dp->d_altname && (flags & FNM_SHORTNAME)) {
- if (fnmatch(pat, enc, dp->d_altname, flags) == 0) return 1;
+ if (fnmatch(pat, enc, dp->d_altname, flags) == 0) return 1;
}
#endif
return 0;
@@ -2068,39 +2068,39 @@ join_path_from_pattern(struct glob_pattern **beg)
size_t path_len = 0;
for (p = *beg; p; p = p->next) {
- const char *str;
- switch (p->type) {
- case RECURSIVE:
- str = "**";
- break;
- case MATCH_DIR:
- /* append last slash */
- str = "";
- break;
- default:
- str = p->str;
- if (!str) continue;
- }
- if (!path) {
- path_len = strlen(str);
- path = GLOB_ALLOC_N(char, path_len + 1);
+ const char *str;
+ switch (p->type) {
+ case RECURSIVE:
+ str = "**";
+ break;
+ case MATCH_DIR:
+ /* append last slash */
+ str = "";
+ break;
+ default:
+ str = p->str;
+ if (!str) continue;
+ }
+ if (!path) {
+ path_len = strlen(str);
+ path = GLOB_ALLOC_N(char, path_len + 1);
if (path) {
memcpy(path, str, path_len);
path[path_len] = '\0';
}
}
else {
- size_t len = strlen(str);
- char *tmp;
- tmp = GLOB_REALLOC(path, path_len + len + 2);
- if (tmp) {
- path = tmp;
- path[path_len++] = '/';
- memcpy(path + path_len, str, len);
- path_len += len;
- path[path_len] = '\0';
- }
- }
+ size_t len = strlen(str);
+ char *tmp;
+ tmp = GLOB_REALLOC(path, path_len + len + 2);
+ if (tmp) {
+ path = tmp;
+ path[path_len++] = '/';
+ memcpy(path + path_len, str, len);
+ path_len += len;
+ path[path_len] = '\0';
+ }
+ }
}
return path;
}
@@ -2108,7 +2108,7 @@ join_path_from_pattern(struct glob_pattern **beg)
static int push_caller(const char *path, VALUE val, void *enc);
static int ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg,
- rb_encoding *enc, VALUE var);
+ rb_encoding *enc, VALUE var);
static const size_t rb_dirent_name_offset =
offsetof(rb_dirent_t, d_type) + sizeof(uint8_t);
@@ -2216,7 +2216,7 @@ glob_opendir(ruby_glob_entries_t *ent, DIR *dirp, int flags, rb_encoding *enc)
ent->sort.entries = newp;
}
#endif
- while ((dp = READDIR(dirp, enc)) != NULL) {
+ while ((dp = READDIR(dirp, enc)) != NULL) {
rb_dirent_t *rdp = dirent_copy(dp, NULL);
if (!rdp) {
goto nomem;
@@ -2288,133 +2288,133 @@ glob_helper(
rb_check_stack_overflow();
for (cur = beg; cur < end; ++cur) {
- struct glob_pattern *p = *cur;
- if (p->type == RECURSIVE) {
- recursive = 1;
- p = p->next;
- }
- switch (p->type) {
- case PLAIN:
- plain = 1;
- break;
- case ALPHA:
+ struct glob_pattern *p = *cur;
+ if (p->type == RECURSIVE) {
+ recursive = 1;
+ p = p->next;
+ }
+ switch (p->type) {
+ case PLAIN:
+ plain = 1;
+ break;
+ case ALPHA:
#if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
- plain = 1;
+ plain = 1;
#else
- magical = 1;
-#endif
- break;
- case BRACE:
- if (!recursive) {
- brace = 1;
- }
- break;
- case MAGICAL:
- magical = 2;
- break;
- case MATCH_ALL:
- match_all = 1;
- break;
- case MATCH_DIR:
- match_dir = 1;
- break;
- case RECURSIVE:
- rb_bug("continuous RECURSIVEs");
- }
+ magical = 1;
+#endif
+ break;
+ case BRACE:
+ if (!recursive || strchr(p->str, '/')) {
+ brace = 1;
+ }
+ break;
+ case MAGICAL:
+ magical = 2;
+ break;
+ case MATCH_ALL:
+ match_all = 1;
+ break;
+ case MATCH_DIR:
+ match_dir = 1;
+ break;
+ case RECURSIVE:
+ rb_bug("continuous RECURSIVEs");
+ }
}
if (brace) {
- struct push_glob_args args;
- char* brace_path = join_path_from_pattern(beg);
- if (!brace_path) return -1;
- args.fd = fd;
- args.path = path;
- args.baselen = baselen;
- args.namelen = namelen;
- args.dirsep = dirsep;
- args.pathtype = pathtype;
- args.flags = flags;
- args.funcs = funcs;
- args.arg = arg;
- status = ruby_brace_expand(brace_path, flags, push_caller, (VALUE)&args, enc, Qfalse);
- GLOB_FREE(brace_path);
- return status;
+ struct push_glob_args args;
+ char* brace_path = join_path_from_pattern(beg);
+ if (!brace_path) return -1;
+ args.fd = fd;
+ args.path = path;
+ args.baselen = baselen;
+ args.namelen = namelen;
+ args.dirsep = dirsep;
+ args.pathtype = pathtype;
+ args.flags = flags;
+ args.funcs = funcs;
+ args.arg = arg;
+ status = ruby_brace_expand(brace_path, flags, push_caller, (VALUE)&args, enc, Qfalse);
+ GLOB_FREE(brace_path);
+ return status;
}
if (*path) {
- if (match_all && pathtype == path_unknown) {
- if (do_lstat(fd, baselen, path, &st, flags, enc) == 0) {
- pathtype = IFTODT(st.st_mode);
- }
- else {
- pathtype = path_noent;
- }
- }
- if (match_dir && (pathtype == path_unknown || pathtype == path_symlink)) {
- if (do_stat(fd, baselen, path, &st, flags, enc) == 0) {
- pathtype = IFTODT(st.st_mode);
- }
- else {
- pathtype = path_noent;
- }
- }
- if (match_all && pathtype > path_noent) {
- const char *subpath = path + baselen + (baselen && path[baselen] == '/');
- status = glob_call_func(funcs->match, subpath, arg, enc);
- if (status) return status;
- }
- if (match_dir && pathtype == path_directory) {
- int seplen = (baselen && path[baselen] == '/');
- const char *subpath = path + baselen + seplen;
- char *tmp = join_path(subpath, namelen - seplen, dirsep, "", 0);
- if (!tmp) return -1;
- status = glob_call_func(funcs->match, tmp, arg, enc);
- GLOB_FREE(tmp);
- if (status) return status;
- }
+ if (match_all && pathtype == path_unknown) {
+ if (do_lstat(fd, baselen, path, &st, flags, enc) == 0) {
+ pathtype = IFTODT(st.st_mode);
+ }
+ else {
+ pathtype = path_noent;
+ }
+ }
+ if (match_dir && (pathtype == path_unknown || pathtype == path_symlink)) {
+ if (do_stat(fd, baselen, path, &st, flags, enc) == 0) {
+ pathtype = IFTODT(st.st_mode);
+ }
+ else {
+ pathtype = path_noent;
+ }
+ }
+ if (match_all && pathtype > path_noent) {
+ const char *subpath = path + baselen + (baselen && path[baselen] == '/');
+ status = glob_call_func(funcs->match, subpath, arg, enc);
+ if (status) return status;
+ }
+ if (match_dir && pathtype == path_directory) {
+ int seplen = (baselen && path[baselen] == '/');
+ const char *subpath = path + baselen + seplen;
+ char *tmp = join_path(subpath, namelen - seplen, dirsep, "", 0);
+ if (!tmp) return -1;
+ status = glob_call_func(funcs->match, tmp, arg, enc);
+ GLOB_FREE(tmp);
+ if (status) return status;
+ }
}
if (pathtype == path_noent) return 0;
if (magical || recursive) {
- rb_dirent_t *dp;
- DIR *dirp;
+ rb_dirent_t *dp;
+ DIR *dirp;
# if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
- char *plainname = 0;
+ char *plainname = 0;
# endif
- IF_NORMALIZE_UTF8PATH(int norm_p);
+ IF_NORMALIZE_UTF8PATH(int norm_p);
# if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
- if (cur + 1 == end && (*cur)->type <= ALPHA) {
- plainname = join_path(path, pathlen, dirsep, (*cur)->str, strlen((*cur)->str));
- if (!plainname) return -1;
- dirp = do_opendir(fd, basename, plainname, flags, enc, funcs->error, arg, &status);
- GLOB_FREE(plainname);
- }
- else
+ if (cur + 1 == end && (*cur)->type <= ALPHA) {
+ plainname = join_path(path, pathlen, dirsep, (*cur)->str, strlen((*cur)->str));
+ if (!plainname) return -1;
+ dirp = do_opendir(fd, basename, plainname, flags, enc, funcs->error, arg, &status);
+ GLOB_FREE(plainname);
+ }
+ else
# else
- ;
+ ;
# endif
- dirp = do_opendir(fd, baselen, path, flags, enc, funcs->error, arg, &status);
- if (dirp == NULL) {
+ dirp = do_opendir(fd, baselen, path, flags, enc, funcs->error, arg, &status);
+ if (dirp == NULL) {
# if FNM_SYSCASE || NORMALIZE_UTF8PATH
- if ((magical < 2) && !recursive && (errno == EACCES)) {
- /* no read permission, fallback */
- goto literally;
- }
+ if ((magical < 2) && !recursive && (errno == EACCES)) {
+ /* no read permission, fallback */
+ goto literally;
+ }
# endif
- return status;
- }
- IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *path ? path : "."));
+ return status;
+ }
+ IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *path ? path : "."));
# if NORMALIZE_UTF8PATH
- if (!(norm_p || magical || recursive)) {
- closedir(dirp);
- goto literally;
- }
+ if (!(norm_p || magical || recursive)) {
+ closedir(dirp);
+ goto literally;
+ }
# endif
# ifdef HAVE_GETATTRLIST
- if (is_case_sensitive(dirp, path) == 0)
- flags |= FNM_CASEFOLD;
+ if (is_case_sensitive(dirp, path) == 0)
+ flags |= FNM_CASEFOLD;
# endif
ruby_glob_entries_t globent;
if (!glob_opendir(&globent, dirp, flags, enc)) {
@@ -2428,182 +2428,182 @@ glob_helper(
return status;
}
- int skipdot = (flags & FNM_GLOB_SKIPDOT);
- flags |= FNM_GLOB_SKIPDOT;
-
- while ((dp = glob_getent(&globent, flags, enc)) != NULL) {
- char *buf;
- rb_pathtype_t new_pathtype = path_unknown;
- const char *name;
- size_t namlen;
- int dotfile = 0;
- IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil);
-
- name = dp->d_name;
- namlen = dp->d_namlen;
- if (name[0] == '.') {
- ++dotfile;
- if (namlen == 1) {
- /* unless DOTMATCH, skip current directories not to recurse infinitely */
- if (recursive && !(flags & FNM_DOTMATCH)) continue;
- if (skipdot) continue;
- ++dotfile;
- new_pathtype = path_directory; /* force to skip stat/lstat */
- }
- else if (namlen == 2 && name[1] == '.') {
- /* always skip parent directories not to recurse infinitely */
- continue;
- }
- }
+ int skipdot = (flags & FNM_GLOB_SKIPDOT);
+ flags |= FNM_GLOB_SKIPDOT;
+
+ while ((dp = glob_getent(&globent, flags, enc)) != NULL) {
+ char *buf;
+ rb_pathtype_t new_pathtype = path_unknown;
+ const char *name;
+ size_t namlen;
+ int dotfile = 0;
+ IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil);
+
+ name = dp->d_name;
+ namlen = dp->d_namlen;
+ if (name[0] == '.') {
+ ++dotfile;
+ if (namlen == 1) {
+ /* unless DOTMATCH, skip current directories not to recurse infinitely */
+ if (recursive && !(flags & FNM_DOTMATCH)) continue;
+ if (skipdot) continue;
+ ++dotfile;
+ new_pathtype = path_directory; /* force to skip stat/lstat */
+ }
+ else if (namlen == 2 && name[1] == '.') {
+ /* always skip parent directories not to recurse infinitely */
+ continue;
+ }
+ }
# if NORMALIZE_UTF8PATH
- if (norm_p && has_nonascii(name, namlen)) {
- if (!NIL_P(utf8str = rb_str_normalize_ospath(name, namlen))) {
- RSTRING_GETMEM(utf8str, name, namlen);
- }
- }
+ if (norm_p && has_nonascii(name, namlen)) {
+ if (!NIL_P(utf8str = rb_str_normalize_ospath(name, namlen))) {
+ RSTRING_GETMEM(utf8str, name, namlen);
+ }
+ }
# endif
- buf = join_path(path, pathlen, dirsep, name, namlen);
- IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0));
- if (!buf) {
- status = -1;
- break;
- }
- name = buf + pathlen + (dirsep != 0);
+ buf = join_path(path, pathlen, dirsep, name, namlen);
+ IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0));
+ if (!buf) {
+ status = -1;
+ break;
+ }
+ name = buf + pathlen + (dirsep != 0);
#if !EMULATE_IFTODT
- if (dp->d_type != DT_UNKNOWN) {
- /* Got it. We need no more lstat. */
- new_pathtype = dp->d_type;
- }
-#endif
- if (recursive && dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1) &&
- new_pathtype == path_unknown) {
- /* RECURSIVE never match dot files unless FNM_DOTMATCH is set */
- if (do_lstat(fd, baselen, buf, &st, flags, enc) == 0)
- new_pathtype = IFTODT(st.st_mode);
- else
- new_pathtype = path_noent;
- }
-
- new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, (end - beg) * 2);
- if (!new_beg) {
- GLOB_FREE(buf);
- status = -1;
- break;
- }
-
- for (cur = beg; cur < end; ++cur) {
- struct glob_pattern *p = *cur;
- struct dirent_brace_args args;
- if (p->type == RECURSIVE) {
- if (new_pathtype == path_directory || /* not symlink but real directory */
- new_pathtype == path_exist) {
- if (dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1))
- *new_end++ = p; /* append recursive pattern */
- }
- p = p->next; /* 0 times recursion */
- }
- switch (p->type) {
- case BRACE:
- args.name = name;
- args.dp = dp;
- args.flags = flags;
- if (ruby_brace_expand(p->str, flags, dirent_match_brace,
- (VALUE)&args, enc, Qfalse) > 0)
- *new_end++ = p->next;
- break;
- case ALPHA:
+ if (dp->d_type != DT_UNKNOWN) {
+ /* Got it. We need no more lstat. */
+ new_pathtype = dp->d_type;
+ }
+#endif
+ if (recursive && dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1) &&
+ new_pathtype == path_unknown) {
+ /* RECURSIVE never match dot files unless FNM_DOTMATCH is set */
+ if (do_lstat(fd, baselen, buf, &st, flags, enc) == 0)
+ new_pathtype = IFTODT(st.st_mode);
+ else
+ new_pathtype = path_noent;
+ }
+
+ new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, (end - beg) * 2);
+ if (!new_beg) {
+ GLOB_FREE(buf);
+ status = -1;
+ break;
+ }
+
+ for (cur = beg; cur < end; ++cur) {
+ struct glob_pattern *p = *cur;
+ struct dirent_brace_args args;
+ if (p->type == RECURSIVE) {
+ if (new_pathtype == path_directory || /* not symlink but real directory */
+ new_pathtype == path_exist) {
+ if (dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1))
+ *new_end++ = p; /* append recursive pattern */
+ }
+ p = p->next; /* 0 times recursion */
+ }
+ switch (p->type) {
+ case BRACE:
+ args.name = name;
+ args.dp = dp;
+ args.flags = flags;
+ if (ruby_brace_expand(p->str, flags, dirent_match_brace,
+ (VALUE)&args, enc, Qfalse) > 0)
+ *new_end++ = p->next;
+ break;
+ case ALPHA:
# if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
- if (plainname) {
- *new_end++ = p->next;
- break;
- }
+ if (plainname) {
+ *new_end++ = p->next;
+ break;
+ }
# endif
- case PLAIN:
- case MAGICAL:
- if (dirent_match(p->str, enc, name, dp, flags))
- *new_end++ = p->next;
- default:
- break;
- }
- }
-
- status = glob_helper(fd, buf, baselen, name - buf - baselen + namlen, 1,
- new_pathtype, new_beg, new_end,
- flags, funcs, arg, enc);
- GLOB_FREE(buf);
- GLOB_FREE(new_beg);
- if (status) break;
- }
+ case PLAIN:
+ case MAGICAL:
+ if (dirent_match(p->str, enc, name, dp, flags))
+ *new_end++ = p->next;
+ default:
+ break;
+ }
+ }
+
+ status = glob_helper(fd, buf, baselen, name - buf - baselen + namlen, 1,
+ new_pathtype, new_beg, new_end,
+ flags, funcs, arg, enc);
+ GLOB_FREE(buf);
+ GLOB_FREE(new_beg);
+ if (status) break;
+ }
glob_dir_finish(&globent, flags);
}
else if (plain) {
- struct glob_pattern **copy_beg, **copy_end, **cur2;
+ struct glob_pattern **copy_beg, **copy_end, **cur2;
# if FNM_SYSCASE || NORMALIZE_UTF8PATH
literally:
# endif
- copy_beg = copy_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
- if (!copy_beg) return -1;
- for (cur = beg; cur < end; ++cur)
- *copy_end++ = (*cur)->type <= ALPHA ? *cur : 0;
-
- for (cur = copy_beg; cur < copy_end; ++cur) {
- if (*cur) {
- rb_pathtype_t new_pathtype = path_unknown;
- char *buf;
- char *name;
- size_t len = strlen((*cur)->str) + 1;
- name = GLOB_ALLOC_N(char, len);
- if (!name) {
- status = -1;
- break;
- }
- memcpy(name, (*cur)->str, len);
- if (escape)
- len = remove_backslashes(name, name+len-1, enc) - name;
-
- new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
- if (!new_beg) {
- GLOB_FREE(name);
- status = -1;
- break;
- }
- *new_end++ = (*cur)->next;
- for (cur2 = cur + 1; cur2 < copy_end; ++cur2) {
- if (*cur2 && fnmatch((*cur2)->str, enc, name, flags) == 0) {
- *new_end++ = (*cur2)->next;
- *cur2 = 0;
- }
- }
-
- buf = join_path(path, pathlen, dirsep, name, len);
- GLOB_FREE(name);
- if (!buf) {
- GLOB_FREE(new_beg);
- status = -1;
- break;
- }
+ copy_beg = copy_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
+ if (!copy_beg) return -1;
+ for (cur = beg; cur < end; ++cur)
+ *copy_end++ = (*cur)->type <= ALPHA ? *cur : 0;
+
+ for (cur = copy_beg; cur < copy_end; ++cur) {
+ if (*cur) {
+ rb_pathtype_t new_pathtype = path_unknown;
+ char *buf;
+ char *name;
+ size_t len = strlen((*cur)->str) + 1;
+ name = GLOB_ALLOC_N(char, len);
+ if (!name) {
+ status = -1;
+ break;
+ }
+ memcpy(name, (*cur)->str, len);
+ if (escape)
+ len = remove_backslashes(name, name+len-1, enc) - name;
+
+ new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
+ if (!new_beg) {
+ GLOB_FREE(name);
+ status = -1;
+ break;
+ }
+ *new_end++ = (*cur)->next;
+ for (cur2 = cur + 1; cur2 < copy_end; ++cur2) {
+ if (*cur2 && fnmatch((*cur2)->str, enc, name, flags) == 0) {
+ *new_end++ = (*cur2)->next;
+ *cur2 = 0;
+ }
+ }
+
+ buf = join_path(path, pathlen, dirsep, name, len);
+ GLOB_FREE(name);
+ if (!buf) {
+ GLOB_FREE(new_beg);
+ status = -1;
+ break;
+ }
#if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
- if ((*cur)->type == ALPHA) {
- buf = replace_real_basename(buf, pathlen + (dirsep != 0), enc,
- IF_NORMALIZE_UTF8PATH(1)+0,
- flags, &new_pathtype);
- if (!buf) break;
- }
-#endif
- status = glob_helper(fd, buf, baselen,
- namelen + strlen(buf + pathlen), 1,
- new_pathtype, new_beg, new_end,
- flags, funcs, arg, enc);
- GLOB_FREE(buf);
- GLOB_FREE(new_beg);
- if (status) break;
- }
- }
-
- GLOB_FREE(copy_beg);
+ if ((*cur)->type == ALPHA) {
+ buf = replace_real_basename(buf, pathlen + (dirsep != 0), enc,
+ IF_NORMALIZE_UTF8PATH(1)+0,
+ flags, &new_pathtype);
+ if (!buf) break;
+ }
+#endif
+ status = glob_helper(fd, buf, baselen,
+ namelen + strlen(buf + pathlen), 1,
+ new_pathtype, new_beg, new_end,
+ flags, funcs, arg, enc);
+ GLOB_FREE(buf);
+ GLOB_FREE(new_beg);
+ if (status) break;
+ }
+ }
+
+ GLOB_FREE(copy_beg);
}
return status;
@@ -2618,11 +2618,11 @@ push_caller(const char *path, VALUE val, void *enc)
list = glob_make_pattern(path, path + strlen(path), arg->flags, enc);
if (!list) {
- return -1;
+ return -1;
}
status = glob_helper(arg->fd, arg->path, arg->baselen, arg->namelen, arg->dirsep,
- arg->pathtype, &list, &list + 1, arg->flags, arg->funcs,
- arg->arg, enc);
+ arg->pathtype, &list, &list + 1, arg->flags, arg->funcs,
+ arg->arg, enc);
glob_free_pattern(list);
return status;
}
@@ -2647,8 +2647,8 @@ push_glob0_caller(const char *path, VALUE val, void *enc)
static int
ruby_glob0(const char *path, int fd, const char *base, int flags,
- const ruby_glob_funcs_t *funcs, VALUE arg,
- rb_encoding *enc)
+ const ruby_glob_funcs_t *funcs, VALUE arg,
+ rb_encoding *enc)
{
struct glob_pattern *list;
const char *root, *start;
@@ -2677,10 +2677,10 @@ ruby_glob0(const char *path, int fd, const char *base, int flags,
n = root - start;
if (!n && base) {
- n = strlen(base);
- baselen = n;
- start = base;
- dirsep = TRUE;
+ n = strlen(base);
+ baselen = n;
+ start = base;
+ dirsep = TRUE;
}
buf = GLOB_ALLOC_N(char, n + 1);
if (!buf) return -1;
@@ -2689,12 +2689,12 @@ ruby_glob0(const char *path, int fd, const char *base, int flags,
list = glob_make_pattern(root, root + strlen(root), flags, enc);
if (!list) {
- GLOB_FREE(buf);
- return -1;
+ GLOB_FREE(buf);
+ return -1;
}
status = glob_helper(fd, buf, baselen, n-baselen, dirsep,
- path_unknown, &list, &list + 1,
- flags, funcs, arg, enc);
+ path_unknown, &list, &list + 1,
+ flags, funcs, arg, enc);
glob_free_pattern(list);
GLOB_FREE(buf);
@@ -2708,7 +2708,7 @@ ruby_glob(const char *path, int flags, ruby_glob_func *func, VALUE arg)
funcs.match = func;
funcs.error = 0;
return ruby_glob0(path, AT_FDCWD, 0, flags & ~GLOB_VERBOSE,
- &funcs, arg, rb_ascii8bit_encoding());
+ &funcs, arg, rb_ascii8bit_encoding());
}
static int
@@ -2737,7 +2737,7 @@ rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg)
args.enc = rb_ascii8bit_encoding();
status = ruby_glob0(path, AT_FDCWD, 0, GLOB_VERBOSE, &rb_glob_funcs,
- (VALUE)&args, args.enc);
+ (VALUE)&args, args.enc);
if (status) GLOB_JUMP_TAG(status);
}
@@ -2756,7 +2756,7 @@ push_pattern(const char *path, VALUE ary, void *enc)
static int
ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg,
- rb_encoding *enc, VALUE var)
+ rb_encoding *enc, VALUE var)
{
const int escape = !(flags & FNM_NOESCAPE);
const char *p = str;
@@ -2766,48 +2766,48 @@ ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg,
int nest = 0, status = 0;
while (*p) {
- if (*p == '{' && nest++ == 0) {
- lbrace = p;
- }
- if (*p == '}' && lbrace && --nest == 0) {
- rbrace = p;
- break;
- }
- if (*p == '\\' && escape) {
- if (!*++p) break;
- }
- Inc(p, pend, enc);
+ if (*p == '{' && nest++ == 0) {
+ lbrace = p;
+ }
+ if (*p == '}' && lbrace && --nest == 0) {
+ rbrace = p;
+ break;
+ }
+ if (*p == '\\' && escape) {
+ if (!*++p) break;
+ }
+ Inc(p, pend, enc);
}
if (lbrace && rbrace) {
- size_t len = strlen(s) + 1;
- char *buf = GLOB_ALLOC_N(char, len);
- long shift;
-
- if (!buf) return -1;
- memcpy(buf, s, lbrace-s);
- shift = (lbrace-s);
- p = lbrace;
- while (p < rbrace) {
- const char *t = ++p;
- nest = 0;
- while (p < rbrace && !(*p == ',' && nest == 0)) {
- if (*p == '{') nest++;
- if (*p == '}') nest--;
- if (*p == '\\' && escape) {
- if (++p == rbrace) break;
- }
- Inc(p, pend, enc);
- }
- memcpy(buf+shift, t, p-t);
- strlcpy(buf+shift+(p-t), rbrace+1, len-(shift+(p-t)));
- status = ruby_brace_expand(buf, flags, func, arg, enc, var);
- if (status) break;
- }
- GLOB_FREE(buf);
+ size_t len = strlen(s) + 1;
+ char *buf = GLOB_ALLOC_N(char, len);
+ long shift;
+
+ if (!buf) return -1;
+ memcpy(buf, s, lbrace-s);
+ shift = (lbrace-s);
+ p = lbrace;
+ while (p < rbrace) {
+ const char *t = ++p;
+ nest = 0;
+ while (p < rbrace && !(*p == ',' && nest == 0)) {
+ if (*p == '{') nest++;
+ if (*p == '}') nest--;
+ if (*p == '\\' && escape) {
+ if (++p == rbrace) break;
+ }
+ Inc(p, pend, enc);
+ }
+ memcpy(buf+shift, t, p-t);
+ strlcpy(buf+shift+(p-t), rbrace+1, len-(shift+(p-t)));
+ status = ruby_brace_expand(buf, flags, func, arg, enc, var);
+ if (status) break;
+ }
+ GLOB_FREE(buf);
}
else if (!lbrace && !rbrace) {
- status = glob_call_func(func, s, arg, enc);
+ status = glob_call_func(func, s, arg, enc);
}
RB_GC_GUARD(var);
@@ -2858,9 +2858,9 @@ push_glob(VALUE ary, VALUE str, VALUE base, int flags)
str = rb_str_encode_ospath(str);
#endif
if (rb_enc_to_index(enc) == ENCINDEX_US_ASCII)
- enc = rb_filesystem_encoding();
+ enc = rb_filesystem_encoding();
if (rb_enc_to_index(enc) == ENCINDEX_US_ASCII)
- enc = rb_ascii8bit_encoding();
+ enc = rb_ascii8bit_encoding();
flags |= GLOB_VERBOSE;
args.func = push_pattern;
args.value = ary;
@@ -2868,23 +2868,23 @@ push_glob(VALUE ary, VALUE str, VALUE base, int flags)
args.base = 0;
fd = AT_FDCWD;
if (!NIL_P(base)) {
- if (!RB_TYPE_P(base, T_STRING) || !rb_enc_check(str, base)) {
- struct dir_data *dirp = DATA_PTR(base);
- if (!dirp->dir) dir_closed();
+ if (!RB_TYPE_P(base, T_STRING) || !rb_enc_check(str, base)) {
+ struct dir_data *dirp = DATA_PTR(base);
+ if (!dirp->dir) dir_closed();
#ifdef HAVE_DIRFD
- if ((fd = dirfd(dirp->dir)) == -1)
- rb_sys_fail_path(dir_inspect(base));
+ if ((fd = dirfd(dirp->dir)) == -1)
+ rb_sys_fail_path(dir_inspect(base));
#endif
- base = dirp->path;
- }
- args.base = RSTRING_PTR(base);
+ base = dirp->path;
+ }
+ args.base = RSTRING_PTR(base);
}
#if defined _WIN32 || defined __APPLE__
enc = rb_utf8_encoding();
#endif
return ruby_glob0(RSTRING_PTR(str), fd, args.base, flags, &rb_glob_funcs,
- (VALUE)&args, enc);
+ (VALUE)&args, enc);
}
static VALUE
@@ -2895,13 +2895,13 @@ rb_push_glob(VALUE str, VALUE base, int flags) /* '\0' is delimiter */
/* can contain null bytes as separators */
if (!RB_TYPE_P(str, T_STRING)) {
- FilePathValue(str);
+ FilePathValue(str);
}
else if (!rb_str_to_cstr(str)) {
rb_raise(rb_eArgError, "nul-separated glob pattern is deprecated");
}
else {
- rb_enc_check(str, rb_enc_from_encoding(rb_usascii_encoding()));
+ rb_enc_check(str, rb_enc_from_encoding(rb_usascii_encoding()));
}
ary = rb_ary_new();
@@ -2918,11 +2918,11 @@ dir_globs(VALUE args, VALUE base, int flags)
long i;
for (i = 0; i < RARRAY_LEN(args); ++i) {
- int status;
- VALUE str = RARRAY_AREF(args, i);
- FilePathValue(str);
- status = push_glob(ary, str, base, flags);
- if (status) GLOB_JUMP_TAG(status);
+ int status;
+ VALUE str = RARRAY_AREF(args, i);
+ FilePathValue(str);
+ status = push_glob(ary, str, base, flags);
+ if (status) GLOB_JUMP_TAG(status);
}
RB_GC_GUARD(args);
@@ -2932,12 +2932,12 @@ dir_globs(VALUE args, VALUE base, int flags)
static VALUE
dir_glob_option_base(VALUE base)
{
- if (base == Qundef || NIL_P(base)) {
- return Qnil;
+ if (NIL_OR_UNDEF_P(base)) {
+ return Qnil;
}
#if USE_OPENDIR_AT
if (rb_typeddata_is_kind_of(base, &dir_data_type)) {
- return base;
+ return base;
}
#endif
FilePathValue(base);
@@ -2957,7 +2957,7 @@ dir_s_aref(rb_execution_context_t *ec, VALUE obj, VALUE args, VALUE base, VALUE
const int flags = dir_glob_option_sort(sort);
base = dir_glob_option_base(base);
if (RARRAY_LEN(args) == 1) {
- return rb_push_glob(RARRAY_AREF(args, 0), base, flags);
+ return rb_push_glob(RARRAY_AREF(args, 0), base, flags);
}
return dir_globs(args, base, flags);
}
@@ -2969,15 +2969,15 @@ dir_s_glob(rb_execution_context_t *ec, VALUE obj, VALUE str, VALUE rflags, VALUE
const int flags = (NUM2INT(rflags) | dir_glob_option_sort(sort)) & ~FNM_CASEFOLD;
base = dir_glob_option_base(base);
if (NIL_P(ary)) {
- ary = rb_push_glob(str, base, flags);
+ ary = rb_push_glob(str, base, flags);
}
else {
ary = dir_globs(ary, base, flags);
}
if (rb_block_given_p()) {
- rb_ary_each(ary);
- return Qnil;
+ rb_ary_each(ary);
+ return Qnil;
}
return ary;
}
@@ -3174,19 +3174,19 @@ fnmatch_brace(const char *pattern, VALUE val, void *enc)
rb_encoding *enc_path = rb_enc_get(path);
if (enc_pattern != enc_path) {
- if (!rb_enc_asciicompat(enc_pattern))
- return FNM_NOMATCH;
- if (!rb_enc_asciicompat(enc_path))
- return FNM_NOMATCH;
- if (!rb_enc_str_asciionly_p(path)) {
- int cr = ENC_CODERANGE_7BIT;
- long len = strlen(pattern);
- if (rb_str_coderange_scan_restartable(pattern, pattern + len,
- enc_pattern, &cr) != len)
- return FNM_NOMATCH;
- if (cr != ENC_CODERANGE_7BIT)
- return FNM_NOMATCH;
- }
+ if (!rb_enc_asciicompat(enc_pattern))
+ return FNM_NOMATCH;
+ if (!rb_enc_asciicompat(enc_path))
+ return FNM_NOMATCH;
+ if (!rb_enc_str_asciionly_p(path)) {
+ int cr = ENC_CODERANGE_7BIT;
+ long len = strlen(pattern);
+ if (rb_str_coderange_scan_restartable(pattern, pattern + len,
+ enc_pattern, &cr) != len)
+ return FNM_NOMATCH;
+ if (cr != ENC_CODERANGE_7BIT)
+ return FNM_NOMATCH;
+ }
}
return (fnmatch(pattern, enc, RSTRING_PTR(path), arg->flags) == 0);
}
@@ -3200,27 +3200,27 @@ file_s_fnmatch(int argc, VALUE *argv, VALUE obj)
int flags;
if (rb_scan_args(argc, argv, "21", &pattern, &path, &rflags) == 3)
- flags = NUM2INT(rflags);
+ flags = NUM2INT(rflags);
else
- flags = 0;
+ flags = 0;
StringValueCStr(pattern);
FilePathStringValue(path);
if (flags & FNM_EXTGLOB) {
- struct brace_args args;
+ struct brace_args args;
- args.value = path;
- args.flags = flags;
- if (ruby_brace_expand(RSTRING_PTR(pattern), flags, fnmatch_brace,
- (VALUE)&args, rb_enc_get(pattern), pattern) > 0)
- return Qtrue;
+ args.value = path;
+ args.flags = flags;
+ if (ruby_brace_expand(RSTRING_PTR(pattern), flags, fnmatch_brace,
+ (VALUE)&args, rb_enc_get(pattern), pattern) > 0)
+ return Qtrue;
}
else {
- rb_encoding *enc = rb_enc_compatible(pattern, path);
- if (!enc) return Qfalse;
- if (fnmatch(RSTRING_PTR(pattern), enc, RSTRING_PTR(path), flags) == 0)
- return Qtrue;
+ rb_encoding *enc = rb_enc_compatible(pattern, path);
+ if (!enc) return Qfalse;
+ if (fnmatch(RSTRING_PTR(pattern), enc, RSTRING_PTR(path), flags) == 0)
+ return Qtrue;
}
RB_GC_GUARD(pattern);
@@ -3244,12 +3244,12 @@ dir_s_home(int argc, VALUE *argv, VALUE obj)
rb_check_arity(argc, 0, 1);
user = (argc > 0) ? argv[0] : Qnil;
if (!NIL_P(user)) {
- SafeStringValue(user);
- rb_must_asciicompat(user);
- u = StringValueCStr(user);
- if (*u) {
- return rb_home_dir_of(user, rb_str_new(0, 0));
- }
+ SafeStringValue(user);
+ rb_must_asciicompat(user);
+ u = StringValueCStr(user);
+ if (*u) {
+ return rb_home_dir_of(user, rb_str_new(0, 0));
+ }
}
return rb_default_home_dir(rb_str_new(0, 0));
@@ -3279,24 +3279,24 @@ nogvl_dir_empty_p(void *ptr)
VALUE result = Qtrue;
if (!dir) {
- int e = errno;
- switch (gc_for_fd_with_gvl(e)) {
- default:
- dir = opendir(path);
- if (dir) break;
- e = errno;
- /* fall through */
- case 0:
- if (e == ENOTDIR) return (void *)Qfalse;
- errno = e; /* for rb_sys_fail_path */
- return (void *)Qundef;
- }
+ int e = errno;
+ switch (gc_for_fd_with_gvl(e)) {
+ default:
+ dir = opendir(path);
+ if (dir) break;
+ e = errno;
+ /* fall through */
+ case 0:
+ if (e == ENOTDIR) return (void *)Qfalse;
+ errno = e; /* for rb_sys_fail_path */
+ return (void *)Qundef;
+ }
}
while ((dp = READDIR(dir, NULL)) != NULL) {
- if (!to_be_skipped(dp)) {
- result = Qfalse;
- break;
- }
+ if (!to_be_skipped(dp)) {
+ result = Qfalse;
+ break;
+ }
}
closedir(dir);
return (void *)result;
@@ -3324,27 +3324,27 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname)
#if defined HAVE_GETATTRLIST && defined ATTR_DIR_ENTRYCOUNT
{
- u_int32_t attrbuf[SIZEUP32(fsobj_tag_t)];
- struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_OBJTAG,};
- if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) != 0)
- rb_sys_fail_path(orig);
- if (*(const fsobj_tag_t *)(attrbuf+1) == VT_HFS) {
- al.commonattr = 0;
- al.dirattr = ATTR_DIR_ENTRYCOUNT;
- if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) == 0) {
- if (attrbuf[0] >= 2 * sizeof(u_int32_t))
- return RBOOL(attrbuf[1] == 0);
- if (false_on_notdir) return Qfalse;
- }
- rb_sys_fail_path(orig);
- }
+ u_int32_t attrbuf[SIZEUP32(fsobj_tag_t)];
+ struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_OBJTAG,};
+ if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) != 0)
+ rb_sys_fail_path(orig);
+ if (*(const fsobj_tag_t *)(attrbuf+1) == VT_HFS) {
+ al.commonattr = 0;
+ al.dirattr = ATTR_DIR_ENTRYCOUNT;
+ if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) == 0) {
+ if (attrbuf[0] >= 2 * sizeof(u_int32_t))
+ return RBOOL(attrbuf[1] == 0);
+ if (false_on_notdir) return Qfalse;
+ }
+ rb_sys_fail_path(orig);
+ }
}
#endif
result = (VALUE)rb_thread_call_without_gvl(nogvl_dir_empty_p, (void *)path,
- RUBY_UBF_IO, 0);
- if (result == Qundef) {
- rb_sys_fail_path(orig);
+ RUBY_UBF_IO, 0);
+ if (UNDEF_P(result)) {
+ rb_sys_fail_path(orig);
}
return result;
}
diff --git a/dln.c b/dln.c
index b9cfe7dc94..0edd709bbe 100644
--- a/dln.c
+++ b/dln.c
@@ -41,6 +41,10 @@ static void dln_loaderror(const char *format, ...);
# include <strings.h>
#endif
+#if defined __APPLE__
+# include <AvailabilityMacros.h>
+#endif
+
#ifndef xmalloc
void *xmalloc();
void *xcalloc();
@@ -58,7 +62,7 @@ void *xrealloc();
#include <sys/stat.h>
#ifndef S_ISDIR
-# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#ifdef HAVE_SYS_PARAM_H
@@ -110,8 +114,8 @@ init_funcname_len(const char **file)
/* Load the file as an object one */
for (base = p; *p; p++) { /* Find position of last '/' */
- if (*p == '.' && !dot) dot = p;
- if (isdirsep(*p)) base = p+1, dot = NULL;
+ if (*p == '.' && !dot) dot = p;
+ if (isdirsep(*p)) base = p+1, dot = NULL;
}
*file = base;
/* Delete suffix if it exists */
@@ -126,7 +130,7 @@ static const char funcname_prefix[sizeof(FUNCNAME_PREFIX) - 1] = FUNCNAME_PREFIX
const size_t plen = sizeof(funcname_prefix);\
char *const tmp = ALLOCA_N(char, plen+flen+1);\
if (!tmp) {\
- dln_memerror();\
+ dln_memerror();\
}\
memcpy(tmp, funcname_prefix, plen);\
memcpy(tmp+plen, base, flen);\
@@ -170,14 +174,14 @@ dln_strerror(char *message, size_t size)
size_t len = snprintf(message, size, "%d: ", error);
#define format_message(sublang) FormatMessage(\
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, \
- NULL, error, MAKELANGID(LANG_NEUTRAL, (sublang)), \
- message + len, size - len, NULL)
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, \
+ NULL, error, MAKELANGID(LANG_NEUTRAL, (sublang)), \
+ message + len, size - len, NULL)
if (format_message(SUBLANG_ENGLISH_US) == 0)
- format_message(SUBLANG_DEFAULT);
+ format_message(SUBLANG_DEFAULT);
for (p = message + len; *p; p++) {
- if (*p == '\n' || *p == '\r')
- *p = ' ';
+ if (*p == '\n' || *p == '\r')
+ *p = ' ';
}
return message;
}
@@ -200,18 +204,18 @@ aix_loaderror(const char *pathname)
snprintf(errbuf, sizeof(errbuf), "load failed - %s. ", pathname);
if (loadquery(L_GETMESSAGES, &message[0], sizeof(message)) != -1) {
- ERRBUF_APPEND("Please issue below command for detailed reasons:\n\t");
- ERRBUF_APPEND("/usr/sbin/execerror ruby ");
- for (i=0; message[i]; i++) {
- ERRBUF_APPEND("\"");
- ERRBUF_APPEND(message[i]);
- ERRBUF_APPEND("\" ");
- }
- ERRBUF_APPEND("\n");
+ ERRBUF_APPEND("Please issue below command for detailed reasons:\n\t");
+ ERRBUF_APPEND("/usr/sbin/execerror ruby ");
+ for (i=0; message[i]; i++) {
+ ERRBUF_APPEND("\"");
+ ERRBUF_APPEND(message[i]);
+ ERRBUF_APPEND("\" ");
+ }
+ ERRBUF_APPEND("\n");
}
else {
- ERRBUF_APPEND(strerror(errno));
- ERRBUF_APPEND("[loadquery failed]");
+ ERRBUF_APPEND(strerror(errno));
+ ERRBUF_APPEND("[loadquery failed]");
}
dln_loaderror("%s", errbuf);
}
@@ -229,22 +233,22 @@ rb_w32_check_imported(HMODULE ext, HMODULE mine)
desc = ImageDirectoryEntryToData(ext, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size);
if (!desc) return 0;
while (desc->Name) {
- PIMAGE_THUNK_DATA pint = (PIMAGE_THUNK_DATA)((char *)ext + desc->Characteristics);
- PIMAGE_THUNK_DATA piat = (PIMAGE_THUNK_DATA)((char *)ext + desc->FirstThunk);
- for (; piat->u1.Function; piat++, pint++) {
- static const char prefix[] = "rb_";
- PIMAGE_IMPORT_BY_NAME pii;
- const char *name;
-
- if (IMAGE_SNAP_BY_ORDINAL(pint->u1.Ordinal)) continue;
- pii = (PIMAGE_IMPORT_BY_NAME)((char *)ext + (size_t)pint->u1.AddressOfData);
- name = (const char *)pii->Name;
- if (strncmp(name, prefix, sizeof(prefix) - 1) == 0) {
- FARPROC addr = GetProcAddress(mine, name);
- if (addr) return (FARPROC)piat->u1.Function == addr;
- }
- }
- desc++;
+ PIMAGE_THUNK_DATA pint = (PIMAGE_THUNK_DATA)((char *)ext + desc->Characteristics);
+ PIMAGE_THUNK_DATA piat = (PIMAGE_THUNK_DATA)((char *)ext + desc->FirstThunk);
+ for (; piat->u1.Function; piat++, pint++) {
+ static const char prefix[] = "rb_";
+ PIMAGE_IMPORT_BY_NAME pii;
+ const char *name;
+
+ if (IMAGE_SNAP_BY_ORDINAL(pint->u1.Ordinal)) continue;
+ pii = (PIMAGE_IMPORT_BY_NAME)((char *)ext + (size_t)pint->u1.AddressOfData);
+ name = (const char *)pii->Name;
+ if (strncmp(name, prefix, sizeof(prefix) - 1) == 0) {
+ FARPROC addr = GetProcAddress(mine, name);
+ if (addr) return (FARPROC)piat->u1.Function == addr;
+ }
+ }
+ desc++;
}
return 1;
}
@@ -252,11 +256,11 @@ rb_w32_check_imported(HMODULE ext, HMODULE mine)
#if defined(DLN_NEEDS_ALT_SEPARATOR) && DLN_NEEDS_ALT_SEPARATOR
#define translit_separator(src) do { \
- char *tmp = ALLOCA_N(char, strlen(src) + 1), *p = tmp, c; \
- do { \
- *p++ = ((c = *file++) == '/') ? DLN_NEEDS_ALT_SEPARATOR : c; \
- } while (c); \
- (src) = tmp; \
+ char *tmp = ALLOCA_N(char, strlen(src) + 1), *p = tmp, c; \
+ do { \
+ *p++ = ((c = *file++) == '/') ? DLN_NEEDS_ALT_SEPARATOR : c; \
+ } while (c); \
+ (src) = tmp; \
} while (0)
#else
#define translit_separator(str) (void)(str)
@@ -273,7 +277,7 @@ dln_incompatible_func(void *handle, const char *funcname, void *const fp, const
if (!ex) return false;
if (ex == fp) return false;
if (dladdr(ex, &dli)) {
- *libname = dli.dli_fname;
+ *libname = dli.dli_fname;
}
return true;
}
@@ -287,7 +291,7 @@ dln_incompatible_library_p(void *handle, const char **libname)
{
#define check_func(func) \
if (dln_incompatible_func(handle, EXTERNAL_PREFIX #func, (void *)&func, libname)) \
- return true
+ return true
check_func(ruby_xmalloc);
return false;
}
@@ -298,15 +302,15 @@ COMPILER_WARNING_POP
/* assume others than old Mac OS X have no problem */
# define dln_disable_dlclose() false
-#elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11
-/* targeting newer versions only */
-# define dln_disable_dlclose() false
-
#elif !defined(MAC_OS_X_VERSION_10_11) || \
(MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11)
/* targeting older versions only */
# define dln_disable_dlclose() true
+#elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11
+/* targeting newer versions only */
+# define dln_disable_dlclose() false
+
#else
/* support both versions, and check at runtime */
# include <sys/sysctl.h>
@@ -337,7 +341,7 @@ dln_open(const char *file)
/* Convert the file path to wide char */
WCHAR *winfile = rb_w32_mbstr_to_wstr(CP_UTF8, file, -1, NULL);
if (!winfile) {
- dln_memerror();
+ dln_memerror();
}
/* Load file */
@@ -345,15 +349,15 @@ dln_open(const char *file)
free(winfile);
if (!handle) {
- error = dln_strerror();
- goto failed;
+ error = dln_strerror();
+ goto failed;
}
# if defined(RUBY_EXPORT)
if (!rb_w32_check_imported(handle, rb_libruby_handle())) {
- FreeLibrary(handle);
- error = incompatible;
- goto failed;
+ FreeLibrary(handle);
+ error = incompatible;
+ goto failed;
}
# endif
@@ -378,24 +382,24 @@ dln_open(const char *file)
# if defined(RUBY_EXPORT)
{
- const char *libruby_name = NULL;
- if (dln_incompatible_library_p(handle, &libruby_name)) {
- if (dln_disable_dlclose()) {
- /* dlclose() segfaults */
- if (libruby_name) {
- dln_fatalerror("linked to incompatible %s - %s", libruby_name, file);
- }
- dln_fatalerror("%s - %s", incompatible, file);
- }
- else {
- dlclose(handle);
- if (libruby_name) {
- dln_loaderror("linked to incompatible %s - %s", libruby_name, file);
- }
- error = incompatible;
- goto failed;
- }
- }
+ const char *libruby_name = NULL;
+ if (dln_incompatible_library_p(handle, &libruby_name)) {
+ if (dln_disable_dlclose()) {
+ /* dlclose() segfaults */
+ if (libruby_name) {
+ dln_fatalerror("linked to incompatible %s - %s", libruby_name, file);
+ }
+ dln_fatalerror("%s - %s", incompatible, file);
+ }
+ else {
+ dlclose(handle);
+ if (libruby_name) {
+ dln_loaderror("linked to incompatible %s - %s", libruby_name, file);
+ }
+ error = incompatible;
+ goto failed;
+ }
+ }
}
# endif
#endif
@@ -471,17 +475,17 @@ dln_load(const char *file)
#elif defined(_AIX)
{
- void (*init_fct)(void);
-
- init_fct = (void(*)(void))load((char*)file, 1, 0);
- if (init_fct == NULL) {
- aix_loaderror(file);
- }
- if (loadbind(0, (void*)dln_load, (void*)init_fct) == -1) {
- aix_loaderror(file);
- }
- (*init_fct)();
- return (void*)init_fct;
+ void (*init_fct)(void);
+
+ init_fct = (void(*)(void))load((char*)file, 1, 0);
+ if (init_fct == NULL) {
+ aix_loaderror(file);
+ }
+ if (loadbind(0, (void*)dln_load, (void*)init_fct) == -1) {
+ aix_loaderror(file);
+ }
+ (*init_fct)();
+ return (void*)init_fct;
}
#else
dln_notimplement();
diff --git a/dln_find.c b/dln_find.c
index 96e06d34c4..5d380f5d39 100644
--- a/dln_find.c
+++ b/dln_find.c
@@ -53,26 +53,26 @@ char *getenv();
#endif
static char *dln_find_1(const char *fname, const char *path, char *buf, size_t size, int exe_flag
- DLN_FIND_EXTRA_ARG_DECL);
+ DLN_FIND_EXTRA_ARG_DECL);
char *
dln_find_exe_r(const char *fname, const char *path, char *buf, size_t size
- DLN_FIND_EXTRA_ARG_DECL)
+ DLN_FIND_EXTRA_ARG_DECL)
{
char *envpath = 0;
if (!path) {
- path = getenv(PATH_ENV);
- if (path) path = envpath = strdup(path);
+ path = getenv(PATH_ENV);
+ if (path) path = envpath = strdup(path);
}
if (!path) {
- path =
- "/usr/local/bin" PATH_SEP
- "/usr/ucb" PATH_SEP
- "/usr/bin" PATH_SEP
- "/bin" PATH_SEP
- ".";
+ path =
+ "/usr/local/bin" PATH_SEP
+ "/usr/ucb" PATH_SEP
+ "/usr/bin" PATH_SEP
+ "/bin" PATH_SEP
+ ".";
}
buf = dln_find_1(fname, path, buf, size, 1 DLN_FIND_EXTRA_ARG);
if (envpath) free(envpath);
@@ -81,7 +81,7 @@ dln_find_exe_r(const char *fname, const char *path, char *buf, size_t size
char *
dln_find_file_r(const char *fname, const char *path, char *buf, size_t size
- DLN_FIND_EXTRA_ARG_DECL)
+ DLN_FIND_EXTRA_ARG_DECL)
{
if (!path) path = ".";
return dln_find_1(fname, path, buf, size, 0 DLN_FIND_EXTRA_ARG);
@@ -89,8 +89,8 @@ dln_find_file_r(const char *fname, const char *path, char *buf, size_t size
static char *
dln_find_1(const char *fname, const char *path, char *fbuf, size_t size,
- int exe_flag /* non 0 if looking for executable. */
- DLN_FIND_EXTRA_ARG_DECL)
+ int exe_flag /* non 0 if looking for executable. */
+ DLN_FIND_EXTRA_ARG_DECL)
{
register const char *dp;
register const char *ep;
@@ -99,7 +99,7 @@ dln_find_1(const char *fname, const char *path, char *fbuf, size_t size,
size_t i, fnlen, fspace;
#ifdef DOSISH
static const char extension[][5] = {
- EXECUTABLE_EXTS,
+ EXECUTABLE_EXTS,
};
size_t j;
int is_abs = 0, has_path = 0;
@@ -110,21 +110,21 @@ dln_find_1(const char *fname, const char *path, char *fbuf, size_t size,
static const char pathname_too_long[] = "openpath: pathname too long (ignored)\n\
\tDirectory \"%.*s\"%s\n\tFile \"%.*s\"%s\n";
#define PATHNAME_TOO_LONG() dln_warning(dln_warning_arg pathname_too_long, \
- ((bp - fbuf) > 100 ? 100 : (int)(bp - fbuf)), fbuf, \
- ((bp - fbuf) > 100 ? "..." : ""), \
- (fnlen > 100 ? 100 : (int)fnlen), fname, \
- (fnlen > 100 ? "..." : ""))
+ ((bp - fbuf) > 100 ? 100 : (int)(bp - fbuf)), fbuf, \
+ ((bp - fbuf) > 100 ? "..." : ""), \
+ (fnlen > 100 ? 100 : (int)fnlen), fname, \
+ (fnlen > 100 ? "..." : ""))
#define RETURN_IF(expr) if (expr) return (char *)fname;
RETURN_IF(!fname);
fnlen = strlen(fname);
if (fnlen >= size) {
- dln_warning(dln_warning_arg
- "openpath: pathname too long (ignored)\n\tFile \"%.*s\"%s\n",
- (fnlen > 100 ? 100 : (int)fnlen), fname,
- (fnlen > 100 ? "..." : ""));
- return NULL;
+ dln_warning(dln_warning_arg
+ "openpath: pathname too long (ignored)\n\tFile \"%.*s\"%s\n",
+ (fnlen > 100 ? 100 : (int)fnlen), fname,
+ (fnlen > 100 ? "..." : ""));
+ return NULL;
}
#ifdef DOSISH
# ifndef CharNext
@@ -132,52 +132,52 @@ dln_find_1(const char *fname, const char *path, char *fbuf, size_t size,
# endif
# ifdef DOSISH_DRIVE_LETTER
if (((p[0] | 0x20) - 'a') < 26 && p[1] == ':') {
- p += 2;
- is_abs = 1;
+ p += 2;
+ is_abs = 1;
}
# endif
switch (*p) {
case '/': case '\\':
- is_abs = 1;
- p++;
+ is_abs = 1;
+ p++;
}
has_path = is_abs;
while (*p) {
- switch (*p) {
- case '/': case '\\':
- has_path = 1;
- ext = 0;
- p++;
- break;
- case '.':
- ext = p;
- p++;
- break;
- default:
- p = CharNext(p);
- }
+ switch (*p) {
+ case '/': case '\\':
+ has_path = 1;
+ ext = 0;
+ p++;
+ break;
+ case '.':
+ ext = p;
+ p++;
+ break;
+ default:
+ p = CharNext(p);
+ }
}
if (ext) {
- for (j = 0; STRCASECMP(ext, extension[j]); ) {
- if (++j == sizeof(extension) / sizeof(extension[0])) {
- ext = 0;
- break;
- }
- }
+ for (j = 0; STRCASECMP(ext, extension[j]); ) {
+ if (++j == sizeof(extension) / sizeof(extension[0])) {
+ ext = 0;
+ break;
+ }
+ }
}
ep = bp = 0;
if (!exe_flag) {
- RETURN_IF(is_abs);
+ RETURN_IF(is_abs);
}
else if (has_path) {
- RETURN_IF(ext);
- i = p - fname;
- if (i + 1 > size) goto toolong;
- fspace = size - i - 1;
- bp = fbuf;
- ep = p;
- memcpy(fbuf, fname, i + 1);
- goto needs_extension;
+ RETURN_IF(ext);
+ i = p - fname;
+ if (i + 1 > size) goto toolong;
+ fspace = size - i - 1;
+ bp = fbuf;
+ ep = p;
+ memcpy(fbuf, fname, i + 1);
+ goto needs_extension;
}
p = fname;
#endif
@@ -189,85 +189,85 @@ dln_find_1(const char *fname, const char *path, char *fbuf, size_t size,
#undef RETURN_IF
for (dp = path;; dp = ++ep) {
- register size_t l;
-
- /* extract a component */
- ep = strchr(dp, PATH_SEP[0]);
- if (ep == NULL)
- ep = dp+strlen(dp);
-
- /* find the length of that component */
- l = ep - dp;
- bp = fbuf;
- fspace = size - 2;
- if (l > 0) {
- /*
- ** If the length of the component is zero length,
- ** start from the current directory. If the
- ** component begins with "~", start from the
- ** user's $HOME environment variable. Otherwise
- ** take the path literally.
- */
-
- if (*dp == '~' && (l == 1 ||
+ register size_t l;
+
+ /* extract a component */
+ ep = strchr(dp, PATH_SEP[0]);
+ if (ep == NULL)
+ ep = dp+strlen(dp);
+
+ /* find the length of that component */
+ l = ep - dp;
+ bp = fbuf;
+ fspace = size - 2;
+ if (l > 0) {
+ /*
+ ** If the length of the component is zero length,
+ ** start from the current directory. If the
+ ** component begins with "~", start from the
+ ** user's $HOME environment variable. Otherwise
+ ** take the path literally.
+ */
+
+ if (*dp == '~' && (l == 1 ||
#if defined(DOSISH)
- dp[1] == '\\' ||
+ dp[1] == '\\' ||
#endif
- dp[1] == '/')) {
- const char *home;
-
- home = getenv("HOME");
- if (home != NULL) {
- i = strlen(home);
- if (fspace < i)
- goto toolong;
- fspace -= i;
- memcpy(bp, home, i);
- bp += i;
- }
- dp++;
- l--;
- }
- if (l > 0) {
- if (fspace < l)
- goto toolong;
- fspace -= l;
- memcpy(bp, dp, l);
- bp += l;
- }
-
- /* add a "/" between directory and filename */
- if (ep[-1] != '/')
- *bp++ = '/';
- }
-
- /* now append the file name */
- i = fnlen;
- if (fspace < i) {
+ dp[1] == '/')) {
+ const char *home;
+
+ home = getenv("HOME");
+ if (home != NULL) {
+ i = strlen(home);
+ if (fspace < i)
+ goto toolong;
+ fspace -= i;
+ memcpy(bp, home, i);
+ bp += i;
+ }
+ dp++;
+ l--;
+ }
+ if (l > 0) {
+ if (fspace < l)
+ goto toolong;
+ fspace -= l;
+ memcpy(bp, dp, l);
+ bp += l;
+ }
+
+ /* add a "/" between directory and filename */
+ if (ep[-1] != '/')
+ *bp++ = '/';
+ }
+
+ /* now append the file name */
+ i = fnlen;
+ if (fspace < i) {
goto toolong;
- }
- fspace -= i;
- memcpy(bp, fname, i + 1);
+ }
+ fspace -= i;
+ memcpy(bp, fname, i + 1);
#if defined(DOSISH)
- if (exe_flag && !ext) {
+ if (exe_flag && !ext) {
goto needs_extension;
- }
+ }
#endif
#ifndef S_ISREG
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
- if (stat(fbuf, &st) == 0 && S_ISREG(st.st_mode)) {
- if (exe_flag == 0) return fbuf;
- /* looking for executable */
- if (eaccess(fbuf, X_OK) == 0) return fbuf;
- }
+ if (stat(fbuf, &st) == 0 && S_ISREG(st.st_mode)) {
+ if (exe_flag == 0) return fbuf;
+ /* looking for executable */
+ if (eaccess(fbuf, X_OK) == 0) return fbuf;
+ }
next:
- /* if not, and no other alternatives, life is bleak */
- if (*ep == '\0') {
- return NULL;
- }
+ /* if not, and no other alternatives, life is bleak */
+ if (*ep == '\0') {
+ return NULL;
+ }
continue;
toolong:
@@ -287,6 +287,6 @@ dln_find_1(const char *fname, const char *path, char *fbuf, size_t size,
}
goto next;
#endif
- /* otherwise try the next component in the search path */
+ /* otherwise try the next component in the search path */
}
}
diff --git a/dmyenc.c b/dmyenc.c
index 7e006e826c..75b8a2da43 100644
--- a/dmyenc.c
+++ b/dmyenc.c
@@ -5,6 +5,6 @@ void
Init_enc(void)
{
if (require("enc/encdb.so") == 1) {
- require("enc/trans/transdb.so");
+ require("enc/trans/transdb.so");
}
}
diff --git a/doc/.document b/doc/.document
index 03d332367c..f589dda07c 100644
--- a/doc/.document
+++ b/doc/.document
@@ -5,3 +5,5 @@ contributing
NEWS
syntax
optparse
+rdoc
+yjit
diff --git a/doc/ChangeLog-2.3.0 b/doc/ChangeLog-2.3.0
index 629fd9c4ec..94996cffd0 100644
--- a/doc/ChangeLog-2.3.0
+++ b/doc/ChangeLog-2.3.0
@@ -170,7 +170,7 @@ Tue Dec 22 14:31:28 2015 Toru Iwase <tietew@tietew.net>
should return unfrozen new string.
[ruby-core:72426] [Bug #11858]
-Tue Dec 22 05:39:58 2015 Takashi Kokubun <takashikkbn@gmail.com>
+Tue Dec 22 05:39:58 2015 Takashi Kokubun <k0kubun@ruby-lang.org>
* ext/cgi/escape/escape.c (preserve_original_state): Preserve
original state for tainted and frozen. [Fix GH-1166]
@@ -208,7 +208,7 @@ Mon Dec 21 09:33:17 2015 Karol Bucek <kares@users.noreply.github.com>
* ext/openssl/lib/openssl/ssl.rb (OpenSSL::SSL::SSLSocket): fix
NotImplementedError typo. [Fix GH-1165]
-Sun Dec 20 20:54:51 2015 Takashi Kokubun <takashikkbn@gmail.com>
+Sun Dec 20 20:54:51 2015 Takashi Kokubun <k0kubun@ruby-lang.org>
* cgi/escape/escape.c: Optimize CGI.escapeHTML for
ASCII-compatible encodings. [Fix GH-1164]
@@ -476,7 +476,7 @@ Tue Dec 15 17:57:57 2015 Martin Duerst <duerst@it.aoyama.ac.jp>
to the correct one in the IANA registry (IBM037)
and added an alias (ebcdic-cp-us)
-Tue Dec 15 16:19:26 2015 Takashi Kokubun <takashikkbn@gmail.com>
+Tue Dec 15 16:19:26 2015 Takashi Kokubun <k0kubun@ruby-lang.org>
* lib/erb.rb: Render erb with array buffer for function call optimization.
[fix GH-1143]
@@ -488,7 +488,7 @@ Tue Dec 15 13:50:05 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
* string.c (rb_str_oct): [DOC] mention radix indicators.
[ruby-core:71310] [Bug #11648]
-Tue Dec 15 12:20:30 2015 Takashi Kokubun <takashikkbn@gmail.com>
+Tue Dec 15 12:20:30 2015 Takashi Kokubun <k0kubun@ruby-lang.org>
* lib/erb.rb: Simplify regexp to optimize erb scanner.
[fix GH-1144]
@@ -2670,7 +2670,7 @@ Sat Nov 7 09:51:38 2015 Koichi Sasada <ko1@atdot.net>
* vm_trace.c (rb_threadptr_exec_event_hooks_orig):
maintain trace_running counter on internal events.
- This patch is made by Takashi Kokubun <takashikkbn@gmail.com>.
+ This patch is made by Takashi Kokubun <k0kubun@ruby-lang.org>.
[Bug #11603] https://github.com/ruby/ruby/pull/1059
Sat Nov 7 03:32:27 2015 Koichi Sasada <ko1@atdot.net>
diff --git a/doc/NEWS/NEWS-3.0.0.md b/doc/NEWS/NEWS-3.0.0.md
index 00c26fe585..bdbd47327b 100644
--- a/doc/NEWS/NEWS-3.0.0.md
+++ b/doc/NEWS/NEWS-3.0.0.md
@@ -512,6 +512,18 @@ Outstanding ones only.
* This version is Ractor compatible.
+* URI
+
+ * URI.escape and URI.unescape have been removed.
+ Instead, use the following methods depending on your specific use case.
+
+ * CGI.escape
+ * URI.encode_www_form
+ * URI.encode_www_form_component
+ * CGI.unescape
+ * URI.decode_www_form
+ * URI.decode_www_form_component
+
## Compatibility issues
Excluding feature bug fixes.
diff --git a/doc/command_injection.rdoc b/doc/command_injection.rdoc
index 8f1303bcf7..af09be23f0 100644
--- a/doc/command_injection.rdoc
+++ b/doc/command_injection.rdoc
@@ -8,7 +8,7 @@ They should not be called with unknown or unsanitized commands.
These methods include:
- Kernel.system
-- {`command` (backtick method)}[rdoc-ref:Kernel#`]
+- {\`command` (backtick method)}[rdoc-ref:Kernel#`]
(also called by the expression <tt>%x[command]</tt>).
- IO.popen(command).
- IO.read(command).
diff --git a/doc/contributing/building_ruby.md b/doc/contributing/building_ruby.md
index 52d6042ec3..469c9d8361 100644
--- a/doc/contributing/building_ruby.md
+++ b/doc/contributing/building_ruby.md
@@ -6,7 +6,7 @@
* C compiler
* autoconf - 2.67 or later
- * bison - 2.0 or later
+ * bison - 3.0 or later
* gperf - 3.0.3 or later
* ruby - 2.7 or later
@@ -18,6 +18,7 @@
* libffi
* libyaml
* libexecinfo (FreeBSD)
+ * rustc - 1.58.0 or later (if you wish to build [YJIT](/doc/yjit/yjit.md))
3. Checkout the CRuby source code:
@@ -25,23 +26,54 @@
git clone https://github.com/ruby/ruby.git
```
-4. Generate the configuration files and build. It's generally advisable to use a build directory:
+4. Generate the configure file:
```
./autogen.sh
- mkdir build && cd build # it's good practice to build outside of source dir
- mkdir ~/.rubies # we will install to .rubies/ruby-master in our home dir
+ ```
+
+5. Create a `build` directory outside of the source directory:
+
+ ```
+ mkdir build && cd build
+ ```
+
+ While it's not necessary to build in a separate directory, it's good practice to do so.
+
+6. We'll install Ruby in `~/.rubies/ruby-master`, so create the directory:
+
+ ```
+ mkdir ~/.rubies
+ ```
+
+7. Run configure:
+
+ ```
../configure --prefix="${HOME}/.rubies/ruby-master"
- make install
```
-5. Optional: If you are frequently building Ruby, disabling documentation will reduce the time it takes to `make`:
+ - If you are frequently building Ruby, add the `--disable-install-doc` flag to not build documentation which will speed up the build process.
- ``` shell
- ../configure --disable-install-doc
+8. Build Ruby:
+
+ ```
+ make install
```
-6. [Run tests](testing_ruby.md) to confirm your build succeeded
+ - If you're on macOS and installed \OpenSSL through Homebrew, you may encounter failure to build \OpenSSL that look like this:
+
+ ```
+ openssl:
+ Could not be configured. It will not be installed.
+ ruby/ext/openssl/extconf.rb: OpenSSL library could not be found. You might want to use --with-openssl-dir=<dir> option to specify the prefix where OpenSSL is installed.
+ Check ext/openssl/mkmf.log for more details.
+ ```
+
+ Adding `--with-openssl-dir=$(brew --prefix openssl)` to the list of options passed to configure may solve the issue.
+
+ Remember to delete your `build` directory and start again from the configure step.
+
+9. [Run tests](testing_ruby.md) to confirm your build succeeded.
### Unexplainable Build Errors
diff --git a/doc/contributing/documentation_guide.md b/doc/contributing/documentation_guide.md
index f011841809..9cfd59d629 100644
--- a/doc/contributing/documentation_guide.md
+++ b/doc/contributing/documentation_guide.md
@@ -137,6 +137,19 @@ or [list](rdoc-ref:RDoc::Markup@Simple+Lists)
should be preceded by and followed by a blank line.
This is unnecessary for the HTML output, but helps in the `ri` output.
+### \Method Names
+
+For a method name in text:
+
+- For a method in the current class or module,
+ use a double-colon for a singleton method,
+ or a hash mark for an instance method:
+ <tt>::bar</tt>, <tt>#baz</tt>.
+- Otherwise, include the class or module name
+ and use a dot for a singleton method,
+ or a hash mark for an instance method:
+ <tt>Foo.bar</tt>, <tt>Foo#baz</tt>.
+
### Auto-Linking
In general, \RDoc's auto-linking should not be suppressed.
@@ -151,6 +164,28 @@ We might consider whether to suppress when:
- The same reference is repeated many times
(e.g., _RDoc_ on this page).
+### HTML Tags
+
+In general, avoid using HTML tags (even in formats where it's allowed)
+because `ri` (the Ruby Interactive reference tool)
+may not render them properly.
+
+### Tables
+
+In particular, avoid building tables with HTML tags
+(<tt><table></tt>, etc.).
+
+Alternatives are:
+
+- The GFM (GitHub Flavored Markdown) table extension,
+ which is enabled by default. See
+ {GFM tables extension}[https://github.github.com/gfm/#tables-extension-].
+
+- A {verbatim text block}[rdoc-ref:RDoc::MarkupReference@Verbatim+Text+Blocks],
+ using spaces and punctuation to format the text.
+ Note that {text markup}[rdoc-ref:RDoc::MarkupReference@Text+Markup]
+ will not be honored.
+
## Documenting Classes and Modules
The general structure of the class or module documentation should be:
@@ -376,12 +411,22 @@ Mention aliases in the form
In some cases, it is useful to document which methods are related to
the current method. For example, documentation for `Hash#[]` might
mention `Hash#fetch` as a related method, and `Hash#merge` might mention
-`Hash#merge!` as a related method. Consider which methods may be related
-to the current method, and if you think the reader would benefit it,
-at the end of the method documentation, add a line starting with
-"Related: " (e.g. "Related: #fetch"). Don't list more than three
-related methods. If you think more than three methods are related,
-pick the three you think are most important and list those three.
+`Hash#merge!` as a related method.
+
+- Consider which methods may be related
+ to the current method, and if you think the reader would benefit it,
+ at the end of the method documentation, add a line starting with
+ "Related: " (e.g. "Related: #fetch.").
+- Don't list more than three related methods.
+ If you think more than three methods are related,
+ list the three you think are most important.
+- Consider adding:
+
+ - A phrase suggesting how the related method is similar to,
+ or different from,the current method.
+ See an example at Time#getutc.
+ - Example code that illustrates the similarities and differences.
+ See examples at Time#ctime, Time#inspect, Time#to_s.
### Methods Accepting Multiple Argument Types
diff --git a/doc/contributing/testing_ruby.md b/doc/contributing/testing_ruby.md
index dd46ba5dbc..6247686efc 100644
--- a/doc/contributing/testing_ruby.md
+++ b/doc/contributing/testing_ruby.md
@@ -20,6 +20,13 @@ We can run any of the make scripts [in parallel](building_ruby.md#label-Running+
make btest OPTS=-v
```
+ To run individual bootstrap tests, we can either specify a list of filenames or use the `--sets` flag in the variable `BTESTS`:
+
+ ```
+ make btest BTESTS="bootstraptest/test_fork.rb bootstraptest/tes_gc.rb"
+ make btest BTESTS="--sets=fork,gc"
+ ```
+
If we want to run the bootstrap test suite on Ruby (not Miniruby), we can use:
```
diff --git a/doc/date/calendars.rdoc b/doc/date/calendars.rdoc
new file mode 100644
index 0000000000..b8690841b1
--- /dev/null
+++ b/doc/date/calendars.rdoc
@@ -0,0 +1,62 @@
+== Julian and Gregorian Calendars
+
+The difference between the
+{Julian calendar}[https://en.wikipedia.org/wiki/Julian_calendar]
+and the
+{Gregorian calendar}[https://en.wikipedia.org/wiki/Gregorian_calendar]
+may matter to your program if it uses dates before the switchovers.
+
+- October 15, 1582.
+- September 14, 1752.
+
+A date will be different in the two calendars, in general.
+
+=== Different switchover dates
+
+The reasons for the difference are religious/political histories.
+
+- On October 15, 1582, several countries changed
+ from the Julian calendar to the Gregorian calendar;
+ these included Italy, Poland, Portugal, and Spain.
+ Other contries in the Western world retained the Julian calendar.
+- On September 14, 1752, most of the British empire
+ changed from the Julian calendar to the Gregorian calendar.
+
+When your code uses a date before these switchover dates,
+it will matter whether it considers the switchover date
+to be the earlier date or the later date (or neither).
+
+See also {a concrete example here}[rdoc-ref:DateTime@When+should+you+use+DateTime+and+when+should+you+use+Time-3F].
+
+=== Argument +start+
+
+Certain methods in class \Date handle differences in the
+{Julian and Gregorian calendars}[rdoc-ref:calendars.rdoc@Julian+and+Gregorian+Calendars]
+by accepting an optional argument +start+, whose value may be:
+
+- Date::ITALY (the default): the created date is Julian
+ if before October 15, 1582, Gregorian otherwise:
+
+ d = Date.new(1582, 10, 15)
+ d.prev_day.julian? # => true
+ d.julian? # => false
+ d.gregorian? # => true
+
+- Date::ENGLAND: the created date is Julian if before September 14, 1752,
+ Gregorian otherwise:
+
+ d = Date.new(1752, 9, 14, Date::ENGLAND)
+ d.prev_day.julian? # => true
+ d.julian? # => false
+ d.gregorian? # => true
+
+- Date::JULIAN: the created date is Julian regardless of its value:
+
+ d = Date.new(1582, 10, 15, Date::JULIAN)
+ d.julian? # => true
+
+- Date::GREGORIAN: the created date is Gregorian regardless of its value:
+
+ d = Date.new(1752, 9, 14, Date::GREGORIAN)
+ d.prev_day.gregorian? # => true
+
diff --git a/doc/encodings.rdoc b/doc/encodings.rdoc
index c61ab11e9a..1f3c54d740 100644
--- a/doc/encodings.rdoc
+++ b/doc/encodings.rdoc
@@ -467,12 +467,13 @@ These keyword-value pairs specify encoding options:
with a carriage-return character (<tt>"\r"</tt>).
- <tt>:crlf_newline: true</tt>: Replace each line-feed character (<tt>"\n"</tt>)
with a carriage-return/line-feed string (<tt>"\r\n"</tt>).
- - <tt>:universal_newline: true</tt>: Replace each carriage-return/line-feed string
+ - <tt>:universal_newline: true</tt>: Replace each carriage-return
+ character (<tt>"\r"</tt>) and each carriage-return/line-feed string
(<tt>"\r\n"</tt>) with a line-feed character (<tt>"\n"</tt>).
Examples:
- s = "\n \r\n" # => "\n \r\n"
- s.encode('ASCII', cr_newline: true) # => "\r \r\r"
- s.encode('ASCII', crlf_newline: true) # => "\r\n \r\r\n"
- s.encode('ASCII', universal_newline: true) # => "\n \n"
+ s = "\n \r \r\n" # => "\n \r \r\n"
+ s.encode('ASCII', cr_newline: true) # => "\r \r \r\r"
+ s.encode('ASCII', crlf_newline: true) # => "\r\n \r \r\r\n"
+ s.encode('ASCII', universal_newline: true) # => "\n \n \n"
diff --git a/doc/examples/files.rdoc b/doc/examples/files.rdoc
new file mode 100644
index 0000000000..f736132770
--- /dev/null
+++ b/doc/examples/files.rdoc
@@ -0,0 +1,26 @@
+# English text with newlines.
+text = <<~EOT
+ First line
+ Second line
+
+ Fourth line
+ Fifth line
+EOT
+
+# Russian text.
+russian = "\u{442 435 441 442}" # => "тест"
+
+# Binary data.
+data = "\u9990\u9991\u9992\u9993\u9994"
+
+# Text file.
+File.write('t.txt', text)
+
+# File with Russian text.
+File.write('t.rus', russian)
+
+# File with binary data.
+f = File.new('t.dat', 'wb:UTF-16')
+f.write(data)
+f.close
+
diff --git a/doc/mjit/mjit.md b/doc/mjit/mjit.md
new file mode 100644
index 0000000000..6f19ab3ea7
--- /dev/null
+++ b/doc/mjit/mjit.md
@@ -0,0 +1,39 @@
+# MJIT
+
+This document has some tips that might be useful when you work on MJIT.
+
+## Supported platforms
+
+The following platforms are either tested on CI or assumed to work.
+
+* OS: Linux, macOS
+* Arch: x86\_64, aarch64, arm64, i686, i386
+
+### Not supported
+
+The MJIT support for the following platforms is no longer maintained.
+
+* OS: Windows (mswin, MinGW), Solaris
+* Arch: SPARC, s390x
+
+## Developing MJIT
+
+### Bindgen
+
+If you see an "MJIT bindgen" GitHub Actions failure, please commit the `git diff` shown on the failed job.
+
+For doing the same thing locally, run `make mjit-bindgen` after installing libclang.
+macOS seems to have libclang by default. On Ubuntu, you can install it with `apt install libclang1`.
+
+### Always run make install
+
+Always run `make install` before running MJIT. It could easily cause a SEGV if you don't.
+MJIT looks for the installed header for security reasons.
+
+### --mjit-debug vs --mjit-debug=-ggdb3
+
+`--mjit-debug=[flags]` allows you to specify arbitrary flags while keeping other compiler flags like `-O3`,
+which is useful for profiling benchmarks.
+
+`--mjit-debug` alone, on the other hand, disables `-O3` and adds debug flags.
+If you're debugging MJIT, what you need to use is not `--mjit-debug=-ggdb3` but `--mjit-debug`.
diff --git a/doc/net-http/examples.rdoc b/doc/net-http/examples.rdoc
new file mode 100644
index 0000000000..c1366e7ad1
--- /dev/null
+++ b/doc/net-http/examples.rdoc
@@ -0,0 +1,31 @@
+Examples here assume that <tt>net/http</tt> has been required
+(which also requires +uri+):
+
+ require 'net/http'
+
+Many code examples here use these example websites:
+
+- https://jsonplaceholder.typicode.com.
+- http://example.com.
+
+Some examples also assume these variables:
+
+ uri = URI('https://jsonplaceholder.typicode.com/')
+ uri.freeze # Examples may not modify.
+ hostname = uri.hostname # => "jsonplaceholder.typicode.com"
+ path = uri.path # => "/"
+ port = uri.port # => 443
+
+So that example requests may be written as:
+
+ Net::HTTP.get(uri)
+ Net::HTTP.get(hostname, '/index.html')
+ Net::HTTP.start(hostname) do |http|
+ http.get('/todos/1')
+ http.get('/todos/2')
+ end
+
+An example that needs a modified URI first duplicates +uri+, then modifies the duplicate:
+
+ _uri = uri.dup
+ _uri.path = '/todos/1'
diff --git a/doc/net-http/included_getters.rdoc b/doc/net-http/included_getters.rdoc
new file mode 100644
index 0000000000..7ac327f4b4
--- /dev/null
+++ b/doc/net-http/included_getters.rdoc
@@ -0,0 +1,3 @@
+This class also includes (indirectly) module Net::HTTPHeader,
+which gives access to its
+{methods for getting headers}[rdoc-ref:Net::HTTPHeader@Getters].
diff --git a/doc/packed_data.rdoc b/doc/packed_data.rdoc
new file mode 100644
index 0000000000..ec13b24c69
--- /dev/null
+++ b/doc/packed_data.rdoc
@@ -0,0 +1,590 @@
+== Packed \Data
+
+Certain Ruby core methods deal with packing and unpacking data:
+
+- \Method Array#pack:
+ Formats each element in array +self+ into a binary string;
+ returns that string.
+- \Method String#unpack:
+ Extracts data from string +self+,
+ forming objects that become the elements of a new array;
+ returns that array.
+- \Method String#unpack1:
+ Does the same, but unpacks and returns only the first extracted object.
+
+Each of these methods accepts a string +template+,
+consisting of zero or more _directive_ characters,
+each followed by zero or more _modifier_ characters.
+
+Examples (directive <tt>'C'</tt> specifies 'unsigned character'):
+
+ [65].pack('C') # => "A" # One element, one directive.
+ [65, 66].pack('CC') # => "AB" # Two elements, two directives.
+ [65, 66].pack('C') # => "A" # Extra element is ignored.
+ [65].pack('') # => "" # No directives.
+ [65].pack('CC') # Extra directive raises ArgumentError.
+
+ 'A'.unpack('C') # => [65] # One character, one directive.
+ 'AB'.unpack('CC') # => [65, 66] # Two characters, two directives.
+ 'AB'.unpack('C') # => [65] # Extra character is ignored.
+ 'A'.unpack('CC') # => [65, nil] # Extra directive generates nil.
+ 'AB'.unpack('') # => [] # No directives.
+
+The string +template+ may contain any mixture of valid directives
+(directive <tt>'c'</tt> specifies 'signed character'):
+
+ [65, -1].pack('cC') # => "A\xFF"
+ "A\xFF".unpack('cC') # => [65, 255]
+
+The string +template+ may contain whitespace (which is ignored)
+and comments, each of which begins with character <tt>'#'</tt>
+and continues up to and including the next following newline:
+
+ [0,1].pack(" C #foo \n C ") # => "\x00\x01"
+ "\0\1".unpack(" C #foo \n C ") # => [0, 1]
+
+Any directive may be followed by either of these modifiers:
+
+- <tt>'*'</tt> - The directive is to be applied as many times as needed:
+
+ [65, 66].pack('C*') # => "AB"
+ 'AB'.unpack('C*') # => [65, 66]
+
+- Integer +count+ - The directive is to be applied +count+ times:
+
+ [65, 66].pack('C2') # => "AB"
+ [65, 66].pack('C3') # Raises ArgumentError.
+ 'AB'.unpack('C2') # => [65, 66]
+ 'AB'.unpack('C3') # => [65, 66, nil]
+
+ Note: Directives in <tt>%w[A a Z m]</tt> use +count+ differently;
+ see {String Directives}[rdoc-ref:packed_data.rdoc@String+Directives].
+
+If elements don't fit the provided directive, only least significant bits are encoded:
+
+ [257].pack("C").unpack("C") # => [1]
+
+=== Packing \Method
+
+\Method Array#pack accepts optional keyword argument
++buffer+ that specifies the target string (instead of a new string):
+
+ [65, 66].pack('C*', buffer: 'foo') # => "fooAB"
+
+The method can accept a block:
+
+ # Packed string is passed to the block.
+ [65, 66].pack('C*') {|s| p s } # => "AB"
+
+=== Unpacking Methods
+
+Methods String#unpack and String#unpack1 each accept
+an optional keyword argument +offset+ that specifies an offset
+into the string:
+
+ 'ABC'.unpack('C*', offset: 1) # => [66, 67]
+ 'ABC'.unpack1('C*', offset: 1) # => 66
+
+Both methods can accept a block:
+
+ # Each unpacked object is passed to the block.
+ ret = []
+ "ABCD".unpack("C*") {|c| ret << c }
+ ret # => [65, 66, 67, 68]
+
+ # The single unpacked object is passed to the block.
+ 'AB'.unpack1('C*') {|ele| p ele } # => 65
+
+=== \Integer Directives
+
+Each integer directive specifies the packing or unpacking
+for one element in the input or output array.
+
+==== 8-Bit \Integer Directives
+
+- <tt>'c'</tt> - 8-bit signed integer
+ (like C <tt>signed char</tt>):
+
+ [0, 1, 255].pack('c*') # => "\x00\x01\xFF"
+ s = [0, 1, -1].pack('c*') # => "\x00\x01\xFF"
+ s.unpack('c*') # => [0, 1, -1]
+
+- <tt>'C'</tt> - 8-bit signed integer
+ (like C <tt>unsigned char</tt>):
+
+ [0, 1, 255].pack('C*') # => "\x00\x01\xFF"
+ s = [0, 1, -1].pack('C*') # => "\x00\x01\xFF"
+ s.unpack('C*') # => [0, 1, 255]
+
+==== 16-Bit \Integer Directives
+
+- <tt>'s'</tt> - 16-bit signed integer, native-endian
+ (like C <tt>int16_t</tt>):
+
+ [513, -514].pack('s*') # => "\x01\x02\xFE\xFD"
+ s = [513, 65022].pack('s*') # => "\x01\x02\xFE\xFD"
+ s.unpack('s*') # => [513, -514]
+
+- <tt>'S'</tt> - 16-bit unsigned integer, native-endian
+ (like C <tt>uint16_t</tt>):
+
+ [513, -514].pack('S*') # => "\x01\x02\xFE\xFD"
+ s = [513, 65022].pack('S*') # => "\x01\x02\xFE\xFD"
+ s.unpack('S*') # => [513, 65022]
+
+- <tt>'n'</tt> - 16-bit network integer, big-endian:
+
+ s = [0, 1, -1, 32767, -32768, 65535].pack('n*')
+ # => "\x00\x00\x00\x01\xFF\xFF\x7F\xFF\x80\x00\xFF\xFF"
+ s.unpack('n*')
+ # => [0, 1, 65535, 32767, 32768, 65535]
+
+- <tt>'v'</tt> - 16-bit VAX integer, little-endian:
+
+ s = [0, 1, -1, 32767, -32768, 65535].pack('v*')
+ # => "\x00\x00\x01\x00\xFF\xFF\xFF\x7F\x00\x80\xFF\xFF"
+ s.unpack('v*')
+ # => [0, 1, 65535, 32767, 32768, 65535]
+
+==== 32-Bit \Integer Directives
+
+- <tt>'l'</tt> - 32-bit signed integer, native-endian
+ (like C <tt>int32_t</tt>):
+
+ s = [67305985, -50462977].pack('l*')
+ # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC"
+ s.unpack('l*')
+ # => [67305985, -50462977]
+
+- <tt>'L'</tt> - 32-bit unsigned integer, native-endian
+ (like C <tt>uint32_t</tt>):
+
+ s = [67305985, 4244504319].pack('L*')
+ # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC"
+ s.unpack('L*')
+ # => [67305985, 4244504319]
+
+- <tt>'N'</tt> - 32-bit network integer, big-endian:
+
+ s = [0,1,-1].pack('N*')
+ # => "\x00\x00\x00\x00\x00\x00\x00\x01\xFF\xFF\xFF\xFF"
+ s.unpack('N*')
+ # => [0, 1, 4294967295]
+
+- <tt>'V'</tt> - 32-bit VAX integer, little-endian:
+
+ s = [0,1,-1].pack('V*')
+ # => "\x00\x00\x00\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF"
+ s.unpack('v*')
+ # => [0, 0, 1, 0, 65535, 65535]
+
+==== 64-Bit \Integer Directives
+
+- <tt>'q'</tt> - 64-bit signed integer, native-endian
+ (like C <tt>int64_t</tt>):
+
+ s = [578437695752307201, -506097522914230529].pack('q*')
+ # => "\x01\x02\x03\x04\x05\x06\a\b\xFF\xFE\xFD\xFC\xFB\xFA\xF9\xF8"
+ s.unpack('q*')
+ # => [578437695752307201, -506097522914230529]
+
+- <tt>'Q'</tt> - 64-bit unsigned integer, native-endian
+ (like C <tt>uint64_t</tt>):
+
+ s = [578437695752307201, 17940646550795321087].pack('Q*')
+ # => "\x01\x02\x03\x04\x05\x06\a\b\xFF\xFE\xFD\xFC\xFB\xFA\xF9\xF8"
+ s.unpack('Q*')
+ # => [578437695752307201, 17940646550795321087]
+
+==== Platform-Dependent \Integer Directives
+
+- <tt>'i'</tt> - Platform-dependent width signed integer,
+ native-endian (like C <tt>int</tt>):
+
+ s = [67305985, -50462977].pack('i*')
+ # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC"
+ s.unpack('i*')
+ # => [67305985, -50462977]
+
+- <tt>'I'</tt> - Platform-dependent width unsigned integer,
+ native-endian (like C <tt>unsigned int</tt>):
+
+ s = [67305985, -50462977].pack('I*')
+ # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC"
+ s.unpack('I*')
+ # => [67305985, 4244504319]
+
+==== Pointer Directives
+
+- <tt>'j'</tt> - 64-bit pointer-width signed integer,
+ native-endian (like C <tt>intptr_t</tt>):
+
+ s = [67305985, -50462977].pack('j*')
+ # => "\x01\x02\x03\x04\x00\x00\x00\x00\xFF\xFE\xFD\xFC\xFF\xFF\xFF\xFF"
+ s.unpack('j*')
+ # => [67305985, -50462977]
+
+- <tt>'j'</tt> - 64-bit pointer-width unsigned integer,
+ native-endian (like C <tt>uintptr_t</tt>):
+
+ s = [67305985, 4244504319].pack('J*')
+ # => "\x01\x02\x03\x04\x00\x00\x00\x00\xFF\xFE\xFD\xFC\x00\x00\x00\x00"
+ s.unpack('J*')
+ # => [67305985, 4244504319]
+
+==== Other \Integer Directives
+:
+- <tt>'U'</tt> - UTF-8 character:
+
+ s = [4194304].pack('U*')
+ # => "\xF8\x90\x80\x80\x80"
+ s.unpack('U*')
+ # => [4194304]
+
+- <tt>'w'</tt> - BER-encoded integer
+ (see {BER enocding}[https://en.wikipedia.org/wiki/X.690#BER_encoding]):
+
+ s = [1073741823].pack('w*')
+ # => "\x83\xFF\xFF\xFF\x7F"
+ s.unpack('w*')
+ # => [1073741823]
+
+==== Modifiers for \Integer Directives
+
+For directives in
+<tt>'i'</tt>,
+<tt>'I'</tt>,
+<tt>'s'</tt>,
+<tt>'S'</tt>,
+<tt>'l'</tt>,
+<tt>'L'</tt>,
+<tt>'q'</tt>,
+<tt>'Q'</tt>,
+<tt>'j'</tt>, and
+<tt>'J'</tt>,
+these modifiers may be suffixed:
+
+- <tt>'!'</tt> or <tt>'_'</tt> - Underlying platform’s native size.
+- <tt>'>'</tt> - Big-endian.
+- <tt>'<'</tt> - Little-endian.
+
+=== \Float Directives
+
+Each float directive specifies the packing or unpacking
+for one element in the input or output array.
+
+==== Single-Precision \Float Directives
+
+- <tt>'F'</tt> or <tt>'f'</tt> - Native format:
+
+ s = [3.0].pack('F') # => "\x00\x00@@"
+ s.unpack('F') # => [3.0]
+
+- <tt>'e'</tt> - Little-endian:
+
+ s = [3.0].pack('e') # => "\x00\x00@@"
+ s.unpack('e') # => [3.0]
+
+- <tt>'g'</tt> - Big-endian:
+
+ s = [3.0].pack('g') # => "@@\x00\x00"
+ s.unpack('g') # => [3.0]
+
+==== Double-Precision \Float Directives
+
+- <tt>'D'</tt> or <tt>'d'</tt> - Native format:
+
+ s = [3.0].pack('D') # => "\x00\x00\x00\x00\x00\x00\b@"
+ s.unpack('D') # => [3.0]
+
+- <tt>'E'</tt> - Little-endian:
+
+ s = [3.0].pack('E') # => "\x00\x00\x00\x00\x00\x00\b@"
+ s.unpack('E') # => [3.0]
+
+- <tt>'G'</tt> - Big-endian:
+
+ s = [3.0].pack('G') # => "@\b\x00\x00\x00\x00\x00\x00"
+ s.unpack('G') # => [3.0]
+
+A float directive may be infinity or not-a-number:
+
+ inf = 1.0/0.0 # => Infinity
+ [inf].pack('f') # => "\x00\x00\x80\x7F"
+ "\x00\x00\x80\x7F".unpack('f') # => [Infinity]
+
+ nan = inf/inf # => NaN
+ [nan].pack('f') # => "\x00\x00\xC0\x7F"
+ "\x00\x00\xC0\x7F".unpack('f') # => [NaN]
+
+=== \String Directives
+
+Each string directive specifies the packing or unpacking
+for one byte in the input or output string.
+
+==== Binary \String Directives
+
+- <tt>'A'</tt> - Arbitrary binary string (space padded; count is width);
+ +nil+ is treated as the empty string:
+
+ ['foo'].pack('A') # => "f"
+ ['foo'].pack('A*') # => "foo"
+ ['foo'].pack('A2') # => "fo"
+ ['foo'].pack('A4') # => "foo "
+ [nil].pack('A') # => " "
+ [nil].pack('A*') # => ""
+ [nil].pack('A2') # => " "
+ [nil].pack('A4') # => " "
+
+ "foo\0".unpack('A') # => ["f"]
+ "foo\0".unpack('A4') # => ["foo"]
+ "foo\0bar".unpack('A10') # => ["foo\x00bar"] # Reads past "\0".
+ "foo ".unpack('A') # => ["f"]
+ "foo ".unpack('A4') # => ["foo"]
+ "foo".unpack('A4') # => ["foo"]
+
+ russian = "\u{442 435 441 442}" # => "тест"
+ russian.size # => 4
+ russian.bytesize # => 8
+ [russian].pack('A') # => "\xD1"
+ [russian].pack('A*') # => "\xD1\x82\xD0\xB5\xD1\x81\xD1\x82"
+ russian.unpack('A') # => ["\xD1"]
+ russian.unpack('A2') # => ["\xD1\x82"]
+ russian.unpack('A4') # => ["\xD1\x82\xD0\xB5"]
+ russian.unpack('A*') # => ["\xD1\x82\xD0\xB5\xD1\x81\xD1\x82"]
+
+- <tt>'a'</tt> - Arbitrary binary string (null padded; count is width):
+
+ ["foo"].pack('a') # => "f"
+ ["foo"].pack('a*') # => "foo"
+ ["foo"].pack('a2') # => "fo"
+ ["foo\0"].pack('a4') # => "foo\x00"
+ [nil].pack('a') # => "\x00"
+ [nil].pack('a*') # => ""
+ [nil].pack('a2') # => "\x00\x00"
+ [nil].pack('a4') # => "\x00\x00\x00\x00"
+
+ "foo\0".unpack('a') # => ["f"]
+ "foo\0".unpack('a4') # => ["foo\x00"]
+ "foo ".unpack('a4') # => ["foo "]
+ "foo".unpack('a4') # => ["foo"]
+ "foo\0bar".unpack('a4') # => ["foo\x00"] # Reads past "\0".
+
+- <tt>'Z'</tt> - Same as <tt>'a'</tt>,
+ except that null is added or ignored with <tt>'*'</tt>:
+
+ ["foo"].pack('Z*') # => "foo\x00"
+ [nil].pack('Z*') # => "\x00"
+
+ "foo\0".unpack('Z*') # => ["foo"]
+ "foo".unpack('Z*') # => ["foo"]
+ "foo\0bar".unpack('Z*') # => ["foo"] # Does not read past "\0".
+
+==== Bit \String Directives
+
+- <tt>'B'</tt> - Bit string (high byte first):
+
+ ['11111111' + '00000000'].pack('B*') # => "\xFF\x00"
+ ['10000000' + '01000000'].pack('B*') # => "\x80@"
+
+ ['1'].pack('B0') # => ""
+ ['1'].pack('B1') # => "\x80"
+ ['1'].pack('B2') # => "\x80\x00"
+ ['1'].pack('B3') # => "\x80\x00"
+ ['1'].pack('B4') # => "\x80\x00\x00"
+ ['1'].pack('B5') # => "\x80\x00\x00"
+ ['1'].pack('B6') # => "\x80\x00\x00\x00"
+
+ "\xff\x00".unpack("B*") # => ["1111111100000000"]
+ "\x01\x02".unpack("B*") # => ["0000000100000010"]
+
+ "".unpack("B0") # => [""]
+ "\x80".unpack("B1") # => ["1"]
+ "\x80".unpack("B2") # => ["10"]
+ "\x80".unpack("B3") # => ["100"]
+
+- <tt>'b'</tt> - Bit string (low byte first):
+
+ ['11111111' + '00000000'].pack('b*') # => "\xFF\x00"
+ ['10000000' + '01000000'].pack('b*') # => "\x01\x02"
+
+ ['1'].pack('b0') # => ""
+ ['1'].pack('b1') # => "\x01"
+ ['1'].pack('b2') # => "\x01\x00"
+ ['1'].pack('b3') # => "\x01\x00"
+ ['1'].pack('b4') # => "\x01\x00\x00"
+ ['1'].pack('b5') # => "\x01\x00\x00"
+ ['1'].pack('b6') # => "\x01\x00\x00\x00"
+
+ "\xff\x00".unpack("b*") # => ["1111111100000000"]
+ "\x01\x02".unpack("b*") # => ["1000000001000000"]
+
+ "".unpack("b0") # => [""]
+ "\x01".unpack("b1") # => ["1"]
+ "\x01".unpack("b2") # => ["10"]
+ "\x01".unpack("b3") # => ["100"]
+
+==== Hex \String Directives
+
+- <tt>'H'</tt> - Hex string (high nibble first):
+
+ ['10ef'].pack('H*') # => "\x10\xEF"
+ ['10ef'].pack('H0') # => ""
+ ['10ef'].pack('H3') # => "\x10\xE0"
+ ['10ef'].pack('H5') # => "\x10\xEF\x00"
+
+ ['fff'].pack('H3') # => "\xFF\xF0"
+ ['fff'].pack('H4') # => "\xFF\xF0"
+ ['fff'].pack('H5') # => "\xFF\xF0\x00"
+ ['fff'].pack('H6') # => "\xFF\xF0\x00"
+ ['fff'].pack('H7') # => "\xFF\xF0\x00\x00"
+ ['fff'].pack('H8') # => "\xFF\xF0\x00\x00"
+
+ "\x10\xef".unpack('H*') # => ["10ef"]
+ "\x10\xef".unpack('H0') # => [""]
+ "\x10\xef".unpack('H1') # => ["1"]
+ "\x10\xef".unpack('H2') # => ["10"]
+ "\x10\xef".unpack('H3') # => ["10e"]
+ "\x10\xef".unpack('H4') # => ["10ef"]
+ "\x10\xef".unpack('H5') # => ["10ef"]
+
+- <tt>'h'</tt> - Hex string (low nibble first):
+
+ ['10ef'].pack('h*') # => "\x01\xFE"
+ ['10ef'].pack('h0') # => ""
+ ['10ef'].pack('h3') # => "\x01\x0E"
+ ['10ef'].pack('h5') # => "\x01\xFE\x00"
+
+ ['fff'].pack('h3') # => "\xFF\x0F"
+ ['fff'].pack('h4') # => "\xFF\x0F"
+ ['fff'].pack('h5') # => "\xFF\x0F\x00"
+ ['fff'].pack('h6') # => "\xFF\x0F\x00"
+ ['fff'].pack('h7') # => "\xFF\x0F\x00\x00"
+ ['fff'].pack('h8') # => "\xFF\x0F\x00\x00"
+
+ "\x01\xfe".unpack('h*') # => ["10ef"]
+ "\x01\xfe".unpack('h0') # => [""]
+ "\x01\xfe".unpack('h1') # => ["1"]
+ "\x01\xfe".unpack('h2') # => ["10"]
+ "\x01\xfe".unpack('h3') # => ["10e"]
+ "\x01\xfe".unpack('h4') # => ["10ef"]
+ "\x01\xfe".unpack('h5') # => ["10ef"]
+
+==== Pointer \String Directives
+
+- <tt>'P'</tt> - Pointer to a structure (fixed-length string):
+
+ s = ['abc'].pack('P') # => "\xE0O\x7F\xE5\xA1\x01\x00\x00"
+ s.unpack('P*') # => ["abc"]
+ ".".unpack("P") # => []
+ ("\0" * 8).unpack("P") # => [nil]
+ [nil].pack("P") # => "\x00\x00\x00\x00\x00\x00\x00\x00"
+
+- <tt>'p'</tt> - Pointer to a null-terminated string:
+
+ s = ['abc'].pack('p') # => "(\xE4u\xE5\xA1\x01\x00\x00"
+ s.unpack('p*') # => ["abc"]
+ ".".unpack("p") # => []
+ ("\0" * 8).unpack("p") # => [nil]
+ [nil].pack("p") # => "\x00\x00\x00\x00\x00\x00\x00\x00"
+
+==== Other \String Directives
+
+- <tt>'M'</tt> - Quoted printable, MIME encoding;
+ text mode, but input must use LF and output LF;
+ (see {RFC 2045}[https://www.ietf.org/rfc/rfc2045.txt]):
+
+ ["a b c\td \ne"].pack('M') # => "a b c\td =\n\ne=\n"
+ ["\0"].pack('M') # => "=00=\n"
+
+ ["a"*1023].pack('M') == ("a"*73+"=\n")*14+"a=\n" # => true
+ ("a"*73+"=\na=\n").unpack('M') == ["a"*74] # => true
+ (("a"*73+"=\n")*14+"a=\n").unpack('M') == ["a"*1023] # => true
+
+ "a b c\td =\n\ne=\n".unpack('M') # => ["a b c\td \ne"]
+ "=00=\n".unpack('M') # => ["\x00"]
+
+ "pre=31=32=33after".unpack('M') # => ["pre123after"]
+ "pre=\nafter".unpack('M') # => ["preafter"]
+ "pre=\r\nafter".unpack('M') # => ["preafter"]
+ "pre=".unpack('M') # => ["pre="]
+ "pre=\r".unpack('M') # => ["pre=\r"]
+ "pre=hoge".unpack('M') # => ["pre=hoge"]
+ "pre==31after".unpack('M') # => ["pre==31after"]
+ "pre===31after".unpack('M') # => ["pre===31after"]
+
+- <tt>'m'</tt> - Base64 encoded string;
+ count specifies input bytes between each newline,
+ rounded down to nearest multiple of 3;
+ if count is zero, no newlines are added;
+ (see {RFC 4648}[https://www.ietf.org/rfc/rfc4648.txt]):
+
+ [""].pack('m') # => ""
+ ["\0"].pack('m') # => "AA==\n"
+ ["\0\0"].pack('m') # => "AAA=\n"
+ ["\0\0\0"].pack('m') # => "AAAA\n"
+ ["\377"].pack('m') # => "/w==\n"
+ ["\377\377"].pack('m') # => "//8=\n"
+ ["\377\377\377"].pack('m') # => "////\n"
+
+ "".unpack('m') # => [""]
+ "AA==\n".unpack('m') # => ["\x00"]
+ "AAA=\n".unpack('m') # => ["\x00\x00"]
+ "AAAA\n".unpack('m') # => ["\x00\x00\x00"]
+ "/w==\n".unpack('m') # => ["\xFF"]
+ "//8=\n".unpack('m') # => ["\xFF\xFF"]
+ "////\n".unpack('m') # => ["\xFF\xFF\xFF"]
+ "A\n".unpack('m') # => [""]
+ "AA\n".unpack('m') # => ["\x00"]
+ "AA=\n".unpack('m') # => ["\x00"]
+ "AAA\n".unpack('m') # => ["\x00\x00"]
+
+ [""].pack('m0') # => ""
+ ["\0"].pack('m0') # => "AA=="
+ ["\0\0"].pack('m0') # => "AAA="
+ ["\0\0\0"].pack('m0') # => "AAAA"
+ ["\377"].pack('m0') # => "/w=="
+ ["\377\377"].pack('m0') # => "//8="
+ ["\377\377\377"].pack('m0') # => "////"
+
+ "".unpack('m0') # => [""]
+ "AA==".unpack('m0') # => ["\x00"]
+ "AAA=".unpack('m0') # => ["\x00\x00"]
+ "AAAA".unpack('m0') # => ["\x00\x00\x00"]
+ "/w==".unpack('m0') # => ["\xFF"]
+ "//8=".unpack('m0') # => ["\xFF\xFF"]
+ "////".unpack('m0') # => ["\xFF\xFF\xFF"]
+
+- <tt>'u'</tt> - UU-encoded string:
+
+ [0].pack("U") # => "\u0000"
+ [0x3fffffff].pack("U") # => "\xFC\xBF\xBF\xBF\xBF\xBF"
+ [0x40000000].pack("U") # => "\xFD\x80\x80\x80\x80\x80"
+ [0x7fffffff].pack("U") # => "\xFD\xBF\xBF\xBF\xBF\xBF"
+
+=== Offset Directives
+
+- <tt>'@'</tt> - Begin packing at the given byte offset;
+ for packing, null fill if necessary:
+
+ [1, 2].pack("C@0C") # => "\x02"
+ [1, 2].pack("C@1C") # => "\x01\x02"
+ [1, 2].pack("C@5C") # => "\x01\x00\x00\x00\x00\x02"
+
+ "\x01\x00\x00\x02".unpack("C@3C") # => [1, 2]
+ "\x00".unpack("@1C") # => [nil]
+
+- <tt>'X'</tt> - Back up a byte:
+
+ [0, 1, 2].pack("CCXC") # => "\x00\x02"
+ [0, 1, 2].pack("CCX2C") # => "\x02"
+ "\x00\x02".unpack("CCXC") # => [0, 2, 2]
+
+=== Null Byte Direcive
+
+- <tt>'x'</tt> - Null byte:
+
+ [].pack("x0") # => ""
+ [].pack("x") # => "\x00"
+ [].pack("x8") # => "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x02".unpack("CxC") # => [0, 2]
diff --git a/doc/rdoc/markup_reference.rb b/doc/rdoc/markup_reference.rb
new file mode 100644
index 0000000000..66ec6786c0
--- /dev/null
+++ b/doc/rdoc/markup_reference.rb
@@ -0,0 +1,1257 @@
+require 'rdoc'
+
+# \Class \RDoc::MarkupReference exists only to provide a suitable home
+# for a reference document for \RDoc markup.
+#
+# All objects defined in this class -- classes, modules, methods, aliases,
+# attributes, and constants -- are solely for illustrating \RDoc markup,
+# and have no other legitimate use.
+#
+# = \RDoc Markup Reference
+#
+# Notes:
+#
+# - Examples in this reference are Ruby code and comments;
+# certain differences from other sources
+# (such as C code and comments) are noted.
+# - An example that shows rendered HTML output
+# displays that output in a blockquote:
+#
+# Rendered HTML:
+# >>>
+# Some stuff
+#
+# \RDoc-generated documentation is derived from and controlled by:
+#
+# - Single-line or multi-line comments that precede certain definitions;
+# see {Markup in Comments}[rdoc-ref:RDoc::MarkupReference@Markup+in+Comments].
+# - \RDoc directives in trailing comments (on the same line as code);
+# see <tt>:nodoc:</tt>, <tt>:doc:</tt>, and <tt>:notnew</tt>.
+# - \RDoc directives in single-line comments;
+# see other {Directives}[rdoc-ref:RDoc::MarkupReference@Directives].
+# - The Ruby code itself (but not from C code);
+# see {Documentation Derived from Ruby Code}[rdoc-ref:RDoc::MarkupReference@Documentation+Derived+from+Ruby+Code].
+#
+# == Markup in Comments
+#
+# The treatment of markup in comments varies according to the type of file:
+#
+# - <tt>.rb</tt> (Ruby code file): markup is parsed from Ruby comments.
+# - <tt>.c</tt> (C code file): markup is parsed from C comments.
+# - <tt>.rdoc</tt> (RDoc text file): markup is parsed from the entire file.
+#
+# The comment associated with
+# a Ruby class, module, method, alias, constant, or attribute
+# becomes the documentation for that defined object:
+#
+# - In a Ruby file, that comment immediately precedes
+# the definition of the object.
+# - In a C file, that comment immediately precedes
+# the function that implements a method,
+# or otherwise immediately precedes the definition of the object.
+#
+# In either a Ruby or a C file,
+# \RDoc ignores comments that do not precede object definitions.
+#
+# In an \RDoc file, the text is not associated with any code object,
+# but may (depending on how the documentation is built),
+# become a separate page.
+#
+# Almost all examples on this page are all RDoc-like;
+# that is, they have no comment markers like Ruby <tt>#</tt>
+# or C <tt>/* ... */</tt>.
+#
+# === Margins
+#
+# In a multi-line comment,
+# \RDoc looks for the comment's natural left margin,
+# which becomes the <em>base margin</em> for the comment
+# and is the initial <em>current margin</em> for for the comment.
+#
+# The current margin can change, and does so, for example in a list.
+#
+# === Blocks
+#
+# It's convenient to think of \RDoc markup input as a sequence of _blocks_
+# of various types (details at the links):
+#
+# - {Paragraph}[rdoc-ref:RDoc::MarkupReference@Paragraphs]:
+# an ordinary paragraph.
+# - {Verbatim text block}[rdoc-ref:RDoc::MarkupReference@Verbatim+Text+Blocks]:
+# a block of text to be rendered literally.
+# - {Code block}[rdoc-ref:RDoc::MarkupReference@Code+Blocks]:
+# a verbatim text block containing Ruby code,
+# to be rendered with code highlighting.
+# - {Block quote}[rdoc-ref:RDoc::MarkupReference@Block+Quotes]:
+# a longish quoted passage, to be rendered with indentation
+# instead of quote marks.
+# - {List}[rdoc-ref:RDoc::MarkupReference@Lists]: items for
+# a bullet list, numbered list, lettered list, or labeled list.
+# - {Heading}[rdoc-ref:RDoc::MarkupReference@Headings]:
+# a section heading.
+# - {Horizontal rule}[rdoc-ref:RDoc::MarkupReference@Horizontal+Rules]:
+# a line across the rendered page.
+# - {Directive}[rdoc-ref:RDoc::MarkupReference@Directives]:
+# various special directions for the rendering.
+# - {Text Markup}[rdoc-ref:RDoc:MarkupReference@Text+Markup]:
+# text to be rendered in a special way.
+#
+# About the blocks:
+#
+# - Except for a paragraph, a block is distinguished by its indentation,
+# or by unusual initial or embedded characters.
+# - Any block may appear independently
+# (that is, not nested in another block);
+# some blocks may be nested, as detailed below.
+#
+# ==== Paragraphs
+#
+# A paragraph consists of one or more non-empty lines of ordinary text,
+# each beginning at the current margin.
+#
+# Note: Here, <em>ordinary text</em> means text that is <em>not identified</em>
+# by indentation, or by unusual initial or embedded characters.
+# See below.
+#
+# Paragraphs are separated by one or more empty lines.
+#
+# Example input:
+#
+# \RDoc produces HTML and command-line documentation for Ruby projects.
+# \RDoc includes the rdoc and ri tools for generating and displaying
+# documentation from the command-line.
+#
+# You'll love it.
+#
+# Rendered HTML:
+# >>>
+# \RDoc produces HTML and command-line documentation for Ruby projects.
+# \RDoc includes the rdoc and ri tools for generating and displaying
+# documentation from the command-line.
+#
+# You'll love it.
+#
+# A paragraph may contain nested blocks, including:
+#
+# - {Verbatim text blocks}[rdoc-ref:RDoc::MarkupReference@Verbatim+Text+Blocks].
+# - {Code blocks}[rdoc-ref:RDoc::MarkupReference@Code+Blocks].
+# - {Block quotes}[rdoc-ref:RDoc::MarkupReference@Block+Quotes].
+# - {Lists}[rdoc-ref:RDoc::MarkupReference@Lists].
+# - {Headings}[rdoc-ref:RDoc::MarkupReference@Headings].
+# - {Horizontal rules}[rdoc-ref:RDoc::MarkupReference@Horizontal+Rules].
+# - {Text Markup}[rdoc-ref:RDoc:MarkupReference@Text+Markup].
+#
+# ==== Verbatim Text Blocks
+#
+# Text indented farther than the current margin becomes a <em>verbatim text block</em>
+# (or a code block, described next).
+# In the rendered HTML, such text:
+#
+# - Is indented.
+# - Has a contrasting background color.
+#
+# The verbatim text block ends at the first line beginning at the current margin.
+#
+# Example input:
+#
+# This is not verbatim text.
+#
+# This is verbatim text.
+# Whitespace is honored. # See?
+# Whitespace is honored. # See?
+#
+# This is still the same verbatim text block.
+#
+# This is not verbatim text.
+#
+# Rendered HTML:
+# >>>
+# This is not verbatim text.
+#
+# This is verbatim text.
+# Whitespace is honored. # See?
+# Whitespace is honored. # See?
+#
+# This is still the same verbatim text block.
+#
+# This is not verbatim text.
+#
+# A verbatim text block may not contain nested blocks of any kind
+# -- it's verbatim.
+#
+# ==== Code Blocks
+#
+# A special case of verbatim text is the <em>code block</em>,
+# which is merely verbatim text that \RDoc recognizes as Ruby code:
+#
+# In the rendered HTML, the code block:
+#
+# - Is indented.
+# - Has a contrasting background color.
+# - Has syntax highlighting.
+#
+# Example input:
+#
+# Consider this method:
+#
+# def foo(name = '', value = 0)
+# @name = name # Whitespace is still honored.
+# @value = value
+# end
+#
+#
+# Rendered HTML:
+# >>>
+# Consider this method:
+#
+# def foo(name = '', value = 0)
+# @name = name # Whitespace is still honored.
+# @value = value
+# end
+#
+# Pro tip: If your indented Ruby code does not get highlighted,
+# it may contain a syntax error.
+#
+# A code block may not contain nested blocks of any kind
+# -- it's verbatim.
+#
+# ==== Block Quotes
+#
+# You can use the characters <tt>>>></tt> (unindented),
+# followed by indented text, to treat the text
+# as a {block quote}[https://en.wikipedia.org/wiki/Block_quotation]:
+#
+# Example input:
+#
+# Here's a block quote:
+# >>>
+# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer
+# commodo quam iaculis massa posuere, dictum fringilla justo pulvinar.
+# Quisque turpis erat, pharetra eu dui at, sollicitudin accumsan nulla.
+#
+# Aenean congue ligula eu ligula molestie, eu pellentesque purus
+# faucibus. In id leo non ligula condimentum lobortis. Duis vestibulum,
+# diam in pellentesque aliquet, mi tellus placerat sapien, id euismod
+# purus magna ut tortor.
+#
+# Rendered HTML:
+#
+# >>>
+# Here's a block quote:
+# >>>
+# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer
+# commodo quam iaculis massa posuere, dictum fringilla justo pulvinar.
+# Quisque turpis erat, pharetra eu dui at, sollicitudin accumsan nulla.
+#
+# Aenean congue ligula eu ligula molestie, eu pellentesque purus
+# faucibus. In id leo non ligula condimentum lobortis. Duis vestibulum,
+# diam in pellentesque aliquet, mi tellus placerat sapien, id euismod
+# purus magna ut tortor.
+#
+# Note that, unlike verbatim text, single newlines are not honored,
+# but that a double newline begins a new paragraph in the block quote.
+#
+# A block quote may contain nested blocks, including:
+#
+# - Other block quotes.
+# - {Paragraphs}[rdoc-ref:RDoc::MarkupReference@Paragraphs].
+# - {Verbatim text blocks}[rdoc-ref:RDoc::MarkupReference@Verbatim+Text+Blocks].
+# - {Code blocks}[rdoc-ref:RDoc::MarkupReference@Code+Blocks].
+# - {Lists}[rdoc-ref:RDoc::MarkupReference@Lists].
+# - {Headings}[rdoc-ref:RDoc::MarkupReference@Headings].
+# - {Horizontal rules}[rdoc-ref:RDoc::MarkupReference@Horizontal+Rules].
+# - {Text Markup}[rdoc-ref:RDoc:MarkupReference@Text+Markup].
+#
+# ==== Lists
+#
+# Each type of list item is marked by a special beginning:
+#
+# - Bullet list item: Begins with a hyphen or asterisk.
+# - Numbered list item: Begins with digits and a period.
+# - Lettered list item: Begins with an alphabetic character and a period.
+# - Labeled list item: Begins with one of:
+# - Square-bracketed text.
+# - A word followed by two colons.
+#
+# A list begins with a list item and continues, even across blank lines,
+# as long as list items of the same type are found at the same indentation level.
+#
+# A new list resets the current margin inward.
+# Additional lines of text aligned at that margin
+# are part of the continuing list item.
+#
+# A list item may be continued on additional lines that are aligned
+# with the first line. See examples below.
+#
+# A list item may contain nested blocks, including:
+#
+# - Other lists of any type.
+# - {Paragraphs}[rdoc-ref:RDoc::MarkupReference@Paragraphs].
+# - {Verbatim text blocks}[rdoc-ref:RDoc::MarkupReference@Verbatim+Text+Blocks].
+# - {Code blocks}[rdoc-ref:RDoc::MarkupReference@Code+Blocks].
+# - {Block quotes}[rdoc-ref:RDoc::MarkupReference@Block+Quotes].
+# - {Headings}[rdoc-ref:RDoc::MarkupReference@Headings].
+# - {Horizontal rules}[rdoc-ref:RDoc::MarkupReference@Horizontal+Rules].
+# - {Text Markup}[rdoc-ref:RDoc:MarkupReference@Text+Markup].
+#
+# ===== Bullet Lists
+#
+# A bullet list item begins with a hyphen or asterisk.
+#
+# Example input:
+#
+# - An item.
+# - Another.
+# - An item spanning
+# multiple lines.
+#
+# * Yet another.
+# - Last one.
+#
+# Rendered HTML:
+# >>>
+# - An item.
+# - Another.
+# - An item spanning
+# multiple lines.
+#
+# * Yet another.
+# - Last one.
+#
+# ===== Numbered Lists
+#
+# A numbered list item begins with digits and a period.
+#
+# The items are automatically re-numbered.
+#
+# Example input:
+#
+# 100. An item.
+# 10. Another.
+# 1. An item spanning
+# multiple lines.
+#
+# 1. Yet another.
+# 1000. Last one.
+#
+# Rendered HTML:
+# >>>
+# 100. An item.
+# 10. Another.
+# 1. An item spanning
+# multiple lines.
+#
+# 1. Yet another.
+# 1000. Last one.
+#
+# ===== Lettered Lists
+#
+# A numbered list item begins with a letters and a period.
+#
+# The items are automatically "re-lettered."
+#
+# Example input:
+#
+# z. An item.
+# y. Another.
+# x. An item spanning
+# multiple lines.
+#
+# x. Yet another.
+# a. Last one.
+#
+# Rendered HTML:
+# >>>
+# z. An item.
+# y. Another.
+#
+# x. Yet another.
+# a. Last one.
+#
+# ===== Labeled Lists
+#
+# A labeled list item begins with one of:
+#
+# - Square-bracketed text: the label and text are on two lines.
+# - A word followed by two colons: the label and text are on the same line.
+#
+# Example input:
+#
+# [foo] An item.
+# bat:: Another.
+# [bag] An item spanning
+# multiple lines.
+#
+# [bar baz] Yet another.
+# bam:: Last one.
+#
+# Rendered HTML:
+# >>>
+# [foo] An item.
+# bat:: Another.
+# [bag] An item spanning
+# multiple lines.
+#
+# [bar baz] Yet another.
+# bam:: Last one.
+#
+# ==== Headings
+#
+# A heading begins with up to six equal-signs, followed by heading text.
+# Whitespace between those and the heading text is optional.
+#
+# Examples:
+#
+# = Section 1
+# == Section 1.1
+# === Section 1.1.1
+# === Section 1.1.2
+# == Section 1.2
+# = Section 2
+# = Foo
+# == Bar
+# === Baz
+# ==== Bam
+# ===== Bat
+# ====== Bad
+# ============Still a Heading (Level 6)
+# \== Not a Heading
+#
+# A heading may contain only one type of nested block:
+#
+# - {Text Markup}[rdoc-ref:RDoc:MarkupReference@Text+Markup].
+#
+# ==== Horizontal Rules
+#
+# A horizontal rule consists of a line with three or more hyphens
+# and nothing more.
+#
+# Example input:
+#
+# ---
+# --- Not a horizontal rule.
+#
+# -- Also not a horizontal rule.
+# ---
+#
+# Rendered HTML:
+# >>>
+# ---
+# --- Not a horizontal rule.
+#
+# -- Also not a horizontal rule.
+# ---
+#
+# ==== Directives
+#
+# ===== Directives for Allowing or Suppressing Documentation
+#
+# - <tt># :stopdoc:</tt>:
+#
+# - Appears on a line by itself.
+# - Specifies that \RDoc should ignore markup
+# until next <tt>:startdoc:</tt> directive or end-of-file.
+#
+# - <tt># :startdoc:</tt>:
+#
+# - Appears on a line by itself.
+# - Specifies that \RDoc should resume parsing markup.
+#
+# - <tt># :enddoc:</tt>:
+#
+# - Appears on a line by itself.
+# - Specifies that \RDoc should ignore markup to end-of-file
+# regardless of other directives.
+#
+# - <tt># :nodoc:</tt>:
+#
+# - Appended to a line of code
+# that defines a class, module, method, alias, constant, or attribute.
+# - Specifies that the defined object should not be documented.
+#
+# - <tt># :nodoc: all</tt>:
+#
+# - Appended to a line of code
+# that defines a class or module.
+# - Specifies that the class or module should not be documented.
+# By default, however, a nested class or module _will_ be documented.
+#
+# - <tt># :doc:</tt>:
+#
+# - Appended to a line of code
+# that defines a class, module, method, alias, constant, or attribute.
+# - Specifies the defined object should be documented, even if otherwise
+# would not be documented.
+#
+# - <tt># :notnew:</tt> (aliased as <tt>:not_new:</tt> and <tt>:not-new:</tt>):
+#
+# - Appended to a line of code
+# that defines instance method +initialize+.
+# - Specifies that singleton method +new+ should not be documented.
+# By default, Ruby fakes a corresponding singleton method +new+,
+# which \RDoc includes in the documentation.
+# Note that instance method +initialize+ is private, and so by default
+# is not documented.
+#
+# For Ruby code, but not for other \RDoc sources,
+# there is a shorthand for <tt>:stopdoc:</tt> and <tt>:startdoc:</tt>:
+#
+# # Documented.
+# #--
+# # Not documented.
+# #++
+# # Documented.
+#
+# For C code, any of directives <tt>:startdoc:</tt>, <tt>:enddoc:</tt>,
+# and <tt>:nodoc:</tt> may appear in a stand-alone comment:
+#
+# /* :startdoc: */
+# /* :stopdoc: */
+# /* :enddoc: */
+#
+# ===== Directive for Specifying \RDoc Source Format
+#
+# - <tt># :markup: _type_</tt>:
+#
+# - Appears on a line by itself.
+# - Specifies the format for the \RDoc input;
+# parameter +type+ is one of +markdown+, +rd+, +rdoc+, +tomdoc+.
+#
+# ===== Directives for HTML Output
+#
+# - <tt># :title: _text_</tt>:
+#
+# - Appears on a line by itself.
+# - Specifies the title for the HTML output.
+#
+# - <tt># :main: _filename_</tt>:
+# - Appears on a line by itself.
+# - Specifies the HTML file to be displayed first.
+#
+# ===== Directives for Method Documentation
+#
+# - <tt># :call-seq:</tt>:
+#
+# - Appears on a line by itself.
+# - Specifies the calling sequence to be reported in the HTML,
+# overriding the actual calling sequence in the code.
+# See method #call_seq_directive.
+#
+# Note that \RDoc can build the calling sequence for a Ruby-coded method,
+# but not for other languages.
+# You may want to override that by explicitly giving a <tt>:call-seq:</tt>
+# directive if you want to include:
+#
+# - A return type, which is not automatically inferred.
+# - Multiple calling sequences.
+#
+# For C code, the directive may appear in a stand-alone comment.
+#
+# - <tt># :args: _arg_names_</tt> (aliased as <tt>:arg:</tt>):
+#
+# - Appears on a line by itself.
+# - Specifies the arguments to be reported in the HTML,
+# overriding the actual arguments in the code.
+# See method #args_directive.
+#
+# - <tt># :yields: _arg_names_</tt> (aliased as <tt>:yield:</tt>):
+#
+# - Appears on a line by itself.
+# - Specifies the yield arguments to be reported in the HTML,
+# overriding the actual yield in the code.
+# See method #yields_directive.
+#
+# ===== Directives for Organizing Documentation
+#
+# By default, \RDoc groups:
+#
+# - Singleton methods together in alphabetical order.
+# - Instance methods and their aliases together in alphabetical order.
+# - Attributes and their aliases together in alphabetical order.
+#
+# You can use directives to modify those behaviors.
+#
+# - <tt># :section: _section_title_</tt>:
+#
+# - Appears on a line by itself.
+# - Specifies that following methods are to be grouped into the section
+# with the given <em>section_title</em>,
+# or into the default section if no title is given.
+# The directive remains in effect until another such directive is given,
+# but may be temporarily overridden by directive <tt>:category:</tt>.
+# See below.
+#
+# The comment block containing this directive:
+#
+# - Must be separated by a blank line from the documentation for the next item.
+# - May have one or more lines preceding the directive.
+# These will be removed, along with any trailing lines that match them.
+# Such lines may be visually helpful.
+# - Lines of text that are not so removed become the descriptive text
+# for the section.
+#
+# Example:
+#
+# # ----------------------------------------
+# # :section: My Section
+# # This is the section that I wrote.
+# # See it glisten in the noon-day sun.
+# # ----------------------------------------
+#
+# ##
+# # Comment for some_method
+# def some_method
+# # ...
+# end
+#
+# You can use directive <tt>:category:</tt> to temporarily
+# override the current section.
+#
+# - <tt># :category: _section_title_</tt>:
+#
+# - Appears on a line by itself.
+# - Specifies that just one following method is to be included
+# in the given section, or in the default section if no title is given.
+# Subsequent methods are to be grouped into the current section.
+#
+# ===== Directive for Including a File
+#
+# - <tt># :include: _filepath_</tt>:
+#
+# - Appears on a line by itself.
+# - Specifies that the contents of the given file
+# are to be included at this point.
+# The file content is shifted to have the same indentation as the colon
+# at the start of the directive.
+#
+# The file is searched for in the directories
+# given with the <tt>--include</tt> command-line option,
+# or by default in the current directory.
+#
+# For C code, the directive may appear in a stand-alone comment
+#
+# ==== Text Markup
+#
+# Text markup is metatext that affects HTML rendering:
+#
+# - Typeface: italic, bold, monofont.
+# - Character conversions: copyright, trademark, certain punctuation.
+# - Links.
+# - Escapes: marking text as "not markup."
+#
+# ===== Typeface Markup
+#
+# Typeface markup can specify that text is to be rendered
+# as italic, bold, or monofont.
+#
+# Typeface markup may contain only one type of nested block:
+#
+# - More typeface markup:
+# italic, bold, monofont.
+#
+# ====== Italic
+#
+# Text may be marked as italic via HTML tag <tt><i></tt> or <tt><em></tt>.
+#
+# Example input:
+#
+# <i>Italicized words</i> in a paragraph.
+#
+# >>>
+# <i>Italicized words in a block quote</i>.
+#
+# - <i>Italicized words</i> in a list item.
+#
+# ====== <i>Italicized words</i> in a Heading
+#
+# <i>Italicized passage containing *bold* and +monofont+.</i>
+#
+# Rendered HTML:
+# >>>
+# <i>Italicized words</i> in a paragraph.
+#
+# >>>
+# <i>Italicized words in a block quote</i>.
+#
+# - <i>Italicized words</i> in a list item.
+#
+# ====== <i>Italicized words</i> in a Heading
+#
+# <i>Italicized passage containing *bold* and +monofont+.</i>
+#
+# A single word may be italicized via a shorthand:
+# prefixed and suffixed underscores.
+#
+# Example input:
+#
+# _Italic_ in a paragraph.
+#
+# >>>
+# _Italic_ in a block quote.
+#
+# - _Italic_ in a list item.
+#
+# ====== _Italic_ in a Heading
+#
+# Rendered HTML:
+# >>>
+# _Italic_ in a paragraph.
+#
+# >>>
+# _Italic_ in a block quote.
+#
+# - _Italic_ in a list item.
+#
+# ====== _Italic_ in a Heading
+#
+# ====== Bold
+#
+# Text may be marked as bold via HTML tag <tt><b></tt>.
+#
+# Example input:
+#
+# <b>Bold words</b> in a paragraph.
+#
+# >>>
+# <b>Bold words</b> in a block quote.
+#
+# - <b>Bold words</b> in a list item.
+#
+# ====== <b>Bold words</b> in a Heading
+#
+# <b>Bold passage containing _italics_ and +monofont+.</b>
+#
+# Rendered HTML:
+#
+# >>>
+# <b>Bold words</b> in a paragraph.
+#
+# >>>
+# <b>Bold words</b> in a block quote.
+#
+# - <b>Bold words</b> in a list item.
+#
+# ====== <b>Bold words</b> in a Heading
+#
+# <b>Bold passage containing _italics_ and +monofont+.</b>
+#
+# A single word may be made bold via a shorthand:
+# prefixed and suffixed asterisks.
+#
+# Example input:
+#
+# *Bold* in a paragraph.
+#
+# >>>
+# *Bold* in a block quote.
+#
+# - *Bold* in a list item.
+#
+# ===== *Bold* in a Heading
+#
+# Rendered HTML:
+#
+# >>>
+# *Bold* in a paragraph.
+#
+# >>>
+# *Bold* in a block quote.
+#
+# - *Bold* in a list item.
+#
+# ===== *Bold* in a Heading
+#
+# ====== Monofont
+#
+# Text may be marked as monofont
+# -- sometimes called 'typewriter font' --
+# via HTML tag <tt><tt></tt> or <tt><code></tt>.
+#
+# Example input:
+#
+# <tt>Monofont words</tt> in a paragraph.
+#
+# >>>
+# <tt>Monofont words</tt> in a block quote.
+#
+# - <tt>Monofont words</tt> in a list item.
+#
+# ====== <tt>Monofont words</tt> in heading
+#
+# <tt>Monofont passage containing _italics_ and *bold*.</tt>
+#
+# Rendered HTML:
+#
+# >>>
+# <tt>Monofont words</tt> in a paragraph.
+#
+# >>>
+# <tt>Monofont words</tt> in a block quote.
+#
+# - <tt>Monofont words</tt> in a list item.
+#
+# ====== <tt>Monofont words</tt> in heading
+#
+# <tt>Monofont passage containing _italics_ and *bold*.</tt>
+#
+# A single word may be made monofont by a shorthand:
+# prefixed and suffixed plus-signs.
+#
+# Example input:
+#
+# +Monofont+ in a paragraph.
+#
+# >>>
+# +Monofont+ in a block quote.
+#
+# - +Monofont+ in a list item.
+#
+# ====== +Monofont+ in a Heading
+#
+# Rendered HTML:
+#
+# >>>
+# +Monofont+ in a paragraph.
+#
+# >>>
+# +Monofont+ in a block quote.
+#
+# - +Monofont+ in a list item.
+#
+# ====== +Monofont+ in a Heading
+#
+# ==== Character Conversions
+#
+# Certain combinations of characters may be converted to special characters;
+# whether the conversion occurs depends on whether the special character
+# is available in the current encoding.
+#
+# - <tt>(c)</tt> converts to (c) (copyright character); must be lowercase.
+#
+# - <tt>(r)</tt> converts to (r) (registered trademark character); must be lowercase.
+#
+# - <tt>'foo'</tt> converts to 'foo' (smart single-quotes).
+#
+# - <tt>"foo"</tt> converts to "foo" (smart double-quotes).
+#
+# - <tt>foo ... bar</tt> converts to foo ... bar (1-character ellipsis).
+#
+# - <tt>foo -- bar</tt> converts to foo -- bar (1-character en-dash).
+#
+# - <tt>foo --- bar</tt> converts to foo --- bar (1-character em-dash).
+#
+# ==== Links
+#
+# Certain strings in \RDoc text are converted to links.
+# Any such link may be suppressed by prefixing a backslash.
+# This section shows how to link to various
+# targets.
+#
+# [Class]
+#
+# - On-page: <tt>DummyClass</tt> links to DummyClass.
+# - Off-page: <tt>RDoc::Alias</tt> links to RDoc::Alias.
+#
+# [Module]
+#
+# - On-page: <tt>DummyModule</tt> links to DummyModule.
+# - Off-page: <tt>RDoc</tt> links to RDoc.
+#
+# [Constant]
+#
+# - On-page: <tt>DUMMY_CONSTANT</tt> links to DUMMY_CONSTANT.
+# - Off-page: <tt>RDoc::Text::MARKUP_FORMAT</tt> links to RDoc::Text::MARKUP_FORMAT.
+#
+# [Singleton Method]
+#
+# - On-page: <tt>::dummy_singleton_method</tt> links to ::dummy_singleton_method.
+# - Off-page<tt>RDoc::TokenStream::to_html</tt> links to RDoc::TokenStream::to_html.
+#
+# Note: Occasionally \RDoc is not linked to a method whose name
+# has only special characters. Check whether the links you were expecting
+# are actually there. If not, you'll need to put in an explicit link;
+# see below.
+#
+# Pro tip: The link to any method is available in the alphabetical table of contents
+# at the top left of the page for the class or module.
+#
+# [Instance Method]
+#
+# - On-page: <tt>#dummy_instance_method</tt> links to #dummy_instance_method.
+# - Off-page: <tt>RDoc::Alias#html_name</tt> links to RDoc::Alias#html_name.
+#
+# See the Note and Pro Tip immediately above.
+#
+# [Attribute]
+#
+# - On-page: <tt>#dummy_attribute</tt> links to #dummy_attribute.
+# - Off-page: <tt>RDoc::Alias#name</tt> links to RDoc::Alias#name.
+#
+# [Alias]
+#
+# - On-page: <tt>#dummy_instance_alias</tt> links to #dummy_instance_alias.
+# - Off-page: <tt>RDoc::Alias#new_name</tt> links to RDoc::Alias#new_name.
+#
+# [Protocol +http+]
+#
+# - Linked: <tt>http://yahoo.com</tt> links to http://yahoo.com.
+#
+# [Protocol +https+]
+#
+# - Linked: <tt>https://github.com</tt> links to https://github.com.
+#
+# [Protocol +www+]
+#
+# - Linked: <tt>www.yahoo.com</tt> links to www.yahoo.com.
+#
+# [Protocol +ftp+]
+#
+# - Linked: <tt>ftp://nosuch.site</tt> links to ftp://nosuch.site.
+#
+# [Protocol +mailto+]
+#
+# - Linked: <tt>mailto:/foo@bar.com</tt> links to mailto://foo@bar.com.
+#
+# [Protocol +irc+]
+#
+# - link: <tt>irc://irc.freenode.net/ruby</tt> links to irc://irc.freenode.net/ruby.
+#
+# [Image Filename Extensions]
+#
+# - Link: <tt>https://www.ruby-lang.org/images/header-ruby-logo@2x.png</tt> is
+# converted to an in-line HTML +img+ tag, which displays the image in the HTML:
+#
+# https://www.ruby-lang.org/images/header-ruby-logo@2x.png
+#
+# Also works for +bmp+, +gif+, +jpeg+, and +jpg+ files.
+#
+# Note: Works only for a fully qualified URL.
+#
+# [Heading]
+#
+# - Link: <tt>RDoc::RD@LICENSE</tt> links to RDoc::RDoc::RD@LICENSE.
+#
+# Note that spaces in the actual heading are represented by <tt>+</tt> characters
+# in the linkable text.
+#
+# - Link: <tt>RDoc::Options@Saved+Options</tt>
+# links to RDoc::Options@Saved+Options.
+#
+# Punctuation and other special characters must be escaped like CGI.escape.
+#
+# Pro tip: The link to any heading is available in the alphabetical table of contents
+# at the top left of the page for the class or module.
+#
+# [Section]
+#
+# See {Directives for Organizing Documentation}[#class-RDoc::MarkupReference-label-Directives+for+Organizing+Documentation].
+#
+# - Link: <tt>RDoc::Markup::ToHtml@Visitor</tt> links to RDoc::Markup::ToHtml@Visitor.
+#
+# If a section and a heading share the same name, the link target is the section.
+#
+# [Single-Word Text Link]
+#
+# Use square brackets to create single-word text link:
+#
+# - <tt>GitHub[https://github.com]</tt> links to GitHub[https://github.com].
+#
+# [Multi-Word Text Link]
+#
+# Use square brackets and curly braces to create a multi-word text link.
+#
+# - <tt>{GitHub home page}[https://github.com]</tt> links to
+# {GitHub home page}[https://github.com].
+#
+# [<tt>rdoc-ref</tt> Scheme]
+#
+# A link with the <tt>rdoc-ref:</tt> scheme links to the referenced item,
+# if that item exists.
+# The referenced item may be a class, module, method, file, etc.
+#
+# - Class: <tt>Alias[rdoc-ref:RDoc::Alias]</tt> links to Alias[rdoc-ref:RDoc::Alias].
+# - Module: <tt>RDoc[rdoc-ref:RDoc]</tt> links to RDoc[rdoc-ref:RDoc].
+# - Method: <tt>foo[rdoc-ref:RDoc::Markup::ToHtml#handle_regexp_RDOCLINK]</tt>
+# links to foo[rdoc-ref:RDoc::Markup::ToHtml#handle_regexp_RDOCLINK].
+# - Constant: <tt>bar[rdoc-ref:RDoc::Markup::ToHtml::LIST_TYPE_TO_HTML]</tt>
+# links to bar[rdoc-ref:RDoc::Markup::ToHtml::LIST_TYPE_TO_HTML].
+# - Attribute: <tt>baz[rdoc-ref:RDoc::Markup::ToHtml#code_object]</tt>
+# links to baz[rdoc-ref:RDoc::Markup::ToHtml#code_object].
+# - Alias: <tt>bad[rdoc-ref:RDoc::MarkupReference#dummy_instance_alias]</tt> links to
+# bad[rdoc-ref:RDoc::MarkupReference#dummy_instance_alias].
+#
+# If the referenced item does not exist, no link is generated
+# and entire <tt>rdoc-ref:</tt> square-bracketed clause is removed
+# from the resulting text.
+#
+# - <tt>Nosuch[rdoc-ref:RDoc::Nosuch]</tt> is rendered as
+# Nosuch[rdoc-ref:RDoc::Nosuch].
+#
+#
+# [<tt>rdoc-label</tt> Scheme]
+#
+# [Simple]
+#
+# You can specify a link target using this form,
+# where the second part cites the id of an HTML element.
+#
+# This link refers to the constant +DUMMY_CONSTANT+ on this page:
+#
+# - <tt>{DUMMY_CONSTANT}[rdoc-label:DUMMY_CONSTANT]</tt>
+#
+# Thus:
+#
+# {DUMMY_CONSTANT}[rdoc-label:DUMMY_CONSTANT]
+#
+# [With Return]
+#
+# You can specify both a link target and a local label
+# that can be used as the target for a return link.
+# These two links refer to each other:
+#
+# - <tt>{go to addressee}[rdoc-label:addressee:sender]</tt>
+# - <tt>{return to sender}[rdoc-label:sender:addressee]</tt>
+#
+# Thus:
+#
+# {go to addressee}[rdoc-label:addressee:sender]
+#
+# Some text.
+#
+# {return to sender}[rdoc-label:sender:addressee]
+#
+# [<tt>link:</tt> Scheme]
+#
+# - <tt>link:README_rdoc.html</tt> links to link:README_rdoc.html.
+#
+# [<tt>rdoc-image</tt> Scheme]
+#
+# Use the <tt>rdoc-image</tt> scheme to display an image that is also a link:
+#
+# # {rdoc-image:path/to/image}[link_target]
+#
+# - Link: <tt>{rdoc-image:https://www.ruby-lang.org/images/header-ruby-logo@2x.png}[https://www.ruby-lang.org]</tt>
+# displays image <tt>https://www.ruby-lang.org/images/header-ruby-logo@2x.png</tt>
+# as a link to <tt>https://www.ruby-lang.org</tt>.
+#
+# {rdoc-image:https://www.ruby-lang.org/images/header-ruby-logo@2x.png}[https://www.ruby-lang.org]
+#
+# A relative path as the target also works:
+#
+# - Link: <tt>{rdoc-image:https://www.ruby-lang.org/images/header-ruby-logo@2x.png}[./Alias.html]</tt> links to <tt>./Alias.html</tt>
+#
+# {rdoc-image:https://www.ruby-lang.org/images/header-ruby-logo@2x.png}[./Alias.html]
+#
+# === Escaping Text
+#
+# Text that would otherwise be interpreted as markup
+# can be "escaped," so that it is not interpreted as markup;
+# the escape character is the backslash (<tt>'\\'</tt>).
+#
+# In a verbatim text block or a code block,
+# the escape character is always preserved:
+#
+# Example input:
+#
+# This is not verbatim text.
+#
+# This is verbatim text, with an escape character \.
+#
+# This is not a code block.
+#
+# def foo
+# 'String with an escape character.'
+# end
+#
+# Rendered HTML:
+#
+# >>>
+# This is not verbatim text.
+#
+# This is verbatim text, with an escape character \.
+#
+# This is not a code block.
+#
+# def foo
+# 'This is a code block with an escape character \.'
+# end
+#
+# In typeface markup (italic, bold, or monofont),
+# an escape character is preserved unless it is immediately
+# followed by nested typeface markup.
+#
+# Example input:
+#
+# This list is about escapes; it contains:
+#
+# - <tt>Monofont text with unescaped nested _italic_</tt>.
+# - <tt>Monofont text with escaped nested \_italic_</tt>.
+# - <tt>Monofont text with an escape character \</tt>.
+#
+# Rendered HTML:
+#
+# >>>
+# This list is about escapes; it contains:
+#
+# - <tt>Monofont text with unescaped nested _italic_</tt>.
+# - <tt>Monofont text with escaped nested \_italic_</tt>.
+# - <tt>Monofont text with an escape character \ </tt>.
+#
+# In other text-bearing blocks
+# (paragraphs, block quotes, list items, headings):
+#
+# - A single escape character immediately followed by markup
+# escapes the markup.
+# - A single escape character followed by whitespace is preserved.
+# - A single escape character anywhere else is ignored.
+# - A double escape character is rendered as a single backslash.
+#
+# Example input:
+#
+# This list is about escapes; it contains:
+#
+# - An unescaped class name, RDoc, that will become a link.
+# - An escaped class name, \RDoc, that will not become a link.
+# - An escape character followed by whitespace \ .
+# - An escape character \that is ignored.
+# - A double escape character \\ that is rendered
+# as a single backslash.
+#
+# Rendered HTML:
+#
+# >>>
+# This list is about escapes; it contains:
+#
+# - An unescaped class name, RDoc, that will become a link.
+# - An escaped class name, \RDoc, that will not become a link.
+# - An escape character followed by whitespace \ .
+# - An escape character \that is ignored.
+# - A double escape character \\ that is rendered
+# as a single backslash.
+#
+# == Documentation Derived from Ruby Code
+#
+# [Class]
+#
+# By default, \RDoc documents:
+#
+# - \Class name.
+# - Parent class.
+# - Singleton methods.
+# - Instance methods.
+# - Aliases.
+# - Constants.
+# - Attributes.
+#
+# [Module]
+#
+# By default, \RDoc documents:
+#
+# - \Module name.
+# - \Singleton methods.
+# - Instance methods.
+# - Aliases.
+# - Constants.
+# - Attributes.
+#
+# [Method]
+#
+# By default, \RDoc documents:
+#
+# - \Method name.
+# - Arguments.
+# - Yielded values.
+#
+# See #method.
+#
+# [Alias]
+#
+# By default, \RDoc documents:
+#
+# - Alias name.
+# - Aliased name.
+#
+# See #dummy_instance_alias and #dummy_instance_method.
+#
+# [Constant]
+#
+# By default, \RDoc documents:
+#
+# - \Constant name.
+#
+# See DUMMY_CONSTANT.
+#
+# [Attribute]
+#
+# By default, \RDoc documents:
+#
+# - Attribute name.
+# - Attribute type (<tt>[R]</tt>, <tt>[W]</tt>, or <tt>[RW]</tt>)
+#
+# See #dummy_attribute.
+#
+class RDoc::MarkupReference
+
+ class DummyClass; end
+ module DummyModule; end
+ def self.dummy_singleton_method(foo, bar); end
+ def dummy_instance_method(foo, bar); end;
+ alias dummy_instance_alias dummy_instance_method
+ attr_accessor :dummy_attribute
+ alias dummy_attribute_alias dummy_attribute
+ DUMMY_CONSTANT = ''
+
+ # :call-seq:
+ # call_seq_directive(foo, bar)
+ # Can be anything -> bar
+ # Also anything more -> baz or bat
+ #
+ # The <tt>:call-seq:</tt> directive overrides the actual calling sequence
+ # found in the Ruby code.
+ #
+ # - It can specify anything at all.
+ # - It can have multiple calling sequences.
+ #
+ # This one includes <tt>Can be anything -> foo</tt>, which is nonsense.
+ #
+ # Note that the "arrow" is two characters, hyphen and right angle-bracket,
+ # which is made into a single character in the HTML.
+ #
+ # Click on the calling sequence to see the code.
+ #
+ # Here is the <tt>:call-seq:</tt> directive given for the method:
+ #
+ # :call-seq:
+ # call_seq_directive(foo, bar)
+ # Can be anything -> bar
+ # Also anything more -> baz or bat
+ #
+ def call_seq_directive
+ nil
+ end
+
+ # The <tt>:args:</tt> directive overrides the actual arguments found in the Ruby code.
+ #
+ # Click on the calling sequence to see the code.
+ #
+ def args_directive(foo, bar) # :args: baz
+ nil
+ end
+
+ # The <tt>:yields:</tt> directive overrides the actual yield found in the Ruby code.
+ #
+ # Click on the calling sequence to see the code.
+ #
+ def yields_directive(foo, bar) # :yields: 'bat'
+ yield 'baz'
+ end
+
+ # This method is documented only by \RDoc, except for these comments.
+ #
+ # Click on the calling sequence to see the code.
+ #
+ def method(foo, bar)
+ yield 'baz'
+ end
+
+end
diff --git a/doc/regexp.rdoc b/doc/regexp.rdoc
index f3844d5729..92c7ecf66e 100644
--- a/doc/regexp.rdoc
+++ b/doc/regexp.rdoc
@@ -28,7 +28,7 @@ Specifically, <tt>/st/</tt> requires that the string contains the letter
_s_ followed by the letter _t_, so it matches _haystack_, also.
Note that any Regexp matching will raise a RuntimeError if timeout is set and
-exceeded. See "Timeout" section in detail.
+exceeded. See {"Timeout"}[#label-Timeout] section in detail.
== \Regexp Interpolation
@@ -550,12 +550,16 @@ characters, <i>anchoring</i> the match to a specific position.
* <tt>(?<!</tt><i>pat</i><tt>)</tt> - <i>Negative lookbehind</i>
assertion: ensures that the preceding characters do not match
<i>pat</i>, but doesn't include those characters in the matched text
-* <tt>\K</tt> - Uses an positive lookbehind of the content preceding
- <tt>\K</tt> in the regexp. For example, the following two regexps are
- almost equivalent:
- /ab\Kc/
- /(?<=ab)c/
+* <tt>\K</tt> - <i>Match reset</i>: the matched content preceding
+ <tt>\K</tt> in the regexp is excluded from the result. For example,
+ the following two regexps are almost equivalent:
+
+ /ab\Kc/ =~ "abc" #=> 0
+ /(?<=ab)c/ =~ "abc" #=> 2
+
+ These match same string and <i>$&</i> equals <tt>"c"</tt>, while the
+ matched position is different.
As are the following two regexps:
@@ -777,7 +781,7 @@ with <i>a{0,29}</i>:
== Timeout
-There are two APIs to set timeout. One is Timeout.timeout=, which is
+There are two APIs to set timeout. One is Regexp.timeout=, which is
process-global configuration of timeout for Regexp matching.
Regexp.timeout = 3
@@ -792,6 +796,6 @@ The other is timeout keyword of Regexp.new.
When using Regexps to process untrusted input, you should use the timeout
feature to avoid excessive backtracking. Otherwise, a malicious user can
-provide input to Regexp causing Denail-of-Service attack.
+provide input to Regexp causing Denial-of-Service attack.
Note that the timeout is not set by default because an appropriate limit
highly depends on an application requirement and context.
diff --git a/doc/strftime_formatting.rdoc b/doc/strftime_formatting.rdoc
index 6c27fa6a23..30a629bf68 100644
--- a/doc/strftime_formatting.rdoc
+++ b/doc/strftime_formatting.rdoc
@@ -294,6 +294,124 @@ longhand specifier.
DateTime.now.strftime('%a %b %e %H:%M:%S %Z %Y')
# => "Wed Jun 29 08:32:18 -05:00 2022"
+=== Flags
+
+Flags may affect certain formatting specifications.
+
+Multiple flags may be given with a single conversion specified;
+order does not matter.
+
+==== Padding Flags
+
+- <tt>0</tt> - Pad with zeroes:
+
+ Time.new(10).strftime('%0Y') # => "0010"
+
+- <tt>_</tt> - Pad with blanks:
+
+ Time.new(10).strftime('%_Y') # => " 10"
+
+- <tt>-</tt> - Don't pad:
+
+ Time.new(10).strftime('%-Y') # => "10"
+
+==== Casing Flags
+
+- <tt>^</tt> - Upcase result:
+
+ Time.new(2022, 1).strftime('%B') # => "January" # No casing flag.
+ Time.new(2022, 1).strftime('%^B') # => "JANUARY"
+
+- <tt>#</tt> - Swapcase result:
+
+ Time.now.strftime('%p') # => "AM"
+ Time.now.strftime('%^p') # => "AM"
+ Time.now.strftime('%#p') # => "am"
+
+==== Timezone Flags
+
+- <tt>:</tt> - Put timezone as colon-separated hours and minutes:
+
+ Time.now.strftime('%:z') # => "-05:00"
+
+- <tt>::</tt> - Put timezone as colon-separated hours, minutes, and seconds:
+
+ Time.now.strftime('%::z') # => "-05:00:00"
+
+=== Width Specifiers
+
+The integer width specifier gives a minimum width for the returned string:
+
+ Time.new(2002).strftime('%Y') # => "2002" # No width specifier.
+ Time.new(2002).strftime('%10Y') # => "0000002002"
+ Time.new(2002, 12).strftime('%B') # => "December" # No width specifier.
+ Time.new(2002, 12).strftime('%10B') # => " December"
+ Time.new(2002, 12).strftime('%3B') # => "December" # Ignored if too small.
+
+== Specialized Format Strings
+
+Here are a few specialized format strings,
+each based on an external standard.
+
+=== HTTP Format
+
+The HTTP date format is based on
+{RFC 2616}[https://datatracker.ietf.org/doc/html/rfc2616],
+and treats dates in the format <tt>'%a, %d %b %Y %T GMT'</tt>:
+
+ d = Date.new(2001, 2, 3) # => #<Date: 2001-02-03>
+ # Return HTTP-formatted string.
+ httpdate = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT"
+ # Return new date parsed from HTTP-formatted string.
+ Date.httpdate(httpdate) # => #<Date: 2001-02-03>
+ # Return hash parsed from HTTP-formatted string.
+ Date._httpdate(httpdate)
+ # => {:wday=>6, :mday=>3, :mon=>2, :year=>2001, :hour=>0, :min=>0, :sec=>0, :zone=>"GMT", :offset=>0}
+
+=== RFC 3339 Format
+
+The RFC 3339 date format is based on
+{RFC 3339}[https://datatracker.ietf.org/doc/html/rfc3339]:
+
+ d = Date.new(2001, 2, 3) # => #<Date: 2001-02-03>
+ # Return 3339-formatted string.
+ rfc3339 = d.rfc3339 # => "2001-02-03T00:00:00+00:00"
+ # Return new date parsed from 3339-formatted string.
+ Date.rfc3339(rfc3339) # => #<Date: 2001-02-03>
+ # Return hash parsed from 3339-formatted string.
+ Date._rfc3339(rfc3339)
+ # => {:year=>2001, :mon=>2, :mday=>3, :hour=>0, :min=>0, :sec=>0, :zone=>"+00:00", :offset=>0}
+
+=== RFC 2822 Format
+
+The RFC 2822 date format is based on
+{RFC 2822}[https://datatracker.ietf.org/doc/html/rfc2822],
+and treats dates in the format <tt>'%a, %-d %b %Y %T %z'</tt>]:
+
+ d = Date.new(2001, 2, 3) # => #<Date: 2001-02-03>
+ # Return 2822-formatted string.
+ rfc2822 = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000"
+ # Return new date parsed from 2822-formatted string.
+ Date.rfc2822(rfc2822) # => #<Date: 2001-02-03>
+ # Return hash parsed from 2822-formatted string.
+ Date._rfc2822(rfc2822)
+ # => {:wday=>6, :mday=>3, :mon=>2, :year=>2001, :hour=>0, :min=>0, :sec=>0, :zone=>"+0000", :offset=>0}
+
+=== JIS X 0301 Format
+
+The JIS X 0301 format includes the
+{Japanese era name}[https://en.wikipedia.org/wiki/Japanese_era_name],
+and treats dates in the format <tt>'%Y-%m-%d'</tt>
+with the first letter of the romanized era name prefixed:
+
+ d = Date.new(2001, 2, 3) # => #<Date: 2001-02-03>
+ # Return 0301-formatted string.
+ jisx0301 = d.jisx0301 # => "H13.02.03"
+ # Return new date parsed from 0301-formatted string.
+ Date.jisx0301(jisx0301) # => #<Date: 2001-02-03>
+ # Return hash parsed from 0301-formatted string.
+ Date._jisx0301(jisx0301) # => {:year=>2001, :mon=>2, :mday=>3}
+
=== ISO 8601 Format Specifications
This section shows format specifications that are compatible with
@@ -407,57 +525,3 @@ separated by the letter +T+.
For the relevant +strftime+ formats, see
{Dates}[rdoc-ref:strftime_formatting.rdoc@Dates]
and {Times}[rdoc-ref:strftime_formatting.rdoc@Times] above.
-
-=== Flags
-
-Flags may affect certain formatting specifications.
-
-Multiple flags may be given with a single conversion specified;
-order does not matter.
-
-==== Padding Flags
-
-- <tt>0</tt> - Pad with zeroes:
-
- Time.new(10).strftime('%0Y') # => "0010"
-
-- <tt>_</tt> - Pad with blanks:
-
- Time.new(10).strftime('%_Y') # => " 10"
-
-- <tt>-</tt> - Don't pad:
-
- Time.new(10).strftime('%-Y') # => "10"
-
-==== Casing Flags
-
-- <tt>^</tt> - Upcase result:
-
- Time.new(2022, 1).strftime('%B') # => "January" # No casing flag.
- Time.new(2022, 1).strftime('%^B') # => "JANUARY"
-
-- <tt>#</tt> - Swapcase result:
-
- Time.now.strftime('%p') # => "AM"
- Time.now.strftime('%^p') # => "AM"
- Time.now.strftime('%#p') # => "am"
-
-==== Timezone Flags
-
-- <tt>:</tt> - Put timezone as colon-separated hours and minutes:
-
- Time.now.strftime('%:z') # => "-05:00"
-
-- <tt>::</tt> - Put timezone as colon-separated hours, minutes, and seconds:
-
- Time.now.strftime('%::z') # => "-05:00:00"
-
-=== Width Specifiers
-
-The integer width specifier gives a minimum width for the returned string:
-
- Time.new(2002).strftime('%Y') # => "2002" # No width specifier.
- Time.new(2002).strftime('%10Y') # => "0000002002"
- Time.new(2002, 12).strftime('%B') # => "December" # No width specifier.
- Time.new(2002, 12).strftime('%10B') # => " December"
- Time.new(2002, 12).strftime('%3B') # => "December" # Ignored if too small.
diff --git a/doc/syntax/literals.rdoc b/doc/syntax/literals.rdoc
index 5e10e6a140..b641433249 100644
--- a/doc/syntax/literals.rdoc
+++ b/doc/syntax/literals.rdoc
@@ -277,6 +277,12 @@ the content. Note that empty lines and lines consisting solely of literal tabs
and spaces will be ignored for the purposes of determining indentation, but
escaped tabs and spaces are considered non-indentation characters.
+For the purpose of measuring an indentation, a horizontal tab is regarded as a
+sequence of one to eight spaces such that the column position corresponding to
+its end is a multiple of eight. The amount to be removed is counted in terms
+of the number of spaces. If the boundary appears in the middle of a tab, that
+tab is not removed.
+
A heredoc allows interpolation and escaped characters. You may disable
interpolation and escaping by surrounding the opening identifier with single
quotes:
diff --git a/doc/time/in.rdoc b/doc/time/in.rdoc
deleted file mode 100644
index f47db76a35..0000000000
--- a/doc/time/in.rdoc
+++ /dev/null
@@ -1,7 +0,0 @@
-- <tt>in: zone</tt>: a timezone _zone_, which may be:
- - A string offset from UTC.
- - A single letter offset from UTC, in the range <tt>'A'..'Z'</tt>,
- <tt>'J'</tt> (the so-called military timezone) excluded.
- - An integer number of seconds.
- - A timezone object;
- see {Timezone Argument}[#class-Time-label-Timezone+Argument] for details.
diff --git a/doc/time/mon-min.rdoc b/doc/time/mon-min.rdoc
deleted file mode 100644
index 5bd430c74a..0000000000
--- a/doc/time/mon-min.rdoc
+++ /dev/null
@@ -1,8 +0,0 @@
-- +month+: a month value, which may be:
- - An integer month in the range <tt>1..12</tt>.
- - A 3-character string that matches regular expression
- <tt>/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/i</tt>.
-- +day+: an integer day in the range <tt>1..31</tt>
- (less than 31 for some months).
-- +hour+: an integer hour in the range <tt>0..23</tt>.
-- +min+: an integer minute in the range <tt>0..59</tt>.
diff --git a/doc/time/msec.rdoc b/doc/time/msec.rdoc
deleted file mode 100644
index ce5d1e6145..0000000000
--- a/doc/time/msec.rdoc
+++ /dev/null
@@ -1,2 +0,0 @@
-- +msec+ is the number of milliseconds (Integer, Float, or Rational)
- in the range <tt>0..1000</tt>.
diff --git a/doc/time/nsec.rdoc b/doc/time/nsec.rdoc
deleted file mode 100644
index a2dfe2d608..0000000000
--- a/doc/time/nsec.rdoc
+++ /dev/null
@@ -1,2 +0,0 @@
-- +nsec+ is the number of nanoseconds (Integer, Float, or Rational)
- in the range <tt>0..1000000000</tt>.
diff --git a/doc/time/sec.rdoc b/doc/time/sec.rdoc
deleted file mode 100644
index 049c712110..0000000000
--- a/doc/time/sec.rdoc
+++ /dev/null
@@ -1,2 +0,0 @@
-- +sec+ is the number of seconds (Integer, Float, or Rational)
- in the range <tt>0..60</tt>.
diff --git a/doc/time/sec_i.rdoc b/doc/time/sec_i.rdoc
deleted file mode 100644
index 99c8eddc65..0000000000
--- a/doc/time/sec_i.rdoc
+++ /dev/null
@@ -1 +0,0 @@
-- +isec_i+ is the integer number of seconds in the range <tt>0..60</tt>.
diff --git a/doc/time/usec.rdoc b/doc/time/usec.rdoc
deleted file mode 100644
index bb5a46419a..0000000000
--- a/doc/time/usec.rdoc
+++ /dev/null
@@ -1,2 +0,0 @@
-- +usec+ is the number of microseconds (Integer, Float, or Rational)
- in the range <tt>0..1000000</tt>.
diff --git a/doc/time/year.rdoc b/doc/time/year.rdoc
deleted file mode 100644
index 2222b830d7..0000000000
--- a/doc/time/year.rdoc
+++ /dev/null
@@ -1 +0,0 @@
-- +year+: an integer year.
diff --git a/doc/time/zone_and_in.rdoc b/doc/time/zone_and_in.rdoc
deleted file mode 100644
index e09e22874b..0000000000
--- a/doc/time/zone_and_in.rdoc
+++ /dev/null
@@ -1,8 +0,0 @@
-- +zone+: a timezone, which may be:
- - A string offset from UTC.
- - A single letter offset from UTC, in the range <tt>'A'..'Z'</tt>,
- <tt>'J'</tt> (the so-called military timezone) excluded.
- - An integer number of seconds.
- - A timezone object;
- see {Timezone Argument}[#class-Time-label-Timezone+Argument] for details.
-- <tt>in: zone</tt>: a timezone _zone_, which may be as above.
diff --git a/doc/timezones.rdoc b/doc/timezones.rdoc
new file mode 100644
index 0000000000..c3aae88fde
--- /dev/null
+++ b/doc/timezones.rdoc
@@ -0,0 +1,108 @@
+== Timezones
+
+=== Timezone Specifiers
+
+Certain \Time methods accept arguments that specify timezones:
+
+- Time.at: keyword argument +in:+.
+- Time.new: positional argument +zone+ or keyword argument +in:+.
+- Time.now: keyword argument +in:+.
+- Time#getlocal: positional argument +zone+.
+- Time#localtime: positional argument +zone+.
+
+The value given with any of these must be one of the following
+(each detailed below):
+
+- {Hours/minutes offset}[rdoc-ref:timezones.rdoc@Hours-2FMinutes+Offsets].
+- {Single-letter offset}[rdoc-ref:timezones.rdoc@Single-Letter+Offsets].
+- {Integer offset}[rdoc-ref:timezones.rdoc@Integer+Offsets].
+- {Timezone object}[rdoc-ref:timezones.rdoc@Timezone+Objects].
+
+==== Hours/Minutes Offsets
+
+The zone value may be a string offset from UTC
+in the form <tt>'+HH:MM'</tt> or <tt>'-HH:MM'</tt>,
+where:
+
+- +HH+ is the 2-digit hour in the range <tt>0..23</tt>.
+- +MM+ is the 2-digit minute in the range <tt>0..59</tt>.
+
+Examples:
+
+ t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
+ Time.at(t, in: '-23:59') # => 1999-12-31 20:16:01 -2359
+ Time.at(t, in: '+23:59') # => 2000-01-02 20:14:01 +2359
+
+==== Single-Letter Offsets
+
+The zone value may be a letter in the range <tt>'A'..'I'</tt>
+or <tt>'K'..'Z'</tt>;
+see {List of military time zones}[https://en.wikipedia.org/wiki/List_of_military_time_zones]:
+
+ t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
+ Time.at(t, in: 'A') # => 2000-01-01 21:15:01 +0100
+ Time.at(t, in: 'I') # => 2000-01-02 05:15:01 +0900
+ Time.at(t, in: 'K') # => 2000-01-02 06:15:01 +1000
+ Time.at(t, in: 'Y') # => 2000-01-01 08:15:01 -1200
+ Time.at(t, in: 'Z') # => 2000-01-01 20:15:01 UTC
+
+==== \Integer Offsets
+
+The zone value may be an integer number of seconds
+in the range <tt>-86399..86399</tt>:
+
+ t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
+ Time.at(t, in: -86399) # => 1999-12-31 20:15:02 -235959
+ Time.at(t, in: 86399) # => 2000-01-02 20:15:00 +235959
+
+==== Timezone Objects
+
+In most cases, the zone value may be an object
+responding to certain timezone methods.
+
+\Exceptions (timezone object not allowed):
+
+- Time.new with positional argument +zone+.
+- Time.now with keyword argument +in:+.
+
+The timezone methods are:
+
+- +local_to_utc+:
+
+ - Called when Time.new is invoked with +tz+
+ as the value of positional argument +zone+ or keyword argument +in:+.
+ - Argument: a <tt>Time::tm</tt> object.
+ - Returns: a \Time-like object in the UTC timezone.
+
+- +utc_to_local+:
+
+ - Called when Time.at or Time.now is invoked with +tz+
+ as the value for keyword argument +in:+,
+ and when Time#getlocal or Time#localtime is called with +tz+
+ as the value for positional argument +zone+.
+ - Argument: a <tt>Time::tm</tt> object.
+ - Returns: a \Time-like object in the local timezone.
+
+A custom timezone class may have these instance methods,
+which will be called if defined:
+
+- +abbr+:
+
+ - Called when Time#strftime is invoked with a format involving <tt>%Z</tt>.
+ - Argument: a <tt>Time::tm</tt> object.
+ - Returns: a string abbreviation for the timezone name.
+
+- +dst?+:
+
+ - Called when Time.at or Time.now is invoked with +tz+
+ as the value for keyword argument +in:+,
+ and when Time#getlocal or Time#localtime is called with +tz+
+ as the value for positional argument +zone+.
+ - Argument: a <tt>Time::tm</tt> object.
+ - Returns: whether the time is daylight saving time.
+
+- +name+:
+
+ - Called when <tt>Marshal.dump(t) is invoked
+ - Argument: none.
+ - Returns: the string name of the timezone.
diff --git a/doc/yjit/yjit.md b/doc/yjit/yjit.md
index fd45096beb..67b2ffa5f0 100644
--- a/doc/yjit/yjit.md
+++ b/doc/yjit/yjit.md
@@ -8,46 +8,54 @@
YJIT - Yet Another Ruby JIT
===========================
-**DISCLAIMER: Please note that this project is experimental. It is very much a work in progress, it may cause your software to crash, and current performance results will vary widely, especially on larger applications.**
-
YJIT is a lightweight, minimalistic Ruby JIT built inside CRuby.
-It lazily compiles code using a Basic Block Versioning (BBV) architecture. The target use case is that of servers running
-Ruby on Rails, an area where MJIT has not yet managed to deliver speedups.
-To simplify development, we currently support only macOS and Linux on x86-64, but an ARM64 backend
-is part of future plans.
+It lazily compiles code using a Basic Block Versioning (BBV) architecture.
+The target use case is that of servers running Ruby on Rails.
+YJIT is currently supported for macOS, Linux and BSD on x86-64 and arm64/aarch64 CPUs.
This project is open source and falls under the same license as CRuby.
+<p align="center"><b>
+ If you're using YJIT in production, please
+ <a href="mailto:maxime.chevalierboisvert@shopify.com">share your success stories with us!</a>
+ </b></p>
+
If you wish to learn more about the approach taken, here are some conference talks and publications:
+- RubyKaigi 2022 keynote: [Stories from developing YJIT](https://www.youtube.com/watch?v=EMchdR9C8XM)
+- RubyKaigi 2022 talk: [Building a Lightweight IR and Backend for YJIT](https://www.youtube.com/watch?v=BbLGqTxTRp0)
- RubyKaigi 2021 talk: [YJIT: Building a New JIT Compiler Inside CRuby](https://www.youtube.com/watch?v=PBVLf3yfMs8)
- Blog post: [YJIT: Building a New JIT Compiler Inside CRuby](https://pointersgonewild.com/2021/06/02/yjit-building-a-new-jit-compiler-inside-cruby/)
+- VMIL 2021 paper: [YJIT: A Basic Block Versioning JIT Compiler for CRuby](https://dl.acm.org/doi/10.1145/3486606.3486781)
- MoreVMs 2021 talk: [YJIT: Building a New JIT Compiler Inside CRuby](https://www.youtube.com/watch?v=vucLAqv7qpc)
- ECOOP 2016 talk: [Interprocedural Type Specialization of JavaScript Programs Without Type Analysis](https://www.youtube.com/watch?v=sRNBY7Ss97A)
- ECOOP 2016 paper: [Interprocedural Type Specialization of JavaScript Programs Without Type Analysis](https://drops.dagstuhl.de/opus/volltexte/2016/6101/pdf/LIPIcs-ECOOP-2016-7.pdf)
- ECOOP 2015 talk: [Simple and Effective Type Check Removal through Lazy Basic Block Versioning](https://www.youtube.com/watch?v=S-aHBuoiYE0)
- ECOOP 2015 paper: [Simple and Effective Type Check Removal through Lazy Basic Block Versioning](https://arxiv.org/pdf/1411.0352.pdf)
-To cite this repository in your publications, please use this bibtex snippet:
-
-```
-@misc{yjit_ruby_jit,
- author = {Chevalier-Boisvert, Maxime and Wu, Alan and Patterson, Aaron},
- title = {YJIT - Yet Another Ruby JIT},
- year = {2021},
- publisher = {GitHub},
- journal = {GitHub repository},
- howpublished = {\url{https://github.com/Shopify/yjit}},
+To cite YJIT in your publications, please cite the VMIL 2021 paper:
+
+```
+@inproceedings{yjit_vmil2021,
+author = {Chevalier-Boisvert, Maxime and Gibbs, Noah and Boussier, Jean and Wu, Si Xing (Alan) and Patterson, Aaron and Newton, Kevin and Hawthorn, John},
+title = {YJIT: A Basic Block Versioning JIT Compiler for CRuby},
+year = {2021},
+isbn = {9781450391092},
+publisher = {Association for Computing Machinery},
+address = {New York, NY, USA},
+url = {https://doi.org/10.1145/3486606.3486781},
+doi = {10.1145/3486606.3486781},
+booktitle = {Proceedings of the 13th ACM SIGPLAN International Workshop on Virtual Machines and Intermediate Languages},
+pages = {25–32},
+numpages = {8},
+keywords = {ruby, dynamically typed, compiler, optimization, just-in-time, bytecode},
+location = {Chicago, IL, USA},
+series = {VMIL 2021}
}
```
## Current Limitations
-YJIT is a work in progress and as such may not yet be mature enough for mission-critical software. Below is a list of known limitations, all of which we plan to eventually address:
-
-- No garbage collection for generated code.
-- Currently supports only macOS and Linux.
-- Currently supports only x86-64 CPUs.
-
-Because there is no GC for generated code yet, your software could run out of executable memory if it is large enough. You can change how much executable memory is allocated using [YJIT's command-line options](#command-line-options).
+YJIT may not be suitable for certain applications. It currently only supports macOS and Linux on x86-64 and arm64/aarch64 CPUs. YJIT will use more memory than the Ruby interpreter because the JIT compiler needs to generate machine code in memory and maintain additional state information.
+You can change how much executable memory is allocated using [YJIT's command-line options](#command-line-options). There is a slight performance tradeoff because allocating less executable memory could result in the generated machine code being collected more often.
## Installation
@@ -57,6 +65,7 @@ You will need to install:
- A C compiler such as GCC or Clang
- GNU Make and Autoconf
- The Rust compiler `rustc` and Cargo (if you want to build in dev/debug mode)
+ - The Rust version must be [>= 1.58.0](../../yjit/Cargo.toml).
To install the Rust build toolchain, we suggest following the [recommended installation method][rust-install]. Rust also provides first class [support][editor-tools] for many source code editors.
@@ -67,51 +76,62 @@ To install the Rust build toolchain, we suggest following the [recommended insta
Start by cloning the `ruby/ruby` repository:
-```
+```sh
git clone https://github.com/ruby/ruby yjit
cd yjit
```
-The YJIT `ruby` binary can be built with either GCC or Clang. It can be built either in dev (debug) mode or in release mode. For maximum performance, compile YJIT in release mode with GCC. More detailed build instructions are provided in the [Ruby README](https://github.com/ruby/ruby#how-to-compile-and-install).
+The YJIT `ruby` binary can be built with either GCC or Clang. It can be built either in dev (debug) mode or in release mode. For maximum performance, compile YJIT in release mode with GCC. More detailed build instructions are provided in the [Ruby README](https://github.com/ruby/ruby#how-to-build).
-```
+```sh
# Configure in release mode for maximum performance, build and install
./autogen.sh
-./configure --enable-yjit --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --disable--install-rdoc
-make -j install
+./configure --enable-yjit --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc
+make -j && make install
```
or
+```sh
+# Configure in lower-performance dev (debug) mode for development, build and install
+./autogen.sh
+./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc
+make -j && make install
```
-# Configure in dev (debug) mode for development, build and install
+
+Dev mode includes extended YJIT statistics, but can be slow. For only statistics you can configure in stats mode:
+
+```sh
+# Configure in extended-stats mode without slow runtime checks, build and install
./autogen.sh
-./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --disable--install-rdoc
-make -j install
+./configure --enable-yjit=stats --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc
+make -j && make install
```
On macOS, you may need to specify where to find some libraries:
-```
+```sh
# Install dependencies
brew install openssl readline libyaml
# Configure in dev (debug) mode for development, build and install
./autogen.sh
-./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --disable--install-rdoc --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)"
-make -j install
+./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)"
+make -j && make install
```
Typically configure will choose the default C compiler. To specify the C compiler, use
-```
+
+```sh
# Choosing a specific c compiler
export CC=/path/to/my/chosen/c/compiler
```
+
before running `./configure`.
You can test that YJIT works correctly by running:
-```
+```sh
# Quick tests found in /bootstraptest
make btest
@@ -126,71 +146,111 @@ make -j test-all
Once YJIT is built, you can either use `./miniruby` from within your build directory, or switch to the YJIT version of `ruby`
by using the `chruby` tool:
-```
+```sh
chruby ruby-yjit
ruby myscript.rb
```
You can dump statistics about compilation and execution by running YJIT with the `--yjit-stats` command-line option:
-```
+```sh
./miniruby --yjit-stats myscript.rb
```
The machine code generated for a given method can be printed by adding `puts RubyVM::YJIT.disasm(method(:method_name))` to a Ruby script. Note that no code will be generated if the method is not compiled.
-
### Command-Line Options
YJIT supports all command-line options supported by upstream CRuby, but also adds a few YJIT-specific options:
- `--yjit`: enable YJIT (disabled by default)
-- `--yjit-call-threshold=N`: number of calls after which YJIT begins to compile a function (default 2)
-- `--yjit-exec-mem-size=N`: size of the executable memory block to allocate, in MiB (default 256 MiB)
-- `--yjit-stats`: produce statistics after the execution of a program (must compile with `cppflags=-DRUBY_DEBUG` to use this)
-- `--yjit-max-versions=N`: maximum number of versions to generate per basic block (default 4)
-- `--yjit-greedy-versioning`: greedy versioning mode (disabled by default, may increase code size)
+- `--yjit-call-threshold=N`: number of calls after which YJIT begins to compile a function (default 30)
+- `--yjit-exec-mem-size=N`: size of the executable memory block to allocate, in MiB (default 64 MiB)
+- `--yjit-stats`: print statistics after the execution of a program (incurs a run-time cost)
+- `--yjit-trace-exits`: produce a Marshal dump of backtraces from specific exits. Automatically enables `--yjit-stats`
+
+Note that there is also an environment variable `RUBY_YJIT_ENABLE` which can be used to enable YJIT.
+This can be useful for some deployment scripts where specifying an extra command-line option to Ruby is not practical.
### Benchmarking
We have collected a set of benchmarks and implemented a simple benchmarking harness in the [yjit-bench](https://github.com/Shopify/yjit-bench) repository. This benchmarking harness is designed to disable CPU frequency scaling, set process affinity and disable address space randomization so that the variance between benchmarking runs will be as small as possible. Please kindly note that we are at an early stage in this project.
-### Performance Tips
+## Performance Tips for Production Deployments
+
+While YJIT options default to what we think would work well for most workloads,
+they might not necessarily be the best configuration for your application.
+
+This section covers tips on improving YJIT performance in case YJIT does not
+speed up your application in production.
+
+### Increasing --yjit-exec-mem-size
+
+When JIT code size (`RubyVM::YJIT.runtime_stats[:code_region_size]`) reaches this value,
+YJIT triggers "code GC" that frees all JIT code and starts recompiling everything.
+Compiling code takes some time, so scheduling code GC too frequently slows down your application.
+Increasing `--yjit-exec-mem-size` may speed up your application if `RubyVM::YJIT.runtime_stats[:code_gc_count]` is not 0 or 1.
+
+### Running workers as long as possible
+
+It's helpful to call the same code as many times as possible before a process restarts.
+If a process is killed too frequently, the time taken for compiling methods may outweigh
+the speedup obtained by compiling them.
+
+You should monitor the number of requests each process has served.
+If you're periodically killing worker processes, e.g. with `unicorn-worker-killer` or `puma_worker_killer`,
+you may want to reduce the killing frequency or increase the limit.
+
+## Saving YJIT Memory Usage
+
+YJIT allocates memory for JIT code and metadata. Enabling YJIT generally results in more memory usage.
-This section contains tips on writing Ruby code that will run as fast as possible on YJIT. Some of this advice is based on current limitations of YJIT, while other advice is broadly applicable. It probably won't be practical to apply these tips everywhere in your codebase, but you can profile your code using a tool such as [stackprof](https://github.com/tmm1/stackprof) and refactor the specific methods that make up the largest fractions of the execution time.
+This section goes over tips on minimizing YJIT memory usage in case it uses more than your capacity.
-- Use exceptions for error recovery only, not as part of normal control-flow
+### Increasing --yjit-call-threshold
+
+As of Ruby 3.2, `--yjit-call-threshold` defaults to 30. With this default, some applications end up
+compiling methods that are used only during the application boot. Increasing this option may help
+you reduce the size of JIT code and metadata. It's worth trying different values like `--yjit-call-threshold=100`.
+
+Note that increasing the value too much may result in compiling code too late.
+You should monitor how many requests each worker processes before it's restarted. For example,
+if each process only handles 1000 requests, `--yjit-call-threshold=1000` might be too large for your application.
+
+### Decreasing --yjit-exec-mem-size
+
+`--yjit-exec-mem-size` specifies the JIT code size, but YJIT also uses memory for its metadata,
+which often consumes more memory than JIT code. Generally, YJIT adds memory overhead by roughly
+3-4x of `--yjit-exec-mem-size` in production as of Ruby 3.2. You should multiply that by the number
+of worker processes to estimate the worst case memory overhead.
+
+Running code GC adds overhead, but it could be still faster than recovering from a whole process killed by OOM.
+
+## Code Optimization Tips
+
+This section contains tips on writing Ruby code that will run as fast as possible on YJIT. Some of this advice is based on current limitations of YJIT, while other advice is broadly applicable. It probably won't be practical to apply these tips everywhere in your codebase. You should ideally start by profiling your application using a tool such as [stackprof](https://github.com/tmm1/stackprof) so that you can determine which methods make up most of the execution time. You can then refactor the specific methods that make up the largest fractions of the execution time. We do not recommend modifying your entire codebase based on the current limitations of YJIT.
+
+- Avoid using `OpenStruct`
- Avoid redefining basic integer operations (i.e. +, -, <, >, etc.)
- Avoid redefining the meaning of `nil`, equality, etc.
- Avoid allocating objects in the hot parts of your code
-- Use while loops if you can, instead of `integer.times`
- Minimize layers of indirection
- Avoid classes that wrap objects if you can
- Avoid methods that just call another method, trivial one liner methods
-- CRuby method calls are costly. Favor larger methods over smaller methods.
- Try to write code so that the same variables always have the same type
+- Use `while` loops if you can, instead of C methods like `Array#each`
+ - This is not idiomatic Ruby, but could help in hot methods
+- CRuby method calls are costly. Avoid things such as methods that only return a value from a hash or return a constant.
-You can also compile YJIT in debug mode and use the `--yjit-stats` command-line option to see which bytecodes cause YJIT to exit, and refactor your code to avoid using these instructions in the hottest methods of your code.
-
-### Memory Statistics
-
-YJIT, including in production configuration, keeps track of the size of generated code. If you check YJIT.runtime_stats you can see them:
-
-```
-$ RUBYOPT="--yjit" irb
-irb(main):001:0> RubyVM::YJIT.runtime_stats
-=> {:inline_code_size=>331945, :outlined_code_size=>272980}
-```
-
-These are the size in bytes of generated inlined code and generated outlined code. If the combined sizes for generated code are very close to the total YJIT exec-mem-size (see above), YJIT will stop generating code once the limit is reached. Try to make sure you have enough exec-mem-size for the program you're running. By default YJIT will allocate 268,435,456 bytes (256 MiB) of space for generated inlined and outlined code.
+You can also use the `--yjit-stats` command-line option to see which bytecodes cause YJIT to exit, and refactor your code to avoid using these instructions in the hottest methods of your code.
### Other Statistics
-If you compile Ruby with RUBY_DEBUG and/or YJIT_STATS defined and run with "--yjit --yjit-stats", YJIT will track and return performance statistics in RubyVM::YJIT.runtime_stats.
+If you run `ruby` with `--yjit --yjit-stats`, YJIT will track and return performance statistics in `RubyVM::YJIT.runtime_stats`.
-```
+```rb
$ RUBYOPT="--yjit --yjit-stats" irb
-irb(main):001:0> YJIT.runtime_stats
+irb(main):001:0> RubyVM::YJIT.runtime_stats
=>
{:inline_code_size=>340745,
:outlined_code_size=>297664,
@@ -204,16 +264,24 @@ irb(main):001:0> YJIT.runtime_stats
Some of the counters include:
-:exec_instruction - how many Ruby bytecode instructions have been executed
-:binding_allocations - number of bindings allocated
-:binding_set - number of variables set via a binding
-:vm_insns_count - number of instructions executed by the Ruby interpreter
-:compiled_iseq_count - number of bytecode sequences compiled
+* :exec_instruction - how many Ruby bytecode instructions have been executed
+* :binding_allocations - number of bindings allocated
+* :binding_set - number of variables set via a binding
+* :code_gc_count - number of garbage collections of compiled code since process start
+* :vm_insns_count - number of instructions executed by the Ruby interpreter
+* :compiled_iseq_count - number of bytecode sequences compiled
+* :inline_code_size - size in bytes of compiled YJIT blocks
+* :outline_code_size - size in bytes of YJIT error-handling compiled code
+* :side_exit_count - number of side exits taken at runtime
+* :total_exit_count - number of exits, including side exits, taken at runtime
+* :avg_len_in_yjit - avg. number of instructions in compiled blocks before exiting to interpreter
Counters starting with "exit_" show reasons for YJIT code taking a side exit (return to the interpreter.) See yjit_hacking.md for more details.
Performance counter names are not guaranteed to remain the same between Ruby versions. If you're curious what one does, it's usually best to search the source code for it &mdash; but it may change in a later Ruby version.
+The printed text after a --yjit-stats run includes other information that may be named differently than the information in runtime_stats.
+
## Contributing
We welcome open source contributors. You should feel free to open new issues to report bugs or just to ask questions.
@@ -241,8 +309,6 @@ The YJIT source code is divided between:
- `yjit/src/options.rs`: handling of command-line options
- `yjit/bindgen/src/main.rs`: C bindings exposed to the Rust codebase through bindgen
- `yjit/src/cruby.rs`: C bindings manually exposed to the Rust codebase
-- `misc/test_yjit_asm.sh`: script to compile and run the in-memory assembler tests
-- `misc/yjit_asm_tests.c`: tests for the in-memory assembler
The core of CRuby's interpreter logic is found in:
- `insns.def`: defines Ruby's bytecode instructions (gets compiled into `vm.inc`)
@@ -273,49 +339,49 @@ There are 3 test suites:
The tests can be run in parallel like this:
-```
+```sh
make -j test-all RUN_OPTS="--yjit-call-threshold=1"
```
Or single-threaded like this, to more easily identify which specific test is failing:
-```
+```sh
make test-all TESTOPTS=--verbose RUN_OPTS="--yjit-call-threshold=1"
```
To debug a single test in `test-all`:
-```
+```sh
make test-all TESTS='test/-ext-/marshal/test_usrmarshal.rb' RUNRUBYOPT=--debugger=lldb RUN_OPTS="--yjit-call-threshold=1"
```
You can also run one specific test in `btest`:
-```
+```sh
make btest BTESTS=bootstraptest/test_ractor.rb RUN_OPTS="--yjit-call-threshold=1"
```
There are shortcuts to run/debug your own test/repro in `test.rb`:
-```
+```sh
make run # runs ./miniruby test.rb
make lldb # launches ./miniruby test.rb in lldb
```
You can use the Intel syntax for disassembly in LLDB, keeping it consistent with YJIT's disassembly:
-```
+```sh
echo "settings set target.x86-disassembly-flavor intel" >> ~/.lldbinit
```
-## Running YJIT on M1
+## Running x86 YJIT on Apple's Rosetta
-It is possible to run YJIT on an Apple M1 via Rosetta. You can find basic
+For development purposes, it is possible to run x86 YJIT on an Apple M1 via Rosetta. You can find basic
instructions below, but there are a few caveats listed further down.
First, install Rosetta:
-```
+```sh
$ softwareupdate --install-rosetta
```
@@ -323,13 +389,13 @@ Now any command can be run with Rosetta via the `arch` command line tool.
Then you can start your shell in an x86 environment:
-```
+```sh
$ arch -x86_64 zsh
```
You can double check your current architecture via the `arch` command:
-```
+```sh
$ arch -x86_64 zsh
$ arch
i386
@@ -337,16 +403,15 @@ i386
You may need to set the default target for `rustc` to x86-64, e.g.
-```
+```sh
$ rustup default stable-x86_64-apple-darwin
```
While in your i386 shell, install Cargo and Homebrew, then hack away!
-### M1 Caveats
+### Rosetta Caveats
1. You must install a version of Homebrew for each architecture
2. Cargo will install in $HOME/.cargo by default, and I don't know a good way to change architectures after install
-3. `dev` won't work if you have i386 Homebrew installed on an M1
If you use Fish shell you can [read this link](https://tenderlovemaking.com/2022/01/07/homebrew-rosetta-and-ruby.html) for information on making the dev environment easier.
diff --git a/enc/Makefile.in b/enc/Makefile.in
index 5e5d39cd76..9d0c367134 100644
--- a/enc/Makefile.in
+++ b/enc/Makefile.in
@@ -22,6 +22,7 @@ TRANSSODIR = $(ENCSODIR)/trans
DLEXT = @DLEXT@
OBJEXT = @OBJEXT@
LIBEXT = @LIBEXT@
+EXEEXT = @EXEEXT@
TIMESTAMPDIR = $(EXTOUT)/.timestamp
ENC_TRANS_D = $(TIMESTAMPDIR)/.enc-trans.time
ENC_TRANS_SO_D = $(TIMESTAMPDIR)/.enc-trans.so.time
@@ -35,6 +36,7 @@ RUBY_SO_NAME = @RUBY_SO_NAME@
LIBRUBY = @LIBRUBY@
LIBRUBYARG_SHARED = @LIBRUBYARG_SHARED@
LIBRUBYARG_STATIC = $(LIBRUBYARG_SHARED)
+BUILTRUBY = $(topdir)/miniruby$(EXEEXT)
empty =
AR = @AR@
@@ -49,7 +51,7 @@ optflags = @optflags@
debugflags = @debugflags@
warnflags = @warnflags@
CCDLFLAGS = @CCDLFLAGS@
-INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(top_srcdir)
+INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(top_srcdir) @incflags@
DEFS = @DEFS@
CPPFLAGS = @CPPFLAGS@ -DONIG_ENC_REGISTER=rb_enc_register
LDFLAGS = @LDFLAGS@
diff --git a/enc/cesu_8.c b/enc/cesu_8.c
index decbb928f4..75f62df280 100644
--- a/enc/cesu_8.c
+++ b/enc/cesu_8.c
@@ -42,6 +42,8 @@
#define VALID_CODE_LIMIT 0x0010ffff
#define utf8_islead(c) ((UChar )((c) & 0xc0) != 0x80)
+#define utf16_is_high_surrogate(v) ((v >> 10) == 0x36)
+#define utf16_is_low_surrogate(v) ((v >> 10) == 0x37)
static const int EncLen_CESU8[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -283,6 +285,12 @@ is_mbc_newline(const UChar* p, const UChar* end, OnigEncoding enc)
return 0;
}
+static int
+utf8_decode_3byte_sequence(const UChar* p)
+{
+ return ((p[0] & 0xF) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);
+}
+
static OnigCodePoint
mbc_to_code(const UChar* p, const UChar* end, OnigEncoding enc)
{
@@ -295,11 +303,11 @@ mbc_to_code(const UChar* p, const UChar* end, OnigEncoding enc)
case 2:
return ((p[0] & 0x1F) << 6) | (p[1] & 0x3f);
case 3:
- return ((p[0] & 0xF) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);
+ return utf8_decode_3byte_sequence(p);
case 6:
{
- int high = ((p[0] & 0xF) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);
- int low = ((p[3] & 0xF) << 12) | ((p[4] & 0x3f) << 6) | (p[5] & 0x3f);
+ int high = utf8_decode_3byte_sequence(p);
+ int low = utf8_decode_3byte_sequence(p + 3);
return ((high & 0x03ff) << 10) + (low & 0x03ff) + 0x10000;
}
}
@@ -410,7 +418,6 @@ get_ctype_code_range(OnigCtype ctype, OnigCodePoint *sb_out,
return onigenc_unicode_ctype_code_range(ctype, ranges);
}
-
static UChar*
left_adjust_char_head(const UChar* start, const UChar* s, const UChar* end, OnigEncoding enc ARG_UNUSED)
{
@@ -420,6 +427,14 @@ left_adjust_char_head(const UChar* start, const UChar* s, const UChar* end, Onig
p = s;
while (!utf8_islead(*p) && p > start) p--;
+
+ if (p > start && s - p == 2 && utf16_is_low_surrogate(utf8_decode_3byte_sequence(p))) {
+ const UChar *p_surrogate_pair = p - 1;
+ while (!utf8_islead(*p_surrogate_pair) && p_surrogate_pair > start) p_surrogate_pair--;
+ if (p - p_surrogate_pair == 3 && utf16_is_high_surrogate(utf8_decode_3byte_sequence(p_surrogate_pair))) {
+ return (UChar* )p_surrogate_pair;
+ }
+ }
return (UChar* )p;
}
diff --git a/enc/depend b/enc/depend
index 60c5a3ebb2..973ad93010 100644
--- a/enc/depend
+++ b/enc/depend
@@ -7018,6 +7018,7 @@ enc/trans/iso2022.$(OBJEXT): internal/attr/nodiscard.h
enc/trans/iso2022.$(OBJEXT): internal/attr/noexcept.h
enc/trans/iso2022.$(OBJEXT): internal/attr/noinline.h
enc/trans/iso2022.$(OBJEXT): internal/attr/nonnull.h
+enc/trans/iso2022.$(OBJEXT): internal/attr/nonstring.h
enc/trans/iso2022.$(OBJEXT): internal/attr/noreturn.h
enc/trans/iso2022.$(OBJEXT): internal/attr/pure.h
enc/trans/iso2022.$(OBJEXT): internal/attr/restrict.h
diff --git a/enc/encdb.c b/enc/encdb.c
index a1936df804..8247e9ff6a 100644
--- a/enc/encdb.c
+++ b/enc/encdb.c
@@ -17,7 +17,7 @@
#define ENC_DEFINE(name) rb_encdb_declare(name)
#define ENC_SET_BASE(name, orig) rb_enc_set_base((name), (orig))
#define ENC_SET_DUMMY(name, orig) rb_enc_set_dummy(name)
-#define ENC_DUMMY_UNICODE(name) rb_encdb_set_unicode(rb_enc_set_dummy(ENC_REPLICATE((name), name "BE")))
+#define ENC_DUMMY_UNICODE(name) ENC_DUMMY(name)
void
Init_encdb(void)
diff --git a/enc/jis/props.h.blt b/enc/jis/props.h.blt
index 54aa94f8bc..508a084449 100644
--- a/enc/jis/props.h.blt
+++ b/enc/jis/props.h.blt
@@ -69,7 +69,7 @@ struct enc_property {
unsigned char ctype;
};
-static const struct enc_property *onig_jis_property(/*const char *str, unsigned int len*/);
+static const struct enc_property *onig_jis_property(register const char *str, register size_t len);
#line 43 "enc/jis/props.kwd"
struct enc_property;
@@ -82,7 +82,7 @@ struct enc_property;
#ifndef GPERF_DOWNCASE
#define GPERF_DOWNCASE 1
-static unsigned char gperf_downcase[256] =
+static const 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,
diff --git a/enc/jis/props.kwd b/enc/jis/props.kwd
index 659cf0aff4..9606828459 100644
--- a/enc/jis/props.kwd
+++ b/enc/jis/props.kwd
@@ -37,7 +37,7 @@ struct enc_property {
unsigned char ctype;
};
-static const struct enc_property *onig_jis_property(/*!ANSI{*/const char *str, unsigned int len/*}!ANSI*/);
+static const struct enc_property *onig_jis_property(register const char *str, register size_t len);
%}
struct enc_property;
diff --git a/enc/jis/props.src b/enc/jis/props.src
index 659cf0aff4..9606828459 100644
--- a/enc/jis/props.src
+++ b/enc/jis/props.src
@@ -37,7 +37,7 @@ struct enc_property {
unsigned char ctype;
};
-static const struct enc_property *onig_jis_property(/*!ANSI{*/const char *str, unsigned int len/*}!ANSI*/);
+static const struct enc_property *onig_jis_property(register const char *str, register size_t len);
%}
struct enc_property;
diff --git a/enc/make_encmake.rb b/enc/make_encmake.rb
index bc0597e3f4..fcfc2c9267 100755
--- a/enc/make_encmake.rb
+++ b/enc/make_encmake.rb
@@ -134,7 +134,7 @@ else
end
mkin = File.read(File.join($srcdir, "Makefile.in"))
mkin.gsub!(/@(#{CONFIG.keys.join('|')})@/) {CONFIG[$1]}
-open(ARGV[0], 'wb') {|f|
+File.open(ARGV[0], 'wb') {|f|
f.puts mkin, dep
}
if MODULE_TYPE == :static
diff --git a/enc/trans/newline.trans b/enc/trans/newline.trans
index 9e763407f9..95e082f5bd 100644
--- a/enc/trans/newline.trans
+++ b/enc/trans/newline.trans
@@ -17,10 +17,16 @@
map_cr["0a"] = "0d"
transcode_generate_node(ActionMap.parse(map_cr), "cr_newline")
+
+ map_normalize = {}
+ map_normalize["{00-ff}"] = :func_so
+
+ transcode_generate_node(ActionMap.parse(map_normalize), "lf_newline")
%>
<%= transcode_generated_code %>
+#define lf_newline universal_newline
#define STATE (sp[0])
#define NORMAL 0
#define JUST_AFTER_CR 1
@@ -126,10 +132,24 @@ rb_cr_newline = {
0, 0, 0, 0
};
+static const rb_transcoder
+rb_lf_newline = {
+ "", "lf_newline", lf_newline,
+ TRANSCODE_TABLE_INFO,
+ 1, /* input_unit_length */
+ 1, /* max_input */
+ 2, /* max_output */
+ asciicompat_converter, /* asciicompat_type */
+ 2, universal_newline_init, universal_newline_init, /* state_size, state_init, state_fini */
+ 0, 0, 0, fun_so_universal_newline,
+ universal_newline_finish
+};
+
void
Init_newline(void)
{
rb_register_transcoder(&rb_universal_newline);
rb_register_transcoder(&rb_crlf_newline);
rb_register_transcoder(&rb_cr_newline);
+ rb_register_transcoder(&rb_lf_newline);
}
diff --git a/enc/unicode/14.0.0/casefold.h b/enc/unicode/15.0.0/casefold.h
index d387cff628..51120d867d 100644
--- a/enc/unicode/14.0.0/casefold.h
+++ b/enc/unicode/15.0.0/casefold.h
@@ -1,15 +1,15 @@
/* DO NOT EDIT THIS FILE. */
-/* Generated by enc/unicode/case-folding.rb */
+/* Generated by enc-case-folding.rb */
#if defined ONIG_UNICODE_VERSION_STRING && !( \
- ONIG_UNICODE_VERSION_MAJOR == 14 && \
+ ONIG_UNICODE_VERSION_MAJOR == 15 && \
ONIG_UNICODE_VERSION_MINOR == 0 && \
ONIG_UNICODE_VERSION_TEENY == 0 && \
1)
# error ONIG_UNICODE_VERSION_STRING mismatch
#endif
-#define ONIG_UNICODE_VERSION_STRING "14.0.0"
-#define ONIG_UNICODE_VERSION_MAJOR 14
+#define ONIG_UNICODE_VERSION_STRING "15.0.0"
+#define ONIG_UNICODE_VERSION_MAJOR 15
#define ONIG_UNICODE_VERSION_MINOR 0
#define ONIG_UNICODE_VERSION_TEENY 0
diff --git a/enc/unicode/14.0.0/name2ctype.h b/enc/unicode/15.0.0/name2ctype.h
index 99a3eeca19..a2c996423d 100644
--- a/enc/unicode/14.0.0/name2ctype.h
+++ b/enc/unicode/15.0.0/name2ctype.h
@@ -43,7 +43,7 @@ static const OnigCodePoint CR_NEWLINE[] = {
/* 'Alpha': [[:Alpha:]] */
static const OnigCodePoint CR_Alpha[] = {
- 722,
+ 732,
0x0041, 0x005a,
0x0061, 0x007a,
0x00aa, 0x00aa,
@@ -178,8 +178,7 @@ static const OnigCodePoint CR_Alpha[] = {
0x0bca, 0x0bcc,
0x0bd0, 0x0bd0,
0x0bd7, 0x0bd7,
- 0x0c00, 0x0c03,
- 0x0c05, 0x0c0c,
+ 0x0c00, 0x0c0c,
0x0c0e, 0x0c10,
0x0c12, 0x0c28,
0x0c2a, 0x0c39,
@@ -202,7 +201,7 @@ static const OnigCodePoint CR_Alpha[] = {
0x0cd5, 0x0cd6,
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d3a,
@@ -240,7 +239,7 @@ static const OnigCodePoint CR_Alpha[] = {
0x0f00, 0x0f00,
0x0f40, 0x0f47,
0x0f49, 0x0f6c,
- 0x0f71, 0x0f81,
+ 0x0f71, 0x0f83,
0x0f88, 0x0f97,
0x0f99, 0x0fbc,
0x1000, 0x1036,
@@ -542,7 +541,7 @@ static const OnigCodePoint CR_Alpha[] = {
0x10fe0, 0x10ff6,
0x11000, 0x11045,
0x11071, 0x11075,
- 0x11082, 0x110b8,
+ 0x11080, 0x110b8,
0x110c2, 0x110c2,
0x110d0, 0x110e8,
0x11100, 0x11132,
@@ -557,7 +556,7 @@ static const OnigCodePoint CR_Alpha[] = {
0x11200, 0x11211,
0x11213, 0x11234,
0x11237, 0x11237,
- 0x1123e, 0x1123e,
+ 0x1123e, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -637,12 +636,16 @@ static const OnigCodePoint CR_Alpha[] = {
0x11d93, 0x11d96,
0x11d98, 0x11d98,
0x11ee0, 0x11ef6,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f40,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -666,7 +669,9 @@ static const OnigCodePoint CR_Alpha[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -705,16 +710,20 @@ static const OnigCodePoint CR_Alpha[] = {
0x1d7aa, 0x1d7c2,
0x1d7c4, 0x1d7cb,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e137, 0x1e13d,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ad,
0x1e2c0, 0x1e2eb,
+ 0x1e4d0, 0x1e4eb,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -760,12 +769,13 @@ static const OnigCodePoint CR_Alpha[] = {
0x1f150, 0x1f169,
0x1f170, 0x1f189,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Alpha */
/* 'Blank': [[:Blank:]] */
@@ -790,7 +800,7 @@ static const OnigCodePoint CR_Cntrl[] = {
/* 'Digit': [[:Digit:]] */
static const OnigCodePoint CR_Digit[] = {
- 62,
+ 64,
0x0030, 0x0039,
0x0660, 0x0669,
0x06f0, 0x06f9,
@@ -845,19 +855,21 @@ static const OnigCodePoint CR_Digit[] = {
0x11c50, 0x11c59,
0x11d50, 0x11d59,
0x11da0, 0x11da9,
+ 0x11f50, 0x11f59,
0x16a60, 0x16a69,
0x16ac0, 0x16ac9,
0x16b50, 0x16b59,
0x1d7ce, 0x1d7ff,
0x1e140, 0x1e149,
0x1e2f0, 0x1e2f9,
+ 0x1e4f0, 0x1e4f9,
0x1e950, 0x1e959,
0x1fbf0, 0x1fbf9,
}; /* CR_Digit */
/* 'Graph': [[:Graph:]] */
static const OnigCodePoint CR_Graph[] = {
- 703,
+ 712,
0x0021, 0x007e,
0x00a1, 0x0377,
0x037a, 0x037f,
@@ -980,7 +992,7 @@ static const OnigCodePoint CR_Graph[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d44,
@@ -1010,7 +1022,7 @@ static const OnigCodePoint CR_Graph[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
0x0f00, 0x0f47,
@@ -1285,7 +1297,7 @@ static const OnigCodePoint CR_Graph[] = {
0x10e80, 0x10ea9,
0x10eab, 0x10ead,
0x10eb0, 0x10eb1,
- 0x10f00, 0x10f27,
+ 0x10efd, 0x10f27,
0x10f30, 0x10f59,
0x10f70, 0x10f89,
0x10fb0, 0x10fcb,
@@ -1302,7 +1314,7 @@ static const OnigCodePoint CR_Graph[] = {
0x11180, 0x111df,
0x111e1, 0x111f4,
0x11200, 0x11211,
- 0x11213, 0x1123e,
+ 0x11213, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -1355,6 +1367,7 @@ static const OnigCodePoint CR_Graph[] = {
0x11a00, 0x11a47,
0x11a50, 0x11aa2,
0x11ab0, 0x11af8,
+ 0x11b00, 0x11b09,
0x11c00, 0x11c08,
0x11c0a, 0x11c36,
0x11c38, 0x11c45,
@@ -1376,6 +1389,9 @@ static const OnigCodePoint CR_Graph[] = {
0x11d93, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef8,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f59,
0x11fb0, 0x11fb0,
0x11fc0, 0x11ff1,
0x11fff, 0x12399,
@@ -1383,8 +1399,7 @@ static const OnigCodePoint CR_Graph[] = {
0x12470, 0x12474,
0x12480, 0x12543,
0x12f90, 0x12ff2,
- 0x13000, 0x1342e,
- 0x13430, 0x13438,
+ 0x13000, 0x13455,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -1411,7 +1426,9 @@ static const OnigCodePoint CR_Graph[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -1426,6 +1443,7 @@ static const OnigCodePoint CR_Graph[] = {
0x1d100, 0x1d126,
0x1d129, 0x1d1ea,
0x1d200, 0x1d245,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d300, 0x1d356,
0x1d360, 0x1d378,
@@ -1453,11 +1471,14 @@ static const OnigCodePoint CR_Graph[] = {
0x1da9b, 0x1da9f,
0x1daa1, 0x1daaf,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e130, 0x1e13d,
0x1e140, 0x1e149,
@@ -1465,6 +1486,7 @@ static const OnigCodePoint CR_Graph[] = {
0x1e290, 0x1e2ae,
0x1e2c0, 0x1e2f9,
0x1e2ff, 0x1e2ff,
+ 0x1e4d0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -1523,10 +1545,10 @@ static const OnigCodePoint CR_Graph[] = {
0x1f250, 0x1f251,
0x1f260, 0x1f265,
0x1f300, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -1537,25 +1559,24 @@ static const OnigCodePoint CR_Graph[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
0xe0001, 0xe0001,
0xe0020, 0xe007f,
0xe0100, 0xe01ef,
@@ -1565,7 +1586,7 @@ static const OnigCodePoint CR_Graph[] = {
/* 'Lower': [[:Lower:]] */
static const OnigCodePoint CR_Lower[] = {
- 664,
+ 671,
0x0061, 0x007a,
0x00aa, 0x00aa,
0x00b5, 0x00b5,
@@ -1842,7 +1863,7 @@ static const OnigCodePoint CR_Lower[] = {
0x052f, 0x052f,
0x0560, 0x0588,
0x10d0, 0x10fa,
- 0x10fd, 0x10ff,
+ 0x10fc, 0x10ff,
0x13f8, 0x13fd,
0x1c80, 0x1c88,
0x1d00, 0x1dbf,
@@ -2182,10 +2203,11 @@ static const OnigCodePoint CR_Lower[] = {
0xa7d5, 0xa7d5,
0xa7d7, 0xa7d7,
0xa7d9, 0xa7d9,
+ 0xa7f2, 0xa7f4,
0xa7f6, 0xa7f6,
0xa7f8, 0xa7fa,
0xab30, 0xab5a,
- 0xab5c, 0xab68,
+ 0xab5c, 0xab69,
0xab70, 0xabbf,
0xfb00, 0xfb06,
0xfb13, 0xfb17,
@@ -2196,6 +2218,10 @@ static const OnigCodePoint CR_Lower[] = {
0x105a3, 0x105b1,
0x105b3, 0x105b9,
0x105bb, 0x105bc,
+ 0x10780, 0x10780,
+ 0x10783, 0x10785,
+ 0x10787, 0x107b0,
+ 0x107b2, 0x107ba,
0x10cc0, 0x10cf2,
0x118c0, 0x118df,
0x16e60, 0x16e7f,
@@ -2229,12 +2255,14 @@ static const OnigCodePoint CR_Lower[] = {
0x1d7cb, 0x1d7cb,
0x1df00, 0x1df09,
0x1df0b, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e030, 0x1e06d,
0x1e922, 0x1e943,
}; /* CR_Lower */
/* 'Print': [[:Print:]] */
static const OnigCodePoint CR_Print[] = {
- 700,
+ 709,
0x0020, 0x007e,
0x00a0, 0x0377,
0x037a, 0x037f,
@@ -2357,7 +2385,7 @@ static const OnigCodePoint CR_Print[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d44,
@@ -2387,7 +2415,7 @@ static const OnigCodePoint CR_Print[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
0x0f00, 0x0f47,
@@ -2659,7 +2687,7 @@ static const OnigCodePoint CR_Print[] = {
0x10e80, 0x10ea9,
0x10eab, 0x10ead,
0x10eb0, 0x10eb1,
- 0x10f00, 0x10f27,
+ 0x10efd, 0x10f27,
0x10f30, 0x10f59,
0x10f70, 0x10f89,
0x10fb0, 0x10fcb,
@@ -2676,7 +2704,7 @@ static const OnigCodePoint CR_Print[] = {
0x11180, 0x111df,
0x111e1, 0x111f4,
0x11200, 0x11211,
- 0x11213, 0x1123e,
+ 0x11213, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -2729,6 +2757,7 @@ static const OnigCodePoint CR_Print[] = {
0x11a00, 0x11a47,
0x11a50, 0x11aa2,
0x11ab0, 0x11af8,
+ 0x11b00, 0x11b09,
0x11c00, 0x11c08,
0x11c0a, 0x11c36,
0x11c38, 0x11c45,
@@ -2750,6 +2779,9 @@ static const OnigCodePoint CR_Print[] = {
0x11d93, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef8,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f59,
0x11fb0, 0x11fb0,
0x11fc0, 0x11ff1,
0x11fff, 0x12399,
@@ -2757,8 +2789,7 @@ static const OnigCodePoint CR_Print[] = {
0x12470, 0x12474,
0x12480, 0x12543,
0x12f90, 0x12ff2,
- 0x13000, 0x1342e,
- 0x13430, 0x13438,
+ 0x13000, 0x13455,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -2785,7 +2816,9 @@ static const OnigCodePoint CR_Print[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -2800,6 +2833,7 @@ static const OnigCodePoint CR_Print[] = {
0x1d100, 0x1d126,
0x1d129, 0x1d1ea,
0x1d200, 0x1d245,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d300, 0x1d356,
0x1d360, 0x1d378,
@@ -2827,11 +2861,14 @@ static const OnigCodePoint CR_Print[] = {
0x1da9b, 0x1da9f,
0x1daa1, 0x1daaf,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e130, 0x1e13d,
0x1e140, 0x1e149,
@@ -2839,6 +2876,7 @@ static const OnigCodePoint CR_Print[] = {
0x1e290, 0x1e2ae,
0x1e2c0, 0x1e2f9,
0x1e2ff, 0x1e2ff,
+ 0x1e4d0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -2897,10 +2935,10 @@ static const OnigCodePoint CR_Print[] = {
0x1f250, 0x1f251,
0x1f260, 0x1f265,
0x1f300, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -2911,25 +2949,24 @@ static const OnigCodePoint CR_Print[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
0xe0001, 0xe0001,
0xe0020, 0xe007f,
0xe0100, 0xe01ef,
@@ -2939,7 +2976,7 @@ static const OnigCodePoint CR_Print[] = {
/* 'XPosixPunct': [[:Punct:]] */
static const OnigCodePoint CR_XPosixPunct[] = {
- 184,
+ 186,
0x0021, 0x002f,
0x003a, 0x0040,
0x005b, 0x0060,
@@ -3109,9 +3146,11 @@ static const OnigCodePoint CR_XPosixPunct[] = {
0x11a3f, 0x11a46,
0x11a9a, 0x11a9c,
0x11a9e, 0x11aa2,
+ 0x11b00, 0x11b09,
0x11c41, 0x11c45,
0x11c70, 0x11c71,
0x11ef7, 0x11ef8,
+ 0x11f43, 0x11f4f,
0x11fff, 0x11fff,
0x12470, 0x12474,
0x12ff1, 0x12ff2,
@@ -3807,7 +3846,7 @@ static const OnigCodePoint CR_XDigit[] = {
/* 'Word': [[:Word:]] */
static const OnigCodePoint CR_Word[] = {
- 758,
+ 770,
0x0030, 0x0039,
0x0041, 0x005a,
0x005f, 0x005f,
@@ -3965,7 +4004,7 @@ static const OnigCodePoint CR_Word[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d44,
@@ -3998,7 +4037,7 @@ static const OnigCodePoint CR_Word[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
0x0f00, 0x0f00,
@@ -4311,7 +4350,7 @@ static const OnigCodePoint CR_Word[] = {
0x10e80, 0x10ea9,
0x10eab, 0x10eac,
0x10eb0, 0x10eb1,
- 0x10f00, 0x10f1c,
+ 0x10efd, 0x10f1c,
0x10f27, 0x10f27,
0x10f30, 0x10f50,
0x10f70, 0x10f85,
@@ -4334,7 +4373,7 @@ static const OnigCodePoint CR_Word[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x11237,
- 0x1123e, 0x1123e,
+ 0x1123e, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -4415,12 +4454,17 @@ static const OnigCodePoint CR_Word[] = {
0x11d93, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef6,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f42,
+ 0x11f50, 0x11f59,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13440, 0x13455,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -4448,7 +4492,9 @@ static const OnigCodePoint CR_Word[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -4502,17 +4548,21 @@ static const OnigCodePoint CR_Word[] = {
0x1da9b, 0x1da9f,
0x1daa1, 0x1daaf,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e130, 0x1e13d,
0x1e140, 0x1e149,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ae,
0x1e2c0, 0x1e2f9,
+ 0x1e4d0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -4559,18 +4609,19 @@ static const OnigCodePoint CR_Word[] = {
0x1f170, 0x1f189,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
0xe0100, 0xe01ef,
}; /* CR_Word */
/* 'Alnum': [[:Alnum:]] */
static const OnigCodePoint CR_Alnum[] = {
- 760,
+ 772,
0x0030, 0x0039,
0x0041, 0x005a,
0x0061, 0x007a,
@@ -4709,8 +4760,7 @@ static const OnigCodePoint CR_Alnum[] = {
0x0bd0, 0x0bd0,
0x0bd7, 0x0bd7,
0x0be6, 0x0bef,
- 0x0c00, 0x0c03,
- 0x0c05, 0x0c0c,
+ 0x0c00, 0x0c0c,
0x0c0e, 0x0c10,
0x0c12, 0x0c28,
0x0c2a, 0x0c39,
@@ -4735,7 +4785,7 @@ static const OnigCodePoint CR_Alnum[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d3a,
@@ -4778,7 +4828,7 @@ static const OnigCodePoint CR_Alnum[] = {
0x0f20, 0x0f29,
0x0f40, 0x0f47,
0x0f49, 0x0f6c,
- 0x0f71, 0x0f81,
+ 0x0f71, 0x0f83,
0x0f88, 0x0f97,
0x0f99, 0x0fbc,
0x1000, 0x1036,
@@ -5088,7 +5138,7 @@ static const OnigCodePoint CR_Alnum[] = {
0x11000, 0x11045,
0x11066, 0x1106f,
0x11071, 0x11075,
- 0x11082, 0x110b8,
+ 0x11080, 0x110b8,
0x110c2, 0x110c2,
0x110d0, 0x110e8,
0x110f0, 0x110f9,
@@ -5104,7 +5154,7 @@ static const OnigCodePoint CR_Alnum[] = {
0x11200, 0x11211,
0x11213, 0x11234,
0x11237, 0x11237,
- 0x1123e, 0x1123e,
+ 0x1123e, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -5194,12 +5244,17 @@ static const OnigCodePoint CR_Alnum[] = {
0x11d98, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef6,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f40,
+ 0x11f50, 0x11f59,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -5226,7 +5281,9 @@ static const OnigCodePoint CR_Alnum[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -5266,11 +5323,14 @@ static const OnigCodePoint CR_Alnum[] = {
0x1d7c4, 0x1d7cb,
0x1d7ce, 0x1d7ff,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e137, 0x1e13d,
0x1e140, 0x1e149,
@@ -5278,6 +5338,8 @@ static const OnigCodePoint CR_Alnum[] = {
0x1e290, 0x1e2ad,
0x1e2c0, 0x1e2eb,
0x1e2f0, 0x1e2f9,
+ 0x1e4d0, 0x1e4eb,
+ 0x1e4f0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -5325,12 +5387,13 @@ static const OnigCodePoint CR_Alnum[] = {
0x1f170, 0x1f189,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Alnum */
/* 'ASCII': [[:ASCII:]] */
@@ -5341,7 +5404,7 @@ static const OnigCodePoint CR_ASCII[] = {
/* 'Punct' */
static const OnigCodePoint CR_Punct[] = {
- 189,
+ 191,
0x0021, 0x0023,
0x0025, 0x002a,
0x002c, 0x002f,
@@ -5516,9 +5579,11 @@ static const OnigCodePoint CR_Punct[] = {
0x11a3f, 0x11a46,
0x11a9a, 0x11a9c,
0x11a9e, 0x11aa2,
+ 0x11b00, 0x11b09,
0x11c41, 0x11c45,
0x11c70, 0x11c71,
0x11ef7, 0x11ef8,
+ 0x11f43, 0x11f4f,
0x11fff, 0x11fff,
0x12470, 0x12474,
0x12ff1, 0x12ff2,
@@ -5542,7 +5607,7 @@ static const OnigCodePoint CR_Any[] = {
/* 'Assigned': - */
static const OnigCodePoint CR_Assigned[] = {
- 698,
+ 707,
0x0000, 0x0377,
0x037a, 0x037f,
0x0384, 0x038a,
@@ -5664,7 +5729,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d44,
@@ -5694,7 +5759,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
0x0f00, 0x0f47,
@@ -5965,7 +6030,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x10e80, 0x10ea9,
0x10eab, 0x10ead,
0x10eb0, 0x10eb1,
- 0x10f00, 0x10f27,
+ 0x10efd, 0x10f27,
0x10f30, 0x10f59,
0x10f70, 0x10f89,
0x10fb0, 0x10fcb,
@@ -5982,7 +6047,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x11180, 0x111df,
0x111e1, 0x111f4,
0x11200, 0x11211,
- 0x11213, 0x1123e,
+ 0x11213, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -6035,6 +6100,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x11a00, 0x11a47,
0x11a50, 0x11aa2,
0x11ab0, 0x11af8,
+ 0x11b00, 0x11b09,
0x11c00, 0x11c08,
0x11c0a, 0x11c36,
0x11c38, 0x11c45,
@@ -6056,6 +6122,9 @@ static const OnigCodePoint CR_Assigned[] = {
0x11d93, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef8,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f59,
0x11fb0, 0x11fb0,
0x11fc0, 0x11ff1,
0x11fff, 0x12399,
@@ -6063,8 +6132,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x12470, 0x12474,
0x12480, 0x12543,
0x12f90, 0x12ff2,
- 0x13000, 0x1342e,
- 0x13430, 0x13438,
+ 0x13000, 0x13455,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -6091,7 +6159,9 @@ static const OnigCodePoint CR_Assigned[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -6106,6 +6176,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x1d100, 0x1d126,
0x1d129, 0x1d1ea,
0x1d200, 0x1d245,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d300, 0x1d356,
0x1d360, 0x1d378,
@@ -6133,11 +6204,14 @@ static const OnigCodePoint CR_Assigned[] = {
0x1da9b, 0x1da9f,
0x1daa1, 0x1daaf,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e130, 0x1e13d,
0x1e140, 0x1e149,
@@ -6145,6 +6219,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x1e290, 0x1e2ae,
0x1e2c0, 0x1e2f9,
0x1e2ff, 0x1e2ff,
+ 0x1e4d0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -6203,10 +6278,10 @@ static const OnigCodePoint CR_Assigned[] = {
0x1f250, 0x1f251,
0x1f260, 0x1f265,
0x1f300, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -6217,25 +6292,24 @@ static const OnigCodePoint CR_Assigned[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
0xe0001, 0xe0001,
0xe0020, 0xe007f,
0xe0100, 0xe01ef,
@@ -6245,7 +6319,7 @@ static const OnigCodePoint CR_Assigned[] = {
/* 'C': Major Category */
static const OnigCodePoint CR_C[] = {
- 701,
+ 712,
0x0000, 0x001f,
0x007f, 0x009f,
0x00ad, 0x00ad,
@@ -6372,7 +6446,7 @@ static const OnigCodePoint CR_C[] = {
0x0cdf, 0x0cdf,
0x0ce4, 0x0ce5,
0x0cf0, 0x0cf0,
- 0x0cf3, 0x0cff,
+ 0x0cf4, 0x0cff,
0x0d0d, 0x0d0d,
0x0d11, 0x0d11,
0x0d45, 0x0d45,
@@ -6402,7 +6476,7 @@ static const OnigCodePoint CR_C[] = {
0x0ebe, 0x0ebf,
0x0ec5, 0x0ec5,
0x0ec7, 0x0ec7,
- 0x0ece, 0x0ecf,
+ 0x0ecf, 0x0ecf,
0x0eda, 0x0edb,
0x0ee0, 0x0eff,
0x0f48, 0x0f48,
@@ -6674,7 +6748,7 @@ static const OnigCodePoint CR_C[] = {
0x10e7f, 0x10e7f,
0x10eaa, 0x10eaa,
0x10eae, 0x10eaf,
- 0x10eb2, 0x10eff,
+ 0x10eb2, 0x10efc,
0x10f28, 0x10f2f,
0x10f5a, 0x10f6f,
0x10f8a, 0x10faf,
@@ -6692,7 +6766,7 @@ static const OnigCodePoint CR_C[] = {
0x111e0, 0x111e0,
0x111f5, 0x111ff,
0x11212, 0x11212,
- 0x1123f, 0x1127f,
+ 0x11242, 0x1127f,
0x11287, 0x11287,
0x11289, 0x11289,
0x1128e, 0x1128e,
@@ -6744,7 +6818,8 @@ static const OnigCodePoint CR_C[] = {
0x119e5, 0x119ff,
0x11a48, 0x11a4f,
0x11aa3, 0x11aaf,
- 0x11af9, 0x11bff,
+ 0x11af9, 0x11aff,
+ 0x11b0a, 0x11bff,
0x11c09, 0x11c09,
0x11c37, 0x11c37,
0x11c46, 0x11c4f,
@@ -6765,7 +6840,10 @@ static const OnigCodePoint CR_C[] = {
0x11d92, 0x11d92,
0x11d99, 0x11d9f,
0x11daa, 0x11edf,
- 0x11ef9, 0x11faf,
+ 0x11ef9, 0x11eff,
+ 0x11f11, 0x11f11,
+ 0x11f3b, 0x11f3d,
+ 0x11f5a, 0x11faf,
0x11fb1, 0x11fbf,
0x11ff2, 0x11ffe,
0x1239a, 0x123ff,
@@ -6773,7 +6851,8 @@ static const OnigCodePoint CR_C[] = {
0x12475, 0x1247f,
0x12544, 0x12f8f,
0x12ff3, 0x12fff,
- 0x1342f, 0x143ff,
+ 0x13430, 0x1343f,
+ 0x13456, 0x143ff,
0x14647, 0x167ff,
0x16a39, 0x16a3f,
0x16a5f, 0x16a5f,
@@ -6799,8 +6878,10 @@ static const OnigCodePoint CR_C[] = {
0x1aff4, 0x1aff4,
0x1affc, 0x1affc,
0x1afff, 0x1afff,
- 0x1b123, 0x1b14f,
- 0x1b153, 0x1b163,
+ 0x1b123, 0x1b131,
+ 0x1b133, 0x1b14f,
+ 0x1b153, 0x1b154,
+ 0x1b156, 0x1b163,
0x1b168, 0x1b16f,
0x1b2fc, 0x1bbff,
0x1bc6b, 0x1bc6f,
@@ -6815,7 +6896,8 @@ static const OnigCodePoint CR_C[] = {
0x1d127, 0x1d128,
0x1d173, 0x1d17a,
0x1d1eb, 0x1d1ff,
- 0x1d246, 0x1d2df,
+ 0x1d246, 0x1d2bf,
+ 0x1d2d4, 0x1d2df,
0x1d2f4, 0x1d2ff,
0x1d357, 0x1d35f,
0x1d379, 0x1d3ff,
@@ -6842,19 +6924,23 @@ static const OnigCodePoint CR_C[] = {
0x1da8c, 0x1da9a,
0x1daa0, 0x1daa0,
0x1dab0, 0x1deff,
- 0x1df1f, 0x1dfff,
+ 0x1df1f, 0x1df24,
+ 0x1df2b, 0x1dfff,
0x1e007, 0x1e007,
0x1e019, 0x1e01a,
0x1e022, 0x1e022,
0x1e025, 0x1e025,
- 0x1e02b, 0x1e0ff,
+ 0x1e02b, 0x1e02f,
+ 0x1e06e, 0x1e08e,
+ 0x1e090, 0x1e0ff,
0x1e12d, 0x1e12f,
0x1e13e, 0x1e13f,
0x1e14a, 0x1e14d,
0x1e150, 0x1e28f,
0x1e2af, 0x1e2bf,
0x1e2fa, 0x1e2fe,
- 0x1e300, 0x1e7df,
+ 0x1e300, 0x1e4cf,
+ 0x1e4fa, 0x1e7df,
0x1e7e7, 0x1e7e7,
0x1e7ec, 0x1e7ec,
0x1e7ef, 0x1e7ef,
@@ -6912,11 +6998,11 @@ static const OnigCodePoint CR_C[] = {
0x1f249, 0x1f24f,
0x1f252, 0x1f25f,
0x1f266, 0x1f2ff,
- 0x1f6d8, 0x1f6dc,
+ 0x1f6d8, 0x1f6db,
0x1f6ed, 0x1f6ef,
0x1f6fd, 0x1f6ff,
- 0x1f774, 0x1f77f,
- 0x1f7d9, 0x1f7df,
+ 0x1f777, 0x1f77a,
+ 0x1f7da, 0x1f7df,
0x1f7ec, 0x1f7ef,
0x1f7f1, 0x1f7ff,
0x1f80c, 0x1f80f,
@@ -6927,25 +7013,24 @@ static const OnigCodePoint CR_C[] = {
0x1f8b2, 0x1f8ff,
0x1fa54, 0x1fa5f,
0x1fa6e, 0x1fa6f,
- 0x1fa75, 0x1fa77,
0x1fa7d, 0x1fa7f,
- 0x1fa87, 0x1fa8f,
- 0x1faad, 0x1faaf,
- 0x1fabb, 0x1fabf,
- 0x1fac6, 0x1facf,
- 0x1fada, 0x1fadf,
- 0x1fae8, 0x1faef,
- 0x1faf7, 0x1faff,
+ 0x1fa89, 0x1fa8f,
+ 0x1fabe, 0x1fabe,
+ 0x1fac6, 0x1facd,
+ 0x1fadc, 0x1fadf,
+ 0x1fae9, 0x1faef,
+ 0x1faf9, 0x1faff,
0x1fb93, 0x1fb93,
0x1fbcb, 0x1fbef,
0x1fbfa, 0x1ffff,
0x2a6e0, 0x2a6ff,
- 0x2b739, 0x2b73f,
+ 0x2b73a, 0x2b73f,
0x2b81e, 0x2b81f,
0x2cea2, 0x2ceaf,
0x2ebe1, 0x2f7ff,
0x2fa1e, 0x2ffff,
- 0x3134b, 0xe00ff,
+ 0x3134b, 0x3134f,
+ 0x323b0, 0xe00ff,
0xe01f0, 0x10ffff,
}; /* CR_C */
@@ -6971,7 +7056,7 @@ static const OnigCodePoint CR_Cf[] = {
0xfff9, 0xfffb,
0x110bd, 0x110bd,
0x110cd, 0x110cd,
- 0x13430, 0x13438,
+ 0x13430, 0x1343f,
0x1bca0, 0x1bca3,
0x1d173, 0x1d17a,
0xe0001, 0xe0001,
@@ -6980,7 +7065,7 @@ static const OnigCodePoint CR_Cf[] = {
/* 'Cn': General Category */
static const OnigCodePoint CR_Cn[] = {
- 698,
+ 707,
0x0378, 0x0379,
0x0380, 0x0383,
0x038b, 0x038b,
@@ -7102,7 +7187,7 @@ static const OnigCodePoint CR_Cn[] = {
0x0cdf, 0x0cdf,
0x0ce4, 0x0ce5,
0x0cf0, 0x0cf0,
- 0x0cf3, 0x0cff,
+ 0x0cf4, 0x0cff,
0x0d0d, 0x0d0d,
0x0d11, 0x0d11,
0x0d45, 0x0d45,
@@ -7132,7 +7217,7 @@ static const OnigCodePoint CR_Cn[] = {
0x0ebe, 0x0ebf,
0x0ec5, 0x0ec5,
0x0ec7, 0x0ec7,
- 0x0ece, 0x0ecf,
+ 0x0ecf, 0x0ecf,
0x0eda, 0x0edb,
0x0ee0, 0x0eff,
0x0f48, 0x0f48,
@@ -7402,7 +7487,7 @@ static const OnigCodePoint CR_Cn[] = {
0x10e7f, 0x10e7f,
0x10eaa, 0x10eaa,
0x10eae, 0x10eaf,
- 0x10eb2, 0x10eff,
+ 0x10eb2, 0x10efc,
0x10f28, 0x10f2f,
0x10f5a, 0x10f6f,
0x10f8a, 0x10faf,
@@ -7420,7 +7505,7 @@ static const OnigCodePoint CR_Cn[] = {
0x111e0, 0x111e0,
0x111f5, 0x111ff,
0x11212, 0x11212,
- 0x1123f, 0x1127f,
+ 0x11242, 0x1127f,
0x11287, 0x11287,
0x11289, 0x11289,
0x1128e, 0x1128e,
@@ -7472,7 +7557,8 @@ static const OnigCodePoint CR_Cn[] = {
0x119e5, 0x119ff,
0x11a48, 0x11a4f,
0x11aa3, 0x11aaf,
- 0x11af9, 0x11bff,
+ 0x11af9, 0x11aff,
+ 0x11b0a, 0x11bff,
0x11c09, 0x11c09,
0x11c37, 0x11c37,
0x11c46, 0x11c4f,
@@ -7493,7 +7579,10 @@ static const OnigCodePoint CR_Cn[] = {
0x11d92, 0x11d92,
0x11d99, 0x11d9f,
0x11daa, 0x11edf,
- 0x11ef9, 0x11faf,
+ 0x11ef9, 0x11eff,
+ 0x11f11, 0x11f11,
+ 0x11f3b, 0x11f3d,
+ 0x11f5a, 0x11faf,
0x11fb1, 0x11fbf,
0x11ff2, 0x11ffe,
0x1239a, 0x123ff,
@@ -7501,8 +7590,7 @@ static const OnigCodePoint CR_Cn[] = {
0x12475, 0x1247f,
0x12544, 0x12f8f,
0x12ff3, 0x12fff,
- 0x1342f, 0x1342f,
- 0x13439, 0x143ff,
+ 0x13456, 0x143ff,
0x14647, 0x167ff,
0x16a39, 0x16a3f,
0x16a5f, 0x16a5f,
@@ -7528,8 +7616,10 @@ static const OnigCodePoint CR_Cn[] = {
0x1aff4, 0x1aff4,
0x1affc, 0x1affc,
0x1afff, 0x1afff,
- 0x1b123, 0x1b14f,
- 0x1b153, 0x1b163,
+ 0x1b123, 0x1b131,
+ 0x1b133, 0x1b14f,
+ 0x1b153, 0x1b154,
+ 0x1b156, 0x1b163,
0x1b168, 0x1b16f,
0x1b2fc, 0x1bbff,
0x1bc6b, 0x1bc6f,
@@ -7543,7 +7633,8 @@ static const OnigCodePoint CR_Cn[] = {
0x1d0f6, 0x1d0ff,
0x1d127, 0x1d128,
0x1d1eb, 0x1d1ff,
- 0x1d246, 0x1d2df,
+ 0x1d246, 0x1d2bf,
+ 0x1d2d4, 0x1d2df,
0x1d2f4, 0x1d2ff,
0x1d357, 0x1d35f,
0x1d379, 0x1d3ff,
@@ -7570,19 +7661,23 @@ static const OnigCodePoint CR_Cn[] = {
0x1da8c, 0x1da9a,
0x1daa0, 0x1daa0,
0x1dab0, 0x1deff,
- 0x1df1f, 0x1dfff,
+ 0x1df1f, 0x1df24,
+ 0x1df2b, 0x1dfff,
0x1e007, 0x1e007,
0x1e019, 0x1e01a,
0x1e022, 0x1e022,
0x1e025, 0x1e025,
- 0x1e02b, 0x1e0ff,
+ 0x1e02b, 0x1e02f,
+ 0x1e06e, 0x1e08e,
+ 0x1e090, 0x1e0ff,
0x1e12d, 0x1e12f,
0x1e13e, 0x1e13f,
0x1e14a, 0x1e14d,
0x1e150, 0x1e28f,
0x1e2af, 0x1e2bf,
0x1e2fa, 0x1e2fe,
- 0x1e300, 0x1e7df,
+ 0x1e300, 0x1e4cf,
+ 0x1e4fa, 0x1e7df,
0x1e7e7, 0x1e7e7,
0x1e7ec, 0x1e7ec,
0x1e7ef, 0x1e7ef,
@@ -7640,11 +7735,11 @@ static const OnigCodePoint CR_Cn[] = {
0x1f249, 0x1f24f,
0x1f252, 0x1f25f,
0x1f266, 0x1f2ff,
- 0x1f6d8, 0x1f6dc,
+ 0x1f6d8, 0x1f6db,
0x1f6ed, 0x1f6ef,
0x1f6fd, 0x1f6ff,
- 0x1f774, 0x1f77f,
- 0x1f7d9, 0x1f7df,
+ 0x1f777, 0x1f77a,
+ 0x1f7da, 0x1f7df,
0x1f7ec, 0x1f7ef,
0x1f7f1, 0x1f7ff,
0x1f80c, 0x1f80f,
@@ -7655,25 +7750,24 @@ static const OnigCodePoint CR_Cn[] = {
0x1f8b2, 0x1f8ff,
0x1fa54, 0x1fa5f,
0x1fa6e, 0x1fa6f,
- 0x1fa75, 0x1fa77,
0x1fa7d, 0x1fa7f,
- 0x1fa87, 0x1fa8f,
- 0x1faad, 0x1faaf,
- 0x1fabb, 0x1fabf,
- 0x1fac6, 0x1facf,
- 0x1fada, 0x1fadf,
- 0x1fae8, 0x1faef,
- 0x1faf7, 0x1faff,
+ 0x1fa89, 0x1fa8f,
+ 0x1fabe, 0x1fabe,
+ 0x1fac6, 0x1facd,
+ 0x1fadc, 0x1fadf,
+ 0x1fae9, 0x1faef,
+ 0x1faf9, 0x1faff,
0x1fb93, 0x1fb93,
0x1fbcb, 0x1fbef,
0x1fbfa, 0x1ffff,
0x2a6e0, 0x2a6ff,
- 0x2b739, 0x2b73f,
+ 0x2b73a, 0x2b73f,
0x2b81e, 0x2b81f,
0x2cea2, 0x2ceaf,
0x2ebe1, 0x2f7ff,
0x2fa1e, 0x2ffff,
- 0x3134b, 0xe0000,
+ 0x3134b, 0x3134f,
+ 0x323b0, 0xe0000,
0xe0002, 0xe001f,
0xe0080, 0xe00ff,
0xe01f0, 0xeffff,
@@ -7697,7 +7791,7 @@ static const OnigCodePoint CR_Cs[] = {
/* 'L': Major Category */
static const OnigCodePoint CR_L[] = {
- 648,
+ 659,
0x0041, 0x005a,
0x0061, 0x007a,
0x00aa, 0x00aa,
@@ -8167,6 +8261,7 @@ static const OnigCodePoint CR_L[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x1122b,
+ 0x1123f, 0x11240,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -8229,11 +8324,15 @@ static const OnigCodePoint CR_L[] = {
0x11d6a, 0x11d89,
0x11d98, 0x11d98,
0x11ee0, 0x11ef2,
+ 0x11f02, 0x11f02,
+ 0x11f04, 0x11f10,
+ 0x11f12, 0x11f33,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -8256,7 +8355,9 @@ static const OnigCodePoint CR_L[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -8294,11 +8395,14 @@ static const OnigCodePoint CR_L[] = {
0x1d7aa, 0x1d7c2,
0x1d7c4, 0x1d7cb,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e030, 0x1e06d,
0x1e100, 0x1e12c,
0x1e137, 0x1e13d,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ad,
0x1e2c0, 0x1e2eb,
+ 0x1e4d0, 0x1e4eb,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -8340,17 +8444,18 @@ static const OnigCodePoint CR_L[] = {
0x1eea5, 0x1eea9,
0x1eeab, 0x1eebb,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_L */
/* 'LC': General Category */
static const OnigCodePoint CR_LC[] = {
- 142,
+ 143,
0x0041, 0x005a,
0x0061, 0x007a,
0x00b5, 0x00b5,
@@ -8492,12 +8597,13 @@ static const OnigCodePoint CR_LC[] = {
0x1d7c4, 0x1d7cb,
0x1df00, 0x1df09,
0x1df0b, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e900, 0x1e943,
}; /* CR_LC */
/* 'Ll': General Category */
static const OnigCodePoint CR_Ll[] = {
- 657,
+ 658,
0x0061, 0x007a,
0x00b5, 0x00b5,
0x00df, 0x00f6,
@@ -9154,12 +9260,13 @@ static const OnigCodePoint CR_Ll[] = {
0x1d7cb, 0x1d7cb,
0x1df00, 0x1df09,
0x1df0b, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e922, 0x1e943,
}; /* CR_Ll */
/* 'Lm': General Category */
static const OnigCodePoint CR_Lm[] = {
- 69,
+ 71,
0x02b0, 0x02c1,
0x02c6, 0x02d1,
0x02e0, 0x02e4,
@@ -9227,13 +9334,15 @@ static const OnigCodePoint CR_Lm[] = {
0x1aff0, 0x1aff3,
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
+ 0x1e030, 0x1e06d,
0x1e137, 0x1e13d,
+ 0x1e4eb, 0x1e4eb,
0x1e94b, 0x1e94b,
}; /* CR_Lm */
/* 'Lo': General Category */
static const OnigCodePoint CR_Lo[] = {
- 501,
+ 510,
0x00aa, 0x00aa,
0x00ba, 0x00ba,
0x01bb, 0x01bb,
@@ -9598,6 +9707,7 @@ static const OnigCodePoint CR_Lo[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x1122b,
+ 0x1123f, 0x11240,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -9659,11 +9769,15 @@ static const OnigCodePoint CR_Lo[] = {
0x11d6a, 0x11d89,
0x11d98, 0x11d98,
0x11ee0, 0x11ef2,
+ 0x11f02, 0x11f02,
+ 0x11f04, 0x11f10,
+ 0x11f12, 0x11f33,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -9678,7 +9792,9 @@ static const OnigCodePoint CR_Lo[] = {
0x18800, 0x18cd5,
0x18d00, 0x18d08,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -9690,6 +9806,7 @@ static const OnigCodePoint CR_Lo[] = {
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ad,
0x1e2c0, 0x1e2eb,
+ 0x1e4d0, 0x1e4ea,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -9729,12 +9846,13 @@ static const OnigCodePoint CR_Lo[] = {
0x1eea5, 0x1eea9,
0x1eeab, 0x1eebb,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Lo */
/* 'Lt': General Category */
@@ -10405,7 +10523,7 @@ static const OnigCodePoint CR_Lu[] = {
/* 'M': Major Category */
static const OnigCodePoint CR_M[] = {
- 299,
+ 310,
0x0300, 0x036f,
0x0483, 0x0489,
0x0591, 0x05bd,
@@ -10486,6 +10604,7 @@ static const OnigCodePoint CR_M[] = {
0x0cca, 0x0ccd,
0x0cd5, 0x0cd6,
0x0ce2, 0x0ce3,
+ 0x0cf3, 0x0cf3,
0x0d00, 0x0d03,
0x0d3b, 0x0d3c,
0x0d3e, 0x0d44,
@@ -10504,7 +10623,7 @@ static const OnigCodePoint CR_M[] = {
0x0e47, 0x0e4e,
0x0eb1, 0x0eb1,
0x0eb4, 0x0ebc,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0f18, 0x0f19,
0x0f35, 0x0f35,
0x0f37, 0x0f37,
@@ -10606,6 +10725,7 @@ static const OnigCodePoint CR_M[] = {
0x10ae5, 0x10ae6,
0x10d24, 0x10d27,
0x10eab, 0x10eac,
+ 0x10efd, 0x10eff,
0x10f46, 0x10f50,
0x10f82, 0x10f85,
0x11000, 0x11002,
@@ -10625,6 +10745,7 @@ static const OnigCodePoint CR_M[] = {
0x111ce, 0x111cf,
0x1122c, 0x11237,
0x1123e, 0x1123e,
+ 0x11241, 0x11241,
0x112df, 0x112ea,
0x11300, 0x11303,
0x1133b, 0x1133c,
@@ -10672,6 +10793,12 @@ static const OnigCodePoint CR_M[] = {
0x11d90, 0x11d91,
0x11d93, 0x11d97,
0x11ef3, 0x11ef6,
+ 0x11f00, 0x11f01,
+ 0x11f03, 0x11f03,
+ 0x11f34, 0x11f3a,
+ 0x11f3e, 0x11f42,
+ 0x13440, 0x13440,
+ 0x13447, 0x13455,
0x16af0, 0x16af4,
0x16b30, 0x16b36,
0x16f4f, 0x16f4f,
@@ -10699,9 +10826,11 @@ static const OnigCodePoint CR_M[] = {
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e08f, 0x1e08f,
0x1e130, 0x1e136,
0x1e2ae, 0x1e2ae,
0x1e2ec, 0x1e2ef,
+ 0x1e4ec, 0x1e4ef,
0x1e8d0, 0x1e8d6,
0x1e944, 0x1e94a,
0xe0100, 0xe01ef,
@@ -10709,7 +10838,7 @@ static const OnigCodePoint CR_M[] = {
/* 'Mc': General Category */
static const OnigCodePoint CR_Mc[] = {
- 177,
+ 182,
0x0903, 0x0903,
0x093b, 0x093b,
0x093e, 0x0940,
@@ -10745,6 +10874,7 @@ static const OnigCodePoint CR_Mc[] = {
0x0cc7, 0x0cc8,
0x0cca, 0x0ccb,
0x0cd5, 0x0cd6,
+ 0x0cf3, 0x0cf3,
0x0d02, 0x0d03,
0x0d3e, 0x0d40,
0x0d46, 0x0d48,
@@ -10883,6 +11013,10 @@ static const OnigCodePoint CR_Mc[] = {
0x11d93, 0x11d94,
0x11d96, 0x11d96,
0x11ef5, 0x11ef6,
+ 0x11f03, 0x11f03,
+ 0x11f34, 0x11f35,
+ 0x11f3e, 0x11f3f,
+ 0x11f41, 0x11f41,
0x16f51, 0x16f87,
0x16ff0, 0x16ff1,
0x1d165, 0x1d166,
@@ -10901,7 +11035,7 @@ static const OnigCodePoint CR_Me[] = {
/* 'Mn': General Category */
static const OnigCodePoint CR_Mn[] = {
- 336,
+ 346,
0x0300, 0x036f,
0x0483, 0x0487,
0x0591, 0x05bd,
@@ -10994,7 +11128,7 @@ static const OnigCodePoint CR_Mn[] = {
0x0e47, 0x0e4e,
0x0eb1, 0x0eb1,
0x0eb4, 0x0ebc,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0f18, 0x0f19,
0x0f35, 0x0f35,
0x0f37, 0x0f37,
@@ -11125,6 +11259,7 @@ static const OnigCodePoint CR_Mn[] = {
0x10ae5, 0x10ae6,
0x10d24, 0x10d27,
0x10eab, 0x10eac,
+ 0x10efd, 0x10eff,
0x10f46, 0x10f50,
0x10f82, 0x10f85,
0x11001, 0x11001,
@@ -11147,6 +11282,7 @@ static const OnigCodePoint CR_Mn[] = {
0x11234, 0x11234,
0x11236, 0x11237,
0x1123e, 0x1123e,
+ 0x11241, 0x11241,
0x112df, 0x112df,
0x112e3, 0x112ea,
0x11300, 0x11301,
@@ -11208,6 +11344,12 @@ static const OnigCodePoint CR_Mn[] = {
0x11d95, 0x11d95,
0x11d97, 0x11d97,
0x11ef3, 0x11ef4,
+ 0x11f00, 0x11f01,
+ 0x11f36, 0x11f3a,
+ 0x11f40, 0x11f40,
+ 0x11f42, 0x11f42,
+ 0x13440, 0x13440,
+ 0x13447, 0x13455,
0x16af0, 0x16af4,
0x16b30, 0x16b36,
0x16f4f, 0x16f4f,
@@ -11232,9 +11374,11 @@ static const OnigCodePoint CR_Mn[] = {
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e08f, 0x1e08f,
0x1e130, 0x1e136,
0x1e2ae, 0x1e2ae,
0x1e2ec, 0x1e2ef,
+ 0x1e4ec, 0x1e4ef,
0x1e8d0, 0x1e8d6,
0x1e944, 0x1e94a,
0xe0100, 0xe01ef,
@@ -11242,7 +11386,7 @@ static const OnigCodePoint CR_Mn[] = {
/* 'N': Major Category */
static const OnigCodePoint CR_N[] = {
- 134,
+ 137,
0x0030, 0x0039,
0x00b2, 0x00b3,
0x00b9, 0x00b9,
@@ -11356,6 +11500,7 @@ static const OnigCodePoint CR_N[] = {
0x11c50, 0x11c6c,
0x11d50, 0x11d59,
0x11da0, 0x11da9,
+ 0x11f50, 0x11f59,
0x11fc0, 0x11fd4,
0x12400, 0x1246e,
0x16a60, 0x16a69,
@@ -11363,11 +11508,13 @@ static const OnigCodePoint CR_N[] = {
0x16b50, 0x16b59,
0x16b5b, 0x16b61,
0x16e80, 0x16e96,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d360, 0x1d378,
0x1d7ce, 0x1d7ff,
0x1e140, 0x1e149,
0x1e2f0, 0x1e2f9,
+ 0x1e4f0, 0x1e4f9,
0x1e8c7, 0x1e8cf,
0x1e950, 0x1e959,
0x1ec71, 0x1ecab,
@@ -11401,7 +11548,7 @@ static const OnigCodePoint CR_Nl[] = {
/* 'No': General Category */
static const OnigCodePoint CR_No[] = {
- 71,
+ 72,
0x00b2, 0x00b3,
0x00b9, 0x00b9,
0x00bc, 0x00be,
@@ -11464,6 +11611,7 @@ static const OnigCodePoint CR_No[] = {
0x11fc0, 0x11fd4,
0x16b5b, 0x16b61,
0x16e80, 0x16e96,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d360, 0x1d378,
0x1e8c7, 0x1e8cf,
@@ -11627,7 +11775,7 @@ static const OnigCodePoint CR_Pi[] = {
/* 'Po': General Category */
static const OnigCodePoint CR_Po[] = {
- 185,
+ 187,
0x0021, 0x0023,
0x0025, 0x0027,
0x002a, 0x002a,
@@ -11798,9 +11946,11 @@ static const OnigCodePoint CR_Po[] = {
0x11a3f, 0x11a46,
0x11a9a, 0x11a9c,
0x11a9e, 0x11aa2,
+ 0x11b00, 0x11b09,
0x11c41, 0x11c45,
0x11c70, 0x11c71,
0x11ef7, 0x11ef8,
+ 0x11f43, 0x11f4f,
0x11fff, 0x11fff,
0x12470, 0x12474,
0x12ff1, 0x12ff2,
@@ -11901,7 +12051,7 @@ static const OnigCodePoint CR_Ps[] = {
/* 'S': Major Category */
static const OnigCodePoint CR_S[] = {
- 234,
+ 232,
0x0024, 0x0024,
0x002b, 0x002b,
0x003c, 0x003e,
@@ -12111,10 +12261,10 @@ static const OnigCodePoint CR_S[] = {
0x1f250, 0x1f251,
0x1f260, 0x1f265,
0x1f300, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -12125,15 +12275,13 @@ static const OnigCodePoint CR_S[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
}; /* CR_S */
@@ -12271,7 +12419,7 @@ static const OnigCodePoint CR_Sm[] = {
/* 'So': General Category */
static const OnigCodePoint CR_So[] = {
- 186,
+ 184,
0x00a6, 0x00a6,
0x00a9, 0x00a9,
0x00ae, 0x00ae,
@@ -12433,10 +12581,10 @@ static const OnigCodePoint CR_So[] = {
0x1f260, 0x1f265,
0x1f300, 0x1f3fa,
0x1f400, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -12447,15 +12595,13 @@ static const OnigCodePoint CR_So[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
}; /* CR_So */
@@ -12651,7 +12797,7 @@ static const OnigCodePoint CR_Math[] = {
/* 'Cased': Derived Property */
static const OnigCodePoint CR_Cased[] = {
- 151,
+ 157,
0x0041, 0x005a,
0x0061, 0x007a,
0x00aa, 0x00aa,
@@ -12683,7 +12829,7 @@ static const OnigCodePoint CR_Cased[] = {
0x10c7, 0x10c7,
0x10cd, 0x10cd,
0x10d0, 0x10fa,
- 0x10fd, 0x10ff,
+ 0x10fc, 0x10ff,
0x13a0, 0x13f5,
0x13f8, 0x13fd,
0x1c80, 0x1c88,
@@ -12743,10 +12889,10 @@ static const OnigCodePoint CR_Cased[] = {
0xa7d0, 0xa7d1,
0xa7d3, 0xa7d3,
0xa7d5, 0xa7d9,
- 0xa7f5, 0xa7f6,
+ 0xa7f2, 0xa7f6,
0xa7f8, 0xa7fa,
0xab30, 0xab5a,
- 0xab5c, 0xab68,
+ 0xab5c, 0xab69,
0xab70, 0xabbf,
0xfb00, 0xfb06,
0xfb13, 0xfb17,
@@ -12763,6 +12909,10 @@ static const OnigCodePoint CR_Cased[] = {
0x105a3, 0x105b1,
0x105b3, 0x105b9,
0x105bb, 0x105bc,
+ 0x10780, 0x10780,
+ 0x10783, 0x10785,
+ 0x10787, 0x107b0,
+ 0x107b2, 0x107ba,
0x10c80, 0x10cb2,
0x10cc0, 0x10cf2,
0x118a0, 0x118df,
@@ -12799,6 +12949,8 @@ static const OnigCodePoint CR_Cased[] = {
0x1d7c4, 0x1d7cb,
0x1df00, 0x1df09,
0x1df0b, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e030, 0x1e06d,
0x1e900, 0x1e943,
0x1f130, 0x1f149,
0x1f150, 0x1f169,
@@ -12807,7 +12959,7 @@ static const OnigCodePoint CR_Cased[] = {
/* 'Case_Ignorable': Derived Property */
static const OnigCodePoint CR_Case_Ignorable[] = {
- 427,
+ 437,
0x0027, 0x0027,
0x002e, 0x002e,
0x003a, 0x003a,
@@ -12921,7 +13073,7 @@ static const OnigCodePoint CR_Case_Ignorable[] = {
0x0eb1, 0x0eb1,
0x0eb4, 0x0ebc,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0f18, 0x0f19,
0x0f35, 0x0f35,
0x0f37, 0x0f37,
@@ -13110,6 +13262,7 @@ static const OnigCodePoint CR_Case_Ignorable[] = {
0x10ae5, 0x10ae6,
0x10d24, 0x10d27,
0x10eab, 0x10eac,
+ 0x10efd, 0x10eff,
0x10f46, 0x10f50,
0x10f82, 0x10f85,
0x11001, 0x11001,
@@ -13134,6 +13287,7 @@ static const OnigCodePoint CR_Case_Ignorable[] = {
0x11234, 0x11234,
0x11236, 0x11237,
0x1123e, 0x1123e,
+ 0x11241, 0x11241,
0x112df, 0x112df,
0x112e3, 0x112ea,
0x11300, 0x11301,
@@ -13195,7 +13349,12 @@ static const OnigCodePoint CR_Case_Ignorable[] = {
0x11d95, 0x11d95,
0x11d97, 0x11d97,
0x11ef3, 0x11ef4,
- 0x13430, 0x13438,
+ 0x11f00, 0x11f01,
+ 0x11f36, 0x11f3a,
+ 0x11f40, 0x11f40,
+ 0x11f42, 0x11f42,
+ 0x13430, 0x13440,
+ 0x13447, 0x13455,
0x16af0, 0x16af4,
0x16b30, 0x16b36,
0x16b40, 0x16b43,
@@ -13226,9 +13385,12 @@ static const OnigCodePoint CR_Case_Ignorable[] = {
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e130, 0x1e13d,
0x1e2ae, 0x1e2ae,
0x1e2ec, 0x1e2ef,
+ 0x1e4eb, 0x1e4ef,
0x1e8d0, 0x1e8d6,
0x1e944, 0x1e94b,
0x1f3fb, 0x1f3ff,
@@ -15879,7 +16041,7 @@ static const OnigCodePoint CR_Changes_When_Casemapped[] = {
/* 'ID_Start': Derived Property */
static const OnigCodePoint CR_ID_Start[] = {
- 648,
+ 659,
0x0041, 0x005a,
0x0061, 0x007a,
0x00aa, 0x00aa,
@@ -16348,6 +16510,7 @@ static const OnigCodePoint CR_ID_Start[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x1122b,
+ 0x1123f, 0x11240,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -16410,12 +16573,16 @@ static const OnigCodePoint CR_ID_Start[] = {
0x11d6a, 0x11d89,
0x11d98, 0x11d98,
0x11ee0, 0x11ef2,
+ 0x11f02, 0x11f02,
+ 0x11f04, 0x11f10,
+ 0x11f12, 0x11f33,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -16438,7 +16605,9 @@ static const OnigCodePoint CR_ID_Start[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -16476,11 +16645,14 @@ static const OnigCodePoint CR_ID_Start[] = {
0x1d7aa, 0x1d7c2,
0x1d7c4, 0x1d7cb,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e030, 0x1e06d,
0x1e100, 0x1e12c,
0x1e137, 0x1e13d,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ad,
0x1e2c0, 0x1e2eb,
+ 0x1e4d0, 0x1e4eb,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -16522,17 +16694,18 @@ static const OnigCodePoint CR_ID_Start[] = {
0x1eea5, 0x1eea9,
0x1eeab, 0x1eebb,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_ID_Start */
/* 'ID_Continue': Derived Property */
static const OnigCodePoint CR_ID_Continue[] = {
- 756,
+ 768,
0x0030, 0x0039,
0x0041, 0x005a,
0x005f, 0x005f,
@@ -16691,7 +16864,7 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d44,
@@ -16724,7 +16897,7 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
0x0f00, 0x0f00,
@@ -17037,7 +17210,7 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x10e80, 0x10ea9,
0x10eab, 0x10eac,
0x10eb0, 0x10eb1,
- 0x10f00, 0x10f1c,
+ 0x10efd, 0x10f1c,
0x10f27, 0x10f27,
0x10f30, 0x10f50,
0x10f70, 0x10f85,
@@ -17060,7 +17233,7 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x11237,
- 0x1123e, 0x1123e,
+ 0x1123e, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -17141,12 +17314,17 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x11d93, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef6,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f42,
+ 0x11f50, 0x11f59,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13440, 0x13455,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -17174,7 +17352,9 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -17228,17 +17408,21 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x1da9b, 0x1da9f,
0x1daa1, 0x1daaf,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e130, 0x1e13d,
0x1e140, 0x1e149,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ae,
0x1e2c0, 0x1e2f9,
+ 0x1e4d0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -17282,18 +17466,19 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x1eeab, 0x1eebb,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
0xe0100, 0xe01ef,
}; /* CR_ID_Continue */
/* 'XID_Start': Derived Property */
static const OnigCodePoint CR_XID_Start[] = {
- 655,
+ 666,
0x0041, 0x005a,
0x0061, 0x007a,
0x00aa, 0x00aa,
@@ -17769,6 +17954,7 @@ static const OnigCodePoint CR_XID_Start[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x1122b,
+ 0x1123f, 0x11240,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -17831,12 +18017,16 @@ static const OnigCodePoint CR_XID_Start[] = {
0x11d6a, 0x11d89,
0x11d98, 0x11d98,
0x11ee0, 0x11ef2,
+ 0x11f02, 0x11f02,
+ 0x11f04, 0x11f10,
+ 0x11f12, 0x11f33,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -17859,7 +18049,9 @@ static const OnigCodePoint CR_XID_Start[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -17897,11 +18089,14 @@ static const OnigCodePoint CR_XID_Start[] = {
0x1d7aa, 0x1d7c2,
0x1d7c4, 0x1d7cb,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e030, 0x1e06d,
0x1e100, 0x1e12c,
0x1e137, 0x1e13d,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ad,
0x1e2c0, 0x1e2eb,
+ 0x1e4d0, 0x1e4eb,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -17943,17 +18138,18 @@ static const OnigCodePoint CR_XID_Start[] = {
0x1eea5, 0x1eea9,
0x1eeab, 0x1eebb,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_XID_Start */
/* 'XID_Continue': Derived Property */
static const OnigCodePoint CR_XID_Continue[] = {
- 763,
+ 775,
0x0030, 0x0039,
0x0041, 0x005a,
0x005f, 0x005f,
@@ -18112,7 +18308,7 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d44,
@@ -18145,7 +18341,7 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
0x0f00, 0x0f00,
@@ -18465,7 +18661,7 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x10e80, 0x10ea9,
0x10eab, 0x10eac,
0x10eb0, 0x10eb1,
- 0x10f00, 0x10f1c,
+ 0x10efd, 0x10f1c,
0x10f27, 0x10f27,
0x10f30, 0x10f50,
0x10f70, 0x10f85,
@@ -18488,7 +18684,7 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x11237,
- 0x1123e, 0x1123e,
+ 0x1123e, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -18569,12 +18765,17 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x11d93, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef6,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f42,
+ 0x11f50, 0x11f59,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13440, 0x13455,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -18602,7 +18803,9 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -18656,17 +18859,21 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x1da9b, 0x1da9f,
0x1daa1, 0x1daaf,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e130, 0x1e13d,
0x1e140, 0x1e149,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ae,
0x1e2c0, 0x1e2f9,
+ 0x1e4d0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -18710,12 +18917,13 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x1eeab, 0x1eebb,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
0xe0100, 0xe01ef,
}; /* CR_XID_Continue */
@@ -18743,7 +18951,7 @@ static const OnigCodePoint CR_Default_Ignorable_Code_Point[] = {
/* 'Grapheme_Extend': Derived Property */
static const OnigCodePoint CR_Grapheme_Extend[] = {
- 353,
+ 363,
0x0300, 0x036f,
0x0483, 0x0489,
0x0591, 0x05bd,
@@ -18846,7 +19054,7 @@ static const OnigCodePoint CR_Grapheme_Extend[] = {
0x0e47, 0x0e4e,
0x0eb1, 0x0eb1,
0x0eb4, 0x0ebc,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0f18, 0x0f19,
0x0f35, 0x0f35,
0x0f37, 0x0f37,
@@ -18975,6 +19183,7 @@ static const OnigCodePoint CR_Grapheme_Extend[] = {
0x10ae5, 0x10ae6,
0x10d24, 0x10d27,
0x10eab, 0x10eac,
+ 0x10efd, 0x10eff,
0x10f46, 0x10f50,
0x10f82, 0x10f85,
0x11001, 0x11001,
@@ -18997,6 +19206,7 @@ static const OnigCodePoint CR_Grapheme_Extend[] = {
0x11234, 0x11234,
0x11236, 0x11237,
0x1123e, 0x1123e,
+ 0x11241, 0x11241,
0x112df, 0x112df,
0x112e3, 0x112ea,
0x11300, 0x11301,
@@ -19064,6 +19274,12 @@ static const OnigCodePoint CR_Grapheme_Extend[] = {
0x11d95, 0x11d95,
0x11d97, 0x11d97,
0x11ef3, 0x11ef4,
+ 0x11f00, 0x11f01,
+ 0x11f36, 0x11f3a,
+ 0x11f40, 0x11f40,
+ 0x11f42, 0x11f42,
+ 0x13440, 0x13440,
+ 0x13447, 0x13455,
0x16af0, 0x16af4,
0x16b30, 0x16b36,
0x16f4f, 0x16f4f,
@@ -19090,9 +19306,11 @@ static const OnigCodePoint CR_Grapheme_Extend[] = {
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e08f, 0x1e08f,
0x1e130, 0x1e136,
0x1e2ae, 0x1e2ae,
0x1e2ec, 0x1e2ef,
+ 0x1e4ec, 0x1e4ef,
0x1e8d0, 0x1e8d6,
0x1e944, 0x1e94a,
0xe0020, 0xe007f,
@@ -19101,7 +19319,7 @@ static const OnigCodePoint CR_Grapheme_Extend[] = {
/* 'Grapheme_Base': Derived Property */
static const OnigCodePoint CR_Grapheme_Base[] = {
- 861,
+ 875,
0x0020, 0x007e,
0x00a0, 0x00ac,
0x00ae, 0x02ff,
@@ -19251,7 +19469,7 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce1,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d02, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d3a,
@@ -19670,6 +19888,7 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x11232, 0x11233,
0x11235, 0x11235,
0x11238, 0x1123d,
+ 0x1123f, 0x11240,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -19756,6 +19975,7 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x11a97, 0x11a97,
0x11a9a, 0x11aa2,
0x11ab0, 0x11af8,
+ 0x11b00, 0x11b09,
0x11c00, 0x11c08,
0x11c0a, 0x11c2f,
0x11c3e, 0x11c3e,
@@ -19779,6 +19999,11 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x11da0, 0x11da9,
0x11ee0, 0x11ef2,
0x11ef5, 0x11ef8,
+ 0x11f02, 0x11f10,
+ 0x11f12, 0x11f35,
+ 0x11f3e, 0x11f3f,
+ 0x11f41, 0x11f41,
+ 0x11f43, 0x11f59,
0x11fb0, 0x11fb0,
0x11fc0, 0x11ff1,
0x11fff, 0x12399,
@@ -19786,7 +20011,8 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x12470, 0x12474,
0x12480, 0x12543,
0x12f90, 0x12ff2,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -19814,7 +20040,9 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -19834,6 +20062,7 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x1d1ae, 0x1d1ea,
0x1d200, 0x1d241,
0x1d245, 0x1d245,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d300, 0x1d356,
0x1d360, 0x1d378,
@@ -19863,6 +20092,8 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x1da76, 0x1da83,
0x1da85, 0x1da8b,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e030, 0x1e06d,
0x1e100, 0x1e12c,
0x1e137, 0x1e13d,
0x1e140, 0x1e149,
@@ -19871,6 +20102,8 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x1e2c0, 0x1e2eb,
0x1e2f0, 0x1e2f9,
0x1e2ff, 0x1e2ff,
+ 0x1e4d0, 0x1e4eb,
+ 0x1e4f0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -19930,10 +20163,10 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x1f250, 0x1f251,
0x1f260, 0x1f265,
0x1f300, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -19944,30 +20177,29 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Grapheme_Base */
/* 'Grapheme_Link': Derived Property */
static const OnigCodePoint CR_Grapheme_Link[] = {
- 55,
+ 56,
0x094d, 0x094d,
0x09cd, 0x09cd,
0x0a4d, 0x0a4d,
@@ -20023,11 +20255,12 @@ static const OnigCodePoint CR_Grapheme_Link[] = {
0x11c3f, 0x11c3f,
0x11d44, 0x11d45,
0x11d97, 0x11d97,
+ 0x11f41, 0x11f42,
}; /* CR_Grapheme_Link */
/* 'Common': Script */
static const OnigCodePoint CR_Common[] = {
- 174,
+ 173,
0x0000, 0x0040,
0x005b, 0x0060,
0x007b, 0x00a9,
@@ -20134,6 +20367,7 @@ static const OnigCodePoint CR_Common[] = {
0x1d183, 0x1d184,
0x1d18c, 0x1d1a9,
0x1d1ae, 0x1d1ea,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d300, 0x1d356,
0x1d360, 0x1d378,
@@ -20174,10 +20408,10 @@ static const OnigCodePoint CR_Common[] = {
0x1f250, 0x1f251,
0x1f260, 0x1f265,
0x1f300, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -20188,15 +20422,13 @@ static const OnigCodePoint CR_Common[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
0x1fbf0, 0x1fbf9,
@@ -20206,7 +20438,7 @@ static const OnigCodePoint CR_Common[] = {
/* 'Latin': Script */
static const OnigCodePoint CR_Latin[] = {
- 38,
+ 39,
0x0041, 0x005a,
0x0061, 0x007a,
0x00aa, 0x00aa,
@@ -20245,6 +20477,7 @@ static const OnigCodePoint CR_Latin[] = {
0x10787, 0x107b0,
0x107b2, 0x107ba,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
}; /* CR_Latin */
/* 'Greek': Script */
@@ -20290,7 +20523,7 @@ static const OnigCodePoint CR_Greek[] = {
/* 'Cyrillic': Script */
static const OnigCodePoint CR_Cyrillic[] = {
- 8,
+ 10,
0x0400, 0x0484,
0x0487, 0x052f,
0x1c80, 0x1c88,
@@ -20299,6 +20532,8 @@ static const OnigCodePoint CR_Cyrillic[] = {
0x2de0, 0x2dff,
0xa640, 0xa69f,
0xfe2e, 0xfe2f,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
}; /* CR_Cyrillic */
/* 'Armenian': Script */
@@ -20326,7 +20561,7 @@ static const OnigCodePoint CR_Hebrew[] = {
/* 'Arabic': Script */
static const OnigCodePoint CR_Arabic[] = {
- 57,
+ 58,
0x0600, 0x0604,
0x0606, 0x060b,
0x060d, 0x061a,
@@ -20350,6 +20585,7 @@ static const OnigCodePoint CR_Arabic[] = {
0xfe70, 0xfe74,
0xfe76, 0xfefc,
0x10e60, 0x10e7e,
+ 0x10efd, 0x10eff,
0x1ee00, 0x1ee03,
0x1ee05, 0x1ee1f,
0x1ee21, 0x1ee22,
@@ -20403,11 +20639,12 @@ static const OnigCodePoint CR_Thaana[] = {
/* 'Devanagari': Script */
static const OnigCodePoint CR_Devanagari[] = {
- 4,
+ 5,
0x0900, 0x0950,
0x0955, 0x0963,
0x0966, 0x097f,
0xa8e0, 0xa8ff,
+ 0x11b00, 0x11b09,
}; /* CR_Devanagari */
/* 'Bengali': Script */
@@ -20544,7 +20781,7 @@ static const OnigCodePoint CR_Kannada[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
}; /* CR_Kannada */
/* 'Malayalam': Script */
@@ -20595,7 +20832,7 @@ static const OnigCodePoint CR_Lao[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
}; /* CR_Lao */
@@ -20746,17 +20983,18 @@ static const OnigCodePoint CR_Mongolian[] = {
/* 'Hiragana': Script */
static const OnigCodePoint CR_Hiragana[] = {
- 5,
+ 6,
0x3041, 0x3096,
0x309d, 0x309f,
0x1b001, 0x1b11f,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
0x1f200, 0x1f200,
}; /* CR_Hiragana */
/* 'Katakana': Script */
static const OnigCodePoint CR_Katakana[] = {
- 13,
+ 14,
0x30a1, 0x30fa,
0x30fd, 0x30ff,
0x31f0, 0x31ff,
@@ -20769,6 +21007,7 @@ static const OnigCodePoint CR_Katakana[] = {
0x1affd, 0x1affe,
0x1b000, 0x1b000,
0x1b120, 0x1b122,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
}; /* CR_Katakana */
@@ -20782,7 +21021,7 @@ static const OnigCodePoint CR_Bopomofo[] = {
/* 'Han': Script */
static const OnigCodePoint CR_Han[] = {
- 20,
+ 21,
0x2e80, 0x2e99,
0x2e9b, 0x2ef3,
0x2f00, 0x2fd5,
@@ -20797,12 +21036,13 @@ static const OnigCodePoint CR_Han[] = {
0x16fe2, 0x16fe3,
0x16ff0, 0x16ff1,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Han */
/* 'Yi': Script */
@@ -21165,9 +21405,8 @@ static const OnigCodePoint CR_Avestan[] = {
/* 'Egyptian_Hieroglyphs': Script */
static const OnigCodePoint CR_Egyptian_Hieroglyphs[] = {
- 2,
- 0x13000, 0x1342e,
- 0x13430, 0x13438,
+ 1,
+ 0x13000, 0x13455,
}; /* CR_Egyptian_Hieroglyphs */
/* 'Samaritan': Script */
@@ -21382,7 +21621,7 @@ static const OnigCodePoint CR_Pahawh_Hmong[] = {
static const OnigCodePoint CR_Khojki[] = {
2,
0x11200, 0x11211,
- 0x11213, 0x1123e,
+ 0x11213, 0x11241,
}; /* CR_Khojki */
/* 'Linear_A': Script */
@@ -21772,6 +22011,20 @@ static const OnigCodePoint CR_Vithkuqi[] = {
0x105bb, 0x105bc,
}; /* CR_Vithkuqi */
+/* 'Kawi': Script */
+static const OnigCodePoint CR_Kawi[] = {
+ 3,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f59,
+}; /* CR_Kawi */
+
+/* 'Nag_Mundari': Script */
+static const OnigCodePoint CR_Nag_Mundari[] = {
+ 1,
+ 0x1e4d0, 0x1e4f9,
+}; /* CR_Nag_Mundari */
+
/* 'White_Space': Binary Property */
#define CR_White_Space CR_Space
@@ -21853,7 +22106,7 @@ static const OnigCodePoint CR_Quotation_Mark[] = {
/* 'Terminal_Punctuation': Binary Property */
static const OnigCodePoint CR_Terminal_Punctuation[] = {
- 107,
+ 108,
0x0021, 0x0021,
0x002c, 0x002c,
0x002e, 0x002e,
@@ -21953,6 +22206,7 @@ static const OnigCodePoint CR_Terminal_Punctuation[] = {
0x11c41, 0x11c43,
0x11c71, 0x11c71,
0x11ef7, 0x11ef8,
+ 0x11f43, 0x11f44,
0x12470, 0x12474,
0x16a6e, 0x16a6f,
0x16af5, 0x16af5,
@@ -22118,7 +22372,7 @@ static const OnigCodePoint CR_Hex_Digit[] = {
/* 'Other_Alphabetic': Binary Property */
static const OnigCodePoint CR_Other_Alphabetic[] = {
- 233,
+ 240,
0x0345, 0x0345,
0x05b0, 0x05bd,
0x05bf, 0x05bf,
@@ -22178,7 +22432,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x0bc6, 0x0bc8,
0x0bca, 0x0bcc,
0x0bd7, 0x0bd7,
- 0x0c00, 0x0c03,
+ 0x0c00, 0x0c04,
0x0c3e, 0x0c44,
0x0c46, 0x0c48,
0x0c4a, 0x0c4c,
@@ -22190,6 +22444,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x0cca, 0x0ccc,
0x0cd5, 0x0cd6,
0x0ce2, 0x0ce3,
+ 0x0cf3, 0x0cf3,
0x0d00, 0x0d03,
0x0d3e, 0x0d44,
0x0d46, 0x0d48,
@@ -22208,7 +22463,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x0eb4, 0x0eb9,
0x0ebb, 0x0ebc,
0x0ecd, 0x0ecd,
- 0x0f71, 0x0f81,
+ 0x0f71, 0x0f83,
0x0f8d, 0x0f97,
0x0f99, 0x0fbc,
0x102b, 0x1036,
@@ -22281,7 +22536,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x11000, 0x11002,
0x11038, 0x11045,
0x11073, 0x11074,
- 0x11082, 0x11082,
+ 0x11080, 0x11082,
0x110b0, 0x110b8,
0x110c2, 0x110c2,
0x11100, 0x11102,
@@ -22293,6 +22548,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x1122c, 0x11234,
0x11237, 0x11237,
0x1123e, 0x1123e,
+ 0x11241, 0x11241,
0x112df, 0x112e8,
0x11300, 0x11303,
0x1133e, 0x11344,
@@ -22338,6 +22594,10 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x11d90, 0x11d91,
0x11d93, 0x11d96,
0x11ef3, 0x11ef6,
+ 0x11f00, 0x11f01,
+ 0x11f03, 0x11f03,
+ 0x11f34, 0x11f3a,
+ 0x11f3e, 0x11f40,
0x16f4f, 0x16f4f,
0x16f51, 0x16f87,
0x16f8f, 0x16f92,
@@ -22348,6 +22608,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e08f, 0x1e08f,
0x1e947, 0x1e947,
0x1f130, 0x1f149,
0x1f150, 0x1f169,
@@ -22356,7 +22617,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
/* 'Ideographic': Binary Property */
static const OnigCodePoint CR_Ideographic[] = {
- 19,
+ 20,
0x3006, 0x3007,
0x3021, 0x3029,
0x3038, 0x303a,
@@ -22370,17 +22631,18 @@ static const OnigCodePoint CR_Ideographic[] = {
0x18d00, 0x18d08,
0x1b170, 0x1b2fb,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Ideographic */
/* 'Diacritic': Binary Property */
static const OnigCodePoint CR_Diacritic[] = {
- 192,
+ 195,
0x005e, 0x005e,
0x0060, 0x0060,
0x00a8, 0x00a8,
@@ -22520,6 +22782,7 @@ static const OnigCodePoint CR_Diacritic[] = {
0x107b2, 0x107ba,
0x10ae5, 0x10ae6,
0x10d22, 0x10d27,
+ 0x10efd, 0x10eff,
0x10f46, 0x10f50,
0x10f82, 0x10f85,
0x11046, 0x11046,
@@ -22553,6 +22816,7 @@ static const OnigCodePoint CR_Diacritic[] = {
0x11d42, 0x11d42,
0x11d44, 0x11d45,
0x11d97, 0x11d97,
+ 0x13447, 0x13455,
0x16af0, 0x16af4,
0x16b30, 0x16b36,
0x16f8f, 0x16f9f,
@@ -22567,6 +22831,7 @@ static const OnigCodePoint CR_Diacritic[] = {
0x1d17b, 0x1d182,
0x1d185, 0x1d18b,
0x1d1aa, 0x1d1ad,
+ 0x1e030, 0x1e06d,
0x1e130, 0x1e136,
0x1e2ae, 0x1e2ae,
0x1e2ec, 0x1e2ef,
@@ -22615,7 +22880,7 @@ static const OnigCodePoint CR_Extender[] = {
/* 'Other_Lowercase': Binary Property */
static const OnigCodePoint CR_Other_Lowercase[] = {
- 20,
+ 28,
0x00aa, 0x00aa,
0x00ba, 0x00ba,
0x02b0, 0x02b8,
@@ -22623,6 +22888,7 @@ static const OnigCodePoint CR_Other_Lowercase[] = {
0x02e0, 0x02e4,
0x0345, 0x0345,
0x037a, 0x037a,
+ 0x10fc, 0x10fc,
0x1d2c, 0x1d6a,
0x1d78, 0x1d78,
0x1d9b, 0x1dbf,
@@ -22634,8 +22900,15 @@ static const OnigCodePoint CR_Other_Lowercase[] = {
0x2c7c, 0x2c7d,
0xa69c, 0xa69d,
0xa770, 0xa770,
+ 0xa7f2, 0xa7f4,
0xa7f8, 0xa7f9,
0xab5c, 0xab5f,
+ 0xab69, 0xab69,
+ 0x10780, 0x10780,
+ 0x10783, 0x10785,
+ 0x10787, 0x107b0,
+ 0x107b2, 0x107ba,
+ 0x1e030, 0x1e06d,
}; /* CR_Other_Lowercase */
/* 'Other_Uppercase': Binary Property */
@@ -22724,7 +22997,7 @@ static const OnigCodePoint CR_Radical[] = {
/* 'Unified_Ideograph': Binary Property */
static const OnigCodePoint CR_Unified_Ideograph[] = {
- 15,
+ 16,
0x3400, 0x4dbf,
0x4e00, 0x9fff,
0xfa0e, 0xfa0f,
@@ -22735,11 +23008,12 @@ static const OnigCodePoint CR_Unified_Ideograph[] = {
0xfa23, 0xfa24,
0xfa27, 0xfa29,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Unified_Ideograph */
/* 'Other_Default_Ignorable_Code_Point': Binary Property */
@@ -22773,7 +23047,7 @@ static const OnigCodePoint CR_Deprecated[] = {
/* 'Soft_Dotted': Binary Property */
static const OnigCodePoint CR_Soft_Dotted[] = {
- 32,
+ 34,
0x0069, 0x006a,
0x012f, 0x012f,
0x0249, 0x0249,
@@ -22806,6 +23080,8 @@ static const OnigCodePoint CR_Soft_Dotted[] = {
0x1d65e, 0x1d65f,
0x1d692, 0x1d693,
0x1df1a, 0x1df1a,
+ 0x1e04c, 0x1e04d,
+ 0x1e068, 0x1e068,
}; /* CR_Soft_Dotted */
/* 'Logical_Order_Exception': Binary Property */
@@ -22840,7 +23116,7 @@ static const OnigCodePoint CR_Other_ID_Continue[] = {
/* 'Sentence_Terminal': Binary Property */
static const OnigCodePoint CR_Sentence_Terminal[] = {
- 79,
+ 80,
0x0021, 0x0021,
0x002e, 0x002e,
0x003f, 0x003f,
@@ -22913,6 +23189,7 @@ static const OnigCodePoint CR_Sentence_Terminal[] = {
0x11a9b, 0x11a9c,
0x11c41, 0x11c42,
0x11ef7, 0x11ef8,
+ 0x11f43, 0x11f44,
0x16a6e, 0x16a6f,
0x16af5, 0x16af5,
0x16b37, 0x16b38,
@@ -22994,7 +23271,7 @@ static const OnigCodePoint CR_Regional_Indicator[] = {
/* 'Emoji': Emoji */
static const OnigCodePoint CR_Emoji[] = {
- 153,
+ 151,
0x0023, 0x0023,
0x002a, 0x002a,
0x0030, 0x0039,
@@ -23129,7 +23406,7 @@ static const OnigCodePoint CR_Emoji[] = {
0x1f680, 0x1f6c5,
0x1f6cb, 0x1f6d2,
0x1f6d5, 0x1f6d7,
- 0x1f6dd, 0x1f6e5,
+ 0x1f6dc, 0x1f6e5,
0x1f6e9, 0x1f6e9,
0x1f6eb, 0x1f6ec,
0x1f6f0, 0x1f6f0,
@@ -23139,20 +23416,18 @@ static const OnigCodePoint CR_Emoji[] = {
0x1f90c, 0x1f93a,
0x1f93c, 0x1f945,
0x1f947, 0x1f9ff,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
}; /* CR_Emoji */
/* 'Emoji_Presentation': Emoji */
static const OnigCodePoint CR_Emoji_Presentation[] = {
- 83,
+ 81,
0x231a, 0x231b,
0x23e9, 0x23ec,
0x23f0, 0x23f0,
@@ -23219,7 +23494,7 @@ static const OnigCodePoint CR_Emoji_Presentation[] = {
0x1f6cc, 0x1f6cc,
0x1f6d0, 0x1f6d2,
0x1f6d5, 0x1f6d7,
- 0x1f6dd, 0x1f6df,
+ 0x1f6dc, 0x1f6df,
0x1f6eb, 0x1f6ec,
0x1f6f4, 0x1f6fc,
0x1f7e0, 0x1f7eb,
@@ -23227,15 +23502,13 @@ static const OnigCodePoint CR_Emoji_Presentation[] = {
0x1f90c, 0x1f93a,
0x1f93c, 0x1f945,
0x1f947, 0x1f9ff,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
}; /* CR_Emoji_Presentation */
/* 'Emoji_Modifier': Emoji */
@@ -23286,7 +23559,7 @@ static const OnigCodePoint CR_Emoji_Modifier_Base[] = {
0x1f9cd, 0x1f9cf,
0x1f9d1, 0x1f9dd,
0x1fac3, 0x1fac5,
- 0x1faf0, 0x1faf6,
+ 0x1faf0, 0x1faf8,
}; /* CR_Emoji_Modifier_Base */
/* 'Emoji_Component': Emoji */
@@ -23389,7 +23662,7 @@ static const OnigCodePoint CR_Extended_Pictographic[] = {
/* 'Unknown': Script */
static const OnigCodePoint CR_Unknown[] = {
- 696,
+ 705,
0x0378, 0x0379,
0x0380, 0x0383,
0x038b, 0x038b,
@@ -23511,7 +23784,7 @@ static const OnigCodePoint CR_Unknown[] = {
0x0cdf, 0x0cdf,
0x0ce4, 0x0ce5,
0x0cf0, 0x0cf0,
- 0x0cf3, 0x0cff,
+ 0x0cf4, 0x0cff,
0x0d0d, 0x0d0d,
0x0d11, 0x0d11,
0x0d45, 0x0d45,
@@ -23541,7 +23814,7 @@ static const OnigCodePoint CR_Unknown[] = {
0x0ebe, 0x0ebf,
0x0ec5, 0x0ec5,
0x0ec7, 0x0ec7,
- 0x0ece, 0x0ecf,
+ 0x0ecf, 0x0ecf,
0x0eda, 0x0edb,
0x0ee0, 0x0eff,
0x0f48, 0x0f48,
@@ -23811,7 +24084,7 @@ static const OnigCodePoint CR_Unknown[] = {
0x10e7f, 0x10e7f,
0x10eaa, 0x10eaa,
0x10eae, 0x10eaf,
- 0x10eb2, 0x10eff,
+ 0x10eb2, 0x10efc,
0x10f28, 0x10f2f,
0x10f5a, 0x10f6f,
0x10f8a, 0x10faf,
@@ -23829,7 +24102,7 @@ static const OnigCodePoint CR_Unknown[] = {
0x111e0, 0x111e0,
0x111f5, 0x111ff,
0x11212, 0x11212,
- 0x1123f, 0x1127f,
+ 0x11242, 0x1127f,
0x11287, 0x11287,
0x11289, 0x11289,
0x1128e, 0x1128e,
@@ -23881,7 +24154,8 @@ static const OnigCodePoint CR_Unknown[] = {
0x119e5, 0x119ff,
0x11a48, 0x11a4f,
0x11aa3, 0x11aaf,
- 0x11af9, 0x11bff,
+ 0x11af9, 0x11aff,
+ 0x11b0a, 0x11bff,
0x11c09, 0x11c09,
0x11c37, 0x11c37,
0x11c46, 0x11c4f,
@@ -23902,7 +24176,10 @@ static const OnigCodePoint CR_Unknown[] = {
0x11d92, 0x11d92,
0x11d99, 0x11d9f,
0x11daa, 0x11edf,
- 0x11ef9, 0x11faf,
+ 0x11ef9, 0x11eff,
+ 0x11f11, 0x11f11,
+ 0x11f3b, 0x11f3d,
+ 0x11f5a, 0x11faf,
0x11fb1, 0x11fbf,
0x11ff2, 0x11ffe,
0x1239a, 0x123ff,
@@ -23910,8 +24187,7 @@ static const OnigCodePoint CR_Unknown[] = {
0x12475, 0x1247f,
0x12544, 0x12f8f,
0x12ff3, 0x12fff,
- 0x1342f, 0x1342f,
- 0x13439, 0x143ff,
+ 0x13456, 0x143ff,
0x14647, 0x167ff,
0x16a39, 0x16a3f,
0x16a5f, 0x16a5f,
@@ -23937,8 +24213,10 @@ static const OnigCodePoint CR_Unknown[] = {
0x1aff4, 0x1aff4,
0x1affc, 0x1affc,
0x1afff, 0x1afff,
- 0x1b123, 0x1b14f,
- 0x1b153, 0x1b163,
+ 0x1b123, 0x1b131,
+ 0x1b133, 0x1b14f,
+ 0x1b153, 0x1b154,
+ 0x1b156, 0x1b163,
0x1b168, 0x1b16f,
0x1b2fc, 0x1bbff,
0x1bc6b, 0x1bc6f,
@@ -23952,7 +24230,8 @@ static const OnigCodePoint CR_Unknown[] = {
0x1d0f6, 0x1d0ff,
0x1d127, 0x1d128,
0x1d1eb, 0x1d1ff,
- 0x1d246, 0x1d2df,
+ 0x1d246, 0x1d2bf,
+ 0x1d2d4, 0x1d2df,
0x1d2f4, 0x1d2ff,
0x1d357, 0x1d35f,
0x1d379, 0x1d3ff,
@@ -23979,19 +24258,23 @@ static const OnigCodePoint CR_Unknown[] = {
0x1da8c, 0x1da9a,
0x1daa0, 0x1daa0,
0x1dab0, 0x1deff,
- 0x1df1f, 0x1dfff,
+ 0x1df1f, 0x1df24,
+ 0x1df2b, 0x1dfff,
0x1e007, 0x1e007,
0x1e019, 0x1e01a,
0x1e022, 0x1e022,
0x1e025, 0x1e025,
- 0x1e02b, 0x1e0ff,
+ 0x1e02b, 0x1e02f,
+ 0x1e06e, 0x1e08e,
+ 0x1e090, 0x1e0ff,
0x1e12d, 0x1e12f,
0x1e13e, 0x1e13f,
0x1e14a, 0x1e14d,
0x1e150, 0x1e28f,
0x1e2af, 0x1e2bf,
0x1e2fa, 0x1e2fe,
- 0x1e300, 0x1e7df,
+ 0x1e300, 0x1e4cf,
+ 0x1e4fa, 0x1e7df,
0x1e7e7, 0x1e7e7,
0x1e7ec, 0x1e7ec,
0x1e7ef, 0x1e7ef,
@@ -24049,11 +24332,11 @@ static const OnigCodePoint CR_Unknown[] = {
0x1f249, 0x1f24f,
0x1f252, 0x1f25f,
0x1f266, 0x1f2ff,
- 0x1f6d8, 0x1f6dc,
+ 0x1f6d8, 0x1f6db,
0x1f6ed, 0x1f6ef,
0x1f6fd, 0x1f6ff,
- 0x1f774, 0x1f77f,
- 0x1f7d9, 0x1f7df,
+ 0x1f777, 0x1f77a,
+ 0x1f7da, 0x1f7df,
0x1f7ec, 0x1f7ef,
0x1f7f1, 0x1f7ff,
0x1f80c, 0x1f80f,
@@ -24064,25 +24347,24 @@ static const OnigCodePoint CR_Unknown[] = {
0x1f8b2, 0x1f8ff,
0x1fa54, 0x1fa5f,
0x1fa6e, 0x1fa6f,
- 0x1fa75, 0x1fa77,
0x1fa7d, 0x1fa7f,
- 0x1fa87, 0x1fa8f,
- 0x1faad, 0x1faaf,
- 0x1fabb, 0x1fabf,
- 0x1fac6, 0x1facf,
- 0x1fada, 0x1fadf,
- 0x1fae8, 0x1faef,
- 0x1faf7, 0x1faff,
+ 0x1fa89, 0x1fa8f,
+ 0x1fabe, 0x1fabe,
+ 0x1fac6, 0x1facd,
+ 0x1fadc, 0x1fadf,
+ 0x1fae9, 0x1faef,
+ 0x1faf9, 0x1faff,
0x1fb93, 0x1fb93,
0x1fbcb, 0x1fbef,
0x1fbfa, 0x1ffff,
0x2a6e0, 0x2a6ff,
- 0x2b739, 0x2b73f,
+ 0x2b73a, 0x2b73f,
0x2b81e, 0x2b81f,
0x2cea2, 0x2ceaf,
0x2ebe1, 0x2f7ff,
0x2fa1e, 0x2ffff,
- 0x3134b, 0xe0000,
+ 0x3134b, 0x3134f,
+ 0x323b0, 0xe0000,
0xe0002, 0xe001f,
0xe0080, 0xe00ff,
0xe01f0, 0x10ffff,
@@ -36632,10 +36914,730 @@ static const OnigCodePoint CR_Age_14_0[] = {
0xefffe, 0x10ffff,
}; /* CR_Age_14_0 */
+/* 'Age_15_0': Derived Age 15.0 */
+static const OnigCodePoint CR_Age_15_0[] = {
+ 715,
+ 0x0000, 0x0377,
+ 0x037a, 0x037f,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x058a,
+ 0x058d, 0x058f,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f4,
+ 0x0600, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x07fd, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x0860, 0x086a,
+ 0x0870, 0x088e,
+ 0x0890, 0x0891,
+ 0x0898, 0x0983,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bc, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09ce,
+ 0x09d7, 0x09d7,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e3,
+ 0x09e6, 0x09fe,
+ 0x0a01, 0x0a03,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3c, 0x0a3c,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a51, 0x0a51,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a76,
+ 0x0a81, 0x0a83,
+ 0x0a85, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abc, 0x0ac5,
+ 0x0ac7, 0x0ac9,
+ 0x0acb, 0x0acd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae3,
+ 0x0ae6, 0x0af1,
+ 0x0af9, 0x0aff,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b55, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b63,
+ 0x0b66, 0x0b77,
+ 0x0b82, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb9,
+ 0x0bbe, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcd,
+ 0x0bd0, 0x0bd0,
+ 0x0bd7, 0x0bd7,
+ 0x0be6, 0x0bfa,
+ 0x0c00, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3c, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c5d, 0x0c5d,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c77, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cdd, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf3,
+ 0x0d00, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4f,
+ 0x0d54, 0x0d63,
+ 0x0d66, 0x0d7f,
+ 0x0d81, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0de6, 0x0def,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ece,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f71, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fbe, 0x0fcc,
+ 0x0fce, 0x0fda,
+ 0x1000, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x1248,
+ 0x124a, 0x124d,
+ 0x1250, 0x1256,
+ 0x1258, 0x1258,
+ 0x125a, 0x125d,
+ 0x1260, 0x1288,
+ 0x128a, 0x128d,
+ 0x1290, 0x12b0,
+ 0x12b2, 0x12b5,
+ 0x12b8, 0x12be,
+ 0x12c0, 0x12c0,
+ 0x12c2, 0x12c5,
+ 0x12c8, 0x12d6,
+ 0x12d8, 0x1310,
+ 0x1312, 0x1315,
+ 0x1318, 0x135a,
+ 0x135d, 0x137c,
+ 0x1380, 0x1399,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1400, 0x169c,
+ 0x16a0, 0x16f8,
+ 0x1700, 0x1715,
+ 0x171f, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x1819,
+ 0x1820, 0x1878,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191e,
+ 0x1920, 0x192b,
+ 0x1930, 0x193b,
+ 0x1940, 0x1940,
+ 0x1944, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x19d0, 0x19da,
+ 0x19de, 0x1a1b,
+ 0x1a1e, 0x1a5e,
+ 0x1a60, 0x1a7c,
+ 0x1a7f, 0x1a89,
+ 0x1a90, 0x1a99,
+ 0x1aa0, 0x1aad,
+ 0x1ab0, 0x1ace,
+ 0x1b00, 0x1b4c,
+ 0x1b50, 0x1b7e,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cc7,
+ 0x1cd0, 0x1cfa,
+ 0x1d00, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fc4,
+ 0x1fc6, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fdd, 0x1fef,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffe,
+ 0x2000, 0x2064,
+ 0x2066, 0x2071,
+ 0x2074, 0x208e,
+ 0x2090, 0x209c,
+ 0x20a0, 0x20c0,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x218b,
+ 0x2190, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b97, 0x2cf3,
+ 0x2cf9, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d70,
+ 0x2d7f, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2de0, 0x2e5d,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x3190, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7ca,
+ 0xa7d0, 0xa7d1,
+ 0xa7d3, 0xa7d3,
+ 0xa7d5, 0xa7d9,
+ 0xa7f2, 0xa82c,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c5,
+ 0xa8ce, 0xa8d9,
+ 0xa8e0, 0xa953,
+ 0xa95f, 0xa97c,
+ 0xa980, 0xa9cd,
+ 0xa9cf, 0xa9d9,
+ 0xa9de, 0xa9fe,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa5c, 0xaac2,
+ 0xaadb, 0xaaf6,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xab30, 0xab6b,
+ 0xab70, 0xabed,
+ 0xabf0, 0xabf9,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xd800, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbc2,
+ 0xfbd3, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdcf, 0xfe19,
+ 0xfe20, 0xfe52,
+ 0xfe54, 0xfe66,
+ 0xfe68, 0xfe6b,
+ 0xfe70, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xfeff, 0xfeff,
+ 0xff01, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0xffe0, 0xffe6,
+ 0xffe8, 0xffee,
+ 0xfff9, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10100, 0x10102,
+ 0x10107, 0x10133,
+ 0x10137, 0x1018e,
+ 0x10190, 0x1019c,
+ 0x101a0, 0x101a0,
+ 0x101d0, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x102e0, 0x102fb,
+ 0x10300, 0x10323,
+ 0x1032d, 0x1034a,
+ 0x10350, 0x1037a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x1056f, 0x1057a,
+ 0x1057c, 0x1058a,
+ 0x1058c, 0x10592,
+ 0x10594, 0x10595,
+ 0x10597, 0x105a1,
+ 0x105a3, 0x105b1,
+ 0x105b3, 0x105b9,
+ 0x105bb, 0x105bc,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10780, 0x10785,
+ 0x10787, 0x107b0,
+ 0x107b2, 0x107ba,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10857, 0x1089e,
+ 0x108a7, 0x108af,
+ 0x108e0, 0x108f2,
+ 0x108f4, 0x108f5,
+ 0x108fb, 0x1091b,
+ 0x1091f, 0x10939,
+ 0x1093f, 0x1093f,
+ 0x10980, 0x109b7,
+ 0x109bc, 0x109cf,
+ 0x109d2, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a48,
+ 0x10a50, 0x10a58,
+ 0x10a60, 0x10a9f,
+ 0x10ac0, 0x10ae6,
+ 0x10aeb, 0x10af6,
+ 0x10b00, 0x10b35,
+ 0x10b39, 0x10b55,
+ 0x10b58, 0x10b72,
+ 0x10b78, 0x10b91,
+ 0x10b99, 0x10b9c,
+ 0x10ba9, 0x10baf,
+ 0x10c00, 0x10c48,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x10cfa, 0x10d27,
+ 0x10d30, 0x10d39,
+ 0x10e60, 0x10e7e,
+ 0x10e80, 0x10ea9,
+ 0x10eab, 0x10ead,
+ 0x10eb0, 0x10eb1,
+ 0x10efd, 0x10f27,
+ 0x10f30, 0x10f59,
+ 0x10f70, 0x10f89,
+ 0x10fb0, 0x10fcb,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x1104d,
+ 0x11052, 0x11075,
+ 0x1107f, 0x110c2,
+ 0x110cd, 0x110cd,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11147,
+ 0x11150, 0x11176,
+ 0x11180, 0x111df,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x11241,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a9,
+ 0x112b0, 0x112ea,
+ 0x112f0, 0x112f9,
+ 0x11300, 0x11303,
+ 0x11305, 0x1130c,
+ 0x1130f, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132a, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133b, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134d,
+ 0x11350, 0x11350,
+ 0x11357, 0x11357,
+ 0x1135d, 0x11363,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+ 0x11400, 0x1145b,
+ 0x1145d, 0x11461,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115dd,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+ 0x11660, 0x1166c,
+ 0x11680, 0x116b9,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x11746,
+ 0x11800, 0x1183b,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x11906,
+ 0x11909, 0x11909,
+ 0x1190c, 0x11913,
+ 0x11915, 0x11916,
+ 0x11918, 0x11935,
+ 0x11937, 0x11938,
+ 0x1193b, 0x11946,
+ 0x11950, 0x11959,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119e4,
+ 0x11a00, 0x11a47,
+ 0x11a50, 0x11aa2,
+ 0x11ab0, 0x11af8,
+ 0x11b00, 0x11b09,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c36,
+ 0x11c38, 0x11c45,
+ 0x11c50, 0x11c6c,
+ 0x11c70, 0x11c8f,
+ 0x11c92, 0x11ca7,
+ 0x11ca9, 0x11cb6,
+ 0x11d00, 0x11d06,
+ 0x11d08, 0x11d09,
+ 0x11d0b, 0x11d36,
+ 0x11d3a, 0x11d3a,
+ 0x11d3c, 0x11d3d,
+ 0x11d3f, 0x11d47,
+ 0x11d50, 0x11d59,
+ 0x11d60, 0x11d65,
+ 0x11d67, 0x11d68,
+ 0x11d6a, 0x11d8e,
+ 0x11d90, 0x11d91,
+ 0x11d93, 0x11d98,
+ 0x11da0, 0x11da9,
+ 0x11ee0, 0x11ef8,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f59,
+ 0x11fb0, 0x11fb0,
+ 0x11fc0, 0x11ff1,
+ 0x11fff, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+ 0x12f90, 0x12ff2,
+ 0x13000, 0x13455,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16abe,
+ 0x16ac0, 0x16ac9,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e9a,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe4,
+ 0x16ff0, 0x16ff1,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18cd5,
+ 0x18d00, 0x18d08,
+ 0x1aff0, 0x1aff3,
+ 0x1aff5, 0x1affb,
+ 0x1affd, 0x1affe,
+ 0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
+ 0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bca3,
+ 0x1cf00, 0x1cf2d,
+ 0x1cf30, 0x1cf46,
+ 0x1cf50, 0x1cfc3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1ea,
+ 0x1d200, 0x1d245,
+ 0x1d2c0, 0x1d2d3,
+ 0x1d2e0, 0x1d2f3,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d378,
+ 0x1d400, 0x1d454,
+ 0x1d456, 0x1d49c,
+ 0x1d49e, 0x1d49f,
+ 0x1d4a2, 0x1d4a2,
+ 0x1d4a5, 0x1d4a6,
+ 0x1d4a9, 0x1d4ac,
+ 0x1d4ae, 0x1d4b9,
+ 0x1d4bb, 0x1d4bb,
+ 0x1d4bd, 0x1d4c3,
+ 0x1d4c5, 0x1d505,
+ 0x1d507, 0x1d50a,
+ 0x1d50d, 0x1d514,
+ 0x1d516, 0x1d51c,
+ 0x1d51e, 0x1d539,
+ 0x1d53b, 0x1d53e,
+ 0x1d540, 0x1d544,
+ 0x1d546, 0x1d546,
+ 0x1d54a, 0x1d550,
+ 0x1d552, 0x1d6a5,
+ 0x1d6a8, 0x1d7cb,
+ 0x1d7ce, 0x1da8b,
+ 0x1da9b, 0x1da9f,
+ 0x1daa1, 0x1daaf,
+ 0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
+ 0x1e100, 0x1e12c,
+ 0x1e130, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14f,
+ 0x1e290, 0x1e2ae,
+ 0x1e2c0, 0x1e2f9,
+ 0x1e2ff, 0x1e2ff,
+ 0x1e4d0, 0x1e4f9,
+ 0x1e7e0, 0x1e7e6,
+ 0x1e7e8, 0x1e7eb,
+ 0x1e7ed, 0x1e7ee,
+ 0x1e7f0, 0x1e7fe,
+ 0x1e800, 0x1e8c4,
+ 0x1e8c7, 0x1e8d6,
+ 0x1e900, 0x1e94b,
+ 0x1e950, 0x1e959,
+ 0x1e95e, 0x1e95f,
+ 0x1ec71, 0x1ecb4,
+ 0x1ed01, 0x1ed3d,
+ 0x1ee00, 0x1ee03,
+ 0x1ee05, 0x1ee1f,
+ 0x1ee21, 0x1ee22,
+ 0x1ee24, 0x1ee24,
+ 0x1ee27, 0x1ee27,
+ 0x1ee29, 0x1ee32,
+ 0x1ee34, 0x1ee37,
+ 0x1ee39, 0x1ee39,
+ 0x1ee3b, 0x1ee3b,
+ 0x1ee42, 0x1ee42,
+ 0x1ee47, 0x1ee47,
+ 0x1ee49, 0x1ee49,
+ 0x1ee4b, 0x1ee4b,
+ 0x1ee4d, 0x1ee4f,
+ 0x1ee51, 0x1ee52,
+ 0x1ee54, 0x1ee54,
+ 0x1ee57, 0x1ee57,
+ 0x1ee59, 0x1ee59,
+ 0x1ee5b, 0x1ee5b,
+ 0x1ee5d, 0x1ee5d,
+ 0x1ee5f, 0x1ee5f,
+ 0x1ee61, 0x1ee62,
+ 0x1ee64, 0x1ee64,
+ 0x1ee67, 0x1ee6a,
+ 0x1ee6c, 0x1ee72,
+ 0x1ee74, 0x1ee77,
+ 0x1ee79, 0x1ee7c,
+ 0x1ee7e, 0x1ee7e,
+ 0x1ee80, 0x1ee89,
+ 0x1ee8b, 0x1ee9b,
+ 0x1eea1, 0x1eea3,
+ 0x1eea5, 0x1eea9,
+ 0x1eeab, 0x1eebb,
+ 0x1eef0, 0x1eef1,
+ 0x1f000, 0x1f02b,
+ 0x1f030, 0x1f093,
+ 0x1f0a0, 0x1f0ae,
+ 0x1f0b1, 0x1f0bf,
+ 0x1f0c1, 0x1f0cf,
+ 0x1f0d1, 0x1f0f5,
+ 0x1f100, 0x1f1ad,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f6d7,
+ 0x1f6dc, 0x1f6ec,
+ 0x1f6f0, 0x1f6fc,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f7f0, 0x1f7f0,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f8b0, 0x1f8b1,
+ 0x1f900, 0x1fa53,
+ 0x1fa60, 0x1fa6d,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
+ 0x1fb00, 0x1fb92,
+ 0x1fb94, 0x1fbca,
+ 0x1fbf0, 0x1fbf9,
+ 0x1fffe, 0x2a6df,
+ 0x2a700, 0x2b739,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x3134a,
+ 0x31350, 0x323af,
+ 0x3fffe, 0x3ffff,
+ 0x4fffe, 0x4ffff,
+ 0x5fffe, 0x5ffff,
+ 0x6fffe, 0x6ffff,
+ 0x7fffe, 0x7ffff,
+ 0x8fffe, 0x8ffff,
+ 0x9fffe, 0x9ffff,
+ 0xafffe, 0xaffff,
+ 0xbfffe, 0xbffff,
+ 0xcfffe, 0xcffff,
+ 0xdfffe, 0xdffff,
+ 0xe0001, 0xe0001,
+ 0xe0020, 0xe007f,
+ 0xe0100, 0xe01ef,
+ 0xefffe, 0x10ffff,
+}; /* CR_Age_15_0 */
+
#endif /* USE_UNICODE_AGE_PROPERTIES */
/* 'Grapheme_Cluster_Break_Prepend': Grapheme_Cluster_Break=Prepend */
static const OnigCodePoint CR_Grapheme_Cluster_Break_Prepend[] = {
- 14,
+ 15,
0x0600, 0x0605,
0x06dd, 0x06dd,
0x070f, 0x070f,
@@ -36650,6 +37652,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Prepend[] = {
0x11a3a, 0x11a3a,
0x11a84, 0x11a89,
0x11d46, 0x11d46,
+ 0x11f02, 0x11f02,
}; /* CR_Grapheme_Cluster_Break_Prepend */
/* 'Grapheme_Cluster_Break_CR': Grapheme_Cluster_Break=CR */
@@ -36677,7 +37680,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Control[] = {
0x2060, 0x206f,
0xfeff, 0xfeff,
0xfff0, 0xfffb,
- 0x13430, 0x13438,
+ 0x13430, 0x1343f,
0x1bca0, 0x1bca3,
0x1d173, 0x1d17a,
0xe0000, 0xe001f,
@@ -36687,7 +37690,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Control[] = {
/* 'Grapheme_Cluster_Break_Extend': Grapheme_Cluster_Break=Extend */
static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
- 354,
+ 364,
0x0300, 0x036f,
0x0483, 0x0489,
0x0591, 0x05bd,
@@ -36790,7 +37793,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
0x0e47, 0x0e4e,
0x0eb1, 0x0eb1,
0x0eb4, 0x0ebc,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0f18, 0x0f19,
0x0f35, 0x0f35,
0x0f37, 0x0f37,
@@ -36919,6 +37922,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
0x10ae5, 0x10ae6,
0x10d24, 0x10d27,
0x10eab, 0x10eac,
+ 0x10efd, 0x10eff,
0x10f46, 0x10f50,
0x10f82, 0x10f85,
0x11001, 0x11001,
@@ -36941,6 +37945,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
0x11234, 0x11234,
0x11236, 0x11237,
0x1123e, 0x1123e,
+ 0x11241, 0x11241,
0x112df, 0x112df,
0x112e3, 0x112ea,
0x11300, 0x11301,
@@ -37008,6 +38013,12 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
0x11d95, 0x11d95,
0x11d97, 0x11d97,
0x11ef3, 0x11ef4,
+ 0x11f00, 0x11f01,
+ 0x11f36, 0x11f3a,
+ 0x11f40, 0x11f40,
+ 0x11f42, 0x11f42,
+ 0x13440, 0x13440,
+ 0x13447, 0x13455,
0x16af0, 0x16af4,
0x16b30, 0x16b36,
0x16f4f, 0x16f4f,
@@ -37034,9 +38045,11 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e08f, 0x1e08f,
0x1e130, 0x1e136,
0x1e2ae, 0x1e2ae,
0x1e2ec, 0x1e2ef,
+ 0x1e4ec, 0x1e4ef,
0x1e8d0, 0x1e8d6,
0x1e944, 0x1e94a,
0x1f3fb, 0x1f3ff,
@@ -37049,7 +38062,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
/* 'Grapheme_Cluster_Break_SpacingMark': Grapheme_Cluster_Break=SpacingMark */
static const OnigCodePoint CR_Grapheme_Cluster_Break_SpacingMark[] = {
- 161,
+ 165,
0x0903, 0x0903,
0x093b, 0x093b,
0x093e, 0x0940,
@@ -37081,6 +38094,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_SpacingMark[] = {
0x0cc3, 0x0cc4,
0x0cc7, 0x0cc8,
0x0cca, 0x0ccb,
+ 0x0cf3, 0x0cf3,
0x0d02, 0x0d03,
0x0d3f, 0x0d40,
0x0d46, 0x0d48,
@@ -37183,7 +38197,6 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_SpacingMark[] = {
0x116ac, 0x116ac,
0x116ae, 0x116af,
0x116b6, 0x116b6,
- 0x11720, 0x11721,
0x11726, 0x11726,
0x1182c, 0x1182e,
0x11838, 0x11838,
@@ -37207,6 +38220,10 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_SpacingMark[] = {
0x11d93, 0x11d94,
0x11d96, 0x11d96,
0x11ef5, 0x11ef6,
+ 0x11f03, 0x11f03,
+ 0x11f34, 0x11f35,
+ 0x11f3e, 0x11f3f,
+ 0x11f41, 0x11f41,
0x16f51, 0x16f87,
0x16ff0, 0x16ff1,
0x1d166, 0x1d166,
@@ -39275,6 +40292,12 @@ static const OnigCodePoint CR_In_Yezidi[] = {
0x10e80, 0x10ebf,
}; /* CR_In_Yezidi */
+/* 'In_Arabic_Extended_C': Block */
+static const OnigCodePoint CR_In_Arabic_Extended_C[] = {
+ 1,
+ 0x10ec0, 0x10eff,
+}; /* CR_In_Arabic_Extended_C */
+
/* 'In_Old_Sogdian': Block */
static const OnigCodePoint CR_In_Old_Sogdian[] = {
1,
@@ -39458,6 +40481,12 @@ static const OnigCodePoint CR_In_Pau_Cin_Hau[] = {
0x11ac0, 0x11aff,
}; /* CR_In_Pau_Cin_Hau */
+/* 'In_Devanagari_Extended_A': Block */
+static const OnigCodePoint CR_In_Devanagari_Extended_A[] = {
+ 1,
+ 0x11b00, 0x11b5f,
+}; /* CR_In_Devanagari_Extended_A */
+
/* 'In_Bhaiksuki': Block */
static const OnigCodePoint CR_In_Bhaiksuki[] = {
1,
@@ -39488,6 +40517,12 @@ static const OnigCodePoint CR_In_Makasar[] = {
0x11ee0, 0x11eff,
}; /* CR_In_Makasar */
+/* 'In_Kawi': Block */
+static const OnigCodePoint CR_In_Kawi[] = {
+ 1,
+ 0x11f00, 0x11f5f,
+}; /* CR_In_Kawi */
+
/* 'In_Lisu_Supplement': Block */
static const OnigCodePoint CR_In_Lisu_Supplement[] = {
1,
@@ -39533,7 +40568,7 @@ static const OnigCodePoint CR_In_Egyptian_Hieroglyphs[] = {
/* 'In_Egyptian_Hieroglyph_Format_Controls': Block */
static const OnigCodePoint CR_In_Egyptian_Hieroglyph_Format_Controls[] = {
1,
- 0x13430, 0x1343f,
+ 0x13430, 0x1345f,
}; /* CR_In_Egyptian_Hieroglyph_Format_Controls */
/* 'In_Anatolian_Hieroglyphs': Block */
@@ -39680,6 +40715,12 @@ static const OnigCodePoint CR_In_Ancient_Greek_Musical_Notation[] = {
0x1d200, 0x1d24f,
}; /* CR_In_Ancient_Greek_Musical_Notation */
+/* 'In_Kaktovik_Numerals': Block */
+static const OnigCodePoint CR_In_Kaktovik_Numerals[] = {
+ 1,
+ 0x1d2c0, 0x1d2df,
+}; /* CR_In_Kaktovik_Numerals */
+
/* 'In_Mayan_Numerals': Block */
static const OnigCodePoint CR_In_Mayan_Numerals[] = {
1,
@@ -39722,6 +40763,12 @@ static const OnigCodePoint CR_In_Glagolitic_Supplement[] = {
0x1e000, 0x1e02f,
}; /* CR_In_Glagolitic_Supplement */
+/* 'In_Cyrillic_Extended_D': Block */
+static const OnigCodePoint CR_In_Cyrillic_Extended_D[] = {
+ 1,
+ 0x1e030, 0x1e08f,
+}; /* CR_In_Cyrillic_Extended_D */
+
/* 'In_Nyiakeng_Puachue_Hmong': Block */
static const OnigCodePoint CR_In_Nyiakeng_Puachue_Hmong[] = {
1,
@@ -39740,6 +40787,12 @@ static const OnigCodePoint CR_In_Wancho[] = {
0x1e2c0, 0x1e2ff,
}; /* CR_In_Wancho */
+/* 'In_Nag_Mundari': Block */
+static const OnigCodePoint CR_In_Nag_Mundari[] = {
+ 1,
+ 0x1e4d0, 0x1e4ff,
+}; /* CR_In_Nag_Mundari */
+
/* 'In_Ethiopic_Extended_B': Block */
static const OnigCodePoint CR_In_Ethiopic_Extended_B[] = {
1,
@@ -39914,6 +40967,12 @@ static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_G[] = {
0x30000, 0x3134f,
}; /* CR_In_CJK_Unified_Ideographs_Extension_G */
+/* 'In_CJK_Unified_Ideographs_Extension_H': Block */
+static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_H[] = {
+ 1,
+ 0x31350, 0x323af,
+}; /* CR_In_CJK_Unified_Ideographs_Extension_H */
+
/* 'In_Tags': Block */
static const OnigCodePoint CR_In_Tags[] = {
1,
@@ -39952,7 +41011,6 @@ static const OnigCodePoint CR_In_No_Block[] = {
0x10bb0, 0x10bff,
0x10c50, 0x10c7f,
0x10d40, 0x10e5f,
- 0x10ec0, 0x10eff,
0x11250, 0x1127f,
0x11380, 0x113ff,
0x114e0, 0x1157f,
@@ -39960,12 +41018,12 @@ static const OnigCodePoint CR_In_No_Block[] = {
0x11750, 0x117ff,
0x11850, 0x1189f,
0x11960, 0x1199f,
- 0x11b00, 0x11bff,
+ 0x11b60, 0x11bff,
0x11cc0, 0x11cff,
0x11db0, 0x11edf,
- 0x11f00, 0x11faf,
+ 0x11f60, 0x11faf,
0x12550, 0x12f8f,
- 0x13440, 0x143ff,
+ 0x13460, 0x143ff,
0x14680, 0x167ff,
0x16b90, 0x16e3f,
0x16ea0, 0x16eff,
@@ -39974,12 +41032,13 @@ static const OnigCodePoint CR_In_No_Block[] = {
0x1b300, 0x1bbff,
0x1bcb0, 0x1ceff,
0x1cfd0, 0x1cfff,
- 0x1d250, 0x1d2df,
+ 0x1d250, 0x1d2bf,
0x1d380, 0x1d3ff,
0x1dab0, 0x1deff,
- 0x1e030, 0x1e0ff,
+ 0x1e090, 0x1e0ff,
0x1e150, 0x1e28f,
- 0x1e300, 0x1e7df,
+ 0x1e300, 0x1e4cf,
+ 0x1e500, 0x1e7df,
0x1e8e0, 0x1e8ff,
0x1e960, 0x1ec6f,
0x1ecc0, 0x1ecff,
@@ -39989,7 +41048,7 @@ static const OnigCodePoint CR_In_No_Block[] = {
0x2a6e0, 0x2a6ff,
0x2ebf0, 0x2f7ff,
0x2fa20, 0x2ffff,
- 0x31350, 0xdffff,
+ 0x323b0, 0xdffff,
0xe0080, 0xe00ff,
0xe01f0, 0xeffff,
}; /* CR_In_No_Block */
@@ -40233,6 +41292,8 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_Tangsa,
CR_Toto,
CR_Vithkuqi,
+ CR_Kawi,
+ CR_Nag_Mundari,
CR_White_Space,
CR_Bidi_Control,
CR_Join_Control,
@@ -40299,6 +41360,7 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_Age_12_1,
CR_Age_13_0,
CR_Age_14_0,
+ CR_Age_15_0,
#endif /* USE_UNICODE_AGE_PROPERTIES */
CR_Grapheme_Cluster_Break_Prepend,
CR_Grapheme_Cluster_Break_CR,
@@ -40522,6 +41584,7 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_In_Hanifi_Rohingya,
CR_In_Rumi_Numeral_Symbols,
CR_In_Yezidi,
+ CR_In_Arabic_Extended_C,
CR_In_Old_Sogdian,
CR_In_Sogdian,
CR_In_Old_Uyghur,
@@ -40553,11 +41616,13 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_In_Soyombo,
CR_In_Unified_Canadian_Aboriginal_Syllabics_Extended_A,
CR_In_Pau_Cin_Hau,
+ CR_In_Devanagari_Extended_A,
CR_In_Bhaiksuki,
CR_In_Marchen,
CR_In_Masaram_Gondi,
CR_In_Gunjala_Gondi,
CR_In_Makasar,
+ CR_In_Kawi,
CR_In_Lisu_Supplement,
CR_In_Tamil_Supplement,
CR_In_Cuneiform,
@@ -40590,6 +41655,7 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_In_Byzantine_Musical_Symbols,
CR_In_Musical_Symbols,
CR_In_Ancient_Greek_Musical_Notation,
+ CR_In_Kaktovik_Numerals,
CR_In_Mayan_Numerals,
CR_In_Tai_Xuan_Jing_Symbols,
CR_In_Counting_Rod_Numerals,
@@ -40597,9 +41663,11 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_In_Sutton_SignWriting,
CR_In_Latin_Extended_G,
CR_In_Glagolitic_Supplement,
+ CR_In_Cyrillic_Extended_D,
CR_In_Nyiakeng_Puachue_Hmong,
CR_In_Toto,
CR_In_Wancho,
+ CR_In_Nag_Mundari,
CR_In_Ethiopic_Extended_B,
CR_In_Mende_Kikakui,
CR_In_Adlam,
@@ -40629,6 +41697,7 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_In_CJK_Unified_Ideographs_Extension_F,
CR_In_CJK_Compatibility_Ideographs_Supplement,
CR_In_CJK_Unified_Ideographs_Extension_G,
+ CR_In_CJK_Unified_Ideographs_Extension_H,
CR_In_Tags,
CR_In_Variation_Selectors_Supplement,
CR_In_Supplementary_Private_Use_Area_A,
@@ -40642,11 +41711,7 @@ struct uniname2ctype_struct {
};
#define uniname2ctype_offset(str) offsetof(struct uniname2ctype_pool_t, uniname2ctype_pool_##str)
-static const struct uniname2ctype_struct *uniname2ctype_p(
-#if !(1+0) /* if ANSI, old style not to conflict with generated prototype */
- const char *, unsigned int
-#endif
-);
+static const struct uniname2ctype_struct *uniname2ctype_p(register const char *str, register size_t len);
#ifndef USE_UNICODE_PROPERTIES
#define TOTAL_KEYWORDS 15
@@ -40657,9 +41722,9 @@ static const struct uniname2ctype_struct *uniname2ctype_p(
/* maximum key range = 15, duplicates = 0 */
#else /* USE_UNICODE_PROPERTIES */
#ifndef USE_UNICODE_AGE_PROPERTIES
-#define TOTAL_KEYWORDS 856
+#define TOTAL_KEYWORDS 866
#else /* USE_UNICODE_AGE_PROPERTIES */
-#define TOTAL_KEYWORDS 880
+#define TOTAL_KEYWORDS 891
#endif /* USE_UNICODE_AGE_PROPERTIES */
#define MIN_WORD_LENGTH 1
#define MAX_WORD_LENGTH 45
@@ -40708,13 +41773,13 @@ uniname2ctype_hash (register const char *str, register size_t len)
6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099,
#else /* USE_UNICODE_AGE_PROPERTIES */
6099, 6099, 6099, 6099, 6099, 6099, 12, 6099, 3, 1,
- 4, 8, 36, 24, 14, 16, 10, 7, 6099, 6099,
+ 4, 8, 32, 26, 14, 17, 10, 7, 6099, 6099,
#endif /* USE_UNICODE_AGE_PROPERTIES */
6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099,
6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099,
6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099,
6099, 6099, 6099, 6099, 6099, 6099, 6099, 1, 1425, 113,
- 437, 37, 1086, 1071, 1051, 4, 1267, 9, 500, 88,
+ 437, 37, 1086, 1071, 1051, 4, 1984, 9, 500, 88,
8, 18, 1371, 1287, 54, 203, 310, 619, 1958, 603,
275, 1624, 44, 1, 22, 6099, 6099, 6099, 6099, 6099
#endif /* USE_UNICODE_PROPERTIES */
@@ -40944,6 +42009,7 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str636[sizeof("innewa")];
char uniname2ctype_pool_str639[sizeof("sk")];
char uniname2ctype_pool_str642[sizeof("control")];
+ char uniname2ctype_pool_str643[sizeof("inkawi")];
char uniname2ctype_pool_str645[sizeof("inancientsymbols")];
char uniname2ctype_pool_str647[sizeof("palm")];
char uniname2ctype_pool_str650[sizeof("inlycian")];
@@ -40958,6 +42024,7 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str695[sizeof("inwarangciti")];
char uniname2ctype_pool_str696[sizeof("sora")];
char uniname2ctype_pool_str697[sizeof("inopticalcharacterrecognition")];
+ char uniname2ctype_pool_str700[sizeof("kawi")];
char uniname2ctype_pool_str703[sizeof("inoldsogdian")];
char uniname2ctype_pool_str705[sizeof("inmalayalam")];
char uniname2ctype_pool_str707[sizeof("bamum")];
@@ -41078,11 +42145,13 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str1182[sizeof("inogham")];
char uniname2ctype_pool_str1183[sizeof("cher")];
char uniname2ctype_pool_str1185[sizeof("chakma")];
+ char uniname2ctype_pool_str1186[sizeof("inkaktoviknumerals")];
char uniname2ctype_pool_str1190[sizeof("emoji")];
char uniname2ctype_pool_str1191[sizeof("insiddham")];
char uniname2ctype_pool_str1197[sizeof("cherokee")];
char uniname2ctype_pool_str1198[sizeof("khar")];
char uniname2ctype_pool_str1203[sizeof("inmongolian")];
+ char uniname2ctype_pool_str1204[sizeof("innagmundari")];
char uniname2ctype_pool_str1207[sizeof("incherokeesupplement")];
char uniname2ctype_pool_str1209[sizeof("manichaean")];
char uniname2ctype_pool_str1212[sizeof("inolchiki")];
@@ -41126,48 +42195,46 @@ struct uniname2ctype_pool_t
#ifdef USE_UNICODE_AGE_PROPERTIES
char uniname2ctype_pool_str1257[sizeof("age=6.0")];
char uniname2ctype_pool_str1258[sizeof("age=6.2")];
- char uniname2ctype_pool_str1259[sizeof("age=7.0")];
+ char uniname2ctype_pool_str1259[sizeof("age=15.0")];
+ char uniname2ctype_pool_str1260[sizeof("age=7.0")];
char uniname2ctype_pool_str1262[sizeof("age=6.3")];
#endif /* USE_UNICODE_AGE_PROPERTIES */
char uniname2ctype_pool_str1263[sizeof("cwt")];
#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str1265[sizeof("age=5.1")];
+ char uniname2ctype_pool_str1265[sizeof("age=14.0")];
#endif /* USE_UNICODE_AGE_PROPERTIES */
char uniname2ctype_pool_str1266[sizeof("unassigned")];
#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str1267[sizeof("age=5.0")];
- char uniname2ctype_pool_str1268[sizeof("age=5.2")];
- char uniname2ctype_pool_str1269[sizeof("age=14.0")];
+ char uniname2ctype_pool_str1267[sizeof("age=5.1")];
+ char uniname2ctype_pool_str1269[sizeof("age=5.0")];
+ char uniname2ctype_pool_str1270[sizeof("age=5.2")];
#endif /* USE_UNICODE_AGE_PROPERTIES */
char uniname2ctype_pool_str1271[sizeof("diacritic")];
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ char uniname2ctype_pool_str1273[sizeof("age=4.1")];
+#endif /* USE_UNICODE_AGE_PROPERTIES */
char uniname2ctype_pool_str1274[sizeof("ahom")];
#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str1277[sizeof("age=4.1")];
- char uniname2ctype_pool_str1279[sizeof("age=4.0")];
+ char uniname2ctype_pool_str1275[sizeof("age=4.0")];
#endif /* USE_UNICODE_AGE_PROPERTIES */
char uniname2ctype_pool_str1282[sizeof("incjkunifiedideographsextensione")];
- char uniname2ctype_pool_str1284[sizeof("hani")];
char uniname2ctype_pool_str1285[sizeof("khmr")];
- char uniname2ctype_pool_str1287[sizeof("han")];
char uniname2ctype_pool_str1289[sizeof("insinhala")];
char uniname2ctype_pool_str1292[sizeof("inmiscellaneoustechnical")];
char uniname2ctype_pool_str1297[sizeof("saur")];
- char uniname2ctype_pool_str1298[sizeof("hano")];
char uniname2ctype_pool_str1300[sizeof("guru")];
char uniname2ctype_pool_str1301[sizeof("sundanese")];
char uniname2ctype_pool_str1306[sizeof("punct")];
char uniname2ctype_pool_str1314[sizeof("paucinhau")];
char uniname2ctype_pool_str1317[sizeof("gurmukhi")];
- char uniname2ctype_pool_str1323[sizeof("inkhojki")];
- char uniname2ctype_pool_str1327[sizeof("hanunoo")];
char uniname2ctype_pool_str1328[sizeof("chorasmian")];
- char uniname2ctype_pool_str1330[sizeof("hira")];
char uniname2ctype_pool_str1331[sizeof("logicalorderexception")];
char uniname2ctype_pool_str1340[sizeof("khmer")];
char uniname2ctype_pool_str1343[sizeof("limbu")];
char uniname2ctype_pool_str1349[sizeof("chrs")];
char uniname2ctype_pool_str1352[sizeof("oriya")];
char uniname2ctype_pool_str1354[sizeof("inscriptionalpahlavi")];
+ char uniname2ctype_pool_str1356[sizeof("incyrillicextendedd")];
char uniname2ctype_pool_str1358[sizeof("incjkunifiedideographsextensionc")];
char uniname2ctype_pool_str1360[sizeof("cntrl")];
char uniname2ctype_pool_str1365[sizeof("inlatinextendedadditional")];
@@ -41235,7 +42302,6 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str1587[sizeof("indogra")];
char uniname2ctype_pool_str1597[sizeof("arab")];
char uniname2ctype_pool_str1598[sizeof("medefaidrin")];
- char uniname2ctype_pool_str1601[sizeof("hatran")];
char uniname2ctype_pool_str1607[sizeof("inshorthandformatcontrols")];
char uniname2ctype_pool_str1613[sizeof("phli")];
char uniname2ctype_pool_str1617[sizeof("inimperialaramaic")];
@@ -41244,7 +42310,6 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str1623[sizeof("inanatolianhieroglyphs")];
char uniname2ctype_pool_str1629[sizeof("punctuation")];
char uniname2ctype_pool_str1635[sizeof("graphemeextend")];
- char uniname2ctype_pool_str1636[sizeof("hatr")];
char uniname2ctype_pool_str1643[sizeof("cwl")];
char uniname2ctype_pool_str1644[sizeof("vith")];
char uniname2ctype_pool_str1654[sizeof("ingeometricshapes")];
@@ -41302,7 +42367,6 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str1845[sizeof("oidc")];
char uniname2ctype_pool_str1848[sizeof("bopo")];
char uniname2ctype_pool_str1851[sizeof("cuneiform")];
- char uniname2ctype_pool_str1857[sizeof("hex")];
char uniname2ctype_pool_str1866[sizeof("caseignorable")];
char uniname2ctype_pool_str1871[sizeof("inoldpersian")];
char uniname2ctype_pool_str1881[sizeof("cwu")];
@@ -41321,10 +42385,8 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str1935[sizeof("oids")];
char uniname2ctype_pool_str1936[sizeof("inarabicextendeda")];
char uniname2ctype_pool_str1941[sizeof("modifierletter")];
- char uniname2ctype_pool_str1948[sizeof("gujr")];
char uniname2ctype_pool_str1950[sizeof("incjksymbolsandpunctuation")];
char uniname2ctype_pool_str1956[sizeof("olower")];
- char uniname2ctype_pool_str1957[sizeof("gujarati")];
char uniname2ctype_pool_str1958[sizeof("bopomofo")];
char uniname2ctype_pool_str1964[sizeof("inlisu")];
char uniname2ctype_pool_str1967[sizeof("inoldpermic")];
@@ -41338,19 +42400,26 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str1993[sizeof("inbalinese")];
char uniname2ctype_pool_str1994[sizeof("sorasompeng")];
char uniname2ctype_pool_str1996[sizeof("closepunctuation")];
+ char uniname2ctype_pool_str2001[sizeof("hani")];
char uniname2ctype_pool_str2002[sizeof("inmayannumerals")];
+ char uniname2ctype_pool_str2004[sizeof("han")];
char uniname2ctype_pool_str2006[sizeof("inmiscellaneousmathematicalsymbolsb")];
char uniname2ctype_pool_str2010[sizeof("inlepcha")];
char uniname2ctype_pool_str2011[sizeof("patsyn")];
char uniname2ctype_pool_str2012[sizeof("inlisusupplement")];
char uniname2ctype_pool_str2014[sizeof("insyriacsupplement")];
+ char uniname2ctype_pool_str2015[sizeof("hano")];
char uniname2ctype_pool_str2016[sizeof("newa")];
char uniname2ctype_pool_str2023[sizeof("spacingmark")];
char uniname2ctype_pool_str2024[sizeof("inpalmyrene")];
char uniname2ctype_pool_str2026[sizeof("takr")];
char uniname2ctype_pool_str2033[sizeof("xposixpunct")];
+ char uniname2ctype_pool_str2040[sizeof("inkhojki")];
char uniname2ctype_pool_str2042[sizeof("taile")];
char uniname2ctype_pool_str2043[sizeof("assigned")];
+ char uniname2ctype_pool_str2044[sizeof("hanunoo")];
+ char uniname2ctype_pool_str2047[sizeof("hira")];
+ char uniname2ctype_pool_str2048[sizeof("inarabicextendedc")];
char uniname2ctype_pool_str2062[sizeof("newtailue")];
char uniname2ctype_pool_str2070[sizeof("space")];
char uniname2ctype_pool_str2073[sizeof("intelugu")];
@@ -41401,41 +42470,35 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2278[sizeof("shaw")];
char uniname2ctype_pool_str2279[sizeof("palmyrene")];
char uniname2ctype_pool_str2283[sizeof("soyo")];
+ char uniname2ctype_pool_str2296[sizeof("incjkunifiedideographsextensionh")];
char uniname2ctype_pool_str2305[sizeof("sgnw")];
char uniname2ctype_pool_str2308[sizeof("toto")];
char uniname2ctype_pool_str2312[sizeof("caucasianalbanian")];
char uniname2ctype_pool_str2315[sizeof("inmathematicalalphanumericsymbols")];
char uniname2ctype_pool_str2316[sizeof("incjkunifiedideographsextensiong")];
+ char uniname2ctype_pool_str2318[sizeof("hatran")];
char uniname2ctype_pool_str2321[sizeof("taiviet")];
char uniname2ctype_pool_str2323[sizeof("meroitichieroglyphs")];
char uniname2ctype_pool_str2327[sizeof("ingeorgianextended")];
char uniname2ctype_pool_str2331[sizeof("incjkunifiedideographsextensionf")];
char uniname2ctype_pool_str2333[sizeof("oldpersian")];
- char uniname2ctype_pool_str2341[sizeof("mahj")];
char uniname2ctype_pool_str2343[sizeof("induployan")];
char uniname2ctype_pool_str2344[sizeof("incyrillicextendedb")];
char uniname2ctype_pool_str2345[sizeof("dash")];
- char uniname2ctype_pool_str2350[sizeof("mahajani")];
- char uniname2ctype_pool_str2351[sizeof("hang")];
+ char uniname2ctype_pool_str2353[sizeof("hatr")];
char uniname2ctype_pool_str2361[sizeof("innyiakengpuachuehmong")];
char uniname2ctype_pool_str2364[sizeof("incombiningdiacriticalmarks")];
- char uniname2ctype_pool_str2370[sizeof("ingujarati")];
char uniname2ctype_pool_str2373[sizeof("nl")];
char uniname2ctype_pool_str2374[sizeof("incombiningdiacriticalmarksforsymbols")];
char uniname2ctype_pool_str2375[sizeof("khudawadi")];
- char uniname2ctype_pool_str2389[sizeof("ingunjalagondi")];
char uniname2ctype_pool_str2397[sizeof("incjkradicalssupplement")];
char uniname2ctype_pool_str2398[sizeof("inglagolitic")];
char uniname2ctype_pool_str2405[sizeof("orkh")];
- char uniname2ctype_pool_str2406[sizeof("hiragana")];
char uniname2ctype_pool_str2414[sizeof("syrc")];
- char uniname2ctype_pool_str2418[sizeof("inrejang")];
char uniname2ctype_pool_str2427[sizeof("surrogate")];
- char uniname2ctype_pool_str2428[sizeof("khoj")];
char uniname2ctype_pool_str2433[sizeof("indevanagari")];
char uniname2ctype_pool_str2434[sizeof("avestan")];
char uniname2ctype_pool_str2437[sizeof("oldpermic")];
- char uniname2ctype_pool_str2438[sizeof("hmng")];
char uniname2ctype_pool_str2440[sizeof("ethi")];
char uniname2ctype_pool_str2451[sizeof("ogam")];
char uniname2ctype_pool_str2454[sizeof("rohg")];
@@ -41443,7 +42506,7 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2464[sizeof("java")];
char uniname2ctype_pool_str2470[sizeof("inphagspa")];
char uniname2ctype_pool_str2475[sizeof("lepcha")];
- char uniname2ctype_pool_str2476[sizeof("inenclosedcjklettersandmonths")];
+ char uniname2ctype_pool_str2476[sizeof("indevanagariextendeda")];
char uniname2ctype_pool_str2478[sizeof("intifinagh")];
char uniname2ctype_pool_str2479[sizeof("intagalog")];
char uniname2ctype_pool_str2481[sizeof("incombiningdiacriticalmarkssupplement")];
@@ -41453,6 +42516,7 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2513[sizeof("insymbolsandpictographsextendeda")];
char uniname2ctype_pool_str2530[sizeof("syriac")];
char uniname2ctype_pool_str2534[sizeof("inbengali")];
+ char uniname2ctype_pool_str2535[sizeof("nagm")];
char uniname2ctype_pool_str2545[sizeof("extendedpictographic")];
char uniname2ctype_pool_str2548[sizeof("buhd")];
char uniname2ctype_pool_str2549[sizeof("javanese")];
@@ -41461,6 +42525,7 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2567[sizeof("inlatin1supplement")];
char uniname2ctype_pool_str2570[sizeof("ingothic")];
char uniname2ctype_pool_str2572[sizeof("invariationselectors")];
+ char uniname2ctype_pool_str2574[sizeof("hex")];
char uniname2ctype_pool_str2575[sizeof("inverticalforms")];
char uniname2ctype_pool_str2576[sizeof("ebase")];
char uniname2ctype_pool_str2582[sizeof("incurrencysymbols")];
@@ -41474,15 +42539,15 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2652[sizeof("invedicextensions")];
char uniname2ctype_pool_str2656[sizeof("inlimbu")];
char uniname2ctype_pool_str2657[sizeof("olditalic")];
- char uniname2ctype_pool_str2660[sizeof("rjng")];
+ char uniname2ctype_pool_str2665[sizeof("gujr")];
char uniname2ctype_pool_str2666[sizeof("mathsymbol")];
char uniname2ctype_pool_str2670[sizeof("incjkunifiedideographsextensionb")];
+ char uniname2ctype_pool_str2674[sizeof("gujarati")];
char uniname2ctype_pool_str2688[sizeof("phagspa")];
char uniname2ctype_pool_str2689[sizeof("invariationselectorssupplement")];
char uniname2ctype_pool_str2694[sizeof("currencysymbol")];
char uniname2ctype_pool_str2705[sizeof("inlinearbsyllabary")];
char uniname2ctype_pool_str2726[sizeof("wancho")];
- char uniname2ctype_pool_str2738[sizeof("hmnp")];
char uniname2ctype_pool_str2750[sizeof("inpaucinhau")];
char uniname2ctype_pool_str2761[sizeof("other")];
char uniname2ctype_pool_str2762[sizeof("otheridcontinue")];
@@ -41492,7 +42557,6 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2772[sizeof("warangciti")];
char uniname2ctype_pool_str2775[sizeof("othernumber")];
char uniname2ctype_pool_str2786[sizeof("digit")];
- char uniname2ctype_pool_str2787[sizeof("hebr")];
char uniname2ctype_pool_str2793[sizeof("nonspacingmark")];
char uniname2ctype_pool_str2801[sizeof("titlecaseletter")];
char uniname2ctype_pool_str2808[sizeof("inmeroiticcursive")];
@@ -41512,7 +42576,6 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2871[sizeof("noncharactercodepoint")];
char uniname2ctype_pool_str2879[sizeof("oldhungarian")];
char uniname2ctype_pool_str2886[sizeof("insymbolsforlegacycomputing")];
- char uniname2ctype_pool_str2901[sizeof("hangul")];
char uniname2ctype_pool_str2902[sizeof("insmallformvariants")];
char uniname2ctype_pool_str2904[sizeof("inhangulsyllables")];
char uniname2ctype_pool_str2905[sizeof("emojipresentation")];
@@ -41524,15 +42587,12 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2964[sizeof("inpsalterpahlavi")];
char uniname2ctype_pool_str2966[sizeof("whitespace")];
char uniname2ctype_pool_str2967[sizeof("finalpunctuation")];
- char uniname2ctype_pool_str2969[sizeof("hung")];
char uniname2ctype_pool_str2970[sizeof("orya")];
- char uniname2ctype_pool_str2972[sizeof("hexdigit")];
char uniname2ctype_pool_str2980[sizeof("phlp")];
char uniname2ctype_pool_str2984[sizeof("inbamumsupplement")];
char uniname2ctype_pool_str2986[sizeof("buhid")];
char uniname2ctype_pool_str2987[sizeof("paragraphseparator")];
char uniname2ctype_pool_str2988[sizeof("inalphabeticpresentationforms")];
- char uniname2ctype_pool_str2993[sizeof("hluw")];
char uniname2ctype_pool_str2997[sizeof("inlatinextendedg")];
char uniname2ctype_pool_str3001[sizeof("elba")];
char uniname2ctype_pool_str3002[sizeof("changeswhentitlecased")];
@@ -41549,30 +42609,39 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str3048[sizeof("tagbanwa")];
char uniname2ctype_pool_str3052[sizeof("tamil")];
char uniname2ctype_pool_str3053[sizeof("khitansmallscript")];
+ char uniname2ctype_pool_str3058[sizeof("mahj")];
+ char uniname2ctype_pool_str3067[sizeof("mahajani")];
+ char uniname2ctype_pool_str3068[sizeof("hang")];
char uniname2ctype_pool_str3071[sizeof("tirh")];
char uniname2ctype_pool_str3072[sizeof("sylotinagri")];
char uniname2ctype_pool_str3082[sizeof("talu")];
+ char uniname2ctype_pool_str3084[sizeof("nagmundari")];
char uniname2ctype_pool_str3086[sizeof("deva")];
+ char uniname2ctype_pool_str3087[sizeof("ingujarati")];
char uniname2ctype_pool_str3091[sizeof("deprecated")];
char uniname2ctype_pool_str3099[sizeof("inarabicpresentationformsb")];
char uniname2ctype_pool_str3104[sizeof("devanagari")];
+ char uniname2ctype_pool_str3106[sizeof("ingunjalagondi")];
char uniname2ctype_pool_str3107[sizeof("graphemeclusterbreak=t")];
char uniname2ctype_pool_str3109[sizeof("graphemeclusterbreak=lvt")];
char uniname2ctype_pool_str3110[sizeof("taitham")];
char uniname2ctype_pool_str3111[sizeof("nbat")];
char uniname2ctype_pool_str3118[sizeof("telu")];
+ char uniname2ctype_pool_str3123[sizeof("hiragana")];
char uniname2ctype_pool_str3125[sizeof("nabataean")];
- char uniname2ctype_pool_str3140[sizeof("inmahjongtiles")];
+ char uniname2ctype_pool_str3135[sizeof("inrejang")];
char uniname2ctype_pool_str3142[sizeof("intangutsupplement")];
+ char uniname2ctype_pool_str3145[sizeof("khoj")];
+ char uniname2ctype_pool_str3155[sizeof("hmng")];
char uniname2ctype_pool_str3157[sizeof("cyprominoan")];
char uniname2ctype_pool_str3158[sizeof("inhebrew")];
char uniname2ctype_pool_str3176[sizeof("inmathematicaloperators")];
char uniname2ctype_pool_str3180[sizeof("inarabicsupplement")];
+ char uniname2ctype_pool_str3193[sizeof("inenclosedcjklettersandmonths")];
char uniname2ctype_pool_str3209[sizeof("changeswhenlowercased")];
char uniname2ctype_pool_str3212[sizeof("tangut")];
char uniname2ctype_pool_str3215[sizeof("elbasan")];
char uniname2ctype_pool_str3218[sizeof("osmanya")];
- char uniname2ctype_pool_str3227[sizeof("inyijinghexagramsymbols")];
char uniname2ctype_pool_str3237[sizeof("insuperscriptsandsubscripts")];
char uniname2ctype_pool_str3239[sizeof("graphemeclusterbreak=extend")];
char uniname2ctype_pool_str3240[sizeof("graphemeclusterbreak=prepend")];
@@ -41583,7 +42652,6 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str3275[sizeof("kayahli")];
char uniname2ctype_pool_str3284[sizeof("inplayingcards")];
char uniname2ctype_pool_str3287[sizeof("elym")];
- char uniname2ctype_pool_str3290[sizeof("injavanese")];
char uniname2ctype_pool_str3297[sizeof("graphemeclusterbreak=l")];
char uniname2ctype_pool_str3303[sizeof("graphemeclusterbreak=control")];
char uniname2ctype_pool_str3313[sizeof("ogrext")];
@@ -41599,6 +42667,7 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str3371[sizeof("cypriot")];
char uniname2ctype_pool_str3372[sizeof("any")];
char uniname2ctype_pool_str3373[sizeof("otheruppercase")];
+ char uniname2ctype_pool_str3377[sizeof("rjng")];
char uniname2ctype_pool_str3391[sizeof("wspace")];
char uniname2ctype_pool_str3396[sizeof("inindicsiyaqnumbers")];
char uniname2ctype_pool_str3405[sizeof("inprivateusearea")];
@@ -41606,11 +42675,12 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str3428[sizeof("oupper")];
char uniname2ctype_pool_str3433[sizeof("signwriting")];
char uniname2ctype_pool_str3436[sizeof("nushu")];
- char uniname2ctype_pool_str3452[sizeof("hanifirohingya")];
+ char uniname2ctype_pool_str3455[sizeof("hmnp")];
char uniname2ctype_pool_str3458[sizeof("upper")];
char uniname2ctype_pool_str3460[sizeof("insupplementalarrowsc")];
char uniname2ctype_pool_str3483[sizeof("omath")];
char uniname2ctype_pool_str3502[sizeof("modifiersymbol")];
+ char uniname2ctype_pool_str3504[sizeof("hebr")];
char uniname2ctype_pool_str3505[sizeof("inhalfwidthandfullwidthforms")];
char uniname2ctype_pool_str3511[sizeof("insupplementalmathematicaloperators")];
char uniname2ctype_pool_str3532[sizeof("inpahawhhmong")];
@@ -41619,60 +42689,68 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str3580[sizeof("dupl")];
char uniname2ctype_pool_str3590[sizeof("ogham")];
char uniname2ctype_pool_str3613[sizeof("dashpunctuation")];
+ char uniname2ctype_pool_str3618[sizeof("hangul")];
char uniname2ctype_pool_str3648[sizeof("inhanguljamoextendedb")];
char uniname2ctype_pool_str3659[sizeof("bassavah")];
char uniname2ctype_pool_str3664[sizeof("aghb")];
+ char uniname2ctype_pool_str3686[sizeof("hung")];
+ char uniname2ctype_pool_str3689[sizeof("hexdigit")];
char uniname2ctype_pool_str3698[sizeof("incypriotsyllabary")];
char uniname2ctype_pool_str3699[sizeof("indivesakuru")];
char uniname2ctype_pool_str3701[sizeof("tibt")];
char uniname2ctype_pool_str3705[sizeof("inlatinextendedb")];
+ char uniname2ctype_pool_str3710[sizeof("hluw")];
char uniname2ctype_pool_str3713[sizeof("tibetan")];
char uniname2ctype_pool_str3721[sizeof("inyisyllables")];
char uniname2ctype_pool_str3744[sizeof("oldnortharabian")];
char uniname2ctype_pool_str3754[sizeof("defaultignorablecodepoint")];
char uniname2ctype_pool_str3766[sizeof("inhighprivateusesurrogates")];
- char uniname2ctype_pool_str3770[sizeof("rejang")];
char uniname2ctype_pool_str3799[sizeof("soyombo")];
char uniname2ctype_pool_str3807[sizeof("otherdefaultignorablecodepoint")];
char uniname2ctype_pool_str3842[sizeof("pahawhhmong")];
char uniname2ctype_pool_str3845[sizeof("unifiedideograph")];
char uniname2ctype_pool_str3850[sizeof("othermath")];
char uniname2ctype_pool_str3854[sizeof("changeswhencasefolded")];
+ char uniname2ctype_pool_str3857[sizeof("inmahjongtiles")];
char uniname2ctype_pool_str3868[sizeof("dep")];
char uniname2ctype_pool_str3881[sizeof("divesakuru")];
char uniname2ctype_pool_str3884[sizeof("graphemeclusterbreak=lf")];
char uniname2ctype_pool_str3891[sizeof("uppercaseletter")];
char uniname2ctype_pool_str3924[sizeof("insupplementalpunctuation")];
char uniname2ctype_pool_str3942[sizeof("ethiopic")];
+ char uniname2ctype_pool_str3944[sizeof("inyijinghexagramsymbols")];
char uniname2ctype_pool_str3949[sizeof("ecomp")];
char uniname2ctype_pool_str3976[sizeof("inglagoliticsupplement")];
- char uniname2ctype_pool_str3978[sizeof("hebrew")];
char uniname2ctype_pool_str3998[sizeof("inbopomofoextended")];
- char uniname2ctype_pool_str4066[sizeof("graphemeclusterbreak=zwj")];
+ char uniname2ctype_pool_str4007[sizeof("injavanese")];
char uniname2ctype_pool_str4106[sizeof("otherpunctuation")];
char uniname2ctype_pool_str4116[sizeof("tifinagh")];
char uniname2ctype_pool_str4127[sizeof("tfng")];
+ char uniname2ctype_pool_str4169[sizeof("hanifirohingya")];
char uniname2ctype_pool_str4231[sizeof("tavt")];
char uniname2ctype_pool_str4308[sizeof("inboxdrawing")];
char uniname2ctype_pool_str4309[sizeof("oldsoutharabian")];
- char uniname2ctype_pool_str4321[sizeof("hyphen")];
char uniname2ctype_pool_str4348[sizeof("inegyptianhieroglyphs")];
char uniname2ctype_pool_str4361[sizeof("inegyptianhieroglyphformatcontrols")];
char uniname2ctype_pool_str4459[sizeof("tagb")];
+ char uniname2ctype_pool_str4487[sizeof("rejang")];
char uniname2ctype_pool_str4604[sizeof("tglg")];
char uniname2ctype_pool_str4626[sizeof("tagalog")];
char uniname2ctype_pool_str4627[sizeof("othergraphemeextend")];
char uniname2ctype_pool_str4674[sizeof("insupplementaryprivateuseareaa")];
char uniname2ctype_pool_str4683[sizeof("inhighsurrogates")];
+ char uniname2ctype_pool_str4695[sizeof("hebrew")];
char uniname2ctype_pool_str4734[sizeof("duployan")];
char uniname2ctype_pool_str4755[sizeof("graphemeclusterbreak=v")];
char uniname2ctype_pool_str4756[sizeof("graphemeclusterbreak=lv")];
char uniname2ctype_pool_str4772[sizeof("insupplementalarrowsb")];
+ char uniname2ctype_pool_str4783[sizeof("graphemeclusterbreak=zwj")];
char uniname2ctype_pool_str4810[sizeof("telugu")];
char uniname2ctype_pool_str4898[sizeof("zyyy")];
char uniname2ctype_pool_str4982[sizeof("olduyghur")];
char uniname2ctype_pool_str4986[sizeof("inhangulcompatibilityjamo")];
char uniname2ctype_pool_str5018[sizeof("openpunctuation")];
+ char uniname2ctype_pool_str5038[sizeof("hyphen")];
char uniname2ctype_pool_str5134[sizeof("insupplementalsymbolsandpictographs")];
char uniname2ctype_pool_str5141[sizeof("egyp")];
char uniname2ctype_pool_str5300[sizeof("nyiakengpuachuehmong")];
@@ -41849,6 +42927,7 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"innewa",
"sk",
"control",
+ "inkawi",
"inancientsymbols",
"palm",
"inlycian",
@@ -41863,6 +42942,7 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"inwarangciti",
"sora",
"inopticalcharacterrecognition",
+ "kawi",
"inoldsogdian",
"inmalayalam",
"bamum",
@@ -41995,11 +43075,13 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"inogham",
"cher",
"chakma",
+ "inkaktoviknumerals",
"emoji",
"insiddham",
"cherokee",
"khar",
"inmongolian",
+ "innagmundari",
"incherokeesupplement",
"manichaean",
"inolchiki",
@@ -42043,48 +43125,46 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
#ifdef USE_UNICODE_AGE_PROPERTIES
"age=6.0",
"age=6.2",
+ "age=15.0",
"age=7.0",
"age=6.3",
#endif /* USE_UNICODE_AGE_PROPERTIES */
"cwt",
#ifdef USE_UNICODE_AGE_PROPERTIES
- "age=5.1",
+ "age=14.0",
#endif /* USE_UNICODE_AGE_PROPERTIES */
"unassigned",
#ifdef USE_UNICODE_AGE_PROPERTIES
+ "age=5.1",
"age=5.0",
"age=5.2",
- "age=14.0",
#endif /* USE_UNICODE_AGE_PROPERTIES */
"diacritic",
- "ahom",
#ifdef USE_UNICODE_AGE_PROPERTIES
"age=4.1",
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ "ahom",
+#ifdef USE_UNICODE_AGE_PROPERTIES
"age=4.0",
#endif /* USE_UNICODE_AGE_PROPERTIES */
"incjkunifiedideographsextensione",
- "hani",
"khmr",
- "han",
"insinhala",
"inmiscellaneoustechnical",
"saur",
- "hano",
"guru",
"sundanese",
"punct",
"paucinhau",
"gurmukhi",
- "inkhojki",
- "hanunoo",
"chorasmian",
- "hira",
"logicalorderexception",
"khmer",
"limbu",
"chrs",
"oriya",
"inscriptionalpahlavi",
+ "incyrillicextendedd",
"incjkunifiedideographsextensionc",
#endif /* USE_UNICODE_PROPERTIES */
"cntrl",
@@ -42156,7 +43236,6 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"indogra",
"arab",
"medefaidrin",
- "hatran",
"inshorthandformatcontrols",
"phli",
"inimperialaramaic",
@@ -42165,7 +43244,6 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"inanatolianhieroglyphs",
"punctuation",
"graphemeextend",
- "hatr",
"cwl",
"vith",
"ingeometricshapes",
@@ -42223,7 +43301,6 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"oidc",
"bopo",
"cuneiform",
- "hex",
"caseignorable",
"inoldpersian",
"cwu",
@@ -42242,10 +43319,8 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"oids",
"inarabicextendeda",
"modifierletter",
- "gujr",
"incjksymbolsandpunctuation",
"olower",
- "gujarati",
"bopomofo",
"inlisu",
"inoldpermic",
@@ -42259,12 +43334,15 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"inbalinese",
"sorasompeng",
"closepunctuation",
+ "hani",
"inmayannumerals",
+ "han",
"inmiscellaneousmathematicalsymbolsb",
"inlepcha",
"patsyn",
"inlisusupplement",
"insyriacsupplement",
+ "hano",
"newa",
"spacingmark",
"inpalmyrene",
@@ -42274,8 +43352,12 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
#ifndef USE_UNICODE_PROPERTIES
"lower",
#else /* USE_UNICODE_PROPERTIES */
+ "inkhojki",
"taile",
"assigned",
+ "hanunoo",
+ "hira",
+ "inarabicextendedc",
"newtailue",
"space",
"intelugu",
@@ -42328,41 +43410,35 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"shaw",
"palmyrene",
"soyo",
+ "incjkunifiedideographsextensionh",
"sgnw",
"toto",
"caucasianalbanian",
"inmathematicalalphanumericsymbols",
"incjkunifiedideographsextensiong",
+ "hatran",
"taiviet",
"meroitichieroglyphs",
"ingeorgianextended",
"incjkunifiedideographsextensionf",
"oldpersian",
- "mahj",
"induployan",
"incyrillicextendedb",
"dash",
- "mahajani",
- "hang",
+ "hatr",
"innyiakengpuachuehmong",
"incombiningdiacriticalmarks",
- "ingujarati",
"nl",
"incombiningdiacriticalmarksforsymbols",
"khudawadi",
- "ingunjalagondi",
"incjkradicalssupplement",
"inglagolitic",
"orkh",
- "hiragana",
"syrc",
- "inrejang",
"surrogate",
- "khoj",
"indevanagari",
"avestan",
"oldpermic",
- "hmng",
"ethi",
"ogam",
"rohg",
@@ -42370,7 +43446,7 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"java",
"inphagspa",
"lepcha",
- "inenclosedcjklettersandmonths",
+ "indevanagariextendeda",
"intifinagh",
"intagalog",
"incombiningdiacriticalmarkssupplement",
@@ -42380,6 +43456,7 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"insymbolsandpictographsextendeda",
"syriac",
"inbengali",
+ "nagm",
"extendedpictographic",
"buhd",
"javanese",
@@ -42388,6 +43465,7 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"inlatin1supplement",
"ingothic",
"invariationselectors",
+ "hex",
"inverticalforms",
"ebase",
"incurrencysymbols",
@@ -42401,15 +43479,15 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"invedicextensions",
"inlimbu",
"olditalic",
- "rjng",
+ "gujr",
"mathsymbol",
"incjkunifiedideographsextensionb",
+ "gujarati",
"phagspa",
"invariationselectorssupplement",
"currencysymbol",
"inlinearbsyllabary",
"wancho",
- "hmnp",
"inpaucinhau",
"other",
"otheridcontinue",
@@ -42423,7 +43501,6 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
#ifndef USE_UNICODE_PROPERTIES
"blank"
#else /* USE_UNICODE_PROPERTIES */
- "hebr",
"nonspacingmark",
"titlecaseletter",
"inmeroiticcursive",
@@ -42443,7 +43520,6 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"noncharactercodepoint",
"oldhungarian",
"insymbolsforlegacycomputing",
- "hangul",
"insmallformvariants",
"inhangulsyllables",
"emojipresentation",
@@ -42455,15 +43531,12 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"inpsalterpahlavi",
"whitespace",
"finalpunctuation",
- "hung",
"orya",
- "hexdigit",
"phlp",
"inbamumsupplement",
"buhid",
"paragraphseparator",
"inalphabeticpresentationforms",
- "hluw",
"inlatinextendedg",
"elba",
"changeswhentitlecased",
@@ -42480,30 +43553,39 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"tagbanwa",
"tamil",
"khitansmallscript",
+ "mahj",
+ "mahajani",
+ "hang",
"tirh",
"sylotinagri",
"talu",
+ "nagmundari",
"deva",
+ "ingujarati",
"deprecated",
"inarabicpresentationformsb",
"devanagari",
+ "ingunjalagondi",
"graphemeclusterbreak=t",
"graphemeclusterbreak=lvt",
"taitham",
"nbat",
"telu",
+ "hiragana",
"nabataean",
- "inmahjongtiles",
+ "inrejang",
"intangutsupplement",
+ "khoj",
+ "hmng",
"cyprominoan",
"inhebrew",
"inmathematicaloperators",
"inarabicsupplement",
+ "inenclosedcjklettersandmonths",
"changeswhenlowercased",
"tangut",
"elbasan",
"osmanya",
- "inyijinghexagramsymbols",
"insuperscriptsandsubscripts",
"graphemeclusterbreak=extend",
"graphemeclusterbreak=prepend",
@@ -42514,7 +43596,6 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"kayahli",
"inplayingcards",
"elym",
- "injavanese",
"graphemeclusterbreak=l",
"graphemeclusterbreak=control",
"ogrext",
@@ -42530,6 +43611,7 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"cypriot",
"any",
"otheruppercase",
+ "rjng",
"wspace",
"inindicsiyaqnumbers",
"inprivateusearea",
@@ -42537,11 +43619,12 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"oupper",
"signwriting",
"nushu",
- "hanifirohingya",
+ "hmnp",
"upper",
"insupplementalarrowsc",
"omath",
"modifiersymbol",
+ "hebr",
"inhalfwidthandfullwidthforms",
"insupplementalmathematicaloperators",
"inpahawhhmong",
@@ -42550,60 +43633,68 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"dupl",
"ogham",
"dashpunctuation",
+ "hangul",
"inhanguljamoextendedb",
"bassavah",
"aghb",
+ "hung",
+ "hexdigit",
"incypriotsyllabary",
"indivesakuru",
"tibt",
"inlatinextendedb",
+ "hluw",
"tibetan",
"inyisyllables",
"oldnortharabian",
"defaultignorablecodepoint",
"inhighprivateusesurrogates",
- "rejang",
"soyombo",
"otherdefaultignorablecodepoint",
"pahawhhmong",
"unifiedideograph",
"othermath",
"changeswhencasefolded",
+ "inmahjongtiles",
"dep",
"divesakuru",
"graphemeclusterbreak=lf",
"uppercaseletter",
"insupplementalpunctuation",
"ethiopic",
+ "inyijinghexagramsymbols",
"ecomp",
"inglagoliticsupplement",
- "hebrew",
"inbopomofoextended",
- "graphemeclusterbreak=zwj",
+ "injavanese",
"otherpunctuation",
"tifinagh",
"tfng",
+ "hanifirohingya",
"tavt",
"inboxdrawing",
"oldsoutharabian",
- "hyphen",
"inegyptianhieroglyphs",
"inegyptianhieroglyphformatcontrols",
"tagb",
+ "rejang",
"tglg",
"tagalog",
"othergraphemeextend",
"insupplementaryprivateuseareaa",
"inhighsurrogates",
+ "hebrew",
"duployan",
"graphemeclusterbreak=v",
"graphemeclusterbreak=lv",
"insupplementalarrowsb",
+ "graphemeclusterbreak=zwj",
"telugu",
"zyyy",
"olduyghur",
"inhangulcompatibilityjamo",
"openpunctuation",
+ "hyphen",
"insupplementalsymbolsandpictographs",
"egyp",
"nyiakengpuachuehmong",
@@ -42639,13 +43730,13 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str48), 95},
{uniname2ctype_offset(str49), 95},
{-1}, {-1},
- {uniname2ctype_offset(str52), 343},
+ {uniname2ctype_offset(str52), 346},
{-1}, {-1},
{uniname2ctype_offset(str55), 21},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str64), 44},
{-1},
- {uniname2ctype_offset(str66), 330},
+ {uniname2ctype_offset(str66), 333},
{uniname2ctype_offset(str67), 52},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str71), 181},
@@ -42663,9 +43754,9 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str94), 33},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str100), 149},
- {uniname2ctype_offset(str101), 510},
+ {uniname2ctype_offset(str101), 513},
{uniname2ctype_offset(str102), 108},
- {uniname2ctype_offset(str103), 261},
+ {uniname2ctype_offset(str103), 263},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str107), 31},
{uniname2ctype_offset(str108), 77},
@@ -42684,27 +43775,27 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str130), 42},
{uniname2ctype_offset(str131), 172},
{-1}, {-1},
- {uniname2ctype_offset(str134), 494},
+ {uniname2ctype_offset(str134), 497},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str139), 170},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str145), 513},
- {uniname2ctype_offset(str146), 569},
+ {uniname2ctype_offset(str145), 516},
+ {uniname2ctype_offset(str146), 575},
{-1},
- {uniname2ctype_offset(str148), 574},
- {uniname2ctype_offset(str149), 531},
+ {uniname2ctype_offset(str148), 580},
+ {uniname2ctype_offset(str149), 535},
{-1},
{uniname2ctype_offset(str151), 18},
{uniname2ctype_offset(str152), 169},
{uniname2ctype_offset(str153), 160},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str158), 276},
+ {uniname2ctype_offset(str158), 278},
{-1}, {-1},
- {uniname2ctype_offset(str161), 324},
+ {uniname2ctype_offset(str161), 327},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str177), 349},
- {uniname2ctype_offset(str178), 558},
+ {uniname2ctype_offset(str177), 352},
+ {uniname2ctype_offset(str178), 563},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str183), 75},
{-1}, {-1},
@@ -42713,19 +43804,19 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str190), 208},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str203), 357},
- {uniname2ctype_offset(str204), 485},
+ {uniname2ctype_offset(str203), 360},
+ {uniname2ctype_offset(str204), 488},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str210), 575},
+ {uniname2ctype_offset(str210), 581},
{-1},
- {uniname2ctype_offset(str212), 362},
+ {uniname2ctype_offset(str212), 365},
{uniname2ctype_offset(str213), 115},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str218), 545},
+ {uniname2ctype_offset(str218), 549},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str226), 171},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str230), 526},
+ {uniname2ctype_offset(str230), 530},
{uniname2ctype_offset(str231), 31},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str236), 25},
@@ -42737,64 +43828,64 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1},
{uniname2ctype_offset(str253), 102},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str260), 562},
+ {uniname2ctype_offset(str260), 568},
{-1}, {-1},
{uniname2ctype_offset(str263), 161},
{-1},
{uniname2ctype_offset(str265), 19},
{-1},
{uniname2ctype_offset(str267), 79},
- {uniname2ctype_offset(str268), 354},
+ {uniname2ctype_offset(str268), 357},
{-1},
- {uniname2ctype_offset(str270), 268},
+ {uniname2ctype_offset(str270), 270},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str274), 561},
- {uniname2ctype_offset(str275), 514},
+ {uniname2ctype_offset(str274), 567},
+ {uniname2ctype_offset(str275), 517},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str281), 318},
+ {uniname2ctype_offset(str281), 321},
{uniname2ctype_offset(str282), 40},
{uniname2ctype_offset(str283), 79},
{-1},
- {uniname2ctype_offset(str285), 533},
+ {uniname2ctype_offset(str285), 537},
{-1},
{uniname2ctype_offset(str287), 144},
{uniname2ctype_offset(str288), 144},
- {uniname2ctype_offset(str289), 555},
+ {uniname2ctype_offset(str289), 560},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str293), 218},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str297), 212},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str301), 392},
+ {uniname2ctype_offset(str301), 395},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str311), 322},
+ {uniname2ctype_offset(str311), 325},
{-1},
- {uniname2ctype_offset(str313), 453},
+ {uniname2ctype_offset(str313), 456},
{-1},
- {uniname2ctype_offset(str315), 241},
+ {uniname2ctype_offset(str315), 243},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str320), 269},
+ {uniname2ctype_offset(str320), 271},
{-1},
{uniname2ctype_offset(str322), 129},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str328), 331},
+ {uniname2ctype_offset(str328), 334},
{-1}, {-1},
{uniname2ctype_offset(str331), 76},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str335), 556},
+ {uniname2ctype_offset(str335), 561},
{-1}, {-1},
- {uniname2ctype_offset(str338), 329},
+ {uniname2ctype_offset(str338), 332},
{-1},
{uniname2ctype_offset(str340), 76},
{-1},
- {uniname2ctype_offset(str342), 346},
+ {uniname2ctype_offset(str342), 349},
{-1}, {-1},
{uniname2ctype_offset(str345), 53},
- {uniname2ctype_offset(str346), 268},
+ {uniname2ctype_offset(str346), 270},
{-1},
- {uniname2ctype_offset(str348), 423},
+ {uniname2ctype_offset(str348), 426},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str352), 529},
+ {uniname2ctype_offset(str352), 533},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str362), 163},
{-1}, {-1}, {-1},
@@ -42802,14 +43893,14 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str373), 160},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str381), 550},
+ {uniname2ctype_offset(str381), 554},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str386), 368},
+ {uniname2ctype_offset(str386), 371},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str399), 327},
+ {uniname2ctype_offset(str399), 330},
{-1},
- {uniname2ctype_offset(str401), 544},
+ {uniname2ctype_offset(str401), 548},
{-1}, {-1},
{uniname2ctype_offset(str404), 81},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -42832,176 +43923,179 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1},
{uniname2ctype_offset(str470), 22},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str475), 521},
+ {uniname2ctype_offset(str475), 524},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str480), 454},
+ {uniname2ctype_offset(str480), 457},
{uniname2ctype_offset(str481), 188},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str486), 473},
+ {uniname2ctype_offset(str486), 476},
{-1},
- {uniname2ctype_offset(str488), 582},
+ {uniname2ctype_offset(str488), 588},
{-1}, {-1},
- {uniname2ctype_offset(str491), 467},
+ {uniname2ctype_offset(str491), 470},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str500), 127},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str504), 187},
- {uniname2ctype_offset(str505), 247},
+ {uniname2ctype_offset(str505), 249},
{uniname2ctype_offset(str506), 24},
{-1}, {-1},
{uniname2ctype_offset(str509), 24},
{-1},
- {uniname2ctype_offset(str511), 460},
+ {uniname2ctype_offset(str511), 463},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str520), 420},
+ {uniname2ctype_offset(str520), 423},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str533), 230},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str538), 91},
{-1}, {-1},
- {uniname2ctype_offset(str541), 549},
+ {uniname2ctype_offset(str541), 553},
{-1},
{uniname2ctype_offset(str543), 91},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str556), 542},
+ {uniname2ctype_offset(str556), 546},
{-1},
- {uniname2ctype_offset(str558), 347},
+ {uniname2ctype_offset(str558), 350},
{uniname2ctype_offset(str559), 70},
- {uniname2ctype_offset(str560), 512},
+ {uniname2ctype_offset(str560), 515},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str565), 615},
+ {uniname2ctype_offset(str565), 624},
{uniname2ctype_offset(str566), 37},
{-1},
{uniname2ctype_offset(str568), 113},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str572), 499},
+ {uniname2ctype_offset(str572), 502},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str577), 602},
+ {uniname2ctype_offset(str577), 611},
{-1},
{uniname2ctype_offset(str579), 106},
{-1}, {-1},
- {uniname2ctype_offset(str582), 403},
- {uniname2ctype_offset(str583), 477},
+ {uniname2ctype_offset(str582), 406},
+ {uniname2ctype_offset(str583), 480},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str590), 74},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str594), 168},
{-1},
- {uniname2ctype_offset(str596), 613},
+ {uniname2ctype_offset(str596), 622},
{uniname2ctype_offset(str597), 146},
{-1}, {-1},
- {uniname2ctype_offset(str600), 487},
+ {uniname2ctype_offset(str600), 490},
{-1},
{uniname2ctype_offset(str602), 70},
{-1},
- {uniname2ctype_offset(str604), 573},
- {uniname2ctype_offset(str605), 620},
+ {uniname2ctype_offset(str604), 579},
+ {uniname2ctype_offset(str605), 629},
{-1}, {-1},
- {uniname2ctype_offset(str608), 628},
+ {uniname2ctype_offset(str608), 637},
{uniname2ctype_offset(str609), 229},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str614), 603},
+ {uniname2ctype_offset(str614), 612},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str624), 195},
- {uniname2ctype_offset(str625), 444},
+ {uniname2ctype_offset(str625), 447},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str630), 29},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str636), 539},
+ {uniname2ctype_offset(str636), 543},
{-1}, {-1},
{uniname2ctype_offset(str639), 49},
{-1}, {-1},
{uniname2ctype_offset(str642), 19},
- {-1}, {-1},
- {uniname2ctype_offset(str645), 482},
+ {uniname2ctype_offset(str643), 564},
+ {-1},
+ {uniname2ctype_offset(str645), 485},
{-1},
{uniname2ctype_offset(str647), 192},
{-1}, {-1},
- {uniname2ctype_offset(str650), 484},
+ {uniname2ctype_offset(str650), 487},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str657), 51},
{-1}, {-1},
- {uniname2ctype_offset(str660), 266},
+ {uniname2ctype_offset(str660), 268},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str672), 332},
+ {uniname2ctype_offset(str672), 335},
{-1}, {-1},
{uniname2ctype_offset(str675), 68},
{-1}, {-1},
{uniname2ctype_offset(str678), 171},
- {uniname2ctype_offset(str679), 599},
+ {uniname2ctype_offset(str679), 607},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str683), 265},
+ {uniname2ctype_offset(str683), 267},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str692), 69},
{-1}, {-1},
- {uniname2ctype_offset(str695), 547},
+ {uniname2ctype_offset(str695), 551},
{uniname2ctype_offset(str696), 175},
- {uniname2ctype_offset(str697), 396},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str703), 523},
+ {uniname2ctype_offset(str697), 399},
+ {-1}, {-1},
+ {uniname2ctype_offset(str700), 236},
+ {-1}, {-1},
+ {uniname2ctype_offset(str703), 527},
{-1},
- {uniname2ctype_offset(str705), 344},
+ {uniname2ctype_offset(str705), 347},
{-1},
{uniname2ctype_offset(str707), 158},
- {uniname2ctype_offset(str708), 581},
+ {uniname2ctype_offset(str708), 587},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str713), 373},
+ {uniname2ctype_offset(str713), 376},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str720), 72},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str737), 7},
- {uniname2ctype_offset(str738), 370},
+ {uniname2ctype_offset(str738), 373},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str742), 6},
{-1}, {-1},
- {uniname2ctype_offset(str745), 267},
+ {uniname2ctype_offset(str745), 269},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str753), 238},
+ {uniname2ctype_offset(str753), 240},
{-1},
- {uniname2ctype_offset(str755), 511},
+ {uniname2ctype_offset(str755), 514},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str760), 428},
+ {uniname2ctype_offset(str760), 431},
{uniname2ctype_offset(str761), 167},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str766), 156},
- {uniname2ctype_offset(str767), 600},
+ {uniname2ctype_offset(str767), 608},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str771), 167},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str776), 266},
+ {uniname2ctype_offset(str776), 268},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str783), 156},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str787), 254},
+ {uniname2ctype_offset(str787), 256},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str791), 193},
{-1}, {-1},
- {uniname2ctype_offset(str794), 583},
+ {uniname2ctype_offset(str794), 589},
{-1}, {-1},
{uniname2ctype_offset(str797), 50},
{-1},
- {uniname2ctype_offset(str799), 608},
+ {uniname2ctype_offset(str799), 617},
{-1}, {-1},
{uniname2ctype_offset(str802), 13},
- {uniname2ctype_offset(str803), 587},
+ {uniname2ctype_offset(str803), 593},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str809), 443},
+ {uniname2ctype_offset(str809), 446},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str814), 490},
+ {uniname2ctype_offset(str814), 493},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str818), 395},
+ {uniname2ctype_offset(str818), 398},
{-1}, {-1},
- {uniname2ctype_offset(str821), 479},
- {uniname2ctype_offset(str822), 589},
+ {uniname2ctype_offset(str821), 482},
+ {uniname2ctype_offset(str822), 595},
{uniname2ctype_offset(str823), 47},
{uniname2ctype_offset(str824), 112},
- {uniname2ctype_offset(str825), 441},
+ {uniname2ctype_offset(str825), 444},
{-1}, {-1},
- {uniname2ctype_offset(str828), 590},
+ {uniname2ctype_offset(str828), 596},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str838), 157},
{-1}, {-1}, {-1},
@@ -43014,32 +44108,32 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str855), 67},
{-1},
- {uniname2ctype_offset(str857), 316},
+ {uniname2ctype_offset(str857), 319},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str875), 366},
+ {uniname2ctype_offset(str875), 369},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str886), 401},
+ {uniname2ctype_offset(str886), 404},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str895), 409},
+ {uniname2ctype_offset(str895), 412},
{-1}, {-1},
- {uniname2ctype_offset(str898), 497},
+ {uniname2ctype_offset(str898), 500},
{-1},
- {uniname2ctype_offset(str900), 612},
+ {uniname2ctype_offset(str900), 621},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str906), 518},
- {uniname2ctype_offset(str907), 446},
+ {uniname2ctype_offset(str906), 521},
+ {uniname2ctype_offset(str907), 449},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str920), 416},
+ {uniname2ctype_offset(str920), 419},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str924), 68},
- {uniname2ctype_offset(str925), 592},
- {uniname2ctype_offset(str926), 341},
+ {uniname2ctype_offset(str925), 599},
+ {uniname2ctype_offset(str926), 344},
{-1},
- {uniname2ctype_offset(str928), 536},
- {uniname2ctype_offset(str929), 458},
+ {uniname2ctype_offset(str928), 540},
+ {uniname2ctype_offset(str929), 461},
{uniname2ctype_offset(str930), 41},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -43048,13 +44142,13 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str961), 2},
{-1},
- {uniname2ctype_offset(str963), 255},
+ {uniname2ctype_offset(str963), 257},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str974), 507},
+ {uniname2ctype_offset(str974), 510},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str986), 367},
+ {uniname2ctype_offset(str986), 370},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str994), 85},
{uniname2ctype_offset(str995), 104},
@@ -43062,21 +44156,21 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1010), 26},
{-1}, {-1},
- {uniname2ctype_offset(str1013), 492},
+ {uniname2ctype_offset(str1013), 495},
{-1},
- {uniname2ctype_offset(str1015), 481},
+ {uniname2ctype_offset(str1015), 484},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1021), 67},
{-1}, {-1},
{uniname2ctype_offset(str1024), 53},
- {uniname2ctype_offset(str1025), 456},
+ {uniname2ctype_offset(str1025), 459},
{-1}, {-1},
{uniname2ctype_offset(str1028), 136},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1032), 377},
+ {uniname2ctype_offset(str1032), 380},
{-1}, {-1},
- {uniname2ctype_offset(str1035), 319},
- {uniname2ctype_offset(str1036), 563},
+ {uniname2ctype_offset(str1035), 322},
+ {uniname2ctype_offset(str1036), 569},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1049), 173},
@@ -43086,26 +44180,26 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1072), 197},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1081), 411},
+ {uniname2ctype_offset(str1081), 414},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1085), 257},
+ {uniname2ctype_offset(str1085), 259},
{-1},
- {uniname2ctype_offset(str1087), 593},
+ {uniname2ctype_offset(str1087), 600},
{-1},
{uniname2ctype_offset(str1089), 115},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1095), 248},
+ {uniname2ctype_offset(str1095), 250},
{uniname2ctype_offset(str1096), 71},
- {uniname2ctype_offset(str1097), 537},
+ {uniname2ctype_offset(str1097), 541},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1102), 520},
+ {uniname2ctype_offset(str1102), 523},
{-1},
{uniname2ctype_offset(str1104), 228},
{uniname2ctype_offset(str1105), 217},
{-1},
- {uniname2ctype_offset(str1107), 538},
+ {uniname2ctype_offset(str1107), 542},
{-1},
- {uniname2ctype_offset(str1109), 237},
+ {uniname2ctype_offset(str1109), 239},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1114), 69},
{uniname2ctype_offset(str1115), 11},
@@ -43114,7 +44208,7 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str1120), 60},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1134), 422},
+ {uniname2ctype_offset(str1134), 425},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1140), 93},
{-1},
@@ -43122,153 +44216,155 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1},
{uniname2ctype_offset(str1145), 137},
{uniname2ctype_offset(str1146), 131},
- {uniname2ctype_offset(str1147), 264},
+ {uniname2ctype_offset(str1147), 266},
{-1},
{uniname2ctype_offset(str1149), 158},
{uniname2ctype_offset(str1150), 98},
- {uniname2ctype_offset(str1151), 495},
+ {uniname2ctype_offset(str1151), 498},
{uniname2ctype_offset(str1152), 217},
{uniname2ctype_offset(str1153), 138},
{-1}, {-1},
- {uniname2ctype_offset(str1156), 525},
+ {uniname2ctype_offset(str1156), 529},
{uniname2ctype_offset(str1157), 203},
{uniname2ctype_offset(str1158), 166},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1164), 238},
+ {uniname2ctype_offset(str1164), 240},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1168), 104},
{-1},
- {uniname2ctype_offset(str1170), 386},
- {uniname2ctype_offset(str1171), 532},
+ {uniname2ctype_offset(str1170), 389},
+ {uniname2ctype_offset(str1171), 536},
{-1}, {-1},
- {uniname2ctype_offset(str1174), 323},
+ {uniname2ctype_offset(str1174), 326},
{uniname2ctype_offset(str1175), 26},
{uniname2ctype_offset(str1176), 208},
{uniname2ctype_offset(str1177), 74},
- {uniname2ctype_offset(str1178), 350},
+ {uniname2ctype_offset(str1178), 353},
{-1},
{uniname2ctype_offset(str1180), 183},
{uniname2ctype_offset(str1181), 151},
- {uniname2ctype_offset(str1182), 356},
+ {uniname2ctype_offset(str1182), 359},
{uniname2ctype_offset(str1183), 101},
{-1},
{uniname2ctype_offset(str1185), 170},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1190), 270},
- {uniname2ctype_offset(str1191), 541},
+ {uniname2ctype_offset(str1186), 597},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1190), 272},
+ {uniname2ctype_offset(str1191), 545},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1197), 101},
{uniname2ctype_offset(str1198), 135},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1203), 363},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1207), 459},
+ {uniname2ctype_offset(str1203), 366},
+ {uniname2ctype_offset(str1204), 609},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1207), 462},
{-1},
{uniname2ctype_offset(str1209), 186},
{-1}, {-1},
- {uniname2ctype_offset(str1212), 376},
+ {uniname2ctype_offset(str1212), 379},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str1223), 578},
+ {uniname2ctype_offset(str1223), 584},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1227), 241},
+ {uniname2ctype_offset(str1227), 243},
{-1},
{uniname2ctype_offset(str1229), 235},
- {uniname2ctype_offset(str1230), 265},
+ {uniname2ctype_offset(str1230), 267},
{uniname2ctype_offset(str1231), 206},
- {uniname2ctype_offset(str1232), 352},
+ {uniname2ctype_offset(str1232), 355},
{uniname2ctype_offset(str1233), 73},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1234), 296},
- {uniname2ctype_offset(str1235), 298},
- {uniname2ctype_offset(str1236), 295},
- {uniname2ctype_offset(str1237), 297},
+ {uniname2ctype_offset(str1234), 298},
+ {uniname2ctype_offset(str1235), 300},
+ {uniname2ctype_offset(str1236), 297},
+ {uniname2ctype_offset(str1237), 299},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1241), 299},
- {uniname2ctype_offset(str1242), 277},
+ {uniname2ctype_offset(str1241), 301},
+ {uniname2ctype_offset(str1242), 279},
#endif /* USE_UNICODE_AGE_PROPERTIES */
{uniname2ctype_offset(str1243), 25},
- {uniname2ctype_offset(str1244), 338},
+ {uniname2ctype_offset(str1244), 341},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1245), 279},
+ {uniname2ctype_offset(str1245), 281},
#endif /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1246), 432},
+ {uniname2ctype_offset(str1246), 435},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1247), 278},
+ {uniname2ctype_offset(str1247), 280},
#endif /* USE_UNICODE_AGE_PROPERTIES */
{uniname2ctype_offset(str1248), 30},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1}, {-1}, {-1}, {-1}, {-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1249), 281},
- {uniname2ctype_offset(str1250), 294},
- {uniname2ctype_offset(str1251), 280},
- {uniname2ctype_offset(str1252), 282},
- {uniname2ctype_offset(str1253), 293},
+ {uniname2ctype_offset(str1249), 283},
+ {uniname2ctype_offset(str1250), 296},
+ {uniname2ctype_offset(str1251), 282},
+ {uniname2ctype_offset(str1252), 284},
+ {uniname2ctype_offset(str1253), 295},
#endif /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1254), 560},
+ {uniname2ctype_offset(str1254), 566},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1255), 289},
+ {uniname2ctype_offset(str1255), 291},
#endif /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1256), 276},
+ {uniname2ctype_offset(str1256), 278},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1263), 64},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1257), 288},
- {uniname2ctype_offset(str1258), 290},
- {uniname2ctype_offset(str1259), 292},
+ {uniname2ctype_offset(str1257), 290},
+ {uniname2ctype_offset(str1258), 292},
+ {uniname2ctype_offset(str1259), 303},
+ {uniname2ctype_offset(str1260), 294},
+ {-1},
+ {uniname2ctype_offset(str1262), 293},
#endif /* USE_UNICODE_AGE_PROPERTIES */
- {-1}, {-1},
-#ifdef USE_UNICODE_AGE_PROPERTIES
- {uniname2ctype_offset(str1262), 291},
{uniname2ctype_offset(str1263), 64},
+#ifndef USE_UNICODE_AGE_PROPERTIES
+ {-1}, {-1},
+#else /* USE_UNICODE_AGE_PROPERTIES */
{-1},
- {uniname2ctype_offset(str1265), 286},
+ {uniname2ctype_offset(str1265), 302},
#endif /* USE_UNICODE_AGE_PROPERTIES */
{uniname2ctype_offset(str1266), 21},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1}, {-1}, {-1}, {-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1267), 285},
- {uniname2ctype_offset(str1268), 287},
- {uniname2ctype_offset(str1269), 300},
+ {uniname2ctype_offset(str1267), 288},
{-1},
+ {uniname2ctype_offset(str1269), 287},
+ {uniname2ctype_offset(str1270), 289},
#endif /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1271), 248},
+ {uniname2ctype_offset(str1271), 250},
+#ifndef USE_UNICODE_AGE_PROPERTIES
{-1}, {-1},
+#else /* USE_UNICODE_AGE_PROPERTIES */
+ {-1},
+ {uniname2ctype_offset(str1273), 286},
+#endif /* USE_UNICODE_AGE_PROPERTIES */
{uniname2ctype_offset(str1274), 200},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {-1}, {-1},
- {uniname2ctype_offset(str1277), 284},
- {-1},
- {uniname2ctype_offset(str1279), 283},
- {-1}, {-1},
+ {uniname2ctype_offset(str1275), 285},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#endif /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1282), 626},
- {-1},
- {uniname2ctype_offset(str1284), 110},
+ {uniname2ctype_offset(str1282), 635},
+ {-1}, {-1},
{uniname2ctype_offset(str1285), 105},
- {-1},
- {uniname2ctype_offset(str1287), 110},
- {-1},
- {uniname2ctype_offset(str1289), 345},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1289), 348},
{-1}, {-1},
- {uniname2ctype_offset(str1292), 394},
+ {uniname2ctype_offset(str1292), 397},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1297), 145},
- {uniname2ctype_offset(str1298), 117},
- {-1},
+ {-1}, {-1},
{uniname2ctype_offset(str1300), 86},
{uniname2ctype_offset(str1301), 141},
{-1}, {-1}, {-1}, {-1},
@@ -43277,14 +44373,11 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str1314), 193},
{-1}, {-1},
{uniname2ctype_offset(str1317), 86},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1323), 535},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1327), 117},
- {uniname2ctype_offset(str1328), 227},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str1330), 107},
- {uniname2ctype_offset(str1331), 261},
+ {uniname2ctype_offset(str1328), 227},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1331), 263},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1340), 105},
{-1}, {-1},
@@ -43295,18 +44388,20 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str1352), 88},
{-1},
{uniname2ctype_offset(str1354), 164},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1358), 624},
+ {-1},
+ {uniname2ctype_offset(str1356), 605},
+ {-1},
+ {uniname2ctype_offset(str1358), 633},
{-1},
{uniname2ctype_offset(str1360), 3},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1365), 384},
+ {uniname2ctype_offset(str1365), 387},
{-1},
- {uniname2ctype_offset(str1367), 530},
+ {uniname2ctype_offset(str1367), 534},
{-1},
- {uniname2ctype_offset(str1369), 256},
+ {uniname2ctype_offset(str1369), 258},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1373), 272},
+ {uniname2ctype_offset(str1373), 274},
{-1},
{uniname2ctype_offset(str1375), 135},
{-1}, {-1}, {-1}, {-1},
@@ -43319,29 +44414,29 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str1392), 138},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1400), 201},
- {uniname2ctype_offset(str1401), 397},
+ {uniname2ctype_offset(str1401), 400},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1407), 224},
{-1},
{uniname2ctype_offset(str1409), 38},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1415), 570},
+ {uniname2ctype_offset(str1415), 576},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1419), 140},
{uniname2ctype_offset(str1420), 140},
{-1},
- {uniname2ctype_offset(str1422), 321},
+ {uniname2ctype_offset(str1422), 324},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1426), 39},
{-1},
{uniname2ctype_offset(str1428), 181},
{uniname2ctype_offset(str1429), 36},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1438), 434},
+ {uniname2ctype_offset(str1438), 437},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1442), 540},
+ {uniname2ctype_offset(str1442), 544},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1448), 505},
+ {uniname2ctype_offset(str1448), 508},
{uniname2ctype_offset(str1449), 122},
{-1},
{uniname2ctype_offset(str1451), 203},
@@ -43352,109 +44447,106 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1},
{uniname2ctype_offset(str1460), 215},
{-1},
- {uniname2ctype_offset(str1462), 554},
+ {uniname2ctype_offset(str1462), 559},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1470), 504},
- {uniname2ctype_offset(str1471), 506},
+ {uniname2ctype_offset(str1470), 507},
+ {uniname2ctype_offset(str1471), 509},
{-1}, {-1},
{uniname2ctype_offset(str1474), 134},
- {uniname2ctype_offset(str1475), 426},
- {uniname2ctype_offset(str1476), 508},
+ {uniname2ctype_offset(str1475), 429},
+ {uniname2ctype_offset(str1476), 511},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1480), 245},
+ {uniname2ctype_offset(str1480), 247},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1489), 33},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1495), 260},
+ {uniname2ctype_offset(str1495), 262},
{-1},
- {uniname2ctype_offset(str1497), 496},
+ {uniname2ctype_offset(str1497), 499},
{-1},
- {uniname2ctype_offset(str1499), 611},
+ {uniname2ctype_offset(str1499), 620},
{-1},
{uniname2ctype_offset(str1501), 196},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1507), 122},
{uniname2ctype_offset(str1508), 231},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1517), 610},
+ {uniname2ctype_offset(str1517), 619},
{-1}, {-1},
- {uniname2ctype_offset(str1520), 237},
+ {uniname2ctype_offset(str1520), 239},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1524), 483},
+ {uniname2ctype_offset(str1524), 486},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1529), 120},
{-1},
- {uniname2ctype_offset(str1531), 419},
+ {uniname2ctype_offset(str1531), 422},
{-1},
{uniname2ctype_offset(str1533), 142},
{-1}, {-1},
{uniname2ctype_offset(str1536), 127},
- {uniname2ctype_offset(str1537), 269},
+ {uniname2ctype_offset(str1537), 271},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1542), 465},
+ {uniname2ctype_offset(str1542), 468},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1547), 168},
{-1},
- {uniname2ctype_offset(str1549), 519},
+ {uniname2ctype_offset(str1549), 522},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1557), 85},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1563), 273},
+ {uniname2ctype_offset(str1563), 275},
{-1},
- {uniname2ctype_offset(str1565), 326},
+ {uniname2ctype_offset(str1565), 329},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1570), 210},
{-1},
{uniname2ctype_offset(str1572), 115},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1577), 564},
+ {uniname2ctype_offset(str1577), 570},
{-1}, {-1},
{uniname2ctype_offset(str1580), 131},
{-1},
{uniname2ctype_offset(str1582), 219},
{uniname2ctype_offset(str1583), 125},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1587), 546},
+ {uniname2ctype_offset(str1587), 550},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1597), 81},
{uniname2ctype_offset(str1598), 219},
- {-1}, {-1},
- {uniname2ctype_offset(str1601), 202},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1607), 586},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1607), 592},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1613), 164},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1617), 502},
- {uniname2ctype_offset(str1618), 272},
+ {uniname2ctype_offset(str1617), 505},
+ {uniname2ctype_offset(str1618), 274},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1622), 385},
- {uniname2ctype_offset(str1623), 567},
+ {uniname2ctype_offset(str1622), 388},
+ {uniname2ctype_offset(str1623), 573},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1629), 39},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1635), 72},
- {uniname2ctype_offset(str1636), 202},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1643), 62},
{uniname2ctype_offset(str1644), 235},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1654), 400},
- {uniname2ctype_offset(str1655), 274},
+ {uniname2ctype_offset(str1654), 403},
+ {uniname2ctype_offset(str1655), 276},
{-1},
{uniname2ctype_offset(str1657), 114},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1662), 129},
{-1},
- {uniname2ctype_offset(str1664), 448},
+ {uniname2ctype_offset(str1664), 451},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1671), 340},
+ {uniname2ctype_offset(str1671), 343},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1675), 469},
+ {uniname2ctype_offset(str1675), 472},
{-1},
- {uniname2ctype_offset(str1677), 314},
+ {uniname2ctype_offset(str1677), 317},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1682), 625},
+ {uniname2ctype_offset(str1682), 634},
{-1},
{uniname2ctype_offset(str1684), 199},
{-1},
@@ -43463,186 +44555,192 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1691), 124},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1699), 379},
+ {uniname2ctype_offset(str1699), 382},
{-1},
- {uniname2ctype_offset(str1701), 522},
+ {uniname2ctype_offset(str1701), 525},
{-1}, {-1},
{uniname2ctype_offset(str1704), 207},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1714), 207},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1722), 359},
+ {uniname2ctype_offset(str1722), 362},
{-1},
- {uniname2ctype_offset(str1724), 576},
+ {uniname2ctype_offset(str1724), 582},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1728), 221},
- {uniname2ctype_offset(str1729), 442},
+ {uniname2ctype_offset(str1729), 445},
{uniname2ctype_offset(str1730), 222},
- {uniname2ctype_offset(str1731), 534},
- {uniname2ctype_offset(str1732), 247},
+ {uniname2ctype_offset(str1731), 538},
+ {uniname2ctype_offset(str1732), 249},
{uniname2ctype_offset(str1733), 123},
{uniname2ctype_offset(str1734), 114},
- {uniname2ctype_offset(str1735), 258},
+ {uniname2ctype_offset(str1735), 260},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1740), 129},
{-1},
{uniname2ctype_offset(str1742), 161},
{-1}, {-1},
- {uniname2ctype_offset(str1745), 524},
- {uniname2ctype_offset(str1746), 402},
+ {uniname2ctype_offset(str1745), 528},
+ {uniname2ctype_offset(str1746), 405},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1750), 20},
{-1},
- {uniname2ctype_offset(str1752), 516},
+ {uniname2ctype_offset(str1752), 519},
{uniname2ctype_offset(str1753), 148},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1757), 515},
+ {uniname2ctype_offset(str1757), 518},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1766), 73},
{-1},
{uniname2ctype_offset(str1768), 148},
- {uniname2ctype_offset(str1769), 374},
+ {uniname2ctype_offset(str1769), 377},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1776), 126},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1781), 552},
+ {uniname2ctype_offset(str1781), 556},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1788), 97},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1793), 97},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1806), 348},
+ {uniname2ctype_offset(str1806), 351},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1810), 630},
- {uniname2ctype_offset(str1811), 245},
+ {uniname2ctype_offset(str1810), 640},
+ {uniname2ctype_offset(str1811), 247},
{-1},
- {uniname2ctype_offset(str1813), 264},
+ {uniname2ctype_offset(str1813), 266},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1821), 224},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1828), 399},
+ {uniname2ctype_offset(str1828), 402},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1838), 614},
+ {uniname2ctype_offset(str1838), 623},
{-1}, {-1},
- {uniname2ctype_offset(str1841), 457},
- {uniname2ctype_offset(str1842), 391},
+ {uniname2ctype_offset(str1841), 460},
+ {uniname2ctype_offset(str1842), 394},
{uniname2ctype_offset(str1843), 65},
{-1},
- {uniname2ctype_offset(str1845), 263},
+ {uniname2ctype_offset(str1845), 265},
{-1}, {-1},
{uniname2ctype_offset(str1848), 109},
{-1}, {-1},
{uniname2ctype_offset(str1851), 137},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1857), 244},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1866), 61},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1871), 491},
+ {uniname2ctype_offset(str1871), 494},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1881), 63},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1888), 527},
- {uniname2ctype_offset(str1889), 551},
+ {uniname2ctype_offset(str1888), 531},
+ {uniname2ctype_offset(str1889), 555},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1896), 617},
+ {uniname2ctype_offset(str1896), 626},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1902), 431},
+ {uniname2ctype_offset(str1902), 434},
{-1},
- {uniname2ctype_offset(str1904), 452},
- {uniname2ctype_offset(str1905), 584},
+ {uniname2ctype_offset(str1904), 455},
+ {uniname2ctype_offset(str1905), 590},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1912), 580},
+ {uniname2ctype_offset(str1912), 586},
{uniname2ctype_offset(str1913), 143},
{-1}, {-1},
- {uniname2ctype_offset(str1916), 588},
+ {uniname2ctype_offset(str1916), 594},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1924), 143},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1929), 429},
+ {uniname2ctype_offset(str1929), 432},
{-1}, {-1},
- {uniname2ctype_offset(str1932), 412},
+ {uniname2ctype_offset(str1932), 415},
{-1}, {-1},
- {uniname2ctype_offset(str1935), 262},
- {uniname2ctype_offset(str1936), 335},
+ {uniname2ctype_offset(str1935), 264},
+ {uniname2ctype_offset(str1936), 338},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1941), 27},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1948), 87},
- {-1},
- {uniname2ctype_offset(str1950), 421},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1950), 424},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1956), 250},
- {uniname2ctype_offset(str1957), 87},
+ {uniname2ctype_offset(str1956), 252},
+ {-1},
{uniname2ctype_offset(str1958), 109},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1964), 437},
+ {uniname2ctype_offset(str1964), 440},
{-1}, {-1},
- {uniname2ctype_offset(str1967), 489},
- {uniname2ctype_offset(str1968), 634},
- {uniname2ctype_offset(str1969), 249},
+ {uniname2ctype_offset(str1967), 492},
+ {uniname2ctype_offset(str1968), 644},
+ {uniname2ctype_offset(str1969), 251},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1974), 355},
+ {uniname2ctype_offset(str1974), 358},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1981), 176},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1985), 405},
+ {uniname2ctype_offset(str1985), 408},
{-1}, {-1},
- {uniname2ctype_offset(str1988), 438},
+ {uniname2ctype_offset(str1988), 441},
{-1}, {-1},
{uniname2ctype_offset(str1991), 1},
{-1},
- {uniname2ctype_offset(str1993), 372},
+ {uniname2ctype_offset(str1993), 375},
{uniname2ctype_offset(str1994), 175},
{-1},
{uniname2ctype_offset(str1996), 42},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2002), 591},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2006), 407},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2010), 375},
- {uniname2ctype_offset(str2011), 267},
- {uniname2ctype_offset(str2012), 559},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2001), 110},
+ {uniname2ctype_offset(str2002), 598},
{-1},
- {uniname2ctype_offset(str2014), 333},
+ {uniname2ctype_offset(str2004), 110},
+ {-1},
+ {uniname2ctype_offset(str2006), 410},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2010), 378},
+ {uniname2ctype_offset(str2011), 269},
+ {uniname2ctype_offset(str2012), 565},
{-1},
+ {uniname2ctype_offset(str2014), 336},
+ {uniname2ctype_offset(str2015), 117},
{uniname2ctype_offset(str2016), 209},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2023), 32},
- {uniname2ctype_offset(str2024), 503},
+ {uniname2ctype_offset(str2024), 506},
{-1},
{uniname2ctype_offset(str2026), 176},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2033), 8},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2040), 539},
+ {-1},
{uniname2ctype_offset(str2042), 121},
{uniname2ctype_offset(str2043), 17},
+ {uniname2ctype_offset(str2044), 117},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2047), 107},
+ {uniname2ctype_offset(str2048), 526},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2062), 130},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2070), 9},
{-1}, {-1},
- {uniname2ctype_offset(str2073), 342},
+ {uniname2ctype_offset(str2073), 345},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2077), 150},
- {uniname2ctype_offset(str2078), 254},
+ {uniname2ctype_offset(str2078), 256},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str2090), 275},
+ {uniname2ctype_offset(str2090), 277},
{-1},
{uniname2ctype_offset(str2092), 150},
{-1}, {-1},
- {uniname2ctype_offset(str2095), 353},
+ {uniname2ctype_offset(str2095), 356},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2103), 162},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2110), 123},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2114), 436},
- {uniname2ctype_offset(str2115), 381},
+ {uniname2ctype_offset(str2114), 439},
+ {uniname2ctype_offset(str2115), 384},
{-1},
{uniname2ctype_offset(str2117), 174},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -43653,15 +44751,15 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1},
{uniname2ctype_offset(str2137), 12},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2141), 242},
+ {uniname2ctype_offset(str2141), 244},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2145), 52},
- {uniname2ctype_offset(str2146), 413},
+ {uniname2ctype_offset(str2146), 416},
{-1}, {-1},
{uniname2ctype_offset(str2149), 221},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2163), 249},
+ {uniname2ctype_offset(str2163), 251},
{-1},
{uniname2ctype_offset(str2165), 174},
{uniname2ctype_offset(str2166), 5},
@@ -43674,107 +44772,100 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str2182), 216},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2195), 351},
- {uniname2ctype_offset(str2196), 493},
+ {uniname2ctype_offset(str2195), 354},
+ {uniname2ctype_offset(str2196), 496},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
{uniname2ctype_offset(str2207), 196},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2211), 20},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2216), 364},
+ {uniname2ctype_offset(str2216), 367},
{uniname2ctype_offset(str2217), 128},
- {uniname2ctype_offset(str2218), 543},
+ {uniname2ctype_offset(str2218), 547},
{uniname2ctype_offset(str2219), 78},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2224), 450},
+ {uniname2ctype_offset(str2224), 453},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2228), 43},
{uniname2ctype_offset(str2229), 35},
{-1}, {-1},
- {uniname2ctype_offset(str2232), 382},
+ {uniname2ctype_offset(str2232), 385},
{uniname2ctype_offset(str2233), 216},
{uniname2ctype_offset(str2234), 92},
- {uniname2ctype_offset(str2235), 486},
+ {uniname2ctype_offset(str2235), 489},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2241), 92},
{-1}, {-1},
- {uniname2ctype_offset(str2244), 440},
+ {uniname2ctype_offset(str2244), 443},
{-1}, {-1},
{uniname2ctype_offset(str2247), 36},
- {uniname2ctype_offset(str2248), 595},
+ {uniname2ctype_offset(str2248), 602},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2276), 415},
+ {uniname2ctype_offset(str2276), 418},
{-1},
{uniname2ctype_offset(str2278), 124},
{uniname2ctype_offset(str2279), 192},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2283), 214},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2296), 639},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2305), 205},
{-1}, {-1},
{uniname2ctype_offset(str2308), 234},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2312), 177},
{-1}, {-1},
- {uniname2ctype_offset(str2315), 594},
- {uniname2ctype_offset(str2316), 629},
- {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2315), 601},
+ {uniname2ctype_offset(str2316), 638},
+ {-1},
+ {uniname2ctype_offset(str2318), 202},
+ {-1}, {-1},
{uniname2ctype_offset(str2321), 153},
{-1},
{uniname2ctype_offset(str2323), 172},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2327), 378},
+ {uniname2ctype_offset(str2327), 381},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2331), 627},
+ {uniname2ctype_offset(str2331), 636},
{-1},
{uniname2ctype_offset(str2333), 134},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2341), 185},
- {-1},
- {uniname2ctype_offset(str2343), 585},
- {uniname2ctype_offset(str2344), 439},
- {uniname2ctype_offset(str2345), 239},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2350), 185},
- {uniname2ctype_offset(str2351), 99},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2361), 598},
- {-1}, {-1},
- {uniname2ctype_offset(str2364), 320},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2370), 339},
+ {uniname2ctype_offset(str2343), 591},
+ {uniname2ctype_offset(str2344), 442},
+ {uniname2ctype_offset(str2345), 241},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2353), 202},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2361), 606},
{-1}, {-1},
+ {uniname2ctype_offset(str2364), 323},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2373), 37},
- {uniname2ctype_offset(str2374), 389},
+ {uniname2ctype_offset(str2374), 392},
{uniname2ctype_offset(str2375), 197},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2389), 557},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2397), 418},
- {uniname2ctype_offset(str2398), 410},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2397), 421},
+ {uniname2ctype_offset(str2398), 413},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2405), 165},
- {uniname2ctype_offset(str2406), 107},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2414), 82},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2418), 449},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2427), 23},
- {uniname2ctype_offset(str2428), 183},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2433), 336},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2433), 339},
{uniname2ctype_offset(str2434), 154},
{-1}, {-1},
{uniname2ctype_offset(str2437), 194},
- {uniname2ctype_offset(str2438), 182},
- {-1},
+ {-1}, {-1},
{uniname2ctype_offset(str2440), 100},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
@@ -43782,36 +44873,36 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1},
{uniname2ctype_offset(str2454), 220},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2460), 255},
+ {uniname2ctype_offset(str2460), 257},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2464), 159},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2470), 445},
+ {uniname2ctype_offset(str2470), 448},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2475), 142},
- {uniname2ctype_offset(str2476), 430},
+ {uniname2ctype_offset(str2476), 558},
{-1},
- {uniname2ctype_offset(str2478), 414},
- {uniname2ctype_offset(str2479), 358},
+ {uniname2ctype_offset(str2478), 417},
+ {uniname2ctype_offset(str2479), 361},
{-1},
- {uniname2ctype_offset(str2481), 383},
+ {uniname2ctype_offset(str2481), 386},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2500), 121},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2506), 528},
+ {uniname2ctype_offset(str2506), 532},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2511), 242},
+ {uniname2ctype_offset(str2511), 244},
{-1},
- {uniname2ctype_offset(str2513), 621},
+ {uniname2ctype_offset(str2513), 630},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2530), 82},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2534), 337},
+ {uniname2ctype_offset(str2534), 340},
+ {uniname2ctype_offset(str2535), 237},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {uniname2ctype_offset(str2545), 275},
+ {uniname2ctype_offset(str2545), 277},
{-1}, {-1},
{uniname2ctype_offset(str2548), 118},
{uniname2ctype_offset(str2549), 159},
@@ -43819,74 +44910,74 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str2551), 89},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2564), 369},
+ {uniname2ctype_offset(str2564), 372},
{-1}, {-1},
- {uniname2ctype_offset(str2567), 315},
+ {uniname2ctype_offset(str2567), 318},
{-1}, {-1},
- {uniname2ctype_offset(str2570), 488},
+ {uniname2ctype_offset(str2570), 491},
{-1},
- {uniname2ctype_offset(str2572), 470},
- {-1}, {-1},
- {uniname2ctype_offset(str2575), 471},
- {uniname2ctype_offset(str2576), 273},
+ {uniname2ctype_offset(str2572), 473},
+ {-1},
+ {uniname2ctype_offset(str2574), 246},
+ {uniname2ctype_offset(str2575), 474},
+ {uniname2ctype_offset(str2576), 275},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2582), 388},
+ {uniname2ctype_offset(str2582), 391},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2588), 154},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2602), 210},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2606), 371},
+ {uniname2ctype_offset(str2606), 374},
{-1},
- {uniname2ctype_offset(str2608), 455},
+ {uniname2ctype_offset(str2608), 458},
{uniname2ctype_offset(str2609), 55},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2625), 260},
+ {uniname2ctype_offset(str2625), 262},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2648), 252},
+ {uniname2ctype_offset(str2648), 254},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2652), 380},
+ {uniname2ctype_offset(str2652), 383},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2656), 365},
+ {uniname2ctype_offset(str2656), 368},
{uniname2ctype_offset(str2657), 112},
- {-1}, {-1},
- {uniname2ctype_offset(str2660), 147},
- {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2665), 87},
{uniname2ctype_offset(str2666), 50},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2670), 623},
+ {uniname2ctype_offset(str2670), 632},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2674), 87},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2688), 139},
- {uniname2ctype_offset(str2689), 631},
+ {uniname2ctype_offset(str2689), 641},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2694), 48},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str2705), 478},
+ {uniname2ctype_offset(str2705), 481},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
{uniname2ctype_offset(str2726), 226},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {uniname2ctype_offset(str2738), 225},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {uniname2ctype_offset(str2750), 553},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2750), 557},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
{uniname2ctype_offset(str2761), 18},
- {uniname2ctype_offset(str2762), 263},
+ {uniname2ctype_offset(str2762), 265},
{-1}, {-1},
{uniname2ctype_offset(str2765), 133},
{uniname2ctype_offset(str2766), 54},
{-1}, {-1},
- {uniname2ctype_offset(str2769), 480},
+ {uniname2ctype_offset(str2769), 483},
{-1}, {-1},
{uniname2ctype_offset(str2772), 199},
{-1}, {-1},
@@ -43894,94 +44985,87 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
{uniname2ctype_offset(str2786), 4},
- {uniname2ctype_offset(str2787), 80},
- {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2793), 34},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2801), 29},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2808), 509},
+ {uniname2ctype_offset(str2808), 512},
{-1},
{uniname2ctype_offset(str2810), 226},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2816), 307},
+ {uniname2ctype_offset(str2816), 310},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2821), 390},
+ {uniname2ctype_offset(str2821), 393},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2830), 605},
+ {uniname2ctype_offset(str2830), 614},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2834), 361},
+ {uniname2ctype_offset(str2834), 364},
{-1},
{uniname2ctype_offset(str2836), 78},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str2847), 246},
+ {uniname2ctype_offset(str2847), 248},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2852), 302},
+ {uniname2ctype_offset(str2852), 305},
{-1}, {-1},
{uniname2ctype_offset(str2855), 190},
{uniname2ctype_offset(str2856), 66},
{-1}, {-1},
- {uniname2ctype_offset(str2859), 424},
+ {uniname2ctype_offset(str2859), 427},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2867), 306},
- {uniname2ctype_offset(str2868), 246},
+ {uniname2ctype_offset(str2867), 309},
+ {uniname2ctype_offset(str2868), 248},
{-1}, {-1},
- {uniname2ctype_offset(str2871), 252},
+ {uniname2ctype_offset(str2871), 254},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2879), 204},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2886), 622},
+ {uniname2ctype_offset(str2886), 631},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2901), 99},
- {uniname2ctype_offset(str2902), 474},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2902), 477},
{-1},
- {uniname2ctype_offset(str2904), 461},
- {uniname2ctype_offset(str2905), 271},
+ {uniname2ctype_offset(str2904), 464},
+ {uniname2ctype_offset(str2905), 273},
{-1},
- {uniname2ctype_offset(str2907), 271},
+ {uniname2ctype_offset(str2907), 273},
{-1},
- {uniname2ctype_offset(str2909), 571},
+ {uniname2ctype_offset(str2909), 577},
{-1},
- {uniname2ctype_offset(str2911), 447},
+ {uniname2ctype_offset(str2911), 450},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str2941), 360},
+ {uniname2ctype_offset(str2941), 363},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
{uniname2ctype_offset(str2953), 198},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str2964), 517},
+ {uniname2ctype_offset(str2964), 520},
{-1},
- {uniname2ctype_offset(str2966), 236},
+ {uniname2ctype_offset(str2966), 238},
{uniname2ctype_offset(str2967), 43},
- {-1},
- {uniname2ctype_offset(str2969), 204},
+ {-1}, {-1},
{uniname2ctype_offset(str2970), 88},
- {-1},
- {uniname2ctype_offset(str2972), 244},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2980), 195},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2984), 568},
+ {uniname2ctype_offset(str2984), 574},
{-1},
{uniname2ctype_offset(str2986), 118},
{uniname2ctype_offset(str2987), 54},
- {uniname2ctype_offset(str2988), 468},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2993), 201},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2997), 596},
+ {uniname2ctype_offset(str2988), 471},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2997), 603},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str3001), 180},
{uniname2ctype_offset(str3002), 64},
{-1}, {-1},
- {uniname2ctype_offset(str3005), 472},
- {uniname2ctype_offset(str3006), 577},
+ {uniname2ctype_offset(str3005), 475},
+ {uniname2ctype_offset(str3006), 583},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3015), 83},
{-1}, {-1},
@@ -43989,60 +45073,73 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str3019), 165},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3026), 83},
- {uniname2ctype_offset(str3027), 500},
+ {uniname2ctype_offset(str3027), 503},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3035), 232},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3042), 211},
{-1}, {-1},
- {uniname2ctype_offset(str3045), 606},
+ {uniname2ctype_offset(str3045), 615},
{-1}, {-1},
{uniname2ctype_offset(str3048), 119},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str3052), 89},
{uniname2ctype_offset(str3053), 229},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3058), 185},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3067), 185},
+ {uniname2ctype_offset(str3068), 99},
+ {-1}, {-1},
{uniname2ctype_offset(str3071), 198},
{uniname2ctype_offset(str3072), 133},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3082), 130},
- {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str3084), 237},
+ {-1},
{uniname2ctype_offset(str3086), 84},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3091), 259},
+ {uniname2ctype_offset(str3087), 342},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3091), 261},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3099), 475},
+ {uniname2ctype_offset(str3099), 478},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3104), 84},
- {-1}, {-1},
- {uniname2ctype_offset(str3107), 310},
{-1},
- {uniname2ctype_offset(str3109), 312},
+ {uniname2ctype_offset(str3106), 562},
+ {uniname2ctype_offset(str3107), 313},
+ {-1},
+ {uniname2ctype_offset(str3109), 315},
{uniname2ctype_offset(str3110), 152},
{uniname2ctype_offset(str3111), 191},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3118), 90},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3123), 107},
+ {-1},
{uniname2ctype_offset(str3125), 191},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3140), 607},
- {-1},
- {uniname2ctype_offset(str3142), 579},
+ {uniname2ctype_offset(str3135), 452},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3142), 585},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3145), 183},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3155), 182},
+ {-1},
{uniname2ctype_offset(str3157), 231},
- {uniname2ctype_offset(str3158), 325},
+ {uniname2ctype_offset(str3158), 328},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3176), 393},
+ {uniname2ctype_offset(str3176), 396},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str3180), 328},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3180), 331},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3193), 433},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3209), 62},
{-1}, {-1},
{uniname2ctype_offset(str3212), 211},
@@ -44050,97 +45147,97 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str3215), 180},
{-1}, {-1},
{uniname2ctype_offset(str3218), 125},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3227), 433},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3237), 387},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3237), 390},
{-1},
- {uniname2ctype_offset(str3239), 305},
- {uniname2ctype_offset(str3240), 301},
+ {uniname2ctype_offset(str3239), 308},
+ {uniname2ctype_offset(str3240), 304},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3248), 213},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3254), 250},
+ {uniname2ctype_offset(str3254), 252},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str3265), 601},
+ {uniname2ctype_offset(str3265), 610},
{-1},
{uniname2ctype_offset(str3267), 28},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3275), 146},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3284), 609},
+ {uniname2ctype_offset(str3284), 618},
{-1}, {-1},
{uniname2ctype_offset(str3287), 223},
- {-1}, {-1},
- {uniname2ctype_offset(str3290), 451},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3297), 308},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3297), 311},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3303), 304},
+ {uniname2ctype_offset(str3303), 307},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3313), 253},
+ {uniname2ctype_offset(str3313), 255},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3320), 223},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3328), 63},
- {uniname2ctype_offset(str3329), 616},
+ {uniname2ctype_offset(str3329), 625},
{-1},
{uniname2ctype_offset(str3331), 222},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3338), 262},
+ {uniname2ctype_offset(str3338), 264},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3348), 404},
+ {uniname2ctype_offset(str3348), 407},
{-1},
- {uniname2ctype_offset(str3350), 498},
+ {uniname2ctype_offset(str3350), 501},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3355), 47},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3360), 334},
+ {uniname2ctype_offset(str3360), 337},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
{uniname2ctype_offset(str3371), 126},
{uniname2ctype_offset(str3372), 16},
- {uniname2ctype_offset(str3373), 251},
+ {uniname2ctype_offset(str3373), 253},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3377), 147},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3391), 236},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3396), 604},
+ {uniname2ctype_offset(str3391), 238},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3396), 613},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3405), 466},
+ {uniname2ctype_offset(str3405), 469},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
{uniname2ctype_offset(str3416), 51},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str3428), 251},
+ {uniname2ctype_offset(str3428), 253},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3433), 205},
{-1}, {-1},
{uniname2ctype_offset(str3436), 213},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3452), 220},
- {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3455), 225},
+ {-1}, {-1},
{uniname2ctype_offset(str3458), 10},
{-1},
- {uniname2ctype_offset(str3460), 618},
+ {uniname2ctype_offset(str3460), 627},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3483), 243},
+ {uniname2ctype_offset(str3483), 245},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3502), 49},
- {-1}, {-1},
- {uniname2ctype_offset(str3505), 476},
+ {-1},
+ {uniname2ctype_offset(str3504), 80},
+ {uniname2ctype_offset(str3505), 479},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3511), 408},
+ {uniname2ctype_offset(str3511), 411},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str3532), 572},
+ {uniname2ctype_offset(str3532), 578},
{uniname2ctype_offset(str3533), 57},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44156,11 +45253,13 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3613), 41},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3618), 99},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3648), 462},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3648), 465},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
{uniname2ctype_offset(str3659), 178},
@@ -44168,18 +45267,23 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str3664), 177},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3698), 501},
- {uniname2ctype_offset(str3699), 548},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3686), 204},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3689), 246},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3698), 504},
+ {uniname2ctype_offset(str3699), 552},
{-1},
{uniname2ctype_offset(str3701), 96},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str3705), 317},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3705), 320},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3710), 201},
+ {-1}, {-1},
{uniname2ctype_offset(str3713), 96},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3721), 435},
+ {uniname2ctype_offset(str3721), 438},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
@@ -44188,57 +45292,59 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str3754), 71},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str3766), 464},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3770), 147},
+ {uniname2ctype_offset(str3766), 467},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3799), 214},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3807), 258},
+ {uniname2ctype_offset(str3807), 260},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3842), 182},
{-1}, {-1},
- {uniname2ctype_offset(str3845), 257},
+ {uniname2ctype_offset(str3845), 259},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3850), 243},
+ {uniname2ctype_offset(str3850), 245},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str3854), 65},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3857), 616},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3868), 259},
+ {-1},
+ {uniname2ctype_offset(str3868), 261},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str3881), 228},
{-1}, {-1},
- {uniname2ctype_offset(str3884), 303},
+ {uniname2ctype_offset(str3884), 306},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3891), 30},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3924), 417},
+ {uniname2ctype_offset(str3924), 420},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3942), 100},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3949), 274},
+ {-1},
+ {uniname2ctype_offset(str3944), 436},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3949), 276},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3976), 597},
- {-1},
- {uniname2ctype_offset(str3978), 80},
+ {uniname2ctype_offset(str3976), 604},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {uniname2ctype_offset(str3998), 427},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3998), 430},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4007), 454},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44246,13 +45352,10 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str4066), 313},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str4106), 45},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str4116), 132},
@@ -44263,14 +45366,15 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4169), 220},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str4231), 153},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44281,18 +45385,17 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str4308), 398},
+ {uniname2ctype_offset(str4308), 401},
{uniname2ctype_offset(str4309), 162},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {uniname2ctype_offset(str4321), 240},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str4348), 565},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str4348), 571},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str4361), 566},
+ {uniname2ctype_offset(str4361), 572},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44308,6 +45411,7 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4487), 147},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44320,42 +45424,45 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str4604), 116},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str4626), 116},
- {uniname2ctype_offset(str4627), 253},
+ {uniname2ctype_offset(str4627), 255},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str4674), 632},
+ {uniname2ctype_offset(str4674), 642},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str4683), 463},
+ {uniname2ctype_offset(str4683), 466},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str4695), 80},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
{uniname2ctype_offset(str4734), 179},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str4755), 309},
- {uniname2ctype_offset(str4756), 311},
+ {uniname2ctype_offset(str4755), 312},
+ {uniname2ctype_offset(str4756), 314},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str4772), 406},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4772), 409},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str4783), 316},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str4810), 90},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44380,7 +45487,7 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1},
{uniname2ctype_offset(str4982), 232},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str4986), 425},
+ {uniname2ctype_offset(str4986), 428},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44388,6 +45495,8 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str5018), 46},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str5038), 242},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44398,8 +45507,8 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str5134), 619},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str5134), 628},
#endif /* USE_UNICODE_PROPERTIES */
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#ifndef USE_UNICODE_PROPERTIES
@@ -44529,7 +45638,7 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str6098), 633}
+ {uniname2ctype_offset(str6098), 643}
#endif /* USE_UNICODE_PROPERTIES */
};
@@ -44560,22 +45669,22 @@ uniname2ctype(const UChar *name, unsigned int len)
return -1;
}
#if defined ONIG_UNICODE_VERSION_STRING && !( \
- ONIG_UNICODE_VERSION_MAJOR == 14 && \
+ ONIG_UNICODE_VERSION_MAJOR == 15 && \
ONIG_UNICODE_VERSION_MINOR == 0 && \
ONIG_UNICODE_VERSION_TEENY == 0 && \
1)
# error ONIG_UNICODE_VERSION_STRING mismatch
#endif
-#define ONIG_UNICODE_VERSION_STRING "14.0.0"
-#define ONIG_UNICODE_VERSION_MAJOR 14
+#define ONIG_UNICODE_VERSION_STRING "15.0.0"
+#define ONIG_UNICODE_VERSION_MAJOR 15
#define ONIG_UNICODE_VERSION_MINOR 0
#define ONIG_UNICODE_VERSION_TEENY 0
#if defined ONIG_UNICODE_EMOJI_VERSION_STRING && !( \
- ONIG_UNICODE_EMOJI_VERSION_MAJOR == 14 && \
+ ONIG_UNICODE_EMOJI_VERSION_MAJOR == 15 && \
ONIG_UNICODE_EMOJI_VERSION_MINOR == 0 && \
1)
# error ONIG_UNICODE_EMOJI_VERSION_STRING mismatch
#endif
-#define ONIG_UNICODE_EMOJI_VERSION_STRING "14.0"
-#define ONIG_UNICODE_EMOJI_VERSION_MAJOR 14
+#define ONIG_UNICODE_EMOJI_VERSION_STRING "15.0"
+#define ONIG_UNICODE_EMOJI_VERSION_MAJOR 15
#define ONIG_UNICODE_EMOJI_VERSION_MINOR 0
diff --git a/enc/utf_16_32.h b/enc/utf_16_32.h
index 9f9216d8ff..4d669019bf 100644
--- a/enc/utf_16_32.h
+++ b/enc/utf_16_32.h
@@ -1,5 +1,5 @@
#include "regenc.h"
/* dummy for unsupported, stateful encoding */
-#define ENC_DUMMY_UNICODE(name) ENC_REPLICATE(name, name "BE")
+#define ENC_DUMMY_UNICODE(name) ENC_DUMMY(name)
ENC_DUMMY_UNICODE("UTF-16");
ENC_DUMMY_UNICODE("UTF-32");
diff --git a/encoding.c b/encoding.c
index 0e52949fa0..2f4b47bdfa 100644
--- a/encoding.c
+++ b/encoding.c
@@ -17,6 +17,7 @@
#include "internal.h"
#include "internal/enc.h"
#include "internal/encoding.h"
+#include "internal/error.h"
#include "internal/inits.h"
#include "internal/load.h"
#include "internal/object.h"
@@ -49,16 +50,14 @@ void rb_encdb_declare(const char *name);
int rb_encdb_replicate(const char *name, const char *orig);
int rb_encdb_dummy(const char *name);
int rb_encdb_alias(const char *alias, const char *orig);
-void rb_encdb_set_unicode(int index);
#pragma GCC visibility pop
#endif
static ID id_encoding;
VALUE rb_cEncoding;
-#define DEFAULT_ENCODING_LIST_CAPA 128
-static VALUE rb_default_encoding_list;
-static VALUE rb_additional_encoding_list;
+#define ENCODING_LIST_CAPA 256
+static VALUE rb_encoding_list;
struct rb_encoding_entry {
const char *name;
@@ -67,9 +66,8 @@ struct rb_encoding_entry {
};
static struct enc_table {
- struct rb_encoding_entry *list;
+ struct rb_encoding_entry list[ENCODING_LIST_CAPA];
int count;
- int size;
st_table *names;
} global_enc_table;
@@ -128,47 +126,25 @@ enc_new(rb_encoding *encoding)
static void
enc_list_update(int index, rb_raw_encoding *encoding)
{
- if (index < DEFAULT_ENCODING_LIST_CAPA) {
- VALUE list = rb_default_encoding_list;
- if (list && NIL_P(rb_ary_entry(list, index))) {
- /* initialize encoding data */
- rb_ary_store(list, index, enc_new(encoding));
- }
- }
- else {
- RB_VM_LOCK_ENTER();
- {
- VALUE list = rb_additional_encoding_list;
- if (list && NIL_P(rb_ary_entry(list, index))) {
- /* initialize encoding data */
- rb_ary_store(list, index - DEFAULT_ENCODING_LIST_CAPA, enc_new(encoding));
- }
- }
- RB_VM_LOCK_LEAVE();
+ RUBY_ASSERT(index < ENCODING_LIST_CAPA);
+
+ VALUE list = rb_encoding_list;
+ if (list && NIL_P(rb_ary_entry(list, index))) {
+ /* initialize encoding data */
+ rb_ary_store(list, index, enc_new(encoding));
}
}
static VALUE
enc_list_lookup(int idx)
{
- VALUE list, enc;
+ VALUE list, enc = Qnil;
- if (idx < DEFAULT_ENCODING_LIST_CAPA) {
- if (!(list = rb_default_encoding_list)) {
- rb_bug("rb_enc_from_encoding_index(%d): no rb_default_encoding_list", idx);
- }
+ if (idx < ENCODING_LIST_CAPA) {
+ list = rb_encoding_list;
+ RUBY_ASSERT(list);
enc = rb_ary_entry(list, idx);
}
- else {
- RB_VM_LOCK_ENTER();
- {
- if (!(list = rb_additional_encoding_list)) {
- rb_bug("rb_enc_from_encoding_index(%d): no rb_additional_encoding_list", idx);
- }
- enc = rb_ary_entry(list, idx - DEFAULT_ENCODING_LIST_CAPA);
- }
- RB_VM_LOCK_LEAVE();
- }
if (NIL_P(enc)) {
rb_bug("rb_enc_from_encoding_index(%d): not created yet", idx);
@@ -210,7 +186,7 @@ check_encoding(rb_encoding *enc)
{
int index = rb_enc_to_index(enc);
if (rb_enc_from_index(index) != enc)
- return -1;
+ return -1;
if (rb_enc_autoload_p(enc)) {
index = rb_enc_autoload(enc);
}
@@ -221,7 +197,7 @@ static int
enc_check_encoding(VALUE obj)
{
if (!is_obj_encoding(obj)) {
- return -1;
+ return -1;
}
return check_encoding(RDATA(obj)->data);
}
@@ -231,7 +207,7 @@ static void
not_encoding(VALUE enc)
{
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Encoding)",
- rb_obj_class(enc));
+ rb_obj_class(enc));
}
static rb_encoding *
@@ -239,7 +215,7 @@ must_encoding(VALUE enc)
{
int index = enc_check_encoding(enc);
if (index < 0) {
- not_encoding(enc);
+ not_encoding(enc);
}
return DATA_PTR(enc);
}
@@ -249,16 +225,16 @@ must_encindex(int index)
{
rb_encoding *enc = rb_enc_from_index(index);
if (!enc) {
- rb_raise(rb_eEncodingError, "encoding index out of bound: %d",
- index);
+ rb_raise(rb_eEncodingError, "encoding index out of bound: %d",
+ index);
}
if (ENC_TO_ENCINDEX(enc) != (int)(index & ENC_INDEX_MASK)) {
- rb_raise(rb_eEncodingError, "wrong encoding index %d for %s (expected %d)",
- index, rb_enc_name(enc), ENC_TO_ENCINDEX(enc));
+ rb_raise(rb_eEncodingError, "wrong encoding index %d for %s (expected %d)",
+ index, rb_enc_name(enc), ENC_TO_ENCINDEX(enc));
}
if (rb_enc_autoload_p(enc) && rb_enc_autoload(enc) == -1) {
- rb_loaderror("failed to load encoding (%s)",
- rb_enc_name(enc));
+ rb_loaderror("failed to load encoding (%s)",
+ rb_enc_name(enc));
}
return enc;
}
@@ -271,16 +247,16 @@ rb_to_encoding_index(VALUE enc)
idx = enc_check_encoding(enc);
if (idx >= 0) {
- return idx;
+ return idx;
}
else if (NIL_P(enc = rb_check_string_type(enc))) {
- return -1;
+ return -1;
}
if (!rb_enc_asciicompat(rb_enc_get(enc))) {
- return -1;
+ return -1;
}
if (!(name = rb_str_to_cstr(enc))) {
- return -1;
+ return -1;
}
return rb_enc_find_index(name);
}
@@ -292,10 +268,10 @@ name_for_encoding(volatile VALUE *enc)
const char *n;
if (!rb_enc_asciicompat(rb_enc_get(name))) {
- rb_raise(rb_eArgError, "invalid encoding name (non ASCII)");
+ rb_raise(rb_eArgError, "invalid encoding name (non ASCII)");
}
if (!(n = rb_str_to_cstr(name))) {
- rb_raise(rb_eArgError, "invalid encoding name (NUL byte)");
+ rb_raise(rb_eArgError, "invalid encoding name (NUL byte)");
}
return n;
}
@@ -314,7 +290,7 @@ str_to_encindex(VALUE enc)
{
int idx = str_find_encindex(enc);
if (idx < 0) {
- rb_raise(rb_eArgError, "unknown encoding name - %"PRIsVALUE, enc);
+ rb_raise(rb_eArgError, "unknown encoding name - %"PRIsVALUE, enc);
}
return idx;
}
@@ -345,16 +321,10 @@ rb_find_encoding(VALUE enc)
static int
enc_table_expand(struct enc_table *enc_table, int newsize)
{
- struct rb_encoding_entry *ent;
- int count = newsize;
-
- if (enc_table->size >= newsize) return newsize;
- newsize = (newsize + 7) / 8 * 8;
- ent = REALLOC_N(enc_table->list, struct rb_encoding_entry, newsize);
- memset(ent + enc_table->size, 0, sizeof(*ent)*(newsize - enc_table->size));
- enc_table->list = ent;
- enc_table->size = newsize;
- return count;
+ if (newsize > ENCODING_LIST_CAPA) {
+ rb_raise(rb_eEncodingError, "too many encoding (> %d)", ENCODING_LIST_CAPA);
+ }
+ return newsize;
}
static int
@@ -365,20 +335,20 @@ enc_register_at(struct enc_table *enc_table, int index, const char *name, rb_enc
if (!valid_encoding_name_p(name)) return -1;
if (!ent->name) {
- ent->name = name = strdup(name);
+ ent->name = name = strdup(name);
}
else if (STRCASECMP(name, ent->name)) {
- return -1;
+ return -1;
}
encoding = (rb_raw_encoding *)ent->enc;
if (!encoding) {
- encoding = xmalloc(sizeof(rb_encoding));
+ encoding = xmalloc(sizeof(rb_encoding));
}
if (base_encoding) {
- *encoding = *base_encoding;
+ *encoding = *base_encoding;
}
else {
- memset(encoding, 0, sizeof(*ent->enc));
+ memset(encoding, 0, sizeof(*ent->enc));
}
encoding->name = name;
encoding->ruby_encoding_index = index;
@@ -405,7 +375,7 @@ static rb_encoding *
enc_from_index(struct enc_table *enc_table, int index)
{
if (UNLIKELY(index < 0 || enc_table->count <= (index &= ENC_INDEX_MASK))) {
- return 0;
+ return 0;
}
return enc_table->list[index].enc;
}
@@ -413,17 +383,7 @@ enc_from_index(struct enc_table *enc_table, int index)
rb_encoding *
rb_enc_from_index(int index)
{
- rb_encoding *enc;
-
- switch (index) {
- case ENCINDEX_ASCII_8BIT: return global_enc_ascii;
- case ENCINDEX_UTF_8: return global_enc_utf_8;
- case ENCINDEX_US_ASCII: return global_enc_us_ascii;
- default:
- GLOBAL_ENC_TABLE_EVAL(enc_table,
- enc = enc_from_index(enc_table, index));
- return enc;
- }
+ return enc_from_index(&global_enc_table, index);
}
int
@@ -462,9 +422,9 @@ enc_registered(struct enc_table *enc_table, const char *name)
st_data_t idx = 0;
if (!name) return -1;
- if (!enc_table->list) return -1;
+ if (!enc_table->names) return -1;
if (st_lookup(enc_table->names, (st_data_t)name, &idx)) {
- return (int)idx;
+ return (int)idx;
}
return -1;
}
@@ -484,10 +444,13 @@ rb_encdb_declare(const char *name)
}
static void
-enc_check_duplication(struct enc_table *enc_table, const char *name)
+enc_check_addable(struct enc_table *enc_table, const char *name)
{
if (enc_registered(enc_table, name) >= 0) {
- rb_raise(rb_eArgError, "encoding %s is already registered", name);
+ rb_raise(rb_eArgError, "encoding %s is already registered", name);
+ }
+ else if (!valid_encoding_name_p(name)) {
+ rb_raise(rb_eArgError, "invalid encoding name: %s", name);
}
}
@@ -524,11 +487,7 @@ rb_enc_set_base(const char *name, const char *orig)
int
rb_enc_set_dummy(int index)
{
- rb_encoding *enc;
-
- GLOBAL_ENC_TABLE_EVAL(enc_table,
- enc = enc_table->list[index].enc);
-
+ rb_encoding *enc = global_enc_table.list[index].enc;
ENC_SET_DUMMY((rb_raw_encoding *)enc);
return index;
}
@@ -538,7 +497,7 @@ enc_replicate(struct enc_table *enc_table, const char *name, rb_encoding *encodi
{
int idx;
- enc_check_duplication(enc_table, name);
+ enc_check_addable(enc_table, name);
idx = enc_register(enc_table, name, encoding);
if (idx < 0) rb_raise(rb_eArgError, "invalid encoding name: %s", name);
set_base_encoding(enc_table, idx, encoding);
@@ -569,7 +528,10 @@ rb_enc_replicate(const char *name, rb_encoding *encoding)
static VALUE
enc_replicate_m(VALUE encoding, VALUE name)
{
- int idx = rb_enc_replicate(name_for_encoding(&name), rb_to_encoding(encoding));
+ int idx;
+ rb_warn_deprecated_to_remove("3.3", "Encoding#replicate", "the original encoding");
+
+ idx = rb_enc_replicate(name_for_encoding(&name), rb_to_encoding(encoding));
RB_GC_GUARD(name);
return rb_enc_from_encoding_index(idx);
}
@@ -581,11 +543,11 @@ enc_replicate_with_index(struct enc_table *enc_table, const char *name, rb_encod
idx = enc_register(enc_table, name, origenc);
}
else {
- idx = enc_register_at(enc_table, idx, name, origenc);
+ idx = enc_register_at(enc_table, idx, name, origenc);
}
if (idx >= 0) {
- set_base_encoding(enc_table, idx, origenc);
- set_encoding_const(name, rb_enc_from_index(idx));
+ set_base_encoding(enc_table, idx, origenc);
+ set_encoding_const(name, rb_enc_from_index(idx));
}
else {
rb_raise(rb_eArgError, "failed to replicate encoding");
@@ -705,7 +667,7 @@ static int
enc_alias_internal(struct enc_table *enc_table, const char *alias, int idx)
{
return st_insert2(enc_table->names, (st_data_t)alias, (st_data_t)idx,
- enc_dup_name);
+ enc_dup_name);
}
static int
@@ -713,7 +675,7 @@ enc_alias(struct enc_table *enc_table, const char *alias, int idx)
{
if (!valid_encoding_name_p(alias)) return -1;
if (!enc_alias_internal(enc_table, alias, idx))
- set_encoding_const(alias, enc_from_index(enc_table, idx));
+ set_encoding_const(alias, enc_from_index(enc_table, idx));
return idx;
}
@@ -724,7 +686,7 @@ rb_enc_alias(const char *alias, const char *orig)
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
- enc_check_duplication(enc_table, alias);
+ enc_check_addable(enc_table, alias);
if ((idx = rb_enc_find_index(orig)) < 0) {
r = -1;
}
@@ -756,20 +718,12 @@ rb_encdb_alias(const char *alias, const char *orig)
return r;
}
-void
-rb_encdb_set_unicode(int index)
-{
- rb_raw_encoding *enc = (rb_raw_encoding *)rb_enc_from_index(index);
- ASSUME(enc);
- enc->flags |= ONIGENC_FLAG_UNICODE;
-}
-
static void
rb_enc_init(struct enc_table *enc_table)
{
enc_table_expand(enc_table, ENCODING_COUNT + 1);
if (!enc_table->names) {
- enc_table->names = st_init_strcasetable();
+ enc_table->names = st_init_strcasetable_with_size(ENCODING_LIST_CAPA);
}
#define OnigEncodingASCII_8BIT OnigEncodingASCII
#define ENC_REGISTER(enc) enc_register_at(enc_table, ENCINDEX_##enc, rb_enc_name(&OnigEncoding##enc), &OnigEncoding##enc)
@@ -815,9 +769,9 @@ load_encoding(const char *name)
int idx;
while (s < e) {
- if (!ISALNUM(*s)) *s = '_';
- else if (ISUPPER(*s)) *s = (char)TOLOWER(*s);
- ++s;
+ if (!ISALNUM(*s)) *s = '_';
+ else if (ISUPPER(*s)) *s = (char)TOLOWER(*s);
+ ++s;
}
enclib = rb_fstring(enclib);
ruby_debug = Qfalse;
@@ -850,16 +804,16 @@ enc_autoload_body(struct enc_table *enc_table, rb_encoding *enc)
if (base) {
int i = 0;
- do {
- if (i >= enc_table->count) return -1;
- } while (enc_table->list[i].enc != base && (++i, 1));
- if (rb_enc_autoload_p(base)) {
- if (rb_enc_autoload(base) < 0) return -1;
- }
- i = enc->ruby_encoding_index;
- enc_register_at(enc_table, i & ENC_INDEX_MASK, rb_enc_name(enc), base);
+ do {
+ if (i >= enc_table->count) return -1;
+ } while (enc_table->list[i].enc != base && (++i, 1));
+ if (rb_enc_autoload_p(base)) {
+ if (rb_enc_autoload(base) < 0) return -1;
+ }
+ i = enc->ruby_encoding_index;
+ enc_register_at(enc_table, i & ENC_INDEX_MASK, rb_enc_name(enc), base);
((rb_raw_encoding *)enc)->ruby_encoding_index = i;
- i &= ENC_INDEX_MASK;
+ i &= ENC_INDEX_MASK;
return i;
}
else {
@@ -882,25 +836,23 @@ rb_enc_autoload(rb_encoding *enc)
int
rb_enc_find_index(const char *name)
{
- int i;
+ int i = enc_registered(&global_enc_table, name);
rb_encoding *enc;
- GLOBAL_ENC_TABLE_EVAL(enc_table, i = enc_registered(enc_table, name));
-
if (i < 0) {
- i = load_encoding(name);
+ i = load_encoding(name);
}
else if (!(enc = rb_enc_from_index(i))) {
- if (i != UNSPECIFIED_ENCODING) {
- rb_raise(rb_eArgError, "encoding %s is not registered", name);
- }
+ if (i != UNSPECIFIED_ENCODING) {
+ rb_raise(rb_eArgError, "encoding %s is not registered", name);
+ }
}
else if (rb_enc_autoload_p(enc)) {
- if (rb_enc_autoload(enc) < 0) {
- rb_warn("failed to load encoding (%s); use ASCII-8BIT instead",
- name);
- return 0;
- }
+ if (rb_enc_autoload(enc) < 0) {
+ rb_warn("failed to load encoding (%s); use ASCII-8BIT instead",
+ name);
+ return 0;
+ }
}
return i;
}
@@ -933,11 +885,11 @@ enc_capable(VALUE obj)
case T_REGEXP:
case T_FILE:
case T_SYMBOL:
- return TRUE;
+ return TRUE;
case T_DATA:
- if (is_data_encoding(obj)) return TRUE;
+ if (is_data_encoding(obj)) return TRUE;
default:
- return FALSE;
+ return FALSE;
}
}
@@ -959,11 +911,11 @@ enc_get_index_str(VALUE str)
{
int i = ENCODING_GET_INLINED(str);
if (i == ENCODING_INLINE_MAX) {
- VALUE iv;
+ VALUE iv;
#if 0
- iv = rb_ivar_get(str, rb_id_encoding());
- i = NUM2INT(iv);
+ iv = rb_ivar_get(str, rb_id_encoding());
+ i = NUM2INT(iv);
#else
/*
* Tentatively, assume ASCII-8BIT, if encoding index instance
@@ -984,31 +936,31 @@ rb_enc_get_index(VALUE obj)
VALUE tmp;
if (SPECIAL_CONST_P(obj)) {
- if (!SYMBOL_P(obj)) return -1;
- obj = rb_sym2str(obj);
+ if (!SYMBOL_P(obj)) return -1;
+ obj = rb_sym2str(obj);
}
switch (BUILTIN_TYPE(obj)) {
case T_STRING:
case T_SYMBOL:
case T_REGEXP:
- i = enc_get_index_str(obj);
- break;
+ i = enc_get_index_str(obj);
+ break;
case T_FILE:
- tmp = rb_funcallv(obj, rb_intern("internal_encoding"), 0, 0);
- if (NIL_P(tmp)) {
- tmp = rb_funcallv(obj, rb_intern("external_encoding"), 0, 0);
- }
- if (is_obj_encoding(tmp)) {
- i = enc_check_encoding(tmp);
- }
- break;
+ tmp = rb_funcallv(obj, rb_intern("internal_encoding"), 0, 0);
+ if (NIL_P(tmp)) {
+ tmp = rb_funcallv(obj, rb_intern("external_encoding"), 0, 0);
+ }
+ if (is_obj_encoding(tmp)) {
+ i = enc_check_encoding(tmp);
+ }
+ break;
case T_DATA:
- if (is_data_encoding(obj)) {
- i = enc_check_encoding(obj);
- }
- break;
+ if (is_data_encoding(obj)) {
+ i = enc_check_encoding(obj);
+ }
+ break;
default:
- break;
+ break;
}
return i;
}
@@ -1021,8 +973,8 @@ enc_set_index(VALUE obj, int idx)
}
if (idx < ENCODING_INLINE_MAX) {
- ENCODING_SET_INLINED(obj, idx);
- return;
+ ENCODING_SET_INLINED(obj, idx);
+ return;
}
ENCODING_SET_INLINED(obj, ENCODING_INLINE_MAX);
rb_ivar_set(obj, rb_id_encoding(), INT2NUM(idx));
@@ -1046,19 +998,19 @@ rb_enc_associate_index(VALUE obj, int idx)
rb_check_frozen(obj);
oldidx = rb_enc_get_index(obj);
if (oldidx == idx)
- return obj;
+ return obj;
if (SPECIAL_CONST_P(obj)) {
- rb_raise(rb_eArgError, "cannot set encoding");
+ rb_raise(rb_eArgError, "cannot set encoding");
}
enc = must_encindex(idx);
if (!ENC_CODERANGE_ASCIIONLY(obj) ||
- !rb_enc_asciicompat(enc)) {
- ENC_CODERANGE_CLEAR(obj);
+ !rb_enc_asciicompat(enc)) {
+ ENC_CODERANGE_CLEAR(obj);
}
termlen = rb_enc_mbminlen(enc);
oldtermlen = rb_enc_mbminlen(rb_enc_from_index(oldidx));
if (oldtermlen != termlen && RB_TYPE_P(obj, T_STRING)) {
- rb_str_change_terminator_length(obj, oldtermlen, termlen);
+ rb_str_change_terminator_length(obj, oldtermlen, termlen);
}
enc_set_index(obj, idx);
return obj;
@@ -1080,9 +1032,9 @@ static rb_encoding*
rb_encoding_check(rb_encoding* enc, VALUE str1, VALUE str2)
{
if (!enc)
- rb_raise(rb_eEncCompatError, "incompatible character encodings: %s and %s",
- rb_enc_name(rb_enc_get(str1)),
- rb_enc_name(rb_enc_get(str2)));
+ rb_raise(rb_eEncCompatError, "incompatible character encodings: %s and %s",
+ rb_enc_name(rb_enc_get(str1)),
+ rb_enc_name(rb_enc_get(str2)));
return enc;
}
@@ -1111,48 +1063,48 @@ enc_compatible_latter(VALUE str1, VALUE str2, int idx1, int idx2)
isstr2 = RB_TYPE_P(str2, T_STRING);
if (isstr2 && RSTRING_LEN(str2) == 0)
- return enc1;
+ return enc1;
isstr1 = RB_TYPE_P(str1, T_STRING);
if (isstr1 && isstr2 && RSTRING_LEN(str1) == 0)
- return (rb_enc_asciicompat(enc1) && rb_enc_str_asciionly_p(str2)) ? enc1 : enc2;
+ return (rb_enc_asciicompat(enc1) && rb_enc_str_asciionly_p(str2)) ? enc1 : enc2;
if (!rb_enc_asciicompat(enc1) || !rb_enc_asciicompat(enc2)) {
- return 0;
+ return 0;
}
/* objects whose encoding is the same of contents */
if (!isstr2 && idx2 == ENCINDEX_US_ASCII)
- return enc1;
+ return enc1;
if (!isstr1 && idx1 == ENCINDEX_US_ASCII)
- return enc2;
+ return enc2;
if (!isstr1) {
- VALUE tmp = str1;
- int idx0 = idx1;
- str1 = str2;
- str2 = tmp;
- idx1 = idx2;
- idx2 = idx0;
- idx0 = isstr1;
- isstr1 = isstr2;
- isstr2 = idx0;
+ VALUE tmp = str1;
+ int idx0 = idx1;
+ str1 = str2;
+ str2 = tmp;
+ idx1 = idx2;
+ idx2 = idx0;
+ idx0 = isstr1;
+ isstr1 = isstr2;
+ isstr2 = idx0;
}
if (isstr1) {
- int cr1, cr2;
-
- cr1 = rb_enc_str_coderange(str1);
- if (isstr2) {
- cr2 = rb_enc_str_coderange(str2);
- if (cr1 != cr2) {
- /* may need to handle ENC_CODERANGE_BROKEN */
- if (cr1 == ENC_CODERANGE_7BIT) return enc2;
- if (cr2 == ENC_CODERANGE_7BIT) return enc1;
- }
- if (cr2 == ENC_CODERANGE_7BIT) {
- return enc1;
- }
- }
- if (cr1 == ENC_CODERANGE_7BIT)
- return enc2;
+ int cr1, cr2;
+
+ cr1 = rb_enc_str_coderange(str1);
+ if (isstr2) {
+ cr2 = rb_enc_str_coderange(str2);
+ if (cr1 != cr2) {
+ /* may need to handle ENC_CODERANGE_BROKEN */
+ if (cr1 == ENC_CODERANGE_7BIT) return enc2;
+ if (cr2 == ENC_CODERANGE_7BIT) return enc1;
+ }
+ if (cr2 == ENC_CODERANGE_7BIT) {
+ return enc1;
+ }
+ }
+ if (cr1 == ENC_CODERANGE_7BIT)
+ return enc2;
}
return 0;
}
@@ -1167,10 +1119,10 @@ enc_compatible_str(VALUE str1, VALUE str2)
return 0;
if (idx1 == idx2) {
- return rb_enc_from_index(idx1);
+ return rb_enc_from_index(idx1);
}
else {
- return enc_compatible_latter(str1, str2, idx1, idx2);
+ return enc_compatible_latter(str1, str2, idx1, idx2);
}
}
@@ -1184,7 +1136,7 @@ rb_enc_compatible(VALUE str1, VALUE str2)
return 0;
if (idx1 == idx2) {
- return rb_enc_from_index(idx1);
+ return rb_enc_from_index(idx1);
}
return enc_compatible_latter(str1, str2, idx1, idx2);
@@ -1209,7 +1161,7 @@ rb_obj_encoding(VALUE obj)
{
int idx = rb_enc_get_index(obj);
if (idx < 0) {
- rb_raise(rb_eTypeError, "unknown encoding");
+ rb_raise(rb_eTypeError, "unknown encoding");
}
return rb_enc_from_encoding_index(idx & ENC_INDEX_MASK);
}
@@ -1276,7 +1228,7 @@ rb_enc_codepoint_len(const char *p, const char *e, int *len_p, rb_encoding *enc)
rb_raise(rb_eArgError, "empty string");
r = rb_enc_precise_mbclen(p, e, enc);
if (!MBCLEN_CHARFOUND_P(r)) {
- rb_raise(rb_eArgError, "invalid byte sequence in %s", rb_enc_name(enc));
+ rb_raise(rb_eArgError, "invalid byte sequence in %s", rb_enc_name(enc));
}
if (len_p) *len_p = MBCLEN_CHARFOUND_LEN(r);
return rb_enc_mbc_to_codepoint(p, e, enc);
@@ -1287,7 +1239,7 @@ rb_enc_codelen(int c, rb_encoding *enc)
{
int n = ONIGENC_CODE_TO_MBCLEN(enc,c);
if (n == 0) {
- rb_raise(rb_eArgError, "invalid codepoint 0x%x in %s", c, rb_enc_name(enc));
+ rb_raise(rb_eArgError, "invalid codepoint 0x%x in %s", c, rb_enc_name(enc));
}
return n;
}
@@ -1319,16 +1271,16 @@ enc_inspect(VALUE self)
rb_encoding *enc;
if (!is_data_encoding(self)) {
- not_encoding(self);
+ not_encoding(self);
}
if (!(enc = DATA_PTR(self)) || rb_enc_from_index(rb_enc_to_index(enc)) != enc) {
- rb_raise(rb_eTypeError, "broken Encoding");
+ rb_raise(rb_eTypeError, "broken Encoding");
}
return rb_enc_sprintf(rb_usascii_encoding(),
- "#<%"PRIsVALUE":%s%s%s>", rb_obj_class(self),
- rb_enc_name(enc),
- (ENC_DUMMY_P(enc) ? " (dummy)" : ""),
- rb_enc_autoload_p(enc) ? " (autoload)" : "");
+ "#<%"PRIsVALUE":%s%s%s>", rb_obj_class(self),
+ rb_enc_name(enc),
+ (ENC_DUMMY_P(enc) ? " (dummy)" : ""),
+ rb_enc_autoload_p(enc) ? " (autoload)" : "");
}
/*
@@ -1352,8 +1304,8 @@ enc_names_i(st_data_t name, st_data_t idx, st_data_t args)
VALUE *arg = (VALUE *)args;
if ((int)idx == (int)arg[0]) {
- VALUE str = rb_fstring_cstr((char *)name);
- rb_ary_push(arg[1], str);
+ VALUE str = rb_fstring_cstr((char *)name);
+ rb_ary_push(arg[1], str);
}
return ST_CONTINUE;
}
@@ -1373,10 +1325,7 @@ enc_names(VALUE self)
args[0] = (VALUE)rb_to_encoding_index(self);
args[1] = rb_ary_new2(0);
-
- GLOBAL_ENC_TABLE_EVAL(enc_table,
- st_foreach(enc_table->names, enc_names_i, (st_data_t)args));
-
+ st_foreach(global_enc_table.names, enc_names_i, (st_data_t)args);
return args[1];
}
@@ -1402,14 +1351,7 @@ static VALUE
enc_list(VALUE klass)
{
VALUE ary = rb_ary_new2(0);
-
- RB_VM_LOCK_ENTER();
- {
- rb_ary_replace(ary, rb_default_encoding_list);
- rb_ary_concat(ary, rb_additional_encoding_list);
- }
- RB_VM_LOCK_LEAVE();
-
+ rb_ary_replace(ary, rb_encoding_list);
return ary;
}
@@ -1440,7 +1382,7 @@ enc_find(VALUE klass, VALUE enc)
{
int idx;
if (is_obj_encoding(enc))
- return enc;
+ return enc;
idx = str_to_encindex(enc);
if (idx == UNSPECIFIED_ENCODING) return Qnil;
return rb_enc_from_encoding_index(idx);
@@ -1558,15 +1500,17 @@ rb_locale_encindex(void)
if (idx < 0) idx = ENCINDEX_UTF_8;
- GLOBAL_ENC_TABLE_ENTER(enc_table);
- if (enc_registered(enc_table, "locale") < 0) {
+ if (enc_registered(&global_enc_table, "locale") < 0) {
# if defined _WIN32
- void Init_w32_codepage(void);
- Init_w32_codepage();
+ void Init_w32_codepage(void);
+ Init_w32_codepage();
# endif
- enc_alias_internal(enc_table, "locale", idx);
+ GLOBAL_ENC_TABLE_ENTER(enc_table);
+ {
+ enc_alias_internal(enc_table, "locale", idx);
+ }
+ GLOBAL_ENC_TABLE_LEAVE();
}
- GLOBAL_ENC_TABLE_LEAVE();
return idx;
}
@@ -1580,13 +1524,8 @@ rb_locale_encoding(void)
int
rb_filesystem_encindex(void)
{
- int idx;
-
- GLOBAL_ENC_TABLE_EVAL(enc_table,
- idx = enc_registered(enc_table, "filesystem"));
-
- if (idx < 0)
- idx = ENCINDEX_ASCII_8BIT;
+ int idx = enc_registered(&global_enc_table, "filesystem");
+ if (idx < 0) idx = ENCINDEX_ASCII_8BIT;
return idx;
}
@@ -1609,15 +1548,22 @@ enc_set_default_encoding(struct default_encoding *def, VALUE encoding, const cha
int overridden = FALSE;
if (def->index != -2)
- /* Already set */
- overridden = TRUE;
+ /* Already set */
+ overridden = TRUE;
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
if (NIL_P(encoding)) {
def->index = -1;
def->enc = 0;
- st_insert(enc_table->names, (st_data_t)strdup(name),
+ char *name_dup = strdup(name);
+
+ st_data_t existing_name = (st_data_t)name_dup;
+ if (st_delete(enc_table->names, &existing_name, NULL)) {
+ xfree((void *)existing_name);
+ }
+
+ st_insert(enc_table->names, (st_data_t)name_dup,
(st_data_t)UNSPECIFIED_ENCODING);
}
else {
@@ -1809,45 +1755,45 @@ set_encoding_const(const char *name, rb_encoding *enc)
if (ISDIGIT(*s)) return;
if (ISUPPER(*s)) {
- hasupper = 1;
- while (*++s && (ISALNUM(*s) || *s == '_')) {
- if (ISLOWER(*s)) haslower = 1;
- }
+ hasupper = 1;
+ while (*++s && (ISALNUM(*s) || *s == '_')) {
+ if (ISLOWER(*s)) haslower = 1;
+ }
}
if (!*s) {
- if (s - name > ENCODING_NAMELEN_MAX) return;
- valid = 1;
- rb_define_const(rb_cEncoding, name, encoding);
+ if (s - name > ENCODING_NAMELEN_MAX) return;
+ valid = 1;
+ rb_define_const(rb_cEncoding, name, encoding);
}
if (!valid || haslower) {
- size_t len = s - name;
- if (len > ENCODING_NAMELEN_MAX) return;
- if (!haslower || !hasupper) {
- do {
- if (ISLOWER(*s)) haslower = 1;
- if (ISUPPER(*s)) hasupper = 1;
- } while (*++s && (!haslower || !hasupper));
- len = s - name;
- }
- len += strlen(s);
- if (len++ > ENCODING_NAMELEN_MAX) return;
- MEMCPY(s = ALLOCA_N(char, len), name, char, len);
- name = s;
- if (!valid) {
- if (ISLOWER(*s)) *s = ONIGENC_ASCII_CODE_TO_UPPER_CASE((int)*s);
- for (; *s; ++s) {
- if (!ISALNUM(*s)) *s = '_';
- }
- if (hasupper) {
- rb_define_const(rb_cEncoding, name, encoding);
- }
- }
- if (haslower) {
- for (s = (char *)name; *s; ++s) {
- if (ISLOWER(*s)) *s = ONIGENC_ASCII_CODE_TO_UPPER_CASE((int)*s);
- }
- rb_define_const(rb_cEncoding, name, encoding);
- }
+ size_t len = s - name;
+ if (len > ENCODING_NAMELEN_MAX) return;
+ if (!haslower || !hasupper) {
+ do {
+ if (ISLOWER(*s)) haslower = 1;
+ if (ISUPPER(*s)) hasupper = 1;
+ } while (*++s && (!haslower || !hasupper));
+ len = s - name;
+ }
+ len += strlen(s);
+ if (len++ > ENCODING_NAMELEN_MAX) return;
+ MEMCPY(s = ALLOCA_N(char, len), name, char, len);
+ name = s;
+ if (!valid) {
+ if (ISLOWER(*s)) *s = ONIGENC_ASCII_CODE_TO_UPPER_CASE((int)*s);
+ for (; *s; ++s) {
+ if (!ISALNUM(*s)) *s = '_';
+ }
+ if (hasupper) {
+ rb_define_const(rb_cEncoding, name, encoding);
+ }
+ }
+ if (haslower) {
+ for (s = (char *)name; *s; ++s) {
+ if (ISLOWER(*s)) *s = ONIGENC_ASCII_CODE_TO_UPPER_CASE((int)*s);
+ }
+ rb_define_const(rb_cEncoding, name, encoding);
+ }
}
}
@@ -1877,15 +1823,8 @@ rb_enc_name_list_i(st_data_t name, st_data_t idx, st_data_t arg)
static VALUE
rb_enc_name_list(VALUE klass)
{
- VALUE ary;
-
- GLOBAL_ENC_TABLE_ENTER(enc_table);
- {
- ary = rb_ary_new2(enc_table->names->num_entries);
- st_foreach(enc_table->names, rb_enc_name_list_i, (st_data_t)ary);
- }
- GLOBAL_ENC_TABLE_LEAVE();
-
+ VALUE ary = rb_ary_new2(global_enc_table.names->num_entries);
+ st_foreach(global_enc_table.names, rb_enc_name_list_i, (st_data_t)ary);
return ary;
}
@@ -1898,14 +1837,14 @@ rb_enc_aliases_enc_i(st_data_t name, st_data_t orig, st_data_t arg)
VALUE key, str = rb_ary_entry(ary, idx);
if (NIL_P(str)) {
- rb_encoding *enc = rb_enc_from_index(idx);
+ rb_encoding *enc = rb_enc_from_index(idx);
- if (!enc) return ST_CONTINUE;
- if (STRCASECMP((char*)name, rb_enc_name(enc)) == 0) {
- return ST_CONTINUE;
- }
- str = rb_fstring_cstr(rb_enc_name(enc));
- rb_ary_store(ary, idx, str);
+ if (!enc) return ST_CONTINUE;
+ if (STRCASECMP((char*)name, rb_enc_name(enc)) == 0) {
+ return ST_CONTINUE;
+ }
+ str = rb_fstring_cstr(rb_enc_name(enc));
+ rb_ary_store(ary, idx, str);
}
key = rb_fstring_cstr((char *)name);
rb_hash_aset(aliases, key, str);
@@ -1931,8 +1870,7 @@ rb_enc_aliases(VALUE klass)
aliases[0] = rb_hash_new();
aliases[1] = rb_ary_new();
- GLOBAL_ENC_TABLE_EVAL(enc_table,
- st_foreach(enc_table->names, rb_enc_aliases_enc_i, (st_data_t)aliases));
+ st_foreach(global_enc_table.names, rb_enc_aliases_enc_i, (st_data_t)aliases);
return aliases[0];
}
@@ -2001,18 +1939,12 @@ Init_Encoding(void)
struct enc_table *enc_table = &global_enc_table;
- if (DEFAULT_ENCODING_LIST_CAPA < enc_table->count) rb_bug("DEFAULT_ENCODING_LIST_CAPA is too small");
-
- list = rb_additional_encoding_list = rb_ary_new();
- RBASIC_CLEAR_CLASS(list);
- rb_gc_register_mark_object(list);
-
- list = rb_default_encoding_list = rb_ary_new2(DEFAULT_ENCODING_LIST_CAPA);
+ list = rb_encoding_list = rb_ary_new2(ENCODING_LIST_CAPA);
RBASIC_CLEAR_CLASS(list);
rb_gc_register_mark_object(list);
for (i = 0; i < enc_table->count; ++i) {
- rb_ary_push(list, enc_new(enc_table->list[i].enc));
+ rb_ary_push(list, enc_new(enc_table->list[i].enc));
}
rb_marshal_define_compat(rb_cEncoding, Qnil, 0, enc_m_loader);
@@ -2029,5 +1961,5 @@ Init_encodings(void)
void
rb_enc_foreach_name(int (*func)(st_data_t name, st_data_t idx, st_data_t arg), st_data_t arg)
{
- GLOBAL_ENC_TABLE_EVAL(enc_table, st_foreach(enc_table->names, func, arg));
+ st_foreach(global_enc_table.names, func, arg);
}
diff --git a/enum.c b/enum.c
index 205ea6a451..b3c715e0a1 100644
--- a/enum.c
+++ b/enum.c
@@ -65,9 +65,9 @@ static VALUE
enum_yield(int argc, VALUE ary)
{
if (argc > 1)
- return rb_yield_force_blockarg(ary);
+ return rb_yield_force_blockarg(ary);
if (argc == 1)
- return rb_yield(ary);
+ return rb_yield(ary);
return rb_yield_values2(0, 0);
}
@@ -77,9 +77,9 @@ enum_yield_array(VALUE ary)
long len = RARRAY_LEN(ary);
if (len > 1)
- return rb_yield_force_blockarg(ary);
+ return rb_yield_force_blockarg(ary);
if (len == 1)
- return rb_yield(RARRAY_AREF(ary, 0));
+ return rb_yield(RARRAY_AREF(ary, 0));
return rb_yield_values2(0, 0);
}
@@ -90,7 +90,7 @@ grep_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
ENUM_WANT_SVALUE();
if (RTEST(rb_funcallv(memo->v1, id_eqq, 1, &i)) == RTEST(memo->u3.value)) {
- rb_ary_push(memo->v2, i);
+ rb_ary_push(memo->v2, i);
}
return Qnil;
}
@@ -106,7 +106,7 @@ grep_regexp_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
converted_element = SYMBOL_P(i) ? i : rb_check_string_type(i);
match = NIL_P(converted_element) ? Qfalse : rb_reg_match_p(memo->v1, i, 0);
if (match == memo->u3.value) {
- rb_ary_push(memo->v2, i);
+ rb_ary_push(memo->v2, i);
}
return Qnil;
}
@@ -118,7 +118,7 @@ grep_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
ENUM_WANT_SVALUE();
if (RTEST(rb_funcallv(memo->v1, id_eqq, 1, &i)) == RTEST(memo->u3.value)) {
- rb_ary_push(memo->v2, enum_yield(argc, i));
+ rb_ary_push(memo->v2, enum_yield(argc, i));
}
return Qnil;
}
@@ -130,14 +130,14 @@ enum_grep0(VALUE obj, VALUE pat, VALUE test)
struct MEMO *memo = MEMO_NEW(pat, ary, test);
rb_block_call_func_t fn;
if (rb_block_given_p()) {
- fn = grep_iter_i;
+ fn = grep_iter_i;
}
else if (RB_TYPE_P(pat, T_REGEXP) &&
LIKELY(rb_method_basic_definition_p(CLASS_OF(pat), idEqq))) {
- fn = grep_regexp_i;
+ fn = grep_regexp_i;
}
else {
- fn = grep_i;
+ fn = grep_i;
}
rb_block_call(obj, id_each, 0, 0, fn, (VALUE)memo);
@@ -214,13 +214,13 @@ static void
imemo_count_up(struct MEMO *memo)
{
if (memo->flags & COUNT_BIGNUM) {
- MEMO_V3_SET(memo, rb_int_succ(memo->u3.value));
+ MEMO_V3_SET(memo, rb_int_succ(memo->u3.value));
}
else if (++memo->u3.cnt == 0) {
- /* overflow */
- unsigned long buf[2] = {0, 1};
- MEMO_V3_SET(memo, rb_big_unpack(buf, 2));
- memo->flags |= COUNT_BIGNUM;
+ /* overflow */
+ unsigned long buf[2] = {0, 1};
+ MEMO_V3_SET(memo, rb_big_unpack(buf, 2));
+ memo->flags |= COUNT_BIGNUM;
}
}
@@ -228,10 +228,10 @@ static VALUE
imemo_count_value(struct MEMO *memo)
{
if (memo->flags & COUNT_BIGNUM) {
- return memo->u3.value;
+ return memo->u3.value;
}
else {
- return ULONG2NUM(memo->u3.cnt);
+ return ULONG2NUM(memo->u3.cnt);
}
}
@@ -243,7 +243,7 @@ count_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
ENUM_WANT_SVALUE();
if (rb_equal(i, memo->v1)) {
- imemo_count_up(memo);
+ imemo_count_up(memo);
}
return Qnil;
}
@@ -254,7 +254,7 @@ count_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
struct MEMO *memo = MEMO_CAST(memop);
if (RTEST(rb_yield_values2(argc, argv))) {
- imemo_count_up(memo);
+ imemo_count_up(memo);
}
return Qnil;
}
@@ -302,18 +302,18 @@ enum_count(int argc, VALUE *argv, VALUE obj)
rb_block_call_func *func;
if (argc == 0) {
- if (rb_block_given_p()) {
- func = count_iter_i;
- }
- else {
- func = count_all_i;
- }
+ if (rb_block_given_p()) {
+ func = count_iter_i;
+ }
+ else {
+ func = count_all_i;
+ }
}
else {
- rb_scan_args(argc, argv, "1", &item);
- if (rb_block_given_p()) {
- rb_warn("given block not used");
- }
+ rb_scan_args(argc, argv, "1", &item);
+ if (rb_block_given_p()) {
+ rb_warn("given block not used");
+ }
func = count_i;
}
@@ -328,10 +328,10 @@ find_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
ENUM_WANT_SVALUE();
if (RTEST(enum_yield(argc, i))) {
- struct MEMO *memo = MEMO_CAST(memop);
- MEMO_V1_SET(memo, i);
- memo->u3.cnt = 1;
- rb_iter_break();
+ struct MEMO *memo = MEMO_CAST(memop);
+ MEMO_V1_SET(memo, i);
+ memo->u3.cnt = 1;
+ rb_iter_break();
}
return Qnil;
}
@@ -368,10 +368,10 @@ enum_find(int argc, VALUE *argv, VALUE obj)
memo = MEMO_NEW(Qundef, 0, 0);
rb_block_call(obj, id_each, 0, 0, find_i, (VALUE)memo);
if (memo->u3.cnt) {
- return memo->v1;
+ return memo->v1;
}
if (!NIL_P(if_none)) {
- return rb_funcallv(if_none, id_call, 0, 0);
+ return rb_funcallv(if_none, id_call, 0, 0);
}
return Qnil;
}
@@ -384,8 +384,8 @@ find_index_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
ENUM_WANT_SVALUE();
if (rb_equal(i, memo->v2)) {
- MEMO_V1_SET(memo, imemo_count_value(memo));
- rb_iter_break();
+ MEMO_V1_SET(memo, imemo_count_value(memo));
+ rb_iter_break();
}
imemo_count_up(memo);
return Qnil;
@@ -397,8 +397,8 @@ find_index_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
struct MEMO *memo = MEMO_CAST(memop);
if (RTEST(rb_yield_values2(argc, argv))) {
- MEMO_V1_SET(memo, imemo_count_value(memo));
- rb_iter_break();
+ MEMO_V1_SET(memo, imemo_count_value(memo));
+ rb_iter_break();
}
imemo_count_up(memo);
return Qnil;
@@ -440,10 +440,10 @@ enum_find_index(int argc, VALUE *argv, VALUE obj)
func = find_index_iter_i;
}
else {
- rb_scan_args(argc, argv, "1", &condition_value);
- if (rb_block_given_p()) {
- rb_warn("given block not used");
- }
+ rb_scan_args(argc, argv, "1", &condition_value);
+ if (rb_block_given_p()) {
+ rb_warn("given block not used");
+ }
func = find_index_i;
}
@@ -458,7 +458,7 @@ find_all_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
ENUM_WANT_SVALUE();
if (RTEST(enum_yield(argc, i))) {
- rb_ary_push(ary, i);
+ rb_ary_push(ary, i);
}
return Qnil;
}
@@ -566,7 +566,7 @@ reject_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
ENUM_WANT_SVALUE();
if (!RTEST(enum_yield(argc, i))) {
- rb_ary_push(ary, i);
+ rb_ary_push(ary, i);
}
return Qnil;
}
@@ -658,10 +658,10 @@ flat_map_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
tmp = rb_check_array_type(i);
if (NIL_P(tmp)) {
- rb_ary_push(ary, i);
+ rb_ary_push(ary, i);
}
else {
- rb_ary_concat(ary, tmp);
+ rb_ary_concat(ary, tmp);
}
return Qnil;
}
@@ -779,11 +779,11 @@ inject_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, p))
ENUM_WANT_SVALUE();
- if (memo->v1 == Qundef) {
- MEMO_V1_SET(memo, i);
+ if (UNDEF_P(memo->v1)) {
+ MEMO_V1_SET(memo, i);
}
else {
- MEMO_V1_SET(memo, rb_yield_values(2, memo->v1, i));
+ MEMO_V1_SET(memo, rb_yield_values(2, memo->v1, i));
}
return Qnil;
}
@@ -796,18 +796,18 @@ inject_op_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, p))
ENUM_WANT_SVALUE();
- if (memo->v1 == Qundef) {
- MEMO_V1_SET(memo, i);
+ if (UNDEF_P(memo->v1)) {
+ MEMO_V1_SET(memo, i);
}
else if (SYMBOL_P(name = memo->u3.value)) {
- const ID mid = SYM2ID(name);
- MEMO_V1_SET(memo, rb_funcallv_public(memo->v1, mid, 1, &i));
+ const ID mid = SYM2ID(name);
+ MEMO_V1_SET(memo, rb_funcallv_public(memo->v1, mid, 1, &i));
}
else {
- VALUE args[2];
- args[0] = name;
- args[1] = i;
- MEMO_V1_SET(memo, rb_f_send(numberof(args), args, memo->v1));
+ VALUE args[2];
+ args[0] = name;
+ args[1] = i;
+ MEMO_V1_SET(memo, rb_f_send(numberof(args), args, memo->v1));
}
return Qnil;
}
@@ -820,9 +820,9 @@ ary_inject_op(VALUE ary, VALUE init, VALUE op)
long i, n;
if (RARRAY_LEN(ary) == 0)
- return init == Qundef ? Qnil : init;
+ return UNDEF_P(init) ? Qnil : init;
- if (init == Qundef) {
+ if (UNDEF_P(init)) {
v = RARRAY_AREF(ary, 0);
i = 1;
if (RARRAY_LEN(ary) == 1)
@@ -835,9 +835,9 @@ ary_inject_op(VALUE ary, VALUE init, VALUE op)
id = SYM2ID(op);
if (id == idPLUS) {
- if (RB_INTEGER_TYPE_P(v) &&
- rb_method_basic_definition_p(rb_cInteger, idPLUS) &&
- rb_obj_respond_to(v, idPLUS, FALSE)) {
+ if (RB_INTEGER_TYPE_P(v) &&
+ rb_method_basic_definition_p(rb_cInteger, idPLUS) &&
+ rb_obj_respond_to(v, idPLUS, FALSE)) {
n = 0;
for (; i < RARRAY_LEN(ary); i++) {
e = RARRAY_AREF(ary, i);
@@ -1021,25 +1021,25 @@ enum_inject(int argc, VALUE *argv, VALUE obj)
switch (num_args) {
case 0:
- init = Qundef;
- break;
+ init = Qundef;
+ break;
case 1:
- if (rb_block_given_p()) {
- break;
- }
- id = rb_check_id(&init);
- op = id ? ID2SYM(id) : init;
- init = Qundef;
- iter = inject_op_i;
- break;
+ if (rb_block_given_p()) {
+ break;
+ }
+ id = rb_check_id(&init);
+ op = id ? ID2SYM(id) : init;
+ init = Qundef;
+ iter = inject_op_i;
+ break;
case 2:
- if (rb_block_given_p()) {
- rb_warning("given block not used");
- }
- id = rb_check_id(&op);
- if (id) op = ID2SYM(id);
- iter = inject_op_i;
- break;
+ if (rb_block_given_p()) {
+ rb_warning("given block not used");
+ }
+ id = rb_check_id(&op);
+ if (id) op = ID2SYM(id);
+ iter = inject_op_i;
+ break;
}
if (iter == inject_op_i &&
@@ -1051,7 +1051,7 @@ enum_inject(int argc, VALUE *argv, VALUE obj)
memo = MEMO_NEW(init, Qnil, op);
rb_block_call(obj, id_each, 0, 0, iter, (VALUE)memo);
- if (memo->v1 == Qundef) return Qnil;
+ if (UNDEF_P(memo->v1)) return Qnil;
return memo->v1;
}
@@ -1063,10 +1063,10 @@ partition_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arys))
ENUM_WANT_SVALUE();
if (RTEST(enum_yield(argc, i))) {
- ary = memo->v1;
+ ary = memo->v1;
}
else {
- ary = memo->v2;
+ ary = memo->v2;
}
rb_ary_push(ary, i);
return Qnil;
@@ -1124,11 +1124,11 @@ group_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
group = enum_yield(argc, i);
values = rb_hash_aref(hash, group);
if (!RB_TYPE_P(values, T_ARRAY)) {
- values = rb_ary_new3(1, i);
- rb_hash_aset(hash, group, values);
+ values = rb_ary_new3(1, i);
+ rb_hash_aset(hash, group, values);
}
else {
- rb_ary_push(values, i);
+ rb_ary_push(values, i);
}
return Qnil;
}
@@ -1288,12 +1288,12 @@ enum_first(int argc, VALUE *argv, VALUE obj)
struct MEMO *memo;
rb_check_arity(argc, 0, 1);
if (argc > 0) {
- return enum_take(obj, argv[0]);
+ return enum_take(obj, argv[0]);
}
else {
- memo = MEMO_NEW(Qnil, 0, 0);
- rb_block_call(obj, id_each, 0, 0, first_i, (VALUE)memo);
- return memo->v1;
+ memo = MEMO_NEW(Qnil, 0, 0);
+ rb_block_call(obj, id_each, 0, 0, first_i, (VALUE)memo);
+ return memo->v1;
}
}
@@ -1354,18 +1354,18 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _data))
v = enum_yield(argc, i);
if (RBASIC(ary)->klass) {
- rb_raise(rb_eRuntimeError, "sort_by reentered");
+ rb_raise(rb_eRuntimeError, "sort_by reentered");
}
if (RARRAY_LEN(data->buf) != SORT_BY_BUFSIZE*2) {
- rb_raise(rb_eRuntimeError, "sort_by reentered");
+ rb_raise(rb_eRuntimeError, "sort_by reentered");
}
RARRAY_ASET(data->buf, data->n*2, v);
RARRAY_ASET(data->buf, data->n*2+1, i);
data->n++;
if (data->n == SORT_BY_BUFSIZE) {
- rb_ary_concat(ary, data->buf);
- data->n = 0;
+ rb_ary_concat(ary, data->buf);
+ data->n = 0;
}
return Qnil;
}
@@ -1373,19 +1373,18 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _data))
static int
sort_by_cmp(const void *ap, const void *bp, void *data)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE a;
VALUE b;
VALUE ary = (VALUE)data;
if (RBASIC(ary)->klass) {
- rb_raise(rb_eRuntimeError, "sort_by reentered");
+ rb_raise(rb_eRuntimeError, "sort_by reentered");
}
a = *(VALUE *)ap;
b = *(VALUE *)bp;
- return OPTIMIZED_CMP(a, b, cmp_opt);
+ return OPTIMIZED_CMP(a, b);
}
/*
@@ -1481,13 +1480,13 @@ enum_sort_by(VALUE obj)
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
if (RB_TYPE_P(obj, T_ARRAY) && RARRAY_LEN(obj) <= LONG_MAX/2) {
- ary = rb_ary_new2(RARRAY_LEN(obj)*2);
+ ary = rb_ary_new2(RARRAY_LEN(obj)*2);
}
else {
- ary = rb_ary_new();
+ ary = rb_ary_new();
}
RBASIC_CLEAR_CLASS(ary);
- buf = rb_ary_tmp_new(SORT_BY_BUFSIZE*2);
+ buf = rb_ary_hidden_new(SORT_BY_BUFSIZE*2);
rb_ary_store(buf, SORT_BY_BUFSIZE*2-1, Qnil);
memo = MEMO_NEW(0, 0, 0);
data = (struct sort_by_data *)&memo->v1;
@@ -1498,8 +1497,8 @@ enum_sort_by(VALUE obj)
ary = data->ary;
buf = data->buf;
if (data->n) {
- rb_ary_resize(buf, data->n*2);
- rb_ary_concat(ary, buf);
+ rb_ary_resize(buf, data->n*2);
+ rb_ary_concat(ary, buf);
}
if (RARRAY_LEN(ary) > 2) {
RARRAY_PTR_USE(ary, ptr,
@@ -1507,10 +1506,10 @@ enum_sort_by(VALUE obj)
sort_by_cmp, (void *)ary));
}
if (RBASIC(ary)->klass) {
- rb_raise(rb_eRuntimeError, "sort_by reentered");
+ rb_raise(rb_eRuntimeError, "sort_by reentered");
}
for (i=1; i<RARRAY_LEN(ary); i+=2) {
- RARRAY_ASET(ary, i/2, RARRAY_AREF(ary, i));
+ RARRAY_ASET(ary, i/2, RARRAY_AREF(ary, i));
}
rb_ary_resize(ary, RARRAY_LEN(ary)/2);
RBASIC_SET_CLASS_RAW(ary, rb_cArray);
@@ -1556,8 +1555,8 @@ enum_##name##_func(VALUE result, struct MEMO *memo)
DEFINE_ENUMFUNCS(all)
{
if (!RTEST(result)) {
- MEMO_V1_SET(memo, Qfalse);
- rb_iter_break();
+ MEMO_V1_SET(memo, Qfalse);
+ rb_iter_break();
}
return Qnil;
}
@@ -1617,8 +1616,8 @@ enum_all(int argc, VALUE *argv, VALUE obj)
DEFINE_ENUMFUNCS(any)
{
if (RTEST(result)) {
- MEMO_V1_SET(memo, Qtrue);
- rb_iter_break();
+ MEMO_V1_SET(memo, Qtrue);
+ rb_iter_break();
}
return Qnil;
}
@@ -1677,13 +1676,13 @@ enum_any(int argc, VALUE *argv, VALUE obj)
DEFINE_ENUMFUNCS(one)
{
if (RTEST(result)) {
- if (memo->v1 == Qundef) {
- MEMO_V1_SET(memo, Qtrue);
- }
- else if (memo->v1 == Qtrue) {
- MEMO_V1_SET(memo, Qfalse);
- rb_iter_break();
- }
+ if (UNDEF_P(memo->v1)) {
+ MEMO_V1_SET(memo, Qtrue);
+ }
+ else if (memo->v1 == Qtrue) {
+ MEMO_V1_SET(memo, Qfalse);
+ rb_iter_break();
+ }
}
return Qnil;
}
@@ -1713,11 +1712,10 @@ cmpint_reenter_check(struct nmin_data *data, VALUE val)
static int
nmin_cmp(const void *ap, const void *bp, void *_data)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
struct nmin_data *data = (struct nmin_data *)_data;
VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp;
#define rb_cmpint(cmp, a, b) rb_cmpint(cmpint_reenter_check(data, (cmp)), a, b)
- return OPTIMIZED_CMP(a, b, cmp_opt);
+ return OPTIMIZED_CMP(a, b);
#undef rb_cmpint
}
@@ -1745,7 +1743,7 @@ nmin_filter(struct nmin_data *data)
long i, j;
if (data->curlen <= data->n)
- return;
+ return;
n = data->n;
beg = RARRAY_PTR(data->buf);
@@ -1765,46 +1763,46 @@ nmin_filter(struct nmin_data *data)
} while (0)
while (1) {
- long pivot_index = left + (right-left)/2;
- long num_pivots = 1;
-
- SWAP(pivot_index, right);
- pivot_index = right;
-
- store_index = left;
- i = left;
- while (i <= right-num_pivots) {
- int c = data->cmpfunc(GETPTR(i), GETPTR(pivot_index), data);
- if (data->rev)
- c = -c;
- if (c == 0) {
- SWAP(i, right-num_pivots);
- num_pivots++;
- continue;
- }
- if (c < 0) {
- SWAP(i, store_index);
- store_index++;
- }
- i++;
- }
- j = store_index;
- for (i = right; right-num_pivots < i; i--) {
- if (i <= j)
- break;
- SWAP(j, i);
- j++;
- }
-
- if (store_index <= n && n <= store_index+num_pivots)
- break;
-
- if (n < store_index) {
- right = store_index-1;
- }
- else {
- left = store_index+num_pivots;
- }
+ long pivot_index = left + (right-left)/2;
+ long num_pivots = 1;
+
+ SWAP(pivot_index, right);
+ pivot_index = right;
+
+ store_index = left;
+ i = left;
+ while (i <= right-num_pivots) {
+ int c = data->cmpfunc(GETPTR(i), GETPTR(pivot_index), data);
+ if (data->rev)
+ c = -c;
+ if (c == 0) {
+ SWAP(i, right-num_pivots);
+ num_pivots++;
+ continue;
+ }
+ if (c < 0) {
+ SWAP(i, store_index);
+ store_index++;
+ }
+ i++;
+ }
+ j = store_index;
+ for (i = right; right-num_pivots < i; i--) {
+ if (i <= j)
+ break;
+ SWAP(j, i);
+ j++;
+ }
+
+ if (store_index <= n && n <= store_index+num_pivots)
+ break;
+
+ if (n < store_index) {
+ right = store_index-1;
+ }
+ else {
+ left = store_index+num_pivots;
+ }
}
#undef GETPTR
#undef SWAP
@@ -1823,11 +1821,11 @@ nmin_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _data))
ENUM_WANT_SVALUE();
if (data->by)
- cmpv = enum_yield(argc, i);
+ cmpv = enum_yield(argc, i);
else
- cmpv = i;
+ cmpv = i;
- if (data->limit != Qundef) {
+ if (!UNDEF_P(data->limit)) {
int c = data->cmpfunc(&cmpv, &data->limit, data);
if (data->rev)
c = -c;
@@ -1836,13 +1834,13 @@ nmin_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _data))
}
if (data->by)
- rb_ary_push(data->buf, cmpv);
+ rb_ary_push(data->buf, cmpv);
rb_ary_push(data->buf, i);
data->curlen++;
if (data->curlen == data->bufmax) {
- nmin_filter(data);
+ nmin_filter(data);
}
return Qnil;
@@ -1863,28 +1861,28 @@ rb_nmin_run(VALUE obj, VALUE num, int by, int rev, int ary)
rb_raise(rb_eArgError, "too big size");
data.bufmax = data.n * 4;
data.curlen = 0;
- data.buf = rb_ary_tmp_new(data.bufmax * (by ? 2 : 1));
+ data.buf = rb_ary_hidden_new(data.bufmax * (by ? 2 : 1));
data.limit = Qundef;
data.cmpfunc = by ? nmin_cmp :
rb_block_given_p() ? nmin_block_cmp :
- nmin_cmp;
+ nmin_cmp;
data.rev = rev;
data.by = by;
if (ary) {
- long i;
- for (i = 0; i < RARRAY_LEN(obj); i++) {
- VALUE args[1];
- args[0] = RARRAY_AREF(obj, i);
+ long i;
+ for (i = 0; i < RARRAY_LEN(obj); i++) {
+ VALUE args[1];
+ args[0] = RARRAY_AREF(obj, i);
nmin_i(obj, (VALUE)&data, 1, args, Qundef);
- }
+ }
}
else {
- rb_block_call(obj, id_each, 0, 0, nmin_i, (VALUE)&data);
+ rb_block_call(obj, id_each, 0, 0, nmin_i, (VALUE)&data);
}
nmin_filter(&data);
result = data.buf;
if (by) {
- long i;
+ long i;
RARRAY_PTR_USE(result, ptr, {
ruby_qsort(ptr,
RARRAY_LEN(result)/2,
@@ -1894,7 +1892,7 @@ rb_nmin_run(VALUE obj, VALUE num, int by, int rev, int ary)
ptr[i/2] = ptr[i];
}
});
- rb_ary_resize(result, RARRAY_LEN(result)/2);
+ rb_ary_resize(result, RARRAY_LEN(result)/2);
}
else {
RARRAY_PTR_USE(result, ptr, {
@@ -1962,15 +1960,15 @@ enum_one(int argc, VALUE *argv, VALUE obj)
WARN_UNUSED_BLOCK(argc);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(one), (VALUE)memo);
result = memo->v1;
- if (result == Qundef) return Qfalse;
+ if (UNDEF_P(result)) return Qfalse;
return result;
}
DEFINE_ENUMFUNCS(none)
{
if (RTEST(result)) {
- MEMO_V1_SET(memo, Qfalse);
- rb_iter_break();
+ MEMO_V1_SET(memo, Qfalse);
+ rb_iter_break();
}
return Qnil;
}
@@ -2027,7 +2025,6 @@ enum_none(int argc, VALUE *argv, VALUE obj)
struct min_t {
VALUE min;
- struct cmp_opt_data cmp_opt;
};
static VALUE
@@ -2037,13 +2034,13 @@ min_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
ENUM_WANT_SVALUE();
- if (memo->min == Qundef) {
- memo->min = i;
+ if (UNDEF_P(memo->min)) {
+ memo->min = i;
}
else {
- if (OPTIMIZED_CMP(i, memo->min, memo->cmp_opt) < 0) {
- memo->min = i;
- }
+ if (OPTIMIZED_CMP(i, memo->min) < 0) {
+ memo->min = i;
+ }
}
return Qnil;
}
@@ -2056,14 +2053,14 @@ min_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
ENUM_WANT_SVALUE();
- if (memo->min == Qundef) {
- memo->min = i;
+ if (UNDEF_P(memo->min)) {
+ memo->min = i;
}
else {
- cmp = rb_yield_values(2, i, memo->min);
- if (rb_cmpint(cmp, i, memo->min) < 0) {
- memo->min = i;
- }
+ cmp = rb_yield_values(2, i, memo->min);
+ if (rb_cmpint(cmp, i, memo->min) < 0) {
+ memo->min = i;
+ }
}
return Qnil;
}
@@ -2130,7 +2127,7 @@ static VALUE
enum_min(int argc, VALUE *argv, VALUE obj)
{
VALUE memo;
- struct min_t *m = NEW_CMP_OPT_MEMO(struct min_t, memo);
+ struct min_t *m = NEW_MEMO_FOR(struct min_t, memo);
VALUE result;
VALUE num;
@@ -2138,22 +2135,19 @@ enum_min(int argc, VALUE *argv, VALUE obj)
return rb_nmin_run(obj, num, 0, 0, 0);
m->min = Qundef;
- m->cmp_opt.opt_methods = 0;
- m->cmp_opt.opt_inited = 0;
if (rb_block_given_p()) {
- rb_block_call(obj, id_each, 0, 0, min_ii, memo);
+ rb_block_call(obj, id_each, 0, 0, min_ii, memo);
}
else {
- rb_block_call(obj, id_each, 0, 0, min_i, memo);
+ rb_block_call(obj, id_each, 0, 0, min_i, memo);
}
result = m->min;
- if (result == Qundef) return Qnil;
+ if (UNDEF_P(result)) return Qnil;
return result;
}
struct max_t {
VALUE max;
- struct cmp_opt_data cmp_opt;
};
static VALUE
@@ -2163,13 +2157,13 @@ max_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
ENUM_WANT_SVALUE();
- if (memo->max == Qundef) {
- memo->max = i;
+ if (UNDEF_P(memo->max)) {
+ memo->max = i;
}
else {
- if (OPTIMIZED_CMP(i, memo->max, memo->cmp_opt) > 0) {
- memo->max = i;
- }
+ if (OPTIMIZED_CMP(i, memo->max) > 0) {
+ memo->max = i;
+ }
}
return Qnil;
}
@@ -2182,14 +2176,14 @@ max_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
ENUM_WANT_SVALUE();
- if (memo->max == Qundef) {
- memo->max = i;
+ if (UNDEF_P(memo->max)) {
+ memo->max = i;
}
else {
- cmp = rb_yield_values(2, i, memo->max);
- if (rb_cmpint(cmp, i, memo->max) > 0) {
- memo->max = i;
- }
+ cmp = rb_yield_values(2, i, memo->max);
+ if (rb_cmpint(cmp, i, memo->max) > 0) {
+ memo->max = i;
+ }
}
return Qnil;
}
@@ -2255,7 +2249,7 @@ static VALUE
enum_max(int argc, VALUE *argv, VALUE obj)
{
VALUE memo;
- struct max_t *m = NEW_CMP_OPT_MEMO(struct max_t, memo);
+ struct max_t *m = NEW_MEMO_FOR(struct max_t, memo);
VALUE result;
VALUE num;
@@ -2263,16 +2257,14 @@ enum_max(int argc, VALUE *argv, VALUE obj)
return rb_nmin_run(obj, num, 0, 1, 0);
m->max = Qundef;
- m->cmp_opt.opt_methods = 0;
- m->cmp_opt.opt_inited = 0;
if (rb_block_given_p()) {
- rb_block_call(obj, id_each, 0, 0, max_ii, (VALUE)memo);
+ rb_block_call(obj, id_each, 0, 0, max_ii, (VALUE)memo);
}
else {
- rb_block_call(obj, id_each, 0, 0, max_i, (VALUE)memo);
+ rb_block_call(obj, id_each, 0, 0, max_i, (VALUE)memo);
}
result = m->max;
- if (result == Qundef) return Qnil;
+ if (UNDEF_P(result)) return Qnil;
return result;
}
@@ -2280,7 +2272,6 @@ struct minmax_t {
VALUE min;
VALUE max;
VALUE last;
- struct cmp_opt_data cmp_opt;
};
static void
@@ -2288,19 +2279,19 @@ minmax_i_update(VALUE i, VALUE j, struct minmax_t *memo)
{
int n;
- if (memo->min == Qundef) {
- memo->min = i;
- memo->max = j;
+ if (UNDEF_P(memo->min)) {
+ memo->min = i;
+ memo->max = j;
}
else {
- n = OPTIMIZED_CMP(i, memo->min, memo->cmp_opt);
- if (n < 0) {
- memo->min = i;
- }
- n = OPTIMIZED_CMP(j, memo->max, memo->cmp_opt);
- if (n > 0) {
- memo->max = j;
- }
+ n = OPTIMIZED_CMP(i, memo->min);
+ if (n < 0) {
+ memo->min = i;
+ }
+ n = OPTIMIZED_CMP(j, memo->max);
+ if (n > 0) {
+ memo->max = j;
+ }
}
}
@@ -2313,14 +2304,14 @@ minmax_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
ENUM_WANT_SVALUE();
- if (memo->last == Qundef) {
+ if (UNDEF_P(memo->last)) {
memo->last = i;
return Qnil;
}
j = memo->last;
memo->last = Qundef;
- n = OPTIMIZED_CMP(j, i, memo->cmp_opt);
+ n = OPTIMIZED_CMP(j, i);
if (n == 0)
i = j;
else if (n < 0) {
@@ -2340,19 +2331,19 @@ minmax_ii_update(VALUE i, VALUE j, struct minmax_t *memo)
{
int n;
- if (memo->min == Qundef) {
- memo->min = i;
- memo->max = j;
+ if (UNDEF_P(memo->min)) {
+ memo->min = i;
+ memo->max = j;
}
else {
- n = rb_cmpint(rb_yield_values(2, i, memo->min), i, memo->min);
- if (n < 0) {
- memo->min = i;
- }
- n = rb_cmpint(rb_yield_values(2, j, memo->max), j, memo->max);
- if (n > 0) {
- memo->max = j;
- }
+ n = rb_cmpint(rb_yield_values(2, i, memo->min), i, memo->min);
+ if (n < 0) {
+ memo->min = i;
+ }
+ n = rb_cmpint(rb_yield_values(2, j, memo->max), j, memo->max);
+ if (n > 0) {
+ memo->max = j;
+ }
}
}
@@ -2365,7 +2356,7 @@ minmax_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
ENUM_WANT_SVALUE();
- if (memo->last == Qundef) {
+ if (UNDEF_P(memo->last)) {
memo->last = i;
return Qnil;
}
@@ -2422,24 +2413,22 @@ static VALUE
enum_minmax(VALUE obj)
{
VALUE memo;
- struct minmax_t *m = NEW_CMP_OPT_MEMO(struct minmax_t, memo);
+ struct minmax_t *m = NEW_MEMO_FOR(struct minmax_t, memo);
m->min = Qundef;
m->last = Qundef;
- m->cmp_opt.opt_methods = 0;
- m->cmp_opt.opt_inited = 0;
if (rb_block_given_p()) {
- rb_block_call(obj, id_each, 0, 0, minmax_ii, memo);
- if (m->last != Qundef)
- minmax_ii_update(m->last, m->last, m);
+ rb_block_call(obj, id_each, 0, 0, minmax_ii, memo);
+ if (!UNDEF_P(m->last))
+ minmax_ii_update(m->last, m->last, m);
}
else {
- rb_block_call(obj, id_each, 0, 0, minmax_i, memo);
- if (m->last != Qundef)
- minmax_i_update(m->last, m->last, m);
+ rb_block_call(obj, id_each, 0, 0, minmax_i, memo);
+ if (!UNDEF_P(m->last))
+ minmax_i_update(m->last, m->last, m);
}
- if (m->min != Qundef) {
- return rb_assoc_new(m->min, m->max);
+ if (!UNDEF_P(m->min)) {
+ return rb_assoc_new(m->min, m->max);
}
return rb_assoc_new(Qnil, Qnil);
}
@@ -2447,20 +2436,19 @@ enum_minmax(VALUE obj)
static VALUE
min_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
struct MEMO *memo = MEMO_CAST(args);
VALUE v;
ENUM_WANT_SVALUE();
v = enum_yield(argc, i);
- if (memo->v1 == Qundef) {
- MEMO_V1_SET(memo, v);
- MEMO_V2_SET(memo, i);
+ if (UNDEF_P(memo->v1)) {
+ MEMO_V1_SET(memo, v);
+ MEMO_V2_SET(memo, i);
}
- else if (OPTIMIZED_CMP(v, memo->v1, cmp_opt) < 0) {
- MEMO_V1_SET(memo, v);
- MEMO_V2_SET(memo, i);
+ else if (OPTIMIZED_CMP(v, memo->v1) < 0) {
+ MEMO_V1_SET(memo, v);
+ MEMO_V2_SET(memo, i);
}
return Qnil;
}
@@ -2522,20 +2510,19 @@ enum_min_by(int argc, VALUE *argv, VALUE obj)
static VALUE
max_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
struct MEMO *memo = MEMO_CAST(args);
VALUE v;
ENUM_WANT_SVALUE();
v = enum_yield(argc, i);
- if (memo->v1 == Qundef) {
- MEMO_V1_SET(memo, v);
- MEMO_V2_SET(memo, i);
+ if (UNDEF_P(memo->v1)) {
+ MEMO_V1_SET(memo, v);
+ MEMO_V2_SET(memo, i);
}
- else if (OPTIMIZED_CMP(v, memo->v1, cmp_opt) > 0) {
- MEMO_V1_SET(memo, v);
- MEMO_V2_SET(memo, i);
+ else if (OPTIMIZED_CMP(v, memo->v1) > 0) {
+ MEMO_V1_SET(memo, v);
+ MEMO_V2_SET(memo, i);
}
return Qnil;
}
@@ -2606,30 +2593,27 @@ struct minmax_by_t {
static void
minmax_by_i_update(VALUE v1, VALUE v2, VALUE i1, VALUE i2, struct minmax_by_t *memo)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
-
- if (memo->min_bv == Qundef) {
- memo->min_bv = v1;
- memo->max_bv = v2;
- memo->min = i1;
- memo->max = i2;
+ if (UNDEF_P(memo->min_bv)) {
+ memo->min_bv = v1;
+ memo->max_bv = v2;
+ memo->min = i1;
+ memo->max = i2;
}
else {
- if (OPTIMIZED_CMP(v1, memo->min_bv, cmp_opt) < 0) {
- memo->min_bv = v1;
- memo->min = i1;
- }
- if (OPTIMIZED_CMP(v2, memo->max_bv, cmp_opt) > 0) {
- memo->max_bv = v2;
- memo->max = i2;
- }
+ if (OPTIMIZED_CMP(v1, memo->min_bv) < 0) {
+ memo->min_bv = v1;
+ memo->min = i1;
+ }
+ if (OPTIMIZED_CMP(v2, memo->max_bv) > 0) {
+ memo->max_bv = v2;
+ memo->max = i2;
+ }
}
}
static VALUE
minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
struct minmax_by_t *memo = MEMO_FOR(struct minmax_by_t, _memo);
VALUE vi, vj, j;
int n;
@@ -2638,7 +2622,7 @@ minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
vi = enum_yield(argc, i);
- if (memo->last_bv == Qundef) {
+ if (UNDEF_P(memo->last_bv)) {
memo->last_bv = vi;
memo->last = i;
return Qnil;
@@ -2647,7 +2631,7 @@ minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
j = memo->last;
memo->last_bv = Qundef;
- n = OPTIMIZED_CMP(vj, vi, cmp_opt);
+ n = OPTIMIZED_CMP(vj, vi);
if (n == 0) {
i = j;
vi = vj;
@@ -2705,7 +2689,7 @@ enum_minmax_by(VALUE obj)
m->last_bv = Qundef;
m->last = Qundef;
rb_block_call(obj, id_each, 0, 0, minmax_by_i, memo);
- if (m->last_bv != Qundef)
+ if (!UNDEF_P(m->last_bv))
minmax_by_i_update(m->last_bv, m->last_bv, m->last, m->last, m);
m = MEMO_FOR(struct minmax_by_t, memo);
return rb_assoc_new(m->min, m->max);
@@ -2717,8 +2701,8 @@ member_i(RB_BLOCK_CALL_FUNC_ARGLIST(iter, args))
struct MEMO *memo = MEMO_CAST(args);
if (rb_equal(rb_enum_values_pack(argc, argv), memo->v1)) {
- MEMO_V2_SET(memo, Qtrue);
- rb_iter_break();
+ MEMO_V2_SET(memo, Qtrue);
+ rb_iter_break();
}
return Qnil;
}
@@ -2935,14 +2919,14 @@ each_slice_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, m))
rb_ary_push(ary, i);
if (RARRAY_LEN(ary) == size) {
- v = rb_yield(ary);
+ v = rb_yield(ary);
- if (memo->v2) {
- MEMO_V1_SET(memo, rb_ary_new2(size));
- }
- else {
- rb_ary_clear(ary);
- }
+ if (memo->v2) {
+ MEMO_V1_SET(memo, rb_ary_new2(size));
+ }
+ else {
+ rb_ary_clear(ary);
+ }
}
return v;
@@ -3018,14 +3002,14 @@ each_cons_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
ENUM_WANT_SVALUE();
if (RARRAY_LEN(ary) == size) {
- rb_ary_shift(ary);
+ rb_ary_shift(ary);
}
rb_ary_push(ary, i);
if (RARRAY_LEN(ary) == size) {
- if (memo->v2) {
- ary = rb_ary_dup(ary);
- }
- v = rb_yield(ary);
+ if (memo->v2) {
+ ary = rb_ary_dup(ary);
+ }
+ v = rb_yield(ary);
}
return v;
}
@@ -3033,7 +3017,6 @@ each_cons_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
static VALUE
enum_each_cons_size(VALUE obj, VALUE args, VALUE eobj)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
const VALUE zero = LONG2FIX(0);
VALUE n, size;
long cons_size = NUM2LONG(RARRAY_AREF(args, 0));
@@ -3043,7 +3026,7 @@ enum_each_cons_size(VALUE obj, VALUE args, VALUE eobj)
if (NIL_P(size)) return Qnil;
n = add_int(size, 1 - cons_size);
- return (OPTIMIZED_CMP(n, zero, cmp_opt) == -1) ? zero : n;
+ return (OPTIMIZED_CMP(n, zero) == -1) ? zero : n;
}
/*
@@ -3130,20 +3113,20 @@ zip_ary(RB_BLOCK_CALL_FUNC_ARGLIST(val, memoval))
tmp = rb_ary_new2(RARRAY_LEN(args) + 1);
rb_ary_store(tmp, 0, rb_enum_values_pack(argc, argv));
for (i=0; i<RARRAY_LEN(args); i++) {
- VALUE e = RARRAY_AREF(args, i);
+ VALUE e = RARRAY_AREF(args, i);
- if (RARRAY_LEN(e) <= n) {
- rb_ary_push(tmp, Qnil);
- }
- else {
- rb_ary_push(tmp, RARRAY_AREF(e, n));
- }
+ if (RARRAY_LEN(e) <= n) {
+ rb_ary_push(tmp, Qnil);
+ }
+ else {
+ rb_ary_push(tmp, RARRAY_AREF(e, n));
+ }
}
if (NIL_P(result)) {
- enum_yield_array(tmp);
+ enum_yield_array(tmp);
}
else {
- rb_ary_push(result, tmp);
+ rb_ary_push(result, tmp);
}
RB_GC_GUARD(args);
@@ -3177,26 +3160,26 @@ zip_i(RB_BLOCK_CALL_FUNC_ARGLIST(val, memoval))
tmp = rb_ary_new2(RARRAY_LEN(args) + 1);
rb_ary_store(tmp, 0, rb_enum_values_pack(argc, argv));
for (i=0; i<RARRAY_LEN(args); i++) {
- if (NIL_P(RARRAY_AREF(args, i))) {
- rb_ary_push(tmp, Qnil);
- }
- else {
- VALUE v[2];
-
- v[1] = RARRAY_AREF(args, i);
- rb_rescue2(call_next, (VALUE)v, call_stop, (VALUE)v, rb_eStopIteration, (VALUE)0);
- if (v[0] == Qundef) {
- RARRAY_ASET(args, i, Qnil);
- v[0] = Qnil;
- }
- rb_ary_push(tmp, v[0]);
- }
+ if (NIL_P(RARRAY_AREF(args, i))) {
+ rb_ary_push(tmp, Qnil);
+ }
+ else {
+ VALUE v[2];
+
+ v[1] = RARRAY_AREF(args, i);
+ rb_rescue2(call_next, (VALUE)v, call_stop, (VALUE)v, rb_eStopIteration, (VALUE)0);
+ if (UNDEF_P(v[0])) {
+ RARRAY_ASET(args, i, Qnil);
+ v[0] = Qnil;
+ }
+ rb_ary_push(tmp, v[0]);
+ }
}
if (NIL_P(result)) {
- enum_yield_array(tmp);
+ enum_yield_array(tmp);
}
else {
- rb_ary_push(result, tmp);
+ rb_ary_push(result, tmp);
}
RB_GC_GUARD(args);
@@ -3283,26 +3266,26 @@ enum_zip(int argc, VALUE *argv, VALUE obj)
argv = RARRAY_PTR(args);
for (i=0; i<argc; i++) {
- VALUE ary = rb_check_array_type(argv[i]);
- if (NIL_P(ary)) {
- allary = FALSE;
- break;
- }
- argv[i] = ary;
+ VALUE ary = rb_check_array_type(argv[i]);
+ if (NIL_P(ary)) {
+ allary = FALSE;
+ break;
+ }
+ argv[i] = ary;
}
if (!allary) {
- static const VALUE sym_each = STATIC_ID2SYM(id_each);
- CONST_ID(conv, "to_enum");
- for (i=0; i<argc; i++) {
- if (!rb_respond_to(argv[i], id_each)) {
- rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (must respond to :each)",
- rb_obj_class(argv[i]));
+ static const VALUE sym_each = STATIC_ID2SYM(id_each);
+ CONST_ID(conv, "to_enum");
+ for (i=0; i<argc; i++) {
+ if (!rb_respond_to(argv[i], id_each)) {
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (must respond to :each)",
+ rb_obj_class(argv[i]));
}
- argv[i] = rb_funcallv(argv[i], conv, 1, &sym_each);
- }
+ argv[i] = rb_funcallv(argv[i], conv, 1, &sym_each);
+ }
}
if (!rb_block_given_p()) {
- result = rb_ary_new();
+ result = rb_ary_new();
}
/* TODO: use NODE_DOT2 as memo(v, v, -) */
@@ -3344,7 +3327,7 @@ enum_take(VALUE obj, VALUE n)
long len = NUM2LONG(n);
if (len < 0) {
- rb_raise(rb_eArgError, "attempt to take negative size");
+ rb_raise(rb_eArgError, "attempt to take negative size");
}
if (len == 0) return rb_ary_new2(0);
@@ -3398,10 +3381,10 @@ drop_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
{
struct MEMO *memo = MEMO_CAST(args);
if (memo->u3.cnt == 0) {
- rb_ary_push(memo->v1, rb_enum_values_pack(argc, argv));
+ rb_ary_push(memo->v1, rb_enum_values_pack(argc, argv));
}
else {
- memo->u3.cnt--;
+ memo->u3.cnt--;
}
return Qnil;
}
@@ -3433,7 +3416,7 @@ enum_drop(VALUE obj, VALUE n)
long len = NUM2LONG(n);
if (len < 0) {
- rb_raise(rb_eArgError, "attempt to drop negative size");
+ rb_raise(rb_eArgError, "attempt to drop negative size");
}
result = rb_ary_new();
@@ -3450,10 +3433,10 @@ drop_while_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
ENUM_WANT_SVALUE();
if (!memo->u3.state && !RTEST(enum_yield(argc, i))) {
- memo->u3.state = TRUE;
+ memo->u3.state = TRUE;
}
if (memo->u3.state) {
- rb_ary_push(memo->v1, i);
+ rb_ary_push(memo->v1, i);
}
return Qnil;
}
@@ -3508,8 +3491,8 @@ enum_cycle_size(VALUE self, VALUE args, VALUE eobj)
VALUE size;
if (args && (RARRAY_LEN(args) > 0)) {
- n = RARRAY_AREF(args, 0);
- if (!NIL_P(n)) mul = NUM2LONG(n);
+ n = RARRAY_AREF(args, 0);
+ if (!NIL_P(n)) mul = NUM2LONG(n);
}
size = enum_size(self, args, 0);
@@ -3572,7 +3555,7 @@ enum_cycle(int argc, VALUE *argv, VALUE obj)
if (len == 0) return Qnil;
while (n < 0 || 0 < --n) {
for (i=0; i<len; i++) {
- enum_yield_array(RARRAY_AREF(ary, i));
+ enum_yield_array(RARRAY_AREF(ary, i));
}
}
return Qnil;
@@ -3599,22 +3582,22 @@ chunk_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _argp))
if (v == alone) {
if (!NIL_P(argp->prev_value)) {
- s = rb_assoc_new(argp->prev_value, argp->prev_elts);
+ s = rb_assoc_new(argp->prev_value, argp->prev_elts);
rb_funcallv(argp->yielder, id_lshift, 1, &s);
argp->prev_value = argp->prev_elts = Qnil;
}
- v = rb_assoc_new(v, rb_ary_new3(1, i));
+ v = rb_assoc_new(v, rb_ary_new3(1, i));
rb_funcallv(argp->yielder, id_lshift, 1, &v);
}
else if (NIL_P(v) || v == separator) {
if (!NIL_P(argp->prev_value)) {
- v = rb_assoc_new(argp->prev_value, argp->prev_elts);
+ v = rb_assoc_new(argp->prev_value, argp->prev_elts);
rb_funcallv(argp->yielder, id_lshift, 1, &v);
argp->prev_value = argp->prev_elts = Qnil;
}
}
else if (SYMBOL_P(v) && (s = rb_sym2str(v), RSTRING_PTR(s)[0] == '_')) {
- rb_raise(rb_eRuntimeError, "symbols beginning with an underscore are reserved");
+ rb_raise(rb_eRuntimeError, "symbols beginning with an underscore are reserved");
}
else {
if (NIL_P(argp->prev_value)) {
@@ -3626,7 +3609,7 @@ chunk_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _argp))
rb_ary_push(argp->prev_elts, i);
}
else {
- s = rb_assoc_new(argp->prev_value, argp->prev_elts);
+ s = rb_assoc_new(argp->prev_value, argp->prev_elts);
rb_funcallv(argp->yielder, id_lshift, 1, &s);
argp->prev_value = v;
argp->prev_elts = rb_ary_new3(1, i);
@@ -3652,8 +3635,8 @@ chunk_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator))
rb_block_call(enumerable, id_each, 0, 0, chunk_ii, arg);
memo = MEMO_FOR(struct chunk_arg, arg);
if (!NIL_P(memo->prev_elts)) {
- arg = rb_assoc_new(memo->prev_value, memo->prev_elts);
- rb_funcallv(memo->yielder, id_lshift, 1, &arg);
+ arg = rb_assoc_new(memo->prev_value, memo->prev_elts);
+ rb_funcallv(memo->yielder, id_lshift, 1, &arg);
}
return Qnil;
}
@@ -3834,7 +3817,7 @@ slicebefore_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator))
/*
* call-seq:
* slice_before(pattern) -> enumerator
- * slice_before {|array| ... } -> enumerator
+ * slice_before {|elt| ... } -> enumerator
*
* With argument +pattern+, returns an enumerator that uses the pattern
* to partition elements into arrays ("slices").
@@ -4155,15 +4138,15 @@ slicewhen_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
ENUM_WANT_SVALUE();
- if (memo->prev_elt == Qundef) {
+ if (UNDEF_P(memo->prev_elt)) {
/* The first element */
memo->prev_elt = i;
memo->prev_elts = rb_ary_new3(1, i);
}
else {
- VALUE args[2];
- args[0] = memo->prev_elt;
- args[1] = i;
+ VALUE args[2];
+ args[0] = memo->prev_elt;
+ args[1] = i;
split_p = RTEST(rb_funcallv(memo->pred, id_call, 2, args));
UPDATE_MEMO;
@@ -4192,7 +4175,7 @@ slicewhen_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator))
VALUE enumerable;
VALUE arg;
struct slicewhen_arg *memo =
- NEW_PARTIAL_MEMO_FOR(struct slicewhen_arg, arg, inverted);
+ NEW_PARTIAL_MEMO_FOR(struct slicewhen_arg, arg, inverted);
enumerable = rb_ivar_get(enumerator, id_slicewhen_enum);
memo->pred = rb_attr_get(enumerator, id_slicewhen_pred);
@@ -4395,7 +4378,7 @@ sum_iter_bignum(VALUE i, struct enum_sum_memo *memo)
static void
sum_iter_rational(VALUE i, struct enum_sum_memo *memo)
{
- if (memo->r == Qundef) {
+ if (UNDEF_P(memo->r)) {
memo->r = i;
}
else {
@@ -4476,7 +4459,7 @@ sum_iter(VALUE i, struct enum_sum_memo *memo)
}
else switch (TYPE(memo->v)) {
default: sum_iter_some_value(i, memo); return;
- case T_FLOAT: sum_iter_Kahan_Babuska(i, memo); return;
+ case T_FLOAT:
case T_FIXNUM:
case T_BIGNUM:
case T_RATIONAL:
@@ -4616,7 +4599,7 @@ enum_sum(int argc, VALUE* argv, VALUE obj)
else {
if (memo.n != 0)
memo.v = rb_fix_plus(LONG2FIX(memo.n), memo.v);
- if (memo.r != Qundef) {
+ if (!UNDEF_P(memo.r)) {
memo.v = rb_rational_plus(memo.r, memo.v);
}
return memo.v;
@@ -4665,7 +4648,7 @@ enum_uniq(VALUE obj)
{
VALUE hash, ret;
rb_block_call_func *const func =
- rb_block_given_p() ? uniq_iter : uniq_func;
+ rb_block_given_p() ? uniq_iter : uniq_func;
hash = rb_obj_hide(rb_hash_new());
rb_block_call(obj, id_each, 0, 0, func, hash);
@@ -4808,7 +4791,7 @@ enum_compact(VALUE obj)
* - #grep_v: Returns elements selected by a given object
* or objects returned by a given block.
* - #reduce, #inject: Returns the object formed by combining all elements.
- * - #sum: Returns the sum of the elements, using method +++.
+ * - #sum: Returns the sum of the elements, using method <tt>+</tt>.
* - #zip: Combines each element with elements from other enumerables;
* returns the n-tuples or calls the block with each.
* - #cycle: Calls the block with each element, cycling repeatedly.
diff --git a/enumerator.c b/enumerator.c
index be469ee38b..d587b63d32 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -20,6 +20,7 @@
#include "id.h"
#include "internal.h"
+#include "internal/class.h"
#include "internal/enumerator.h"
#include "internal/error.h"
#include "internal/hash.h"
@@ -72,6 +73,8 @@
* puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" }
* # => ["0:foo", "1:bar", "2:baz"]
*
+ * == External Iteration
+ *
* An Enumerator can also be used as an external iterator.
* For example, Enumerator#next returns the next value of the iterator
* or raises StopIteration if the Enumerator is at the end.
@@ -82,15 +85,44 @@
* puts e.next # => 3
* puts e.next # raises StopIteration
*
- * Note that enumeration sequence by +next+, +next_values+, +peek+ and
- * +peek_values+ do not affect other non-external
- * enumeration methods, unless the underlying iteration method itself has
- * side-effect, e.g. IO#each_line.
+ * +next+, +next_values+, +peek+ and +peek_values+ are the only methods
+ * which use external iteration (and Array#zip(Enumerable-not-Array) which uses +next+).
+ *
+ * These methods do not affect other internal enumeration methods,
+ * unless the underlying iteration method itself has side-effect, e.g. IO#each_line.
+ *
+ * External iteration differs *significantly* from internal iteration
+ * due to using a Fiber:
+ * - The Fiber adds some overhead compared to internal enumeration.
+ * - The stacktrace will only include the stack from the Enumerator, not above.
+ * - Fiber-local variables are *not* inherited inside the Enumerator Fiber,
+ * which instead starts with no Fiber-local variables.
+ * - Fiber storage variables *are* inherited and are designed
+ * to handle Enumerator Fibers. Assigning to a Fiber storage variable
+ * only affects the current Fiber, so if you want to change state
+ * in the caller Fiber of the Enumerator Fiber, you need to use an
+ * extra indirection (e.g., use some object in the Fiber storage
+ * variable and mutate some ivar of it).
+ *
+ * Concretely:
+ * Thread.current[:fiber_local] = 1
+ * Fiber[:storage_var] = 1
+ * e = Enumerator.new do |y|
+ * p Thread.current[:fiber_local] # for external iteration: nil, for internal iteration: 1
+ * p Fiber[:storage_var] # => 1, inherited
+ * Fiber[:storage_var] += 1
+ * y << 42
+ * end
+ *
+ * p e.next # => 42
+ * p Fiber[:storage_var] # => 1 (it ran in a different Fiber)
*
- * Moreover, implementation typically uses fibers so performance could be
- * slower and exception stacktraces different than expected.
+ * e.each { p _1 }
+ * p Fiber[:storage_var] # => 2 (it ran in the same Fiber/"stack" as the current Fiber)
*
- * You can use this to implement an internal iterator as follows:
+ * == Convert External Iteration to Internal Iteration
+ *
+ * You can use an external iterator to implement an internal iterator as follows:
*
* def ext_each(e)
* while true
@@ -125,14 +157,17 @@
*/
VALUE rb_cEnumerator;
static VALUE rb_cLazy;
-static ID id_rewind, id_new, id_to_enum;
+static ID id_rewind, id_new, id_to_enum, id_each_entry;
static ID id_next, id_result, id_receiver, id_arguments, id_memo, id_method, id_force;
static ID id_begin, id_end, id_step, id_exclude_end;
static VALUE sym_each, sym_cycle, sym_yield;
static VALUE lazy_use_super_method;
+extern ID ruby_static_id_cause;
+
#define id_call idCall
+#define id_cause ruby_static_id_cause
#define id_each idEach
#define id_eqq idEqq
#define id_initialize idInitialize
@@ -173,9 +208,11 @@ struct producer {
typedef struct MEMO *lazyenum_proc_func(VALUE, struct MEMO *, VALUE, long);
typedef VALUE lazyenum_size_func(VALUE, VALUE);
+typedef int lazyenum_precheck_func(VALUE proc_entry);
typedef struct {
lazyenum_proc_func *proc;
lazyenum_size_func *size;
+ lazyenum_precheck_func *precheck;
} lazyenum_funcs;
struct proc_entry {
@@ -194,6 +231,12 @@ struct enum_chain {
long pos;
};
+static VALUE rb_cEnumProduct;
+
+struct enum_product {
+ VALUE enums;
+};
+
VALUE rb_cArithSeq;
/*
@@ -240,9 +283,9 @@ enumerator_memsize(const void *p)
static const rb_data_type_t enumerator_data_type = {
"enumerator",
{
- enumerator_mark,
- enumerator_free,
- enumerator_memsize,
+ enumerator_mark,
+ enumerator_free,
+ enumerator_memsize,
enumerator_compact,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
@@ -254,8 +297,8 @@ enumerator_ptr(VALUE obj)
struct enumerator *ptr;
TypedData_Get_Struct(obj, struct enumerator, &enumerator_data_type, ptr);
- if (!ptr || ptr->obj == Qundef) {
- rb_raise(rb_eArgError, "uninitialized enumerator");
+ if (!ptr || UNDEF_P(ptr->obj)) {
+ rb_raise(rb_eArgError, "uninitialized enumerator");
}
return ptr;
}
@@ -287,9 +330,9 @@ proc_entry_memsize(const void *p)
static const rb_data_type_t proc_entry_data_type = {
"proc_entry",
{
- proc_entry_mark,
- proc_entry_free,
- proc_entry_memsize,
+ proc_entry_mark,
+ proc_entry_free,
+ proc_entry_memsize,
proc_entry_compact,
},
};
@@ -371,12 +414,12 @@ obj_to_enum(int argc, VALUE *argv, VALUE obj)
VALUE enumerator, meth = sym_each;
if (argc > 0) {
- --argc;
- meth = *argv++;
+ --argc;
+ meth = *argv++;
}
enumerator = rb_enumeratorize_with_size(obj, meth, argc, argv, 0);
if (rb_block_given_p()) {
- enumerator_ptr(enumerator)->size = rb_block_proc();
+ enumerator_ptr(enumerator)->size = rb_block_proc();
}
return enumerator;
}
@@ -402,7 +445,7 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, const VALUE *ar
TypedData_Get_Struct(enum_obj, struct enumerator, &enumerator_data_type, ptr);
if (!ptr) {
- rb_raise(rb_eArgError, "unallocated enumerator");
+ rb_raise(rb_eArgError, "unallocated enumerator");
}
ptr->obj = obj;
@@ -482,14 +525,14 @@ enumerator_init_copy(VALUE obj, VALUE orig)
if (!OBJ_INIT_COPY(obj, orig)) return obj;
ptr0 = enumerator_ptr(orig);
if (ptr0->fib) {
- /* Fibers cannot be copied */
- rb_raise(rb_eTypeError, "can't copy execution context");
+ /* Fibers cannot be copied */
+ rb_raise(rb_eTypeError, "can't copy execution context");
}
TypedData_Get_Struct(obj, struct enumerator, &enumerator_data_type, ptr1);
if (!ptr1) {
- rb_raise(rb_eArgError, "unallocated enumerator");
+ rb_raise(rb_eArgError, "unallocated enumerator");
}
ptr1->obj = ptr0->obj;
@@ -513,8 +556,8 @@ rb_enumeratorize(VALUE obj, VALUE meth, int argc, const VALUE *argv)
return rb_enumeratorize_with_size(obj, meth, argc, argv, 0);
}
-static VALUE
-lazy_to_enum_i(VALUE self, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn, int kw_splat);
+static VALUE lazy_to_enum_i(VALUE self, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn, int kw_splat);
+static int lazy_precheck(VALUE procs);
VALUE
rb_enumeratorize_with_size_kw(VALUE obj, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn, int kw_splat)
@@ -547,8 +590,8 @@ enumerator_block_call(VALUE obj, rb_block_call_func *func, VALUE arg)
ID meth = e->meth;
if (e->args) {
- argc = RARRAY_LENINT(e->args);
- argv = RARRAY_CONST_PTR(e->args);
+ argc = RARRAY_LENINT(e->args);
+ argv = RARRAY_CONST_PTR(e->args);
}
return rb_block_call_kw(e->obj, meth, argc, argv, func, arg, e->kw_splat);
}
@@ -592,25 +635,29 @@ enumerator_block_call(VALUE obj, rb_block_call_func *func, VALUE arg)
static VALUE
enumerator_each(int argc, VALUE *argv, VALUE obj)
{
+ struct enumerator *e = enumerator_ptr(obj);
+
if (argc > 0) {
- struct enumerator *e = enumerator_ptr(obj = rb_obj_dup(obj));
- VALUE args = e->args;
- if (args) {
+ VALUE args = (e = enumerator_ptr(obj = rb_obj_dup(obj)))->args;
+ if (args) {
#if SIZEOF_INT < SIZEOF_LONG
- /* check int range overflow */
- rb_long2int(RARRAY_LEN(args) + argc);
+ /* check int range overflow */
+ rb_long2int(RARRAY_LEN(args) + argc);
#endif
- args = rb_ary_dup(args);
- rb_ary_cat(args, argv, argc);
- }
- else {
- args = rb_ary_new4(argc, argv);
- }
- e->args = args;
+ args = rb_ary_dup(args);
+ rb_ary_cat(args, argv, argc);
+ }
+ else {
+ args = rb_ary_new4(argc, argv);
+ }
+ e->args = args;
e->size = Qnil;
e->size_fn = 0;
}
if (!rb_block_given_p()) return obj;
+
+ if (!lazy_precheck(e->procs)) return Qnil;
+
return enumerator_block_call(obj, 0, obj);
}
@@ -622,7 +669,7 @@ enumerator_with_index_i(RB_BLOCK_CALL_FUNC_ARGLIST(val, m))
MEMO_V1_SET(memo, rb_int_succ(idx));
if (argc <= 1)
- return rb_yield_values(2, val, idx);
+ return rb_yield_values(2, val, idx);
return rb_yield_values(2, rb_ary_new4(argc, argv), idx);
}
@@ -679,7 +726,7 @@ static VALUE
enumerator_with_object_i(RB_BLOCK_CALL_FUNC_ARGLIST(val, memo))
{
if (argc <= 1)
- return rb_yield_values(2, val, memo);
+ return rb_yield_values(2, val, memo);
return rb_yield_values(2, rb_ary_new4(argc, argv), memo);
}
@@ -729,7 +776,7 @@ next_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, obj))
VALUE feedvalue = Qnil;
VALUE args = rb_ary_new4(argc, argv);
rb_fiber_yield(1, &args);
- if (e->feedvalue != Qundef) {
+ if (!UNDEF_P(e->feedvalue)) {
feedvalue = e->feedvalue;
e->feedvalue = Qundef;
}
@@ -763,22 +810,30 @@ get_next_values(VALUE obj, struct enumerator *e)
{
VALUE curr, vs;
- if (e->stop_exc)
- rb_exc_raise(e->stop_exc);
+ if (e->stop_exc) {
+ VALUE exc = e->stop_exc;
+ VALUE result = rb_attr_get(exc, id_result);
+ VALUE mesg = rb_attr_get(exc, idMesg);
+ if (!NIL_P(mesg)) mesg = rb_str_dup(mesg);
+ VALUE stop_exc = rb_exc_new_str(rb_eStopIteration, mesg);
+ rb_ivar_set(stop_exc, id_cause, exc);
+ rb_ivar_set(stop_exc, id_result, result);
+ rb_exc_raise(stop_exc);
+ }
curr = rb_fiber_current();
if (!e->fib || !rb_fiber_alive_p(e->fib)) {
- next_init(obj, e);
+ next_init(obj, e);
}
vs = rb_fiber_resume(e->fib, 1, &curr);
if (e->stop_exc) {
- e->fib = 0;
- e->dst = Qnil;
- e->lookahead = Qundef;
- e->feedvalue = Qundef;
- rb_exc_raise(e->stop_exc);
+ e->fib = 0;
+ e->dst = Qnil;
+ e->lookahead = Qundef;
+ e->feedvalue = Qundef;
+ rb_exc_raise(e->stop_exc);
}
return vs;
}
@@ -834,7 +889,7 @@ enumerator_next_values(VALUE obj)
struct enumerator *e = enumerator_ptr(obj);
VALUE vs;
- if (e->lookahead != Qundef) {
+ if (!UNDEF_P(e->lookahead)) {
vs = e->lookahead;
e->lookahead = Qundef;
return vs;
@@ -895,7 +950,7 @@ enumerator_peek_values(VALUE obj)
{
struct enumerator *e = enumerator_ptr(obj);
- if (e->lookahead == Qundef) {
+ if (UNDEF_P(e->lookahead)) {
e->lookahead = get_next_values(obj, e);
}
return e->lookahead;
@@ -1019,8 +1074,8 @@ enumerator_feed(VALUE obj, VALUE v)
{
struct enumerator *e = enumerator_ptr(obj);
- if (e->feedvalue != Qundef) {
- rb_raise(rb_eTypeError, "feed value already set");
+ if (!UNDEF_P(e->feedvalue)) {
+ rb_raise(rb_eTypeError, "feed value already set");
}
e->feedvalue = v;
@@ -1064,37 +1119,37 @@ inspect_enumerator(VALUE obj, VALUE dummy, int recur)
cname = rb_obj_class(obj);
- if (!e || e->obj == Qundef) {
- return rb_sprintf("#<%"PRIsVALUE": uninitialized>", rb_class_path(cname));
+ if (!e || UNDEF_P(e->obj)) {
+ return rb_sprintf("#<%"PRIsVALUE": uninitialized>", rb_class_path(cname));
}
if (recur) {
- str = rb_sprintf("#<%"PRIsVALUE": ...>", rb_class_path(cname));
- return str;
+ str = rb_sprintf("#<%"PRIsVALUE": ...>", rb_class_path(cname));
+ return str;
}
if (e->procs) {
- long i;
-
- eobj = generator_ptr(e->obj)->obj;
- /* In case procs chained enumerator traversing all proc entries manually */
- if (rb_obj_class(eobj) == cname) {
- str = rb_inspect(eobj);
- }
- else {
- str = rb_sprintf("#<%"PRIsVALUE": %+"PRIsVALUE">", rb_class_path(cname), eobj);
- }
- for (i = 0; i < RARRAY_LEN(e->procs); i++) {
- str = rb_sprintf("#<%"PRIsVALUE": %"PRIsVALUE, cname, str);
- append_method(RARRAY_AREF(e->procs, i), str, e->meth, e->args);
- rb_str_buf_cat2(str, ">");
- }
- return str;
+ long i;
+
+ eobj = generator_ptr(e->obj)->obj;
+ /* In case procs chained enumerator traversing all proc entries manually */
+ if (rb_obj_class(eobj) == cname) {
+ str = rb_inspect(eobj);
+ }
+ else {
+ str = rb_sprintf("#<%"PRIsVALUE": %+"PRIsVALUE">", rb_class_path(cname), eobj);
+ }
+ for (i = 0; i < RARRAY_LEN(e->procs); i++) {
+ str = rb_sprintf("#<%"PRIsVALUE": %"PRIsVALUE, cname, str);
+ append_method(RARRAY_AREF(e->procs, i), str, e->meth, e->args);
+ rb_str_buf_cat2(str, ">");
+ }
+ return str;
}
eobj = rb_attr_get(obj, id_receiver);
if (NIL_P(eobj)) {
- eobj = e->obj;
+ eobj = e->obj;
}
/* (1..100).each_cons(2) => "#<Enumerator: 1..100:each_cons(2)>" */
@@ -1129,48 +1184,48 @@ append_method(VALUE obj, VALUE str, ID default_method, VALUE default_args)
method = rb_attr_get(obj, id_method);
if (method != Qfalse) {
- if (!NIL_P(method)) {
- Check_Type(method, T_SYMBOL);
- method = rb_sym2str(method);
- }
- else {
- method = rb_id2str(default_method);
- }
- rb_str_buf_cat2(str, ":");
- rb_str_buf_append(str, method);
+ if (!NIL_P(method)) {
+ Check_Type(method, T_SYMBOL);
+ method = rb_sym2str(method);
+ }
+ else {
+ method = rb_id2str(default_method);
+ }
+ rb_str_buf_cat2(str, ":");
+ rb_str_buf_append(str, method);
}
eargs = rb_attr_get(obj, id_arguments);
if (NIL_P(eargs)) {
- eargs = default_args;
+ eargs = default_args;
}
if (eargs != Qfalse) {
- long argc = RARRAY_LEN(eargs);
- const VALUE *argv = RARRAY_CONST_PTR(eargs); /* WB: no new reference */
+ long argc = RARRAY_LEN(eargs);
+ const VALUE *argv = RARRAY_CONST_PTR(eargs); /* WB: no new reference */
- if (argc > 0) {
- VALUE kwds = Qnil;
+ if (argc > 0) {
+ VALUE kwds = Qnil;
- rb_str_buf_cat2(str, "(");
+ rb_str_buf_cat2(str, "(");
if (RB_TYPE_P(argv[argc-1], T_HASH) && !RHASH_EMPTY_P(argv[argc-1])) {
- int all_key = TRUE;
- rb_hash_foreach(argv[argc-1], key_symbol_p, (VALUE)&all_key);
- if (all_key) kwds = argv[--argc];
- }
+ int all_key = TRUE;
+ rb_hash_foreach(argv[argc-1], key_symbol_p, (VALUE)&all_key);
+ if (all_key) kwds = argv[--argc];
+ }
- while (argc--) {
- VALUE arg = *argv++;
+ while (argc--) {
+ VALUE arg = *argv++;
- rb_str_append(str, rb_inspect(arg));
- rb_str_buf_cat2(str, ", ");
- }
- if (!NIL_P(kwds)) {
- rb_hash_foreach(kwds, kwd_append, str);
- }
- rb_str_set_len(str, RSTRING_LEN(str)-2);
- rb_str_buf_cat2(str, ")");
- }
+ rb_str_append(str, rb_inspect(arg));
+ rb_str_buf_cat2(str, ", ");
+ }
+ if (!NIL_P(kwds)) {
+ rb_hash_foreach(kwds, kwd_append, str);
+ }
+ rb_str_set_len(str, RSTRING_LEN(str)-2);
+ rb_str_buf_cat2(str, ")");
+ }
}
return str;
@@ -1209,31 +1264,31 @@ enumerator_size(VALUE obj)
VALUE size;
if (e->procs) {
- struct generator *g = generator_ptr(e->obj);
- VALUE receiver = rb_check_funcall(g->obj, id_size, 0, 0);
- long i = 0;
-
- for (i = 0; i < RARRAY_LEN(e->procs); i++) {
- VALUE proc = RARRAY_AREF(e->procs, i);
- struct proc_entry *entry = proc_entry_ptr(proc);
- lazyenum_size_func *size_fn = entry->fn->size;
- if (!size_fn) {
- return Qnil;
- }
- receiver = (*size_fn)(proc, receiver);
- }
- return receiver;
+ struct generator *g = generator_ptr(e->obj);
+ VALUE receiver = rb_check_funcall(g->obj, id_size, 0, 0);
+ long i = 0;
+
+ for (i = 0; i < RARRAY_LEN(e->procs); i++) {
+ VALUE proc = RARRAY_AREF(e->procs, i);
+ struct proc_entry *entry = proc_entry_ptr(proc);
+ lazyenum_size_func *size_fn = entry->fn->size;
+ if (!size_fn) {
+ return Qnil;
+ }
+ receiver = (*size_fn)(proc, receiver);
+ }
+ return receiver;
}
if (e->size_fn) {
- return (*e->size_fn)(e->obj, e->args, obj);
+ return (*e->size_fn)(e->obj, e->args, obj);
}
if (e->args) {
- argc = (int)RARRAY_LEN(e->args);
- argv = RARRAY_CONST_PTR(e->args);
+ argc = (int)RARRAY_LEN(e->args);
+ argv = RARRAY_CONST_PTR(e->args);
}
size = rb_check_funcall_kw(e->size, id_call, argc, argv, e->kw_splat);
- if (size != Qundef) return size;
+ if (!UNDEF_P(size)) return size;
return e->size;
}
@@ -1265,9 +1320,9 @@ yielder_memsize(const void *p)
static const rb_data_type_t yielder_data_type = {
"yielder",
{
- yielder_mark,
- yielder_free,
- yielder_memsize,
+ yielder_mark,
+ yielder_free,
+ yielder_memsize,
yielder_compact,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
@@ -1279,8 +1334,8 @@ yielder_ptr(VALUE obj)
struct yielder *ptr;
TypedData_Get_Struct(obj, struct yielder, &yielder_data_type, ptr);
- if (!ptr || ptr->proc == Qundef) {
- rb_raise(rb_eArgError, "uninitialized yielder");
+ if (!ptr || UNDEF_P(ptr->proc)) {
+ rb_raise(rb_eArgError, "uninitialized yielder");
}
return ptr;
}
@@ -1306,7 +1361,7 @@ yielder_init(VALUE obj, VALUE proc)
TypedData_Get_Struct(obj, struct yielder, &yielder_data_type, ptr);
if (!ptr) {
- rb_raise(rb_eArgError, "unallocated yielder");
+ rb_raise(rb_eArgError, "unallocated yielder");
}
ptr->proc = proc;
@@ -1405,9 +1460,9 @@ generator_memsize(const void *p)
static const rb_data_type_t generator_data_type = {
"generator",
{
- generator_mark,
- generator_free,
- generator_memsize,
+ generator_mark,
+ generator_free,
+ generator_memsize,
generator_compact,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
@@ -1419,8 +1474,8 @@ generator_ptr(VALUE obj)
struct generator *ptr;
TypedData_Get_Struct(obj, struct generator, &generator_data_type, ptr);
- if (!ptr || ptr->proc == Qundef) {
- rb_raise(rb_eArgError, "uninitialized generator");
+ if (!ptr || UNDEF_P(ptr->proc)) {
+ rb_raise(rb_eArgError, "uninitialized generator");
}
return ptr;
}
@@ -1447,7 +1502,7 @@ generator_init(VALUE obj, VALUE proc)
TypedData_Get_Struct(obj, struct generator, &generator_data_type, ptr);
if (!ptr) {
- rb_raise(rb_eArgError, "unallocated generator");
+ rb_raise(rb_eArgError, "unallocated generator");
}
ptr->proc = proc;
@@ -1462,21 +1517,21 @@ generator_initialize(int argc, VALUE *argv, VALUE obj)
VALUE proc;
if (argc == 0) {
- rb_need_block();
+ rb_need_block();
- proc = rb_block_proc();
+ proc = rb_block_proc();
}
else {
- rb_scan_args(argc, argv, "1", &proc);
+ rb_scan_args(argc, argv, "1", &proc);
- if (!rb_obj_is_proc(proc))
- rb_raise(rb_eTypeError,
- "wrong argument type %"PRIsVALUE" (expected Proc)",
- rb_obj_class(proc));
+ if (!rb_obj_is_proc(proc))
+ rb_raise(rb_eTypeError,
+ "wrong argument type %"PRIsVALUE" (expected Proc)",
+ rb_obj_class(proc));
- if (rb_block_given_p()) {
- rb_warn("given block not used");
- }
+ if (rb_block_given_p()) {
+ rb_warn("given block not used");
+ }
}
return generator_init(obj, proc);
@@ -1495,7 +1550,7 @@ generator_init_copy(VALUE obj, VALUE orig)
TypedData_Get_Struct(obj, struct generator, &generator_data_type, ptr1);
if (!ptr1) {
- rb_raise(rb_eArgError, "unallocated generator");
+ rb_raise(rb_eArgError, "unallocated generator");
}
ptr1->proc = ptr0->proc;
@@ -1512,7 +1567,7 @@ generator_each(int argc, VALUE *argv, VALUE obj)
rb_ary_push(args, yielder_new());
if (argc > 0) {
- rb_ary_cat(args, argv, argc);
+ rb_ary_cat(args, argv, argc);
}
return rb_proc_call_kw(ptr->proc, args, RB_PASS_CALLED_KEYWORDS);
@@ -1523,7 +1578,7 @@ static VALUE
enum_size(VALUE self)
{
VALUE r = rb_check_funcall(self, id_size, 0, 0);
- return (r == Qundef) ? Qnil : r;
+ return UNDEF_P(r) ? Qnil : r;
}
static VALUE
@@ -1539,24 +1594,24 @@ lazy_init_iterator(RB_BLOCK_CALL_FUNC_ARGLIST(val, m))
{
VALUE result;
if (argc == 1) {
- VALUE args[2];
- args[0] = m;
- args[1] = val;
- result = rb_yield_values2(2, args);
+ VALUE args[2];
+ args[0] = m;
+ args[1] = val;
+ result = rb_yield_values2(2, args);
}
else {
- VALUE args;
- int len = rb_long2int((long)argc + 1);
- VALUE *nargv = ALLOCV_N(VALUE, args, len);
-
- nargv[0] = m;
- if (argc > 0) {
- MEMCPY(nargv + 1, argv, VALUE, argc);
- }
- result = rb_yield_values2(len, nargv);
- ALLOCV_END(args);
- }
- if (result == Qundef) rb_iter_break();
+ VALUE args;
+ int len = rb_long2int((long)argc + 1);
+ VALUE *nargv = ALLOCV_N(VALUE, args, len);
+
+ nargv[0] = m;
+ if (argc > 0) {
+ MEMCPY(nargv + 1, argv, VALUE, argc);
+ }
+ result = rb_yield_values2(len, nargv);
+ ALLOCV_END(args);
+ }
+ if (UNDEF_P(result)) rb_iter_break();
return Qnil;
}
@@ -1590,7 +1645,7 @@ lazy_init_yielder(RB_BLOCK_CALL_FUNC_ARGLIST(_, m))
struct MEMO *result;
result = MEMO_NEW(m, rb_enum_values_pack(argc, argv),
- argc > 1 ? LAZY_MEMO_PACKED : 0);
+ argc > 1 ? LAZY_MEMO_PACKED : 0);
return lazy_yielder_result(result, yielder, procs_array, memos, 0);
}
@@ -1615,19 +1670,19 @@ lazy_yielder_result(struct MEMO *result, VALUE yielder, VALUE procs_array, VALUE
int cont = 1;
for (; i < RARRAY_LEN(procs_array); i++) {
- VALUE proc = RARRAY_AREF(procs_array, i);
- struct proc_entry *entry = proc_entry_ptr(proc);
- if (!(*entry->fn->proc)(proc, result, memos, i)) {
- cont = 0;
- break;
- }
+ VALUE proc = RARRAY_AREF(procs_array, i);
+ struct proc_entry *entry = proc_entry_ptr(proc);
+ if (!(*entry->fn->proc)(proc, result, memos, i)) {
+ cont = 0;
+ break;
+ }
}
if (cont) {
- rb_funcall2(yielder, idLTLT, 1, &(result->memo_value));
+ rb_funcall2(yielder, idLTLT, 1, &(result->memo_value));
}
if (LAZY_MEMO_BREAK_P(result)) {
- rb_iter_break();
+ rb_iter_break();
}
return result->memo_value;
}
@@ -1639,7 +1694,7 @@ lazy_init_block(RB_BLOCK_CALL_FUNC_ARGLIST(val, m))
rb_ivar_set(val, id_memo, rb_ary_new2(RARRAY_LEN(procs)));
rb_block_call(RARRAY_AREF(m, 0), id_each, 0, 0,
- lazy_init_yielder, rb_ary_new3(2, val, procs));
+ lazy_init_yielder, rb_ary_new3(2, val, procs));
return Qnil;
}
@@ -1652,17 +1707,17 @@ lazy_generator_init(VALUE enumerator, VALUE procs)
struct enumerator *e = enumerator_ptr(enumerator);
if (RARRAY_LEN(procs) > 0) {
- struct generator *old_gen_ptr = generator_ptr(e->obj);
- obj = old_gen_ptr->obj;
+ struct generator *old_gen_ptr = generator_ptr(e->obj);
+ obj = old_gen_ptr->obj;
}
else {
- obj = enumerator;
+ obj = enumerator;
}
generator = generator_allocate(rb_cGenerator);
rb_block_call(generator, id_initialize, 0, 0,
- lazy_init_block, rb_ary_new3(2, obj, procs));
+ lazy_init_block, rb_ary_new3(2, obj, procs));
gen_ptr = generator_ptr(generator);
gen_ptr->obj = obj;
@@ -1670,6 +1725,22 @@ lazy_generator_init(VALUE enumerator, VALUE procs)
return generator;
}
+static int
+lazy_precheck(VALUE procs)
+{
+ if (RTEST(procs)) {
+ long num_procs = RARRAY_LEN(procs), i = num_procs;
+ while (i-- > 0) {
+ VALUE proc = RARRAY_AREF(procs, i);
+ struct proc_entry *entry = proc_entry_ptr(proc);
+ lazyenum_precheck_func *precheck = entry->fn->precheck;
+ if (precheck && !precheck(proc)) return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
/*
* Document-class: Enumerator::Lazy
*
@@ -1767,11 +1838,11 @@ lazy_initialize(int argc, VALUE *argv, VALUE self)
rb_check_arity(argc, 1, 2);
if (!rb_block_given_p()) {
- rb_raise(rb_eArgError, "tried to call lazy new without a block");
+ rb_raise(rb_eArgError, "tried to call lazy new without a block");
}
obj = argv[0];
if (argc > 1) {
- size = argv[1];
+ size = argv[1];
}
generator = generator_allocate(rb_cGenerator);
rb_block_call(generator, id_initialize, 0, 0, lazy_init_block_i, obj);
@@ -1790,7 +1861,8 @@ lazy_initialize(int argc, VALUE *argv, VALUE self)
* Expands +lazy+ enumerator to an array.
* See Enumerable#to_a.
*/
-static VALUE lazy_to_a(VALUE self)
+static VALUE
+lazy_to_a(VALUE self)
{
}
#endif
@@ -1801,11 +1873,11 @@ lazy_set_args(VALUE lazy, VALUE args)
ID id = rb_frame_this_func();
rb_ivar_set(lazy, id_method, ID2SYM(id));
if (NIL_P(args)) {
- /* Qfalse indicates that the arguments are empty */
- rb_ivar_set(lazy, id_arguments, Qfalse);
+ /* Qfalse indicates that the arguments are empty */
+ rb_ivar_set(lazy, id_arguments, Qfalse);
}
else {
- rb_ivar_set(lazy, id_arguments, args);
+ rb_ivar_set(lazy, id_arguments, args);
}
}
@@ -1822,7 +1894,7 @@ lazy_set_method(VALUE lazy, VALUE args, rb_enumerator_size_func *size_fn)
static VALUE
lazy_add_method(VALUE obj, int argc, VALUE *argv, VALUE args, VALUE memo,
- const lazyenum_funcs *fn)
+ const lazyenum_funcs *fn)
{
struct enumerator *new_e;
VALUE new_obj;
@@ -1831,9 +1903,9 @@ lazy_add_method(VALUE obj, int argc, VALUE *argv, VALUE args, VALUE memo,
struct enumerator *e = enumerator_ptr(obj);
struct proc_entry *entry;
VALUE entry_obj = TypedData_Make_Struct(rb_cObject, struct proc_entry,
- &proc_entry_data_type, entry);
+ &proc_entry_data_type, entry);
if (rb_block_given_p()) {
- entry->proc = rb_block_proc();
+ entry->proc = rb_block_proc();
}
entry->fn = fn;
entry->memo = args;
@@ -1850,11 +1922,11 @@ lazy_add_method(VALUE obj, int argc, VALUE *argv, VALUE args, VALUE memo,
new_e->procs = new_procs;
if (argc > 0) {
- new_e->meth = rb_to_id(*argv++);
- --argc;
+ new_e->meth = rb_to_id(*argv++);
+ --argc;
}
else {
- new_e->meth = id_each;
+ new_e->meth = id_each;
}
new_e->args = rb_ary_new4(argc, argv);
return new_obj;
@@ -1934,15 +2006,15 @@ lazy_to_enum(int argc, VALUE *argv, VALUE self)
VALUE lazy, meth = sym_each, super_meth;
if (argc > 0) {
- --argc;
- meth = *argv++;
+ --argc;
+ meth = *argv++;
}
if (RTEST((super_meth = rb_hash_aref(lazy_use_super_method, meth)))) {
meth = super_meth;
}
lazy = lazy_to_enum_i(self, meth, argc, argv, 0, rb_keyword_given_p());
if (rb_block_given_p()) {
- enumerator_ptr(lazy)->size = rb_block_proc();
+ enumerator_ptr(lazy)->size = rb_block_proc();
}
return lazy;
}
@@ -1981,9 +2053,9 @@ lazyenum_yield_values(VALUE proc_entry, struct MEMO *result)
int argc = 1;
const VALUE *argv = &result->memo_value;
if (LAZY_MEMO_PACKED_P(result)) {
- const VALUE args = *argv;
- argc = RARRAY_LENINT(args);
- argv = RARRAY_CONST_PTR(args);
+ const VALUE args = *argv;
+ argc = RARRAY_LENINT(args);
+ argv = RARRAY_CONST_PTR(args);
}
return rb_proc_call_with_block(entry->proc, argc, argv, Qnil);
}
@@ -2024,7 +2096,7 @@ static VALUE
lazy_map(VALUE obj)
{
if (!rb_block_given_p()) {
- rb_raise(rb_eArgError, "tried to call lazy map without a block");
+ rb_raise(rb_eArgError, "tried to call lazy map without a block");
}
return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_map_funcs);
@@ -2109,7 +2181,7 @@ static VALUE
lazy_flat_map(VALUE obj)
{
if (!rb_block_given_p()) {
- rb_raise(rb_eArgError, "tried to call lazy flat_map without a block");
+ rb_raise(rb_eArgError, "tried to call lazy flat_map without a block");
}
return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_flat_map_funcs);
@@ -2139,7 +2211,7 @@ static VALUE
lazy_select(VALUE obj)
{
if (!rb_block_given_p()) {
- rb_raise(rb_eArgError, "tried to call lazy select without a block");
+ rb_raise(rb_eArgError, "tried to call lazy select without a block");
}
return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_select_funcs);
@@ -2202,7 +2274,7 @@ static VALUE
lazy_reject(VALUE obj)
{
if (!rb_block_given_p()) {
- rb_raise(rb_eArgError, "tried to call lazy reject without a block");
+ rb_raise(rb_eArgError, "tried to call lazy reject without a block");
}
return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_reject_funcs);
@@ -2251,7 +2323,7 @@ static VALUE
lazy_grep(VALUE obj, VALUE pattern)
{
const lazyenum_funcs *const funcs = rb_block_given_p() ?
- &lazy_grep_iter_funcs : &lazy_grep_funcs;
+ &lazy_grep_iter_funcs : &lazy_grep_funcs;
return lazy_add_method(obj, 0, 0, pattern, rb_ary_new3(1, pattern), funcs);
}
@@ -2325,7 +2397,7 @@ lazy_zip_arrays_func(VALUE proc_entry, struct MEMO *result, VALUE memos, long me
ary = rb_ary_new2(RARRAY_LEN(arrays) + 1);
rb_ary_push(ary, result->memo_value);
for (i = 0; i < RARRAY_LEN(arrays); i++) {
- rb_ary_push(ary, rb_ary_entry(RARRAY_AREF(arrays, i), count));
+ rb_ary_push(ary, rb_ary_entry(RARRAY_AREF(arrays, i), count));
}
LAZY_MEMO_SET_VALUE(result, ary);
LAZY_MEMO_SET_PACKED(result);
@@ -2343,19 +2415,19 @@ lazy_zip_func(VALUE proc_entry, struct MEMO *result, VALUE memos, long memo_inde
long i;
if (NIL_P(arg)) {
- arg = rb_ary_new2(RARRAY_LEN(zip_args));
- for (i = 0; i < RARRAY_LEN(zip_args); i++) {
- rb_ary_push(arg, rb_funcall(RARRAY_AREF(zip_args, i), id_to_enum, 0));
- }
- rb_ary_store(memos, memo_index, arg);
+ arg = rb_ary_new2(RARRAY_LEN(zip_args));
+ for (i = 0; i < RARRAY_LEN(zip_args); i++) {
+ rb_ary_push(arg, rb_funcall(RARRAY_AREF(zip_args, i), id_to_enum, 0));
+ }
+ rb_ary_store(memos, memo_index, arg);
}
ary = rb_ary_new2(RARRAY_LEN(arg) + 1);
rb_ary_push(ary, result->memo_value);
for (i = 0; i < RARRAY_LEN(arg); i++) {
- v = rb_rescue2(call_next, RARRAY_AREF(arg, i), next_stopped, 0,
- rb_eStopIteration, (VALUE)0);
- rb_ary_push(ary, v);
+ v = rb_rescue2(call_next, RARRAY_AREF(arg, i), next_stopped, 0,
+ rb_eStopIteration, (VALUE)0);
+ rb_ary_push(ary, v);
}
LAZY_MEMO_SET_VALUE(result, ary);
LAZY_MEMO_SET_PACKED(result);
@@ -2383,24 +2455,24 @@ lazy_zip(int argc, VALUE *argv, VALUE obj)
const lazyenum_funcs *funcs = &lazy_zip_funcs[1];
if (rb_block_given_p()) {
- return rb_call_super(argc, argv);
+ return rb_call_super(argc, argv);
}
ary = rb_ary_new2(argc);
for (i = 0; i < argc; i++) {
- v = rb_check_array_type(argv[i]);
- if (NIL_P(v)) {
- for (; i < argc; i++) {
- if (!rb_respond_to(argv[i], id_each)) {
- rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (must respond to :each)",
- rb_obj_class(argv[i]));
- }
- }
- ary = rb_ary_new4(argc, argv);
- funcs = &lazy_zip_funcs[0];
- break;
- }
- rb_ary_push(ary, v);
+ v = rb_check_array_type(argv[i]);
+ if (NIL_P(v)) {
+ for (; i < argc; i++) {
+ if (!rb_respond_to(argv[i], id_each)) {
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (must respond to :each)",
+ rb_obj_class(argv[i]));
+ }
+ }
+ ary = rb_ary_new4(argc, argv);
+ funcs = &lazy_zip_funcs[0];
+ break;
+ }
+ rb_ary_push(ary, v);
}
return lazy_add_method(obj, 0, 0, ary, ary, funcs);
@@ -2414,17 +2486,12 @@ lazy_take_proc(VALUE proc_entry, struct MEMO *result, VALUE memos, long memo_ind
VALUE memo = rb_ary_entry(memos, memo_index);
if (NIL_P(memo)) {
- memo = entry->memo;
+ memo = entry->memo;
}
remain = NUM2LONG(memo);
- if (remain == 0) {
- LAZY_MEMO_SET_BREAK(result);
- }
- else {
- if (--remain == 0) LAZY_MEMO_SET_BREAK(result);
- rb_ary_store(memos, memo_index, LONG2NUM(remain));
- }
+ if (--remain == 0) LAZY_MEMO_SET_BREAK(result);
+ rb_ary_store(memos, memo_index, LONG2NUM(remain));
return result;
}
@@ -2433,12 +2500,19 @@ lazy_take_size(VALUE entry, VALUE receiver)
{
long len = NUM2LONG(RARRAY_AREF(rb_ivar_get(entry, id_arguments), 0));
if (NIL_P(receiver) || (FIXNUM_P(receiver) && FIX2LONG(receiver) < len))
- return receiver;
+ return receiver;
return LONG2NUM(len);
}
+static int
+lazy_take_precheck(VALUE proc_entry)
+{
+ struct proc_entry *entry = proc_entry_ptr(proc_entry);
+ return entry->memo != INT2FIX(0);
+}
+
static const lazyenum_funcs lazy_take_funcs = {
- lazy_take_proc, lazy_take_size,
+ lazy_take_proc, lazy_take_size, lazy_take_precheck,
};
/*
@@ -2452,20 +2526,14 @@ static VALUE
lazy_take(VALUE obj, VALUE n)
{
long len = NUM2LONG(n);
- int argc = 0;
- VALUE argv[2];
if (len < 0) {
- rb_raise(rb_eArgError, "attempt to take negative size");
+ rb_raise(rb_eArgError, "attempt to take negative size");
}
- if (len == 0) {
- argv[0] = sym_cycle;
- argv[1] = INT2NUM(0);
- argc = 2;
- }
+ n = LONG2NUM(len); /* no more conversion */
- return lazy_add_method(obj, argc, argv, n, rb_ary_new3(1, n), &lazy_take_funcs);
+ return lazy_add_method(obj, 0, 0, n, rb_ary_new3(1, n), &lazy_take_funcs);
}
static struct MEMO *
@@ -2473,8 +2541,8 @@ lazy_take_while_proc(VALUE proc_entry, struct MEMO *result, VALUE memos, long me
{
VALUE take = lazyenum_yield_values(proc_entry, result);
if (!RTEST(take)) {
- LAZY_MEMO_SET_BREAK(result);
- return 0;
+ LAZY_MEMO_SET_BREAK(result);
+ return 0;
}
return result;
}
@@ -2494,7 +2562,7 @@ static VALUE
lazy_take_while(VALUE obj)
{
if (!rb_block_given_p()) {
- rb_raise(rb_eArgError, "tried to call lazy take_while without a block");
+ rb_raise(rb_eArgError, "tried to call lazy take_while without a block");
}
return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_take_while_funcs);
@@ -2505,10 +2573,10 @@ lazy_drop_size(VALUE proc_entry, VALUE receiver)
{
long len = NUM2LONG(RARRAY_AREF(rb_ivar_get(proc_entry, id_arguments), 0));
if (NIL_P(receiver))
- return receiver;
+ return receiver;
if (FIXNUM_P(receiver)) {
- len = FIX2LONG(receiver) - len;
- return LONG2FIX(len < 0 ? 0 : len);
+ len = FIX2LONG(receiver) - len;
+ return LONG2FIX(len < 0 ? 0 : len);
}
return rb_funcall(receiver, '-', 1, LONG2NUM(len));
}
@@ -2521,13 +2589,13 @@ lazy_drop_proc(VALUE proc_entry, struct MEMO *result, VALUE memos, long memo_ind
VALUE memo = rb_ary_entry(memos, memo_index);
if (NIL_P(memo)) {
- memo = entry->memo;
+ memo = entry->memo;
}
remain = NUM2LONG(memo);
if (remain > 0) {
- --remain;
- rb_ary_store(memos, memo_index, LONG2NUM(remain));
- return 0;
+ --remain;
+ rb_ary_store(memos, memo_index, LONG2NUM(remain));
+ return 0;
}
return result;
@@ -2553,7 +2621,7 @@ lazy_drop(VALUE obj, VALUE n)
argv[1] = n;
if (len < 0) {
- rb_raise(rb_eArgError, "attempt to drop negative size");
+ rb_raise(rb_eArgError, "attempt to drop negative size");
}
return lazy_add_method(obj, 2, argv, n, rb_ary_new3(1, n), &lazy_drop_funcs);
@@ -2566,13 +2634,13 @@ lazy_drop_while_proc(VALUE proc_entry, struct MEMO* result, VALUE memos, long me
VALUE memo = rb_ary_entry(memos, memo_index);
if (NIL_P(memo)) {
- memo = entry->memo;
+ memo = entry->memo;
}
if (!RTEST(memo)) {
- VALUE drop = lazyenum_yield_values(proc_entry, result);
- if (RTEST(drop)) return 0;
- rb_ary_store(memos, memo_index, Qtrue);
+ VALUE drop = lazyenum_yield_values(proc_entry, result);
+ if (RTEST(drop)) return 0;
+ rb_ary_store(memos, memo_index, Qtrue);
}
return result;
}
@@ -2592,7 +2660,7 @@ static VALUE
lazy_drop_while(VALUE obj)
{
if (!rb_block_given_p()) {
- rb_raise(rb_eArgError, "tried to call lazy drop_while without a block");
+ rb_raise(rb_eArgError, "tried to call lazy drop_while without a block");
}
return lazy_add_method(obj, 0, 0, Qfalse, Qnil, &lazy_drop_while_funcs);
@@ -2747,7 +2815,8 @@ lazy_with_index(int argc, VALUE *argv, VALUE obj)
*
* Like Enumerable#chunk, but chains operation to be lazy-evaluated.
*/
-static VALUE lazy_chunk(VALUE self)
+static VALUE
+lazy_chunk(VALUE self)
{
}
@@ -2757,7 +2826,8 @@ static VALUE lazy_chunk(VALUE self)
*
* Like Enumerable#chunk_while, but chains operation to be lazy-evaluated.
*/
-static VALUE lazy_chunk_while(VALUE self)
+static VALUE
+lazy_chunk_while(VALUE self)
{
}
@@ -2768,7 +2838,8 @@ static VALUE lazy_chunk_while(VALUE self)
*
* Like Enumerable#slice_after, but chains operation to be lazy-evaluated.
*/
-static VALUE lazy_slice_after(VALUE self)
+static VALUE
+lazy_slice_after(VALUE self)
{
}
@@ -2779,7 +2850,8 @@ static VALUE lazy_slice_after(VALUE self)
*
* Like Enumerable#slice_before, but chains operation to be lazy-evaluated.
*/
-static VALUE lazy_slice_before(VALUE self)
+static VALUE
+lazy_slice_before(VALUE self)
{
}
@@ -2789,7 +2861,8 @@ static VALUE lazy_slice_before(VALUE self)
*
* Like Enumerable#slice_when, but chains operation to be lazy-evaluated.
*/
-static VALUE lazy_slice_when(VALUE self)
+static VALUE
+lazy_slice_when(VALUE self)
{
}
# endif
@@ -2911,7 +2984,7 @@ producer_ptr(VALUE obj)
struct producer *ptr;
TypedData_Get_Struct(obj, struct producer, &producer_data_type, ptr);
- if (!ptr || ptr->proc == Qundef) {
+ if (!ptr || UNDEF_P(ptr->proc)) {
rb_raise(rb_eArgError, "uninitialized producer");
}
return ptr;
@@ -2966,7 +3039,7 @@ producer_each_i(VALUE obj)
init = ptr->init;
proc = ptr->proc;
- if (init == Qundef) {
+ if (UNDEF_P(init)) {
curr = Qnil;
}
else {
@@ -3097,7 +3170,7 @@ enum_chain_ptr(VALUE obj)
struct enum_chain *ptr;
TypedData_Get_Struct(obj, struct enum_chain, &enum_chain_data_type, ptr);
- if (!ptr || ptr->enums == Qundef) {
+ if (!ptr || UNDEF_P(ptr->enums)) {
rb_raise(rb_eArgError, "uninitialized chain");
}
return ptr;
@@ -3290,7 +3363,7 @@ inspect_enum_chain(VALUE obj, VALUE dummy, int recur)
TypedData_Get_Struct(obj, struct enum_chain, &enum_chain_data_type, ptr);
- if (!ptr || ptr->enums == Qundef) {
+ if (!ptr || UNDEF_P(ptr->enums)) {
return rb_sprintf("#<%"PRIsVALUE": uninitialized>", rb_class_path(klass));
}
@@ -3348,6 +3421,356 @@ enumerator_plus(VALUE obj, VALUE eobj)
}
/*
+ * Document-class: Enumerator::Product
+ *
+ * Enumerator::Product generates a Cartesian product of any number of
+ * enumerable objects. Iterating over the product of enumerable
+ * objects is roughly equivalent to nested each_entry loops where the
+ * loop for the rightmost object is put innermost.
+ *
+ * innings = Enumerator::Product.new(1..9, ['top', 'bottom'])
+ *
+ * innings.each do |i, h|
+ * p [i, h]
+ * end
+ * # [1, "top"]
+ * # [1, "bottom"]
+ * # [2, "top"]
+ * # [2, "bottom"]
+ * # [3, "top"]
+ * # [3, "bottom"]
+ * # ...
+ * # [9, "top"]
+ * # [9, "bottom"]
+ *
+ * The method used against each enumerable object is `each_entry`
+ * instead of `each` so that the product of N enumerable objects
+ * yields an array of exactly N elements in each iteration.
+ *
+ * When no enumerator is given, it calls a given block once yielding
+ * an empty argument list.
+ *
+ * This type of objects can be created by Enumerator.product.
+ */
+
+static void
+enum_product_mark(void *p)
+{
+ struct enum_product *ptr = p;
+ rb_gc_mark_movable(ptr->enums);
+}
+
+static void
+enum_product_compact(void *p)
+{
+ struct enum_product *ptr = p;
+ ptr->enums = rb_gc_location(ptr->enums);
+}
+
+#define enum_product_free RUBY_TYPED_DEFAULT_FREE
+
+static size_t
+enum_product_memsize(const void *p)
+{
+ return sizeof(struct enum_product);
+}
+
+static const rb_data_type_t enum_product_data_type = {
+ "product",
+ {
+ enum_product_mark,
+ enum_product_free,
+ enum_product_memsize,
+ enum_product_compact,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+static struct enum_product *
+enum_product_ptr(VALUE obj)
+{
+ struct enum_product *ptr;
+
+ TypedData_Get_Struct(obj, struct enum_product, &enum_product_data_type, ptr);
+ if (!ptr || UNDEF_P(ptr->enums)) {
+ rb_raise(rb_eArgError, "uninitialized product");
+ }
+ return ptr;
+}
+
+/* :nodoc: */
+static VALUE
+enum_product_allocate(VALUE klass)
+{
+ struct enum_product *ptr;
+ VALUE obj;
+
+ obj = TypedData_Make_Struct(klass, struct enum_product, &enum_product_data_type, ptr);
+ ptr->enums = Qundef;
+
+ return obj;
+}
+
+/*
+ * call-seq:
+ * Enumerator::Product.new(*enums) -> enum
+ *
+ * Generates a new enumerator object that generates a Cartesian
+ * product of given enumerable objects.
+ *
+ * e = Enumerator::Product.new(1..3, [4, 5])
+ * e.to_a #=> [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]]
+ * e.size #=> 6
+ */
+static VALUE
+enum_product_initialize(int argc, VALUE *argv, VALUE obj)
+{
+ struct enum_product *ptr;
+ VALUE enums = Qnil, options = Qnil;
+
+ rb_scan_args(argc, argv, "*:", &enums, &options);
+
+ if (!NIL_P(options) && !RHASH_EMPTY_P(options)) {
+ rb_exc_raise(rb_keyword_error_new("unknown", rb_hash_keys(options)));
+ }
+
+ rb_check_frozen(obj);
+ TypedData_Get_Struct(obj, struct enum_product, &enum_product_data_type, ptr);
+
+ if (!ptr) rb_raise(rb_eArgError, "unallocated product");
+
+ ptr->enums = rb_obj_freeze(enums);
+
+ return obj;
+}
+
+/* :nodoc: */
+static VALUE
+enum_product_init_copy(VALUE obj, VALUE orig)
+{
+ struct enum_product *ptr0, *ptr1;
+
+ if (!OBJ_INIT_COPY(obj, orig)) return obj;
+ ptr0 = enum_product_ptr(orig);
+
+ TypedData_Get_Struct(obj, struct enum_product, &enum_product_data_type, ptr1);
+
+ if (!ptr1) rb_raise(rb_eArgError, "unallocated product");
+
+ ptr1->enums = ptr0->enums;
+
+ return obj;
+}
+
+static VALUE
+enum_product_total_size(VALUE enums)
+{
+ VALUE total = INT2FIX(1);
+ long i;
+
+ for (i = 0; i < RARRAY_LEN(enums); i++) {
+ VALUE size = enum_size(RARRAY_AREF(enums, i));
+
+ if (NIL_P(size) || (RB_TYPE_P(size, T_FLOAT) && isinf(NUM2DBL(size)))) {
+ return size;
+ }
+ if (!RB_INTEGER_TYPE_P(size)) {
+ return Qnil;
+ }
+
+ total = rb_funcall(total, '*', 1, size);
+ }
+
+ return total;
+}
+
+/*
+ * call-seq:
+ * obj.size -> int, Float::INFINITY or nil
+ *
+ * Returns the total size of the enumerator product calculated by
+ * multiplying the sizes of enumerables in the product. If any of the
+ * enumerables reports its size as nil or Float::INFINITY, that value
+ * is returned as the size.
+ */
+static VALUE
+enum_product_size(VALUE obj)
+{
+ return enum_product_total_size(enum_product_ptr(obj)->enums);
+}
+
+static VALUE
+enum_product_enum_size(VALUE obj, VALUE args, VALUE eobj)
+{
+ return enum_product_size(obj);
+}
+
+struct product_state {
+ VALUE obj;
+ VALUE block;
+ int argc;
+ VALUE *argv;
+ int index;
+};
+
+static VALUE product_each(VALUE, struct product_state *);
+
+static VALUE
+product_each_i(RB_BLOCK_CALL_FUNC_ARGLIST(value, state))
+{
+ struct product_state *pstate = (struct product_state *)state;
+ pstate->argv[pstate->index++] = value;
+
+ VALUE val = product_each(pstate->obj, pstate);
+ pstate->index--;
+ return val;
+}
+
+static VALUE
+product_each(VALUE obj, struct product_state *pstate)
+{
+ struct enum_product *ptr = enum_product_ptr(obj);
+ VALUE enums = ptr->enums;
+
+ if (pstate->index < pstate->argc) {
+ VALUE eobj = RARRAY_AREF(enums, pstate->index);
+
+ rb_block_call(eobj, id_each_entry, 0, NULL, product_each_i, (VALUE)pstate);
+ }
+ else {
+ rb_funcall(pstate->block, id_call, 1, rb_ary_new_from_values(pstate->argc, pstate->argv));
+ }
+
+ return obj;
+}
+
+static VALUE
+enum_product_run(VALUE obj, VALUE block)
+{
+ struct enum_product *ptr = enum_product_ptr(obj);
+ int argc = RARRAY_LENINT(ptr->enums);
+ struct product_state state = {
+ .obj = obj,
+ .block = block,
+ .index = 0,
+ .argc = argc,
+ .argv = ALLOCA_N(VALUE, argc),
+ };
+
+ return product_each(obj, &state);
+}
+
+/*
+ * call-seq:
+ * obj.each { |...| ... } -> obj
+ * obj.each -> enumerator
+ *
+ * Iterates over the elements of the first enumerable by calling the
+ * "each_entry" method on it with the given arguments, then proceeds
+ * to the following enumerables in sequence until all of the
+ * enumerables are exhausted.
+ *
+ * If no block is given, returns an enumerator. Otherwise, returns self.
+ */
+static VALUE
+enum_product_each(VALUE obj)
+{
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_product_enum_size);
+
+ return enum_product_run(obj, rb_block_proc());
+}
+
+/*
+ * call-seq:
+ * obj.rewind -> obj
+ *
+ * Rewinds the product enumerator by calling the "rewind" method on
+ * each enumerable in reverse order. Each call is performed only if
+ * the enumerable responds to the method.
+ */
+static VALUE
+enum_product_rewind(VALUE obj)
+{
+ struct enum_product *ptr = enum_product_ptr(obj);
+ VALUE enums = ptr->enums;
+ long i;
+
+ for (i = 0; i < RARRAY_LEN(enums); i++) {
+ rb_check_funcall(RARRAY_AREF(enums, i), id_rewind, 0, 0);
+ }
+
+ return obj;
+}
+
+static VALUE
+inspect_enum_product(VALUE obj, VALUE dummy, int recur)
+{
+ VALUE klass = rb_obj_class(obj);
+ struct enum_product *ptr;
+
+ TypedData_Get_Struct(obj, struct enum_product, &enum_product_data_type, ptr);
+
+ if (!ptr || UNDEF_P(ptr->enums)) {
+ return rb_sprintf("#<%"PRIsVALUE": uninitialized>", rb_class_path(klass));
+ }
+
+ if (recur) {
+ return rb_sprintf("#<%"PRIsVALUE": ...>", rb_class_path(klass));
+ }
+
+ return rb_sprintf("#<%"PRIsVALUE": %+"PRIsVALUE">", rb_class_path(klass), ptr->enums);
+}
+
+/*
+ * call-seq:
+ * obj.inspect -> string
+ *
+ * Returns a printable version of the product enumerator.
+ */
+static VALUE
+enum_product_inspect(VALUE obj)
+{
+ return rb_exec_recursive(inspect_enum_product, obj, 0);
+}
+
+/*
+ * call-seq:
+ * Enumerator.product(*enums) -> enumerator
+ * Enumerator.product(*enums) { |elts| ... } -> enumerator
+ *
+ * Generates a new enumerator object that generates a Cartesian
+ * product of given enumerable objects. This is equivalent to
+ * Enumerator::Product.new.
+ *
+ * e = Enumerator.product(1..3, [4, 5])
+ * e.to_a #=> [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]]
+ * e.size #=> 6
+ *
+ * When a block is given, calls the block with each N-element array
+ * generated and returns +nil+.
+ */
+static VALUE
+enumerator_s_product(int argc, VALUE *argv, VALUE klass)
+{
+ VALUE enums = Qnil, options = Qnil, block = Qnil;
+
+ rb_scan_args(argc, argv, "*:&", &enums, &options, &block);
+
+ if (!NIL_P(options) && !RHASH_EMPTY_P(options)) {
+ rb_exc_raise(rb_keyword_error_new("unknown", rb_hash_keys(options)));
+ }
+
+ VALUE obj = enum_product_initialize(argc, argv, enum_product_allocate(rb_cEnumProduct));
+
+ if (!NIL_P(block)) {
+ enum_product_run(obj, block);
+ return Qnil;
+ }
+
+ return obj;
+}
+
+/*
* Document-class: Enumerator::ArithmeticSequence
*
* Enumerator::ArithmeticSequence is a subclass of Enumerator,
@@ -3459,6 +3882,13 @@ rb_arithmetic_sequence_beg_len_step(VALUE obj, long *begp, long *lenp, long *ste
*stepp = step;
if (step < 0) {
+ if (aseq.exclude_end && !NIL_P(aseq.end)) {
+ /* Handle exclusion before range reversal */
+ aseq.end = LONG2NUM(NUM2LONG(aseq.end) + 1);
+
+ /* Don't exclude the previous beginning */
+ aseq.exclude_end = 0;
+ }
VALUE tmp = aseq.begin;
aseq.begin = aseq.end;
aseq.end = tmp;
@@ -4214,6 +4644,22 @@ InitVM_Enumerator(void)
rb_undef_method(rb_cEnumChain, "peek");
rb_undef_method(rb_cEnumChain, "peek_values");
+ /* Product */
+ rb_cEnumProduct = rb_define_class_under(rb_cEnumerator, "Product", rb_cEnumerator);
+ rb_define_alloc_func(rb_cEnumProduct, enum_product_allocate);
+ rb_define_method(rb_cEnumProduct, "initialize", enum_product_initialize, -1);
+ rb_define_method(rb_cEnumProduct, "initialize_copy", enum_product_init_copy, 1);
+ rb_define_method(rb_cEnumProduct, "each", enum_product_each, 0);
+ rb_define_method(rb_cEnumProduct, "size", enum_product_size, 0);
+ rb_define_method(rb_cEnumProduct, "rewind", enum_product_rewind, 0);
+ rb_define_method(rb_cEnumProduct, "inspect", enum_product_inspect, 0);
+ rb_undef_method(rb_cEnumProduct, "feed");
+ rb_undef_method(rb_cEnumProduct, "next");
+ rb_undef_method(rb_cEnumProduct, "next_values");
+ rb_undef_method(rb_cEnumProduct, "peek");
+ rb_undef_method(rb_cEnumProduct, "peek_values");
+ rb_define_singleton_method(rb_cEnumerator, "product", enumerator_s_product, -1);
+
/* ArithmeticSequence */
rb_cArithSeq = rb_define_class_under(rb_cEnumerator, "ArithmeticSequence", rb_cEnumerator);
rb_undef_alloc_func(rb_cArithSeq);
@@ -4249,6 +4695,7 @@ Init_Enumerator(void)
id_method = rb_intern_const("method");
id_force = rb_intern_const("force");
id_to_enum = rb_intern_const("to_enum");
+ id_each_entry = rb_intern_const("each_entry");
id_begin = rb_intern_const("begin");
id_end = rb_intern_const("end");
id_step = rb_intern_const("step");
diff --git a/error.c b/error.c
index 17f524348f..726f57a4c0 100644
--- a/error.c
+++ b/error.c
@@ -34,6 +34,7 @@
#include "internal/io.h"
#include "internal/load.h"
#include "internal/object.h"
+#include "internal/string.h"
#include "internal/symbol.h"
#include "internal/thread.h"
#include "internal/variable.h"
@@ -99,54 +100,48 @@ static int
err_position_0(char *buf, long len, const char *file, int line)
{
if (!file) {
- return 0;
+ return 0;
}
else if (line == 0) {
- return snprintf(buf, len, "%s: ", file);
+ return snprintf(buf, len, "%s: ", file);
}
else {
- return snprintf(buf, len, "%s:%d: ", file, line);
+ return snprintf(buf, len, "%s:%d: ", file, line);
}
}
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 5, 0)
static VALUE
err_vcatf(VALUE str, const char *pre, const char *file, int line,
- const char *fmt, va_list args)
+ const char *fmt, va_list args)
{
if (file) {
- rb_str_cat2(str, file);
- if (line) rb_str_catf(str, ":%d", line);
- rb_str_cat2(str, ": ");
+ rb_str_cat2(str, file);
+ if (line) rb_str_catf(str, ":%d", line);
+ rb_str_cat2(str, ": ");
}
if (pre) rb_str_cat2(str, pre);
rb_str_vcatf(str, fmt, args);
return str;
}
+static VALUE syntax_error_with_path(VALUE, VALUE, VALUE*, rb_encoding*);
+
VALUE
rb_syntax_error_append(VALUE exc, VALUE file, int line, int column,
- rb_encoding *enc, const char *fmt, va_list args)
+ rb_encoding *enc, const char *fmt, va_list args)
{
const char *fn = NIL_P(file) ? NULL : RSTRING_PTR(file);
if (!exc) {
- VALUE mesg = rb_enc_str_new(0, 0, enc);
- err_vcatf(mesg, NULL, fn, line, fmt, args);
- rb_str_cat2(mesg, "\n");
- rb_write_error_str(mesg);
+ VALUE mesg = rb_enc_str_new(0, 0, enc);
+ err_vcatf(mesg, NULL, fn, line, fmt, args);
+ rb_str_cat2(mesg, "\n");
+ rb_write_error_str(mesg);
}
else {
- VALUE mesg;
- if (NIL_P(exc)) {
- mesg = rb_enc_str_new(0, 0, enc);
- exc = rb_class_new_instance(1, &mesg, rb_eSyntaxError);
- }
- else {
- mesg = rb_attr_get(exc, idMesg);
- if (RSTRING_LEN(mesg) > 0 && *(RSTRING_END(mesg)-1) != '\n')
- rb_str_cat_cstr(mesg, "\n");
- }
- err_vcatf(mesg, NULL, fn, line, fmt, args);
+ VALUE mesg;
+ exc = syntax_error_with_path(exc, file, &mesg, enc);
+ err_vcatf(mesg, NULL, fn, line, fmt, args);
}
return exc;
@@ -357,47 +352,42 @@ warn_vsprintf(rb_encoding *enc, const char *file, int line, const char *fmt, va_
return rb_str_cat2(str, "\n");
}
+#define with_warn_vsprintf(file, line, fmt) \
+ VALUE str; \
+ va_list args; \
+ va_start(args, fmt); \
+ str = warn_vsprintf(NULL, file, line, fmt, args); \
+ va_end(args);
+
void
rb_compile_warn(const char *file, int line, const char *fmt, ...)
{
- VALUE str;
- va_list args;
-
- if (NIL_P(ruby_verbose)) return;
-
- va_start(args, fmt);
- str = warn_vsprintf(NULL, file, line, fmt, args);
- va_end(args);
- rb_write_warning_str(str);
+ if (!NIL_P(ruby_verbose)) {
+ with_warn_vsprintf(file, line, fmt) {
+ rb_write_warning_str(str);
+ }
+ }
}
/* rb_compile_warning() reports only in verbose mode */
void
rb_compile_warning(const char *file, int line, const char *fmt, ...)
{
- VALUE str;
- va_list args;
-
- if (!RTEST(ruby_verbose)) return;
-
- va_start(args, fmt);
- str = warn_vsprintf(NULL, file, line, fmt, args);
- va_end(args);
- rb_write_warning_str(str);
+ if (RTEST(ruby_verbose)) {
+ with_warn_vsprintf(file, line, fmt) {
+ rb_write_warning_str(str);
+ }
+ }
}
void
rb_category_compile_warn(rb_warning_category_t category, const char *file, int line, const char *fmt, ...)
{
- VALUE str;
- va_list args;
-
- if (NIL_P(ruby_verbose)) return;
-
- va_start(args, fmt);
- str = warn_vsprintf(NULL, file, line, fmt, args);
- va_end(args);
- rb_warn_category(str, rb_warning_category_to_name(category));
+ if (!NIL_P(ruby_verbose)) {
+ with_warn_vsprintf(file, line, fmt) {
+ rb_warn_category(str, rb_warning_category_to_name(category));
+ }
+ }
}
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 0)
@@ -410,8 +400,10 @@ warning_string(rb_encoding *enc, const char *fmt, va_list args)
}
#define with_warning_string(mesg, enc, fmt) \
+ with_warning_string_from(mesg, enc, fmt, fmt)
+#define with_warning_string_from(mesg, enc, fmt, last_arg) \
VALUE mesg; \
- va_list args; va_start(args, fmt); \
+ va_list args; va_start(args, last_arg); \
mesg = warning_string(enc, fmt, args); \
va_end(args);
@@ -419,16 +411,16 @@ void
rb_warn(const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
- with_warning_string(mesg, 0, fmt) {
- rb_write_warning_str(mesg);
- }
+ with_warning_string(mesg, 0, fmt) {
+ rb_write_warning_str(mesg);
+ }
}
}
void
rb_category_warn(rb_warning_category_t category, const char *fmt, ...)
{
- if (!NIL_P(ruby_verbose)) {
+ if (!NIL_P(ruby_verbose) && rb_warning_category_enabled_p(category)) {
with_warning_string(mesg, 0, fmt) {
rb_warn_category(mesg, rb_warning_category_to_name(category));
}
@@ -439,9 +431,9 @@ void
rb_enc_warn(rb_encoding *enc, const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
- with_warning_string(mesg, enc, fmt) {
- rb_write_warning_str(mesg);
- }
+ with_warning_string(mesg, enc, fmt) {
+ rb_write_warning_str(mesg);
+ }
}
}
@@ -450,9 +442,9 @@ void
rb_warning(const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
- with_warning_string(mesg, 0, fmt) {
- rb_write_warning_str(mesg);
- }
+ with_warning_string(mesg, 0, fmt) {
+ rb_write_warning_str(mesg);
+ }
}
}
@@ -460,7 +452,7 @@ rb_warning(const char *fmt, ...)
void
rb_category_warning(rb_warning_category_t category, const char *fmt, ...)
{
- if (RTEST(ruby_verbose)) {
+ if (RTEST(ruby_verbose) && rb_warning_category_enabled_p(category)) {
with_warning_string(mesg, 0, fmt) {
rb_warn_category(mesg, rb_warning_category_to_name(category));
}
@@ -480,9 +472,9 @@ void
rb_enc_warning(rb_encoding *enc, const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
- with_warning_string(mesg, enc, fmt) {
- rb_write_warning_str(mesg);
- }
+ with_warning_string(mesg, enc, fmt) {
+ rb_write_warning_str(mesg);
+ }
}
}
#endif
@@ -513,12 +505,9 @@ rb_warn_deprecated(const char *fmt, const char *suggest, ...)
{
if (!deprecation_warning_enabled()) return;
- va_list args;
- va_start(args, suggest);
- VALUE mesg = warning_string(0, fmt, args);
- va_end(args);
-
- warn_deprecated(mesg, NULL, suggest);
+ with_warning_string_from(mesg, 0, fmt, suggest) {
+ warn_deprecated(mesg, NULL, suggest);
+ }
}
void
@@ -526,19 +515,16 @@ rb_warn_deprecated_to_remove(const char *removal, const char *fmt, const char *s
{
if (!deprecation_warning_enabled()) return;
- va_list args;
- va_start(args, suggest);
- VALUE mesg = warning_string(0, fmt, args);
- va_end(args);
-
- warn_deprecated(mesg, removal, suggest);
+ with_warning_string_from(mesg, 0, fmt, suggest) {
+ warn_deprecated(mesg, removal, suggest);
+ }
}
static inline int
end_with_asciichar(VALUE str, int c)
{
return RB_TYPE_P(str, T_STRING) &&
- rb_str_end_with_asciichar(str, c);
+ rb_str_end_with_asciichar(str, c);
}
/* :nodoc: */
@@ -546,7 +532,7 @@ static VALUE
warning_write(int argc, VALUE *argv, VALUE buf)
{
while (argc-- > 0) {
- rb_str_append(buf, *argv++);
+ rb_str_append(buf, *argv++);
}
return buf;
}
@@ -571,38 +557,38 @@ rb_warn_m(rb_execution_context_t *ec, VALUE exc, VALUE msgs, VALUE uplevel, VALU
if (!NIL_P(location)) {
location = rb_ary_entry(location, 0);
}
- }
- if (argc > 1 || !NIL_P(uplevel) || !end_with_asciichar(str, '\n')) {
- VALUE path;
- if (NIL_P(uplevel)) {
- str = rb_str_tmp_new(0);
- }
- else if (NIL_P(location) ||
- NIL_P(path = rb_funcall(location, rb_intern("path"), 0))) {
- str = rb_str_new_cstr("warning: ");
- }
- else {
- str = rb_sprintf("%s:%ld: warning: ",
- rb_string_value_ptr(&path),
- NUM2LONG(rb_funcall(location, rb_intern("lineno"), 0)));
- }
- RBASIC_SET_CLASS(str, rb_cWarningBuffer);
- rb_io_puts(argc, argv, str);
- RBASIC_SET_CLASS(str, rb_cString);
- }
+ }
+ if (argc > 1 || !NIL_P(uplevel) || !end_with_asciichar(str, '\n')) {
+ VALUE path;
+ if (NIL_P(uplevel)) {
+ str = rb_str_tmp_new(0);
+ }
+ else if (NIL_P(location) ||
+ NIL_P(path = rb_funcall(location, rb_intern("path"), 0))) {
+ str = rb_str_new_cstr("warning: ");
+ }
+ else {
+ str = rb_sprintf("%s:%ld: warning: ",
+ rb_string_value_ptr(&path),
+ NUM2LONG(rb_funcall(location, rb_intern("lineno"), 0)));
+ }
+ RBASIC_SET_CLASS(str, rb_cWarningBuffer);
+ rb_io_puts(argc, argv, str);
+ RBASIC_SET_CLASS(str, rb_cString);
+ }
if (!NIL_P(category)) {
category = rb_to_symbol_type(category);
rb_warning_category_from_name(category);
}
- if (exc == rb_mWarning) {
- rb_must_asciicompat(str);
- rb_write_error_str(str);
- }
- else {
+ if (exc == rb_mWarning) {
+ rb_must_asciicompat(str);
+ rb_write_error_str(str);
+ }
+ else {
rb_warn_category(str, category);
- }
+ }
}
return Qnil;
}
@@ -621,7 +607,7 @@ rb_bug_reporter_add(void (*func)(FILE *, void *), void *data)
{
struct bug_reporters *reporter;
if (bug_reporters_size >= MAX_BUG_REPORTERS) {
- return 0; /* failed to register */
+ return 0; /* failed to register */
}
reporter = &bug_reporters[bug_reporters_size++];
reporter->func = func;
@@ -640,7 +626,7 @@ bug_report_file(const char *file, int line)
int len = err_position_0(buf, sizeof(buf), file, line);
if ((ssize_t)fwrite(buf, 1, len, out) == (ssize_t)len ||
- (ssize_t)fwrite(buf, 1, len, (out = stdout)) == (ssize_t)len) {
+ (ssize_t)fwrite(buf, 1, len, (out = stdout)) == (ssize_t)len) {
return out;
}
@@ -657,40 +643,45 @@ bug_important_message(FILE *out, const char *const msg, size_t len)
if (!len) return;
if (isatty(fileno(out))) {
- static const char red[] = "\033[;31;1;7m";
- static const char green[] = "\033[;32;7m";
- static const char reset[] = "\033[m";
- const char *e = strchr(p, '\n');
- const int w = (int)(e - p);
- do {
- int i = (int)(e - p);
- fputs(*p == ' ' ? green : red, out);
- fwrite(p, 1, e - p, out);
- for (; i < w; ++i) fputc(' ', out);
- fputs(reset, out);
- fputc('\n', out);
- } while ((p = e + 1) < endmsg && (e = strchr(p, '\n')) != 0 && e > p + 1);
+ static const char red[] = "\033[;31;1;7m";
+ static const char green[] = "\033[;32;7m";
+ static const char reset[] = "\033[m";
+ const char *e = strchr(p, '\n');
+ const int w = (int)(e - p);
+ do {
+ int i = (int)(e - p);
+ fputs(*p == ' ' ? green : red, out);
+ fwrite(p, 1, e - p, out);
+ for (; i < w; ++i) fputc(' ', out);
+ fputs(reset, out);
+ fputc('\n', out);
+ } while ((p = e + 1) < endmsg && (e = strchr(p, '\n')) != 0 && e > p + 1);
}
fwrite(p, 1, endmsg - p, out);
}
+#undef CRASH_REPORTER_MAY_BE_CREATED
+#if defined(__APPLE__) && \
+ (!defined(MAC_OS_X_VERSION_10_6) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6 || defined(__POWERPC__)) /* 10.6 PPC case */
+# define CRASH_REPORTER_MAY_BE_CREATED
+#endif
static void
preface_dump(FILE *out)
{
#if defined __APPLE__
static const char msg[] = ""
- "-- Crash Report log information "
- "--------------------------------------------\n"
- " See Crash Report log file in one of the following locations:\n"
-# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
- " * ~/Library/Logs/CrashReporter\n"
- " * /Library/Logs/CrashReporter\n"
+ "-- Crash Report log information "
+ "--------------------------------------------\n"
+ " See Crash Report log file in one of the following locations:\n"
+# ifdef CRASH_REPORTER_MAY_BE_CREATED
+ " * ~/Library/Logs/CrashReporter\n"
+ " * /Library/Logs/CrashReporter\n"
# endif
- " * ~/Library/Logs/DiagnosticReports\n"
- " * /Library/Logs/DiagnosticReports\n"
- " for more details.\n"
- "Don't forget to include the above Crash Report log file in bug reports.\n"
- "\n";
+ " * ~/Library/Logs/DiagnosticReports\n"
+ " * /Library/Logs/DiagnosticReports\n"
+ " for more details.\n"
+ "Don't forget to include the above Crash Report log file in bug reports.\n"
+ "\n";
const size_t msglen = sizeof(msg) - 1;
#else
const char *msg = NULL;
@@ -704,15 +695,15 @@ postscript_dump(FILE *out)
{
#if defined __APPLE__
static const char msg[] = ""
- "[IMPORTANT]"
- /*" ------------------------------------------------"*/
- "\n""Don't forget to include the Crash Report log file under\n"
-# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
- "CrashReporter or "
+ "[IMPORTANT]"
+ /*" ------------------------------------------------"*/
+ "\n""Don't forget to include the Crash Report log file under\n"
+# ifdef CRASH_REPORTER_MAY_BE_CREATED
+ "CrashReporter or "
# endif
- "DiagnosticReports directory in bug reports.\n"
- /*"------------------------------------------------------------\n"*/
- "\n";
+ "DiagnosticReports directory in bug reports.\n"
+ /*"------------------------------------------------------------\n"*/
+ "\n";
const size_t msglen = sizeof(msg) - 1;
#else
const char *msg = NULL;
@@ -747,11 +738,11 @@ bug_report_end(FILE *out)
{
/* call additional bug reporters */
{
- int i;
- for (i=0; i<bug_reporters_size; i++) {
- struct bug_reporters *reporter = &bug_reporters[i];
- (*reporter->func)(out, reporter->data);
- }
+ int i;
+ for (i=0; i<bug_reporters_size; i++) {
+ struct bug_reporters *reporter = &bug_reporters[i];
+ (*reporter->func)(out, reporter->data);
+ }
}
postscript_dump(out);
}
@@ -759,18 +750,18 @@ bug_report_end(FILE *out)
#define report_bug(file, line, fmt, ctx) do { \
FILE *out = bug_report_file(file, line); \
if (out) { \
- bug_report_begin(out, fmt); \
- rb_vm_bugreport(ctx); \
- bug_report_end(out); \
+ bug_report_begin(out, fmt); \
+ rb_vm_bugreport(ctx); \
+ bug_report_end(out); \
} \
} while (0) \
#define report_bug_valist(file, line, fmt, ctx, args) do { \
FILE *out = bug_report_file(file, line); \
if (out) { \
- bug_report_begin_valist(out, fmt, args); \
- rb_vm_bugreport(ctx); \
- bug_report_end(out); \
+ bug_report_begin_valist(out, fmt, args); \
+ rb_vm_bugreport(ctx); \
+ bug_report_end(out); \
} \
} while (0) \
@@ -816,7 +807,7 @@ rb_bug_for_fatal_signal(ruby_sighandler_t default_sighandler, int sig, const voi
int line = 0;
if (GET_EC()) {
- file = rb_source_location_cstr(&line);
+ file = rb_source_location_cstr(&line);
}
report_bug(file, line, fmt, ctx);
@@ -856,14 +847,14 @@ rb_async_bug_errno(const char *mesg, int errno_arg)
WRITE_CONST(2, "\n");
if (errno_arg == 0) {
- WRITE_CONST(2, "errno == 0 (NOERROR)\n");
+ WRITE_CONST(2, "errno == 0 (NOERROR)\n");
}
else {
- const char *errno_str = rb_strerrno(errno_arg);
+ const char *errno_str = rb_strerrno(errno_arg);
- if (!errno_str)
- errno_str = "undefined errno";
- write_or_abort(2, errno_str, strlen(errno_str));
+ if (!errno_str)
+ errno_str = "undefined errno";
+ write_or_abort(2, errno_str, strlen(errno_str));
}
WRITE_CONST(2, "\n\n");
write_or_abort(2, rb_dynamic_description, strlen(rb_dynamic_description));
@@ -948,22 +939,22 @@ builtin_class_name(VALUE x)
const char *etype;
if (NIL_P(x)) {
- etype = "nil";
+ etype = "nil";
}
else if (FIXNUM_P(x)) {
- etype = "Integer";
+ etype = "Integer";
}
else if (SYMBOL_P(x)) {
- etype = "Symbol";
+ etype = "Symbol";
}
else if (RB_TYPE_P(x, T_TRUE)) {
- etype = "true";
+ etype = "true";
}
else if (RB_TYPE_P(x, T_FALSE)) {
- etype = "false";
+ etype = "false";
}
else {
- etype = NULL;
+ etype = NULL;
}
return etype;
}
@@ -974,7 +965,7 @@ rb_builtin_class_name(VALUE x)
const char *etype = builtin_class_name(x);
if (!etype) {
- etype = rb_obj_classname(x);
+ etype = rb_obj_classname(x);
}
return etype;
}
@@ -991,14 +982,14 @@ unexpected_type(VALUE x, int xt, int t)
if (tname) {
mesg = rb_sprintf("wrong argument type %"PRIsVALUE" (expected %s)",
displaying_class_of(x), tname);
- exc = rb_eTypeError;
+ exc = rb_eTypeError;
}
else if (xt > T_MASK && xt <= 0x3f) {
- mesg = rb_sprintf("unknown type 0x%x (0x%x given, probably comes"
- " from extension library for ruby 1.8)", t, xt);
+ mesg = rb_sprintf("unknown type 0x%x (0x%x given, probably comes"
+ " from extension library for ruby 1.8)", t, xt);
}
else {
- mesg = rb_sprintf("unknown type 0x%x (0x%x given)", t, xt);
+ mesg = rb_sprintf("unknown type 0x%x (0x%x given)", t, xt);
}
rb_exc_raise(rb_exc_new_str(exc, mesg));
}
@@ -1008,8 +999,8 @@ rb_check_type(VALUE x, int t)
{
int xt;
- if (RB_UNLIKELY(x == Qundef)) {
- rb_bug(UNDEF_LEAKED);
+ if (RB_UNLIKELY(UNDEF_P(x))) {
+ rb_bug(UNDEF_LEAKED);
}
xt = TYPE(x);
@@ -1022,15 +1013,15 @@ rb_check_type(VALUE x, int t)
* So it is not enough to just check `T_DATA`, it must be
* identified by its `type` using `Check_TypedStruct` instead.
*/
- unexpected_type(x, xt, t);
+ unexpected_type(x, xt, t);
}
}
void
rb_unexpected_type(VALUE x, int t)
{
- if (RB_UNLIKELY(x == Qundef)) {
- rb_bug(UNDEF_LEAKED);
+ if (RB_UNLIKELY(UNDEF_P(x))) {
+ rb_bug(UNDEF_LEAKED);
}
unexpected_type(x, TYPE(x), t);
@@ -1040,8 +1031,8 @@ int
rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t *parent)
{
while (child) {
- if (child == parent) return 1;
- child = child->parent;
+ if (child == parent) return 1;
+ child = child->parent;
}
return 0;
}
@@ -1050,8 +1041,8 @@ int
rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
{
if (!RB_TYPE_P(obj, T_DATA) ||
- !RTYPEDDATA_P(obj) || !rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type)) {
- return 0;
+ !RTYPEDDATA_P(obj) || !rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type)) {
+ return 0;
}
return 1;
}
@@ -1231,7 +1222,7 @@ VALUE
rb_get_message(VALUE exc)
{
VALUE e = rb_check_funcall(exc, id_message, 0, 0);
- if (e == Qundef) return Qnil;
+ if (UNDEF_P(e)) return Qnil;
if (!RB_TYPE_P(e, T_STRING)) e = rb_check_string_type(e);
return e;
}
@@ -1246,7 +1237,7 @@ rb_get_detailed_message(VALUE exc, VALUE opt)
else {
e = rb_check_funcall_kw(exc, id_detailed_message, 1, &opt, 1);
}
- if (e == Qundef) return Qnil;
+ if (UNDEF_P(e)) return Qnil;
if (!RB_TYPE_P(e, T_STRING)) e = rb_check_string_type(e);
return e;
}
@@ -1444,8 +1435,15 @@ exc_inspect(VALUE exc)
str = rb_str_buf_new2("#<");
klass = rb_class_name(klass);
rb_str_buf_append(str, klass);
- rb_str_buf_cat(str, ": ", 2);
- rb_str_buf_append(str, exc);
+
+ if (RTEST(rb_str_include(exc, rb_str_new2("\n")))) {
+ rb_str_catf(str, ":%+"PRIsVALUE, exc);
+ }
+ else {
+ rb_str_buf_cat(str, ": ", 2);
+ rb_str_buf_append(str, exc);
+ }
+
rb_str_buf_cat(str, ">", 1);
return str;
@@ -1494,8 +1492,8 @@ exc_backtrace(VALUE exc)
obj = rb_attr_get(exc, id_bt);
if (rb_backtrace_p(obj)) {
- obj = rb_backtrace_to_str_ary(obj);
- /* rb_ivar_set(exc, id_bt, obj); */
+ obj = rb_backtrace_to_str_ary(obj);
+ /* rb_ivar_set(exc, id_bt, obj); */
}
return obj;
@@ -1509,16 +1507,16 @@ rb_get_backtrace(VALUE exc)
ID mid = id_backtrace;
VALUE info;
if (rb_method_basic_definition_p(CLASS_OF(exc), id_backtrace)) {
- VALUE klass = rb_eException;
- rb_execution_context_t *ec = GET_EC();
- if (NIL_P(exc))
- return Qnil;
- EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, exc, mid, mid, klass, Qundef);
- info = exc_backtrace(exc);
- EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, exc, mid, mid, klass, info);
+ VALUE klass = rb_eException;
+ rb_execution_context_t *ec = GET_EC();
+ if (NIL_P(exc))
+ return Qnil;
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, exc, mid, mid, klass, Qundef);
+ info = exc_backtrace(exc);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, exc, mid, mid, klass, info);
}
else {
- info = rb_funcallv(exc, mid, 0, 0);
+ info = rb_funcallv(exc, mid, 0, 0);
}
if (NIL_P(info)) return Qnil;
return rb_check_backtrace(info);
@@ -1541,7 +1539,7 @@ exc_backtrace_locations(VALUE exc)
obj = rb_attr_get(exc, id_bt_locations);
if (!NIL_P(obj)) {
- obj = rb_backtrace_to_location_ary(obj);
+ obj = rb_backtrace_to_location_ary(obj);
}
return obj;
}
@@ -1553,17 +1551,17 @@ rb_check_backtrace(VALUE bt)
static const char err[] = "backtrace must be Array of String";
if (!NIL_P(bt)) {
- if (RB_TYPE_P(bt, T_STRING)) return rb_ary_new3(1, bt);
- if (rb_backtrace_p(bt)) return bt;
- if (!RB_TYPE_P(bt, T_ARRAY)) {
- rb_raise(rb_eTypeError, err);
- }
- for (i=0;i<RARRAY_LEN(bt);i++) {
- VALUE e = RARRAY_AREF(bt, i);
- if (!RB_TYPE_P(e, T_STRING)) {
- rb_raise(rb_eTypeError, err);
- }
- }
+ if (RB_TYPE_P(bt, T_STRING)) return rb_ary_new3(1, bt);
+ if (rb_backtrace_p(bt)) return bt;
+ if (!RB_TYPE_P(bt, T_ARRAY)) {
+ rb_raise(rb_eTypeError, err);
+ }
+ for (i=0;i<RARRAY_LEN(bt);i++) {
+ VALUE e = RARRAY_AREF(bt, i);
+ if (!RB_TYPE_P(e, T_STRING)) {
+ rb_raise(rb_eTypeError, err);
+ }
+ }
}
return bt;
}
@@ -1628,26 +1626,26 @@ exc_equal(VALUE exc, VALUE obj)
if (exc == obj) return Qtrue;
if (rb_obj_class(exc) != rb_obj_class(obj)) {
- int state;
-
- obj = rb_protect(try_convert_to_exception, obj, &state);
- if (state || obj == Qundef) {
- rb_set_errinfo(Qnil);
- return Qfalse;
- }
- if (rb_obj_class(exc) != rb_obj_class(obj)) return Qfalse;
- mesg = rb_check_funcall(obj, id_message, 0, 0);
- if (mesg == Qundef) return Qfalse;
- backtrace = rb_check_funcall(obj, id_backtrace, 0, 0);
- if (backtrace == Qundef) return Qfalse;
+ int state;
+
+ obj = rb_protect(try_convert_to_exception, obj, &state);
+ if (state || UNDEF_P(obj)) {
+ rb_set_errinfo(Qnil);
+ return Qfalse;
+ }
+ if (rb_obj_class(exc) != rb_obj_class(obj)) return Qfalse;
+ mesg = rb_check_funcall(obj, id_message, 0, 0);
+ if (UNDEF_P(mesg)) return Qfalse;
+ backtrace = rb_check_funcall(obj, id_backtrace, 0, 0);
+ if (UNDEF_P(backtrace)) return Qfalse;
}
else {
- mesg = rb_attr_get(obj, id_mesg);
- backtrace = exc_backtrace(obj);
+ mesg = rb_attr_get(obj, id_mesg);
+ backtrace = exc_backtrace(obj);
}
if (!rb_equal(rb_attr_get(exc, id_mesg), mesg))
- return Qfalse;
+ return Qfalse;
return rb_equal(exc_backtrace(exc), backtrace);
}
@@ -1668,37 +1666,37 @@ exit_initialize(int argc, VALUE *argv, VALUE exc)
{
VALUE status;
if (argc > 0) {
- status = *argv;
-
- switch (status) {
- case Qtrue:
- status = INT2FIX(EXIT_SUCCESS);
- ++argv;
- --argc;
- break;
- case Qfalse:
- status = INT2FIX(EXIT_FAILURE);
- ++argv;
- --argc;
- break;
- default:
- status = rb_check_to_int(status);
- if (NIL_P(status)) {
- status = INT2FIX(EXIT_SUCCESS);
- }
- else {
+ status = *argv;
+
+ switch (status) {
+ case Qtrue:
+ status = INT2FIX(EXIT_SUCCESS);
+ ++argv;
+ --argc;
+ break;
+ case Qfalse:
+ status = INT2FIX(EXIT_FAILURE);
+ ++argv;
+ --argc;
+ break;
+ default:
+ status = rb_check_to_int(status);
+ if (NIL_P(status)) {
+ status = INT2FIX(EXIT_SUCCESS);
+ }
+ else {
#if EXIT_SUCCESS != 0
- if (status == INT2FIX(0))
- status = INT2FIX(EXIT_SUCCESS);
+ if (status == INT2FIX(0))
+ status = INT2FIX(EXIT_SUCCESS);
#endif
- ++argv;
- --argc;
- }
- break;
- }
+ ++argv;
+ --argc;
+ }
+ break;
+ }
}
else {
- status = INT2FIX(EXIT_SUCCESS);
+ status = INT2FIX(EXIT_SUCCESS);
}
rb_call_super(argc, argv);
rb_ivar_set(exc, id_status, status);
@@ -1734,7 +1732,7 @@ exit_success_p(VALUE exc)
int status;
if (NIL_P(status_val))
- return Qtrue;
+ return Qtrue;
status = NUM2INT(status_val);
return RBOOL(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
}
@@ -1742,7 +1740,7 @@ exit_success_p(VALUE exc)
static VALUE
err_init_recv(VALUE exc, VALUE recv)
{
- if (recv != Qundef) rb_ivar_set(exc, id_recv, recv);
+ if (!UNDEF_P(recv)) rb_ivar_set(exc, id_recv, recv);
return exc;
}
@@ -1820,7 +1818,9 @@ name_err_init_attr(VALUE exc, VALUE recv, VALUE method)
cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp);
rb_ivar_set(exc, id_name, method);
err_init_recv(exc, recv);
- if (cfp) rb_ivar_set(exc, id_iseq, rb_iseqw_new(cfp->iseq));
+ if (cfp && VM_FRAME_TYPE(cfp) != VM_FRAME_MAGIC_DUMMY) {
+ rb_ivar_set(exc, id_iseq, rb_iseqw_new(cfp->iseq));
+ }
return exc;
}
@@ -1897,10 +1897,10 @@ name_err_local_variables(VALUE self)
VALUE vars = rb_attr_get(self, id_local_variables);
if (NIL_P(vars)) {
- VALUE iseqw = rb_attr_get(self, id_iseq);
- if (!NIL_P(iseqw)) vars = rb_iseqw_local_variables(iseqw);
- if (NIL_P(vars)) vars = rb_ary_new();
- rb_ivar_set(self, id_local_variables, vars);
+ VALUE iseqw = rb_attr_get(self, id_iseq);
+ if (!NIL_P(iseqw)) vars = rb_iseqw_local_variables(iseqw);
+ if (NIL_P(vars)) vars = rb_ary_new();
+ rb_ivar_set(self, id_local_variables, vars);
}
return vars;
}
@@ -1976,9 +1976,9 @@ name_err_mesg_memsize(const void *p)
static const rb_data_type_t name_err_mesg_data_type = {
"name_err_mesg",
{
- name_err_mesg_mark,
- name_err_mesg_free,
- name_err_mesg_memsize,
+ name_err_mesg_mark,
+ name_err_mesg_free,
+ name_err_mesg_memsize,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
@@ -2035,13 +2035,13 @@ name_err_mesg_equal(VALUE obj1, VALUE obj2)
if (obj1 == obj2) return Qtrue;
if (rb_obj_class(obj2) != rb_cNameErrorMesg)
- return Qfalse;
+ return Qfalse;
TypedData_Get_Struct(obj1, VALUE, &name_err_mesg_data_type, ptr1);
TypedData_Get_Struct(obj2, VALUE, &name_err_mesg_data_type, ptr2);
for (i=0; i<NAME_ERR_MESG_COUNT; i++) {
- if (!rb_equal(ptr1[i], ptr2[i]))
- return Qfalse;
+ if (!rb_equal(ptr1[i], ptr2[i]))
+ return Qfalse;
}
return Qtrue;
}
@@ -2067,49 +2067,49 @@ name_err_mesg_to_str(VALUE obj)
mesg = ptr[NAME_ERR_MESG__MESG];
if (NIL_P(mesg)) return Qnil;
else {
- struct RString s_str, d_str;
- VALUE c, s, d = 0, args[4];
- int state = 0, singleton = 0;
- rb_encoding *usascii = rb_usascii_encoding();
+ struct RString s_str, d_str;
+ VALUE c, s, d = 0, args[4];
+ int state = 0, singleton = 0;
+ rb_encoding *usascii = rb_usascii_encoding();
#define FAKE_CSTR(v, str) rb_setup_fake_str((v), (str), rb_strlen_lit(str), usascii)
- obj = ptr[NAME_ERR_MESG__RECV];
- switch (obj) {
- case Qnil:
- d = FAKE_CSTR(&d_str, "nil");
- break;
- case Qtrue:
- d = FAKE_CSTR(&d_str, "true");
- break;
- case Qfalse:
- d = FAKE_CSTR(&d_str, "false");
- break;
- default:
- d = rb_protect(name_err_mesg_receiver_name, obj, &state);
- if (state || d == Qundef || NIL_P(d))
- d = rb_protect(rb_inspect, obj, &state);
- if (state) {
- rb_set_errinfo(Qnil);
- }
- d = rb_check_string_type(d);
- if (NIL_P(d)) {
- d = rb_any_to_s(obj);
- }
- singleton = (RSTRING_LEN(d) > 0 && RSTRING_PTR(d)[0] == '#');
- break;
- }
- if (!singleton) {
- s = FAKE_CSTR(&s_str, ":");
- c = rb_class_name(CLASS_OF(obj));
- }
- else {
- c = s = FAKE_CSTR(&s_str, "");
- }
+ obj = ptr[NAME_ERR_MESG__RECV];
+ switch (obj) {
+ case Qnil:
+ d = FAKE_CSTR(&d_str, "nil");
+ break;
+ case Qtrue:
+ d = FAKE_CSTR(&d_str, "true");
+ break;
+ case Qfalse:
+ d = FAKE_CSTR(&d_str, "false");
+ break;
+ default:
+ d = rb_protect(name_err_mesg_receiver_name, obj, &state);
+ if (state || NIL_OR_UNDEF_P(d))
+ d = rb_protect(rb_inspect, obj, &state);
+ if (state) {
+ rb_set_errinfo(Qnil);
+ }
+ d = rb_check_string_type(d);
+ if (NIL_P(d)) {
+ d = rb_any_to_s(obj);
+ }
+ singleton = (RSTRING_LEN(d) > 0 && RSTRING_PTR(d)[0] == '#');
+ break;
+ }
+ if (!singleton) {
+ s = FAKE_CSTR(&s_str, ":");
+ c = rb_class_name(CLASS_OF(obj));
+ }
+ else {
+ c = s = FAKE_CSTR(&s_str, "");
+ }
args[0] = rb_obj_as_string(ptr[NAME_ERR_MESG__NAME]);
- args[1] = d;
- args[2] = s;
- args[3] = c;
- mesg = rb_str_format(4, args, mesg);
+ args[1] = d;
+ args[2] = s;
+ args[3] = c;
+ mesg = rb_str_format(4, args, mesg);
}
return mesg;
}
@@ -2141,11 +2141,11 @@ name_err_receiver(VALUE self)
VALUE *ptr, recv, mesg;
recv = rb_ivar_lookup(self, id_recv, Qundef);
- if (recv != Qundef) return recv;
+ if (!UNDEF_P(recv)) return recv;
mesg = rb_attr_get(self, id_mesg);
if (!rb_typeddata_is_kind_of(mesg, &name_err_mesg_data_type)) {
- rb_raise(rb_eArgError, "no receiver is available");
+ rb_raise(rb_eArgError, "no receiver is available");
}
ptr = DATA_PTR(mesg);
return ptr[NAME_ERR_MESG__RECV];
@@ -2199,7 +2199,7 @@ key_err_receiver(VALUE self)
VALUE recv;
recv = rb_ivar_lookup(self, id_receiver, Qundef);
- if (recv != Qundef) return recv;
+ if (!UNDEF_P(recv)) return recv;
rb_raise(rb_eArgError, "no receiver is available");
}
@@ -2216,7 +2216,7 @@ key_err_key(VALUE self)
VALUE key;
key = rb_ivar_lookup(self, id_key, Qundef);
- if (key != Qundef) return key;
+ if (!UNDEF_P(key)) return key;
rb_raise(rb_eArgError, "no key is available");
}
@@ -2247,17 +2247,17 @@ key_err_initialize(int argc, VALUE *argv, VALUE self)
rb_call_super(rb_scan_args(argc, argv, "01:", NULL, &options), argv);
if (!NIL_P(options)) {
- ID keywords[2];
- VALUE values[numberof(keywords)];
- int i;
- keywords[0] = id_receiver;
- keywords[1] = id_key;
- rb_get_kwargs(options, keywords, 0, numberof(values), values);
- for (i = 0; i < numberof(values); ++i) {
- if (values[i] != Qundef) {
- rb_ivar_set(self, keywords[i], values[i]);
- }
- }
+ ID keywords[2];
+ VALUE values[numberof(keywords)];
+ int i;
+ keywords[0] = id_receiver;
+ keywords[1] = id_key;
+ rb_get_kwargs(options, keywords, 0, numberof(values), values);
+ for (i = 0; i < numberof(values); ++i) {
+ if (!UNDEF_P(values[i])) {
+ rb_ivar_set(self, keywords[i], values[i]);
+ }
+ }
}
return self;
@@ -2276,7 +2276,7 @@ no_matching_pattern_key_err_matchee(VALUE self)
VALUE matchee;
matchee = rb_ivar_lookup(self, id_matchee, Qundef);
- if (matchee != Qundef) return matchee;
+ if (!UNDEF_P(matchee)) return matchee;
rb_raise(rb_eArgError, "no matchee is available");
}
@@ -2293,7 +2293,7 @@ no_matching_pattern_key_err_key(VALUE self)
VALUE key;
key = rb_ivar_lookup(self, id_key, Qundef);
- if (key != Qundef) return key;
+ if (!UNDEF_P(key)) return key;
rb_raise(rb_eArgError, "no key is available");
}
@@ -2320,7 +2320,7 @@ no_matching_pattern_key_err_initialize(int argc, VALUE *argv, VALUE self)
keywords[1] = id_key;
rb_get_kwargs(options, keywords, 0, numberof(values), values);
for (i = 0; i < numberof(values); ++i) {
- if (values[i] != Qundef) {
+ if (!UNDEF_P(values[i])) {
rb_ivar_set(self, keywords[i], values[i]);
}
}
@@ -2342,13 +2342,32 @@ syntax_error_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE mesg;
if (argc == 0) {
- mesg = rb_fstring_lit("compile error");
- argc = 1;
- argv = &mesg;
+ mesg = rb_fstring_lit("compile error");
+ argc = 1;
+ argv = &mesg;
}
return rb_call_super(argc, argv);
}
+static VALUE
+syntax_error_with_path(VALUE exc, VALUE path, VALUE *mesg, rb_encoding *enc)
+{
+ if (NIL_P(exc)) {
+ *mesg = rb_enc_str_new(0, 0, enc);
+ exc = rb_class_new_instance(1, mesg, rb_eSyntaxError);
+ rb_ivar_set(exc, id_i_path, path);
+ }
+ else {
+ if (rb_attr_get(exc, id_i_path) != path) {
+ rb_raise(rb_eArgError, "SyntaxError#path changed");
+ }
+ VALUE s = *mesg = rb_attr_get(exc, idMesg);
+ if (RSTRING_LEN(s) > 0 && *(RSTRING_END(s)-1) != '\n')
+ rb_str_cat_cstr(s, "\n");
+ }
+ return exc;
+}
+
/*
* Document-module: Errno
*
@@ -2387,30 +2406,30 @@ set_syserr(int n, const char *name)
st_data_t error;
if (!st_lookup(syserr_tbl, n, &error)) {
- error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError);
+ error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError);
- /* capture nonblock errnos for WaitReadable/WaitWritable subclasses */
- switch (n) {
- case EAGAIN:
- rb_eEAGAIN = error;
+ /* capture nonblock errnos for WaitReadable/WaitWritable subclasses */
+ switch (n) {
+ case EAGAIN:
+ rb_eEAGAIN = error;
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
- break;
- case EWOULDBLOCK:
+ break;
+ case EWOULDBLOCK:
#endif
- rb_eEWOULDBLOCK = error;
- break;
- case EINPROGRESS:
- rb_eEINPROGRESS = error;
- break;
- }
+ rb_eEWOULDBLOCK = error;
+ break;
+ case EINPROGRESS:
+ rb_eEINPROGRESS = error;
+ break;
+ }
- rb_define_const(error, "Errno", INT2NUM(n));
- st_add_direct(syserr_tbl, n, error);
+ rb_define_const(error, "Errno", INT2NUM(n));
+ st_add_direct(syserr_tbl, n, error);
}
else {
- rb_define_const(rb_mErrno, name, error);
+ rb_define_const(rb_mErrno, name, error);
}
return error;
}
@@ -2421,10 +2440,10 @@ get_syserr(int n)
st_data_t error;
if (!st_lookup(syserr_tbl, n, &error)) {
- char name[8]; /* some Windows' errno have 5 digits. */
+ char name[8]; /* some Windows' errno have 5 digits. */
- snprintf(name, sizeof(name), "E%03d", n);
- error = set_syserr(n, name);
+ snprintf(name, sizeof(name), "E%03d", n);
+ error = set_syserr(n, name);
}
return error;
}
@@ -2442,41 +2461,38 @@ get_syserr(int n)
static VALUE
syserr_initialize(int argc, VALUE *argv, VALUE self)
{
-#if !defined(_WIN32)
- char *strerror();
-#endif
const char *err;
VALUE mesg, error, func, errmsg;
VALUE klass = rb_obj_class(self);
if (klass == rb_eSystemCallError) {
- st_data_t data = (st_data_t)klass;
- rb_scan_args(argc, argv, "12", &mesg, &error, &func);
- if (argc == 1 && FIXNUM_P(mesg)) {
- error = mesg; mesg = Qnil;
- }
- if (!NIL_P(error) && st_lookup(syserr_tbl, NUM2LONG(error), &data)) {
- klass = (VALUE)data;
- /* change class */
- if (!RB_TYPE_P(self, T_OBJECT)) { /* insurance to avoid type crash */
- rb_raise(rb_eTypeError, "invalid instance type");
- }
- RBASIC_SET_CLASS(self, klass);
- }
+ st_data_t data = (st_data_t)klass;
+ rb_scan_args(argc, argv, "12", &mesg, &error, &func);
+ if (argc == 1 && FIXNUM_P(mesg)) {
+ error = mesg; mesg = Qnil;
+ }
+ if (!NIL_P(error) && st_lookup(syserr_tbl, NUM2LONG(error), &data)) {
+ klass = (VALUE)data;
+ /* change class */
+ if (!RB_TYPE_P(self, T_OBJECT)) { /* insurance to avoid type crash */
+ rb_raise(rb_eTypeError, "invalid instance type");
+ }
+ RBASIC_SET_CLASS(self, klass);
+ }
}
else {
- rb_scan_args(argc, argv, "02", &mesg, &func);
- error = rb_const_get(klass, id_Errno);
+ rb_scan_args(argc, argv, "02", &mesg, &func);
+ error = rb_const_get(klass, id_Errno);
}
if (!NIL_P(error)) err = strerror(NUM2INT(error));
else err = "unknown error";
errmsg = rb_enc_str_new_cstr(err, rb_locale_encoding());
if (!NIL_P(mesg)) {
- VALUE str = StringValue(mesg);
+ VALUE str = StringValue(mesg);
- if (!NIL_P(func)) rb_str_catf(errmsg, " @ %"PRIsVALUE, func);
- rb_str_catf(errmsg, " - %"PRIsVALUE, str);
+ if (!NIL_P(func)) rb_str_catf(errmsg, " @ %"PRIsVALUE, func);
+ rb_str_catf(errmsg, " - %"PRIsVALUE, str);
}
mesg = errmsg;
@@ -2512,13 +2528,13 @@ syserr_eqq(VALUE self, VALUE exc)
VALUE num, e;
if (!rb_obj_is_kind_of(exc, rb_eSystemCallError)) {
- if (!rb_respond_to(exc, id_errno)) return Qfalse;
+ if (!rb_respond_to(exc, id_errno)) return Qfalse;
}
else if (self == rb_eSystemCallError) return Qtrue;
num = rb_attr_get(exc, id_errno);
if (NIL_P(num)) {
- num = rb_funcallv(exc, id_errno, 0, 0);
+ num = rb_funcallv(exc, id_errno, 0, 0);
}
e = rb_const_get(self, id_Errno);
return RBOOL(FIXNUM_P(num) ? num == e : rb_equal(num, e));
@@ -2945,6 +2961,8 @@ ivar_copy_i(st_data_t key, st_data_t val, st_data_t exc)
return ST_CONTINUE;
}
+void rb_exc_check_circular_cause(VALUE exc);
+
static VALUE
exception_loader(VALUE exc, VALUE obj)
{
@@ -2959,6 +2977,8 @@ exception_loader(VALUE exc, VALUE obj)
rb_ivar_foreach(obj, ivar_copy_i, exc);
+ rb_exc_check_circular_cause(exc);
+
if (rb_attr_get(exc, id_bt) == rb_attr_get(exc, id_bt_locations)) {
rb_ivar_set(exc, id_bt_locations, Qnil);
}
@@ -3010,9 +3030,16 @@ Init_Exception(void)
rb_eSyntaxError = rb_define_class("SyntaxError", rb_eScriptError);
rb_define_method(rb_eSyntaxError, "initialize", syntax_error_initialize, -1);
+ /* RDoc will use literal name value while parsing rb_attr,
+ * and will render `idPath` as an attribute name without this trick */
+ ID path = idPath;
+
+ /* the path failed to parse */
+ rb_attr(rb_eSyntaxError, path, TRUE, FALSE, FALSE);
+
rb_eLoadError = rb_define_class("LoadError", rb_eScriptError);
/* the path failed to load */
- rb_attr(rb_eLoadError, rb_intern_const("path"), TRUE, FALSE, FALSE);
+ rb_attr(rb_eLoadError, path, TRUE, FALSE, FALSE);
rb_eNotImpError = rb_define_class("NotImplementedError", rb_eScriptError);
@@ -3166,8 +3193,8 @@ void
rb_notimplement(void)
{
rb_raise(rb_eNotImpError,
- "%"PRIsVALUE"() function is unimplemented on this machine",
- rb_id2str(rb_frame_this_func()));
+ "%"PRIsVALUE"() function is unimplemented on this machine",
+ rb_id2str(rb_frame_this_func()));
}
void
@@ -3198,7 +3225,7 @@ make_errno_exc(const char *mesg)
errno = 0;
if (n == 0) {
- rb_bug("rb_sys_fail(%s) - errno == 0", mesg ? mesg : "");
+ rb_bug("rb_sys_fail(%s) - errno == 0", mesg ? mesg : "");
}
return rb_syserr_new(n, mesg);
}
@@ -3211,8 +3238,8 @@ make_errno_exc_str(VALUE mesg)
errno = 0;
if (!mesg) mesg = Qnil;
if (n == 0) {
- const char *s = !NIL_P(mesg) ? RSTRING_PTR(mesg) : "";
- rb_bug("rb_sys_fail_str(%s) - errno == 0", s);
+ const char *s = !NIL_P(mesg) ? RSTRING_PTR(mesg) : "";
+ rb_bug("rb_sys_fail_str(%s) - errno == 0", s);
}
return rb_syserr_new_str(n, mesg);
}
@@ -3278,10 +3305,10 @@ rb_syserr_new_path_in(const char *func_name, int n, VALUE path)
if (!path) path = Qnil;
if (n == 0) {
- const char *s = !NIL_P(path) ? RSTRING_PTR(path) : "";
- if (!func_name) func_name = "(null)";
- rb_bug("rb_sys_fail_path_in(%s, %s) - errno == 0",
- func_name, s);
+ const char *s = !NIL_P(path) ? RSTRING_PTR(path) : "";
+ if (!func_name) func_name = "(null)";
+ rb_bug("rb_sys_fail_path_in(%s, %s) - errno == 0",
+ func_name, s);
}
args[0] = path;
args[1] = rb_str_new_cstr(func_name);
@@ -3339,11 +3366,11 @@ void
rb_sys_warn(const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
- int errno_save = errno;
- with_warning_string(mesg, 0, fmt) {
- syserr_warning(mesg, errno_save);
- }
- errno = errno_save;
+ int errno_save = errno;
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
}
}
@@ -3351,9 +3378,9 @@ void
rb_syserr_warn(int err, const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
- with_warning_string(mesg, 0, fmt) {
- syserr_warning(mesg, err);
- }
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, err);
+ }
}
}
@@ -3361,11 +3388,11 @@ void
rb_sys_enc_warn(rb_encoding *enc, const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
- int errno_save = errno;
- with_warning_string(mesg, enc, fmt) {
- syserr_warning(mesg, errno_save);
- }
- errno = errno_save;
+ int errno_save = errno;
+ with_warning_string(mesg, enc, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
}
}
@@ -3373,9 +3400,9 @@ void
rb_syserr_enc_warn(int err, rb_encoding *enc, const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
- with_warning_string(mesg, enc, fmt) {
- syserr_warning(mesg, err);
- }
+ with_warning_string(mesg, enc, fmt) {
+ syserr_warning(mesg, err);
+ }
}
}
#endif
@@ -3384,11 +3411,11 @@ void
rb_sys_warning(const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
- int errno_save = errno;
- with_warning_string(mesg, 0, fmt) {
- syserr_warning(mesg, errno_save);
- }
- errno = errno_save;
+ int errno_save = errno;
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
}
}
@@ -3397,9 +3424,9 @@ void
rb_syserr_warning(int err, const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
- with_warning_string(mesg, 0, fmt) {
- syserr_warning(mesg, err);
- }
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, err);
+ }
}
}
#endif
@@ -3408,11 +3435,11 @@ void
rb_sys_enc_warning(rb_encoding *enc, const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
- int errno_save = errno;
- with_warning_string(mesg, enc, fmt) {
- syserr_warning(mesg, errno_save);
- }
- errno = errno_save;
+ int errno_save = errno;
+ with_warning_string(mesg, enc, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
}
}
@@ -3420,9 +3447,9 @@ void
rb_syserr_enc_warning(int err, rb_encoding *enc, const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
- with_warning_string(mesg, enc, fmt) {
- syserr_warning(mesg, err);
- }
+ with_warning_string(mesg, enc, fmt) {
+ syserr_warning(mesg, err);
+ }
}
}
@@ -3480,8 +3507,8 @@ rb_error_frozen_object(VALUE frozen_obj)
rb_exec_recursive(inspect_frozen_obj, frozen_obj, mesg);
if (!NIL_P(debug_info = rb_attr_get(frozen_obj, created_info))) {
- VALUE path = rb_ary_entry(debug_info, 0);
- VALUE line = rb_ary_entry(debug_info, 1);
+ VALUE path = rb_ary_entry(debug_info, 0);
+ VALUE line = rb_ary_entry(debug_info, 1);
rb_str_catf(mesg, ", created at %"PRIsVALUE":%"PRIsVALUE, path, line);
}
diff --git a/eval.c b/eval.c
index a059987824..a61dfb1289 100644
--- a/eval.c
+++ b/eval.c
@@ -21,6 +21,7 @@
#include "gc.h"
#include "internal.h"
#include "internal/class.h"
+#include "internal/cont.h"
#include "internal/error.h"
#include "internal/eval.h"
#include "internal/hash.h"
@@ -42,7 +43,7 @@ NORETURN(static void rb_raise_jump(VALUE, VALUE));
void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
void rb_ec_clear_all_trace_func(const rb_execution_context_t *ec);
-static int rb_ec_cleanup(rb_execution_context_t *ec, int ex);
+static int rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex);
static int rb_ec_exec_node(rb_execution_context_t *ec, void *n);
VALUE rb_eLocalJumpError;
@@ -67,7 +68,7 @@ ruby_setup(void)
enum ruby_tag_type state;
if (GET_VM())
- return 0;
+ return 0;
ruby_init_stack((void *)&state);
@@ -85,9 +86,9 @@ ruby_setup(void)
EC_PUSH_TAG(GET_EC());
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- rb_call_inits();
- ruby_prog_init();
- GET_VM()->running = 1;
+ rb_call_inits();
+ ruby_prog_init();
+ GET_VM()->running = 1;
}
EC_POP_TAG();
@@ -99,9 +100,11 @@ ruby_init(void)
{
int state = ruby_setup();
if (state) {
- if (RTEST(ruby_debug))
- error_print(GET_EC());
- exit(EXIT_FAILURE);
+ if (RTEST(ruby_debug)) {
+ rb_execution_context_t *ec = GET_EC();
+ rb_ec_error_print(ec, ec->errinfo);
+ }
+ exit(EXIT_FAILURE);
}
}
@@ -115,12 +118,13 @@ ruby_options(int argc, char **argv)
ruby_init_stack((void *)&iseq);
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- SAVE_ROOT_JMPBUF(GET_THREAD(), iseq = ruby_process_options(argc, argv));
+ SAVE_ROOT_JMPBUF(GET_THREAD(), iseq = ruby_process_options(argc, argv));
}
else {
rb_ec_clear_current_thread_trace_func(ec);
- state = error_handle(ec, state);
- iseq = (void *)INT2FIX(state);
+ int exitcode = error_handle(ec, ec->errinfo, state);
+ ec->errinfo = Qnil; /* just been handled */
+ iseq = (void *)INT2FIX(exitcode);
}
EC_POP_TAG();
return iseq;
@@ -136,7 +140,7 @@ rb_ec_fiber_scheduler_finalize(rb_execution_context_t *ec)
rb_fiber_scheduler_set(Qnil);
}
else {
- state = error_handle(ec, state);
+ state = error_handle(ec, ec->errinfo, state);
}
EC_POP_TAG();
}
@@ -175,20 +179,21 @@ ruby_finalize(void)
int
ruby_cleanup(int ex)
{
- return rb_ec_cleanup(GET_EC(), ex);
+ return rb_ec_cleanup(GET_EC(), (enum ruby_tag_type)ex);
}
static int
-rb_ec_cleanup(rb_execution_context_t *ec, int ex0)
+rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex)
{
int state;
- volatile VALUE errs[2] = { Qundef, Qundef };
- int nerr;
+ volatile VALUE save_error = Qundef;
+ volatile int sysex = EXIT_SUCCESS;
+ volatile int signaled = 0;
rb_thread_t *th = rb_ec_thread_ptr(ec);
rb_thread_t *const volatile th0 = th;
- volatile int sysex = EXIT_SUCCESS;
volatile int step = 0;
- volatile int ex = ex0;
+ volatile VALUE message = Qnil;
+ VALUE buf;
rb_threadptr_interrupt(th);
rb_threadptr_check_signal(th);
@@ -198,60 +203,61 @@ rb_ec_cleanup(rb_execution_context_t *ec, int ex0)
SAVE_ROOT_JMPBUF(th, { RUBY_VM_CHECK_INTS(ec); });
step_0: step++;
- errs[1] = ec->errinfo;
+ save_error = ec->errinfo;
if (THROW_DATA_P(ec->errinfo)) ec->errinfo = Qnil;
- ruby_init_stack(&errs[STACK_UPPER(errs, 0, 1)]);
+ ruby_init_stack(&message);
+ /* exits with failure but silently when an exception raised
+ * here */
SAVE_ROOT_JMPBUF(th, rb_ec_teardown(ec));
step_1: step++;
- /* protect from Thread#raise */
- th->status = THREAD_KILLED;
+ VALUE err = ec->errinfo;
+ volatile int mode0 = 0, mode1 = 0;
+ if (err != save_error && !NIL_P(err)) {
+ mode0 = exiting_split(err, &sysex, &signaled);
+ }
+
+ /* exceptions after here will be ignored */
+
+ /* build error message including causes */
+ err = ATOMIC_VALUE_EXCHANGE(save_error, Qnil);
+
+ if (!NIL_P(err) && !THROW_DATA_P(err)) {
+ mode1 = exiting_split(err, (mode0 & EXITING_WITH_STATUS) ? NULL : &sysex, &signaled);
+ if (mode1 & EXITING_WITH_MESSAGE) {
+ buf = rb_str_new(NULL, 0);
+ SAVE_ROOT_JMPBUF(th, rb_ec_error_print_detailed(ec, err, buf, Qundef));
+ message = buf;
+ }
+ }
+
+ step_2: step++;
+ /* protect from Thread#raise */
+ th->status = THREAD_KILLED;
- errs[0] = ec->errinfo;
- SAVE_ROOT_JMPBUF(th, rb_ractor_terminate_all());
+ SAVE_ROOT_JMPBUF(th, rb_ractor_terminate_all());
+
+ step_3: step++;
+ if (!NIL_P(buf = message)) {
+ warn_print_str(buf);
+ }
+ else if (!NIL_OR_UNDEF_P(err = save_error) ||
+ (ex != TAG_NONE && !((mode0|mode1) & EXITING_WITH_STATUS))) {
+ sysex = error_handle(ec, err, ex);
+ }
}
else {
th = th0;
- switch (step) {
- case 0: goto step_0;
- case 1: goto step_1;
- }
- if (ex == 0) ex = state;
- }
- ec->errinfo = errs[1];
- sysex = error_handle(ec, ex);
-
- state = 0;
- for (nerr = 0; nerr < numberof(errs); ++nerr) {
- VALUE err = ATOMIC_VALUE_EXCHANGE(errs[nerr], Qnil);
- VALUE sig;
-
- if (!RTEST(err)) continue;
-
- /* ec->errinfo contains a NODE while break'ing */
- if (THROW_DATA_P(err)) continue;
-
- if (rb_obj_is_kind_of(err, rb_eSystemExit)) {
- sysex = sysexit_status(err);
- break;
- }
- else if (rb_obj_is_kind_of(err, rb_eSignal)) {
- VALUE sig = rb_ivar_get(err, id_signo);
- state = NUM2INT(sig);
- break;
- }
- else if (rb_obj_is_kind_of(err, rb_eSystemCallError) &&
- FIXNUM_P(sig = rb_attr_get(err, id_signo))) {
- state = NUM2INT(sig);
- break;
+ switch (step) {
+ case 0: goto step_0;
+ case 1: goto step_1;
+ case 2: goto step_2;
+ case 3: goto step_3;
}
- else if (sysex == EXIT_SUCCESS) {
- sysex = EXIT_FAILURE;
- }
}
- mjit_finish(true); // We still need ISeqs here.
+ mjit_finish(true); // We still need ISeqs here, so it's before rb_ec_finalize().
rb_ec_finalize(ec);
@@ -262,7 +268,10 @@ rb_ec_cleanup(rb_execution_context_t *ec, int ex0)
th = th0;
rb_thread_stop_timer_thread();
ruby_vm_destruct(th->vm);
- if (state) ruby_default_signal(state);
+ // For YJIT, call this after ruby_vm_destruct() frees jit_cont for the root fiber.
+ rb_jit_cont_finish();
+
+ if (signaled) ruby_default_signal(signaled);
return sysex;
}
@@ -277,9 +286,9 @@ rb_ec_exec_node(rb_execution_context_t *ec, void *n)
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
rb_thread_t *const th = rb_ec_thread_ptr(ec);
- SAVE_ROOT_JMPBUF(th, {
- rb_iseq_eval_main(iseq);
- });
+ SAVE_ROOT_JMPBUF(th, {
+ rb_iseq_eval_main(iseq);
+ });
}
EC_POP_TAG();
return state;
@@ -301,8 +310,8 @@ ruby_executable_node(void *n, int *status)
case Qtrue: s = EXIT_SUCCESS; break;
case Qfalse: s = EXIT_FAILURE; break;
default:
- if (!FIXNUM_P(v)) return TRUE;
- s = FIX2INT(v);
+ if (!FIXNUM_P(v)) return TRUE;
+ s = FIX2INT(v);
}
if (status) *status = s;
return FALSE;
@@ -314,8 +323,8 @@ ruby_run_node(void *n)
rb_execution_context_t *ec = GET_EC();
int status;
if (!ruby_executable_node(n, &status)) {
- rb_ec_cleanup(ec, 0);
- return status;
+ rb_ec_cleanup(ec, (NIL_P(ec->errinfo) ? TAG_NONE : TAG_RAISE));
+ return status;
}
ruby_init_stack((void *)&status);
return rb_ec_cleanup(ec, rb_ec_exec_node(ec, n));
@@ -350,12 +359,12 @@ rb_mod_nesting(VALUE _)
const rb_cref_t *cref = rb_vm_cref();
while (cref && CREF_NEXT(cref)) {
- VALUE klass = CREF_CLASS(cref);
- if (!CREF_PUSHED_BY_EVAL(cref) &&
- !NIL_P(klass)) {
- rb_ary_push(ary, klass);
- }
- cref = CREF_NEXT(cref);
+ VALUE klass = CREF_CLASS(cref);
+ if (!CREF_PUSHED_BY_EVAL(cref) &&
+ !NIL_P(klass)) {
+ rb_ary_push(ary, klass);
+ }
+ cref = CREF_NEXT(cref);
}
return ary;
}
@@ -391,23 +400,23 @@ rb_mod_s_constants(int argc, VALUE *argv, VALUE mod)
void *data = 0;
if (argc > 0 || mod != rb_cModule) {
- return rb_mod_constants(argc, argv, mod);
+ return rb_mod_constants(argc, argv, mod);
}
while (cref) {
- klass = CREF_CLASS(cref);
- if (!CREF_PUSHED_BY_EVAL(cref) &&
- !NIL_P(klass)) {
- data = rb_mod_const_at(CREF_CLASS(cref), data);
- if (!cbase) {
- cbase = klass;
- }
- }
- cref = CREF_NEXT(cref);
+ klass = CREF_CLASS(cref);
+ if (!CREF_PUSHED_BY_EVAL(cref) &&
+ !NIL_P(klass)) {
+ data = rb_mod_const_at(CREF_CLASS(cref), data);
+ if (!cbase) {
+ cbase = klass;
+ }
+ }
+ cref = CREF_NEXT(cref);
}
if (cbase) {
- data = rb_mod_const_of(cbase, data);
+ data = rb_mod_const_of(cbase, data);
}
return rb_const_list(data);
}
@@ -422,45 +431,45 @@ void
rb_class_modify_check(VALUE klass)
{
if (SPECIAL_CONST_P(klass)) {
- Check_Type(klass, T_CLASS);
+ Check_Type(klass, T_CLASS);
}
if (RB_TYPE_P(klass, T_MODULE)) {
rb_module_set_initialized(klass);
}
if (OBJ_FROZEN(klass)) {
- const char *desc;
-
- if (FL_TEST(klass, FL_SINGLETON)) {
- desc = "object";
- klass = rb_ivar_get(klass, id__attached__);
- if (!SPECIAL_CONST_P(klass)) {
- switch (BUILTIN_TYPE(klass)) {
- case T_MODULE:
- case T_ICLASS:
- desc = "Module";
- break;
- case T_CLASS:
- desc = "Class";
- break;
+ const char *desc;
+
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ desc = "object";
+ klass = rb_ivar_get(klass, id__attached__);
+ if (!SPECIAL_CONST_P(klass)) {
+ switch (BUILTIN_TYPE(klass)) {
+ case T_MODULE:
+ case T_ICLASS:
+ desc = "Module";
+ break;
+ case T_CLASS:
+ desc = "Class";
+ break;
default:
break;
- }
- }
- }
- else {
- switch (BUILTIN_TYPE(klass)) {
- case T_MODULE:
- case T_ICLASS:
- desc = "module";
- break;
- case T_CLASS:
- desc = "class";
- break;
- default:
+ }
+ }
+ }
+ else {
+ switch (BUILTIN_TYPE(klass)) {
+ case T_MODULE:
+ case T_ICLASS:
+ desc = "module";
+ break;
+ case T_CLASS:
+ desc = "class";
+ break;
+ default:
Check_Type(klass, T_CLASS);
UNREACHABLE;
- }
- }
+ }
+ }
rb_frozen_error_raise(klass, "can't modify frozen %s: %"PRIsVALUE, desc, klass);
}
}
@@ -474,23 +483,23 @@ exc_setup_cause(VALUE exc, VALUE cause)
{
#if OPT_SUPPORT_JOKE
if (NIL_P(cause)) {
- ID id_true_cause;
- CONST_ID(id_true_cause, "true_cause");
-
- cause = rb_attr_get(rb_eFatal, id_true_cause);
- if (NIL_P(cause)) {
- cause = rb_exc_new_cstr(rb_eFatal, "because using such Ruby");
- rb_ivar_set(cause, id_cause, INT2FIX(42)); /* the answer */
- OBJ_FREEZE(cause);
- rb_ivar_set(rb_eFatal, id_true_cause, cause);
- }
+ ID id_true_cause;
+ CONST_ID(id_true_cause, "true_cause");
+
+ cause = rb_attr_get(rb_eFatal, id_true_cause);
+ if (NIL_P(cause)) {
+ cause = rb_exc_new_cstr(rb_eFatal, "because using such Ruby");
+ rb_ivar_set(cause, id_cause, INT2FIX(42)); /* the answer */
+ OBJ_FREEZE(cause);
+ rb_ivar_set(rb_eFatal, id_true_cause, cause);
+ }
}
#endif
if (!NIL_P(cause) && cause != exc) {
- rb_ivar_set(exc, id_cause, cause);
- if (!rb_ivar_defined(cause, id_cause)) {
- rb_ivar_set(cause, id_cause, Qnil);
- }
+ rb_ivar_set(exc, id_cause, cause);
+ if (!rb_ivar_defined(cause, id_cause)) {
+ rb_ivar_set(cause, id_cause, Qnil);
+ }
}
return exc;
}
@@ -502,23 +511,23 @@ exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause)
int nocircular = 0;
if (NIL_P(mesg)) {
- mesg = ec->errinfo;
- if (INTERNAL_EXCEPTION_P(mesg)) EC_JUMP_TAG(ec, TAG_FATAL);
- nocause = 1;
+ mesg = ec->errinfo;
+ if (INTERNAL_EXCEPTION_P(mesg)) EC_JUMP_TAG(ec, TAG_FATAL);
+ nocause = 1;
}
if (NIL_P(mesg)) {
- mesg = rb_exc_new(rb_eRuntimeError, 0, 0);
- nocause = 0;
+ mesg = rb_exc_new(rb_eRuntimeError, 0, 0);
+ nocause = 0;
nocircular = 1;
}
- if (*cause == Qundef) {
- if (nocause) {
- *cause = Qnil;
+ if (UNDEF_P(*cause)) {
+ if (nocause) {
+ *cause = Qnil;
nocircular = 1;
- }
- else if (!rb_ivar_defined(mesg, id_cause)) {
- *cause = get_ec_errinfo(ec);
- }
+ }
+ else if (!rb_ivar_defined(mesg, id_cause)) {
+ *cause = get_ec_errinfo(ec);
+ }
else {
nocircular = 1;
}
@@ -527,13 +536,17 @@ exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause)
rb_raise(rb_eTypeError, "exception object expected");
}
- if (!nocircular && !NIL_P(*cause) && *cause != Qundef && *cause != mesg) {
+ if (!nocircular && !NIL_P(*cause) && !UNDEF_P(*cause) && *cause != mesg) {
+#if 0 /* maybe critical for some cases */
+ rb_exc_check_circular_cause(*cause);
+#else
VALUE c = *cause;
while (!NIL_P(c = rb_attr_get(c, id_cause))) {
if (c == mesg) {
rb_raise(rb_eArgError, "circular causes");
}
}
+#endif
}
return mesg;
}
@@ -546,68 +559,68 @@ setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE
const char *file = rb_source_location_cstr(&line);
const char *const volatile file0 = file;
- if ((file && !NIL_P(mesg)) || (cause != Qundef)) {
- volatile int state = 0;
-
- EC_PUSH_TAG(ec);
- if (EC_EXEC_TAG() == TAG_NONE && !(state = rb_ec_set_raised(ec))) {
- VALUE bt = rb_get_backtrace(mesg);
- if (!NIL_P(bt) || cause == Qundef) {
- if (OBJ_FROZEN(mesg)) {
- mesg = rb_obj_dup(mesg);
- }
- }
- if (cause != Qundef && !THROW_DATA_P(cause)) {
- exc_setup_cause(mesg, cause);
- }
- if (NIL_P(bt)) {
- VALUE at = rb_ec_backtrace_object(ec);
- rb_ivar_set(mesg, idBt_locations, at);
- set_backtrace(mesg, at);
- }
- rb_ec_reset_raised(ec);
- }
- EC_POP_TAG();
+ if ((file && !NIL_P(mesg)) || !UNDEF_P(cause)) {
+ volatile int state = 0;
+
+ EC_PUSH_TAG(ec);
+ if (EC_EXEC_TAG() == TAG_NONE && !(state = rb_ec_set_raised(ec))) {
+ VALUE bt = rb_get_backtrace(mesg);
+ if (!NIL_P(bt) || UNDEF_P(cause)) {
+ if (OBJ_FROZEN(mesg)) {
+ mesg = rb_obj_dup(mesg);
+ }
+ }
+ if (!UNDEF_P(cause) && !THROW_DATA_P(cause)) {
+ exc_setup_cause(mesg, cause);
+ }
+ if (NIL_P(bt)) {
+ VALUE at = rb_ec_backtrace_object(ec);
+ rb_ivar_set(mesg, idBt_locations, at);
+ set_backtrace(mesg, at);
+ }
+ rb_ec_reset_raised(ec);
+ }
+ EC_POP_TAG();
file = file0;
- if (state) goto fatal;
+ if (state) goto fatal;
}
if (!NIL_P(mesg)) {
- ec->errinfo = mesg;
+ ec->errinfo = mesg;
}
if (RTEST(ruby_debug) && !NIL_P(e = ec->errinfo) &&
- !rb_obj_is_kind_of(e, rb_eSystemExit)) {
- enum ruby_tag_type state;
-
- mesg = e;
- EC_PUSH_TAG(ec);
- if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- ec->errinfo = Qnil;
- e = rb_obj_as_string(mesg);
- ec->errinfo = mesg;
- if (file && line) {
- e = rb_sprintf("Exception `%"PRIsVALUE"' at %s:%d - %"PRIsVALUE"\n",
- rb_obj_class(mesg), file, line, e);
- }
- else if (file) {
- e = rb_sprintf("Exception `%"PRIsVALUE"' at %s - %"PRIsVALUE"\n",
- rb_obj_class(mesg), file, e);
- }
- else {
- e = rb_sprintf("Exception `%"PRIsVALUE"' - %"PRIsVALUE"\n",
- rb_obj_class(mesg), e);
- }
- warn_print_str(e);
- }
- EC_POP_TAG();
- if (state == TAG_FATAL && ec->errinfo == exception_error) {
- ec->errinfo = mesg;
- }
- else if (state) {
- rb_ec_reset_raised(ec);
- EC_JUMP_TAG(ec, state);
- }
+ !rb_obj_is_kind_of(e, rb_eSystemExit)) {
+ enum ruby_tag_type state;
+
+ mesg = e;
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ ec->errinfo = Qnil;
+ e = rb_obj_as_string(mesg);
+ ec->errinfo = mesg;
+ if (file && line) {
+ e = rb_sprintf("Exception `%"PRIsVALUE"' at %s:%d - %"PRIsVALUE"\n",
+ rb_obj_class(mesg), file, line, e);
+ }
+ else if (file) {
+ e = rb_sprintf("Exception `%"PRIsVALUE"' at %s - %"PRIsVALUE"\n",
+ rb_obj_class(mesg), file, e);
+ }
+ else {
+ e = rb_sprintf("Exception `%"PRIsVALUE"' - %"PRIsVALUE"\n",
+ rb_obj_class(mesg), e);
+ }
+ warn_print_str(e);
+ }
+ EC_POP_TAG();
+ if (state == TAG_FATAL && ec->errinfo == exception_error) {
+ ec->errinfo = mesg;
+ }
+ else if (state) {
+ rb_ec_reset_raised(ec);
+ EC_JUMP_TAG(ec, state);
+ }
}
if (rb_ec_set_raised(ec)) {
@@ -615,8 +628,8 @@ setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE
}
if (tag != TAG_FATAL) {
- RUBY_DTRACE_HOOK(RAISE, rb_obj_classname(ec->errinfo));
- EXEC_EVENT_HOOK(ec, RUBY_EVENT_RAISE, ec->cfp->self, 0, 0, 0, mesg);
+ RUBY_DTRACE_HOOK(RAISE, rb_obj_classname(ec->errinfo));
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_RAISE, ec->cfp->self, 0, 0, 0, mesg);
}
return;
@@ -630,11 +643,15 @@ setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE
void
rb_ec_setup_exception(const rb_execution_context_t *ec, VALUE mesg, VALUE cause)
{
- if (cause == Qundef) {
- cause = get_ec_errinfo(ec);
+ if (UNDEF_P(cause)) {
+ cause = get_ec_errinfo(ec);
}
if (cause != mesg) {
- rb_ivar_set(mesg, id_cause, cause);
+ if (THROW_DATA_P(cause)) {
+ cause = Qnil;
+ }
+
+ rb_ivar_set(mesg, id_cause, cause);
}
}
@@ -655,7 +672,7 @@ static void
rb_exc_exception(VALUE mesg, int tag, VALUE cause)
{
if (!NIL_P(mesg)) {
- mesg = make_exception(1, &mesg, FALSE);
+ mesg = make_exception(1, &mesg, FALSE);
}
rb_longjmp(GET_EC(), tag, mesg, cause);
}
@@ -699,20 +716,20 @@ extract_raise_opts(int argc, VALUE *argv, VALUE *opts)
{
int i;
if (argc > 0) {
- VALUE opt;
- argc = rb_scan_args(argc, argv, "*:", NULL, &opt);
- if (!NIL_P(opt)) {
- if (!RHASH_EMPTY_P(opt)) {
- ID keywords[1];
- CONST_ID(keywords[0], "cause");
- rb_get_kwargs(opt, keywords, 0, -1-raise_max_opt, opts);
- if (!RHASH_EMPTY_P(opt)) argv[argc++] = opt;
- return argc;
- }
- }
+ VALUE opt;
+ argc = rb_scan_args(argc, argv, "*:", NULL, &opt);
+ if (!NIL_P(opt)) {
+ if (!RHASH_EMPTY_P(opt)) {
+ ID keywords[1];
+ CONST_ID(keywords[0], "cause");
+ rb_get_kwargs(opt, keywords, 0, -1-raise_max_opt, opts);
+ if (!RHASH_EMPTY_P(opt)) argv[argc++] = opt;
+ return argc;
+ }
+ }
}
for (i = 0; i < raise_max_opt; ++i) {
- opts[i] = Qundef;
+ opts[i] = Qundef;
}
return argc;
}
@@ -725,7 +742,7 @@ rb_f_raise(int argc, VALUE *argv)
argc = extract_raise_opts(argc, argv, opts);
if (argc == 0) {
- if (*cause != Qundef) {
+ if (!UNDEF_P(*cause)) {
rb_raise(rb_eArgError, "only cause is given with no arguments");
}
err = get_errinfo();
@@ -784,24 +801,24 @@ make_exception(int argc, const VALUE *argv, int isstr)
case 0:
return Qnil;
case 1:
- exc = argv[0];
+ exc = argv[0];
if (isstr &&! NIL_P(exc)) {
- mesg = rb_check_string_type(exc);
- if (!NIL_P(mesg)) {
+ mesg = rb_check_string_type(exc);
+ if (!NIL_P(mesg)) {
return rb_exc_new3(rb_eRuntimeError, mesg);
- }
- }
+ }
+ }
case 2:
case 3:
- break;
+ break;
default:
rb_error_arity(argc, 0, 3);
}
if (NIL_P(mesg)) {
mesg = rb_check_funcall(argv[0], idException, argc != 1, &argv[1]);
}
- if (mesg == Qundef) {
+ if (UNDEF_P(mesg)) {
rb_raise(rb_eTypeError, "exception class/object expected");
}
if (!rb_obj_is_kind_of(mesg, rb_eException)) {
@@ -842,7 +859,7 @@ void
rb_jump_tag(int tag)
{
if (UNLIKELY(tag < TAG_RETURN || tag > TAG_FATAL)) {
- unknown_longjmp_status(tag);
+ unknown_longjmp_status(tag);
}
EC_JUMP_TAG(GET_EC(), tag);
}
@@ -851,10 +868,10 @@ int
rb_block_given_p(void)
{
if (rb_vm_frame_block_handler(GET_EC()->cfp) == VM_BLOCK_HANDLER_NONE) {
- return FALSE;
+ return FALSE;
}
else {
- return TRUE;
+ return TRUE;
}
}
@@ -872,7 +889,7 @@ void
rb_need_block(void)
{
if (!rb_block_given_p()) {
- rb_vm_localjump_error("no block given", Qnil, 0);
+ rb_vm_localjump_error("no block given", Qnil, 0);
}
}
@@ -901,48 +918,48 @@ rb_vrescue2(VALUE (* b_proc) (VALUE), VALUE data1,
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
retry_entry:
- result = (*b_proc) (data1);
+ result = (*b_proc) (data1);
}
else if (result) {
- /* escape from r_proc */
- if (state == TAG_RETRY) {
- state = TAG_NONE;
- ec->errinfo = Qnil;
- result = Qfalse;
- goto retry_entry;
- }
+ /* escape from r_proc */
+ if (state == TAG_RETRY) {
+ state = TAG_NONE;
+ ec->errinfo = Qnil;
+ result = Qfalse;
+ goto retry_entry;
+ }
}
else {
- rb_vm_rewind_cfp(ec, cfp);
-
- if (state == TAG_RAISE) {
- int handle = FALSE;
- VALUE eclass;
- va_list ap;
-
- result = Qnil;
- /* reuses args when raised again after retrying in r_proc */
- va_copy(ap, args);
- while ((eclass = va_arg(ap, VALUE)) != 0) {
- if (rb_obj_is_kind_of(ec->errinfo, eclass)) {
- handle = TRUE;
- break;
- }
- }
- va_end(ap);
-
- if (handle) {
- state = TAG_NONE;
- if (r_proc) {
- result = (*r_proc) (data2, ec->errinfo);
- }
- ec->errinfo = e_info;
- }
- }
+ rb_vm_rewind_cfp(ec, cfp);
+
+ if (state == TAG_RAISE) {
+ int handle = FALSE;
+ VALUE eclass;
+ va_list ap;
+
+ result = Qnil;
+ /* reuses args when raised again after retrying in r_proc */
+ va_copy(ap, args);
+ while ((eclass = va_arg(ap, VALUE)) != 0) {
+ if (rb_obj_is_kind_of(ec->errinfo, eclass)) {
+ handle = TRUE;
+ break;
+ }
+ }
+ va_end(ap);
+
+ if (handle) {
+ state = TAG_NONE;
+ if (r_proc) {
+ result = (*r_proc) (data2, ec->errinfo);
+ }
+ ec->errinfo = e_info;
+ }
+ }
}
EC_POP_TAG();
if (state)
- EC_JUMP_TAG(ec, state);
+ EC_JUMP_TAG(ec, state);
return result;
}
@@ -952,7 +969,7 @@ rb_rescue(VALUE (* b_proc)(VALUE), VALUE data1,
VALUE (* r_proc)(VALUE, VALUE), VALUE data2)
{
return rb_rescue2(b_proc, data1, r_proc, data2, rb_eStandardError,
- (VALUE)0);
+ (VALUE)0);
}
VALUE
@@ -965,10 +982,10 @@ rb_protect(VALUE (* proc) (VALUE), VALUE data, int *pstate)
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- SAVE_ROOT_JMPBUF(rb_ec_thread_ptr(ec), result = (*proc) (data));
+ SAVE_ROOT_JMPBUF(rb_ec_thread_ptr(ec), result = (*proc) (data));
}
else {
- rb_vm_rewind_cfp(ec, cfp);
+ rb_vm_rewind_cfp(ec, cfp);
}
EC_POP_TAG();
@@ -991,18 +1008,18 @@ rb_ensure(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*e_proc)(VALUE), VALUE dat
ec->ensure_list = &ensure_list;
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- result = (*b_proc) (data1);
+ result = (*b_proc) (data1);
}
EC_POP_TAG();
errinfo = ec->errinfo;
if (!NIL_P(errinfo) && !RB_TYPE_P(errinfo, T_OBJECT)) {
- ec->errinfo = Qnil;
+ ec->errinfo = Qnil;
}
ec->ensure_list=ensure_list.next;
(*ensure_list.entry.e_proc)(ensure_list.entry.data2);
ec->errinfo = errinfo;
if (state)
- EC_JUMP_TAG(ec, state);
+ EC_JUMP_TAG(ec, state);
return result;
}
@@ -1012,10 +1029,10 @@ frame_func_id(const rb_control_frame_t *cfp)
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
if (me) {
- return me->def->original_id;
+ return me->def->original_id;
}
else {
- return 0;
+ return 0;
}
}
@@ -1025,10 +1042,10 @@ frame_called_id(rb_control_frame_t *cfp)
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
if (me) {
- return me->called_id;
+ return me->called_id;
}
else {
- return 0;
+ return 0;
}
}
@@ -1085,8 +1102,8 @@ rb_frame_last_func(void)
ID mid;
while (!(mid = frame_func_id(cfp)) &&
- (cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp),
- !RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)));
+ (cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp),
+ !RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)));
return mid;
}
@@ -1106,7 +1123,7 @@ static VALUE
rb_mod_append_features(VALUE module, VALUE include)
{
if (!CLASS_OR_MODULE_P(include)) {
- Check_Type(include, T_CLASS);
+ Check_Type(include, T_CLASS);
}
rb_include_module(include, module);
@@ -1135,14 +1152,14 @@ rb_mod_include(int argc, VALUE *argv, VALUE module)
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
for (i = 0; i < argc; i++) {
- Check_Type(argv[i], T_MODULE);
+ Check_Type(argv[i], T_MODULE);
if (FL_TEST(argv[i], RMODULE_IS_REFINEMENT)) {
rb_raise(rb_eTypeError, "Cannot include refinement");
}
}
while (argc--) {
- rb_funcall(argv[argc], id_append_features, 1, module);
- rb_funcall(argv[argc], id_included, 1, module);
+ rb_funcall(argv[argc], id_append_features, 1, module);
+ rb_funcall(argv[argc], id_included, 1, module);
}
return module;
}
@@ -1163,7 +1180,7 @@ static VALUE
rb_mod_prepend_features(VALUE module, VALUE prepend)
{
if (!CLASS_OR_MODULE_P(prepend)) {
- Check_Type(prepend, T_CLASS);
+ Check_Type(prepend, T_CLASS);
}
rb_prepend_module(prepend, module);
@@ -1192,14 +1209,14 @@ rb_mod_prepend(int argc, VALUE *argv, VALUE module)
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
for (i = 0; i < argc; i++) {
- Check_Type(argv[i], T_MODULE);
+ Check_Type(argv[i], T_MODULE);
if (FL_TEST(argv[i], RMODULE_IS_REFINEMENT)) {
rb_raise(rb_eTypeError, "Cannot prepend refinement");
}
}
while (argc--) {
- rb_funcall(argv[argc], id_prepend_features, 1, module);
- rb_funcall(argv[argc], id_prepended, 1, module);
+ rb_funcall(argv[argc], id_prepend_features, 1, module);
+ rb_funcall(argv[argc], id_prepended, 1, module);
}
return module;
}
@@ -1208,9 +1225,9 @@ static void
ensure_class_or_module(VALUE obj)
{
if (!RB_TYPE_P(obj, T_CLASS) && !RB_TYPE_P(obj, T_MODULE)) {
- rb_raise(rb_eTypeError,
- "wrong argument type %"PRIsVALUE" (expected Class or Module)",
- rb_obj_class(obj));
+ rb_raise(rb_eTypeError,
+ "wrong argument type %"PRIsVALUE" (expected Class or Module)",
+ rb_obj_class(obj));
}
}
@@ -1227,11 +1244,11 @@ static VALUE
refinement_superclass(VALUE superclass)
{
if (RB_TYPE_P(superclass, T_MODULE)) {
- /* FIXME: Should ancestors of superclass be used here? */
+ /* FIXME: Should ancestors of superclass be used here? */
return rb_include_class_new(RCLASS_ORIGIN(superclass), rb_cBasicObject);
}
else {
- return superclass;
+ return superclass;
}
}
@@ -1246,23 +1263,23 @@ rb_using_refinement(rb_cref_t *cref, VALUE klass, VALUE module)
ensure_class_or_module(klass);
Check_Type(module, T_MODULE);
if (NIL_P(CREF_REFINEMENTS(cref))) {
- CREF_REFINEMENTS_SET(cref, hidden_identity_hash_new());
+ CREF_REFINEMENTS_SET(cref, hidden_identity_hash_new());
}
else {
- if (CREF_OMOD_SHARED(cref)) {
- CREF_REFINEMENTS_SET(cref, rb_hash_dup(CREF_REFINEMENTS(cref)));
- CREF_OMOD_SHARED_UNSET(cref);
- }
- if (!NIL_P(c = rb_hash_lookup(CREF_REFINEMENTS(cref), klass))) {
- superclass = c;
- while (c && RB_TYPE_P(c, T_ICLASS)) {
- if (RBASIC(c)->klass == module) {
- /* already used refinement */
- return;
- }
- c = RCLASS_SUPER(c);
- }
- }
+ if (CREF_OMOD_SHARED(cref)) {
+ CREF_REFINEMENTS_SET(cref, rb_hash_dup(CREF_REFINEMENTS(cref)));
+ CREF_OMOD_SHARED_UNSET(cref);
+ }
+ if (!NIL_P(c = rb_hash_lookup(CREF_REFINEMENTS(cref), klass))) {
+ superclass = c;
+ while (c && RB_TYPE_P(c, T_ICLASS)) {
+ if (RBASIC(c)->klass == module) {
+ /* already used refinement */
+ return;
+ }
+ c = RCLASS_SUPER(c);
+ }
+ }
}
superclass = refinement_superclass(superclass);
c = iclass = rb_include_class_new(module, superclass);
@@ -1272,7 +1289,7 @@ rb_using_refinement(rb_cref_t *cref, VALUE klass, VALUE module)
module = RCLASS_SUPER(module);
while (module && module != klass) {
- c = RCLASS_SET_SUPER(c, rb_include_class_new(module, RCLASS_SUPER(c)));
+ c = RCLASS_SET_SUPER(c, rb_include_class_new(module, RCLASS_SUPER(c)));
RB_OBJ_WRITE(c, &RCLASS_REFINED_CLASS(c), klass);
module = RCLASS_SUPER(module);
}
@@ -1296,21 +1313,21 @@ using_module_recursive(const rb_cref_t *cref, VALUE klass)
super = RCLASS_SUPER(klass);
if (super) {
- using_module_recursive(cref, super);
+ using_module_recursive(cref, super);
}
switch (BUILTIN_TYPE(klass)) {
case T_MODULE:
- module = klass;
- break;
+ module = klass;
+ break;
case T_ICLASS:
- module = RBASIC(klass)->klass;
- break;
+ module = RBASIC(klass)->klass;
+ break;
default:
- rb_raise(rb_eTypeError, "wrong argument type %s (expected Module)",
- rb_obj_classname(klass));
- break;
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Module)",
+ rb_obj_classname(klass));
+ break;
}
CONST_ID(id_refinements, "__refinements__");
refinements = rb_attr_get(module, id_refinements);
@@ -1346,28 +1363,28 @@ rb_refinement_module_get_refined_class(VALUE module)
static void
add_activated_refinement(VALUE activated_refinements,
- VALUE klass, VALUE refinement)
+ VALUE klass, VALUE refinement)
{
VALUE iclass, c, superclass = klass;
if (!NIL_P(c = rb_hash_lookup(activated_refinements, klass))) {
- superclass = c;
- while (c && RB_TYPE_P(c, T_ICLASS)) {
- if (RBASIC(c)->klass == refinement) {
- /* already used refinement */
- return;
- }
- c = RCLASS_SUPER(c);
- }
+ superclass = c;
+ while (c && RB_TYPE_P(c, T_ICLASS)) {
+ if (RBASIC(c)->klass == refinement) {
+ /* already used refinement */
+ return;
+ }
+ c = RCLASS_SUPER(c);
+ }
}
superclass = refinement_superclass(superclass);
c = iclass = rb_include_class_new(refinement, superclass);
RB_OBJ_WRITE(c, &RCLASS_REFINED_CLASS(c), klass);
refinement = RCLASS_SUPER(refinement);
while (refinement && refinement != klass) {
- c = RCLASS_SET_SUPER(c, rb_include_class_new(refinement, RCLASS_SUPER(c)));
+ c = RCLASS_SET_SUPER(c, rb_include_class_new(refinement, RCLASS_SUPER(c)));
RB_OBJ_WRITE(c, &RCLASS_REFINED_CLASS(c), klass);
- refinement = RCLASS_SUPER(refinement);
+ refinement = RCLASS_SUPER(refinement);
}
rb_hash_aset(activated_refinements, klass, iclass);
}
@@ -1392,39 +1409,39 @@ rb_mod_refine(VALUE module, VALUE klass)
VALUE block_handler = rb_vm_frame_block_handler(th->ec->cfp);
if (block_handler == VM_BLOCK_HANDLER_NONE) {
- rb_raise(rb_eArgError, "no block given");
+ rb_raise(rb_eArgError, "no block given");
}
if (vm_block_handler_type(block_handler) != block_handler_type_iseq) {
- rb_raise(rb_eArgError, "can't pass a Proc as a block to Module#refine");
+ rb_raise(rb_eArgError, "can't pass a Proc as a block to Module#refine");
}
ensure_class_or_module(klass);
CONST_ID(id_refinements, "__refinements__");
refinements = rb_attr_get(module, id_refinements);
if (NIL_P(refinements)) {
- refinements = hidden_identity_hash_new();
- rb_ivar_set(module, id_refinements, refinements);
+ refinements = hidden_identity_hash_new();
+ rb_ivar_set(module, id_refinements, refinements);
}
CONST_ID(id_activated_refinements, "__activated_refinements__");
activated_refinements = rb_attr_get(module, id_activated_refinements);
if (NIL_P(activated_refinements)) {
- activated_refinements = hidden_identity_hash_new();
- rb_ivar_set(module, id_activated_refinements,
- activated_refinements);
+ activated_refinements = hidden_identity_hash_new();
+ rb_ivar_set(module, id_activated_refinements,
+ activated_refinements);
}
refinement = rb_hash_lookup(refinements, klass);
if (NIL_P(refinement)) {
- VALUE superclass = refinement_superclass(klass);
- refinement = rb_refinement_new();
- RCLASS_SET_SUPER(refinement, superclass);
+ VALUE superclass = refinement_superclass(klass);
+ refinement = rb_refinement_new();
+ RCLASS_SET_SUPER(refinement, superclass);
RUBY_ASSERT(BUILTIN_TYPE(refinement) == T_MODULE);
- FL_SET(refinement, RMODULE_IS_REFINEMENT);
- CONST_ID(id_refined_class, "__refined_class__");
- rb_ivar_set(refinement, id_refined_class, klass);
- CONST_ID(id_defined_at, "__defined_at__");
- rb_ivar_set(refinement, id_defined_at, module);
- rb_hash_aset(refinements, klass, refinement);
- add_activated_refinement(activated_refinements, klass, refinement);
+ FL_SET(refinement, RMODULE_IS_REFINEMENT);
+ CONST_ID(id_refined_class, "__refined_class__");
+ rb_ivar_set(refinement, id_refined_class, klass);
+ CONST_ID(id_defined_at, "__defined_at__");
+ rb_ivar_set(refinement, id_defined_at, module);
+ rb_hash_aset(refinements, klass, refinement);
+ add_activated_refinement(activated_refinements, klass, refinement);
}
rb_yield_refine_block(refinement, activated_refinements);
return refinement;
@@ -1436,7 +1453,7 @@ ignored_block(VALUE module, const char *klass)
const char *anon = "";
Check_Type(module, T_MODULE);
if (!RTEST(rb_search_class_path(module))) {
- anon = ", maybe for Module.new";
+ anon = ", maybe for Module.new";
}
rb_warn("%s""using doesn't call the given block""%s.", klass, anon);
}
@@ -1455,14 +1472,14 @@ mod_using(VALUE self, VALUE module)
rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
if (prev_frame_func()) {
- rb_raise(rb_eRuntimeError,
- "Module#using is not permitted in methods");
+ rb_raise(rb_eRuntimeError,
+ "Module#using is not permitted in methods");
}
if (prev_cfp && prev_cfp->self != self) {
- rb_raise(rb_eRuntimeError, "Module#using is not called on self");
+ rb_raise(rb_eRuntimeError, "Module#using is not called on self");
}
if (rb_block_given_p()) {
- ignored_block(module, "Module#");
+ ignored_block(module, "Module#");
}
rb_using_module(rb_vm_cref_replace_with_duplicated_cref(), module);
return self;
@@ -1509,8 +1526,8 @@ used_modules_i(VALUE _, VALUE mod, VALUE ary)
ID id_defined_at;
CONST_ID(id_defined_at, "__defined_at__");
while (BUILTIN_TYPE(rb_class_of(mod)) == T_MODULE && FL_TEST(rb_class_of(mod), RMODULE_IS_REFINEMENT)) {
- rb_ary_push(ary, rb_attr_get(rb_class_of(mod), id_defined_at));
- mod = RCLASS_SUPER(mod);
+ rb_ary_push(ary, rb_attr_get(rb_class_of(mod), id_defined_at));
+ mod = RCLASS_SUPER(mod);
}
return ST_CONTINUE;
}
@@ -1547,10 +1564,10 @@ rb_mod_s_used_modules(VALUE _)
VALUE ary = rb_ary_new();
while (cref) {
- if (!NIL_P(CREF_REFINEMENTS(cref))) {
- rb_hash_foreach(CREF_REFINEMENTS(cref), used_modules_i, ary);
- }
- cref = CREF_NEXT(cref);
+ if (!NIL_P(CREF_REFINEMENTS(cref))) {
+ rb_hash_foreach(CREF_REFINEMENTS(cref), used_modules_i, ary);
+ }
+ cref = CREF_NEXT(cref);
}
return rb_funcall(ary, rb_intern("uniq"), 0);
@@ -1561,7 +1578,7 @@ used_refinements_i(VALUE _, VALUE mod, VALUE ary)
{
while (BUILTIN_TYPE(rb_class_of(mod)) == T_MODULE && FL_TEST(rb_class_of(mod), RMODULE_IS_REFINEMENT)) {
rb_ary_push(ary, rb_class_of(mod));
- mod = RCLASS_SUPER(mod);
+ mod = RCLASS_SUPER(mod);
}
return ST_CONTINUE;
}
@@ -1598,10 +1615,10 @@ rb_mod_s_used_refinements(VALUE _)
VALUE ary = rb_ary_new();
while (cref) {
- if (!NIL_P(CREF_REFINEMENTS(cref))) {
- rb_hash_foreach(CREF_REFINEMENTS(cref), used_refinements_i, ary);
- }
- cref = CREF_NEXT(cref);
+ if (!NIL_P(CREF_REFINEMENTS(cref))) {
+ rb_hash_foreach(CREF_REFINEMENTS(cref), used_refinements_i, ary);
+ }
+ cref = CREF_NEXT(cref);
}
return ary;
@@ -1748,18 +1765,28 @@ rb_obj_extend(int argc, VALUE *argv, VALUE obj)
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
for (i = 0; i < argc; i++) {
- Check_Type(argv[i], T_MODULE);
+ Check_Type(argv[i], T_MODULE);
if (FL_TEST(argv[i], RMODULE_IS_REFINEMENT)) {
rb_raise(rb_eTypeError, "Cannot extend object with refinement");
}
}
while (argc--) {
- rb_funcall(argv[argc], id_extend_object, 1, obj);
- rb_funcall(argv[argc], id_extended, 1, obj);
+ rb_funcall(argv[argc], id_extend_object, 1, obj);
+ rb_funcall(argv[argc], id_extended, 1, obj);
}
return obj;
}
+VALUE
+rb_top_main_class(const char *method)
+{
+ VALUE klass = GET_THREAD()->top_wrapper;
+
+ if (!klass) return rb_cObject;
+ rb_warning("main.%s in the wrapped load is effective only in wrapper module", method);
+ return klass;
+}
+
/*
* call-seq:
* include(module, ...) -> self
@@ -1772,13 +1799,7 @@ rb_obj_extend(int argc, VALUE *argv, VALUE obj)
static VALUE
top_include(int argc, VALUE *argv, VALUE self)
{
- rb_thread_t *th = GET_THREAD();
-
- if (th->top_wrapper) {
- rb_warning("main.include in the wrapped load is effective only in wrapper module");
- return rb_mod_include(argc, argv, th->top_wrapper);
- }
- return rb_mod_include(argc, argv, rb_cObject);
+ return rb_mod_include(argc, argv, rb_top_main_class("include"));
}
/*
@@ -1792,14 +1813,16 @@ top_include(int argc, VALUE *argv, VALUE self)
static VALUE
top_using(VALUE self, VALUE module)
{
- const rb_cref_t *cref = rb_vm_cref();
+ const rb_cref_t *cref = CREF_NEXT(rb_vm_cref());;
rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
+ rb_thread_t *th = GET_THREAD();
- if (CREF_NEXT(cref) || (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) {
- rb_raise(rb_eRuntimeError, "main.using is permitted only at toplevel");
+ if ((th->top_wrapper ? CREF_NEXT(cref) : cref) ||
+ (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) {
+ rb_raise(rb_eRuntimeError, "main.using is permitted only at toplevel");
}
if (rb_block_given_p()) {
- ignored_block(module, "main.");
+ ignored_block(module, "main.");
}
rb_using_module(rb_vm_cref_replace_with_duplicated_cref(), module);
return self;
@@ -1812,17 +1835,17 @@ errinfo_place(const rb_execution_context_t *ec)
const rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) {
- if (VM_FRAME_RUBYFRAME_P(cfp)) {
+ if (VM_FRAME_RUBYFRAME_P(cfp)) {
if (ISEQ_BODY(cfp->iseq)->type == ISEQ_TYPE_RESCUE) {
- return &cfp->ep[VM_ENV_INDEX_LAST_LVAR];
- }
+ return &cfp->ep[VM_ENV_INDEX_LAST_LVAR];
+ }
else if (ISEQ_BODY(cfp->iseq)->type == ISEQ_TYPE_ENSURE &&
- !THROW_DATA_P(cfp->ep[VM_ENV_INDEX_LAST_LVAR]) &&
- !FIXNUM_P(cfp->ep[VM_ENV_INDEX_LAST_LVAR])) {
- return &cfp->ep[VM_ENV_INDEX_LAST_LVAR];
- }
- }
- cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+ !THROW_DATA_P(cfp->ep[VM_ENV_INDEX_LAST_LVAR]) &&
+ !FIXNUM_P(cfp->ep[VM_ENV_INDEX_LAST_LVAR])) {
+ return &cfp->ep[VM_ENV_INDEX_LAST_LVAR];
+ }
+ }
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
return 0;
}
@@ -1832,10 +1855,10 @@ rb_ec_get_errinfo(const rb_execution_context_t *ec)
{
const VALUE *ptr = errinfo_place(ec);
if (ptr) {
- return *ptr;
+ return *ptr;
}
else {
- return ec->errinfo;
+ return ec->errinfo;
}
}
@@ -1861,7 +1884,7 @@ void
rb_set_errinfo(VALUE err)
{
if (!NIL_P(err) && !rb_obj_is_kind_of(err, rb_eException)) {
- rb_raise(rb_eTypeError, "assigning non-exception to $!");
+ rb_raise(rb_eTypeError, "assigning non-exception to $!");
}
GET_EC()->errinfo = err;
}
@@ -1871,10 +1894,10 @@ errat_getter(ID id, VALUE *_)
{
VALUE err = get_errinfo();
if (!NIL_P(err)) {
- return rb_get_backtrace(err);
+ return rb_get_backtrace(err);
}
else {
- return Qnil;
+ return Qnil;
}
}
@@ -1883,7 +1906,7 @@ errat_setter(VALUE val, ID id, VALUE *var)
{
VALUE err = get_errinfo();
if (NIL_P(err)) {
- rb_raise(rb_eArgError, "$! not set");
+ rb_raise(rb_eArgError, "$! not set");
}
set_backtrace(err, val);
}
@@ -1904,10 +1927,10 @@ rb_f_method_name(VALUE _)
ID fname = prev_frame_func(); /* need *method* ID */
if (fname) {
- return ID2SYM(fname);
+ return ID2SYM(fname);
}
else {
- return Qnil;
+ return Qnil;
}
}
@@ -1926,10 +1949,10 @@ rb_f_callee_name(VALUE _)
ID fname = prev_frame_callee(); /* need *callee* ID */
if (fname) {
- return ID2SYM(fname);
+ return ID2SYM(fname);
}
else {
- return Qnil;
+ return Qnil;
}
}
@@ -1948,7 +1971,7 @@ f_current_dirname(VALUE _)
{
VALUE base = rb_current_realfilepath();
if (NIL_P(base)) {
- return Qnil;
+ return Qnil;
}
base = rb_file_dirname(base);
return base;
@@ -2045,7 +2068,7 @@ Init_eval(void)
rb_define_private_method(rb_cModule, "using", mod_using, 1);
rb_define_method(rb_cModule, "refinements", mod_refinements, 0);
rb_define_singleton_method(rb_cModule, "used_modules",
- rb_mod_s_used_modules, 0);
+ rb_mod_s_used_modules, 0);
rb_define_singleton_method(rb_cModule, "used_refinements",
rb_mod_s_used_refinements, 0);
rb_undef_method(rb_cClass, "refine");
@@ -2064,9 +2087,9 @@ Init_eval(void)
rb_define_singleton_method(rb_cModule, "constants", rb_mod_s_constants, -1);
rb_define_private_method(rb_singleton_class(rb_vm_top_self()),
- "include", top_include, -1);
+ "include", top_include, -1);
rb_define_private_method(rb_singleton_class(rb_vm_top_self()),
- "using", top_using, 1);
+ "using", top_using, 1);
rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1);
diff --git a/eval_error.c b/eval_error.c
index abbb708bd5..9806683000 100644
--- a/eval_error.c
+++ b/eval_error.c
@@ -10,8 +10,8 @@
#ifdef HAVE_BUILTIN___BUILTIN_CONSTANT_P
#define warn_print(x) RB_GNUC_EXTENSION_BLOCK( \
(__builtin_constant_p(x)) ? \
- rb_write_error2((x), (long)strlen(x)) : \
- rb_write_error(x) \
+ rb_write_error2((x), (long)strlen(x)) : \
+ rb_write_error(x) \
)
#else
#define warn_print(x) rb_write_error(x)
@@ -29,7 +29,7 @@ error_pos(const VALUE str)
{
VALUE pos = error_pos_str();
if (!NIL_P(pos)) {
- write_warn_str(str, pos);
+ write_warn_str(str, pos);
}
}
@@ -40,18 +40,18 @@ error_pos_str(void)
VALUE sourcefile = rb_source_location(&sourceline);
if (!NIL_P(sourcefile)) {
- ID caller_name;
- if (sourceline == 0) {
- return rb_sprintf("%"PRIsVALUE": ", sourcefile);
- }
- else if ((caller_name = rb_frame_callee()) != 0) {
- return rb_sprintf("%"PRIsVALUE":%d:in `%"PRIsVALUE"': ",
- sourcefile, sourceline,
- rb_id2str(caller_name));
- }
- else {
- return rb_sprintf("%"PRIsVALUE":%d: ", sourcefile, sourceline);
- }
+ ID caller_name;
+ if (sourceline == 0) {
+ return rb_sprintf("%"PRIsVALUE": ", sourcefile);
+ }
+ else if ((caller_name = rb_frame_callee()) != 0) {
+ return rb_sprintf("%"PRIsVALUE":%d:in `%"PRIsVALUE"': ",
+ sourcefile, sourceline,
+ rb_id2str(caller_name));
+ }
+ else {
+ return rb_sprintf("%"PRIsVALUE":%d: ", sourcefile, sourceline);
+ }
}
return Qnil;
}
@@ -62,23 +62,17 @@ set_backtrace(VALUE info, VALUE bt)
ID set_backtrace = rb_intern("set_backtrace");
if (rb_backtrace_p(bt)) {
- if (rb_method_basic_definition_p(CLASS_OF(info), set_backtrace)) {
- rb_exc_set_backtrace(info, bt);
- return;
- }
- else {
- bt = rb_backtrace_to_str_ary(bt);
- }
+ if (rb_method_basic_definition_p(CLASS_OF(info), set_backtrace)) {
+ rb_exc_set_backtrace(info, bt);
+ return;
+ }
+ else {
+ bt = rb_backtrace_to_str_ary(bt);
+ }
}
rb_check_funcall(info, set_backtrace, 1, &bt);
}
-static void
-error_print(rb_execution_context_t *ec)
-{
- rb_ec_error_print(ec, ec->errinfo);
-}
-
#define CSI_BEGIN "\033["
#define CSI_SGR "m"
@@ -106,24 +100,24 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VA
}
if (eclass == rb_eRuntimeError && elen == 0) {
- if (highlight) write_warn(str, underline);
- write_warn(str, "unhandled exception");
- if (highlight) write_warn(str, reset);
- write_warn2(str, "\n", 1);
+ if (highlight) write_warn(str, underline);
+ write_warn(str, "unhandled exception");
+ if (highlight) write_warn(str, reset);
+ write_warn2(str, "\n", 1);
}
else {
- VALUE epath;
-
- epath = rb_class_name(eclass);
- if (elen == 0) {
- if (highlight) write_warn(str, underline);
- write_warn_str(str, epath);
- if (highlight) write_warn(str, reset);
- write_warn(str, "\n");
- }
- else {
- write_warn_str(str, emesg);
- write_warn(str, "\n");
+ VALUE epath;
+
+ epath = rb_class_name(eclass);
+ if (elen == 0) {
+ if (highlight) write_warn(str, underline);
+ write_warn_str(str, epath);
+ if (highlight) write_warn(str, reset);
+ write_warn(str, "\n");
+ }
+ else {
+ write_warn_str(str, emesg);
+ write_warn(str, "\n");
}
}
}
@@ -141,73 +135,73 @@ rb_decorate_message(const VALUE eclass, const VALUE emesg, int highlight)
elen = RSTRING_LEN(emesg);
}
if (eclass == rb_eRuntimeError && elen == 0) {
- if (highlight) write_warn(str, underline);
- write_warn(str, "unhandled exception");
- if (highlight) write_warn(str, reset);
+ if (highlight) write_warn(str, underline);
+ write_warn(str, "unhandled exception");
+ if (highlight) write_warn(str, reset);
}
else {
- VALUE epath;
-
- epath = rb_class_name(eclass);
- if (elen == 0) {
- if (highlight) write_warn(str, underline);
- write_warn_str(str, epath);
- if (highlight) write_warn(str, reset);
- }
- else {
+ VALUE epath;
+
+ epath = rb_class_name(eclass);
+ if (elen == 0) {
+ if (highlight) write_warn(str, underline);
+ write_warn_str(str, epath);
+ if (highlight) write_warn(str, reset);
+ }
+ else {
/* emesg is a String instance */
- const char *tail = 0;
+ const char *tail = 0;
if (highlight) write_warn(str, bold);
- if (RSTRING_PTR(epath)[0] == '#')
- epath = 0;
- if ((tail = memchr(einfo, '\n', elen)) != 0) {
+ if (RSTRING_PTR(epath)[0] == '#')
+ epath = 0;
+ if ((tail = memchr(einfo, '\n', elen)) != 0) {
write_warn2(str, einfo, tail - einfo);
- tail++; /* skip newline */
- }
- else {
+ tail++; /* skip newline */
+ }
+ else {
write_warn_str(str, emesg);
- }
- if (epath) {
- write_warn(str, " (");
- if (highlight) write_warn(str, underline);
+ }
+ if (epath) {
+ write_warn(str, " (");
+ if (highlight) write_warn(str, underline);
write_warn_str(str, epath);
- if (highlight) {
- write_warn(str, reset);
- write_warn(str, bold);
- }
- write_warn2(str, ")", 1);
- if (highlight) write_warn(str, reset);
- }
- if (tail && einfo+elen > tail) {
- if (!highlight) {
+ if (highlight) {
+ write_warn(str, reset);
+ write_warn(str, bold);
+ }
+ write_warn2(str, ")", 1);
+ if (highlight) write_warn(str, reset);
+ }
+ if (tail && einfo+elen > tail) {
+ if (!highlight) {
write_warn2(str, "\n", 1);
write_warn2(str, tail, einfo+elen-tail);
- }
- else {
- elen -= tail - einfo;
- einfo = tail;
+ }
+ else {
+ elen -= tail - einfo;
+ einfo = tail;
write_warn2(str, "\n", 1);
- while (elen > 0) {
- tail = memchr(einfo, '\n', elen);
- if (!tail || tail > einfo) {
- write_warn(str, bold);
+ while (elen > 0) {
+ tail = memchr(einfo, '\n', elen);
+ if (!tail || tail > einfo) {
+ write_warn(str, bold);
write_warn2(str, einfo, tail ? tail-einfo : elen);
- write_warn(str, reset);
- if (!tail) {
- break;
- }
- }
- elen -= tail - einfo;
- einfo = tail;
- do ++tail; while (tail < einfo+elen && *tail == '\n');
+ write_warn(str, reset);
+ if (!tail) {
+ break;
+ }
+ }
+ elen -= tail - einfo;
+ einfo = tail;
+ do ++tail; while (tail < einfo+elen && *tail == '\n');
write_warn2(str, einfo, tail-einfo);
- elen -= tail - einfo;
- einfo = tail;
- }
- }
- }
- }
+ elen -= tail - einfo;
+ einfo = tail;
+ }
+ }
+ }
+ }
}
return str;
@@ -217,13 +211,13 @@ static void
print_backtrace(const VALUE eclass, const VALUE errat, const VALUE str, int reverse, long backtrace_limit)
{
if (!NIL_P(errat)) {
- long i;
- long len = RARRAY_LEN(errat);
- const int threshold = 1000000000;
+ long i;
+ long len = RARRAY_LEN(errat);
+ const int threshold = 1000000000;
int width = (len <= 1) ? INT_MIN : ((int)log10((double)(len > threshold ?
- ((len - 1) / threshold) :
- len - 1)) +
- (len < threshold ? 0 : 9) + 1);
+ ((len - 1) / threshold) :
+ len - 1)) +
+ (len < threshold ? 0 : 9) + 1);
long skip_start = -1, skip_len = 0;
@@ -244,19 +238,19 @@ print_backtrace(const VALUE eclass, const VALUE errat, const VALUE str, int reve
skip_len = len - skip_start;
}
- for (i = 1; i < len; i++) {
- if (i == skip_start) {
- write_warn_str(str, rb_sprintf("\t ... %ld levels...\n", skip_len));
- i += skip_len;
+ for (i = 1; i < len; i++) {
+ if (i == skip_start) {
+ write_warn_str(str, rb_sprintf("\t ... %ld levels...\n", skip_len));
+ i += skip_len;
if (i >= len) break;
- }
- VALUE line = RARRAY_AREF(errat, reverse ? len - i : i);
- if (RB_TYPE_P(line, T_STRING)) {
- VALUE bt = rb_str_new_cstr("\t");
- if (reverse) rb_str_catf(bt, "%*ld: ", width, len - i);
- write_warn_str(str, rb_str_catf(bt, "from %"PRIsVALUE"\n", line));
- }
- }
+ }
+ VALUE line = RARRAY_AREF(errat, reverse ? len - i : i);
+ if (RB_TYPE_P(line, T_STRING)) {
+ VALUE bt = rb_str_new_cstr("\t");
+ if (reverse) rb_str_catf(bt, "%*ld: ", width, len - i);
+ write_warn_str(str, rb_str_catf(bt, "from %"PRIsVALUE"\n", line));
+ }
+ }
}
}
@@ -297,6 +291,17 @@ show_cause(VALUE errinfo, VALUE str, VALUE opt, VALUE highlight, VALUE reverse,
}
void
+rb_exc_check_circular_cause(VALUE exc)
+{
+ VALUE cause = exc, shown_causes = 0;
+ do {
+ if (shown_cause_p(cause, &shown_causes)) {
+ rb_raise(rb_eArgError, "circular causes");
+ }
+ } while (!NIL_P(cause = rb_attr_get(cause, id_cause)));
+}
+
+void
rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VALUE highlight, VALUE reverse)
{
volatile VALUE eclass;
@@ -304,68 +309,68 @@ rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VA
long backtrace_limit = rb_backtrace_length_limit;
if (NIL_P(errinfo))
- return;
+ return;
- if (errat == Qundef) {
- errat = Qnil;
+ if (UNDEF_P(errat)) {
+ errat = Qnil;
}
eclass = CLASS_OF(errinfo);
if (reverse) {
- static const char traceback[] = "Traceback "
- "(most recent call last):\n";
- const int bold_part = rb_strlen_lit("Traceback");
- char buff[sizeof(traceback)+sizeof(bold)+sizeof(reset)-2], *p = buff;
- const char *msg = traceback;
- long len = sizeof(traceback) - 1;
- if (RTEST(highlight)) {
+ static const char traceback[] = "Traceback "
+ "(most recent call last):\n";
+ const int bold_part = rb_strlen_lit("Traceback");
+ char buff[sizeof(traceback)+sizeof(bold)+sizeof(reset)-2], *p = buff;
+ const char *msg = traceback;
+ long len = sizeof(traceback) - 1;
+ if (RTEST(highlight)) {
#define APPEND(s, l) (memcpy(p, s, l), p += (l))
- APPEND(bold, sizeof(bold)-1);
- APPEND(traceback, bold_part);
- APPEND(reset, sizeof(reset)-1);
- APPEND(traceback + bold_part, sizeof(traceback)-bold_part-1);
+ APPEND(bold, sizeof(bold)-1);
+ APPEND(traceback, bold_part);
+ APPEND(reset, sizeof(reset)-1);
+ APPEND(traceback + bold_part, sizeof(traceback)-bold_part-1);
#undef APPEND
- len = p - (msg = buff);
- }
- write_warn2(str, msg, len);
+ len = p - (msg = buff);
+ }
+ write_warn2(str, msg, len);
show_cause(errinfo, str, opt, highlight, reverse, backtrace_limit, &shown_causes);
- print_backtrace(eclass, errat, str, TRUE, backtrace_limit);
- print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
+ print_backtrace(eclass, errat, str, TRUE, backtrace_limit);
+ print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
}
else {
- print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
- print_backtrace(eclass, errat, str, FALSE, backtrace_limit);
+ print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
+ print_backtrace(eclass, errat, str, FALSE, backtrace_limit);
show_cause(errinfo, str, opt, highlight, reverse, backtrace_limit, &shown_causes);
}
}
-void
-rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo)
+static void
+rb_ec_error_print_detailed(rb_execution_context_t *const ec, const VALUE errinfo, const VALUE str, VALUE emesg0)
{
volatile uint8_t raised_flag = ec->raised_flag;
volatile VALUE errat = Qundef;
- volatile VALUE emesg = Qundef;
volatile bool written = false;
+ volatile VALUE emesg = emesg0;
VALUE opt = rb_hash_new();
VALUE highlight = rb_stderr_tty_p() ? Qtrue : Qfalse;
rb_hash_aset(opt, ID2SYM(rb_intern_const("highlight")), highlight);
if (NIL_P(errinfo))
- return;
+ return;
rb_ec_raised_clear(ec);
EC_PUSH_TAG(ec);
if (EC_EXEC_TAG() == TAG_NONE) {
- errat = rb_get_backtrace(errinfo);
+ errat = rb_get_backtrace(errinfo);
}
- if (emesg == Qundef) {
- emesg = Qnil;
- emesg = rb_get_detailed_message(errinfo, opt);
+ if (UNDEF_P(emesg)) {
+ emesg = Qnil;
+ emesg = rb_get_detailed_message(errinfo, opt);
}
if (!written) {
written = true;
- rb_error_write(errinfo, emesg, errat, Qnil, opt, highlight, Qfalse);
+ rb_error_write(errinfo, emesg, errat, str, opt, highlight, Qfalse);
}
EC_POP_TAG();
@@ -373,11 +378,17 @@ rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo)
rb_ec_raised_set(ec, raised_flag);
}
+void
+rb_ec_error_print(rb_execution_context_t *volatile ec, volatile VALUE errinfo)
+{
+ rb_ec_error_print_detailed(ec, errinfo, Qnil, Qundef);
+}
+
#define undef_mesg_for(v, k) rb_fstring_lit("undefined"v" method `%1$s' for "k" `%2$s'")
#define undef_mesg(v) ( \
- is_mod ? \
- undef_mesg_for(v, "module") : \
- undef_mesg_for(v, "class"))
+ is_mod ? \
+ undef_mesg_for(v, "module") : \
+ undef_mesg_for(v, "class"))
void
rb_print_undef(VALUE klass, ID id, rb_method_visibility_t visi)
@@ -403,9 +414,9 @@ rb_print_undef_str(VALUE klass, VALUE name)
#define inaccessible_mesg_for(v, k) rb_fstring_lit("method `%1$s' for "k" `%2$s' is "v)
#define inaccessible_mesg(v) ( \
- is_mod ? \
- inaccessible_mesg_for(v, "module") : \
- inaccessible_mesg_for(v, "class"))
+ is_mod ? \
+ inaccessible_mesg_for(v, "module") : \
+ inaccessible_mesg_for(v, "class"))
void
rb_print_inaccessible(VALUE klass, ID id, rb_method_visibility_t visi)
@@ -429,70 +440,109 @@ sysexit_status(VALUE err)
return NUM2INT(st);
}
+enum {
+ EXITING_WITH_MESSAGE = 1,
+ EXITING_WITH_STATUS = 2,
+ EXITING_WITH_SIGNAL = 4
+};
+static int
+exiting_split(VALUE errinfo, volatile int *exitcode, volatile int *sigstatus)
+{
+ int ex = EXIT_SUCCESS;
+ VALUE signo;
+ int sig = 0;
+ int result = 0;
+
+ if (NIL_P(errinfo)) return 0;
+
+ if (THROW_DATA_P(errinfo)) {
+ int throw_state = ((const struct vm_throw_data *)errinfo)->throw_state;
+ ex = throw_state & VM_THROW_STATE_MASK;
+ result |= EXITING_WITH_STATUS;
+ }
+ else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
+ ex = sysexit_status(errinfo);
+ result |= EXITING_WITH_STATUS;
+ }
+ else if (rb_obj_is_kind_of(errinfo, rb_eSignal)) {
+ signo = rb_ivar_get(errinfo, id_signo);
+ sig = FIX2INT(signo);
+ result |= EXITING_WITH_SIGNAL;
+ /* no message when exiting by signal */
+ if (signo == INT2FIX(SIGSEGV) || !rb_obj_is_instance_of(errinfo, rb_eSignal))
+ /* except for SEGV and subclasses */
+ result |= EXITING_WITH_MESSAGE;
+ }
+ else if (rb_obj_is_kind_of(errinfo, rb_eSystemCallError) &&
+ FIXNUM_P(signo = rb_attr_get(errinfo, id_signo))) {
+ sig = FIX2INT(signo);
+ result |= EXITING_WITH_SIGNAL;
+ /* no message when exiting by error to be mapped to signal */
+ }
+ else {
+ ex = EXIT_FAILURE;
+ result |= EXITING_WITH_STATUS | EXITING_WITH_MESSAGE;
+ }
+
+ if (exitcode && (result & EXITING_WITH_STATUS))
+ *exitcode = ex;
+ if (sigstatus && (result & EXITING_WITH_SIGNAL))
+ *sigstatus = sig;
+
+ return result;
+}
+
#define unknown_longjmp_status(status) \
rb_bug("Unknown longjmp status %d", status)
static int
-error_handle(rb_execution_context_t *ec, int ex)
+error_handle(rb_execution_context_t *ec, VALUE errinfo, enum ruby_tag_type ex)
{
int status = EXIT_FAILURE;
if (rb_ec_set_raised(ec))
- return EXIT_FAILURE;
+ return EXIT_FAILURE;
switch (ex & TAG_MASK) {
case 0:
- status = EXIT_SUCCESS;
- break;
+ status = EXIT_SUCCESS;
+ break;
case TAG_RETURN:
- error_pos(Qnil);
- warn_print("unexpected return\n");
- break;
+ error_pos(Qnil);
+ warn_print("unexpected return\n");
+ break;
case TAG_NEXT:
- error_pos(Qnil);
- warn_print("unexpected next\n");
- break;
+ error_pos(Qnil);
+ warn_print("unexpected next\n");
+ break;
case TAG_BREAK:
- error_pos(Qnil);
- warn_print("unexpected break\n");
- break;
+ error_pos(Qnil);
+ warn_print("unexpected break\n");
+ break;
case TAG_REDO:
- error_pos(Qnil);
- warn_print("unexpected redo\n");
- break;
+ error_pos(Qnil);
+ warn_print("unexpected redo\n");
+ break;
case TAG_RETRY:
- error_pos(Qnil);
- warn_print("retry outside of rescue clause\n");
- break;
+ error_pos(Qnil);
+ warn_print("retry outside of rescue clause\n");
+ break;
case TAG_THROW:
- /* TODO: fix me */
- error_pos(Qnil);
- warn_print("unexpected throw\n");
- break;
- case TAG_RAISE: {
- VALUE errinfo = ec->errinfo;
- if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
- status = sysexit_status(errinfo);
- }
- else if (rb_obj_is_instance_of(errinfo, rb_eSignal) &&
- rb_ivar_get(errinfo, id_signo) != INT2FIX(SIGSEGV)) {
- /* no message when exiting by signal */
- }
- else if (rb_obj_is_kind_of(errinfo, rb_eSystemCallError) &&
- FIXNUM_P(rb_attr_get(errinfo, id_signo))) {
- /* no message when exiting by error to be mapped to signal */
+ /* TODO: fix me */
+ error_pos(Qnil);
+ warn_print("unexpected throw\n");
+ break;
+ case TAG_RAISE:
+ if (!(exiting_split(errinfo, &status, NULL) & EXITING_WITH_MESSAGE)) {
+ break;
}
- else {
- rb_ec_error_print(ec, errinfo);
- }
- break;
- }
+ /* fallthrough */
case TAG_FATAL:
- error_print(ec);
- break;
+ rb_ec_error_print(ec, errinfo);
+ break;
default:
- unknown_longjmp_status(ex);
- break;
+ unknown_longjmp_status(ex);
+ break;
}
rb_ec_reset_raised(ec);
return status;
diff --git a/eval_intern.h b/eval_intern.h
index 0efbcb0441..6cbaa51361 100644
--- a/eval_intern.h
+++ b/eval_intern.h
@@ -329,9 +329,9 @@ static inline void
translit_char(char *p, int from, int to)
{
while (*p) {
- if ((unsigned char)*p == from)
- *p = to;
- p = CharNext(p);
+ if ((unsigned char)*p == from)
+ *p = to;
+ p = CharNext(p);
}
}
#endif
diff --git a/eval_jump.c b/eval_jump.c
index 2ea73b0da3..e8e74f4e70 100644
--- a/eval_jump.c
+++ b/eval_jump.c
@@ -40,7 +40,7 @@ rb_f_at_exit(VALUE _)
VALUE proc;
if (!rb_block_given_p()) {
- rb_raise(rb_eArgError, "called without a block");
+ rb_raise(rb_eArgError, "called without a block");
}
proc = rb_block_proc();
rb_set_end_proc(rb_call_end_proc, proc);
@@ -63,10 +63,10 @@ rb_set_end_proc(void (*func)(VALUE), VALUE data)
rb_thread_t *th = GET_THREAD();
if (th->top_wrapper) {
- list = &ephemeral_end_procs;
+ list = &ephemeral_end_procs;
}
else {
- list = &end_procs;
+ list = &end_procs;
}
link->next = *list;
link->func = func;
@@ -81,13 +81,13 @@ rb_mark_end_proc(void)
link = end_procs;
while (link) {
- rb_gc_mark(link->data);
- link = link->next;
+ rb_gc_mark(link->data);
+ link = link->next;
}
link = ephemeral_end_procs;
while (link) {
- rb_gc_mark(link->data);
- link = link->next;
+ rb_gc_mark(link->data);
+ link = link->next;
}
}
@@ -99,11 +99,11 @@ exec_end_procs_chain(struct end_proc_data *volatile *procs, VALUE *errp)
VALUE errinfo = *errp;
while ((link = *procs) != 0) {
- *procs = link->next;
- endproc = *link;
- xfree(link);
- (*endproc.func) (endproc.data);
- *errp = errinfo;
+ *procs = link->next;
+ endproc = *link;
+ xfree(link);
+ (*endproc.func) (endproc.data);
+ *errp = errinfo;
}
}
@@ -116,15 +116,15 @@ rb_ec_exec_end_proc(rb_execution_context_t * ec)
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
again:
- exec_end_procs_chain(&ephemeral_end_procs, &ec->errinfo);
- exec_end_procs_chain(&end_procs, &ec->errinfo);
+ exec_end_procs_chain(&ephemeral_end_procs, &ec->errinfo);
+ exec_end_procs_chain(&end_procs, &ec->errinfo);
}
else {
- EC_TMPPOP_TAG();
- error_handle(ec, state);
- if (!NIL_P(ec->errinfo)) errinfo = ec->errinfo;
- EC_REPUSH_TAG();
- goto again;
+ EC_TMPPOP_TAG();
+ error_handle(ec, ec->errinfo, state);
+ if (!NIL_P(ec->errinfo)) errinfo = ec->errinfo;
+ EC_REPUSH_TAG();
+ goto again;
}
EC_POP_TAG();
diff --git a/ext/-test-/abi/extconf.rb b/ext/-test-/abi/extconf.rb
index d786b15db9..3b090b7553 100644
--- a/ext/-test-/abi/extconf.rb
+++ b/ext/-test-/abi/extconf.rb
@@ -1,3 +1,4 @@
# frozen_string_literal: false
+return unless RUBY_PATCHLEVEL < 0
require_relative "../auto_ext.rb"
auto_ext(inc: true)
diff --git a/ext/-test-/arith_seq/beg_len_step/beg_len_step.c b/ext/-test-/arith_seq/beg_len_step/beg_len_step.c
new file mode 100644
index 0000000000..40c8cbee82
--- /dev/null
+++ b/ext/-test-/arith_seq/beg_len_step/beg_len_step.c
@@ -0,0 +1,19 @@
+#include "ruby/ruby.h"
+
+static VALUE
+arith_seq_s_beg_len_step(VALUE mod, VALUE obj, VALUE len, VALUE err)
+{
+ VALUE r;
+ long beg, len2, step;
+
+ r = rb_arithmetic_sequence_beg_len_step(obj, &beg, &len2, &step, NUM2LONG(len), NUM2INT(err));
+
+ return rb_ary_new_from_args(4, r, LONG2NUM(beg), LONG2NUM(len2), LONG2NUM(step));
+}
+
+void
+Init_beg_len_step(void)
+{
+ VALUE cArithSeq = rb_path2class("Enumerator::ArithmeticSequence");
+ rb_define_singleton_method(cArithSeq, "__beg_len_step__", arith_seq_s_beg_len_step, 3);
+}
diff --git a/ext/-test-/arith_seq/beg_len_step/depend b/ext/-test-/arith_seq/beg_len_step/depend
new file mode 100644
index 0000000000..36a2c4c71b
--- /dev/null
+++ b/ext/-test-/arith_seq/beg_len_step/depend
@@ -0,0 +1,161 @@
+# AUTOGENERATED DEPENDENCIES START
+beg_len_step.o: $(RUBY_EXTCONF_H)
+beg_len_step.o: $(arch_hdrdir)/ruby/config.h
+beg_len_step.o: $(hdrdir)/ruby/assert.h
+beg_len_step.o: $(hdrdir)/ruby/backward.h
+beg_len_step.o: $(hdrdir)/ruby/backward/2/assume.h
+beg_len_step.o: $(hdrdir)/ruby/backward/2/attributes.h
+beg_len_step.o: $(hdrdir)/ruby/backward/2/bool.h
+beg_len_step.o: $(hdrdir)/ruby/backward/2/inttypes.h
+beg_len_step.o: $(hdrdir)/ruby/backward/2/limits.h
+beg_len_step.o: $(hdrdir)/ruby/backward/2/long_long.h
+beg_len_step.o: $(hdrdir)/ruby/backward/2/stdalign.h
+beg_len_step.o: $(hdrdir)/ruby/backward/2/stdarg.h
+beg_len_step.o: $(hdrdir)/ruby/defines.h
+beg_len_step.o: $(hdrdir)/ruby/intern.h
+beg_len_step.o: $(hdrdir)/ruby/internal/abi.h
+beg_len_step.o: $(hdrdir)/ruby/internal/anyargs.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+beg_len_step.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+beg_len_step.o: $(hdrdir)/ruby/internal/assume.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/artificial.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/cold.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/const.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/error.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/format.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/noalias.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/noinline.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/pure.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/restrict.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/warning.h
+beg_len_step.o: $(hdrdir)/ruby/internal/attr/weakref.h
+beg_len_step.o: $(hdrdir)/ruby/internal/cast.h
+beg_len_step.o: $(hdrdir)/ruby/internal/compiler_is.h
+beg_len_step.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+beg_len_step.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+beg_len_step.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+beg_len_step.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+beg_len_step.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+beg_len_step.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+beg_len_step.o: $(hdrdir)/ruby/internal/compiler_since.h
+beg_len_step.o: $(hdrdir)/ruby/internal/config.h
+beg_len_step.o: $(hdrdir)/ruby/internal/constant_p.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core/rarray.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core/rbasic.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core/rbignum.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core/rclass.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core/rdata.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core/rfile.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core/rhash.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core/robject.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core/rregexp.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core/rstring.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core/rstruct.h
+beg_len_step.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+beg_len_step.o: $(hdrdir)/ruby/internal/ctype.h
+beg_len_step.o: $(hdrdir)/ruby/internal/dllexport.h
+beg_len_step.o: $(hdrdir)/ruby/internal/dosish.h
+beg_len_step.o: $(hdrdir)/ruby/internal/error.h
+beg_len_step.o: $(hdrdir)/ruby/internal/eval.h
+beg_len_step.o: $(hdrdir)/ruby/internal/event.h
+beg_len_step.o: $(hdrdir)/ruby/internal/fl_type.h
+beg_len_step.o: $(hdrdir)/ruby/internal/gc.h
+beg_len_step.o: $(hdrdir)/ruby/internal/glob.h
+beg_len_step.o: $(hdrdir)/ruby/internal/globals.h
+beg_len_step.o: $(hdrdir)/ruby/internal/has/attribute.h
+beg_len_step.o: $(hdrdir)/ruby/internal/has/builtin.h
+beg_len_step.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+beg_len_step.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+beg_len_step.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+beg_len_step.o: $(hdrdir)/ruby/internal/has/extension.h
+beg_len_step.o: $(hdrdir)/ruby/internal/has/feature.h
+beg_len_step.o: $(hdrdir)/ruby/internal/has/warning.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/array.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/bignum.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/class.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/compar.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/complex.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/cont.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/dir.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/enum.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/error.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/eval.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/file.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/gc.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/hash.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/io.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/load.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/marshal.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/numeric.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/object.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/parse.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/proc.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/process.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/random.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/range.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/rational.h
+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/signal.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/string.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/struct.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/thread.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/time.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/variable.h
+beg_len_step.o: $(hdrdir)/ruby/internal/intern/vm.h
+beg_len_step.o: $(hdrdir)/ruby/internal/interpreter.h
+beg_len_step.o: $(hdrdir)/ruby/internal/iterator.h
+beg_len_step.o: $(hdrdir)/ruby/internal/memory.h
+beg_len_step.o: $(hdrdir)/ruby/internal/method.h
+beg_len_step.o: $(hdrdir)/ruby/internal/module.h
+beg_len_step.o: $(hdrdir)/ruby/internal/newobj.h
+beg_len_step.o: $(hdrdir)/ruby/internal/rgengc.h
+beg_len_step.o: $(hdrdir)/ruby/internal/scan_args.h
+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/symbol.h
+beg_len_step.o: $(hdrdir)/ruby/internal/value.h
+beg_len_step.o: $(hdrdir)/ruby/internal/value_type.h
+beg_len_step.o: $(hdrdir)/ruby/internal/variable.h
+beg_len_step.o: $(hdrdir)/ruby/internal/warning_push.h
+beg_len_step.o: $(hdrdir)/ruby/internal/xmalloc.h
+beg_len_step.o: $(hdrdir)/ruby/missing.h
+beg_len_step.o: $(hdrdir)/ruby/ruby.h
+beg_len_step.o: $(hdrdir)/ruby/st.h
+beg_len_step.o: $(hdrdir)/ruby/subst.h
+beg_len_step.o: beg_len_step.c
+# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/-test-/arith_seq/beg_len_step/extconf.rb b/ext/-test-/arith_seq/beg_len_step/extconf.rb
new file mode 100644
index 0000000000..e72b3ad01f
--- /dev/null
+++ b/ext/-test-/arith_seq/beg_len_step/extconf.rb
@@ -0,0 +1,2 @@
+# frozen_string_literal: false
+create_makefile("-test-/arith_seq/beg_len_step")
diff --git a/ext/-test-/debug/inspector.c b/ext/-test-/debug/inspector.c
index f0c58e59f9..25f9d894d3 100644
--- a/ext/-test-/debug/inspector.c
+++ b/ext/-test-/debug/inspector.c
@@ -8,13 +8,13 @@ callback(const rb_debug_inspector_t *dbg_context, void *data)
long i, len = RARRAY_LEN(locs);
VALUE binds = rb_ary_new();
for (i = 0; i < len; ++i) {
- VALUE entry = rb_ary_new();
- rb_ary_push(binds, entry);
- rb_ary_push(entry, rb_debug_inspector_frame_self_get(dbg_context, i));
- rb_ary_push(entry, rb_debug_inspector_frame_binding_get(dbg_context, i));
- rb_ary_push(entry, rb_debug_inspector_frame_class_get(dbg_context, i));
- rb_ary_push(entry, rb_debug_inspector_frame_iseq_get(dbg_context, i));
- rb_ary_push(entry, rb_ary_entry(locs, i));
+ VALUE entry = rb_ary_new();
+ rb_ary_push(binds, entry);
+ rb_ary_push(entry, rb_debug_inspector_frame_self_get(dbg_context, i));
+ rb_ary_push(entry, rb_debug_inspector_frame_binding_get(dbg_context, i));
+ rb_ary_push(entry, rb_debug_inspector_frame_class_get(dbg_context, i));
+ rb_ary_push(entry, rb_debug_inspector_frame_iseq_get(dbg_context, i));
+ rb_ary_push(entry, rb_ary_entry(locs, i));
}
return binds;
}
diff --git a/ext/-test-/debug/profile_frames.c b/ext/-test-/debug/profile_frames.c
index 1656ff7d4b..d2bba7d183 100644
--- a/ext/-test-/debug/profile_frames.c
+++ b/ext/-test-/debug/profile_frames.c
@@ -18,19 +18,20 @@ profile_frames(VALUE self, VALUE start_v, VALUE num_v)
collected_size = rb_profile_frames(start, buff_size, buff, lines);
for (i=0; i<collected_size; i++) {
- VALUE ary = rb_ary_new();
- rb_ary_push(ary, rb_profile_frame_path(buff[i]));
- rb_ary_push(ary, rb_profile_frame_absolute_path(buff[i]));
- rb_ary_push(ary, rb_profile_frame_label(buff[i]));
- rb_ary_push(ary, rb_profile_frame_base_label(buff[i]));
- rb_ary_push(ary, rb_profile_frame_full_label(buff[i]));
- rb_ary_push(ary, rb_profile_frame_first_lineno(buff[i]));
- rb_ary_push(ary, rb_profile_frame_classpath(buff[i]));
- rb_ary_push(ary, rb_profile_frame_singleton_method_p(buff[i]));
- rb_ary_push(ary, rb_profile_frame_method_name(buff[i]));
- rb_ary_push(ary, rb_profile_frame_qualified_method_name(buff[i]));
-
- rb_ary_push(result, ary);
+ VALUE ary = rb_ary_new();
+ rb_ary_push(ary, rb_profile_frame_path(buff[i]));
+ rb_ary_push(ary, rb_profile_frame_absolute_path(buff[i]));
+ rb_ary_push(ary, rb_profile_frame_label(buff[i]));
+ rb_ary_push(ary, rb_profile_frame_base_label(buff[i]));
+ rb_ary_push(ary, rb_profile_frame_full_label(buff[i]));
+ rb_ary_push(ary, rb_profile_frame_first_lineno(buff[i]));
+ rb_ary_push(ary, rb_profile_frame_classpath(buff[i]));
+ rb_ary_push(ary, rb_profile_frame_singleton_method_p(buff[i]));
+ rb_ary_push(ary, rb_profile_frame_method_name(buff[i]));
+ rb_ary_push(ary, rb_profile_frame_qualified_method_name(buff[i]));
+ rb_ary_push(ary, INT2NUM(lines[i]));
+
+ rb_ary_push(result, ary);
}
return result;
diff --git a/ext/-test-/econv/append.c b/ext/-test-/econv/append.c
new file mode 100644
index 0000000000..724cd136c0
--- /dev/null
+++ b/ext/-test-/econv/append.c
@@ -0,0 +1,15 @@
+#include "ruby/ruby.h"
+#include "ruby/encoding.h"
+
+static VALUE
+econv_append(VALUE self, VALUE src, VALUE dst)
+{
+ rb_econv_t *ec = DATA_PTR(self);
+ return rb_econv_str_append(ec, src, dst, 0);
+}
+
+void
+Init_econv_append(VALUE klass)
+{
+ rb_define_method(klass, "append", econv_append, 2);
+}
diff --git a/ext/-test-/econv/extconf.rb b/ext/-test-/econv/extconf.rb
new file mode 100644
index 0000000000..d786b15db9
--- /dev/null
+++ b/ext/-test-/econv/extconf.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: false
+require_relative "../auto_ext.rb"
+auto_ext(inc: true)
diff --git a/ext/-test-/econv/init.c b/ext/-test-/econv/init.c
new file mode 100644
index 0000000000..9772ebe71c
--- /dev/null
+++ b/ext/-test-/econv/init.c
@@ -0,0 +1,11 @@
+#include "ruby.h"
+
+#define init(n) {void Init_econv_##n(VALUE klass); Init_econv_##n(klass);}
+
+void
+Init_econv(void)
+{
+ VALUE mBug = rb_define_module("Bug");
+ VALUE klass = rb_define_class_under(mBug, "EConv", rb_path2class("Encoding::Converter"));
+ TEST_INIT_FUNCS(init);
+}
diff --git a/ext/-test-/enumerator_kw/enumerator_kw.c b/ext/-test-/enumerator_kw/enumerator_kw.c
index 947d2b37e6..9104c51869 100644
--- a/ext/-test-/enumerator_kw/enumerator_kw.c
+++ b/ext/-test-/enumerator_kw/enumerator_kw.c
@@ -14,7 +14,8 @@ enumerator_kw(int argc, VALUE *argv, VALUE self)
}
void
-Init_enumerator_kw(void) {
+Init_enumerator_kw(void)
+{
VALUE module = rb_define_module("Bug");
module = rb_define_module_under(module, "EnumeratorKw");
rb_define_method(module, "m", enumerator_kw, -1);
diff --git a/ext/-test-/eval/eval.c b/ext/-test-/eval/eval.c
new file mode 100644
index 0000000000..983468fc34
--- /dev/null
+++ b/ext/-test-/eval/eval.c
@@ -0,0 +1,13 @@
+#include "ruby/ruby.h"
+
+static VALUE
+eval_string(VALUE self, VALUE str)
+{
+ return rb_eval_string(StringValueCStr(str));
+}
+
+void
+Init_eval(void)
+{
+ rb_define_global_function("rb_eval_string", eval_string, 1);
+}
diff --git a/ext/-test-/eval/extconf.rb b/ext/-test-/eval/extconf.rb
new file mode 100644
index 0000000000..cdbf6a8597
--- /dev/null
+++ b/ext/-test-/eval/extconf.rb
@@ -0,0 +1,2 @@
+require 'mkmf'
+create_makefile('-test-/eval')
diff --git a/ext/-test-/file/fs.c b/ext/-test-/file/fs.c
index 1a6c3d06dc..eb17e9768e 100644
--- a/ext/-test-/file/fs.c
+++ b/ext/-test-/file/fs.c
@@ -54,24 +54,24 @@ get_fsname(VALUE self, VALUE str)
FilePathValue(str);
str = rb_str_encode_ospath(str);
if (STATFS(StringValueCStr(str), &st) == -1) {
- rb_sys_fail_str(str);
+ rb_sys_fail_str(str);
}
# ifdef HAVE_STRUCT_STATFS_T_F_FSTYPENAME
if (st.f_fstypename[0])
- return CSTR(st.f_fstypename);
+ return CSTR(st.f_fstypename);
# endif
# ifdef HAVE_STRUCT_STATFS_T_F_TYPE
switch (st.f_type) {
case 0x9123683E: /* BTRFS_SUPER_MAGIC */
- return CSTR("btrfs");
+ return CSTR("btrfs");
case 0x7461636f: /* OCFS2_SUPER_MAGIC */
- return CSTR("ocfs");
+ return CSTR("ocfs");
case 0xEF53: /* EXT2_SUPER_MAGIC EXT3_SUPER_MAGIC EXT4_SUPER_MAGIC */
- return CSTR("ext4");
+ return CSTR("ext4");
case 0x58465342: /* XFS_SUPER_MAGIC */
- return CSTR("xfs");
+ return CSTR("xfs");
case 0x01021994: /* TMPFS_MAGIC */
- return CSTR("tmpfs");
+ return CSTR("tmpfs");
}
# endif
#endif
diff --git a/ext/-test-/funcall/funcall.c b/ext/-test-/funcall/funcall.c
index 43521bf2e9..a68d6de255 100644
--- a/ext/-test-/funcall/funcall.c
+++ b/ext/-test-/funcall/funcall.c
@@ -47,17 +47,17 @@ Init_funcall(void)
VALUE cRelay = rb_define_module_under(cTestFuncall, "Relay");
rb_define_singleton_method(cRelay,
- "with_funcall2",
- with_funcall2,
- -1);
+ "with_funcall2",
+ with_funcall2,
+ -1);
rb_define_singleton_method(cRelay,
"with_funcall_passing_block_kw",
with_funcall_passing_block_kw,
-1);
rb_define_singleton_method(cRelay,
- "with_funcall_passing_block",
- with_funcall_passing_block,
- -1);
+ "with_funcall_passing_block",
+ with_funcall_passing_block,
+ -1);
rb_define_singleton_method(cRelay,
"with_funcallv_public_kw",
with_funcallv_public_kw,
diff --git a/ext/-test-/gvl/call_without_gvl/call_without_gvl.c b/ext/-test-/gvl/call_without_gvl/call_without_gvl.c
index 233635421b..97946e925d 100644
--- a/ext/-test-/gvl/call_without_gvl/call_without_gvl.c
+++ b/ext/-test-/gvl/call_without_gvl/call_without_gvl.c
@@ -17,7 +17,7 @@ thread_runnable_sleep(VALUE thread, VALUE timeout)
struct timeval timeval;
if (NIL_P(timeout)) {
- rb_raise(rb_eArgError, "timeout must be non nil");
+ rb_raise(rb_eArgError, "timeout must be non nil");
}
timeval = rb_time_interval(timeout);
diff --git a/ext/-test-/marshal/internal_ivar/internal_ivar.c b/ext/-test-/marshal/internal_ivar/internal_ivar.c
index de0cf711aa..b2188f737a 100644
--- a/ext/-test-/marshal/internal_ivar/internal_ivar.c
+++ b/ext/-test-/marshal/internal_ivar/internal_ivar.c
@@ -36,10 +36,7 @@ Init_internal_ivar(void)
VALUE newclass = rb_define_class_under(mMarshal, "InternalIVar", rb_cObject);
id_normal_ivar = rb_intern_const("normal");
-#if 0
- /* leave id_internal_ivar being 0 */
- id_internal_ivar = rb_make_internal_id();
-#endif
+ id_internal_ivar = rb_intern_const("K");
id_encoding_short = rb_intern_const("E");
rb_define_method(newclass, "initialize", init, 3);
rb_define_method(newclass, "normal", get_normal, 0);
diff --git a/ext/-test-/memory_status/memory_status.c b/ext/-test-/memory_status/memory_status.c
index afacbee785..f124c97ca1 100644
--- a/ext/-test-/memory_status/memory_status.c
+++ b/ext/-test-/memory_status/memory_status.c
@@ -34,7 +34,7 @@ read_status(VALUE self)
taskinfo.virtual_size = 0;
taskinfo.resident_size = 0;
error = task_info(mach_task_self(), flavor,
- (task_info_t)&taskinfo, &out_count);
+ (task_info_t)&taskinfo, &out_count);
if (error != KERN_SUCCESS) return Qnil;
#ifndef ULL2NUM
/* "long long" does not exist here, use size_t instead. */
@@ -50,7 +50,7 @@ read_status(VALUE self)
PROCESS_MEMORY_COUNTERS c;
c.cb = sizeof(c);
if (!GetProcessMemoryInfo(GetCurrentProcess(), &c, c.cb))
- return Qnil;
+ return Qnil;
size = SIZET2NUM(c.PagefileUsage);
rss = SIZET2NUM(c.WorkingSetSize);
peak = SIZET2NUM(c.PeakWorkingSetSize);
@@ -68,13 +68,13 @@ Init_memory_status(void)
{
VALUE mMemory = rb_define_module("Memory");
cMemoryStatus =
- rb_struct_define_under(mMemory, "Status", "size",
+ rb_struct_define_under(mMemory, "Status", "size",
#ifdef HAVE_RSS
- "rss",
+ "rss",
#endif
#ifdef HAVE_PEAK
- "peak",
+ "peak",
#endif
- (char *)NULL);
+ (char *)NULL);
rb_define_method(cMemoryStatus, "_update", read_status, 0);
}
diff --git a/ext/-test-/num2int/num2int.c b/ext/-test-/num2int/num2int.c
index 3aec3ccf3b..63a441fda6 100644
--- a/ext/-test-/num2int/num2int.c
+++ b/ext/-test-/num2int/num2int.c
@@ -4,7 +4,7 @@ static VALUE
test_num2short(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%d", NUM2SHORT(num));
+ snprintf(buf, sizeof(buf), "%d", NUM2SHORT(num));
return rb_str_new_cstr(buf);
}
@@ -12,7 +12,7 @@ static VALUE
test_num2ushort(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%u", NUM2USHORT(num));
+ snprintf(buf, sizeof(buf), "%u", NUM2USHORT(num));
return rb_str_new_cstr(buf);
}
@@ -20,7 +20,7 @@ static VALUE
test_num2int(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%d", NUM2INT(num));
+ snprintf(buf, sizeof(buf), "%d", NUM2INT(num));
return rb_str_new_cstr(buf);
}
@@ -28,7 +28,7 @@ static VALUE
test_num2uint(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%u", NUM2UINT(num));
+ snprintf(buf, sizeof(buf), "%u", NUM2UINT(num));
return rb_str_new_cstr(buf);
}
@@ -36,7 +36,7 @@ static VALUE
test_num2long(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%ld", NUM2LONG(num));
+ snprintf(buf, sizeof(buf), "%ld", NUM2LONG(num));
return rb_str_new_cstr(buf);
}
@@ -44,7 +44,7 @@ static VALUE
test_num2ulong(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%lu", NUM2ULONG(num));
+ snprintf(buf, sizeof(buf), "%lu", NUM2ULONG(num));
return rb_str_new_cstr(buf);
}
@@ -53,7 +53,7 @@ static VALUE
test_num2ll(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%"PRI_LL_PREFIX"d", NUM2LL(num));
+ snprintf(buf, sizeof(buf), "%"PRI_LL_PREFIX"d", NUM2LL(num));
return rb_str_new_cstr(buf);
}
@@ -61,7 +61,7 @@ static VALUE
test_num2ull(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%"PRI_LL_PREFIX"u", NUM2ULL(num));
+ snprintf(buf, sizeof(buf), "%"PRI_LL_PREFIX"u", NUM2ULL(num));
return rb_str_new_cstr(buf);
}
#endif
@@ -70,7 +70,7 @@ static VALUE
test_fix2short(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%d", FIX2SHORT(num));
+ snprintf(buf, sizeof(buf), "%d", FIX2SHORT(num));
return rb_str_new_cstr(buf);
}
@@ -78,7 +78,7 @@ static VALUE
test_fix2int(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%d", FIX2INT(num));
+ snprintf(buf, sizeof(buf), "%d", FIX2INT(num));
return rb_str_new_cstr(buf);
}
@@ -86,7 +86,7 @@ static VALUE
test_fix2uint(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%u", FIX2UINT(num));
+ snprintf(buf, sizeof(buf), "%u", FIX2UINT(num));
return rb_str_new_cstr(buf);
}
@@ -94,7 +94,7 @@ static VALUE
test_fix2long(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%ld", FIX2LONG(num));
+ snprintf(buf, sizeof(buf), "%ld", FIX2LONG(num));
return rb_str_new_cstr(buf);
}
@@ -102,7 +102,7 @@ static VALUE
test_fix2ulong(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%lu", FIX2ULONG(num));
+ snprintf(buf, sizeof(buf), "%lu", FIX2ULONG(num));
return rb_str_new_cstr(buf);
}
diff --git a/ext/-test-/printf/printf.c b/ext/-test-/printf/printf.c
index e793bb7a48..889c0d9f0d 100644
--- a/ext/-test-/printf/printf.c
+++ b/ext/-test-/printf/printf.c
@@ -25,7 +25,7 @@ uint_to_str(char *p, char *e, unsigned int x)
char *e0 = e;
if (e <= p) return p;
do {
- *--e = x % 10 + '0';
+ *--e = x % 10 + '0';
} while ((x /= 10) != 0 && e > p);
memmove(p, e, e0 - e);
return p + (e0 - e);
@@ -44,48 +44,48 @@ printf_test_call(int argc, VALUE *argv, VALUE self)
if (RSTRING_LEN(type) != 1) rb_raise(rb_eArgError, "wrong length(%ld)", RSTRING_LEN(type));
switch (cnv = RSTRING_PTR(type)[0]) {
case 'd': case 'x': case 'o': case 'X':
- n = NUM2INT(num);
- break;
+ n = NUM2INT(num);
+ break;
case 's':
- s = StringValueCStr(num);
- break;
+ s = StringValueCStr(num);
+ break;
default: rb_raise(rb_eArgError, "wrong conversion(%c)", cnv);
}
*p++ = '%';
if (!NIL_P(opt)) {
- VALUE v;
- Check_Type(opt, T_HASH);
- if (RTEST(rb_hash_aref(opt, ID2SYM(rb_intern("space"))))) {
- *p++ = ' ';
- }
- if (RTEST(rb_hash_aref(opt, ID2SYM(rb_intern("hash"))))) {
- *p++ = '#';
- }
- if (RTEST(rb_hash_aref(opt, ID2SYM(rb_intern("plus"))))) {
- *p++ = '+';
- }
- if (RTEST(rb_hash_aref(opt, ID2SYM(rb_intern("minus"))))) {
- *p++ = '-';
- }
- if (RTEST(rb_hash_aref(opt, ID2SYM(rb_intern("zero"))))) {
- *p++ = '0';
- }
- if (!NIL_P(v = rb_hash_aref(opt, ID2SYM(rb_intern("width"))))) {
- p = uint_to_str(p, format + sizeof(format), NUM2UINT(v));
- }
- if (!NIL_P(v = rb_hash_aref(opt, ID2SYM(rb_intern("prec"))))) {
- *p++ = '.';
- if (FIXNUM_P(v))
- p = uint_to_str(p, format + sizeof(format), NUM2UINT(v));
- }
+ VALUE v;
+ Check_Type(opt, T_HASH);
+ if (RTEST(rb_hash_aref(opt, ID2SYM(rb_intern("space"))))) {
+ *p++ = ' ';
+ }
+ if (RTEST(rb_hash_aref(opt, ID2SYM(rb_intern("hash"))))) {
+ *p++ = '#';
+ }
+ if (RTEST(rb_hash_aref(opt, ID2SYM(rb_intern("plus"))))) {
+ *p++ = '+';
+ }
+ if (RTEST(rb_hash_aref(opt, ID2SYM(rb_intern("minus"))))) {
+ *p++ = '-';
+ }
+ if (RTEST(rb_hash_aref(opt, ID2SYM(rb_intern("zero"))))) {
+ *p++ = '0';
+ }
+ if (!NIL_P(v = rb_hash_aref(opt, ID2SYM(rb_intern("width"))))) {
+ p = uint_to_str(p, format + sizeof(format), NUM2UINT(v));
+ }
+ if (!NIL_P(v = rb_hash_aref(opt, ID2SYM(rb_intern("prec"))))) {
+ *p++ = '.';
+ if (FIXNUM_P(v))
+ p = uint_to_str(p, format + sizeof(format), NUM2UINT(v));
+ }
}
*p++ = cnv;
*p++ = '\0';
if (cnv == 's') {
- result = rb_enc_sprintf(rb_usascii_encoding(), format, s);
+ result = rb_enc_sprintf(rb_usascii_encoding(), format, s);
}
else {
- result = rb_enc_sprintf(rb_usascii_encoding(), format, n);
+ result = rb_enc_sprintf(rb_usascii_encoding(), format, n);
}
return rb_assoc_new(result, rb_usascii_str_new_cstr(format));
}
diff --git a/ext/-test-/proc/super.c b/ext/-test-/proc/super.c
index dbe8af08f1..816520e1df 100644
--- a/ext/-test-/proc/super.c
+++ b/ext/-test-/proc/super.c
@@ -9,7 +9,7 @@ bug_proc_call_super(RB_BLOCK_CALL_FUNC_ARGLIST(yieldarg, procarg))
args[1] = procarg;
ret = rb_call_super(2, args);
if (!NIL_P(blockarg)) {
- ret = rb_proc_call(blockarg, ret);
+ ret = rb_proc_call(blockarg, ret);
}
return ret;
}
diff --git a/ext/-test-/random/bad_version.c b/ext/-test-/random/bad_version.c
new file mode 100644
index 0000000000..dae63a6d19
--- /dev/null
+++ b/ext/-test-/random/bad_version.c
@@ -0,0 +1,135 @@
+#include "ruby/random.h"
+
+#if RUBY_RANDOM_INTERFACE_VERSION_MAJOR < RUBY_RANDOM_INTERFACE_VERSION_MAJOR_MAX
+# define DEFINE_VERSION_MAX 1
+#else
+# define DEFINE_VERSION_MAX 0
+#endif
+
+NORETURN(static void must_not_reach(void));
+static void
+must_not_reach(void)
+{
+ rb_raise(rb_eTypeError, "must not reach");
+}
+
+NORETURN(static void bad_version_init(rb_random_t *, const uint32_t *, size_t));
+static void
+bad_version_init(rb_random_t *rnd, const uint32_t *buf, size_t len)
+{
+ must_not_reach();
+}
+
+NORETURN(static void bad_version_init_int32(rb_random_t *, uint32_t));
+RB_RANDOM_DEFINE_INIT_INT32_FUNC(bad_version)
+
+NORETURN(static void bad_version_get_bytes(rb_random_t *, void *, size_t));
+static void
+bad_version_get_bytes(rb_random_t *rnd, void *p, size_t n)
+{
+ must_not_reach();
+}
+
+NORETURN(static uint32_t bad_version_get_int32(rb_random_t *));
+static uint32_t
+bad_version_get_int32(rb_random_t *rnd)
+{
+ must_not_reach();
+ UNREACHABLE_RETURN(0);
+}
+
+static VALUE
+bad_version_alloc(VALUE klass, const rb_data_type_t *type)
+{
+ rb_random_t *rnd;
+ VALUE obj = TypedData_Make_Struct(klass, rb_random_t, type, rnd);
+ rb_random_base_init(rnd);
+ return obj;
+}
+
+/* version 0 */
+static const rb_random_interface_t random_version_zero_if;
+
+static rb_random_data_type_t version_zero_type = {
+ "random/version_zero",
+ {
+ rb_random_mark,
+ RUBY_TYPED_DEFAULT_FREE,
+ },
+ RB_RANDOM_PARENT,
+ (void *)&random_version_zero_if,
+ RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+static VALUE
+version_zero_alloc(VALUE klass)
+{
+ return bad_version_alloc(klass, &version_zero_type);
+}
+
+static void
+init_version_zero(VALUE mod, VALUE base)
+{
+ VALUE c = rb_define_class_under(mod, "VersionZero", base);
+ rb_define_alloc_func(c, version_zero_alloc);
+ RB_RANDOM_DATA_INIT_PARENT(version_zero_type);
+}
+
+#if DEFINE_VERSION_MAX
+/* version max */
+static const rb_random_interface_t random_version_max_if;
+static rb_random_data_type_t version_max_type = {
+ "random/version_max",
+ {
+ rb_random_mark,
+ RUBY_TYPED_DEFAULT_FREE,
+ },
+ RB_RANDOM_PARENT,
+ (void *)&random_version_max_if,
+ RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+static VALUE
+version_max_alloc(VALUE klass)
+{
+ return bad_version_alloc(klass, &version_max_type);
+}
+
+static void
+init_version_max(VALUE mod, VALUE base)
+{
+ VALUE c = rb_define_class_under(mod, "VersionMax", base);
+ rb_define_alloc_func(c, version_max_alloc);
+ RB_RANDOM_DATA_INIT_PARENT(version_max_type);
+}
+#else
+static void
+init_version_max(mod, base)
+{
+}
+#endif
+
+void
+Init_random_bad_version(VALUE mod, VALUE base)
+{
+ init_version_zero(mod, base);
+ init_version_max(mod, base);
+}
+
+#undef RUBY_RANDOM_INTERFACE_VERSION_MAJOR
+
+#define RUBY_RANDOM_INTERFACE_VERSION_MAJOR 0
+static const rb_random_interface_t random_version_zero_if = {
+ 0,
+ RB_RANDOM_INTERFACE_DEFINE(bad_version)
+};
+#undef RUBY_RANDOM_INTERFACE_VERSION_MAJOR
+
+#if DEFINE_VERSION_MAX
+#define RUBY_RANDOM_INTERFACE_VERSION_MAJOR RUBY_RANDOM_INTERFACE_VERSION_MAJOR_MAX
+static const rb_random_interface_t random_version_max_if = {
+ 0,
+ RB_RANDOM_INTERFACE_DEFINE(bad_version)
+};
+#undef RUBY_RANDOM_INTERFACE_VERSION_MAJOR
+#endif
diff --git a/ext/-test-/random/depend b/ext/-test-/random/depend
index 602526cf7b..f2cbf7fc14 100644
--- a/ext/-test-/random/depend
+++ b/ext/-test-/random/depend
@@ -1,4 +1,164 @@
# AUTOGENERATED DEPENDENCIES START
+bad_version.o: $(RUBY_EXTCONF_H)
+bad_version.o: $(arch_hdrdir)/ruby/config.h
+bad_version.o: $(hdrdir)/ruby/assert.h
+bad_version.o: $(hdrdir)/ruby/backward.h
+bad_version.o: $(hdrdir)/ruby/backward/2/assume.h
+bad_version.o: $(hdrdir)/ruby/backward/2/attributes.h
+bad_version.o: $(hdrdir)/ruby/backward/2/bool.h
+bad_version.o: $(hdrdir)/ruby/backward/2/inttypes.h
+bad_version.o: $(hdrdir)/ruby/backward/2/limits.h
+bad_version.o: $(hdrdir)/ruby/backward/2/long_long.h
+bad_version.o: $(hdrdir)/ruby/backward/2/stdalign.h
+bad_version.o: $(hdrdir)/ruby/backward/2/stdarg.h
+bad_version.o: $(hdrdir)/ruby/defines.h
+bad_version.o: $(hdrdir)/ruby/intern.h
+bad_version.o: $(hdrdir)/ruby/internal/abi.h
+bad_version.o: $(hdrdir)/ruby/internal/anyargs.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+bad_version.o: $(hdrdir)/ruby/internal/assume.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/artificial.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/cold.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/const.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/error.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/format.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/noalias.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/noinline.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/pure.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/restrict.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/warning.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/weakref.h
+bad_version.o: $(hdrdir)/ruby/internal/cast.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_since.h
+bad_version.o: $(hdrdir)/ruby/internal/config.h
+bad_version.o: $(hdrdir)/ruby/internal/constant_p.h
+bad_version.o: $(hdrdir)/ruby/internal/core.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rarray.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rbasic.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rbignum.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rclass.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rdata.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rfile.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rhash.h
+bad_version.o: $(hdrdir)/ruby/internal/core/robject.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rregexp.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rstring.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rstruct.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+bad_version.o: $(hdrdir)/ruby/internal/ctype.h
+bad_version.o: $(hdrdir)/ruby/internal/dllexport.h
+bad_version.o: $(hdrdir)/ruby/internal/dosish.h
+bad_version.o: $(hdrdir)/ruby/internal/error.h
+bad_version.o: $(hdrdir)/ruby/internal/eval.h
+bad_version.o: $(hdrdir)/ruby/internal/event.h
+bad_version.o: $(hdrdir)/ruby/internal/fl_type.h
+bad_version.o: $(hdrdir)/ruby/internal/gc.h
+bad_version.o: $(hdrdir)/ruby/internal/glob.h
+bad_version.o: $(hdrdir)/ruby/internal/globals.h
+bad_version.o: $(hdrdir)/ruby/internal/has/attribute.h
+bad_version.o: $(hdrdir)/ruby/internal/has/builtin.h
+bad_version.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+bad_version.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+bad_version.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+bad_version.o: $(hdrdir)/ruby/internal/has/extension.h
+bad_version.o: $(hdrdir)/ruby/internal/has/feature.h
+bad_version.o: $(hdrdir)/ruby/internal/has/warning.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/array.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/bignum.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/class.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/compar.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/complex.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/cont.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/dir.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/enum.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/error.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/eval.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/file.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/gc.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/hash.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/io.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/load.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/marshal.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/numeric.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/object.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/parse.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/proc.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/process.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/random.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/range.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/rational.h
+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/signal.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/string.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/struct.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/thread.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/time.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/variable.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/vm.h
+bad_version.o: $(hdrdir)/ruby/internal/interpreter.h
+bad_version.o: $(hdrdir)/ruby/internal/iterator.h
+bad_version.o: $(hdrdir)/ruby/internal/memory.h
+bad_version.o: $(hdrdir)/ruby/internal/method.h
+bad_version.o: $(hdrdir)/ruby/internal/module.h
+bad_version.o: $(hdrdir)/ruby/internal/newobj.h
+bad_version.o: $(hdrdir)/ruby/internal/rgengc.h
+bad_version.o: $(hdrdir)/ruby/internal/scan_args.h
+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/symbol.h
+bad_version.o: $(hdrdir)/ruby/internal/value.h
+bad_version.o: $(hdrdir)/ruby/internal/value_type.h
+bad_version.o: $(hdrdir)/ruby/internal/variable.h
+bad_version.o: $(hdrdir)/ruby/internal/warning_push.h
+bad_version.o: $(hdrdir)/ruby/internal/xmalloc.h
+bad_version.o: $(hdrdir)/ruby/missing.h
+bad_version.o: $(hdrdir)/ruby/random.h
+bad_version.o: $(hdrdir)/ruby/ruby.h
+bad_version.o: $(hdrdir)/ruby/st.h
+bad_version.o: $(hdrdir)/ruby/subst.h
+bad_version.o: bad_version.c
init.o: $(RUBY_EXTCONF_H)
init.o: $(arch_hdrdir)/ruby/config.h
init.o: $(hdrdir)/ruby.h
diff --git a/ext/-test-/random/loop.c b/ext/-test-/random/loop.c
index 0572096403..b789ab1d01 100644
--- a/ext/-test-/random/loop.c
+++ b/ext/-test-/random/loop.c
@@ -13,6 +13,7 @@ static const rb_random_interface_t random_loop_if = {
RB_RANDOM_INTERFACE_DEFINE_WITH_REAL(loop)
};
+RB_RANDOM_DEFINE_INIT_INT32_FUNC(loop)
static size_t
random_loop_memsize(const void *ptr)
{
diff --git a/ext/-test-/rational/depend b/ext/-test-/rational/depend
index 8729695886..ce977821b8 100644
--- a/ext/-test-/rational/depend
+++ b/ext/-test-/rational/depend
@@ -174,5 +174,6 @@ rat.o: $(top_srcdir)/internal/static_assert.h
rat.o: $(top_srcdir)/internal/vm.h
rat.o: $(top_srcdir)/internal/warnings.h
rat.o: $(top_srcdir)/ruby_assert.h
+rat.o: $(top_srcdir)/shape.h
rat.o: rat.c
# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/-test-/rb_call_super_kw/rb_call_super_kw.c b/ext/-test-/rb_call_super_kw/rb_call_super_kw.c
index 7f094545d2..61681ed733 100644
--- a/ext/-test-/rb_call_super_kw/rb_call_super_kw.c
+++ b/ext/-test-/rb_call_super_kw/rb_call_super_kw.c
@@ -7,7 +7,8 @@ rb_call_super_kw_m(int argc, VALUE *argv, VALUE self)
}
void
-Init_rb_call_super_kw(void) {
+Init_rb_call_super_kw(void)
+{
VALUE module = rb_define_module("Bug");
module = rb_define_module_under(module, "RbCallSuperKw");
rb_define_method(module, "m", rb_call_super_kw_m, -1);
diff --git a/ext/-test-/st/foreach/foreach.c b/ext/-test-/st/foreach/foreach.c
index 27ac18046f..cde49fb26d 100644
--- a/ext/-test-/st/foreach/foreach.c
+++ b/ext/-test-/st/foreach/foreach.c
@@ -12,22 +12,22 @@ static void
force_unpack_check(struct checker *c, st_data_t key, st_data_t val)
{
if (c->nr == 0) {
- st_data_t i;
+ st_data_t i;
- if (c->tbl->bins != NULL) rb_bug("should be packed\n");
+ if (c->tbl->bins != NULL) rb_bug("should be packed\n");
- /* force unpacking during iteration: */
- for (i = 1; i < expect_size; i++)
- st_add_direct(c->tbl, i, i);
+ /* force unpacking during iteration: */
+ for (i = 1; i < expect_size; i++)
+ st_add_direct(c->tbl, i, i);
- if (c->tbl->bins == NULL) rb_bug("should be unpacked\n");
+ if (c->tbl->bins == NULL) rb_bug("should be unpacked\n");
}
if (key != c->nr) {
- rb_bug("unexpected key: %"PRIuVALUE" (expected %"PRIuVALUE")\n", (VALUE)key, (VALUE)c->nr);
+ rb_bug("unexpected key: %"PRIuVALUE" (expected %"PRIuVALUE")\n", (VALUE)key, (VALUE)c->nr);
}
if (val != c->nr) {
- rb_bug("unexpected val: %"PRIuVALUE" (expected %"PRIuVALUE")\n", (VALUE)val, (VALUE)c->nr);
+ rb_bug("unexpected val: %"PRIuVALUE" (expected %"PRIuVALUE")\n", (VALUE)val, (VALUE)c->nr);
}
c->nr++;
@@ -39,34 +39,34 @@ unp_fec_i(st_data_t key, st_data_t val, st_data_t args, int error)
struct checker *c = (struct checker *)args;
if (error) {
- if (c->test == ID2SYM(rb_intern("delete2")))
- return ST_STOP;
+ if (c->test == ID2SYM(rb_intern("delete2")))
+ return ST_STOP;
- rb_bug("unexpected error");
+ rb_bug("unexpected error");
}
force_unpack_check(c, key, val);
if (c->test == ID2SYM(rb_intern("check"))) {
- return ST_CHECK;
+ return ST_CHECK;
}
if (c->test == ID2SYM(rb_intern("delete1"))) {
- if (c->nr == 1) return ST_DELETE;
- return ST_CHECK;
+ if (c->nr == 1) return ST_DELETE;
+ return ST_CHECK;
}
if (c->test == ID2SYM(rb_intern("delete2"))) {
- if (c->nr == 1) {
- st_data_t k = 0;
- st_data_t v;
-
- if (!st_delete(c->tbl, &k, &v)) {
- rb_bug("failed to delete\n");
- }
- if (v != 0) {
- rb_bug("unexpected value deleted: %"PRIuVALUE" (expected 0)", (VALUE)v);
- }
- }
- return ST_CHECK;
+ if (c->nr == 1) {
+ st_data_t k = 0;
+ st_data_t v;
+
+ if (!st_delete(c->tbl, &k, &v)) {
+ rb_bug("failed to delete\n");
+ }
+ if (v != 0) {
+ rb_bug("unexpected value deleted: %"PRIuVALUE" (expected 0)", (VALUE)v);
+ }
+ }
+ return ST_CHECK;
}
rb_raise(rb_eArgError, "unexpected arg: %+"PRIsVALUE, c->test);
@@ -89,13 +89,13 @@ unp_fec(VALUE self, VALUE test)
st_foreach_check(tbl, unp_fec_i, (st_data_t)&c, -1);
if (c.test == ID2SYM(rb_intern("delete2"))) {
- if (c.nr != 1) {
- rb_bug("mismatched iteration: %"PRIuVALUE" (expected 1)\n", (VALUE)c.nr);
- }
+ if (c.nr != 1) {
+ rb_bug("mismatched iteration: %"PRIuVALUE" (expected 1)\n", (VALUE)c.nr);
+ }
}
else if (c.nr != expect_size) {
- rb_bug("mismatched iteration: %"PRIuVALUE" (expected %"PRIuVALUE")\n",
- (VALUE)c.nr, (VALUE)expect_size);
+ rb_bug("mismatched iteration: %"PRIuVALUE" (expected %"PRIuVALUE")\n",
+ (VALUE)c.nr, (VALUE)expect_size);
}
if (tbl->bins == NULL) rb_bug("should be unpacked\n");
@@ -112,22 +112,22 @@ unp_fe_i(st_data_t key, st_data_t val, st_data_t args)
force_unpack_check(c, key, val);
if (c->test == ID2SYM(rb_intern("unpacked"))) {
- return ST_CONTINUE;
+ return ST_CONTINUE;
}
else if (c->test == ID2SYM(rb_intern("unpack_delete"))) {
- if (c->nr == 1) {
- st_data_t k = 0;
- st_data_t v;
-
- if (!st_delete(c->tbl, &k, &v)) {
- rb_bug("failed to delete\n");
- }
- if (v != 0) {
- rb_bug("unexpected value deleted: %"PRIuVALUE" (expected 0)", (VALUE)v);
- }
- return ST_CONTINUE;
- }
- rb_bug("should never get here\n");
+ if (c->nr == 1) {
+ st_data_t k = 0;
+ st_data_t v;
+
+ if (!st_delete(c->tbl, &k, &v)) {
+ rb_bug("failed to delete\n");
+ }
+ if (v != 0) {
+ rb_bug("unexpected value deleted: %"PRIuVALUE" (expected 0)", (VALUE)v);
+ }
+ return ST_CONTINUE;
+ }
+ rb_bug("should never get here\n");
}
rb_raise(rb_eArgError, "unexpected arg: %+"PRIsVALUE, c->test);
@@ -150,13 +150,13 @@ unp_fe(VALUE self, VALUE test)
st_foreach(tbl, unp_fe_i, (st_data_t)&c);
if (c.test == ID2SYM(rb_intern("unpack_delete"))) {
- if (c.nr != 1) {
- rb_bug("mismatched iteration: %"PRIuVALUE" (expected 1)\n", (VALUE)c.nr);
- }
+ if (c.nr != 1) {
+ rb_bug("mismatched iteration: %"PRIuVALUE" (expected 1)\n", (VALUE)c.nr);
+ }
}
else if (c.nr != expect_size) {
- rb_bug("mismatched iteration: %"PRIuVALUE" (expected %"PRIuVALUE"o)\n",
- (VALUE)c.nr, (VALUE)expect_size);
+ rb_bug("mismatched iteration: %"PRIuVALUE" (expected %"PRIuVALUE"o)\n",
+ (VALUE)c.nr, (VALUE)expect_size);
}
if (tbl->bins == NULL) rb_bug("should be unpacked\n");
diff --git a/ext/-test-/st/numhash/numhash.c b/ext/-test-/st/numhash/numhash.c
index aa8015e86c..7e8d5d9fe2 100644
--- a/ext/-test-/st/numhash/numhash.c
+++ b/ext/-test-/st/numhash/numhash.c
@@ -42,7 +42,7 @@ numhash_aref(VALUE self, VALUE key)
st_table *tbl = (st_table *)Check_TypedStruct(self, &numhash_type);
if (!SPECIAL_CONST_P(key)) rb_raise(rb_eArgError, "not a special const");
if (st_lookup(tbl, (st_data_t)key, &data))
- return (VALUE)data;
+ return (VALUE)data;
return Qnil;
}
@@ -79,12 +79,12 @@ update_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
VALUE ret = rb_yield_values(existing ? 2 : 1, (VALUE)*key, (VALUE)*value);
switch (ret) {
case Qfalse:
- return ST_STOP;
+ return ST_STOP;
case Qnil:
- return ST_DELETE;
+ return ST_DELETE;
default:
- *value = ret;
- return ST_CONTINUE;
+ *value = ret;
+ return ST_CONTINUE;
}
}
@@ -93,9 +93,9 @@ numhash_update(VALUE self, VALUE key)
{
st_table *table = (st_table *)Check_TypedStruct(self, &numhash_type);
if (st_update(table, (st_data_t)key, update_func, 0))
- return Qtrue;
+ return Qtrue;
else
- return Qfalse;
+ return Qfalse;
}
#if SIZEOF_LONG == SIZEOF_VOIDP
@@ -117,7 +117,7 @@ numhash_delete_safe(VALUE self, VALUE key)
st_table *table = (st_table *)Check_TypedStruct(self, &numhash_type);
st_data_t val, k = (st_data_t)key;
if (st_delete_safe(table, &k, &val, (st_data_t)self)) {
- return val;
+ return val;
}
return Qnil;
}
diff --git a/ext/-test-/st/update/update.c b/ext/-test-/st/update/update.c
index 979ad3e334..ea7fab12e1 100644
--- a/ext/-test-/st/update/update.c
+++ b/ext/-test-/st/update/update.c
@@ -7,12 +7,12 @@ update_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
VALUE ret = rb_yield_values(existing ? 2 : 1, (VALUE)*key, (VALUE)*value);
switch (ret) {
case Qfalse:
- return ST_STOP;
+ return ST_STOP;
case Qnil:
- return ST_DELETE;
+ return ST_DELETE;
default:
- *value = ret;
- return ST_CONTINUE;
+ *value = ret;
+ return ST_CONTINUE;
}
}
@@ -20,9 +20,9 @@ static VALUE
test_st_update(VALUE self, VALUE key)
{
if (st_update(RHASH_TBL(self), (st_data_t)key, update_func, 0))
- return Qtrue;
+ return Qtrue;
else
- return Qfalse;
+ return Qfalse;
}
void
diff --git a/ext/-test-/string/coderange.c b/ext/-test-/string/coderange.c
index bc998ca372..4197ecca9f 100644
--- a/ext/-test-/string/coderange.c
+++ b/ext/-test-/string/coderange.c
@@ -8,13 +8,13 @@ coderange_int2sym(int coderange)
{
switch (coderange) {
case ENC_CODERANGE_7BIT:
- return sym_7bit;
+ return sym_7bit;
case ENC_CODERANGE_VALID:
- return sym_valid;
+ return sym_valid;
case ENC_CODERANGE_UNKNOWN:
- return sym_unknown;
+ return sym_unknown;
case ENC_CODERANGE_BROKEN:
- return sym_broken;
+ return sym_broken;
}
rb_bug("wrong condition of coderange");
UNREACHABLE_RETURN(Qnil);
diff --git a/ext/-test-/string/cstr.c b/ext/-test-/string/cstr.c
index 1eadb8b4fd..468ee7a3b1 100644
--- a/ext/-test-/string/cstr.c
+++ b/ext/-test-/string/cstr.c
@@ -42,11 +42,11 @@ bug_str_cstr_term_char(VALUE str)
len = rb_enc_mbminlen(enc);
c = rb_enc_precise_mbclen(s, s + len, enc);
if (!MBCLEN_CHARFOUND_P(c)) {
- c = (unsigned char)*s;
+ c = (unsigned char)*s;
}
else {
- c = rb_enc_mbc_to_codepoint(s, s + len, enc);
- if (!c) return Qnil;
+ c = rb_enc_mbc_to_codepoint(s, s + len, enc);
+ if (!c) return Qnil;
}
return rb_enc_uint_chr((unsigned int)c, enc);
}
@@ -65,14 +65,14 @@ bug_str_unterminated_substring(VALUE str, VALUE vbeg, VALUE vlen)
#if USE_RVARGC
RSTRING(str)->as.embed.len = (short)len;
#else
- RSTRING(str)->basic.flags &= ~RSTRING_EMBED_LEN_MASK;
- RSTRING(str)->basic.flags |= len << RSTRING_EMBED_LEN_SHIFT;
+ RSTRING(str)->basic.flags &= ~RSTRING_EMBED_LEN_MASK;
+ RSTRING(str)->basic.flags |= len << RSTRING_EMBED_LEN_SHIFT;
#endif
memmove(RSTRING(str)->as.embed.ary, RSTRING(str)->as.embed.ary + beg, len);
}
else {
- RSTRING(str)->as.heap.ptr += beg;
- RSTRING(str)->as.heap.len = len;
+ RSTRING(str)->as.heap.ptr += beg;
+ RSTRING(str)->as.heap.len = len;
}
return str;
}
@@ -104,7 +104,7 @@ bug_str_s_cstr_term_char(VALUE self, VALUE str)
const int term_fill_len = (termlen);\
*term_fill_ptr = '\0';\
if (UNLIKELY(term_fill_len > 1))\
- memset(term_fill_ptr, 0, term_fill_len);\
+ memset(term_fill_ptr, 0, term_fill_len);\
} while (0)
static VALUE
diff --git a/ext/-test-/string/fstring.c b/ext/-test-/string/fstring.c
index 2374319fe3..64f079251d 100644
--- a/ext/-test-/string/fstring.c
+++ b/ext/-test-/string/fstring.c
@@ -12,13 +12,13 @@ bug_s_fstring(VALUE self, VALUE str)
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 : RDATA(encoding)->data);
}
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 : RDATA(encoding)->data);
}
void
diff --git a/ext/-test-/string/qsort.c b/ext/-test-/string/qsort.c
index fb7ea3d8cb..4a641e74e3 100644
--- a/ext/-test-/string/qsort.c
+++ b/ext/-test-/string/qsort.c
@@ -35,22 +35,22 @@ bug_str_qsort_bang(int argc, VALUE *argv, VALUE str)
rb_scan_args(argc, argv, "03", &beg, &len, &size);
l = RSTRING_LEN(str);
if (!NIL_P(beg) && (b = NUM2INT(beg)) < 0 && (b += l) < 0) {
- rb_raise(rb_eArgError, "out of bounds");
+ rb_raise(rb_eArgError, "out of bounds");
}
if (!NIL_P(size) && (s = NUM2INT(size)) < 0) {
- rb_raise(rb_eArgError, "negative size");
+ rb_raise(rb_eArgError, "negative size");
}
if (NIL_P(len) ||
- (((n = NUM2INT(len)) < 0) ?
- (rb_raise(rb_eArgError, "negative length"), 0) :
- (b + n * s > l))) {
- n = (l - b) / s;
+ (((n = NUM2INT(len)) < 0) ?
+ (rb_raise(rb_eArgError, "negative length"), 0) :
+ (b + n * s > l))) {
+ n = (l - b) / s;
}
rb_str_modify(str);
d.enc = rb_enc_get(str);
d.elsize = s;
ruby_qsort(RSTRING_PTR(str) + b, n, s,
- rb_block_given_p() ? cmp_1 : cmp_2, &d);
+ rb_block_given_p() ? cmp_1 : cmp_2, &d);
return str;
}
diff --git a/ext/-test-/string/set_len.c b/ext/-test-/string/set_len.c
index 219cea404c..049da2cdb5 100644
--- a/ext/-test-/string/set_len.c
+++ b/ext/-test-/string/set_len.c
@@ -7,8 +7,18 @@ bug_str_set_len(VALUE str, VALUE len)
return str;
}
+static VALUE
+bug_str_append(VALUE str, VALUE addendum)
+{
+ StringValue(addendum);
+ rb_str_modify_expand(str, RSTRING_LEN(addendum));
+ memcpy(RSTRING_END(str), RSTRING_PTR(addendum), RSTRING_LEN(addendum));
+ 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);
}
diff --git a/ext/-test-/struct/member.c b/ext/-test-/struct/member.c
index 1d404039b4..f5400fe477 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/type.c b/ext/-test-/symbol/type.c
index e51e09eb26..8d0e647340 100644
--- a/ext/-test-/symbol/type.c
+++ b/ext/-test-/symbol/type.c
@@ -2,12 +2,12 @@
#ifdef HAVE_RB_IS_CONST_NAME
# define get_symbol_type(type, t, name) do { \
- ID id = rb_check_id(&name); \
- t = (id ? rb_is_##type##_id(id) : rb_is_##type##_name(name)); \
+ ID id = rb_check_id(&name); \
+ t = (id ? rb_is_##type##_id(id) : rb_is_##type##_name(name)); \
} while (0)
#else
# define get_symbol_type(type, t, name) do { \
- t = rb_is_##type##_id(rb_to_id(name)); \
+ t = rb_is_##type##_id(rb_to_id(name)); \
} while (0)
#endif
diff --git a/ext/-test-/tracepoint/gc_hook.c b/ext/-test-/tracepoint/gc_hook.c
index 5fd46fa518..a3f4e7f68a 100644
--- a/ext/-test-/tracepoint/gc_hook.c
+++ b/ext/-test-/tracepoint/gc_hook.c
@@ -28,12 +28,12 @@ static void
gc_start_end_i(VALUE tpval, void *data)
{
if (0) {
- rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
- fprintf(stderr, "trace: %s\n", rb_tracearg_event_flag(tparg) == RUBY_INTERNAL_EVENT_GC_START ? "gc_start" : "gc_end");
+ rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
+ fprintf(stderr, "trace: %s\n", rb_tracearg_event_flag(tparg) == RUBY_INTERNAL_EVENT_GC_START ? "gc_start" : "gc_end");
}
if (invoking == 0) {
- rb_postponed_job_register(0, invoke_proc, data);
+ rb_postponed_job_register(0, invoke_proc, data);
}
}
@@ -45,19 +45,19 @@ set_gc_hook(VALUE module, VALUE proc, rb_event_flag_t event, const char *tp_str,
/* disable previous keys */
if (rb_ivar_defined(module, tp_key) != 0 &&
- RTEST(tpval = rb_ivar_get(module, tp_key))) {
- rb_tracepoint_disable(tpval);
- rb_ivar_set(module, tp_key, Qnil);
+ RTEST(tpval = rb_ivar_get(module, tp_key))) {
+ rb_tracepoint_disable(tpval);
+ rb_ivar_set(module, tp_key, Qnil);
}
if (RTEST(proc)) {
- if (!rb_obj_is_proc(proc)) {
- rb_raise(rb_eTypeError, "trace_func needs to be Proc");
- }
+ if (!rb_obj_is_proc(proc)) {
+ 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_tracepoint_enable(tpval);
+ tpval = rb_tracepoint_new(0, event, gc_start_end_i, (void *)proc);
+ rb_ivar_set(module, tp_key, tpval);
+ rb_tracepoint_enable(tpval);
}
return proc;
@@ -67,7 +67,7 @@ static VALUE
set_after_gc_start(VALUE module, VALUE proc)
{
return set_gc_hook(module, proc, RUBY_INTERNAL_EVENT_GC_START,
- "__set_after_gc_start_tpval__", "__set_after_gc_start_proc__");
+ "__set_after_gc_start_tpval__", "__set_after_gc_start_proc__");
}
static VALUE
diff --git a/ext/-test-/tracepoint/tracepoint.c b/ext/-test-/tracepoint/tracepoint.c
index aa8c212f99..2826cc038c 100644
--- a/ext/-test-/tracepoint/tracepoint.c
+++ b/ext/-test-/tracepoint/tracepoint.c
@@ -21,35 +21,35 @@ tracepoint_track_objspace_events_i(VALUE tpval, void *data)
switch (rb_tracearg_event_flag(tparg)) {
case RUBY_INTERNAL_EVENT_NEWOBJ:
- {
- VALUE obj = rb_tracearg_object(tparg);
- if (track->objects_count < objects_max)
- track->objects[track->objects_count++] = obj;
- track->newobj_count++;
- break;
- }
+ {
+ VALUE obj = rb_tracearg_object(tparg);
+ if (track->objects_count < objects_max)
+ track->objects[track->objects_count++] = obj;
+ track->newobj_count++;
+ break;
+ }
case RUBY_INTERNAL_EVENT_FREEOBJ:
- {
- track->free_count++;
- break;
- }
+ {
+ track->free_count++;
+ break;
+ }
case RUBY_INTERNAL_EVENT_GC_START:
- {
- track->gc_start_count++;
- break;
- }
+ {
+ track->gc_start_count++;
+ break;
+ }
case RUBY_INTERNAL_EVENT_GC_END_MARK:
- {
- track->gc_end_mark_count++;
- break;
- }
+ {
+ track->gc_end_mark_count++;
+ break;
+ }
case RUBY_INTERNAL_EVENT_GC_END_SWEEP:
- {
- track->gc_end_sweep_count++;
- break;
- }
+ {
+ track->gc_end_sweep_count++;
+ break;
+ }
default:
- rb_raise(rb_eRuntimeError, "unknown event");
+ rb_raise(rb_eRuntimeError, "unknown event");
}
}
@@ -58,9 +58,9 @@ tracepoint_track_objspace_events(VALUE self)
{
struct tracepoint_track track = {0, 0, 0, 0, 0,};
VALUE tpval = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ | RUBY_INTERNAL_EVENT_FREEOBJ |
- RUBY_INTERNAL_EVENT_GC_START | RUBY_INTERNAL_EVENT_GC_END_MARK |
- RUBY_INTERNAL_EVENT_GC_END_SWEEP,
- tracepoint_track_objspace_events_i, &track);
+ RUBY_INTERNAL_EVENT_GC_START | RUBY_INTERNAL_EVENT_GC_END_MARK |
+ RUBY_INTERNAL_EVENT_GC_END_SWEEP,
+ tracepoint_track_objspace_events_i, &track);
VALUE result = rb_ary_new();
rb_tracepoint_enable(tpval);
diff --git a/ext/-test-/typeddata/typeddata.c b/ext/-test-/typeddata/typeddata.c
index 2adfd56ae8..cf3178fd18 100644
--- a/ext/-test-/typeddata/typeddata.c
+++ b/ext/-test-/typeddata/typeddata.c
@@ -27,7 +27,7 @@ test_make(VALUE klass, VALUE num)
unsigned long i, n = NUM2UINT(num);
for (i = 0; i < n; i++) {
- test_alloc(klass);
+ test_alloc(klass);
}
return Qnil;
diff --git a/ext/-test-/vm/at_exit.c b/ext/-test-/vm/at_exit.c
index 6cfbfafa9e..efc439b26a 100644
--- a/ext/-test-/vm/at_exit.c
+++ b/ext/-test-/vm/at_exit.c
@@ -23,14 +23,14 @@ register_at_exit(VALUE self, VALUE t)
{
switch (t) {
case Qtrue:
- ruby_vm_at_exit(print_begin);
- break;
+ ruby_vm_at_exit(print_begin);
+ break;
case Qfalse:
- ruby_vm_at_exit(print_end);
- break;
+ ruby_vm_at_exit(print_end);
+ break;
default:
- ruby_vm_at_exit(do_nothing);
- break;
+ ruby_vm_at_exit(do_nothing);
+ break;
}
return self;
}
diff --git a/ext/-test-/win32/console/attribute.c b/ext/-test-/win32/console/attribute.c
index e3e80a199f..b43ba23a5c 100644
--- a/ext/-test-/win32/console/attribute.c
+++ b/ext/-test-/win32/console/attribute.c
@@ -19,13 +19,13 @@ console_info(VALUE klass, VALUE io)
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (!GetConsoleScreenBufferInfo(h, &csbi))
- rb_syserr_fail(rb_w32_map_errno(GetLastError()), "not console");
+ rb_syserr_fail(rb_w32_map_errno(GetLastError()), "not console");
return rb_struct_new(rb_cConsoleScreenBufferInfo,
- INT2FIX(csbi.dwSize.X),
- INT2FIX(csbi.dwSize.Y),
- INT2FIX(csbi.dwCursorPosition.X),
- INT2FIX(csbi.dwCursorPosition.Y),
- INT2FIX(csbi.wAttributes));
+ INT2FIX(csbi.dwSize.X),
+ INT2FIX(csbi.dwSize.Y),
+ INT2FIX(csbi.dwCursorPosition.X),
+ INT2FIX(csbi.dwCursorPosition.Y),
+ INT2FIX(csbi.wAttributes));
}
static VALUE
@@ -44,9 +44,9 @@ void
Init_attribute(VALUE m)
{
rb_cConsoleScreenBufferInfo = rb_struct_define_under(m, "ConsoleScreenBufferInfo",
- "size_x", "size_y",
- "cur_x", "cur_y",
- "attr", NULL);
+ "size_x", "size_y",
+ "cur_x", "cur_y",
+ "attr", NULL);
rb_define_singleton_method(m, "console_info", console_info, 1);
rb_define_singleton_method(m, "console_attribute", console_set_attribute, 2);
diff --git a/ext/-test-/win32/fd_setsize/fd_setsize.c b/ext/-test-/win32/fd_setsize/fd_setsize.c
index 8da8b1eaa0..e29114a0ca 100644
--- a/ext/-test-/win32/fd_setsize/fd_setsize.c
+++ b/ext/-test-/win32/fd_setsize/fd_setsize.c
@@ -37,11 +37,11 @@ test_fdset(VALUE self)
FD_ZERO(&set);
for (i = 0; i < FD_SETSIZE * 2; i++) {
- int sd = socket(AF_INET, SOCK_DGRAM, 0);
- FD_SET(sd, &set);
- if (set.fd_count > FD_SETSIZE) {
- return Qfalse;
- }
+ int sd = socket(AF_INET, SOCK_DGRAM, 0);
+ FD_SET(sd, &set);
+ if (set.fd_count > FD_SETSIZE) {
+ return Qfalse;
+ }
}
return Qtrue;
}
diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c
index b3ef70a2c9..d6ea35c615 100644
--- a/ext/bigdecimal/bigdecimal.c
+++ b/ext/bigdecimal/bigdecimal.c
@@ -7,9 +7,7 @@
*/
/* #define BIGDECIMAL_DEBUG 1 */
-#ifdef BIGDECIMAL_DEBUG
-# define BIGDECIMAL_ENABLE_VPRINT 1
-#endif
+
#include "bigdecimal.h"
#include "ruby/util.h"
@@ -61,6 +59,13 @@ static ID id_to_r;
static ID id_eq;
static ID id_half;
+#define RBD_NUM_ROUNDING_MODES 11
+
+static struct {
+ ID id;
+ uint8_t mode;
+} rbd_rounding_modes[RBD_NUM_ROUNDING_MODES];
+
/* MACRO's to guard objects from GC by keeping them in stack */
#ifdef RBIMPL_ATTR_MAYBE_UNUSED
#define ENTER(n) RBIMPL_ATTR_MAYBE_UNUSED() volatile VALUE vStack[n];int iStack=0
@@ -102,10 +107,164 @@ static ID id_half;
# define RB_OBJ_STRING(obj) StringValueCStr(obj)
#endif
+#ifndef MAYBE_UNUSED
+# define MAYBE_UNUSED(x) x
+#endif
+
#define BIGDECIMAL_POSITIVE_P(bd) ((bd)->sign > 0)
#define BIGDECIMAL_NEGATIVE_P(bd) ((bd)->sign < 0)
/*
+ * ================== Memory allocation ============================
+ */
+
+#ifdef BIGDECIMAL_DEBUG
+static size_t rbd_allocation_count = 0; /* Memory allocation counter */
+static inline void
+atomic_allocation_count_inc(void)
+{
+ RUBY_ATOMIC_SIZE_INC(rbd_allocation_count);
+}
+static inline void
+atomic_allocation_count_dec_nounderflow(void)
+{
+ if (rbd_allocation_count == 0) return;
+ RUBY_ATOMIC_SIZE_DEC(rbd_allocation_count);
+}
+static void
+check_allocation_count_nonzero(void)
+{
+ if (rbd_allocation_count != 0) return;
+ rb_bug("[bigdecimal][rbd_free_struct] Too many memory free calls");
+}
+#else
+# define atomic_allocation_count_inc() /* nothing */
+# define atomic_allocation_count_dec_nounderflow() /* nothing */
+# define check_allocation_count_nonzero() /* nothing */
+#endif /* BIGDECIMAL_DEBUG */
+
+PUREFUNC(static inline size_t rbd_struct_size(size_t const));
+
+static inline size_t
+rbd_struct_size(size_t const internal_digits)
+{
+ size_t const frac_len = (internal_digits == 0) ? 1 : internal_digits;
+ return offsetof(Real, frac) + frac_len * sizeof(DECDIG);
+}
+
+static inline Real *
+rbd_allocate_struct(size_t const internal_digits)
+{
+ size_t const size = rbd_struct_size(internal_digits);
+ Real *real = ruby_xcalloc(1, size);
+ atomic_allocation_count_inc();
+ real->MaxPrec = internal_digits;
+ return real;
+}
+
+static size_t
+rbd_calculate_internal_digits(size_t const digits, bool limit_precision)
+{
+ size_t const len = roomof(digits, BASE_FIG);
+ if (limit_precision) {
+ size_t const prec_limit = VpGetPrecLimit();
+ if (prec_limit > 0) {
+ /* NOTE: 2 more digits for rounding and division */
+ size_t const max_len = roomof(prec_limit, BASE_FIG) + 2;
+ if (len > max_len)
+ return max_len;
+ }
+ }
+
+ return len;
+}
+
+static inline Real *
+rbd_allocate_struct_decimal_digits(size_t const decimal_digits, bool limit_precision)
+{
+ size_t const internal_digits = rbd_calculate_internal_digits(decimal_digits, limit_precision);
+ return rbd_allocate_struct(internal_digits);
+}
+
+static VALUE BigDecimal_wrap_struct(VALUE obj, Real *vp);
+
+static Real *
+rbd_reallocate_struct(Real *real, size_t const internal_digits)
+{
+ size_t const size = rbd_struct_size(internal_digits);
+ VALUE obj = real ? real->obj : 0;
+ Real *new_real = (Real *)ruby_xrealloc(real, size);
+ new_real->MaxPrec = internal_digits;
+ if (obj) {
+ new_real->obj = 0;
+ BigDecimal_wrap_struct(obj, new_real);
+ }
+ return new_real;
+}
+
+static void
+rbd_free_struct(Real *real)
+{
+ if (real != NULL) {
+ check_allocation_count_nonzero();
+ ruby_xfree(real);
+ atomic_allocation_count_dec_nounderflow();
+ }
+}
+
+#define NewZero rbd_allocate_struct_zero
+static Real *
+rbd_allocate_struct_zero(int sign, size_t const digits, bool limit_precision)
+{
+ Real *real = rbd_allocate_struct_decimal_digits(digits, limit_precision);
+ VpSetZero(real, sign);
+ return real;
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_limited(int sign, size_t const digits));
+#define NewZeroLimited rbd_allocate_struct_zero_limited
+static inline Real *
+rbd_allocate_struct_zero_limited(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_zero(sign, digits, true);
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_nolimit(int sign, size_t const digits));
+#define NewZeroNolimit rbd_allocate_struct_zero_nolimit
+static inline Real *
+rbd_allocate_struct_zero_nolimit(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_zero(sign, digits, false);
+}
+
+#define NewOne rbd_allocate_struct_one
+static Real *
+rbd_allocate_struct_one(int sign, size_t const digits, bool limit_precision)
+{
+ Real *real = rbd_allocate_struct_decimal_digits(digits, limit_precision);
+ VpSetOne(real);
+ if (sign < 0)
+ VpSetSign(real, VP_SIGN_NEGATIVE_FINITE);
+ return real;
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_limited(int sign, size_t const digits));
+#define NewOneLimited rbd_allocate_struct_one_limited
+static inline Real *
+rbd_allocate_struct_one_limited(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_one(sign, digits, true);
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_nolimit(int sign, size_t const digits));
+#define NewOneNolimit rbd_allocate_struct_one_nolimit
+static inline Real *
+rbd_allocate_struct_one_nolimit(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_one(sign, digits, false);
+}
+
+/*
* ================== Ruby Interface part ==========================
*/
#define DoSomeOne(x,y,f) rb_num_coerce_bin(x,y,f)
@@ -120,10 +279,7 @@ static VALUE VpCheckGetValue(Real *p);
static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v);
static int VpLimitRound(Real *c, size_t ixDigit);
static Real *VpCopy(Real *pv, Real const* const x);
-
-#ifdef BIGDECIMAL_ENABLE_VPRINT
static int VPrint(FILE *fp,const char *cntl_chr,Real *a);
-#endif
/*
* **** BigDecimal part ****
@@ -138,7 +294,7 @@ static VALUE BigDecimal_negative_zero(void);
static void
BigDecimal_delete(void *pv)
{
- VpFree(pv);
+ rbd_free_struct(pv);
}
static size_t
@@ -161,6 +317,60 @@ static const rb_data_type_t BigDecimal_data_type = {
#endif
};
+static Real *
+rbd_allocate_struct_zero_wrap_klass(VALUE klass, int sign, size_t const digits, bool limit_precision)
+{
+ Real *real = rbd_allocate_struct_zero(sign, digits, limit_precision);
+ if (real != NULL) {
+ VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0);
+ BigDecimal_wrap_struct(obj, real);
+ }
+ return real;
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits));
+#define NewZeroWrapLimited rbd_allocate_struct_zero_limited_wrap
+static inline Real *
+rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_zero_wrap_klass(rb_cBigDecimal, sign, digits, true);
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_nolimit_wrap(int sign, size_t const digits));
+#define NewZeroWrapNolimit rbd_allocate_struct_zero_nolimit_wrap
+static inline Real *
+rbd_allocate_struct_zero_nolimit_wrap(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_zero_wrap_klass(rb_cBigDecimal, sign, digits, false);
+}
+
+static Real *
+rbd_allocate_struct_one_wrap_klass(VALUE klass, int sign, size_t const digits, bool limit_precision)
+{
+ Real *real = rbd_allocate_struct_one(sign, digits, limit_precision);
+ if (real != NULL) {
+ VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0);
+ BigDecimal_wrap_struct(obj, real);
+ }
+ return real;
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_limited_wrap(int sign, size_t const digits));
+#define NewOneWrapLimited rbd_allocate_struct_one_limited_wrap
+static inline Real *
+rbd_allocate_struct_one_limited_wrap(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_one_wrap_klass(rb_cBigDecimal, sign, digits, true);
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_nolimit_wrap(int sign, size_t const digits));
+#define NewOneWrapNolimit rbd_allocate_struct_one_nolimit_wrap
+static inline Real *
+rbd_allocate_struct_one_nolimit_wrap(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_one_wrap_klass(rb_cBigDecimal, sign, digits, false);
+}
+
static inline int
is_kind_of_BigDecimal(VALUE const v)
{
@@ -214,7 +424,7 @@ GetVpValueWithPrec(VALUE v, long prec, int must)
case T_FIXNUM: {
char szD[128];
- sprintf(szD, "%ld", FIX2LONG(v));
+ snprintf(szD, 128, "%ld", FIX2LONG(v));
v = rb_cstr_convert_to_BigDecimal(szD, VpBaseFig() * 2 + 1, must);
break;
}
@@ -249,7 +459,7 @@ SomeOneMayDoIt:
return NULL; /* NULL means to coerce */
}
-static Real*
+static inline Real*
GetVpValue(VALUE v, int must)
{
return GetVpValueWithPrec(v, -1, must);
@@ -264,7 +474,7 @@ GetVpValue(VALUE v, int must)
* BigDecimal.double_fig # => 16
*
*/
-static VALUE
+static inline VALUE
BigDecimal_double_fig(VALUE self)
{
return INT2FIX(VpDblFig());
@@ -486,15 +696,15 @@ BigDecimal_precision_scale(VALUE self)
*
* Returns the number of decimal significant digits in +self+.
*
- * BigDecimal("0").scale # => 0
- * BigDecimal("1").scale # => 1
- * BigDecimal("1.1").scale # => 2
- * BigDecimal("3.1415").scale # => 5
- * BigDecimal("-1e20").precision # => 1
- * BigDecimal("1e-20").precision # => 1
- * BigDecimal("Infinity").scale # => 0
- * BigDecimal("-Infinity").scale # => 0
- * BigDecimal("NaN").scale # => 0
+ * BigDecimal("0").n_significant_digits # => 0
+ * BigDecimal("1").n_significant_digits # => 1
+ * BigDecimal("1.1").n_significant_digits # => 2
+ * BigDecimal("3.1415").n_significant_digits # => 5
+ * BigDecimal("-1e20").n_significant_digits # => 1
+ * BigDecimal("1e-20").n_significant_digits # => 1
+ * BigDecimal("Infinity").n_significant_digits # => 0
+ * BigDecimal("-Infinity").n_significant_digits # => 0
+ * BigDecimal("NaN").n_significant_digits # => 0
*/
static VALUE
BigDecimal_n_significant_digits(VALUE self)
@@ -573,13 +783,15 @@ BigDecimal_dump(int argc, VALUE *argv, VALUE self)
char *psz;
VALUE dummy;
volatile VALUE dump;
+ size_t len;
rb_scan_args(argc, argv, "01", &dummy);
GUARD_OBJ(vp,GetVpValue(self, 1));
dump = rb_str_new(0, VpNumOfChars(vp, "E")+50);
psz = RSTRING_PTR(dump);
- sprintf(psz, "%"PRIuSIZE":", VpMaxPrec(vp)*VpBaseFig());
- VpToString(vp, psz+strlen(psz), 0, 0);
+ snprintf(psz, RSTRING_LEN(dump), "%"PRIuSIZE":", VpMaxPrec(vp)*VpBaseFig());
+ len = strlen(psz);
+ VpToString(vp, psz+len, RSTRING_LEN(dump)-len, 0, 0);
rb_str_resize(dump, strlen(psz));
return dump;
}
@@ -623,18 +835,19 @@ check_rounding_mode_option(VALUE const opts)
assert(RB_TYPE_P(opts, T_HASH));
if (NIL_P(opts))
- goto noopt;
+ goto no_opt;
mode = rb_hash_lookup2(opts, ID2SYM(id_half), Qundef);
if (mode == Qundef || NIL_P(mode))
- goto noopt;
+ goto no_opt;
if (SYMBOL_P(mode))
mode = rb_sym2str(mode);
else if (!RB_TYPE_P(mode, T_STRING)) {
- VALUE str_mode = rb_check_string_type(mode);
- if (NIL_P(str_mode)) goto invalid;
- mode = str_mode;
+ VALUE str_mode = rb_check_string_type(mode);
+ if (NIL_P(str_mode))
+ goto invalid;
+ mode = str_mode;
}
s = RSTRING_PTR(mode);
l = RSTRING_LEN(mode);
@@ -652,13 +865,11 @@ check_rounding_mode_option(VALUE const opts)
default:
break;
}
+
invalid:
- if (NIL_P(mode))
- rb_raise(rb_eArgError, "invalid rounding mode: nil");
- else
- rb_raise(rb_eArgError, "invalid rounding mode: %"PRIsVALUE, mode);
+ rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", mode);
- noopt:
+ no_opt:
return VpGetRoundMode();
}
@@ -667,34 +878,23 @@ check_rounding_mode(VALUE const v)
{
unsigned short sw;
ID id;
- switch (TYPE(v)) {
- case T_SYMBOL:
- id = SYM2ID(v);
- if (id == id_up)
- return VP_ROUND_UP;
- if (id == id_down || id == id_truncate)
- return VP_ROUND_DOWN;
- if (id == id_half_up || id == id_default)
- return VP_ROUND_HALF_UP;
- if (id == id_half_down)
- return VP_ROUND_HALF_DOWN;
- if (id == id_half_even || id == id_banker)
- return VP_ROUND_HALF_EVEN;
- if (id == id_ceiling || id == id_ceil)
- return VP_ROUND_CEIL;
- if (id == id_floor)
- return VP_ROUND_FLOOR;
- rb_raise(rb_eArgError, "invalid rounding mode");
-
- default:
- break;
+ if (RB_TYPE_P(v, T_SYMBOL)) {
+ int i;
+ id = SYM2ID(v);
+ for (i = 0; i < RBD_NUM_ROUNDING_MODES; ++i) {
+ if (rbd_rounding_modes[i].id == id) {
+ return rbd_rounding_modes[i].mode;
+ }
+ }
+ rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", v);
}
-
- sw = NUM2USHORT(v);
- if (!VpIsRoundMode(sw)) {
- rb_raise(rb_eArgError, "invalid rounding mode");
+ else {
+ sw = NUM2USHORT(v);
+ if (!VpIsRoundMode(sw)) {
+ rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", v);
+ }
+ return sw;
}
- return sw;
}
/* call-seq:
@@ -933,11 +1133,17 @@ GetAddSubPrec(Real *a, Real *b)
return mx;
}
-static SIGNED_VALUE
-GetPrecisionInt(VALUE v)
+static inline SIGNED_VALUE
+check_int_precision(VALUE v)
{
SIGNED_VALUE n;
- n = NUM2INT(v);
+#if SIZEOF_VALUE <= SIZEOF_LONG
+ n = (SIGNED_VALUE)NUM2LONG(v);
+#elif SIZEOF_VALUE <= SIZEOF_LONG_LONG
+ n = (SIGNED_VALUE)NUM2LL(v);
+#else
+# error SIZEOF_VALUE is too large
+#endif
if (n < 0) {
rb_raise(rb_eArgError, "negative precision");
}
@@ -979,26 +1185,12 @@ VpCreateRbObject(size_t mx, const char *str, bool raise_exception)
return VpNewRbClass(mx, str, rb_cBigDecimal, true, raise_exception);
}
-#define VpAllocReal(prec) (Real *)VpMemAlloc(offsetof(Real, frac) + (prec) * sizeof(DECDIG))
-
-static Real *
-VpReallocReal(Real *pv, size_t prec)
-{
- VALUE obj = pv ? pv->obj : 0;
- Real *new_pv = (Real *)VpMemRealloc(pv, offsetof(Real, frac) + prec * sizeof(DECDIG));
- if (obj) {
- new_pv->obj = 0;
- BigDecimal_wrap_struct(obj, new_pv);
- }
- return new_pv;
-}
-
static Real *
VpCopy(Real *pv, Real const* const x)
{
assert(x != NULL);
- pv = VpReallocReal(pv, x->MaxPrec);
+ pv = rbd_reallocate_struct(pv, x->MaxPrec);
pv->MaxPrec = x->MaxPrec;
pv->Prec = x->Prec;
pv->exponent = x->exponent;
@@ -1119,7 +1311,7 @@ BigDecimal_to_f(VALUE self)
str = rb_str_new(0, VpNumOfChars(p, "E"));
buf = RSTRING_PTR(str);
- VpToString(p, buf, 0, 0);
+ VpToString(p, buf, RSTRING_LEN(str), 0, 0);
errno = 0;
d = strtod(buf, 0);
if (errno == ERANGE) {
@@ -1276,17 +1468,17 @@ BigDecimal_add(VALUE self, VALUE r)
mx = GetAddSubPrec(a, b);
if (mx == (size_t)-1L) {
- GUARD_OBJ(c, VpCreateRbObject(VpBaseFig() + 1, "0", true));
- VpAddSub(c, a, b, 1);
+ GUARD_OBJ(c, NewZeroWrapLimited(1, VpBaseFig() + 1));
+ VpAddSub(c, a, b, 1);
}
else {
- GUARD_OBJ(c, VpCreateRbObject(mx * (VpBaseFig() + 1), "0", true));
- if(!mx) {
- VpSetInf(c, VpGetSign(a));
- }
- else {
- VpAddSub(c, a, b, 1);
- }
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx * (VpBaseFig() + 1)));
+ if (!mx) {
+ VpSetInf(c, VpGetSign(a));
+ }
+ else {
+ VpAddSub(c, a, b, 1);
+ }
}
return VpCheckGetValue(c);
}
@@ -1331,17 +1523,17 @@ BigDecimal_sub(VALUE self, VALUE r)
mx = GetAddSubPrec(a,b);
if (mx == (size_t)-1L) {
- GUARD_OBJ(c, VpCreateRbObject(VpBaseFig() + 1, "0", true));
- VpAddSub(c, a, b, -1);
+ GUARD_OBJ(c, NewZeroWrapLimited(1, VpBaseFig() + 1));
+ VpAddSub(c, a, b, -1);
}
else {
- GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0", true));
- if (!mx) {
- VpSetInf(c,VpGetSign(a));
- }
- else {
- VpAddSub(c, a, b, -1);
- }
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx *(VpBaseFig() + 1)));
+ if (!mx) {
+ VpSetInf(c,VpGetSign(a));
+ }
+ else {
+ VpAddSub(c, a, b, -1);
+ }
}
return VpCheckGetValue(c);
}
@@ -1581,7 +1773,7 @@ BigDecimal_neg(VALUE self)
ENTER(5);
Real *c, *a;
GUARD_OBJ(a, GetVpValue(self, 1));
- GUARD_OBJ(c, VpCreateRbObject(a->Prec *(VpBaseFig() + 1), "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, a->Prec *(VpBaseFig() + 1)));
VpAsgn(c, a, -1);
return VpCheckGetValue(c);
}
@@ -1608,7 +1800,7 @@ BigDecimal_mult(VALUE self, VALUE r)
SAVE(b);
mx = a->Prec + b->Prec;
- GUARD_OBJ(c, VpCreateRbObject(mx *(VpBaseFig() + 1), "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx * (VpBaseFig() + 1)));
VpMult(c, a, b);
return VpCheckGetValue(c);
}
@@ -1655,8 +1847,8 @@ BigDecimal_divide(VALUE self, VALUE r, Real **c, Real **res, Real **div)
if (2*BIGDECIMAL_DOUBLE_FIGURES > mx)
mx = 2*BIGDECIMAL_DOUBLE_FIGURES;
- GUARD_OBJ((*c), VpCreateRbObject(mx + 2*BASE_FIG, "#0", true));
- GUARD_OBJ((*res), VpCreateRbObject((mx + 1)*2 + 2*BASE_FIG, "#0", true));
+ GUARD_OBJ((*c), NewZeroWrapNolimit(1, mx + 2*BASE_FIG));
+ GUARD_OBJ((*res), NewZeroWrapNolimit(1, (mx + 1)*2 + 2*BASE_FIG));
VpDivd(*c, *res, a, b);
return Qnil;
@@ -1721,7 +1913,7 @@ BigDecimal_quo(int argc, VALUE *argv, VALUE self)
argc = rb_scan_args(argc, argv, "11", &value, &digits);
if (argc > 1) {
- n = GetPrecisionInt(digits);
+ n = check_int_precision(digits);
}
if (n > 0) {
@@ -1811,12 +2003,12 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod)
if (2*BIGDECIMAL_DOUBLE_FIGURES > mx)
mx = 2*BIGDECIMAL_DOUBLE_FIGURES;
- GUARD_OBJ(c, VpCreateRbObject(mx + 2*BASE_FIG, "0", true));
- GUARD_OBJ(res, VpCreateRbObject(mx*2 + 2*BASE_FIG, "#0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx + 2*BASE_FIG));
+ GUARD_OBJ(res, NewZeroWrapNolimit(1, mx*2 + 2*BASE_FIG));
VpDivd(c, res, a, b);
mx = c->Prec * BASE_FIG;
- GUARD_OBJ(d, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(d, NewZeroWrapLimited(1, mx));
VpActiveRound(d, c, VP_ROUND_DOWN, 0);
VpMult(res, d, b);
@@ -1824,10 +2016,10 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod)
if (!VpIsZero(c) && (VpGetSign(a) * VpGetSign(b) < 0)) {
/* result adjustment for negative case */
- res = VpReallocReal(res, d->MaxPrec);
+ res = rbd_reallocate_struct(res, d->MaxPrec);
res->MaxPrec = d->MaxPrec;
VpAddSub(res, d, VpOne(), -1);
- GUARD_OBJ(d, VpCreateRbObject(GetAddSubPrec(c, b) * 2*BASE_FIG, "0", true));
+ GUARD_OBJ(d, NewZeroWrapLimited(1, GetAddSubPrec(c, b) * 2*BASE_FIG));
VpAddSub(d, c, b, 1);
*div = res;
*mod = d;
@@ -1891,17 +2083,17 @@ BigDecimal_divremain(VALUE self, VALUE r, Real **dv, Real **rv)
SAVE(b);
mx = (a->MaxPrec + b->MaxPrec) *VpBaseFig();
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
- GUARD_OBJ(res, VpCreateRbObject((mx+1) * 2 + (VpBaseFig() + 1), "#0", true));
- GUARD_OBJ(rr, VpCreateRbObject((mx+1) * 2 + (VpBaseFig() + 1), "#0", true));
- GUARD_OBJ(ff, VpCreateRbObject((mx+1) * 2 + (VpBaseFig() + 1), "#0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
+ GUARD_OBJ(res, NewZeroWrapNolimit(1, (mx+1) * 2 + (VpBaseFig() + 1)));
+ GUARD_OBJ(rr, NewZeroWrapNolimit(1, (mx+1) * 2 + (VpBaseFig() + 1)));
+ GUARD_OBJ(ff, NewZeroWrapNolimit(1, (mx+1) * 2 + (VpBaseFig() + 1)));
VpDivd(c, res, a, b);
mx = c->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(d, VpCreateRbObject(mx, "0", true));
- GUARD_OBJ(f, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(d, NewZeroWrapLimited(1, mx));
+ GUARD_OBJ(f, NewZeroWrapLimited(1, mx));
VpActiveRound(d, c, VP_ROUND_DOWN, 0); /* 0: round off */
@@ -1986,7 +2178,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
}
/* div in BigDecimal sense */
- ix = GetPrecisionInt(n);
+ ix = check_int_precision(n);
if (ix == 0) {
return BigDecimal_div(self, b);
}
@@ -1997,7 +2189,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
size_t b_prec = ix;
size_t pl = VpSetPrecLimit(0);
- GUARD_OBJ(cv, VpCreateRbObject(mx + VpBaseFig(), "0", true));
+ GUARD_OBJ(cv, NewZeroWrapLimited(1, mx + VpBaseFig()));
GUARD_OBJ(av, GetVpValue(self, 1));
/* TODO: I want to refactor this precision control for a float value later
* by introducing an implicit conversion function instead of
@@ -2008,7 +2200,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
GUARD_OBJ(bv, GetVpValueWithPrec(b, b_prec, 1));
mx = av->Prec + bv->Prec + 2;
if (mx <= cv->MaxPrec) mx = cv->MaxPrec + 1;
- GUARD_OBJ(res, VpCreateRbObject((mx * 2 + 2)*VpBaseFig(), "#0", true));
+ GUARD_OBJ(res, NewZeroWrapNolimit(1, (mx * 2 + 2)*VpBaseFig()));
VpDivd(cv, res, av, bv);
VpSetPrecLimit(pl);
VpLeftRound(cv, VpGetRoundMode(), ix);
@@ -2091,7 +2283,7 @@ BigDecimal_add2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
- SIGNED_VALUE mx = GetPrecisionInt(n);
+ SIGNED_VALUE mx = check_int_precision(n);
if (mx == 0) return BigDecimal_add(self, b);
else {
size_t pl = VpSetPrecLimit(0);
@@ -2121,7 +2313,7 @@ BigDecimal_sub2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
- SIGNED_VALUE mx = GetPrecisionInt(n);
+ SIGNED_VALUE mx = check_int_precision(n);
if (mx == 0) return BigDecimal_sub(self, b);
else {
size_t pl = VpSetPrecLimit(0);
@@ -2164,7 +2356,7 @@ BigDecimal_mult2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
- SIGNED_VALUE mx = GetPrecisionInt(n);
+ SIGNED_VALUE mx = check_int_precision(n);
if (mx == 0) return BigDecimal_mult(self, b);
else {
size_t pl = VpSetPrecLimit(0);
@@ -2196,7 +2388,7 @@ BigDecimal_abs(VALUE self)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpAsgn(c, a, 1);
VpChangeSign(c, 1);
return VpCheckGetValue(c);
@@ -2219,9 +2411,10 @@ BigDecimal_sqrt(VALUE self, VALUE nFig)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- n = GetPrecisionInt(nFig) + VpDblFig() + BASE_FIG;
+ n = check_int_precision(nFig);
+ n += VpDblFig() + VpBaseFig();
if (mx <= n) mx = n;
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSqrt(c, a);
return VpCheckGetValue(c);
}
@@ -2237,7 +2430,7 @@ BigDecimal_fix(VALUE self)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpActiveRound(c, a, VP_ROUND_DOWN, 0); /* 0: round off */
return VpCheckGetValue(c);
}
@@ -2310,7 +2503,7 @@ BigDecimal_round(int argc, VALUE *argv, VALUE self)
pl = VpSetPrecLimit(0);
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSetPrecLimit(pl);
VpActiveRound(c, a, sw, iLoc);
if (round_to_int) {
@@ -2356,7 +2549,7 @@ BigDecimal_truncate(int argc, VALUE *argv, VALUE self)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSetPrecLimit(pl);
VpActiveRound(c, a, VP_ROUND_DOWN, iLoc); /* 0: truncate */
if (argc == 0) {
@@ -2376,7 +2569,7 @@ BigDecimal_frac(VALUE self)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpFrac(c, a);
return VpCheckGetValue(c);
}
@@ -2416,7 +2609,7 @@ BigDecimal_floor(int argc, VALUE *argv, VALUE self)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSetPrecLimit(pl);
VpActiveRound(c, a, VP_ROUND_FLOOR, iLoc);
#ifdef BIGDECIMAL_DEBUG
@@ -2462,7 +2655,7 @@ BigDecimal_ceil(int argc, VALUE *argv, VALUE self)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSetPrecLimit(pl);
VpActiveRound(c, a, VP_ROUND_CEIL, iLoc);
if (argc == 0) {
@@ -2566,10 +2759,10 @@ BigDecimal_to_s(int argc, VALUE *argv, VALUE self)
psz = RSTRING_PTR(str);
if (fmt) {
- VpToFString(vp, psz, mc, fPlus);
+ VpToFString(vp, psz, RSTRING_LEN(str), mc, fPlus);
}
else {
- VpToString (vp, psz, mc, fPlus);
+ VpToString (vp, psz, RSTRING_LEN(str), mc, fPlus);
}
rb_str_resize(str, strlen(psz));
return str;
@@ -2611,7 +2804,7 @@ BigDecimal_split(VALUE self)
GUARD_OBJ(vp, GetVpValue(self, 1));
str = rb_str_new(0, VpNumOfChars(vp, "E"));
psz1 = RSTRING_PTR(str);
- VpSzMantissa(vp, psz1);
+ VpSzMantissa(vp, psz1, RSTRING_LEN(str));
s = 1;
if(psz1[0] == '-') {
size_t len = strlen(psz1 + 1);
@@ -2660,7 +2853,7 @@ BigDecimal_inspect(VALUE self)
nc = VpNumOfChars(vp, "E");
str = rb_str_new(0, nc);
- VpToString(vp, RSTRING_PTR(str), 0, 0);
+ VpToString(vp, RSTRING_PTR(str), RSTRING_LEN(str), 0, 0);
rb_str_resize(str, strlen(RSTRING_PTR(str)));
return str;
}
@@ -2770,7 +2963,7 @@ bigdecimal_power_by_bigdecimal(Real const* x, Real const* exp, ssize_t const n)
volatile VALUE obj = exp->obj;
if (VpIsZero(exp)) {
- return VpCheckGetValue(VpCreateRbObject(n, "1", true));
+ return VpCheckGetValue(NewOneWrapLimited(1, n));
}
log_x = BigMath_log(x->obj, SSIZET2NUM(n+1));
@@ -2808,9 +3001,9 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
n = NIL_P(prec) ? (ssize_t)(x->Prec*VpBaseFig()) : NUM2SSIZET(prec);
if (VpIsNaN(x)) {
- y = VpCreateRbObject(n, "0", true);
- RB_GC_GUARD(y->obj);
- VpSetNaN(y);
+ y = NewZeroWrapLimited(1, n);
+ VpSetNaN(y);
+ RB_GC_GUARD(y->obj);
return VpCheckGetValue(y);
}
@@ -2879,136 +3072,126 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
}
if (VpIsZero(x)) {
- if (is_negative(vexp)) {
- y = VpCreateRbObject(n, "#0", true);
- RB_GC_GUARD(y->obj);
- if (BIGDECIMAL_NEGATIVE_P(x)) {
- if (is_integer(vexp)) {
- if (is_even(vexp)) {
- /* (-0) ** (-even_integer) -> Infinity */
- VpSetPosInf(y);
- }
- else {
- /* (-0) ** (-odd_integer) -> -Infinity */
- VpSetNegInf(y);
- }
- }
- else {
- /* (-0) ** (-non_integer) -> Infinity */
- VpSetPosInf(y);
- }
- }
- else {
- /* (+0) ** (-num) -> Infinity */
- VpSetPosInf(y);
- }
+ if (is_negative(vexp)) {
+ y = NewZeroWrapNolimit(1, n);
+ if (BIGDECIMAL_NEGATIVE_P(x)) {
+ if (is_integer(vexp)) {
+ if (is_even(vexp)) {
+ /* (-0) ** (-even_integer) -> Infinity */
+ VpSetPosInf(y);
+ }
+ else {
+ /* (-0) ** (-odd_integer) -> -Infinity */
+ VpSetNegInf(y);
+ }
+ }
+ else {
+ /* (-0) ** (-non_integer) -> Infinity */
+ VpSetPosInf(y);
+ }
+ }
+ else {
+ /* (+0) ** (-num) -> Infinity */
+ VpSetPosInf(y);
+ }
+ RB_GC_GUARD(y->obj);
return VpCheckGetValue(y);
- }
- else if (is_zero(vexp)) {
- return VpCheckGetValue(VpCreateRbObject(n, "1", true));
- }
- else {
- return VpCheckGetValue(VpCreateRbObject(n, "0", true));
- }
+ }
+ else if (is_zero(vexp)) {
+ return VpCheckGetValue(NewOneWrapLimited(1, n));
+ }
+ else {
+ return VpCheckGetValue(NewZeroWrapLimited(1, n));
+ }
}
if (is_zero(vexp)) {
- return VpCheckGetValue(VpCreateRbObject(n, "1", true));
+ return VpCheckGetValue(NewOneWrapLimited(1, n));
}
else if (is_one(vexp)) {
- return self;
+ return self;
}
if (VpIsInf(x)) {
- if (is_negative(vexp)) {
- if (BIGDECIMAL_NEGATIVE_P(x)) {
- if (is_integer(vexp)) {
- if (is_even(vexp)) {
- /* (-Infinity) ** (-even_integer) -> +0 */
- return VpCheckGetValue(VpCreateRbObject(n, "0", true));
- }
- else {
- /* (-Infinity) ** (-odd_integer) -> -0 */
- return VpCheckGetValue(VpCreateRbObject(n, "-0", true));
- }
- }
- else {
- /* (-Infinity) ** (-non_integer) -> -0 */
- return VpCheckGetValue(VpCreateRbObject(n, "-0", true));
- }
- }
- else {
- return VpCheckGetValue(VpCreateRbObject(n, "0", true));
- }
- }
- else {
- y = VpCreateRbObject(n, "0", true);
- if (BIGDECIMAL_NEGATIVE_P(x)) {
- if (is_integer(vexp)) {
- if (is_even(vexp)) {
- VpSetPosInf(y);
- }
- else {
- VpSetNegInf(y);
- }
- }
- else {
- /* TODO: support complex */
- rb_raise(rb_eMathDomainError,
- "a non-integral exponent for a negative base");
- }
- }
- else {
- VpSetPosInf(y);
- }
+ if (is_negative(vexp)) {
+ if (BIGDECIMAL_NEGATIVE_P(x)) {
+ if (is_integer(vexp)) {
+ if (is_even(vexp)) {
+ /* (-Infinity) ** (-even_integer) -> +0 */
+ return VpCheckGetValue(NewZeroWrapLimited(1, n));
+ }
+ else {
+ /* (-Infinity) ** (-odd_integer) -> -0 */
+ return VpCheckGetValue(NewZeroWrapLimited(-1, n));
+ }
+ }
+ else {
+ /* (-Infinity) ** (-non_integer) -> -0 */
+ return VpCheckGetValue(NewZeroWrapLimited(-1, n));
+ }
+ }
+ else {
+ return VpCheckGetValue(NewZeroWrapLimited(1, n));
+ }
+ }
+ else {
+ y = NewZeroWrapLimited(1, n);
+ if (BIGDECIMAL_NEGATIVE_P(x)) {
+ if (is_integer(vexp)) {
+ if (is_even(vexp)) {
+ VpSetPosInf(y);
+ }
+ else {
+ VpSetNegInf(y);
+ }
+ }
+ else {
+ /* TODO: support complex */
+ rb_raise(rb_eMathDomainError,
+ "a non-integral exponent for a negative base");
+ }
+ }
+ else {
+ VpSetPosInf(y);
+ }
return VpCheckGetValue(y);
- }
+ }
}
if (exp != NULL) {
- return bigdecimal_power_by_bigdecimal(x, exp, n);
+ return bigdecimal_power_by_bigdecimal(x, exp, n);
}
else if (RB_TYPE_P(vexp, T_BIGNUM)) {
- VALUE abs_value = BigDecimal_abs(self);
- if (is_one(abs_value)) {
- return VpCheckGetValue(VpCreateRbObject(n, "1", true));
- }
- else if (RTEST(rb_funcall(abs_value, '<', 1, INT2FIX(1)))) {
- if (is_negative(vexp)) {
- y = VpCreateRbObject(n, "0", true);
- if (is_even(vexp)) {
- VpSetInf(y, VpGetSign(x));
- }
- else {
- VpSetInf(y, -VpGetSign(x));
- }
+ VALUE abs_value = BigDecimal_abs(self);
+ if (is_one(abs_value)) {
+ return VpCheckGetValue(NewOneWrapLimited(1, n));
+ }
+ else if (RTEST(rb_funcall(abs_value, '<', 1, INT2FIX(1)))) {
+ if (is_negative(vexp)) {
+ y = NewZeroWrapLimited(1, n);
+ VpSetInf(y, (is_even(vexp) ? 1 : -1) * VpGetSign(x));
return VpCheckGetValue(y);
- }
- else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
- return VpCheckGetValue(VpCreateRbObject(n, "-0", true));
- }
- else {
- return VpCheckGetValue(VpCreateRbObject(n, "0", true));
- }
- }
- else {
- if (is_positive(vexp)) {
- y = VpCreateRbObject(n, "0", true);
- if (is_even(vexp)) {
- VpSetInf(y, VpGetSign(x));
- }
- else {
- VpSetInf(y, -VpGetSign(x));
- }
+ }
+ else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
+ return VpCheckGetValue(NewZeroWrapLimited(-1, n));
+ }
+ else {
+ return VpCheckGetValue(NewZeroWrapLimited(1, n));
+ }
+ }
+ else {
+ if (is_positive(vexp)) {
+ y = NewZeroWrapLimited(1, n);
+ VpSetInf(y, (is_even(vexp) ? 1 : -1) * VpGetSign(x));
return VpCheckGetValue(y);
- }
- else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
- return VpCheckGetValue(VpCreateRbObject(n, "-0", true));
- }
- else {
- return VpCheckGetValue(VpCreateRbObject(n, "0", true));
- }
- }
+ }
+ else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
+ return VpCheckGetValue(NewZeroWrapLimited(-1, n));
+ }
+ else {
+ return VpCheckGetValue(NewZeroWrapLimited(1, n));
+ }
+ }
}
int_exp = FIX2LONG(vexp);
@@ -3017,15 +3200,15 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
if (ma == 0) ma = 1;
if (VpIsDef(x)) {
- mp = x->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(y, VpCreateRbObject(mp * (ma + 1), "0", true));
+ mp = x->Prec * (VpBaseFig() + 1);
+ GUARD_OBJ(y, NewZeroWrapLimited(1, mp * (ma + 1)));
}
else {
- GUARD_OBJ(y, VpCreateRbObject(1, "0", true));
+ GUARD_OBJ(y, NewZeroWrapLimited(1, 1));
}
VpPowerByInt(y, x, int_exp);
if (!NIL_P(prec) && VpIsDef(y)) {
- VpMidRound(y, VpGetRoundMode(), n);
+ VpMidRound(y, VpGetRoundMode(), n);
}
return VpCheckGetValue(y);
}
@@ -3114,7 +3297,7 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval, RB_UNUSED_VAR(size_t digs), int r
Real *vp;
if (uval == 0) {
- vp = VpAllocReal(1);
+ vp = rbd_allocate_struct(1);
vp->MaxPrec = 1;
vp->Prec = 1;
vp->exponent = 1;
@@ -3122,7 +3305,7 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval, RB_UNUSED_VAR(size_t digs), int r
vp->frac[0] = 0;
}
else if (uval < BASE) {
- vp = VpAllocReal(1);
+ vp = rbd_allocate_struct(1);
vp->MaxPrec = 1;
vp->Prec = 1;
vp->exponent = 1;
@@ -3148,7 +3331,7 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval, RB_UNUSED_VAR(size_t digs), int r
}
const size_t exp = len + ntz;
- vp = VpAllocReal(len);
+ vp = rbd_allocate_struct(len);
vp->MaxPrec = len;
vp->Prec = len;
vp->exponent = exp;
@@ -3500,6 +3683,9 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
* in the value, the result is rounded to that number of digits,
* according to the current rounding mode; see BigDecimal.mode.
*
+ * When +ndigits+ is 0, the number of digits to correctly represent a float number
+ * is determined automatically.
+ *
* Returns +value+ converted to a \BigDecimal, depending on the type of +value+:
*
* - Integer, Float, Rational, Complex, or BigDecimal: converted directly:
@@ -3606,8 +3792,10 @@ BigDecimal_limit(int argc, VALUE *argv, VALUE self)
/* Returns the sign of the value.
*
- * Returns a positive value if > 0, a negative value if < 0, and a
- * zero if == 0.
+ * Returns a positive value if > 0, a negative value if < 0.
+ * It behaves the same with zeros -
+ * it returns a positive value for a positive zero (BigDecimal('0')) and
+ * a negative value for a negative zero (BigDecimal('-0')).
*
* The specific value returned indicates the type and sign of the BigDecimal,
* as follows:
@@ -3771,18 +3959,16 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec)
return VpCheckGetValue(GetVpValueWithPrec(INT2FIX(0), prec, 1));
}
else {
- Real* vy;
- vy = VpCreateRbObject(prec, "#0", true);
+ Real* vy = NewZeroWrapNolimit(1, prec);
VpSetInf(vy, VP_SIGN_POSITIVE_INFINITE);
RB_GC_GUARD(vy->obj);
return VpCheckGetValue(vy);
}
}
else if (nan) {
- Real* vy;
- vy = VpCreateRbObject(prec, "#0", true);
- VpSetNaN(vy);
- RB_GC_GUARD(vy->obj);
+ Real* vy = NewZeroWrapNolimit(1, prec);
+ VpSetNaN(vy);
+ RB_GC_GUARD(vy->obj);
return VpCheckGetValue(vy);
}
else if (vx == NULL) {
@@ -3800,7 +3986,7 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec)
VpSetSign(vx, 1);
}
- one = VpCheckGetValue(VpCreateRbObject(1, "1", true));
+ one = VpCheckGetValue(NewOneWrapLimited(1, 1));
y = one;
d = y;
i = 1;
@@ -3927,15 +4113,13 @@ get_vp_value:
break;
}
if (infinite && !negative) {
- Real* vy;
- vy = VpCreateRbObject(prec, "#0", true);
+ Real *vy = NewZeroWrapNolimit(1, prec);
RB_GC_GUARD(vy->obj);
VpSetInf(vy, VP_SIGN_POSITIVE_INFINITE);
return VpCheckGetValue(vy);
}
else if (nan) {
- Real* vy;
- vy = VpCreateRbObject(prec, "#0", true);
+ Real* vy = NewZeroWrapNolimit(1, prec);
RB_GC_GUARD(vy->obj);
VpSetNaN(vy);
return VpCheckGetValue(vy);
@@ -3949,7 +4133,7 @@ get_vp_value:
}
x = VpCheckGetValue(vx);
- RB_GC_GUARD(one) = VpCheckGetValue(VpCreateRbObject(1, "1", true));
+ RB_GC_GUARD(one) = VpCheckGetValue(NewOneWrapLimited(1, 1));
RB_GC_GUARD(two) = VpCheckGetValue(VpCreateRbObject(1, "2", true));
n = prec + BIGDECIMAL_DOUBLE_FIGURES;
@@ -4414,20 +4598,31 @@ Init_bigdecimal(void)
rb_define_singleton_method(rb_mBigMath, "exp", BigMath_s_exp, 2);
rb_define_singleton_method(rb_mBigMath, "log", BigMath_s_log, 2);
- id_up = rb_intern_const("up");
- id_down = rb_intern_const("down");
- id_truncate = rb_intern_const("truncate");
- id_half_up = rb_intern_const("half_up");
- id_default = rb_intern_const("default");
- id_half_down = rb_intern_const("half_down");
- id_half_even = rb_intern_const("half_even");
- id_banker = rb_intern_const("banker");
- id_ceiling = rb_intern_const("ceiling");
- id_ceil = rb_intern_const("ceil");
- id_floor = rb_intern_const("floor");
+#define ROUNDING_MODE(i, name, value) \
+ id_##name = rb_intern_const(#name); \
+ rbd_rounding_modes[i].id = id_##name; \
+ rbd_rounding_modes[i].mode = value;
+
+ ROUNDING_MODE(0, up, RBD_ROUND_UP);
+ ROUNDING_MODE(1, down, RBD_ROUND_DOWN);
+ ROUNDING_MODE(2, half_up, RBD_ROUND_HALF_UP);
+ ROUNDING_MODE(3, half_down, RBD_ROUND_HALF_DOWN);
+ ROUNDING_MODE(4, ceil, RBD_ROUND_CEIL);
+ ROUNDING_MODE(5, floor, RBD_ROUND_FLOOR);
+ ROUNDING_MODE(6, half_even, RBD_ROUND_HALF_EVEN);
+
+ ROUNDING_MODE(7, default, RBD_ROUND_DEFAULT);
+ ROUNDING_MODE(8, truncate, RBD_ROUND_TRUNCATE);
+ ROUNDING_MODE(9, banker, RBD_ROUND_BANKER);
+ ROUNDING_MODE(10, ceiling, RBD_ROUND_CEILING);
+
+#undef ROUNDING_MODE
+
id_to_r = rb_intern_const("to_r");
id_eq = rb_intern_const("==");
id_half = rb_intern_const("half");
+
+ (void)VPrint; /* suppress unused warning */
}
/*
@@ -4447,7 +4642,7 @@ static int gfCheckVal = 1; /* Value checking flag in VpNmlz() */
#endif /* BIGDECIMAL_DEBUG */
static Real *VpConstOne; /* constant 1.0 */
-static Real *VpPt5; /* constant 0.5 */
+static Real *VpConstPt5; /* constant 0.5 */
#define maxnr 100UL /* Maximum iterations for calculating sqrt. */
/* used in VpSqrt() */
@@ -4478,42 +4673,6 @@ static int VpRdup(Real *m, size_t ind_m);
static int gnAlloc = 0; /* Memory allocation counter */
#endif /* BIGDECIMAL_DEBUG */
-VP_EXPORT void *
-VpMemAlloc(size_t mb)
-{
- void *p = xmalloc(mb);
- memset(p, 0, mb);
-#ifdef BIGDECIMAL_DEBUG
- gnAlloc++; /* Count allocation call */
-#endif /* BIGDECIMAL_DEBUG */
- return p;
-}
-
-VP_EXPORT void *
-VpMemRealloc(void *ptr, size_t mb)
-{
- return xrealloc(ptr, mb);
-}
-
-VP_EXPORT void
-VpFree(Real *pv)
-{
- if (pv != NULL) {
- xfree(pv);
-#ifdef BIGDECIMAL_DEBUG
- gnAlloc--; /* Decrement allocation count */
- if (gnAlloc == 0) {
- printf(" *************** All memories allocated freed ****************\n");
- /*getchar();*/
- }
- if (gnAlloc < 0) {
- printf(" ??????????? Too many memory free calls(%d) ?????????????\n", gnAlloc);
- /*getchar();*/
- }
-#endif /* BIGDECIMAL_DEBUG */
- }
-}
-
/*
* EXCEPTION Handling.
*/
@@ -4902,9 +5061,13 @@ VpInit(DECDIG BaseVal)
/* Setup +/- Inf NaN -0 */
VpGetDoubleNegZero();
- /* Allocates Vp constants. */
- VpConstOne = VpAlloc(1UL, "1", 1, 1);
- VpPt5 = VpAlloc(1UL, ".5", 1, 1);
+ /* Const 1.0 */
+ VpConstOne = NewOneNolimit(1, 1);
+
+ /* Const 0.5 */
+ VpConstPt5 = NewOneNolimit(1, 1);
+ VpConstPt5->exponent = 0;
+ VpConstPt5->frac[0] = 5*BASE1;
#ifdef BIGDECIMAL_DEBUG
gnAlloc = 0;
@@ -4993,7 +5156,7 @@ bigdecimal_parse_special_string(const char *str)
p = str + table[i].len;
while (*p && ISSPACE(*p)) ++p;
if (*p == '\0') {
- Real *vp = VpAllocReal(1);
+ Real *vp = rbd_allocate_struct(1);
vp->MaxPrec = 1;
switch (table[i].sign) {
default:
@@ -5017,11 +5180,11 @@ bigdecimal_parse_special_string(const char *str)
/*
* Allocates variable.
* [Input]
- * mx ... allocation unit, if zero then mx is determined by szVal.
- * The mx is the number of effective digits can to be stored.
- * szVal ... value assigned(char). If szVal==NULL,then zero is assumed.
- * If szVal[0]=='#' then Max. Prec. will not be considered(1.1.7),
- * full precision specified by szVal is allocated.
+ * mx ... The number of decimal digits to be allocated, if zero then mx is determined by szVal.
+ * The mx will be the number of significant digits can to be stored.
+ * szVal ... The value assigned(char). If szVal==NULL, then zero is assumed.
+ * If szVal[0]=='#' then MaxPrec is not affected by the precision limit
+ * so that the full precision specified by szVal is allocated.
*
* [Returns]
* Pointer to the newly allocated variable, or
@@ -5032,48 +5195,40 @@ VpAlloc(size_t mx, const char *szVal, int strict_p, int exc)
{
const char *orig_szVal = szVal;
size_t i, j, ni, ipf, nf, ipe, ne, dot_seen, exp_seen, nalloc;
+ size_t len;
char v, *psz;
int sign=1;
Real *vp = NULL;
- size_t mf = VpGetPrecLimit();
VALUE buf;
- mx = (mx + BASE_FIG - 1) / BASE_FIG; /* Determine allocation unit. */
- if (mx == 0) ++mx;
-
- if (szVal) {
- /* Skipping leading spaces */
- while (ISSPACE(*szVal)) szVal++;
-
- /* Processing the leading one `#` */
- if (*szVal != '#') {
- if (mf) {
- mf = (mf + BASE_FIG - 1) / BASE_FIG + 2; /* Needs 1 more for div */
- if (mx > mf) {
- mx = mf;
- }
- }
- }
- else {
- ++szVal;
- }
- }
- else {
+ if (szVal == NULL) {
return_zero:
/* necessary to be able to store */
/* at least mx digits. */
/* szVal==NULL ==> allocate zero value. */
- vp = VpAllocReal(mx);
- vp->MaxPrec = mx; /* set max precision */
+ vp = rbd_allocate_struct(mx);
+ vp->MaxPrec = rbd_calculate_internal_digits(mx, false); /* Must false */
VpSetZero(vp, 1); /* initialize vp to zero. */
return vp;
}
+ /* Skipping leading spaces */
+ while (ISSPACE(*szVal)) szVal++;
+
/* Check on Inf & NaN */
if ((vp = bigdecimal_parse_special_string(szVal)) != NULL) {
return vp;
}
+ /* Processing the leading one `#` */
+ if (*szVal != '#') {
+ len = rbd_calculate_internal_digits(mx, true);
+ }
+ else {
+ len = rbd_calculate_internal_digits(mx, false);
+ ++szVal;
+ }
+
/* Scanning digits */
/* A buffer for keeping scanned digits */
@@ -5235,11 +5390,11 @@ VpAlloc(size_t mx, const char *szVal, int strict_p, int exc)
nalloc = (ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */
/* units for szVal[] */
- if (mx == 0) mx = 1;
- nalloc = Max(nalloc, mx);
- mx = nalloc;
- vp = VpAllocReal(mx);
- vp->MaxPrec = mx; /* set max precision */
+ if (len == 0) len = 1;
+ nalloc = Max(nalloc, len);
+ len = nalloc;
+ vp = rbd_allocate_struct(len);
+ vp->MaxPrec = len; /* set max precision */
VpSetZero(vp, sign);
VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne);
rb_str_resize(buf, 0);
@@ -5804,7 +5959,7 @@ VpMult(Real *c, Real *a, Real *b)
if (MxIndC < MxIndAB) { /* The Max. prec. of c < Prec(a)+Prec(b) */
w = c;
- c = VpAlloc((size_t)((MxIndAB + 1) * BASE_FIG), "#0", 1, 1);
+ c = NewZeroNolimit(1, (size_t)((MxIndAB + 1) * BASE_FIG));
MxIndC = MxIndAB;
}
@@ -5812,8 +5967,8 @@ VpMult(Real *c, Real *a, Real *b)
c->exponent = a->exponent; /* set exponent */
if (!AddExponent(c, b->exponent)) {
- if (w) VpFree(c);
- return 0;
+ if (w) rbd_free_struct(c);
+ return 0;
}
VpSetSign(c, VpGetSign(a) * VpGetSign(b)); /* set sign */
carry = 0;
@@ -5863,10 +6018,10 @@ VpMult(Real *c, Real *a, Real *b)
}
}
if (w != NULL) { /* free work variable */
- VpNmlz(c);
- VpAsgn(w, c, 1);
- VpFree(c);
- c = w;
+ VpNmlz(c);
+ VpAsgn(w, c, 1);
+ rbd_free_struct(c);
+ c = w;
}
else {
VpLimitRound(c,0);
@@ -6235,7 +6390,6 @@ Exit:
* Note: % must not appear more than once
* a ... VP variable to be printed
*/
-#ifdef BIGDECIMAL_ENABLE_VPRINT
static int
VPrint(FILE *fp, const char *cntl_chr, Real *a)
{
@@ -6248,95 +6402,94 @@ VPrint(FILE *fp, const char *cntl_chr, Real *a)
/* nc : number of characters printed */
ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
while (*(cntl_chr + j)) {
- if (*(cntl_chr + j) == '%' && *(cntl_chr + j + 1) != '%') {
- nc = 0;
- if (VpIsNaN(a)) {
- fprintf(fp, SZ_NaN);
- nc += 8;
- }
- else if (VpIsPosInf(a)) {
- fprintf(fp, SZ_INF);
- nc += 8;
- }
- else if (VpIsNegInf(a)) {
- fprintf(fp, SZ_NINF);
- nc += 9;
- }
- else if (!VpIsZero(a)) {
- if (BIGDECIMAL_NEGATIVE_P(a)) {
- fprintf(fp, "-");
- ++nc;
- }
- nc += fprintf(fp, "0.");
- switch (*(cntl_chr + j + 1)) {
- default:
- break;
+ if (*(cntl_chr + j) == '%' && *(cntl_chr + j + 1) != '%') {
+ nc = 0;
+ if (VpIsNaN(a)) {
+ fprintf(fp, SZ_NaN);
+ nc += 8;
+ }
+ else if (VpIsPosInf(a)) {
+ fprintf(fp, SZ_INF);
+ nc += 8;
+ }
+ else if (VpIsNegInf(a)) {
+ fprintf(fp, SZ_NINF);
+ nc += 9;
+ }
+ else if (!VpIsZero(a)) {
+ if (BIGDECIMAL_NEGATIVE_P(a)) {
+ fprintf(fp, "-");
+ ++nc;
+ }
+ nc += fprintf(fp, "0.");
+ switch (*(cntl_chr + j + 1)) {
+ default:
+ break;
- case '0': case 'z':
- ZeroSup = 0;
- ++j;
- sep = cntl_chr[j] == 'z' ? BIGDECIMAL_COMPONENT_FIGURES : 10;
- break;
- }
- for (i = 0; i < a->Prec; ++i) {
- m = BASE1;
- e = a->frac[i];
- while (m) {
- nn = e / m;
- if (!ZeroSup || nn) {
- nc += fprintf(fp, "%lu", (unsigned long)nn); /* The leading zero(s) */
- /* as 0.00xx will not */
- /* be printed. */
- ++nd;
- ZeroSup = 0; /* Set to print succeeding zeros */
- }
- if (nd >= sep) { /* print ' ' after every 10 digits */
- nd = 0;
- nc += fprintf(fp, " ");
- }
- e = e - nn * m;
- m /= 10;
- }
- }
- nc += fprintf(fp, "E%"PRIdSIZE, VpExponent10(a));
- nc += fprintf(fp, " (%"PRIdVALUE", %lu, %lu)", a->exponent, a->Prec, a->MaxPrec);
- }
- else {
- nc += fprintf(fp, "0.0");
- }
- }
- else {
- ++nc;
- if (*(cntl_chr + j) == '\\') {
- switch (*(cntl_chr + j + 1)) {
- case 'n':
- fprintf(fp, "\n");
- ++j;
- break;
- case 't':
- fprintf(fp, "\t");
- ++j;
- break;
- case 'b':
- fprintf(fp, "\n");
- ++j;
- break;
- default:
- fprintf(fp, "%c", *(cntl_chr + j));
- break;
- }
- }
- else {
- fprintf(fp, "%c", *(cntl_chr + j));
- if (*(cntl_chr + j) == '%') ++j;
- }
- }
- j++;
+ case '0': case 'z':
+ ZeroSup = 0;
+ ++j;
+ sep = cntl_chr[j] == 'z' ? BIGDECIMAL_COMPONENT_FIGURES : 10;
+ break;
+ }
+ for (i = 0; i < a->Prec; ++i) {
+ m = BASE1;
+ e = a->frac[i];
+ while (m) {
+ nn = e / m;
+ if (!ZeroSup || nn) {
+ nc += fprintf(fp, "%lu", (unsigned long)nn); /* The leading zero(s) */
+ /* as 0.00xx will not */
+ /* be printed. */
+ ++nd;
+ ZeroSup = 0; /* Set to print succeeding zeros */
+ }
+ if (nd >= sep) { /* print ' ' after every 10 digits */
+ nd = 0;
+ nc += fprintf(fp, " ");
+ }
+ e = e - nn * m;
+ m /= 10;
+ }
+ }
+ nc += fprintf(fp, "E%"PRIdSIZE, VpExponent10(a));
+ nc += fprintf(fp, " (%"PRIdVALUE", %lu, %lu)", a->exponent, a->Prec, a->MaxPrec);
+ }
+ else {
+ nc += fprintf(fp, "0.0");
+ }
+ }
+ else {
+ ++nc;
+ if (*(cntl_chr + j) == '\\') {
+ switch (*(cntl_chr + j + 1)) {
+ case 'n':
+ fprintf(fp, "\n");
+ ++j;
+ break;
+ case 't':
+ fprintf(fp, "\t");
+ ++j;
+ break;
+ case 'b':
+ fprintf(fp, "\n");
+ ++j;
+ break;
+ default:
+ fprintf(fp, "%c", *(cntl_chr + j));
+ break;
+ }
+ }
+ else {
+ fprintf(fp, "%c", *(cntl_chr + j));
+ if (*(cntl_chr + j) == '%') ++j;
+ }
+ }
+ j++;
}
return (int)nc;
}
-#endif
static void
VpFormatSt(char *psz, size_t fFmt)
@@ -6381,188 +6534,254 @@ VpExponent10(Real *a)
}
VP_EXPORT void
-VpSzMantissa(Real *a,char *psz)
+VpSzMantissa(Real *a, char *buf, size_t buflen)
{
size_t i, n, ZeroSup;
DECDIG_DBL m, e, nn;
if (VpIsNaN(a)) {
- sprintf(psz, SZ_NaN);
- return;
+ snprintf(buf, buflen, SZ_NaN);
+ return;
}
if (VpIsPosInf(a)) {
- sprintf(psz, SZ_INF);
+ snprintf(buf, buflen, SZ_INF);
return;
}
if (VpIsNegInf(a)) {
- sprintf(psz, SZ_NINF);
+ snprintf(buf, buflen, SZ_NINF);
return;
}
ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
if (!VpIsZero(a)) {
- if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-';
- n = a->Prec;
- for (i = 0; i < n; ++i) {
- m = BASE1;
- e = a->frac[i];
- while (m) {
- nn = e / m;
- if (!ZeroSup || nn) {
- sprintf(psz, "%lu", (unsigned long)nn); /* The leading zero(s) */
- psz += strlen(psz);
- /* as 0.00xx will be ignored. */
- ZeroSup = 0; /* Set to print succeeding zeros */
- }
- e = e - nn * m;
- m /= 10;
- }
- }
- *psz = 0;
- while (psz[-1] == '0') *(--psz) = 0;
+ if (BIGDECIMAL_NEGATIVE_P(a)) *buf++ = '-';
+ n = a->Prec;
+ for (i = 0; i < n; ++i) {
+ m = BASE1;
+ e = a->frac[i];
+ while (m) {
+ nn = e / m;
+ if (!ZeroSup || nn) {
+ snprintf(buf, buflen, "%lu", (unsigned long)nn); /* The leading zero(s) */
+ buf += strlen(buf);
+ /* as 0.00xx will be ignored. */
+ ZeroSup = 0; /* Set to print succeeding zeros */
+ }
+ e = e - nn * m;
+ m /= 10;
+ }
+ }
+ *buf = 0;
+ while (buf[-1] == '0') *(--buf) = 0;
}
else {
- if (VpIsPosZero(a)) sprintf(psz, "0");
- else sprintf(psz, "-0");
+ if (VpIsPosZero(a)) snprintf(buf, buflen, "0");
+ else snprintf(buf, buflen, "-0");
}
}
VP_EXPORT int
-VpToSpecialString(Real *a,char *psz,int fPlus)
+VpToSpecialString(Real *a, char *buf, size_t buflen, int fPlus)
/* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
{
if (VpIsNaN(a)) {
- sprintf(psz,SZ_NaN);
- return 1;
+ snprintf(buf, buflen, SZ_NaN);
+ return 1;
}
if (VpIsPosInf(a)) {
- if (fPlus == 1) {
- *psz++ = ' ';
- }
- else if (fPlus == 2) {
- *psz++ = '+';
- }
- sprintf(psz, SZ_INF);
- return 1;
+ if (fPlus == 1) {
+ *buf++ = ' ';
+ }
+ else if (fPlus == 2) {
+ *buf++ = '+';
+ }
+ snprintf(buf, buflen, SZ_INF);
+ return 1;
}
if (VpIsNegInf(a)) {
- sprintf(psz, SZ_NINF);
- return 1;
+ snprintf(buf, buflen, SZ_NINF);
+ return 1;
}
if (VpIsZero(a)) {
- if (VpIsPosZero(a)) {
- if (fPlus == 1) sprintf(psz, " 0.0");
- else if (fPlus == 2) sprintf(psz, "+0.0");
- else sprintf(psz, "0.0");
- }
- else sprintf(psz, "-0.0");
- return 1;
+ if (VpIsPosZero(a)) {
+ if (fPlus == 1) snprintf(buf, buflen, " 0.0");
+ else if (fPlus == 2) snprintf(buf, buflen, "+0.0");
+ else snprintf(buf, buflen, "0.0");
+ }
+ else snprintf(buf, buflen, "-0.0");
+ return 1;
}
return 0;
}
VP_EXPORT void
-VpToString(Real *a, char *psz, size_t fFmt, int fPlus)
+VpToString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus)
/* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
{
size_t i, n, ZeroSup;
DECDIG shift, m, e, nn;
- char *pszSav = psz;
+ char *p = buf;
+ size_t plen = buflen;
ssize_t ex;
- if (VpToSpecialString(a, psz, fPlus)) return;
+ if (VpToSpecialString(a, buf, buflen, fPlus)) return;
ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
- if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-';
- else if (fPlus == 1) *psz++ = ' ';
- else if (fPlus == 2) *psz++ = '+';
+#define ADVANCE(n) do { \
+ if (plen < n) goto overflow; \
+ p += n; \
+ plen -= n; \
+} while (0)
+
+ if (BIGDECIMAL_NEGATIVE_P(a)) {
+ *p = '-';
+ ADVANCE(1);
+ }
+ else if (fPlus == 1) {
+ *p = ' ';
+ ADVANCE(1);
+ }
+ else if (fPlus == 2) {
+ *p = '+';
+ ADVANCE(1);
+ }
+
+ *p = '0'; ADVANCE(1);
+ *p = '.'; ADVANCE(1);
- *psz++ = '0';
- *psz++ = '.';
n = a->Prec;
for (i = 0; i < n; ++i) {
- m = BASE1;
- e = a->frac[i];
- while (m) {
- nn = e / m;
- if (!ZeroSup || nn) {
- sprintf(psz, "%lu", (unsigned long)nn); /* The reading zero(s) */
- psz += strlen(psz);
- /* as 0.00xx will be ignored. */
- ZeroSup = 0; /* Set to print succeeding zeros */
- }
- e = e - nn * m;
- m /= 10;
- }
+ m = BASE1;
+ e = a->frac[i];
+ while (m) {
+ nn = e / m;
+ if (!ZeroSup || nn) {
+ /* The reading zero(s) */
+ size_t n = (size_t)snprintf(p, plen, "%lu", (unsigned long)nn);
+ if (n > plen) goto overflow;
+ ADVANCE(n);
+ /* as 0.00xx will be ignored. */
+ ZeroSup = 0; /* Set to print succeeding zeros */
+ }
+ e = e - nn * m;
+ m /= 10;
+ }
}
+
ex = a->exponent * (ssize_t)BASE_FIG;
shift = BASE1;
while (a->frac[0] / shift == 0) {
- --ex;
- shift /= 10;
+ --ex;
+ shift /= 10;
}
- while (psz[-1] == '0') {
- *(--psz) = 0;
+ while (p - 1 > buf && p[-1] == '0') {
+ *(--p) = '\0';
+ ++plen;
}
- sprintf(psz, "e%"PRIdSIZE, ex);
- if (fFmt) VpFormatSt(pszSav, fFmt);
+ snprintf(p, plen, "e%"PRIdSIZE, ex);
+ if (fFmt) VpFormatSt(buf, fFmt);
+
+ overflow:
+ return;
+#undef ADVANCE
}
VP_EXPORT void
-VpToFString(Real *a, char *psz, size_t fFmt, int fPlus)
+VpToFString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus)
/* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
{
size_t i, n;
DECDIG m, e, nn;
- char *pszSav = psz;
+ char *p = buf;
+ size_t plen = buflen;
ssize_t ex;
- if (VpToSpecialString(a, psz, fPlus)) return;
+ if (VpToSpecialString(a, buf, buflen, fPlus)) return;
- if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-';
- else if (fPlus == 1) *psz++ = ' ';
- else if (fPlus == 2) *psz++ = '+';
+#define ADVANCE(n) do { \
+ if (plen < n) goto overflow; \
+ p += n; \
+ plen -= n; \
+} while (0)
+
+
+ if (BIGDECIMAL_NEGATIVE_P(a)) {
+ *p = '-';
+ ADVANCE(1);
+ }
+ else if (fPlus == 1) {
+ *p = ' ';
+ ADVANCE(1);
+ }
+ else if (fPlus == 2) {
+ *p = '+';
+ ADVANCE(1);
+ }
n = a->Prec;
ex = a->exponent;
if (ex <= 0) {
- *psz++ = '0';*psz++ = '.';
- while (ex < 0) {
- for (i=0; i < BASE_FIG; ++i) *psz++ = '0';
- ++ex;
- }
- ex = -1;
+ *p = '0'; ADVANCE(1);
+ *p = '.'; ADVANCE(1);
+ while (ex < 0) {
+ for (i=0; i < BASE_FIG; ++i) {
+ *p = '0'; ADVANCE(1);
+ }
+ ++ex;
+ }
+ ex = -1;
}
for (i = 0; i < n; ++i) {
- --ex;
- if (i == 0 && ex >= 0) {
- sprintf(psz, "%lu", (unsigned long)a->frac[i]);
- psz += strlen(psz);
- }
- else {
- m = BASE1;
- e = a->frac[i];
- while (m) {
- nn = e / m;
- *psz++ = (char)(nn + '0');
- e = e - nn * m;
- m /= 10;
- }
- }
- if (ex == 0) *psz++ = '.';
+ --ex;
+ if (i == 0 && ex >= 0) {
+ size_t n = snprintf(p, plen, "%lu", (unsigned long)a->frac[i]);
+ if (n > plen) goto overflow;
+ ADVANCE(n);
+ }
+ else {
+ m = BASE1;
+ e = a->frac[i];
+ while (m) {
+ nn = e / m;
+ *p = (char)(nn + '0');
+ ADVANCE(1);
+ e = e - nn * m;
+ m /= 10;
+ }
+ }
+ if (ex == 0) {
+ *p = '.';
+ ADVANCE(1);
+ }
}
while (--ex>=0) {
- m = BASE;
- while (m /= 10) *psz++ = '0';
- if (ex == 0) *psz++ = '.';
+ m = BASE;
+ while (m /= 10) {
+ *p = '0';
+ ADVANCE(1);
+ }
+ if (ex == 0) {
+ *p = '.';
+ ADVANCE(1);
+ }
+ }
+
+ *p = '\0';
+ while (p - 1 > buf && p[-1] == '0') {
+ *(--p) = '\0';
+ ++plen;
+ }
+ if (p - 1 > buf && p[-1] == '.') {
+ snprintf(p, plen, "0");
}
- *psz = 0;
- while (psz[-1] == '0') *(--psz) = 0;
- if (psz[-1] == '.') sprintf(psz, "0");
- if (fFmt) VpFormatSt(pszSav, fFmt);
+ if (fFmt) VpFormatSt(buf, fFmt);
+
+ overflow:
+ return;
+#undef ADVANCE
}
/*
@@ -6971,8 +7190,9 @@ VpSqrt(Real *y, Real *x)
if (x->MaxPrec > (size_t)n) n = (ssize_t)x->MaxPrec;
/* allocate temporally variables */
- f = VpAlloc(y->MaxPrec * (BASE_FIG + 2), "#1", 1, 1);
- r = VpAlloc((n + n) * (BASE_FIG + 2), "#1", 1, 1);
+ /* TODO: reconsider MaxPrec of f and r */
+ f = NewOneNolimit(1, y->MaxPrec * (BASE_FIG + 2));
+ r = NewOneNolimit(1, (n + n) * (BASE_FIG + 2));
nr = 0;
y_prec = y->MaxPrec;
@@ -6997,16 +7217,21 @@ VpSqrt(Real *y, Real *x)
f->MaxPrec = y->MaxPrec + 1;
n = (SIGNED_VALUE)(y_prec * BASE_FIG);
if (n < (SIGNED_VALUE)maxnr) n = (SIGNED_VALUE)maxnr;
+
+ /*
+ * Perform: y_{n+1} = (y_n - x/y_n) / 2
+ */
do {
- y->MaxPrec *= 2;
- if (y->MaxPrec > y_prec) y->MaxPrec = y_prec;
- f->MaxPrec = y->MaxPrec;
- VpDivd(f, r, x, y); /* f = x/y */
- VpAddSub(r, f, y, -1); /* r = f - y */
- VpMult(f, VpPt5, r); /* f = 0.5*r */
- if (VpIsZero(f)) goto converge;
- VpAddSub(r, f, y, 1); /* r = y + f */
- VpAsgn(y, r, 1); /* y = r */
+ y->MaxPrec *= 2;
+ if (y->MaxPrec > y_prec) y->MaxPrec = y_prec;
+ f->MaxPrec = y->MaxPrec;
+ VpDivd(f, r, x, y); /* f = x/y */
+ VpAddSub(r, f, y, -1); /* r = f - y */
+ VpMult(f, VpConstPt5, r); /* f = 0.5*r */
+ if (VpIsZero(f))
+ goto converge;
+ VpAddSub(r, f, y, 1); /* r = y + f */
+ VpAsgn(y, r, 1); /* y = r */
} while (++nr < n);
#ifdef BIGDECIMAL_DEBUG
@@ -7031,8 +7256,8 @@ converge:
y->MaxPrec = y_prec;
Exit:
- VpFree(f);
- VpFree(r);
+ rbd_free_struct(f);
+ rbd_free_struct(r);
return 1;
}
@@ -7423,9 +7648,10 @@ VpPowerByInt(Real *y, Real *x, SIGNED_VALUE n)
}
/* Allocate working variables */
+ /* TODO: reconsider MaxPrec of w1 and w2 */
+ w1 = NewZeroNolimit(1, (y->MaxPrec + 2) * BASE_FIG);
+ w2 = NewZeroNolimit(1, (w1->MaxPrec * 2 + 1) * BASE_FIG);
- w1 = VpAlloc((y->MaxPrec + 2) * BASE_FIG, "#0", 1, 1);
- w2 = VpAlloc((w1->MaxPrec * 2 + 1) * BASE_FIG, "#0", 1, 1);
/* calculation start */
VpAsgn(y, x, 1);
@@ -7454,8 +7680,8 @@ Exit:
printf(" n=%"PRIdVALUE"\n", n);
}
#endif /* BIGDECIMAL_DEBUG */
- VpFree(w2);
- VpFree(w1);
+ rbd_free_struct(w2);
+ rbd_free_struct(w1);
return 1;
}
diff --git a/ext/bigdecimal/bigdecimal.gemspec b/ext/bigdecimal/bigdecimal.gemspec
index 2ed7d09373..d215757188 100644
--- a/ext/bigdecimal/bigdecimal.gemspec
+++ b/ext/bigdecimal/bigdecimal.gemspec
@@ -2,14 +2,14 @@
Gem::Specification.new do |s|
s.name = "bigdecimal"
- s.version = "3.1.2"
+ s.version = "3.1.3"
s.authors = ["Kenta Murata", "Zachary Scott", "Shigeo Kobayashi"]
s.email = ["mrkn@mrkn.jp"]
s.summary = "Arbitrary-precision decimal floating-point number library."
s.description = "This library provides arbitrary-precision decimal floating-point number class."
s.homepage = "https://github.com/ruby/bigdecimal"
- s.license = "Ruby"
+ s.licenses = ["Ruby", "bsd-2-clause"]
s.require_paths = %w[lib]
s.extensions = %w[ext/bigdecimal/extconf.rb]
diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h
index bd1c46743e..54fed811fb 100644
--- a/ext/bigdecimal/bigdecimal.h
+++ b/ext/bigdecimal/bigdecimal.h
@@ -102,7 +102,7 @@ extern VALUE rb_cBigDecimal;
*/
#define VP_EXPORT static
-/* Exception codes */
+/* Exception mode */
#define VP_EXCEPTION_ALL ((unsigned short)0x00FF)
#define VP_EXCEPTION_INFINITY ((unsigned short)0x0001)
#define VP_EXCEPTION_NaN ((unsigned short)0x0002)
@@ -115,18 +115,36 @@ extern VALUE rb_cBigDecimal;
#define BIGDECIMAL_EXCEPTION_MODE_DEFAULT 0U
-/* Computation mode */
+/* This is used in BigDecimal#mode */
#define VP_ROUND_MODE ((unsigned short)0x0100)
-#define VP_ROUND_UP 1
-#define VP_ROUND_DOWN 2
-#define VP_ROUND_HALF_UP 3
-#define VP_ROUND_HALF_DOWN 4
-#define VP_ROUND_CEIL 5
-#define VP_ROUND_FLOOR 6
-#define VP_ROUND_HALF_EVEN 7
+
+/* Rounding mode */
+#define VP_ROUND_UP RBD_ROUND_UP
+#define VP_ROUND_DOWN RBD_ROUND_DOWN
+#define VP_ROUND_HALF_UP RBD_ROUND_HALF_UP
+#define VP_ROUND_HALF_DOWN RBD_ROUND_HALF_DOWN
+#define VP_ROUND_CEIL RBD_ROUND_CEIL
+#define VP_ROUND_FLOOR RBD_ROUND_FLOOR
+#define VP_ROUND_HALF_EVEN RBD_ROUND_HALF_EVEN
+
+enum rbd_rounding_mode {
+ RBD_ROUND_UP = 1,
+ RBD_ROUND_DOWN = 2,
+ RBD_ROUND_HALF_UP = 3,
+ RBD_ROUND_HALF_DOWN = 4,
+ RBD_ROUND_CEIL = 5,
+ RBD_ROUND_FLOOR = 6,
+ RBD_ROUND_HALF_EVEN = 7,
+
+ RBD_ROUND_DEFAULT = RBD_ROUND_HALF_UP,
+ RBD_ROUND_TRUNCATE = RBD_ROUND_DOWN,
+ RBD_ROUND_BANKER = RBD_ROUND_HALF_EVEN,
+ RBD_ROUND_CEILING = RBD_ROUND_CEIL
+};
#define BIGDECIMAL_ROUNDING_MODE_DEFAULT VP_ROUND_HALF_UP
+/* Sign flag */
#define VP_SIGN_NaN 0 /* NaN */
#define VP_SIGN_POSITIVE_ZERO 1 /* Positive zero */
#define VP_SIGN_NEGATIVE_ZERO -1 /* Negative zero */
@@ -135,6 +153,7 @@ extern VALUE rb_cBigDecimal;
#define VP_SIGN_POSITIVE_INFINITE 3 /* Positive infinite number */
#define VP_SIGN_NEGATIVE_INFINITE -3 /* Negative infinite number */
+/* The size of fraction part array */
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
#define FLEXIBLE_ARRAY_SIZE /* */
#elif defined(__GNUC__) && !defined(__STRICT_ANSI__)
@@ -205,9 +224,6 @@ VP_EXPORT int VpIsNegDoubleZero(double v);
#endif
VP_EXPORT size_t VpNumOfChars(Real *vp,const char *pszFmt);
VP_EXPORT size_t VpInit(DECDIG BaseVal);
-VP_EXPORT void *VpMemAlloc(size_t mb);
-VP_EXPORT void *VpMemRealloc(void *ptr, size_t mb);
-VP_EXPORT void VpFree(Real *pv);
VP_EXPORT Real *VpAlloc(size_t mx, const char *szVal, int strict_p, int exc);
VP_EXPORT size_t VpAsgn(Real *c, Real *a, int isw);
VP_EXPORT size_t VpAddSub(Real *c,Real *a,Real *b,int operation);
@@ -215,10 +231,10 @@ VP_EXPORT size_t VpMult(Real *c,Real *a,Real *b);
VP_EXPORT size_t VpDivd(Real *c,Real *r,Real *a,Real *b);
VP_EXPORT int VpComp(Real *a,Real *b);
VP_EXPORT ssize_t VpExponent10(Real *a);
-VP_EXPORT void VpSzMantissa(Real *a,char *psz);
-VP_EXPORT int VpToSpecialString(Real *a,char *psz,int fPlus);
-VP_EXPORT void VpToString(Real *a, char *psz, size_t fFmt, int fPlus);
-VP_EXPORT void VpToFString(Real *a, char *psz, size_t fFmt, int fPlus);
+VP_EXPORT void VpSzMantissa(Real *a, char *buf, size_t bufsize);
+VP_EXPORT int VpToSpecialString(Real *a, char *buf, size_t bufsize, int fPlus);
+VP_EXPORT void VpToString(Real *a, char *buf, size_t bufsize, size_t fFmt, int fPlus);
+VP_EXPORT void VpToFString(Real *a, char *buf, size_t bufsize, size_t fFmt, int fPlus);
VP_EXPORT int VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size_t nf, const char *exp_chr, size_t ne);
VP_EXPORT int VpVtoD(double *d, SIGNED_VALUE *e, Real *m);
VP_EXPORT void VpDtoV(Real *m,double d);
diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb
index 4920374b1a..17e7905dd6 100644
--- a/ext/bigdecimal/extconf.rb
+++ b/ext/bigdecimal/extconf.rb
@@ -71,8 +71,6 @@ have_func("rb_rational_num", "ruby.h")
have_func("rb_rational_den", "ruby.h")
have_func("rb_complex_real", "ruby.h")
have_func("rb_complex_imag", "ruby.h")
-have_func("rb_array_const_ptr", "ruby.h")
-have_func("rb_sym2str", "ruby.h")
have_func("rb_opts_exception_p", "ruby.h")
have_func("rb_category_warn", "ruby.h")
have_const("RB_WARN_CATEGORY_DEPRECATED", "ruby.h")
diff --git a/ext/bigdecimal/lib/bigdecimal/util.rb b/ext/bigdecimal/lib/bigdecimal/util.rb
index cb645d2a71..ad92f7cfe6 100644
--- a/ext/bigdecimal/lib/bigdecimal/util.rb
+++ b/ext/bigdecimal/lib/bigdecimal/util.rb
@@ -33,12 +33,16 @@ class Float < Numeric
#
# Returns the value of +float+ as a BigDecimal.
# The +precision+ parameter is used to determine the number of
- # significant digits for the result (the default is Float::DIG).
+ # significant digits for the result. When +precision+ is set to +0+,
+ # the number of digits to represent the float being converted is determined
+ # automatically.
+ # The default +precision+ is +0+.
#
# require 'bigdecimal'
# require 'bigdecimal/util'
#
# 0.5.to_d # => 0.5e0
+ # 1.234.to_d # => 0.1234e1
# 1.234.to_d(2) # => 0.12e1
#
# See also BigDecimal::new.
diff --git a/ext/bigdecimal/missing.h b/ext/bigdecimal/missing.h
index 49b7c7667f..325554b5f5 100644
--- a/ext/bigdecimal/missing.h
+++ b/ext/bigdecimal/missing.h
@@ -35,10 +35,10 @@ extern "C" {
#endif /* RB_UNUSED_VAR */
#if defined(_MSC_VER) && _MSC_VER >= 1310
-# define HAVE___ASSUME
+# define HAVE___ASSUME 1
#elif defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1300
-# define HAVE___ASSUME
+# define HAVE___ASSUME 1
#endif
#ifndef UNREACHABLE
@@ -172,45 +172,6 @@ rb_complex_imag(VALUE cmp)
}
#endif
-/* array */
-
-#ifndef FIX_CONST_VALUE_PTR
-# if defined(__fcc__) || defined(__fcc_version) || \
- defined(__FCC__) || defined(__FCC_VERSION)
-/* workaround for old version of Fujitsu C Compiler (fcc) */
-# define FIX_CONST_VALUE_PTR(x) ((const VALUE *)(x))
-# else
-# define FIX_CONST_VALUE_PTR(x) (x)
-# endif
-#endif
-
-#ifndef HAVE_RB_ARRAY_CONST_PTR
-static inline const VALUE *
-rb_array_const_ptr(VALUE a)
-{
- return FIX_CONST_VALUE_PTR((RBASIC(a)->flags & RARRAY_EMBED_FLAG) ?
- RARRAY(a)->as.ary : RARRAY(a)->as.heap.ptr);
-}
-#endif
-
-#ifndef RARRAY_CONST_PTR
-# define RARRAY_CONST_PTR(a) rb_array_const_ptr(a)
-#endif
-
-#ifndef RARRAY_AREF
-# define RARRAY_AREF(a, i) (RARRAY_CONST_PTR(a)[i])
-#endif
-
-/* symbol */
-
-#ifndef HAVE_RB_SYM2STR
-static inline VALUE
-rb_sym2str(VALUE sym)
-{
- return rb_id2str(SYM2ID(sym));
-}
-#endif
-
/* st */
#ifndef ST2FIX
diff --git a/ext/cgi/escape/escape.c b/ext/cgi/escape/escape.c
index f88b61478b..c5b76de596 100644
--- a/ext/cgi/escape/escape.c
+++ b/ext/cgi/escape/escape.c
@@ -37,7 +37,7 @@ escaped_length(VALUE str)
{
const long len = RSTRING_LEN(str);
if (len >= LONG_MAX / HTML_ESCAPE_MAX_LEN) {
- ruby_malloc_size_overflow(len, HTML_ESCAPE_MAX_LEN);
+ ruby_malloc_size_overflow(len, HTML_ESCAPE_MAX_LEN);
}
return len * HTML_ESCAPE_MAX_LEN;
}
@@ -81,8 +81,8 @@ optimized_unescape_html(VALUE str)
enum {UNICODE_MAX = 0x10ffff};
rb_encoding *enc = rb_enc_get(str);
unsigned long charlimit = (strcasecmp(rb_enc_name(enc), "UTF-8") == 0 ? UNICODE_MAX :
- strcasecmp(rb_enc_name(enc), "ISO-8859-1") == 0 ? 256 :
- 128);
+ strcasecmp(rb_enc_name(enc), "ISO-8859-1") == 0 ? 256 :
+ 128);
long i, len, beg = 0;
size_t clen, plen;
int overflow;
@@ -94,89 +94,89 @@ optimized_unescape_html(VALUE str)
cstr = RSTRING_PTR(str);
for (i = 0; i < len; i++) {
- unsigned long cc;
- char c = cstr[i];
- if (c != '&') continue;
- plen = i - beg;
- if (++i >= len) break;
- c = (unsigned char)cstr[i];
+ unsigned long cc;
+ char c = cstr[i];
+ if (c != '&') continue;
+ plen = i - beg;
+ if (++i >= len) break;
+ c = (unsigned char)cstr[i];
#define MATCH(s) (len - i >= (int)rb_strlen_lit(s) && \
- memcmp(&cstr[i], s, rb_strlen_lit(s)) == 0 && \
- (i += rb_strlen_lit(s) - 1, 1))
- switch (c) {
- case 'a':
- ++i;
- if (MATCH("pos;")) {
- c = '\'';
- }
- else if (MATCH("mp;")) {
- c = '&';
- }
- else continue;
- break;
- case 'q':
- ++i;
- if (MATCH("uot;")) {
- c = '"';
- }
- else continue;
- break;
- case 'g':
- ++i;
- if (MATCH("t;")) {
- c = '>';
- }
- else continue;
- break;
- case 'l':
- ++i;
- if (MATCH("t;")) {
- c = '<';
- }
- else continue;
- break;
- case '#':
- if (len - ++i >= 2 && ISDIGIT(cstr[i])) {
- cc = ruby_scan_digits(&cstr[i], len-i, 10, &clen, &overflow);
- }
- else if ((cstr[i] == 'x' || cstr[i] == 'X') && len - ++i >= 2 && ISXDIGIT(cstr[i])) {
- cc = ruby_scan_digits(&cstr[i], len-i, 16, &clen, &overflow);
- }
- else continue;
- i += clen;
- if (overflow || cc >= charlimit || cstr[i] != ';') continue;
- if (!dest) {
- dest = rb_str_buf_new(len);
- }
- rb_str_cat(dest, cstr + beg, plen);
- if (charlimit > 256) {
- rb_str_cat(dest, buf, rb_enc_mbcput((OnigCodePoint)cc, buf, enc));
- }
- else {
- c = (unsigned char)cc;
- rb_str_cat(dest, &c, 1);
- }
- beg = i + 1;
- continue;
- default:
- --i;
- continue;
- }
- if (!dest) {
- dest = rb_str_buf_new(len);
- }
- rb_str_cat(dest, cstr + beg, plen);
- rb_str_cat(dest, &c, 1);
- beg = i + 1;
+ memcmp(&cstr[i], s, rb_strlen_lit(s)) == 0 && \
+ (i += rb_strlen_lit(s) - 1, 1))
+ switch (c) {
+ case 'a':
+ ++i;
+ if (MATCH("pos;")) {
+ c = '\'';
+ }
+ else if (MATCH("mp;")) {
+ c = '&';
+ }
+ else continue;
+ break;
+ case 'q':
+ ++i;
+ if (MATCH("uot;")) {
+ c = '"';
+ }
+ else continue;
+ break;
+ case 'g':
+ ++i;
+ if (MATCH("t;")) {
+ c = '>';
+ }
+ else continue;
+ break;
+ case 'l':
+ ++i;
+ if (MATCH("t;")) {
+ c = '<';
+ }
+ else continue;
+ break;
+ case '#':
+ if (len - ++i >= 2 && ISDIGIT(cstr[i])) {
+ cc = ruby_scan_digits(&cstr[i], len-i, 10, &clen, &overflow);
+ }
+ else if ((cstr[i] == 'x' || cstr[i] == 'X') && len - ++i >= 2 && ISXDIGIT(cstr[i])) {
+ cc = ruby_scan_digits(&cstr[i], len-i, 16, &clen, &overflow);
+ }
+ else continue;
+ i += clen;
+ if (overflow || cc >= charlimit || cstr[i] != ';') continue;
+ if (!dest) {
+ dest = rb_str_buf_new(len);
+ }
+ rb_str_cat(dest, cstr + beg, plen);
+ if (charlimit > 256) {
+ rb_str_cat(dest, buf, rb_enc_mbcput((OnigCodePoint)cc, buf, enc));
+ }
+ else {
+ c = (unsigned char)cc;
+ rb_str_cat(dest, &c, 1);
+ }
+ beg = i + 1;
+ continue;
+ default:
+ --i;
+ continue;
+ }
+ if (!dest) {
+ dest = rb_str_buf_new(len);
+ }
+ rb_str_cat(dest, cstr + beg, plen);
+ rb_str_cat(dest, &c, 1);
+ beg = i + 1;
}
if (dest) {
- rb_str_cat(dest, cstr + beg, len - beg);
- preserve_original_state(str, dest);
- return dest;
+ rb_str_cat(dest, cstr + beg, len - beg);
+ preserve_original_state(str, dest);
+ return dest;
}
else {
- return rb_str_dup(str);
+ return rb_str_dup(str);
}
}
@@ -200,7 +200,7 @@ url_unreserved_char(unsigned char c)
}
static VALUE
-optimized_escape(VALUE str)
+optimized_escape(VALUE str, int plus_escape)
{
long i, len, beg = 0;
VALUE dest = 0;
@@ -211,38 +211,38 @@ optimized_escape(VALUE str)
cstr = RSTRING_PTR(str);
for (i = 0; i < len; ++i) {
- const unsigned char c = (unsigned char)cstr[i];
- if (!url_unreserved_char(c)) {
- if (!dest) {
- dest = rb_str_buf_new(len);
- }
-
- rb_str_cat(dest, cstr + beg, i - beg);
- beg = i + 1;
-
- if (c == ' ') {
- rb_str_cat_cstr(dest, "+");
- }
- else {
- buf[1] = upper_hexdigits[(c >> 4) & 0xf];
- buf[2] = upper_hexdigits[c & 0xf];
- rb_str_cat(dest, buf, 3);
- }
- }
+ const unsigned char c = (unsigned char)cstr[i];
+ if (!url_unreserved_char(c)) {
+ if (!dest) {
+ dest = rb_str_buf_new(len);
+ }
+
+ rb_str_cat(dest, cstr + beg, i - beg);
+ beg = i + 1;
+
+ if (plus_escape && c == ' ') {
+ rb_str_cat_cstr(dest, "+");
+ }
+ else {
+ buf[1] = upper_hexdigits[(c >> 4) & 0xf];
+ buf[2] = upper_hexdigits[c & 0xf];
+ rb_str_cat(dest, buf, 3);
+ }
+ }
}
if (dest) {
- rb_str_cat(dest, cstr + beg, len - beg);
- preserve_original_state(str, dest);
- return dest;
+ rb_str_cat(dest, cstr + beg, len - beg);
+ preserve_original_state(str, dest);
+ return dest;
}
else {
- return rb_str_dup(str);
+ return rb_str_dup(str);
}
}
static VALUE
-optimized_unescape(VALUE str, VALUE encoding)
+optimized_unescape(VALUE str, VALUE encoding, int unescape_plus)
{
long i, len, beg = 0;
VALUE dest = 0;
@@ -254,52 +254,52 @@ optimized_unescape(VALUE str, VALUE encoding)
cstr = RSTRING_PTR(str);
for (i = 0; i < len; ++i) {
- char buf[1];
- const char c = cstr[i];
- int clen = 0;
- if (c == '%') {
- if (i + 3 > len) break;
- if (!ISXDIGIT(cstr[i+1])) continue;
- if (!ISXDIGIT(cstr[i+2])) continue;
- buf[0] = ((char_to_number(cstr[i+1]) << 4)
- | char_to_number(cstr[i+2]));
- clen = 2;
- }
- else if (c == '+') {
- buf[0] = ' ';
- }
- else {
- continue;
- }
-
- if (!dest) {
- dest = rb_str_buf_new(len);
- }
-
- rb_str_cat(dest, cstr + beg, i - beg);
- i += clen;
- beg = i + 1;
-
- rb_str_cat(dest, buf, 1);
+ char buf[1];
+ const char c = cstr[i];
+ int clen = 0;
+ if (c == '%') {
+ if (i + 3 > len) break;
+ if (!ISXDIGIT(cstr[i+1])) continue;
+ if (!ISXDIGIT(cstr[i+2])) continue;
+ buf[0] = ((char_to_number(cstr[i+1]) << 4)
+ | char_to_number(cstr[i+2]));
+ clen = 2;
+ }
+ else if (unescape_plus && c == '+') {
+ buf[0] = ' ';
+ }
+ else {
+ continue;
+ }
+
+ if (!dest) {
+ dest = rb_str_buf_new(len);
+ }
+
+ rb_str_cat(dest, cstr + beg, i - beg);
+ i += clen;
+ beg = i + 1;
+
+ rb_str_cat(dest, buf, 1);
}
if (dest) {
- rb_str_cat(dest, cstr + beg, len - beg);
- preserve_original_state(str, dest);
- cr = ENC_CODERANGE_UNKNOWN;
+ rb_str_cat(dest, cstr + beg, len - beg);
+ preserve_original_state(str, dest);
+ cr = ENC_CODERANGE_UNKNOWN;
}
else {
- dest = rb_str_dup(str);
- cr = ENC_CODERANGE(str);
+ dest = rb_str_dup(str);
+ cr = ENC_CODERANGE(str);
}
origenc = rb_enc_get_index(str);
if (origenc != encidx) {
- rb_enc_associate_index(dest, encidx);
- if (!ENC_CODERANGE_CLEAN_P(rb_enc_str_coderange(dest))) {
- rb_enc_associate_index(dest, origenc);
- if (cr != ENC_CODERANGE_UNKNOWN)
- ENC_CODERANGE_SET(dest, cr);
- }
+ rb_enc_associate_index(dest, encidx);
+ if (!ENC_CODERANGE_CLEAN_P(rb_enc_str_coderange(dest))) {
+ rb_enc_associate_index(dest, origenc);
+ if (cr != ENC_CODERANGE_UNKNOWN)
+ ENC_CODERANGE_SET(dest, cr);
+ }
}
return dest;
}
@@ -317,10 +317,10 @@ cgiesc_escape_html(VALUE self, VALUE str)
StringValue(str);
if (rb_enc_str_asciicompat_p(str)) {
- return optimized_escape_html(str);
+ return optimized_escape_html(str);
}
else {
- return rb_call_super(1, &str);
+ return rb_call_super(1, &str);
}
}
@@ -337,10 +337,10 @@ cgiesc_unescape_html(VALUE self, VALUE str)
StringValue(str);
if (rb_enc_str_asciicompat_p(str)) {
- return optimized_unescape_html(str);
+ return optimized_unescape_html(str);
}
else {
- return rb_call_super(1, &str);
+ return rb_call_super(1, &str);
}
}
@@ -348,7 +348,7 @@ cgiesc_unescape_html(VALUE self, VALUE str)
* call-seq:
* CGI.escape(string) -> string
*
- * Returns URL-escaped string.
+ * Returns URL-escaped string (+application/x-www-form-urlencoded+).
*
*/
static VALUE
@@ -357,10 +357,10 @@ cgiesc_escape(VALUE self, VALUE str)
StringValue(str);
if (rb_enc_str_asciicompat_p(str)) {
- return optimized_escape(str);
+ return optimized_escape(str, 1);
}
else {
- return rb_call_super(1, &str);
+ return rb_call_super(1, &str);
}
}
@@ -368,7 +368,7 @@ static VALUE
accept_charset(int argc, VALUE *argv, VALUE self)
{
if (argc > 0)
- return argv[0];
+ return argv[0];
return rb_cvar_get(CLASS_OF(self), id_accept_charset);
}
@@ -376,7 +376,7 @@ accept_charset(int argc, VALUE *argv, VALUE self)
* call-seq:
* CGI.unescape(string, encoding=@@accept_charset) -> string
*
- * Returns URL-unescaped string.
+ * Returns URL-unescaped string (+application/x-www-form-urlencoded+).
*
*/
static VALUE
@@ -387,11 +387,54 @@ cgiesc_unescape(int argc, VALUE *argv, VALUE self)
StringValue(str);
if (rb_enc_str_asciicompat_p(str)) {
- VALUE enc = accept_charset(argc-1, argv+1, self);
- return optimized_unescape(str, enc);
+ VALUE enc = accept_charset(argc-1, argv+1, self);
+ return optimized_unescape(str, enc, 1);
+ }
+ else {
+ return rb_call_super(argc, argv);
+ }
+}
+
+/*
+ * call-seq:
+ * CGI.escapeURIComponent(string) -> string
+ *
+ * Returns URL-escaped string following RFC 3986.
+ *
+ */
+static VALUE
+cgiesc_escape_uri_component(VALUE self, VALUE str)
+{
+ StringValue(str);
+
+ if (rb_enc_str_asciicompat_p(str)) {
+ return optimized_escape(str, 0);
+ }
+ else {
+ return rb_call_super(1, &str);
+ }
+}
+
+/*
+ * call-seq:
+ * CGI.unescapeURIComponent(string, encoding=@@accept_charset) -> string
+ *
+ * Returns URL-unescaped string following RFC 3986.
+ *
+ */
+static VALUE
+cgiesc_unescape_uri_component(int argc, VALUE *argv, VALUE self)
+{
+ VALUE str = (rb_check_arity(argc, 1, 2), argv[0]);
+
+ StringValue(str);
+
+ if (rb_enc_str_asciicompat_p(str)) {
+ VALUE enc = accept_charset(argc-1, argv+1, self);
+ return optimized_unescape(str, enc, 0);
}
else {
- return rb_call_super(argc, argv);
+ return rb_call_super(argc, argv);
}
}
@@ -414,6 +457,8 @@ InitVM_escape(void)
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_method(rb_mEscape, "unescapeURIComponent", cgiesc_unescape_uri_component, -1);
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);
diff --git a/ext/coverage/coverage.c b/ext/coverage/coverage.c
index 3dc2490d40..4578de54e4 100644
--- a/ext/coverage/coverage.c
+++ b/ext/coverage/coverage.c
@@ -24,11 +24,37 @@ static int current_mode;
static VALUE me2counter = Qnil;
/*
+ * call-seq: Coverage.supported?(mode) -> true or false
+ *
+ * Returns true if coverage measurement is supported for the given mode.
+ *
+ * The mode should be one of the following symbols:
+ * +:lines+, +:branches+, +:methods+, +:eval+.
+ *
+ * Example:
+ *
+ * Coverage.supported?(:lines) #=> true
+ * Coverage.supported?(:all) #=> false
+ */
+static VALUE
+rb_coverage_supported(VALUE self, VALUE _mode)
+{
+ ID mode = RB_SYM2ID(_mode);
+
+ return RBOOL(
+ mode == rb_intern("lines") ||
+ mode == rb_intern("branches") ||
+ mode == rb_intern("methods") ||
+ mode == rb_intern("eval")
+ );
+}
+
+/*
* call-seq:
- * Coverage.setup => nil
- * Coverage.setup(:all) => nil
- * Coverage.setup(lines: bool, branches: bool, methods: bool) => nil
- * Coverage.setup(oneshot_lines: true) => nil
+ * Coverage.setup => nil
+ * Coverage.setup(:all) => nil
+ * Coverage.setup(lines: bool, branches: bool, methods: bool, eval: bool) => nil
+ * Coverage.setup(oneshot_lines: true) => nil
*
* Set up the coverage measurement.
*
@@ -44,56 +70,57 @@ rb_coverage_setup(int argc, VALUE *argv, VALUE klass)
int mode;
if (current_state != IDLE) {
- rb_raise(rb_eRuntimeError, "coverage measurement is already setup");
+ rb_raise(rb_eRuntimeError, "coverage measurement is already setup");
}
rb_scan_args(argc, argv, "01", &opt);
if (argc == 0) {
- mode = 0; /* compatible mode */
+ mode = 0; /* compatible mode */
}
else if (opt == ID2SYM(rb_intern("all"))) {
- mode = COVERAGE_TARGET_LINES | COVERAGE_TARGET_BRANCHES | COVERAGE_TARGET_METHODS;
+ mode = COVERAGE_TARGET_LINES | COVERAGE_TARGET_BRANCHES | COVERAGE_TARGET_METHODS | COVERAGE_TARGET_EVAL;
}
else {
- mode = 0;
- opt = rb_convert_type(opt, T_HASH, "Hash", "to_hash");
-
- if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("lines")))))
- mode |= COVERAGE_TARGET_LINES;
- if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("branches")))))
- mode |= COVERAGE_TARGET_BRANCHES;
- if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("methods")))))
- mode |= COVERAGE_TARGET_METHODS;
+ mode = 0;
+ opt = rb_convert_type(opt, T_HASH, "Hash", "to_hash");
+
+ if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("lines")))))
+ mode |= COVERAGE_TARGET_LINES;
+ if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("branches")))))
+ mode |= COVERAGE_TARGET_BRANCHES;
+ if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("methods")))))
+ mode |= COVERAGE_TARGET_METHODS;
if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("oneshot_lines"))))) {
if (mode & COVERAGE_TARGET_LINES)
rb_raise(rb_eRuntimeError, "cannot enable lines and oneshot_lines simultaneously");
mode |= COVERAGE_TARGET_LINES;
mode |= COVERAGE_TARGET_ONESHOT_LINES;
}
+ if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("eval")))))
+ mode |= COVERAGE_TARGET_EVAL;
}
if (mode & COVERAGE_TARGET_METHODS) {
me2counter = rb_ident_hash_new();
}
else {
- me2counter = Qnil;
+ me2counter = Qnil;
}
coverages = rb_get_coverages();
if (!RTEST(coverages)) {
- coverages = rb_hash_new();
- rb_obj_hide(coverages);
- current_mode = mode;
- if (mode == 0) mode = COVERAGE_TARGET_LINES;
- rb_set_coverages(coverages, mode, me2counter);
+ coverages = rb_hash_new();
+ rb_obj_hide(coverages);
+ current_mode = mode;
+ if (mode == 0) mode = COVERAGE_TARGET_LINES;
+ rb_set_coverages(coverages, mode, me2counter);
current_state = SUSPENDED;
}
else if (current_mode != mode) {
- rb_raise(rb_eRuntimeError, "cannot change the measuring target during coverage measurement");
+ rb_raise(rb_eRuntimeError, "cannot change the measuring target during coverage measurement");
}
-
return Qnil;
}
@@ -104,7 +131,7 @@ rb_coverage_setup(int argc, VALUE *argv, VALUE klass)
* Start/resume the coverage measurement.
*
* Caveat: Currently, only process-global coverage measurement is supported.
- * You cannot measure per-thread covearge. If your process has multiple thread,
+ * You cannot measure per-thread coverage. If your process has multiple thread,
* using Coverage.resume/suspend to capture code coverage executed from only
* a limited code block, may yield misleading results.
*/
@@ -112,10 +139,10 @@ VALUE
rb_coverage_resume(VALUE klass)
{
if (current_state == IDLE) {
- rb_raise(rb_eRuntimeError, "coverage measurement is not set up yet");
+ rb_raise(rb_eRuntimeError, "coverage measurement is not set up yet");
}
if (current_state == RUNNING) {
- rb_raise(rb_eRuntimeError, "coverage measurement is already running");
+ rb_raise(rb_eRuntimeError, "coverage measurement is already running");
}
rb_resume_coverages();
current_state = RUNNING;
@@ -124,10 +151,10 @@ rb_coverage_resume(VALUE klass)
/*
* call-seq:
- * Coverage.start => nil
- * Coverage.start(:all) => nil
- * Coverage.start(lines: bool, branches: bool, methods: bool) => nil
- * Coverage.start(oneshot_lines: true) => nil
+ * Coverage.start => nil
+ * Coverage.start(:all) => nil
+ * Coverage.start(lines: bool, branches: bool, methods: bool, eval: bool) => nil
+ * Coverage.start(oneshot_lines: true) => nil
*
* Enables the coverage measurement.
* See the documentation of Coverage class in detail.
@@ -218,45 +245,45 @@ method_coverage_i(void *vstart, void *vend, size_t stride, void *data)
void *poisoned = asan_poisoned_object_p(v);
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;
- VALUE path, first_lineno, first_column, last_lineno, last_column;
- VALUE data[5], ncoverage, methods;
- VALUE methods_id = ID2SYM(rb_intern("methods"));
- VALUE klass;
- const rb_method_entry_t *me2 = rb_resolve_me_location(me, data);
- if (me != me2) continue;
- klass = me->owner;
- if (RB_TYPE_P(klass, T_ICLASS)) {
- rb_bug("T_ICLASS");
- }
- path = data[0];
- first_lineno = data[1];
- first_column = data[2];
- last_lineno = data[3];
- last_column = data[4];
- if (FIX2LONG(first_lineno) <= 0) continue;
- ncoverage = rb_hash_aref(ncoverages, path);
- if (NIL_P(ncoverage)) continue;
- methods = rb_hash_aref(ncoverage, methods_id);
-
- {
- VALUE method_id = ID2SYM(me->def->original_id);
- VALUE rcount = rb_hash_aref(me2counter, (VALUE) me);
- VALUE key = rb_ary_new_from_args(6, klass, method_id, first_lineno, first_column, last_lineno, last_column);
- VALUE rcount2 = rb_hash_aref(methods, key);
-
- if (NIL_P(rcount)) rcount = LONG2FIX(0);
- if (NIL_P(rcount2)) rcount2 = LONG2FIX(0);
- if (!POSFIXABLE(FIX2LONG(rcount) + FIX2LONG(rcount2))) {
- rcount = LONG2FIX(FIXNUM_MAX);
- }
- else {
- rcount = LONG2FIX(FIX2LONG(rcount) + FIX2LONG(rcount2));
- }
- rb_hash_aset(methods, key, rcount);
- }
- }
+ if (RB_TYPE_P(v, T_IMEMO) && imemo_type(v) == imemo_ment) {
+ const rb_method_entry_t *me = (rb_method_entry_t *) v;
+ VALUE path, first_lineno, first_column, last_lineno, last_column;
+ VALUE data[5], ncoverage, methods;
+ VALUE methods_id = ID2SYM(rb_intern("methods"));
+ VALUE klass;
+ const rb_method_entry_t *me2 = rb_resolve_me_location(me, data);
+ if (me != me2) continue;
+ klass = me->owner;
+ if (RB_TYPE_P(klass, T_ICLASS)) {
+ rb_bug("T_ICLASS");
+ }
+ path = data[0];
+ first_lineno = data[1];
+ first_column = data[2];
+ last_lineno = data[3];
+ last_column = data[4];
+ if (FIX2LONG(first_lineno) <= 0) continue;
+ ncoverage = rb_hash_aref(ncoverages, path);
+ if (NIL_P(ncoverage)) continue;
+ methods = rb_hash_aref(ncoverage, methods_id);
+
+ {
+ VALUE method_id = ID2SYM(me->def->original_id);
+ VALUE rcount = rb_hash_aref(me2counter, (VALUE) me);
+ VALUE key = rb_ary_new_from_args(6, klass, method_id, first_lineno, first_column, last_lineno, last_column);
+ VALUE rcount2 = rb_hash_aref(methods, key);
+
+ if (NIL_P(rcount)) rcount = LONG2FIX(0);
+ if (NIL_P(rcount2)) rcount2 = LONG2FIX(0);
+ if (!POSFIXABLE(FIX2LONG(rcount) + FIX2LONG(rcount2))) {
+ rcount = LONG2FIX(FIXNUM_MAX);
+ }
+ else {
+ rcount = LONG2FIX(FIX2LONG(rcount) + FIX2LONG(rcount2));
+ }
+ rb_hash_aset(methods, key, rcount);
+ }
+ }
if (poisoned) {
asan_poison_object(v);
@@ -272,32 +299,32 @@ coverage_peek_result_i(st_data_t key, st_data_t val, st_data_t h)
VALUE coverage = (VALUE)val;
VALUE coverages = (VALUE)h;
if (current_mode == 0) {
- /* compatible mode */
- VALUE lines = rb_ary_dup(RARRAY_AREF(coverage, COVERAGE_INDEX_LINES));
- rb_ary_freeze(lines);
- coverage = lines;
+ /* compatible mode */
+ VALUE lines = rb_ary_dup(RARRAY_AREF(coverage, COVERAGE_INDEX_LINES));
+ rb_ary_freeze(lines);
+ coverage = lines;
}
else {
- VALUE h = rb_hash_new();
+ VALUE h = rb_hash_new();
- if (current_mode & COVERAGE_TARGET_LINES) {
- VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
+ if (current_mode & COVERAGE_TARGET_LINES) {
+ VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
const char *kw = (current_mode & COVERAGE_TARGET_ONESHOT_LINES) ? "oneshot_lines" : "lines";
- lines = rb_ary_dup(lines);
- rb_ary_freeze(lines);
+ lines = rb_ary_dup(lines);
+ rb_ary_freeze(lines);
rb_hash_aset(h, ID2SYM(rb_intern(kw)), lines);
- }
+ }
- if (current_mode & COVERAGE_TARGET_BRANCHES) {
- VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
- rb_hash_aset(h, ID2SYM(rb_intern("branches")), branch_coverage(branches));
- }
+ if (current_mode & COVERAGE_TARGET_BRANCHES) {
+ VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
+ rb_hash_aset(h, ID2SYM(rb_intern("branches")), branch_coverage(branches));
+ }
- if (current_mode & COVERAGE_TARGET_METHODS) {
- rb_hash_aset(h, ID2SYM(rb_intern("methods")), rb_hash_new());
- }
+ if (current_mode & COVERAGE_TARGET_METHODS) {
+ rb_hash_aset(h, ID2SYM(rb_intern("methods")), rb_hash_new());
+ }
- coverage = h;
+ coverage = h;
}
rb_hash_aset(coverages, path, coverage);
@@ -322,13 +349,13 @@ rb_coverage_peek_result(VALUE klass)
VALUE coverages = rb_get_coverages();
VALUE ncoverages = rb_hash_new();
if (!RTEST(coverages)) {
- rb_raise(rb_eRuntimeError, "coverage measurement is not enabled");
+ rb_raise(rb_eRuntimeError, "coverage measurement is not enabled");
}
OBJ_WB_UNPROTECT(coverages);
st_foreach(RHASH_TBL_RAW(coverages), coverage_peek_result_i, ncoverages);
if (current_mode & COVERAGE_TARGET_METHODS) {
- rb_objspace_each_objects(method_coverage_i, &ncoverages);
+ rb_objspace_each_objects(method_coverage_i, &ncoverages);
}
rb_hash_freeze(ncoverages);
@@ -354,7 +381,7 @@ VALUE
rb_coverage_suspend(VALUE klass)
{
if (current_state != RUNNING) {
- rb_raise(rb_eRuntimeError, "coverage measurement is not running");
+ rb_raise(rb_eRuntimeError, "coverage measurement is not running");
}
rb_suspend_coverages();
current_state = SUSPENDED;
@@ -377,7 +404,7 @@ rb_coverage_result(int argc, VALUE *argv, VALUE klass)
int stop = 1, clear = 1;
if (current_state == IDLE) {
- rb_raise(rb_eRuntimeError, "coverage measurement is not enabled");
+ rb_raise(rb_eRuntimeError, "coverage measurement is not enabled");
}
rb_scan_args(argc, argv, "01", &opt);
@@ -443,7 +470,7 @@ rb_coverage_running(VALUE klass)
* This feature is experimental, so these APIs may be changed in future.
*
* Caveat: Currently, only process-global coverage measurement is supported.
- * You cannot measure per-thread covearge.
+ * You cannot measure per-thread coverage.
*
* = Usage
*
@@ -589,6 +616,9 @@ void
Init_coverage(void)
{
VALUE rb_mCoverage = rb_define_module("Coverage");
+
+ rb_define_singleton_method(rb_mCoverage, "supported?", rb_coverage_supported, 1);
+
rb_define_module_function(rb_mCoverage, "setup", rb_coverage_setup, -1);
rb_define_module_function(rb_mCoverage, "start", rb_coverage_start, -1);
rb_define_module_function(rb_mCoverage, "resume", rb_coverage_resume, 0);
diff --git a/ext/coverage/depend b/ext/coverage/depend
index 57d368d3f5..e7fab16484 100644
--- a/ext/coverage/depend
+++ b/ext/coverage/depend
@@ -165,9 +165,12 @@ coverage.o: $(top_srcdir)/ccan/check_type/check_type.h
coverage.o: $(top_srcdir)/ccan/container_of/container_of.h
coverage.o: $(top_srcdir)/ccan/list/list.h
coverage.o: $(top_srcdir)/ccan/str/str.h
+coverage.o: $(top_srcdir)/constant.h
coverage.o: $(top_srcdir)/gc.h
+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/compilers.h
coverage.o: $(top_srcdir)/internal/gc.h
coverage.o: $(top_srcdir)/internal/hash.h
@@ -176,12 +179,14 @@ coverage.o: $(top_srcdir)/internal/sanitizers.h
coverage.o: $(top_srcdir)/internal/serial.h
coverage.o: $(top_srcdir)/internal/static_assert.h
coverage.o: $(top_srcdir)/internal/thread.h
+coverage.o: $(top_srcdir)/internal/variable.h
coverage.o: $(top_srcdir)/internal/vm.h
coverage.o: $(top_srcdir)/internal/warnings.h
coverage.o: $(top_srcdir)/method.h
coverage.o: $(top_srcdir)/node.h
coverage.o: $(top_srcdir)/ruby_assert.h
coverage.o: $(top_srcdir)/ruby_atomic.h
+coverage.o: $(top_srcdir)/shape.h
coverage.o: $(top_srcdir)/thread_pthread.h
coverage.o: $(top_srcdir)/vm_core.h
coverage.o: $(top_srcdir)/vm_opts.h
diff --git a/ext/date/date.gemspec b/ext/date/date.gemspec
index cf07696976..660353ebc5 100644
--- a/ext/date/date.gemspec
+++ b/ext/date/date.gemspec
@@ -10,14 +10,22 @@ Gem::Specification.new do |s|
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.require_path = %w{lib}
- s.files = [
- "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"
- ]
- s.extensions = "ext/date/extconf.rb"
- s.required_ruby_version = ">= 2.4.0"
+ if Gem::Platform === s.platform and s.platform =~ 'java' or RUBY_ENGINE == 'jruby'
+ s.platform = 'java'
+ # No files shipped, no require path, no-op for now on JRuby
+ else
+ s.require_path = %w{lib}
+
+ s.files = [
+ "README.md",
+ "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"
+ ]
+ s.extensions = "ext/date/extconf.rb"
+ end
+
+ s.required_ruby_version = ">= 2.6.0"
s.authors = ["Tadayoshi Funaba"]
s.email = [nil]
diff --git a/ext/date/date_core.c b/ext/date/date_core.c
index d7ebcaa784..21367c0ddf 100644
--- a/ext/date/date_core.c
+++ b/ext/date/date_core.c
@@ -27,6 +27,10 @@ static VALUE eDateError;
static VALUE half_days_in_day, day_in_nanoseconds;
static double positive_inf, negative_inf;
+// used by deconstruct_keys
+static VALUE sym_year, sym_month, sym_day, sym_yday, sym_wday;
+static VALUE sym_hour, sym_min, sym_sec, sym_sec_fraction, sym_zone;
+
#define f_boolcast(x) ((x) ? Qtrue : Qfalse)
#define f_abs(x) rb_funcall(x, rb_intern("abs"), 0)
@@ -60,7 +64,8 @@ static VALUE datetime_initialize(int argc, VALUE *argv, VALUE self);
#define RETURN_FALSE_UNLESS_NUMERIC(obj) if(!RTEST(rb_obj_is_kind_of((obj), rb_cNumeric))) return Qfalse
inline static void
-check_numeric(VALUE obj, const char* field) {
+check_numeric(VALUE obj, const char* field)
+{
if(!RTEST(rb_obj_is_kind_of(obj, rb_cNumeric))) {
rb_raise(rb_eTypeError, "invalid %s (not numeric)", field);
}
@@ -465,6 +470,7 @@ c_find_ldoy(int y, double sg, int *rjd, int *ns)
}
#ifndef NDEBUG
+/* :nodoc: */
static int
c_find_fdom(int y, int m, double sg, int *rjd, int *ns)
{
@@ -621,6 +627,7 @@ c_jd_to_weeknum(int jd, int f, double sg, int *ry, int *rw, int *rd)
}
#ifndef NDEBUG
+/* :nodoc: */
static void
c_nth_kday_to_jd(int y, int m, int n, int k, double sg, int *rjd, int *ns)
{
@@ -646,6 +653,7 @@ c_jd_to_wday(int jd)
}
#ifndef NDEBUG
+/* :nodoc: */
static void
c_jd_to_nth_kday(int jd, double sg, int *ry, int *rm, int *rn, int *rk)
{
@@ -758,6 +766,8 @@ c_valid_civil_p(int y, int m, int d, double sg,
if (m < 0)
m += 13;
+ if (m < 1 || m > 12)
+ return 0;
if (d < 0) {
if (!c_find_ldom(y, m, sg, rjd, ns))
return 0;
@@ -822,6 +832,7 @@ c_valid_weeknum_p(int y, int w, int d, int f, double sg,
}
#ifndef NDEBUG
+/* :nodoc: */
static int
c_valid_nth_kday_p(int y, int m, int n, int k, double sg,
int *rm, int *rn, int *rk, int *rjd, int *ns)
@@ -963,6 +974,7 @@ ns_to_day(VALUE n)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
ms_to_sec(VALUE m)
{
@@ -981,6 +993,7 @@ ns_to_sec(VALUE n)
}
#ifndef NDEBUG
+/* :nodoc: */
inline static VALUE
ins_to_day(int n)
{
@@ -1016,6 +1029,7 @@ day_to_sec(VALUE d)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
day_to_ns(VALUE d)
{
@@ -1040,6 +1054,7 @@ sec_to_ns(VALUE s)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
isec_to_ns(int s)
{
@@ -1066,6 +1081,7 @@ div_df(VALUE d, VALUE *f)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
div_sf(VALUE s, VALUE *f)
{
@@ -1500,6 +1516,7 @@ m_df(union DateData *x)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
m_df_in_day(union DateData *x)
{
@@ -1997,6 +2014,7 @@ expect_numeric(VALUE x)
}
#ifndef NDEBUG
+/* :nodoc: */
static void
civil_to_jd(VALUE y, int m, int d, double sg,
VALUE *nth, int *ry,
@@ -2309,6 +2327,7 @@ valid_weeknum_p(VALUE y, int w, int d, int f, double sg,
}
#ifndef NDEBUG
+/* :nodoc: */
static int
valid_nth_kday_p(VALUE y, int m, int n, int k, double sg,
VALUE *nth, int *ry,
@@ -2446,6 +2465,7 @@ valid_jd_sub(int argc, VALUE *argv, VALUE klass, int need_jd)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
date_s__valid_jd_p(int argc, VALUE *argv, VALUE klass)
{
@@ -2473,7 +2493,7 @@ date_s__valid_jd_p(int argc, VALUE *argv, VALUE klass)
*
* Date.valid_jd?(2451944) # => true
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
*
* Related: Date.jd.
*/
@@ -2535,6 +2555,7 @@ valid_civil_sub(int argc, VALUE *argv, VALUE klass, int need_jd)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
date_s__valid_civil_p(int argc, VALUE *argv, VALUE klass)
{
@@ -2566,7 +2587,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:Date@Argument+start].
+ * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
*
* Date.valid_date? is an alias for Date.valid_civil?.
*
@@ -2626,6 +2647,7 @@ valid_ordinal_sub(int argc, VALUE *argv, VALUE klass, int need_jd)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
date_s__valid_ordinal_p(int argc, VALUE *argv, VALUE klass)
{
@@ -2655,7 +2677,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:Date@Argument+start].
+ * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
*
* Related: Date.jd, Date.ordinal.
*/
@@ -2712,6 +2734,7 @@ valid_commercial_sub(int argc, VALUE *argv, VALUE klass, int need_jd)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
date_s__valid_commercial_p(int argc, VALUE *argv, VALUE klass)
{
@@ -2744,7 +2767,7 @@ date_s__valid_commercial_p(int argc, VALUE *argv, VALUE klass)
*
* See Date.commercial.
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
*
* Related: Date.jd, Date.commercial.
*/
@@ -2773,6 +2796,7 @@ date_s_valid_commercial_p(int argc, VALUE *argv, VALUE klass)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
valid_weeknum_sub(int argc, VALUE *argv, VALUE klass, int need_jd)
{
@@ -2804,6 +2828,7 @@ valid_weeknum_sub(int argc, VALUE *argv, VALUE klass, int need_jd)
}
}
+/* :nodoc: */
static VALUE
date_s__valid_weeknum_p(int argc, VALUE *argv, VALUE klass)
{
@@ -2824,6 +2849,7 @@ date_s__valid_weeknum_p(int argc, VALUE *argv, VALUE klass)
return valid_weeknum_sub(5, argv2, klass, 1);
}
+/* :nodoc: */
static VALUE
date_s_valid_weeknum_p(int argc, VALUE *argv, VALUE klass)
{
@@ -2875,6 +2901,7 @@ valid_nth_kday_sub(int argc, VALUE *argv, VALUE klass, int need_jd)
}
}
+/* :nodoc: */
static VALUE
date_s__valid_nth_kday_p(int argc, VALUE *argv, VALUE klass)
{
@@ -2895,6 +2922,7 @@ date_s__valid_nth_kday_p(int argc, VALUE *argv, VALUE klass)
return valid_nth_kday_sub(5, argv2, klass, 1);
}
+/* :nodoc: */
static VALUE
date_s_valid_nth_kday_p(int argc, VALUE *argv, VALUE klass)
{
@@ -2917,6 +2945,7 @@ date_s_valid_nth_kday_p(int argc, VALUE *argv, VALUE klass)
return Qtrue;
}
+/* :nodoc: */
static VALUE
date_s_zone_to_diff(VALUE klass, VALUE str)
{
@@ -3112,6 +3141,7 @@ old_to_new(VALUE ajd, VALUE of, VALUE sg,
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
date_s_new_bang(int argc, VALUE *argv, VALUE klass)
{
@@ -3319,7 +3349,7 @@ static VALUE d_lite_plus(VALUE, VALUE);
*
* Date.jd(Date::ITALY - 1).julian? # => true
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
*
* Related: Date.new.
*/
@@ -3384,7 +3414,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:Date@Argument+start].
+ * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
*
* Related: Date.jd, Date.new.
*/
@@ -3461,7 +3491,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:Date@Argument+start].
+ * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
*
* Date.civil is an alias for Date.new.
*
@@ -3569,7 +3599,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:Date@Argument+start].
+ * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
*
* Related: Date.jd, Date.new, Date.ordinal.
*/
@@ -3623,6 +3653,7 @@ date_s_commercial(int argc, VALUE *argv, VALUE klass)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
date_s_weeknum(int argc, VALUE *argv, VALUE klass)
{
@@ -3672,6 +3703,7 @@ date_s_weeknum(int argc, VALUE *argv, VALUE klass)
return ret;
}
+/* :nodoc: */
static VALUE
date_s_nth_kday(int argc, VALUE *argv, VALUE klass)
{
@@ -3752,7 +3784,7 @@ static void set_sg(union DateData *, double);
*
* Date.today.to_s # => "2022-07-06"
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
*
*/
static VALUE
@@ -4346,14 +4378,18 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass,
* call-seq:
* Date._strptime(string, format = '%F') -> hash
*
- * Parses the given representation of date and time with the given
- * template, and returns a hash of parsed elements. _strptime does
- * not support specification of flags and width unlike strftime.
+ * Returns a hash of values parsed from +string+
+ * according to the given +format+:
*
- * Date._strptime('2001-02-03', '%Y-%m-%d')
- * #=> {:year=>2001, :mon=>2, :mday=>3}
+ * Date._strptime('2001-02-03', '%Y-%m-%d') # => {:year=>2001, :mon=>2, :mday=>3}
*
- * See also strptime(3) and #strftime.
+ * For other formats, see
+ * {Formats for Dates and Times}[rdoc-ref: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].
+ *
+ * Related: Date.strptime (returns a \Date object).
*/
static VALUE
date_s__strptime(int argc, VALUE *argv, VALUE klass)
@@ -4365,21 +4401,26 @@ date_s__strptime(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date.strptime(string = '-4712-01-01', format = '%F', start = Date::ITALY) -> date
*
- * Parses the given representation of date and time with the given
- * template, and creates a date object. strptime does not support
- * specification of flags and width unlike strftime.
+ * Returns a new \Date object with values parsed from +string+,
+ * according to the given +format+:
*
- * Date.strptime('2001-02-03', '%Y-%m-%d') #=> #<Date: 2001-02-03 ...>
- * Date.strptime('03-02-2001', '%d-%m-%Y') #=> #<Date: 2001-02-03 ...>
- * Date.strptime('2001-034', '%Y-%j') #=> #<Date: 2001-02-03 ...>
- * Date.strptime('2001-W05-6', '%G-W%V-%u') #=> #<Date: 2001-02-03 ...>
- * Date.strptime('2001 04 6', '%Y %U %w') #=> #<Date: 2001-02-03 ...>
- * Date.strptime('2001 05 6', '%Y %W %u') #=> #<Date: 2001-02-03 ...>
- * Date.strptime('sat3feb01', '%a%d%b%y') #=> #<Date: 2001-02-03 ...>
+ * Date.strptime('2001-02-03', '%Y-%m-%d') # => #<Date: 2001-02-03>
+ * Date.strptime('03-02-2001', '%d-%m-%Y') # => #<Date: 2001-02-03>
+ * Date.strptime('2001-034', '%Y-%j') # => #<Date: 2001-02-03>
+ * Date.strptime('2001-W05-6', '%G-W%V-%u') # => #<Date: 2001-02-03>
+ * Date.strptime('2001 04 6', '%Y %U %w') # => #<Date: 2001-02-03>
+ * Date.strptime('2001 05 6', '%Y %W %u') # => #<Date: 2001-02-03>
+ * Date.strptime('sat3feb01', '%a%d%b%y') # => #<Date: 2001-02-03>
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * For other formats, see
+ * {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc].
+ * (Unlike Date.strftime, does not support flags and width.)
*
- * See also strptime(3) and #strftime.
+ * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
+ *
+ * See also {strptime(3)}[https://man7.org/linux/man-pages/man3/strptime.3.html].
+ *
+ * Related: Date._strptime (returns a hash).
*/
static VALUE
date_s_strptime(int argc, VALUE *argv, VALUE klass)
@@ -4468,23 +4509,30 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date._parse(string, comp = true, limit: 128) -> hash
*
- * Parses the given representation of date and time, and returns a
- * hash of parsed elements.
+ * <b>Note</b>:
+ * 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]
*
- * This method *does not* function as a validator. If the input
- * string does not match valid formats strictly, you may get a cryptic
- * result. Should consider to use `Date._strptime` or
- * `DateTime._strptime` instead of this method as possible.
+ * If +string+ does not specify a valid date,
+ * the result is unpredictable;
+ * consider using Date._strptime instead.
*
- * If the optional second argument is true and the detected year is in
- * the range "00" to "99", considers the year a 2-digit form and makes
- * it full.
+ * Returns a hash of values parsed from +string+:
*
- * Date._parse('2001-02-03') #=> {:year=>2001, :mon=>2, :mday=>3}
+ * Date._parse('2001-02-03') # => {:year=>2001, :mon=>2, :mday=>3}
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * If +comp+ is +true+ and the given year is in the range <tt>(0..99)</tt>,
+ * the current century is supplied;
+ * otherwise, the year is taken as given:
+ *
+ * Date._parse('01-02-03', true) # => {:year=>2001, :mon=>2, :mday=>3}
+ * Date._parse('01-02-03', false) # => {:year=>1, :mon=>2, :mday=>3}
+ *
+ * See argument {limit}[rdoc-ref:Date@Argument+limit].
+ *
+ * Related: Date.parse(returns a \Date object).
*/
static VALUE
date_s__parse(int argc, VALUE *argv, VALUE klass)
@@ -4496,27 +4544,34 @@ date_s__parse(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date.parse(string = '-4712-01-01', comp = true, start = Date::ITALY, limit: 128) -> date
*
- * Parses the given representation of date and time, and creates a
- * date object.
+ * <b>Note</b>:
+ * 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]
+ * If +string+ does not specify a valid date,
+ * the result is unpredictable;
+ * consider using Date._strptime instead.
*
- * This method *does not* function as a validator. If the input
- * string does not match valid formats strictly, you may get a cryptic
- * result. Should consider to use `Date.strptime` instead of this
- * method as possible.
+ * Returns a new \Date object with values parsed from +string+:
*
- * If the optional second argument is true and the detected year is in
- * the range "00" to "99", considers the year a 2-digit form and makes
- * it full.
+ * Date.parse('2001-02-03') # => #<Date: 2001-02-03>
+ * Date.parse('20010203') # => #<Date: 2001-02-03>
+ * Date.parse('3rd Feb 2001') # => #<Date: 2001-02-03>
*
- * Date.parse('2001-02-03') #=> #<Date: 2001-02-03 ...>
- * Date.parse('20010203') #=> #<Date: 2001-02-03 ...>
- * Date.parse('3rd Feb 2001') #=> #<Date: 2001-02-03 ...>
+ * If +comp+ is +true+ and the given year is in the range <tt>(0..99)</tt>,
+ * the current century is supplied;
+ * otherwise, the year is taken as given:
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * Date.parse('01-02-03', true) # => #<Date: 2001-02-03>
+ * Date.parse('01-02-03', false) # => #<Date: 0001-02-03>
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * See:
+ *
+ * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
+ * - Argument {limit}[rdoc-ref:Date@Argument+limit].
+ *
+ * Related: Date._parse (returns a hash).
*/
static VALUE
date_s_parse(int argc, VALUE *argv, VALUE klass)
@@ -4557,11 +4612,16 @@ VALUE date__jisx0301(VALUE);
* call-seq:
* Date._iso8601(string, limit: 128) -> hash
*
- * Returns a hash of parsed elements.
+ * 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]:
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * d = Date.new(2001, 2, 3)
+ * s = d.iso8601 # => "2001-02-03"
+ * Date._iso8601(s) # => {:mday=>3, :year=>2001, :mon=>2}
+ *
+ * See argument {limit}[rdoc-ref:Date@Argument+limit].
+ *
+ * Related: Date.iso8601 (returns a \Date object).
*/
static VALUE
date_s__iso8601(int argc, VALUE *argv, VALUE klass)
@@ -4578,18 +4638,20 @@ date_s__iso8601(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date.iso8601(string = '-4712-01-01', start = Date::ITALY, limit: 128) -> date
*
- * Creates a new Date object by parsing from a string according to
- * some typical ISO 8601 formats.
+ * 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]:
*
- * Date.iso8601('2001-02-03') #=> #<Date: 2001-02-03 ...>
- * Date.iso8601('20010203') #=> #<Date: 2001-02-03 ...>
- * Date.iso8601('2001-W05-6') #=> #<Date: 2001-02-03 ...>
+ * d = Date.new(2001, 2, 3)
+ * s = d.iso8601 # => "2001-02-03"
+ * Date.iso8601(s) # => #<Date: 2001-02-03>
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * See:
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
+ * - Argument {limit}[rdoc-ref:Date@Argument+limit].
+ *
+ * Related: Date._iso8601 (returns a hash).
*/
static VALUE
date_s_iso8601(int argc, VALUE *argv, VALUE klass)
@@ -4620,11 +4682,17 @@ date_s_iso8601(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date._rfc3339(string, limit: 128) -> hash
*
- * Returns a hash of parsed elements.
+ * Returns a hash of values parsed from +string+, which should be a valid
+ * {RFC 3339 format}[rdoc-ref:strftime_formatting.rdoc@RFC+3339+Format]:
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * d = Date.new(2001, 2, 3)
+ * s = d.rfc3339 # => "2001-02-03T00:00:00+00:00"
+ * Date._rfc3339(s)
+ * # => {:year=>2001, :mon=>2, :mday=>3, :hour=>0, :min=>0, :sec=>0, :zone=>"+00:00", :offset=>0}
+ *
+ * See argument {limit}[rdoc-ref:Date@Argument+limit].
+ *
+ * Related: Date.rfc3339 (returns a \Date object).
*/
static VALUE
date_s__rfc3339(int argc, VALUE *argv, VALUE klass)
@@ -4641,16 +4709,20 @@ date_s__rfc3339(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date.rfc3339(string = '-4712-01-01T00:00:00+00:00', start = Date::ITALY, limit: 128) -> date
*
- * Creates a new Date object by parsing from a string according to
- * some typical RFC 3339 formats.
+ * 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]:
*
- * Date.rfc3339('2001-02-03T04:05:06+07:00') #=> #<Date: 2001-02-03 ...>
+ * d = Date.new(2001, 2, 3)
+ * s = d.rfc3339 # => "2001-02-03T00:00:00+00:00"
+ * Date.rfc3339(s) # => #<Date: 2001-02-03>
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * See:
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
+ * - Argument {limit}[rdoc-ref:Date@Argument+limit].
+ *
+ * Related: Date._rfc3339 (returns a hash).
*/
static VALUE
date_s_rfc3339(int argc, VALUE *argv, VALUE klass)
@@ -4681,11 +4753,16 @@ date_s_rfc3339(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date._xmlschema(string, limit: 128) -> hash
*
- * Returns a hash of parsed elements.
+ * Returns a hash of values parsed from +string+, which should be a valid
+ * XML date format:
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * d = Date.new(2001, 2, 3)
+ * s = d.xmlschema # => "2001-02-03"
+ * Date._xmlschema(s) # => {:year=>2001, :mon=>2, :mday=>3}
+ *
+ * See argument {limit}[rdoc-ref:Date@Argument+limit].
+ *
+ * Related: Date.xmlschema (returns a \Date object).
*/
static VALUE
date_s__xmlschema(int argc, VALUE *argv, VALUE klass)
@@ -4702,17 +4779,19 @@ date_s__xmlschema(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date.xmlschema(string = '-4712-01-01', start = Date::ITALY, limit: 128) -> date
*
- * Creates a new Date object by parsing from a string according to
- * some typical XML Schema formats.
+ * Returns a new \Date object with values parsed from +string+,
+ * which should be a valid XML date format:
*
- * Date.xmlschema('2001-02-03') #=> #<Date: 2001-02-03 ...>
+ * d = Date.new(2001, 2, 3)
+ * s = d.xmlschema # => "2001-02-03"
+ * Date.xmlschema(s) # => #<Date: 2001-02-03>
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * See:
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
+ * - Argument {limit}[rdoc-ref:Date@Argument+limit].
*
+ * Related: Date._xmlschema (returns a hash).
*/
static VALUE
date_s_xmlschema(int argc, VALUE *argv, VALUE klass)
@@ -4743,13 +4822,19 @@ date_s_xmlschema(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date._rfc2822(string, limit: 128) -> hash
*
- * Returns a hash of parsed elements.
+ * 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]:
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * d = Date.new(2001, 2, 3)
+ * s = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000"
+ * Date._rfc2822(s)
+ * # => {:wday=>6, :mday=>3, :mon=>2, :year=>2001, :hour=>0, :min=>0, :sec=>0, :zone=>"+0000", :offset=>0}
+ *
+ * See argument {limit}[rdoc-ref:Date@Argument+limit].
*
* Date._rfc822 is an alias for Date._rfc2822.
+ *
+ * Related: Date.rfc2822 (returns a \Date object).
*/
static VALUE
date_s__rfc2822(int argc, VALUE *argv, VALUE klass)
@@ -4766,19 +4851,22 @@ date_s__rfc2822(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date.rfc2822(string = 'Mon, 1 Jan -4712 00:00:00 +0000', start = Date::ITALY, limit: 128) -> date
*
- * Creates a new Date object by parsing from a string according to
- * some typical RFC 2822 formats.
+ * 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]:
*
- * Date.rfc2822('Sat, 3 Feb 2001 00:00:00 +0000')
- * #=> #<Date: 2001-02-03 ...>
+ * d = Date.new(2001, 2, 3)
+ * s = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000"
+ * Date.rfc2822(s) # => #<Date: 2001-02-03>
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * See:
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
+ * - Argument {limit}[rdoc-ref:Date@Argument+limit].
*
* Date.rfc822 is an alias for Date.rfc2822.
+ *
+ * Related: Date._rfc2822 (returns a hash).
*/
static VALUE
date_s_rfc2822(int argc, VALUE *argv, VALUE klass)
@@ -4808,11 +4896,15 @@ date_s_rfc2822(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date._httpdate(string, limit: 128) -> hash
*
- * Returns a hash of parsed elements.
+ * Returns a hash of values parsed from +string+, which should be a valid
+ * {HTTP date format}[rdoc-ref:strftime_formatting.rdoc@HTTP+Format]:
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * d = Date.new(2001, 2, 3)
+ * s = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT"
+ * Date._httpdate(s)
+ * # => {:wday=>6, :mday=>3, :mon=>2, :year=>2001, :hour=>0, :min=>0, :sec=>0, :zone=>"GMT", :offset=>0}
+ *
+ * Related: Date.httpdate (returns a \Date object).
*/
static VALUE
date_s__httpdate(int argc, VALUE *argv, VALUE klass)
@@ -4829,18 +4921,20 @@ date_s__httpdate(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date.httpdate(string = 'Mon, 01 Jan -4712 00:00:00 GMT', start = Date::ITALY, limit: 128) -> date
*
- * Creates a new Date object by parsing from a string according to
- * some RFC 2616 format.
+ * 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]:
*
- * Date.httpdate('Sat, 03 Feb 2001 00:00:00 GMT')
- * #=> #<Date: 2001-02-03 ...>
+ * d = Date.new(2001, 2, 3)
+ s = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT"
+ Date.httpdate(s) # => #<Date: 2001-02-03>
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * See:
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
+ * - Argument {limit}[rdoc-ref:Date@Argument+limit].
*
+ * Related: Date._httpdate (returns a hash).
*/
static VALUE
date_s_httpdate(int argc, VALUE *argv, VALUE klass)
@@ -4870,11 +4964,16 @@ date_s_httpdate(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date._jisx0301(string, limit: 128) -> hash
*
- * Returns a hash of parsed elements.
+ * 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]:
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * d = Date.new(2001, 2, 3)
+ * s = d.jisx0301 # => "H13.02.03"
+ * Date._jisx0301(s) # => {:year=>2001, :mon=>2, :mday=>3}
+ *
+ * See argument {limit}[rdoc-ref:Date@Argument+limit].
+ *
+ * Related: Date.jisx0301 (returns a \Date object).
*/
static VALUE
date_s__jisx0301(int argc, VALUE *argv, VALUE klass)
@@ -4891,21 +4990,23 @@ date_s__jisx0301(int argc, VALUE *argv, VALUE klass)
* call-seq:
* Date.jisx0301(string = '-4712-01-01', start = Date::ITALY, limit: 128) -> date
*
- * Creates a new Date object by parsing from a string according to
- * some typical JIS X 0301 formats.
+ * 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]:
*
- * Date.jisx0301('H13.02.03') #=> #<Date: 2001-02-03 ...>
+ * d = Date.new(2001, 2, 3)
+ * s = d.jisx0301 # => "H13.02.03"
+ * Date.jisx0301(s) # => #<Date: 2001-02-03>
*
* For no-era year, legacy format, Heisei is assumed.
*
- * Date.jisx0301('13.02.03') #=> #<Date: 2001-02-03 ...>
+ * Date.jisx0301('13.02.03') # => #<Date: 2001-02-03>
*
- * Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * See:
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
+ * - Argument {limit}[rdoc-ref:Date@Argument+limit].
*
+ * Related: Date._jisx0301 (returns a hash).
*/
static VALUE
date_s_jisx0301(int argc, VALUE *argv, VALUE klass)
@@ -5097,6 +5198,7 @@ d_lite_initialize_copy(VALUE copy, VALUE date)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
d_lite_fill(VALUE self)
{
@@ -5342,6 +5444,7 @@ d_lite_cwday(VALUE self)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
d_lite_wnum0(VALUE self)
{
@@ -5349,6 +5452,7 @@ d_lite_wnum0(VALUE self)
return INT2FIX(m_wnum0(dat));
}
+/* :nodoc: */
static VALUE
d_lite_wnum1(VALUE self)
{
@@ -5465,6 +5569,7 @@ d_lite_saturday_p(VALUE self)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
d_lite_nth_kday_p(VALUE self, VALUE n, VALUE k)
{
@@ -5662,7 +5767,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:Date@Argument+start].
+ * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
*
*/
static VALUE
@@ -5737,7 +5842,7 @@ dup_obj_with_new_start(VALUE obj, double sg)
* d1 = d0.new_start(Date::JULIAN)
* d1.julian? # => true
*
- * See argument {start}[rdoc-ref:Date@Argument+start].
+ * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
*
*/
static VALUE
@@ -6315,9 +6420,9 @@ d_lite_prev_day(int argc, VALUE *argv, VALUE self)
*
* Returns a new \Date object representing the following day:
*
- * d = Date.today
- * d.to_s # => "2022-07-11"
- * d.next.to_s # => "2022-07-12"
+ * d = Date.new(2001, 2, 3)
+ * d.to_s # => "2001-02-03"
+ * d.next.to_s # => "2001-02-04"
*
* Date#succ is an alias for Date#next.
*/
@@ -6679,19 +6784,43 @@ cmp_dd(VALUE self, VALUE other)
/*
* call-seq:
- * d <=> other -> -1, 0, +1 or nil
+ * self <=> other -> -1, 0, 1 or nil
+ *
+ * Compares +self+ and +other+, returning:
+ *
+ * - <tt>-1</tt> if +other+ is larger.
+ * - <tt>0</tt> if the two are equal.
+ * - <tt>1</tt> if +other+ is smaller.
+ * - +nil+ if the two are incomparable.
+ *
+ * Argument +other+ may be:
+ *
+ * - Another \Date object:
+ *
+ * d = Date.new(2022, 7, 27) # => #<Date: 2022-07-27 ((2459788j,0s,0n),+0s,2299161j)>
+ * prev_date = d.prev_day # => #<Date: 2022-07-26 ((2459787j,0s,0n),+0s,2299161j)>
+ * next_date = d.next_day # => #<Date: 2022-07-28 ((2459789j,0s,0n),+0s,2299161j)>
+ * d <=> next_date # => -1
+ * d <=> d # => 0
+ * d <=> prev_date # => 1
+ *
+ * - A DateTime object:
+ *
+ * d <=> DateTime.new(2022, 7, 26) # => 1
+ * d <=> DateTime.new(2022, 7, 27) # => 0
+ * d <=> DateTime.new(2022, 7, 28) # => -1
*
- * Compares the two dates and returns -1, zero, 1 or nil. The other
- * should be a date object or a numeric value as an astronomical
- * Julian day number.
+ * - A numeric (compares <tt>self.ajd</tt> to +other+):
*
- * Date.new(2001,2,3) <=> Date.new(2001,2,4) #=> -1
- * Date.new(2001,2,3) <=> Date.new(2001,2,3) #=> 0
- * Date.new(2001,2,3) <=> Date.new(2001,2,2) #=> 1
- * Date.new(2001,2,3) <=> Object.new #=> nil
- * Date.new(2001,2,3) <=> Rational(4903887,2) #=> 0
+ * d <=> 2459788 # => -1
+ * d <=> 2459787 # => 1
+ * d <=> 2459786 # => 1
+ * d <=> d.ajd # => 0
+ *
+ * - Any other object:
+ *
+ * d <=> Object.new # => nil
*
- * See also Comparable.
*/
static VALUE
d_lite_cmp(VALUE self, VALUE other)
@@ -6751,20 +6880,39 @@ equal_gen(VALUE self, VALUE other)
/*
* call-seq:
- * d === other -> bool
- *
- * Returns true if they are the same day.
- *
- * Date.new(2001,2,3) === Date.new(2001,2,3)
- * #=> true
- * Date.new(2001,2,3) === Date.new(2001,2,4)
- * #=> false
- * DateTime.new(2001,2,3) === DateTime.new(2001,2,3,12)
- * #=> true
- * DateTime.new(2001,2,3) === DateTime.new(2001,2,3,0,0,0,'+24:00')
- * #=> true
- * DateTime.new(2001,2,3) === DateTime.new(2001,2,4,0,0,0,'+24:00')
- * #=> false
+ * self === other -> true, false, or nil.
+ *
+ * Returns +true+ if +self+ and +other+ represent the same date,
+ * +false+ if not, +nil+ if the two are not comparable.
+ *
+ * Argument +other+ may be:
+ *
+ * - Another \Date object:
+ *
+ * d = Date.new(2022, 7, 27) # => #<Date: 2022-07-27 ((2459788j,0s,0n),+0s,2299161j)>
+ * prev_date = d.prev_day # => #<Date: 2022-07-26 ((2459787j,0s,0n),+0s,2299161j)>
+ * next_date = d.next_day # => #<Date: 2022-07-28 ((2459789j,0s,0n),+0s,2299161j)>
+ * d === prev_date # => false
+ * d === d # => true
+ * d === next_date # => false
+ *
+ * - A DateTime object:
+ *
+ * d === DateTime.new(2022, 7, 26) # => false
+ * d === DateTime.new(2022, 7, 27) # => true
+ * d === DateTime.new(2022, 7, 28) # => false
+ *
+ * - A numeric (compares <tt>self.jd</tt> to +other+):
+ *
+ * d === 2459788 # => true
+ * d === 2459787 # => false
+ * d === 2459786 # => false
+ * d === d.jd # => true
+ *
+ * - An object not comparable:
+ *
+ * d === Object.new # => nil
+ *
*/
static VALUE
d_lite_equal(VALUE self, VALUE other)
@@ -6827,12 +6975,14 @@ static VALUE strftimev(const char *, VALUE,
/*
* call-seq:
- * d.to_s -> string
+ * to_s -> string
*
- * Returns a string in an ISO 8601 format. (This method doesn't use the
- * expanded representations.)
+ * Returns a string representation of the date in +self+
+ * in {ISO 8601 extended date format}[rdoc-ref:strftime_formatting.rdoc@ISO+8601+Format+Specifications]
+ * (<tt>'%Y-%m-%d'</tt>):
+ *
+ * Date.new(2001, 2, 3).to_s # => "2001-02-03"
*
- * Date.new(2001,2,3).to_s #=> "2001-02-03"
*/
static VALUE
d_lite_to_s(VALUE self)
@@ -6841,6 +6991,7 @@ d_lite_to_s(VALUE self)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
mk_inspect_raw(union DateData *x, VALUE klass)
{
@@ -6890,6 +7041,7 @@ mk_inspect_raw(union DateData *x, VALUE klass)
}
}
+/* :nodoc: */
static VALUE
d_lite_inspect_raw(VALUE self)
{
@@ -6911,14 +7063,13 @@ mk_inspect(union DateData *x, VALUE klass, VALUE to_s)
/*
* call-seq:
- * d.inspect -> string
+ * inspect -> string
*
- * Returns the value as a string for inspection.
+ * Returns a string representation of +self+:
+ *
+ * Date.new(2001, 2, 3).inspect
+ * # => "#<Date: 2001-02-03 ((2451944j,0s,0n),+0s,2299161j)>"
*
- * Date.new(2001,2,3).inspect
- * #=> "#<Date: 2001-02-03>"
- * DateTime.new(2001,2,3,4,5,6,'-7').inspect
- * #=> "#<DateTime: 2001-02-03T04:05:06-07:00>"
*/
static VALUE
d_lite_inspect(VALUE self)
@@ -7100,15 +7251,15 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self,
/*
* call-seq:
- * strftime(format = '%F') -> string
+ * strftime(format = '%F') -> string
*
- * Returns a string representation of +self+,
+ * Returns a string representation of the date in +self+,
* formatted according the given +format+:
*
- * Date.today.strftime # => "2022-07-01"
+ * Date.new(2001, 2, 3).strftime # => "2001-02-03"
*
* For other formats, see
- * {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html].
+ * {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc].
*
*/
static VALUE
@@ -7137,13 +7288,17 @@ strftimev(const char *fmt, VALUE self,
/*
* call-seq:
- * d.asctime -> string
- * d.ctime -> string
+ * 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]
+ * <tt>'%c'</tt>):
*
- * Returns a string in asctime(3) format (but without "\n\0" at the
- * end). This method is equivalent to strftime('%c').
+ * Date.new(2001, 2, 3).asctime # => "Sat Feb 3 00:00:00 2001"
*
- * See also asctime(3) or ctime(3).
+ * See {asctime}[https://linux.die.net/man/3/asctime].
+ *
+ * Date#ctime is an alias for Date#asctime.
*/
static VALUE
d_lite_asctime(VALUE self)
@@ -7153,10 +7308,15 @@ d_lite_asctime(VALUE self)
/*
* call-seq:
- * d.iso8601 -> string
- * d.xmlschema -> string
+ * iso8601 -> string
+ *
+ * Equivalent to #strftime with argument <tt>'%Y-%m-%d'</tt>
+ * (or its {shorthand form}[rdoc-ref:strftime_formatting.rdoc@Shorthand+Conversion+Specifiers]
+ * <tt>'%F'</tt>);
*
- * This method is equivalent to strftime('%F').
+ * Date.new(2001, 2, 3).iso8601 # => "2001-02-03"
+ *
+ * Date#xmlschema is an alias for Date#iso8601.
*/
static VALUE
d_lite_iso8601(VALUE self)
@@ -7166,9 +7326,13 @@ d_lite_iso8601(VALUE self)
/*
* call-seq:
- * d.rfc3339 -> string
+ * rfc3339 -> string
+ *
+ * Equivalent to #strftime with argument <tt>'%FT%T%:z'</tt>;
+ * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]:
+ *
+ * Date.new(2001, 2, 3).rfc3339 # => "2001-02-03T00:00:00+00:00"
*
- * This method is equivalent to strftime('%FT%T%:z').
*/
static VALUE
d_lite_rfc3339(VALUE self)
@@ -7178,10 +7342,14 @@ d_lite_rfc3339(VALUE self)
/*
* call-seq:
- * d.rfc2822 -> string
- * d.rfc822 -> string
+ * 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]:
+ *
+ * Date.new(2001, 2, 3).rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000"
*
- * This method is equivalent to strftime('%a, %-d %b %Y %T %z').
+ * Date#rfc822 is an alias for Date#rfc2822.
*/
static VALUE
d_lite_rfc2822(VALUE self)
@@ -7191,10 +7359,13 @@ d_lite_rfc2822(VALUE self)
/*
* call-seq:
- * d.httpdate -> string
+ * 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]:
+ *
+ * Date.new(2001, 2, 3).httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT"
*
- * This method is equivalent to strftime('%a, %d %b %Y %T GMT').
- * See also RFC 2616.
*/
static VALUE
d_lite_httpdate(VALUE self)
@@ -7245,11 +7416,13 @@ jisx0301_date_format(char *fmt, size_t size, VALUE jd, VALUE y)
/*
* call-seq:
- * d.jisx0301 -> string
+ * jisx0301 -> string
*
- * Returns a string in a JIS X 0301 format.
+ * Returns a string representation of the date in +self+
+ * in JIS X 0301 format.
+ *
+ * Date.new(2001, 2, 3).jisx0301 # => "H13.02.03"
*
- * Date.new(2001,2,3).jisx0301 #=> "H13.02.03"
*/
static VALUE
d_lite_jisx0301(VALUE self)
@@ -7264,7 +7437,98 @@ d_lite_jisx0301(VALUE self)
return strftimev(fmt, self, set_tmx);
}
+static VALUE
+deconstruct_keys(VALUE self, VALUE keys, int is_datetime)
+{
+ VALUE h = rb_hash_new();
+ long i;
+
+ get_d1(self);
+
+ if (NIL_P(keys)) {
+ rb_hash_aset(h, sym_year, m_real_year(dat));
+ rb_hash_aset(h, sym_month, INT2FIX(m_mon(dat)));
+ rb_hash_aset(h, sym_day, INT2FIX(m_mday(dat)));
+ rb_hash_aset(h, sym_yday, INT2FIX(m_yday(dat)));
+ rb_hash_aset(h, sym_wday, INT2FIX(m_wday(dat)));
+ if (is_datetime) {
+ rb_hash_aset(h, sym_hour, INT2FIX(m_hour(dat)));
+ rb_hash_aset(h, sym_min, INT2FIX(m_min(dat)));
+ rb_hash_aset(h, sym_sec, INT2FIX(m_sec(dat)));
+ rb_hash_aset(h, sym_sec_fraction, m_sf_in_sec(dat));
+ rb_hash_aset(h, sym_zone, m_zone(dat));
+ }
+
+ return h;
+ }
+ if (!RB_TYPE_P(keys, T_ARRAY)) {
+ rb_raise(rb_eTypeError,
+ "wrong argument type %"PRIsVALUE" (expected Array or nil)",
+ rb_obj_class(keys));
+
+ }
+
+ for (i=0; i<RARRAY_LEN(keys); i++) {
+ VALUE key = RARRAY_AREF(keys, i);
+
+ if (sym_year == key) rb_hash_aset(h, key, m_real_year(dat));
+ if (sym_month == key) rb_hash_aset(h, key, INT2FIX(m_mon(dat)));
+ if (sym_day == key) rb_hash_aset(h, key, INT2FIX(m_mday(dat)));
+ if (sym_yday == key) rb_hash_aset(h, key, INT2FIX(m_yday(dat)));
+ if (sym_wday == key) rb_hash_aset(h, key, INT2FIX(m_wday(dat)));
+ if (is_datetime) {
+ if (sym_hour == key) rb_hash_aset(h, key, INT2FIX(m_hour(dat)));
+ if (sym_min == key) rb_hash_aset(h, key, INT2FIX(m_min(dat)));
+ if (sym_sec == key) rb_hash_aset(h, key, INT2FIX(m_sec(dat)));
+ if (sym_sec_fraction == key) rb_hash_aset(h, key, m_sf_in_sec(dat));
+ if (sym_zone == key) rb_hash_aset(h, key, m_zone(dat));
+ }
+ }
+ return h;
+}
+
+/*
+ * call-seq:
+ * deconstruct_keys(array_of_names_or_nil) -> hash
+ *
+ * Returns a hash of the name/value pairs, to use in pattern matching.
+ * Possible keys are: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>,
+ * <tt>:wday</tt>, <tt>:yday</tt>.
+ *
+ * Possible usages:
+ *
+ * d = Date.new(2022, 10, 5)
+ *
+ * if d in wday: 3, day: ..7 # uses deconstruct_keys underneath
+ * puts "first Wednesday of the month"
+ * end
+ * #=> prints "first Wednesday of the month"
+ *
+ * case d
+ * in year: ...2022
+ * puts "too old"
+ * in month: ..9
+ * puts "quarter 1-3"
+ * in wday: 1..5, month:
+ * puts "working day in month #{month}"
+ * end
+ * #=> prints "working day in month 10"
+ *
+ * Note that deconstruction by pattern can also be combined with class check:
+ *
+ * if d in Date(wday: 3, day: ..7)
+ * puts "first Wednesday of the month"
+ * end
+ *
+ */
+static VALUE
+d_lite_deconstruct_keys(VALUE self, VALUE keys)
+{
+ return deconstruct_keys(self, keys, /* is_datetime=false */ 0);
+}
+
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
d_lite_marshal_dump_old(VALUE self)
{
@@ -7551,6 +7815,9 @@ datetime_s_ordinal(int argc, VALUE *argv, VALUE klass)
return ret;
}
+/*
+ * Same as DateTime.new.
+ */
static VALUE
datetime_s_civil(int argc, VALUE *argv, VALUE klass)
{
@@ -7739,6 +8006,7 @@ datetime_s_commercial(int argc, VALUE *argv, VALUE klass)
}
#ifndef NDEBUG
+/* :nodoc: */
static VALUE
datetime_s_weeknum(int argc, VALUE *argv, VALUE klass)
{
@@ -7808,6 +8076,7 @@ datetime_s_weeknum(int argc, VALUE *argv, VALUE klass)
return ret;
}
+/* :nodoc: */
static VALUE
datetime_s_nth_kday(int argc, VALUE *argv, VALUE klass)
{
@@ -8153,9 +8422,9 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass)
* Parses the given representation of date and time, and creates a
* DateTime object.
*
- * This method *does not* function as a validator. If the input
+ * This method *does* *not* function as a validator. If the input
* string does not match valid formats strictly, you may get a cryptic
- * result. Should consider to use `DateTime.strptime` instead of this
+ * result. Should consider to use DateTime.strptime instead of this
* method as possible.
*
* If the optional second argument is true and the detected year is in
@@ -8169,8 +8438,8 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T16:05:06+00:00 ...>
*
* Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * You can stop this check by passing <code>limit: nil</code>, but note
+ * that it may take a long time to parse.
*/
static VALUE
datetime_s_parse(int argc, VALUE *argv, VALUE klass)
@@ -8216,8 +8485,8 @@ datetime_s_parse(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
*
* Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * You can stop this check by passing <code>limit: nil</code>, but note
+ * that it may take a long time to parse.
*/
static VALUE
datetime_s_iso8601(int argc, VALUE *argv, VALUE klass)
@@ -8256,8 +8525,8 @@ datetime_s_iso8601(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
*
* Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * You can stop this check by passing <code>limit: nil</code>, but note
+ * that it may take a long time to parse.
*/
static VALUE
datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass)
@@ -8296,8 +8565,8 @@ datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
*
* Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * You can stop this check by passing <code>limit: nil</code>, but note
+ * that it may take a long time to parse.
*/
static VALUE
datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass)
@@ -8337,8 +8606,8 @@ datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
*
* Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * You can stop this check by passing <code>limit: nil</code>, but note
+ * that it may take a long time to parse.
*/
static VALUE
datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass)
@@ -8377,8 +8646,8 @@ datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T04:05:06+00:00 ...>
*
* Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * You can stop this check by passing <code>limit: nil</code>, but note
+ * that it may take a long time to parse.
*/
static VALUE
datetime_s_httpdate(int argc, VALUE *argv, VALUE klass)
@@ -8422,8 +8691,8 @@ datetime_s_httpdate(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
*
* Raise an ArgumentError when the string length is longer than _limit_.
- * You can stop this check by passing `limit: nil`, but note that
- * it may take a long time to parse.
+ * You can stop this check by passing <code>limit: nil</code>, but note
+ * that it may take a long time to parse.
*/
static VALUE
datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass)
@@ -8566,6 +8835,47 @@ dt_lite_jisx0301(int argc, VALUE *argv, VALUE self)
iso8601_timediv(self, n));
}
+/*
+ * call-seq:
+ * deconstruct_keys(array_of_names_or_nil) -> hash
+ *
+ * Returns a hash of the name/value pairs, to use in pattern matching.
+ * Possible keys are: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>,
+ * <tt>:wday</tt>, <tt>:yday</tt>, <tt>:hour</tt>, <tt>:min</tt>,
+ * <tt>:sec</tt>, <tt>:sec_fraction</tt>, <tt>:zone</tt>.
+ *
+ * Possible usages:
+ *
+ * dt = DateTime.new(2022, 10, 5, 13, 30)
+ *
+ * if d in wday: 1..5, hour: 10..18 # uses deconstruct_keys underneath
+ * puts "Working time"
+ * end
+ * #=> prints "Working time"
+ *
+ * case dt
+ * in year: ...2022
+ * puts "too old"
+ * in month: ..9
+ * puts "quarter 1-3"
+ * in wday: 1..5, month:
+ * puts "working day in month #{month}"
+ * end
+ * #=> prints "working day in month 10"
+ *
+ * Note that deconstruction by pattern can also be combined with class check:
+ *
+ * if d in DateTime(wday: 1..5, hour: 10..18, day: ..7)
+ * puts "Working time, first week of the month"
+ * end
+ *
+ */
+static VALUE
+dt_lite_deconstruct_keys(VALUE self, VALUE keys)
+{
+ return deconstruct_keys(self, keys, /* is_datetime=true */ 1);
+}
+
/* conversions */
#define f_subsec(x) rb_funcall(x, rb_intern("subsec"), 0)
@@ -8644,7 +8954,7 @@ time_to_datetime(VALUE self)
ret = d_complex_new_internal(cDateTime,
nth, 0,
0, sf,
- of, DEFAULT_SG,
+ of, GREGORIAN,
ry, m, d,
h, min, s,
HAVE_CIVIL | HAVE_TIME);
@@ -8657,10 +8967,15 @@ time_to_datetime(VALUE self)
/*
* call-seq:
- * d.to_time -> time
+ * to_time -> time
+ *
+ * Returns a new Time object with the same value as +self+;
+ * if +self+ is a Julian date, derives its Gregorian date
+ * for conversion to the \Time object:
+ *
+ * Date.new(2001, 2, 3).to_time # => 2001-02-03 00:00:00 -0600
+ * Date.new(2001, 2, 3, Date::JULIAN).to_time # => 2001-02-16 00:00:00 -0600
*
- * Returns a Time object which denotes self. If self is a julian date,
- * convert it to a gregorian date before converting it to Time.
*/
static VALUE
date_to_time(VALUE self)
@@ -8681,9 +8996,9 @@ date_to_time(VALUE self)
/*
* call-seq:
- * d.to_date -> self
+ * to_date -> self
*
- * Returns self.
+ * Returns +self+.
*/
static VALUE
date_to_date(VALUE self)
@@ -8695,7 +9010,10 @@ date_to_date(VALUE self)
* call-seq:
* d.to_datetime -> datetime
*
- * Returns a DateTime object which denotes self.
+ * Returns a DateTime whose value is the same as +self+:
+ *
+ * Date.new(2001, 2, 3).to_datetime # => #<DateTime: 2001-02-03T00:00:00+00:00>
+ *
*/
static VALUE
date_to_datetime(VALUE self)
@@ -8740,12 +9058,17 @@ date_to_datetime(VALUE self)
static VALUE
datetime_to_time(VALUE self)
{
- volatile VALUE dup = dup_obj(self);
+ get_d1(self);
+
+ if (m_julian_p(dat)) {
+ self = d_lite_gregorian(self);
+ get_d1a(self);
+ dat = adat;
+ }
+
{
VALUE t;
- get_d1(dup);
-
t = rb_funcall(rb_cTime,
rb_intern("new"),
7,
@@ -8813,6 +9136,7 @@ datetime_to_datetime(VALUE self)
#define MIN_JD -327
#define MAX_JD 366963925
+/* :nodoc: */
static int
test_civil(int from, int to, double sg)
{
@@ -8833,6 +9157,7 @@ test_civil(int from, int to, double sg)
return 1;
}
+/* :nodoc: */
static VALUE
date_s_test_civil(VALUE klass)
{
@@ -8853,6 +9178,7 @@ date_s_test_civil(VALUE klass)
return Qtrue;
}
+/* :nodoc: */
static int
test_ordinal(int from, int to, double sg)
{
@@ -8873,6 +9199,7 @@ test_ordinal(int from, int to, double sg)
return 1;
}
+/* :nodoc: */
static VALUE
date_s_test_ordinal(VALUE klass)
{
@@ -8893,6 +9220,7 @@ date_s_test_ordinal(VALUE klass)
return Qtrue;
}
+/* :nodoc: */
static int
test_commercial(int from, int to, double sg)
{
@@ -8913,6 +9241,7 @@ test_commercial(int from, int to, double sg)
return 1;
}
+/* :nodoc: */
static VALUE
date_s_test_commercial(VALUE klass)
{
@@ -8933,6 +9262,7 @@ date_s_test_commercial(VALUE klass)
return Qtrue;
}
+/* :nodoc: */
static int
test_weeknum(int from, int to, int f, double sg)
{
@@ -8953,6 +9283,7 @@ test_weeknum(int from, int to, int f, double sg)
return 1;
}
+/* :nodoc: */
static VALUE
date_s_test_weeknum(VALUE klass)
{
@@ -8977,6 +9308,7 @@ date_s_test_weeknum(VALUE klass)
return Qtrue;
}
+/* :nodoc: */
static int
test_nth_kday(int from, int to, double sg)
{
@@ -8997,6 +9329,7 @@ test_nth_kday(int from, int to, double sg)
return 1;
}
+/* :nodoc: */
static VALUE
date_s_test_nth_kday(VALUE klass)
{
@@ -9017,6 +9350,7 @@ date_s_test_nth_kday(VALUE klass)
return Qtrue;
}
+/* :nodoc: */
static int
test_unit_v2v(VALUE i,
VALUE (* conv1)(VALUE),
@@ -9028,6 +9362,7 @@ test_unit_v2v(VALUE i,
return f_eqeq_p(o, i);
}
+/* :nodoc: */
static int
test_unit_v2v_iter2(VALUE (* conv1)(VALUE),
VALUE (* conv2)(VALUE))
@@ -9059,6 +9394,7 @@ test_unit_v2v_iter2(VALUE (* conv1)(VALUE),
return 1;
}
+/* :nodoc: */
static int
test_unit_v2v_iter(VALUE (* conv1)(VALUE),
VALUE (* conv2)(VALUE))
@@ -9070,6 +9406,7 @@ test_unit_v2v_iter(VALUE (* conv1)(VALUE),
return 1;
}
+/* :nodoc: */
static VALUE
date_s_test_unit_conv(VALUE klass)
{
@@ -9084,6 +9421,7 @@ date_s_test_unit_conv(VALUE klass)
return Qtrue;
}
+/* :nodoc: */
static VALUE
date_s_test_all(VALUE klass)
{
@@ -9168,6 +9506,17 @@ Init_date_core(void)
id_ge_p = rb_intern_const(">=");
id_eqeq_p = rb_intern_const("==");
+ sym_year = ID2SYM(rb_intern_const("year"));
+ sym_month = ID2SYM(rb_intern_const("month"));
+ sym_yday = ID2SYM(rb_intern_const("yday"));
+ sym_wday = ID2SYM(rb_intern_const("wday"));
+ sym_day = ID2SYM(rb_intern_const("day"));
+ sym_hour = ID2SYM(rb_intern_const("hour"));
+ sym_min = ID2SYM(rb_intern_const("min"));
+ sym_sec = ID2SYM(rb_intern_const("sec"));
+ sym_sec_fraction = ID2SYM(rb_intern_const("sec_fraction"));
+ sym_zone = ID2SYM(rb_intern_const("zone"));
+
half_days_in_day = rb_rational_new2(INT2FIX(1), INT2FIX(2));
#if (LONG_MAX / DAY_IN_SECONDS) > SECOND_IN_NANOSECONDS
@@ -9188,200 +9537,81 @@ Init_date_core(void)
negative_inf = -INFINITY;
/*
- * date and datetime class - Tadayoshi Funaba 1998-2011
- *
- * 'date' provides two classes: Date and DateTime.
- *
- * == Terms and Definitions
+ * \Class \Date provides methods for storing and manipulating
+ * calendar dates.
*
- * Some terms and definitions are based on ISO 8601 and JIS X 0301.
+ * Consider using
+ * {class Time}[rdoc-ref:Time]
+ * instead of class \Date if:
*
- * === Calendar Date
+ * - 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].
*
- * The calendar date is a particular day of a calendar year,
- * identified by its ordinal number within a calendar month within
- * that year.
+ * A \Date object, once created, is immutable, and cannot be modified.
*
- * In those classes, this is so-called "civil".
+ * == Creating a \Date
*
- * === Ordinal Date
+ * You can create a date for the current date, using Date.today:
*
- * The ordinal date is a particular day of a calendar year identified
- * by its ordinal number within the year.
+ * Date.today # => #<Date: 1999-12-31>
*
- * In those classes, this is so-called "ordinal".
+ * You can create a specific date from various combinations of arguments:
*
- * === Week Date
+ * - Date.new takes integer year, month, and day-of-month:
*
- * The week date is a date identified by calendar week and day numbers.
+ * Date.new(1999, 12, 31) # => #<Date: 1999-12-31>
*
- * The calendar week is a seven day period within a calendar year,
- * starting on a Monday and identified by its ordinal number within
- * the year; the first calendar week of the year is the one that
- * includes the first Thursday of that year. In the Gregorian
- * calendar, this is equivalent to the week which includes January 4.
+ * - Date.ordinal takes integer year and day-of-year:
*
- * In those classes, this is so-called "commercial".
+ * Date.ordinal(1999, 365) # => #<Date: 1999-12-31>
*
- * === Julian Day Number
+ * - Date.jd takes integer Julian day:
*
- * The Julian day number is in elapsed days since noon (Greenwich Mean
- * Time) on January 1, 4713 BCE (in the Julian calendar).
+ * Date.jd(2451544) # => #<Date: 1999-12-31>
*
- * In this document, the astronomical Julian day number is the same as
- * the original Julian day number. And the chronological Julian day
- * number is a variation of the Julian day number. Its days begin at
- * midnight on local time.
+ * - Date.commercial takes integer commercial data (year, week, day-of-week):
*
- * In this document, when the term "Julian day number" simply appears,
- * it just refers to "chronological Julian day number", not the
- * original.
+ * Date.commercial(1999, 52, 5) # => #<Date: 1999-12-31>
*
- * In those classes, those are so-called "ajd" and "jd".
+ * - Date.parse takes a string, which it parses heuristically:
*
- * === Modified Julian Day Number
+ * Date.parse('1999-12-31') # => #<Date: 1999-12-31>
+ * Date.parse('31-12-1999') # => #<Date: 1999-12-31>
+ * Date.parse('1999-365') # => #<Date: 1999-12-31>
+ * Date.parse('1999-W52-5') # => #<Date: 1999-12-31>
*
- * The modified Julian day number is in elapsed days since midnight
- * (Coordinated Universal Time) on November 17, 1858 CE (in the
- * Gregorian calendar).
+ * - Date.strptime takes a date string and a format string,
+ * then parses the date string according to the format string:
*
- * In this document, the astronomical modified Julian day number is
- * the same as the original modified Julian day number. And the
- * chronological modified Julian day number is a variation of the
- * modified Julian day number. Its days begin at midnight on local
- * time.
+ * Date.strptime('1999-12-31', '%Y-%m-%d') # => #<Date: 1999-12-31>
+ * Date.strptime('31-12-1999', '%d-%m-%Y') # => #<Date: 1999-12-31>
+ * Date.strptime('1999-365', '%Y-%j') # => #<Date: 1999-12-31>
+ * Date.strptime('1999-W52-5', '%G-W%V-%u') # => #<Date: 1999-12-31>
+ * Date.strptime('1999 52 5', '%Y %U %w') # => #<Date: 1999-12-31>
+ * Date.strptime('1999 52 5', '%Y %W %u') # => #<Date: 1999-12-31>
+ * Date.strptime('fri31dec99', '%a%d%b%y') # => #<Date: 1999-12-31>
*
- * In this document, when the term "modified Julian day number" simply
- * appears, it just refers to "chronological modified Julian day
- * number", not the original.
+ * See also the specialized methods in
+ * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc@Specialized+Format+Strings]
*
- * In those classes, those are so-called "amjd" and "mjd".
+ * == Argument +limit+
*
- * == Date
+ * Certain singleton methods in \Date that parse string arguments
+ * also take optional keyword argument +limit+,
+ * which can limit the length of the string argument.
*
- * A subclass of Object that includes the Comparable module and
- * easily handles date.
+ * When +limit+ is:
*
- * A Date object is created with Date::new, Date::jd, Date::ordinal,
- * Date::commercial, Date::parse, Date::strptime, Date::today,
- * Time#to_date, etc.
- *
- * require 'date'
- *
- * Date.new(2001,2,3)
- * #=> #<Date: 2001-02-03 ...>
- * Date.jd(2451944)
- * #=> #<Date: 2001-02-03 ...>
- * Date.ordinal(2001,34)
- * #=> #<Date: 2001-02-03 ...>
- * Date.commercial(2001,5,6)
- * #=> #<Date: 2001-02-03 ...>
- * Date.parse('2001-02-03')
- * #=> #<Date: 2001-02-03 ...>
- * Date.strptime('03-02-2001', '%d-%m-%Y')
- * #=> #<Date: 2001-02-03 ...>
- * Time.new(2001,2,3).to_date
- * #=> #<Date: 2001-02-03 ...>
- *
- * All date objects are immutable; hence cannot modify themselves.
- *
- * The concept of a date object can be represented as a tuple
- * of the day count, the offset and the day of calendar reform.
- *
- * The day count denotes the absolute position of a temporal
- * dimension. The offset is relative adjustment, which determines
- * decoded local time with the day count. The day of calendar
- * reform denotes the start day of the new style. The old style
- * of the West is the Julian calendar which was adopted by
- * Caesar. The new style is the Gregorian calendar, which is the
- * current civil calendar of many countries.
- *
- * The day count is virtually the astronomical Julian day number.
- * The offset in this class is usually zero, and cannot be
- * specified directly.
- *
- * A Date object can be created with an optional argument,
- * the day of calendar reform as a Julian day number, which
- * should be 2298874 to 2426355 or negative/positive infinity.
- * The default value is +Date::ITALY+ (2299161=1582-10-15).
- * See also sample/cal.rb.
- *
- * $ ruby sample/cal.rb -c it 10 1582
- * October 1582
- * S M Tu W Th F S
- * 1 2 3 4 15 16
- * 17 18 19 20 21 22 23
- * 24 25 26 27 28 29 30
- * 31
- *
- * $ ruby sample/cal.rb -c gb 9 1752
- * September 1752
- * S M Tu W Th F S
- * 1 2 14 15 16
- * 17 18 19 20 21 22 23
- * 24 25 26 27 28 29 30
- *
- * A Date object has various methods. See each reference.
- *
- * d = Date.parse('3rd Feb 2001')
- * #=> #<Date: 2001-02-03 ...>
- * d.year #=> 2001
- * d.mon #=> 2
- * d.mday #=> 3
- * d.wday #=> 6
- * d += 1 #=> #<Date: 2001-02-04 ...>
- * d.strftime('%a %d %b %Y') #=> "Sun 04 Feb 2001"
- *
- * === Argument +start+
- *
- * Certain calculations and comparisons for a \Date object
- * are affected by what the object considers to have been
- * the changeover date from the
- * {Julian}[https://en.wikipedia.org/wiki/Julian_calendar] to the
- * {Gregorian}[https://en.wikipedia.org/wiki/Gregorian_calendar]
- * calendar;
- * this is set by argument +start+ when the object is created:
- *
- * - Dates before the changeover are considered to be Julian.
- * - Dates after the changeover are considered to be Gregorian.
- *
- * The value of the +start+ argument may be:
- *
- * - Date::ITALY (the default) - the changeover date is October 10, 1582:
- *
- * Date::ITALY # => 2299161
- * Date.jd(Date::ITALY).to_s # => "1582-10-15"
- *
- * # Julian base date, Julian result date.
- * (Date.new(1581, 1, 1, Date::ITALY) + 365).to_s # => "1582-01-01"
- * # Gregorian base date, Gregorian result date.
- * (Date.new(1583, 1, 1, Date::ITALY) + 365).to_s # => "1584-01-01"
- *
- * # Julian base date, Gregorian result date.
- * (Date.new(1582, 1, 1, Date::ITALY) + 365).to_s # => "1583-01-11"
- * # Gregorian base date, Julian result date.
- * (Date.new(1583, 1, 1, Date::ITALY) - 365).to_s # => "1581-12-22"
- *
- * - Date::ENGLAND - the changeover date is September 9, 1752:
- *
- * Date::ENGLAND # => 2361222
- * Date.jd(Date::ENGLAND).to_s # => "1752-09-14"
- *
- * # Julian base date, Julian result date.
- * (Date.new(1751, 1, 1, Date::ENGLAND) + 365).to_s # => "1752-01-01"
- * # Gregorian base date, Gregorian result date.
- * (Date.new(1753, 1, 1, Date::ENGLAND) + 365).to_s # => "1754-01-01"
- *
- * # Julian base date, Gregorian result date.
- * (Date.new(1752, 1, 1, Date::ENGLAND) + 365).to_s # => "1753-01-11"
- * # Gregorian base date, Julian result date.
- * (Date.new(1753, 1, 1, Date::ENGLAND) - 365).to_s # => "1751-12-22"
- *
- * - Date::JULIAN - no changeover date; all dates are Julian.
- * - Date::GREGORIAN - no changeover date; all dates are Gregorian.
+ * - Non-negative:
+ * raises ArgumentError if the string length is greater than _limit_.
+ * - Other numeric or +nil+: ignores +limit+.
+ * - Other non-numeric: raises TypeError.
*
*/
cDate = rb_define_class("Date", rb_cObject);
+
+ /* Exception for invalid date/time */
eDateError = rb_define_class_under(cDate, "Error", rb_eArgError);
rb_include_module(cDate, rb_mComparable);
@@ -9608,6 +9838,8 @@ Init_date_core(void)
rb_define_method(cDate, "httpdate", d_lite_httpdate, 0);
rb_define_method(cDate, "jisx0301", d_lite_jisx0301, 0);
+ rb_define_method(cDate, "deconstruct_keys", d_lite_deconstruct_keys, 1);
+
#ifndef NDEBUG
rb_define_method(cDate, "marshal_dump_old", d_lite_marshal_dump_old, 0);
#endif
@@ -9818,6 +10050,8 @@ Init_date_core(void)
rb_define_method(cDateTime, "rfc3339", dt_lite_rfc3339, -1);
rb_define_method(cDateTime, "jisx0301", dt_lite_jisx0301, -1);
+ rb_define_method(cDateTime, "deconstruct_keys", dt_lite_deconstruct_keys, 1);
+
/* conversions */
rb_define_method(rb_cTime, "to_time", time_to_time, 0);
diff --git a/ext/date/date_parse.c b/ext/date/date_parse.c
index 95274d5baa..c6f26ecb91 100644
--- a/ext/date/date_parse.c
+++ b/ext/date/date_parse.c
@@ -413,7 +413,6 @@ VALUE
date_zone_to_diff(VALUE str)
{
VALUE offset = Qnil;
- VALUE vbuf = 0;
long l = RSTRING_LEN(str);
const char *s = RSTRING_PTR(str);
@@ -439,16 +438,26 @@ date_zone_to_diff(VALUE str)
l -= w;
dst = 1;
}
+
{
+ const char *zn = s;
long sl = shrunk_size(s, l);
+ char shrunk_buff[MAX_WORD_LENGTH]; /* no terminator to be added */
+ const struct zone *z = 0;
+
+ if (sl <= 0) {
+ sl = l;
+ }
+ else if (sl <= MAX_WORD_LENGTH) {
+ char *d = shrunk_buff;
+ sl = shrink_space(d, s, l);
+ zn = d;
+ }
+
if (sl > 0 && sl <= MAX_WORD_LENGTH) {
- char *d = ALLOCV_N(char, vbuf, sl);
- l = shrink_space(d, s, l);
- s = d;
+ z = zonetab(zn, (unsigned int)sl);
}
- }
- if (l > 0 && l <= MAX_WORD_LENGTH) {
- const struct zone *z = zonetab(s, (unsigned int)l);
+
if (z) {
int d = z->offset;
if (dst)
@@ -457,6 +466,7 @@ date_zone_to_diff(VALUE str)
goto ok;
}
}
+
{
char *p;
int sign = 0;
@@ -473,27 +483,53 @@ date_zone_to_diff(VALUE str)
s++;
l--;
+#define out_of_range(v, min, max) ((v) < (min) || (max) < (v))
hour = STRTOUL(s, &p, 10);
if (*p == ':') {
+ if (out_of_range(hour, 0, 23)) return Qnil;
s = ++p;
min = STRTOUL(s, &p, 10);
+ if (out_of_range(min, 0, 59)) return Qnil;
if (*p == ':') {
s = ++p;
sec = STRTOUL(s, &p, 10);
+ if (out_of_range(sec, 0, 59)) return Qnil;
}
- goto num;
}
- if (*p == ',' || *p == '.') {
- char *e = 0;
- p++;
- min = STRTOUL(p, &e, 10) * 3600;
+ else if (*p == ',' || *p == '.') {
+ /* fractional hour */
+ size_t n;
+ int ov;
+ /* no over precision for offset; 10**-7 hour = 0.36
+ * milliseconds should be enough. */
+ const size_t max_digits = 7; /* 36 * 10**7 < 32-bit FIXNUM_MAX */
+
+ if (out_of_range(hour, 0, 23)) return Qnil;
+
+ n = (s + l) - ++p;
+ if (n > max_digits) n = max_digits;
+ sec = ruby_scan_digits(p, n, 10, &n, &ov);
+ if ((p += n) < s + l && *p >= ('5' + !(sec & 1)) && *p <= '9') {
+ /* round half to even */
+ sec++;
+ }
+ sec *= 36;
if (sign) {
hour = -hour;
- min = -min;
+ sec = -sec;
+ }
+ if (n <= 2) {
+ /* HH.nn or HH.n */
+ if (n == 1) sec *= 10;
+ offset = INT2FIX(sec + hour * 3600);
+ }
+ else {
+ VALUE denom = rb_int_positive_pow(10, (int)(n - 2));
+ offset = f_add(rb_rational_new(INT2FIX(sec), denom), INT2FIX(hour * 3600));
+ if (rb_rational_den(offset) == INT2FIX(1)) {
+ offset = rb_rational_num(offset);
+ }
}
- offset = rb_rational_new(INT2FIX(min),
- rb_int_positive_pow(10, (int)(e - p)));
- offset = f_add(INT2FIX(hour * 3600), offset);
goto ok;
}
else if (l > 2) {
@@ -506,18 +542,16 @@ date_zone_to_diff(VALUE str)
min = ruby_scan_digits(&s[2 - l % 2], 2, 10, &n, &ov);
if (l >= 5)
sec = ruby_scan_digits(&s[4 - l % 2], 2, 10, &n, &ov);
- goto num;
}
- num:
sec += min * 60 + hour * 3600;
if (sign) sec = -sec;
offset = INT2FIX(sec);
+#undef out_of_range
}
}
}
RB_GC_GUARD(str);
ok:
- ALLOCV_END(vbuf);
return offset;
}
diff --git a/ext/date/date_strptime.c b/ext/date/date_strptime.c
index 7b06a31471..f731629df1 100644
--- a/ext/date/date_strptime.c
+++ b/ext/date/date_strptime.c
@@ -10,28 +10,15 @@
static const char *day_names[] = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday",
- "Sun", "Mon", "Tue", "Wed",
- "Thu", "Fri", "Sat"
};
+static const int ABBREVIATED_DAY_NAME_LENGTH = 3;
static const char *month_names[] = {
"January", "February", "March", "April",
"May", "June", "July", "August", "September",
"October", "November", "December",
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-static const char *merid_names[] = {
- "am", "pm",
- "a.m.", "p.m."
-};
-
-static const char *extz_pats[] = {
- ":z",
- "::z",
- ":::z"
};
+static const int ABBREVIATED_MONTH_NAME_LENGTH = 3;
#define sizeof_array(o) (sizeof o / sizeof o[0])
@@ -75,7 +62,7 @@ num_pattern_p(const char *s)
#define NUM_PATTERN_P() num_pattern_p(&fmt[fi + 1])
static long
-read_digits(const char *s, VALUE *n, size_t width)
+read_digits(const char *s, size_t slen, VALUE *n, size_t width)
{
size_t l;
@@ -83,7 +70,7 @@ read_digits(const char *s, VALUE *n, size_t width)
return 0;
l = 0;
- while (ISDIGIT(s[l])) {
+ while (l < slen && ISDIGIT(s[l])) {
if (++l == width) break;
}
@@ -131,7 +118,7 @@ do { \
#define READ_DIGITS(n,w) \
do { \
size_t l; \
- l = read_digits(&str[si], &n, w); \
+ l = read_digits(&str[si], slen - si, &n, w); \
if (l == 0) \
fail(); \
si += l; \
@@ -161,6 +148,12 @@ do { \
VALUE date_zone_to_diff(VALUE);
+static inline int
+head_match_p(size_t len, const char *name, const char *str, size_t slen, size_t si)
+{
+ return slen - si >= len && strncasecmp(name, &str[si], len) == 0;
+}
+
static size_t
date__strptime_internal(const char *str, size_t slen,
const char *fmt, size_t flen, VALUE hash)
@@ -168,9 +161,18 @@ date__strptime_internal(const char *str, size_t slen,
size_t si, fi;
int c;
+#define HEAD_MATCH_P(len, name) head_match_p(len, name, str, slen, si)
si = fi = 0;
while (fi < flen) {
+ if (isspace((unsigned char)fmt[fi])) {
+ while (si < slen && isspace((unsigned char)str[si]))
+ si++;
+ while (++fi < flen && isspace((unsigned char)fmt[fi]));
+ continue;
+ }
+
+ if (si >= slen) fail();
switch (fmt[fi]) {
case '%':
@@ -194,12 +196,11 @@ date__strptime_internal(const char *str, size_t slen,
{
int i;
- for (i = 0; i < (int)sizeof_array(extz_pats); i++)
- if (strncmp(extz_pats[i], &fmt[fi],
- strlen(extz_pats[i])) == 0) {
- fi += i;
- goto again;
- }
+ for (i = 1; i < 3 && fi + i < flen && fmt[fi+i] == ':'; ++i);
+ if (fmt[fi+i] == 'z') {
+ fi += i - 1;
+ goto again;
+ }
fail();
}
@@ -209,10 +210,12 @@ date__strptime_internal(const char *str, size_t slen,
int i;
for (i = 0; i < (int)sizeof_array(day_names); i++) {
- size_t l = strlen(day_names[i]);
- if (strncasecmp(day_names[i], &str[si], l) == 0) {
+ const char *day_name = day_names[i];
+ size_t l = strlen(day_name);
+ if (HEAD_MATCH_P(l, day_name) ||
+ HEAD_MATCH_P(l = ABBREVIATED_DAY_NAME_LENGTH, day_name)) {
si += l;
- set_hash("wday", INT2FIX(i % 7));
+ set_hash("wday", INT2FIX(i));
goto matched;
}
}
@@ -225,10 +228,12 @@ date__strptime_internal(const char *str, size_t slen,
int i;
for (i = 0; i < (int)sizeof_array(month_names); i++) {
- size_t l = strlen(month_names[i]);
- if (strncasecmp(month_names[i], &str[si], l) == 0) {
+ const char *month_name = month_names[i];
+ size_t l = strlen(month_name);
+ if (HEAD_MATCH_P(l, month_name) ||
+ HEAD_MATCH_P(l = ABBREVIATED_MONTH_NAME_LENGTH, month_name)) {
si += l;
- set_hash("mon", INT2FIX((i % 12) + 1));
+ set_hash("mon", INT2FIX(i + 1));
goto matched;
}
}
@@ -402,18 +407,19 @@ date__strptime_internal(const char *str, size_t slen,
case 'P':
case 'p':
+ if (slen - si < 2) fail();
{
- int i;
-
- for (i = 0; i < 4; i++) {
- size_t l = strlen(merid_names[i]);
- if (strncasecmp(merid_names[i], &str[si], l) == 0) {
- si += l;
- set_hash("_merid", INT2FIX((i % 2) == 0 ? 0 : 12));
- goto matched;
- }
+ char c = str[si];
+ const int hour = (c == 'P' || c == 'p') ? 12 : 0;
+ if (!hour && !(c == 'A' || c == 'a')) fail();
+ if ((c = str[si+1]) == '.') {
+ if (slen - si < 4 || str[si+3] != '.') fail();
+ c = str[si += 2];
}
- fail();
+ if (!(c == 'M' || c == 'm')) fail();
+ si += 2;
+ set_hash("_merid", INT2FIX(hour));
+ goto matched;
}
case 'Q':
@@ -587,7 +593,7 @@ date__strptime_internal(const char *str, size_t slen,
b = rb_backref_get();
rb_match_busy(b);
- m = f_match(pat, rb_usascii_str_new2(&str[si]));
+ m = f_match(pat, rb_usascii_str_new(&str[si], slen - si));
if (!NIL_P(m)) {
VALUE s, l, o;
@@ -619,22 +625,13 @@ date__strptime_internal(const char *str, size_t slen,
if (str[si] != '%')
fail();
si++;
- if (fi < flen)
- if (str[si] != fmt[fi])
+ if (fi < flen) {
+ if (si >= slen || str[si] != fmt[fi])
fail();
- si++;
+ si++;
+ }
goto matched;
}
- case ' ':
- case '\t':
- case '\n':
- case '\v':
- case '\f':
- case '\r':
- while (isspace((unsigned char)str[si]))
- si++;
- fi++;
- break;
default:
ordinal:
if (str[si] != fmt[fi])
diff --git a/ext/date/lib/date.rb b/ext/date/lib/date.rb
index 5770187a8e..a9fe3ce4b0 100644
--- a/ext/date/lib/date.rb
+++ b/ext/date/lib/date.rb
@@ -4,8 +4,12 @@
require 'date_core'
class Date
- VERSION = '3.2.2' # :nodoc:
+ VERSION = "3.3.3" # :nodoc:
+ # call-seq:
+ # infinite? -> false
+ #
+ # Returns +false+
def infinite?
false
end
diff --git a/ext/date/zonetab.h b/ext/date/zonetab.h
index 39a435db16..7ced9e0308 100644
--- a/ext/date/zonetab.h
+++ b/ext/date/zonetab.h
@@ -36,7 +36,7 @@ struct zone {
int name;
int offset;
};
-static const struct zone *zonetab();
+static const struct zone *zonetab(register const char *str, register size_t len);
#line 9 "zonetab.list"
struct zone;
diff --git a/ext/date/zonetab.list b/ext/date/zonetab.list
index d2f902d2d5..748aec1d8a 100644
--- a/ext/date/zonetab.list
+++ b/ext/date/zonetab.list
@@ -3,7 +3,7 @@ struct zone {
int name;
int offset;
};
-static const struct zone *zonetab();
+static const struct zone *zonetab(register const char *str, register size_t len);
%}
struct zone;
diff --git a/ext/digest/bubblebabble/bubblebabble.c b/ext/digest/bubblebabble/bubblebabble.c
index 6557e43c9d..358ab416b9 100644
--- a/ext/digest/bubblebabble/bubblebabble.c
+++ b/ext/digest/bubblebabble/bubblebabble.c
@@ -37,7 +37,7 @@ bubblebabble_str_new(VALUE str_digest)
digest_len = RSTRING_LEN(str_digest);
if ((LONG_MAX - 2) / 3 < (digest_len | 1)) {
- rb_raise(rb_eRuntimeError, "digest string too long");
+ rb_raise(rb_eRuntimeError, "digest string too long");
}
str = rb_str_new(0, (digest_len | 1) * 3 + 2);
diff --git a/ext/digest/digest.c b/ext/digest/digest.c
index 83f4ee42fc..68837a674c 100644
--- a/ext/digest/digest.c
+++ b/ext/digest/digest.c
@@ -154,7 +154,7 @@ static void
rb_digest_instance_method_unimpl(VALUE self, const char *method)
{
rb_raise(rb_eRuntimeError, "%s does not implement %s()",
- rb_obj_classname(self), method);
+ rb_obj_classname(self), method);
}
/*
@@ -383,8 +383,8 @@ rb_digest_instance_equal(VALUE self, VALUE other)
StringValue(str2);
if (RSTRING_LEN(str1) == RSTRING_LEN(str2) &&
- rb_str_cmp(str1, str2) == 0) {
- return Qtrue;
+ rb_str_cmp(str1, str2) == 0) {
+ return Qtrue;
}
return Qfalse;
}
@@ -602,7 +602,7 @@ static inline void
algo_init(const rb_digest_metadata_t *algo, void *pctx)
{
if (algo->init_func(pctx) != 1) {
- rb_raise(rb_eRuntimeError, "Digest initialization failed.");
+ rb_raise(rb_eRuntimeError, "Digest initialization failed.");
}
}
@@ -614,7 +614,7 @@ rb_digest_base_alloc(VALUE klass)
void *pctx;
if (klass == rb_cDigest_Base) {
- rb_raise(rb_eNotImpError, "Digest::Base is an abstract class");
+ rb_raise(rb_eNotImpError, "Digest::Base is an abstract class");
}
algo = get_digest_base_metadata(klass);
@@ -639,7 +639,7 @@ rb_digest_base_copy(VALUE copy, VALUE obj)
algo = get_digest_obj_metadata(copy);
if (algo != get_digest_obj_metadata(obj))
- rb_raise(rb_eTypeError, "different algorithms");
+ rb_raise(rb_eTypeError, "different algorithms");
TypedData_Get_Struct(obj, void, &digest_type, pctx1);
TypedData_Get_Struct(copy, void, &digest_type, pctx2);
diff --git a/ext/digest/digest.h b/ext/digest/digest.h
index 0d4f0e7cc2..8a4c5b7e4e 100644
--- a/ext/digest/digest.h
+++ b/ext/digest/digest.h
@@ -38,7 +38,7 @@ rb_digest_##name##_update(void *ctx, unsigned char *ptr, size_t size) \
const unsigned int stride = 16384; \
\
for (; size > stride; size -= stride, ptr += stride) { \
- name##_Update(ctx, ptr, stride); \
+ name##_Update(ctx, ptr, stride); \
} \
if (size > 0) name##_Update(ctx, ptr, size); \
}
diff --git a/ext/digest/digest_conf.rb b/ext/digest/digest_conf.rb
index 1b929d8732..36a7d75289 100644
--- a/ext/digest/digest_conf.rb
+++ b/ext/digest/digest_conf.rb
@@ -3,7 +3,7 @@
def digest_conf(name)
unless with_config("bundled-#{name}")
cc = with_config("common-digest")
- if cc == true or /\b#{name}\b/ =~ cc
+ 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"
diff --git a/ext/digest/lib/digest/version.rb b/ext/digest/lib/digest/version.rb
index 79e6aeee99..42fd7acf6e 100644
--- a/ext/digest/lib/digest/version.rb
+++ b/ext/digest/lib/digest/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module Digest
- VERSION = "3.1.0"
+ VERSION = "3.1.1"
end
diff --git a/ext/digest/md5/depend b/ext/digest/md5/depend
index 0353e7a40d..ea1ceec7fd 100644
--- a/ext/digest/md5/depend
+++ b/ext/digest/md5/depend
@@ -326,5 +326,6 @@ md5init.o: $(hdrdir)/ruby/subst.h
md5init.o: $(srcdir)/../defs.h
md5init.o: $(srcdir)/../digest.h
md5init.o: md5.h
+md5init.o: md5cc.h
md5init.o: md5init.c
# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/digest/sha1/depend b/ext/digest/sha1/depend
index a4e454d214..48aaef158b 100644
--- a/ext/digest/sha1/depend
+++ b/ext/digest/sha1/depend
@@ -326,5 +326,6 @@ sha1init.o: $(hdrdir)/ruby/subst.h
sha1init.o: $(srcdir)/../defs.h
sha1init.o: $(srcdir)/../digest.h
sha1init.o: sha1.h
+sha1init.o: sha1cc.h
sha1init.o: sha1init.c
# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/digest/sha2/depend b/ext/digest/sha2/depend
index 2fb598aa48..47a859068c 100644
--- a/ext/digest/sha2/depend
+++ b/ext/digest/sha2/depend
@@ -325,5 +325,6 @@ sha2init.o: $(hdrdir)/ruby/st.h
sha2init.o: $(hdrdir)/ruby/subst.h
sha2init.o: $(srcdir)/../digest.h
sha2init.o: sha2.h
+sha2init.o: sha2cc.h
sha2init.o: sha2init.c
# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/digest/sha2/sha2init.c b/ext/digest/sha2/sha2init.c
index 6ed275eb71..94cccf3feb 100644
--- a/ext/digest/sha2/sha2init.c
+++ b/ext/digest/sha2/sha2init.c
@@ -47,7 +47,7 @@ Init_sha2(void)
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));
+ rb_digest_make_metadata(&sha##bitlen));
FOREACH_BITLEN(DEFINE_ALGO_CLASS)
}
diff --git a/ext/erb/escape/escape.c b/ext/erb/escape/escape.c
new file mode 100644
index 0000000000..67b2d1ef34
--- /dev/null
+++ b/ext/erb/escape/escape.c
@@ -0,0 +1,95 @@
+#include "ruby.h"
+#include "ruby/encoding.h"
+
+static VALUE rb_cERB, rb_mEscape, rb_cCGI;
+static ID id_escapeHTML;
+
+#define HTML_ESCAPE_MAX_LEN 6
+
+static const struct {
+ uint8_t len;
+ char str[HTML_ESCAPE_MAX_LEN+1];
+} html_escape_table[UCHAR_MAX+1] = {
+#define HTML_ESCAPE(c, str) [c] = {rb_strlen_lit(str), str}
+ HTML_ESCAPE('\'', "&#39;"),
+ HTML_ESCAPE('&', "&amp;"),
+ HTML_ESCAPE('"', "&quot;"),
+ HTML_ESCAPE('<', "&lt;"),
+ HTML_ESCAPE('>', "&gt;"),
+#undef HTML_ESCAPE
+};
+
+static inline void
+preserve_original_state(VALUE orig, VALUE dest)
+{
+ rb_enc_associate(dest, rb_enc_get(orig));
+}
+
+static inline long
+escaped_length(VALUE str)
+{
+ const long len = RSTRING_LEN(str);
+ if (len >= LONG_MAX / HTML_ESCAPE_MAX_LEN) {
+ ruby_malloc_size_overflow(len, HTML_ESCAPE_MAX_LEN);
+ }
+ return len * HTML_ESCAPE_MAX_LEN;
+}
+
+static VALUE
+optimized_escape_html(VALUE str)
+{
+ VALUE vbuf;
+ char *buf = ALLOCV_N(char, vbuf, escaped_length(str));
+ const char *cstr = RSTRING_PTR(str);
+ const char *end = cstr + RSTRING_LEN(str);
+
+ char *dest = buf;
+ while (cstr < end) {
+ const unsigned char c = *cstr++;
+ uint8_t len = html_escape_table[c].len;
+ if (len) {
+ memcpy(dest, html_escape_table[c].str, len);
+ dest += len;
+ }
+ else {
+ *dest++ = c;
+ }
+ }
+
+ VALUE escaped = str;
+ if (RSTRING_LEN(str) < (dest - buf)) {
+ escaped = rb_str_new(buf, dest - buf);
+ preserve_original_state(str, escaped);
+ }
+ 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
+static VALUE
+erb_escape_html(VALUE self, VALUE str)
+{
+ if (!RB_TYPE_P(str, T_STRING)) {
+ str = rb_convert_type(str, T_STRING, "String", "to_s");
+ }
+
+ if (rb_enc_str_asciicompat_p(str)) {
+ return optimized_escape_html(str);
+ }
+ else {
+ return rb_funcall(rb_cCGI, id_escapeHTML, 1, str);
+ }
+}
+
+void
+Init_escape(void)
+{
+ 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);
+
+ rb_cCGI = rb_define_class("CGI", rb_cObject);
+ id_escapeHTML = rb_intern("escapeHTML");
+}
diff --git a/ext/erb/escape/extconf.rb b/ext/erb/escape/extconf.rb
new file mode 100644
index 0000000000..c1002548ad
--- /dev/null
+++ b/ext/erb/escape/extconf.rb
@@ -0,0 +1,7 @@
+require 'mkmf'
+
+if RUBY_ENGINE == 'truffleruby'
+ File.write('Makefile', dummy_makefile($srcdir).join)
+else
+ create_makefile 'erb/escape'
+end
diff --git a/ext/etc/etc.c b/ext/etc/etc.c
index 4cd941f586..6c7145b40b 100644
--- a/ext/etc/etc.c
+++ b/ext/etc/etc.c
@@ -47,12 +47,16 @@ static VALUE sGroup;
#define HAVE_UNAME 1
#endif
-#ifndef _WIN32
-char *getenv();
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
#endif
char *getlogin();
-#define RUBY_ETC_VERSION "1.4.0"
+#define RUBY_ETC_VERSION "1.4.2"
#ifdef HAVE_RB_DEPRECATE_CONSTANT
void rb_deprecate_constant(VALUE mod, const char *name);
diff --git a/ext/extmk.rb b/ext/extmk.rb
index f080f2676a..4e77a7167b 100755
--- a/ext/extmk.rb
+++ b/ext/extmk.rb
@@ -66,12 +66,17 @@ end
def atomic_write_open(filename)
filename_new = filename + ".new.#$$"
- open(filename_new, "wb") do |f|
+ clean = false
+ File.open(filename_new, "wbx") do |f|
+ clean = true
yield f
end
if File.binread(filename_new) != (File.binread(filename) rescue nil)
File.rename(filename_new, filename)
- else
+ clean = false
+ end
+ensure
+ if clean
File.unlink(filename_new)
end
end
@@ -131,6 +136,14 @@ def extract_makefile(makefile, keep = true)
true
end
+def create_makefile(target, srcprefix = nil)
+ if $static and target.include?("/")
+ base = File.basename(target)
+ $defs << "-DInit_#{base}=Init_#{target.tr('/', '_')}"
+ end
+ super
+end
+
def extmake(target, basedir = 'ext', maybestatic = true)
FileUtils.mkpath target unless File.directory?(target)
begin
@@ -139,7 +152,7 @@ def extmake(target, basedir = 'ext', maybestatic = true)
d = target
until (d = File.dirname(d)) == '.'
if File.exist?("#{$top_srcdir}/#{basedir}/#{d}/extconf.rb")
- parent = (/^all:\s*install/ =~ IO.read("#{d}/Makefile") rescue false)
+ parent = (/^all:\s*install/ =~ File.read("#{d}/Makefile") rescue false)
break
end
end
@@ -158,8 +171,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
@@ -193,7 +204,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)) ||
@@ -256,6 +267,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]
@@ -412,8 +425,10 @@ if CROSS_COMPILING
$ruby = $mflags.defined?("MINIRUBY") || CONFIG['MINIRUBY']
elsif sep = config_string('BUILD_FILE_SEPARATOR')
$ruby = "$(topdir:/=#{sep})#{sep}miniruby" + EXEEXT
-else
+elsif CONFIG['EXTSTATIC']
$ruby = '$(topdir)/miniruby' + EXEEXT
+else
+ $ruby = '$(topdir)/ruby' + EXEEXT
end
$ruby = [$ruby]
$ruby << "-I'$(topdir)'"
@@ -425,6 +440,7 @@ end
topruby = $ruby
$ruby = topruby.join(' ')
$mflags << "ruby=#$ruby"
+$builtruby = '$(topdir)/miniruby' + EXEEXT # Must be an executable path
MTIMES = [__FILE__, 'rbconfig.rb', srcdir+'/lib/mkmf.rb'].collect {|f| File.mtime(f)}
@@ -439,9 +455,8 @@ if $extstatic
end
for dir in ["ext", File::join($top_srcdir, "ext")]
setup = File::join(dir, CONFIG['setup'])
- if File.file? setup
- f = open(setup)
- while line = f.gets()
+ if (f = File.stat(setup) and f.file? rescue next)
+ File.foreach(setup) do |line|
line.chomp!
line.sub!(/#.*$/, '')
next if /^\s*$/ =~ line
@@ -458,7 +473,6 @@ for dir in ["ext", File::join($top_srcdir, "ext")]
end
MTIMES << f.mtime
$setup = setup
- f.close
break
end
end unless $extstatic
@@ -528,9 +542,14 @@ extend Module.new {
def timestamp_file(name, target_prefix = nil)
if @gemname and name == '$(TARGET_SO_DIR)'
- name = "$(arch)/gems/#{@gemname}#{target_prefix}"
+ gem = true
+ name = "$(gem_platform)/$(ruby_version)/gems/#{@gemname}#{target_prefix}"
+ end
+ path = super.sub(%r[/\.extout\.(?:-\.)?], '/.')
+ if gem
+ nil while path.sub!(%r[/\.(gem_platform|ruby_version)\.-(?=\.)], '/$(\1)/')
end
- super.sub(%r[/\.extout\.(?:-\.)?], '/.')
+ path
end
def configuration(srcdir)
@@ -538,7 +557,13 @@ 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('/', '_')}"
+ end
+ return super
+ end
super(*args) do |conf|
conf.find do |s|
s.sub!(%r(^(srcdir *= *)\$\(top_srcdir\)/\.bundle/gems/[^/]+(?=/))) {
@@ -548,7 +573,8 @@ extend Module.new {
"TARGET_TOPDIR = $(topdir)/.bundle\n" "#{$1}$(TARGET_TOPDIR)"
}
s.sub!(/^(TARGET_SO_DIR *= *)\$\(RUBYARCHDIR\)/) {
- "TARGET_GEM_DIR = $(TARGET_TOPDIR)/extensions/$(gem_platform)/$(ruby_version)/#{@gemname}\n"\
+ "TARGET_GEM_DIR = $(TARGET_TOPDIR)/extensions/$(gem_platform)"\
+ "/$(ruby_version)#{$enable_shared ? '' : '-static'}/#{@gemname}\n"\
"#{$1}$(TARGET_GEM_DIR)$(target_prefix)"
}
end
@@ -598,7 +624,7 @@ 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 -f -T $(gem_srcdir)/lib $(gemlib)
+ $(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)
@@ -707,6 +733,8 @@ begin
mf.puts "ECHO1 = $(V:1=@:)"
mf.puts "ECHO = $(ECHO1:0=@echo)"
mf.puts "MFLAGS = -$(MAKEFLAGS)" if $nmake
+ mf.puts "override MFLAGS := $(filter-out -j%,$(MFLAGS))" if $gnumake
+ mf.puts "ext_build_dir = #{File.dirname($command_output)}"
mf.puts
def mf.macro(name, values, max = 70)
@@ -749,6 +777,7 @@ begin
mf.macro "SUBMAKEOPTS", submakeopts
mf.macro "NOTE_MESG", %w[$(RUBY) $(top_srcdir)/tool/lib/colorize.rb skip]
mf.macro "NOTE_NAME", %w[$(RUBY) $(top_srcdir)/tool/lib/colorize.rb fail]
+ %w[RM RMDIRS RMDIR RMALL].each {|w| mf.macro w, [RbConfig::CONFIG[w]]}
mf.puts
targets = %w[all install static install-so install-rb clean distclean realclean]
targets.each do |tgt|
@@ -783,16 +812,20 @@ begin
exts.each do |d|
d = d[0..-2]
t = "#{d}#{tgt}"
- if /^(dist|real)?clean$/ =~ tgt
+ if clean = /^(dist|real)?clean$/.match(tgt)
deps = exts.select {|e|e.start_with?(d)}.map {|e|"#{e[0..-2]}#{tgt}"} - [t]
- pd = ' ' + deps.join(' ') unless deps.empty?
+ pd = [' clean-local', *deps].join(' ')
else
pext = File.dirname(d)
pd = " #{pext}/#{tgt}" if exts.include?("#{pext}/.")
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)"
+ end
end
end
+ mf.puts "\n""clean-local:\n\t$(Q)$(RM) $(ext_build_dir)/*~ $(ext_build_dir)/*.bak $(ext_build_dir)/core"
mf.puts "\n""extso:\n"
mf.puts "\t@echo EXTSO=$(EXTSO)"
diff --git a/ext/fcntl/fcntl.gemspec b/ext/fcntl/fcntl.gemspec
index 048e101aa5..09d3fc2568 100644
--- a/ext/fcntl/fcntl.gemspec
+++ b/ext/fcntl/fcntl.gemspec
@@ -3,7 +3,7 @@
Gem::Specification.new do |spec|
spec.name = "fcntl"
- spec.version = "1.0.1"
+ spec.version = "1.0.2"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c
index 3679e5c9ad..892f522a62 100644
--- a/ext/fiddle/closure.c
+++ b/ext/fiddle/closure.c
@@ -56,6 +56,8 @@ closure_memsize(const void * ptr)
const rb_data_type_t closure_data_type = {
"fiddle/closure",
{0, dealloc, closure_memsize,},
+ 0, 0,
+ RUBY_TYPED_FREE_IMMEDIATELY,
};
struct callback_args {
@@ -90,7 +92,7 @@ with_gvl_callback(void *ptr)
case TYPE_INT:
rb_ary_push(params, INT2NUM(*(int *)x->args[i]));
break;
- case -TYPE_INT:
+ case TYPE_UINT:
rb_ary_push(params, UINT2NUM(*(unsigned int *)x->args[i]));
break;
case TYPE_VOIDP:
@@ -101,19 +103,19 @@ with_gvl_callback(void *ptr)
case TYPE_LONG:
rb_ary_push(params, LONG2NUM(*(long *)x->args[i]));
break;
- case -TYPE_LONG:
+ 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_CHAR:
+ 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_SHORT:
+ case TYPE_USHORT:
rb_ary_push(params, UINT2NUM(*(unsigned short *)x->args[i]));
break;
case TYPE_DOUBLE:
@@ -126,7 +128,7 @@ with_gvl_callback(void *ptr)
case TYPE_LONG_LONG:
rb_ary_push(params, LL2NUM(*(LONG_LONG *)x->args[i]));
break;
- case -TYPE_LONG_LONG:
+ case TYPE_ULONG_LONG:
rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)x->args[i]));
break;
#endif
@@ -149,7 +151,7 @@ with_gvl_callback(void *ptr)
case TYPE_LONG:
*(long *)x->resp = NUM2LONG(ret);
break;
- case -TYPE_LONG:
+ case TYPE_ULONG:
*(unsigned long *)x->resp = NUM2ULONG(ret);
break;
case TYPE_CHAR:
@@ -157,9 +159,9 @@ with_gvl_callback(void *ptr)
case TYPE_INT:
*(ffi_sarg *)x->resp = NUM2INT(ret);
break;
- case -TYPE_CHAR:
- case -TYPE_SHORT:
- case -TYPE_INT:
+ case TYPE_UCHAR:
+ case TYPE_USHORT:
+ case TYPE_UINT:
*(ffi_arg *)x->resp = NUM2UINT(ret);
break;
case TYPE_VOIDP:
@@ -175,7 +177,7 @@ with_gvl_callback(void *ptr)
case TYPE_LONG_LONG:
*(LONG_LONG *)x->resp = NUM2LL(ret);
break;
- case -TYPE_LONG_LONG:
+ case TYPE_ULONG_LONG:
*(unsigned LONG_LONG *)x->resp = NUM2ULL(ret);
break;
#endif
@@ -224,9 +226,27 @@ allocate(VALUE klass)
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(int rbargc, VALUE argv[], VALUE self)
+initialize_body(VALUE user_data)
{
+ initialize_data *data = (initialize_data *)user_data;
VALUE ret;
VALUE args;
VALUE normalized_args;
@@ -237,14 +257,14 @@ initialize(int rbargc, VALUE argv[], VALUE self)
ffi_status result;
int i, argc;
- if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
- abi = INT2NUM(FFI_DEFAULT_ABI);
+ 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(self, fiddle_closure, &closure_data_type, cl);
+ TypedData_Get_Struct(data->self, fiddle_closure, &closure_data_type, cl);
cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
@@ -257,8 +277,8 @@ initialize(int rbargc, VALUE argv[], VALUE self)
cl->argv[argc] = NULL;
ret = rb_fiddle_type_ensure(ret);
- rb_iv_set(self, "@ctype", ret);
- rb_iv_set(self, "@args", normalized_args);
+ rb_iv_set(data->self, "@ctype", ret);
+ rb_iv_set(data->self, "@args", normalized_args);
cif = &cl->cif;
pcl = cl->pcl;
@@ -269,38 +289,75 @@ initialize(int rbargc, VALUE argv[], VALUE self)
rb_fiddle_int_to_ffi_type(NUM2INT(ret)),
cl->argv);
- if (FFI_OK != result)
- rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
+ 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 *)self, cl->code);
+ (void *)(data->self), cl->code);
#else
- result = ffi_prep_closure(pcl, cif, callback, (void *)self);
+ 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");
+ rb_sys_fail("mprotect");
}
#endif
- if (FFI_OK != result)
- rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
+ if (FFI_OK != result) {
+ rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
+ }
- return self;
+ return data->self;
}
static VALUE
-to_i(VALUE self)
+initialize_rescue(VALUE user_data, VALUE exception)
{
- fiddle_closure * cl;
- void *code;
+ initialize_data *data = (initialize_data *)user_data;
+ dealloc(RTYPEDDATA_DATA(data->self));
+ RTYPEDDATA_DATA(data->self) = NULL;
+ rb_exc_raise(exception);
+ return data->self;
+}
- TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
+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);
+}
- code = cl->code;
+static VALUE
+to_i(VALUE self)
+{
+ fiddle_closure *closure = get_raw(self);
+ return PTR2NUM(closure->code);
+}
- return PTR2NUM(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
@@ -353,8 +410,24 @@ Init_fiddle_closure(void)
/*
* Document-method: to_i
*
- * Returns the memory address for this closure
+ * 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/conversions.c b/ext/fiddle/conversions.c
index 6e0ce36378..3b70f7de4c 100644
--- a/ext/fiddle/conversions.c
+++ b/ext/fiddle/conversions.c
@@ -211,32 +211,32 @@ rb_fiddle_value_to_generic(int type, VALUE *src, fiddle_generic *dst)
case TYPE_CHAR:
dst->schar = (signed char)NUM2INT(*src);
break;
- case -TYPE_CHAR:
+ case TYPE_UCHAR:
dst->uchar = (unsigned char)NUM2UINT(*src);
break;
case TYPE_SHORT:
dst->sshort = (unsigned short)NUM2INT(*src);
break;
- case -TYPE_SHORT:
+ case TYPE_USHORT:
dst->sshort = (signed short)NUM2UINT(*src);
break;
case TYPE_INT:
dst->sint = NUM2INT(*src);
break;
- case -TYPE_INT:
+ case TYPE_UINT:
dst->uint = NUM2UINT(*src);
break;
case TYPE_LONG:
dst->slong = NUM2LONG(*src);
break;
- case -TYPE_LONG:
+ case TYPE_ULONG:
dst->ulong = NUM2ULONG(*src);
break;
#if HAVE_LONG_LONG
case TYPE_LONG_LONG:
dst->slong_long = NUM2LL(*src);
break;
- case -TYPE_LONG_LONG:
+ case TYPE_ULONG_LONG:
dst->ulong_long = NUM2ULL(*src);
break;
#endif
@@ -283,24 +283,24 @@ rb_fiddle_generic_to_value(VALUE rettype, fiddle_generic retval)
PTR2NUM((void *)retval.pointer));
case TYPE_CHAR:
return INT2NUM((signed char)retval.fffi_sarg);
- case -TYPE_CHAR:
+ case TYPE_UCHAR:
return INT2NUM((unsigned char)retval.fffi_arg);
case TYPE_SHORT:
return INT2NUM((signed short)retval.fffi_sarg);
- case -TYPE_SHORT:
+ case TYPE_USHORT:
return INT2NUM((unsigned short)retval.fffi_arg);
case TYPE_INT:
return INT2NUM((signed int)retval.fffi_sarg);
- case -TYPE_INT:
+ case TYPE_UINT:
return UINT2NUM((unsigned int)retval.fffi_arg);
case TYPE_LONG:
return LONG2NUM(retval.slong);
- case -TYPE_LONG:
+ case TYPE_ULONG:
return ULONG2NUM(retval.ulong);
#if HAVE_LONG_LONG
case TYPE_LONG_LONG:
return LL2NUM(retval.slong_long);
- case -TYPE_LONG_LONG:
+ case TYPE_ULONG_LONG:
return ULL2NUM(retval.ulong_long);
#endif
case TYPE_FLOAT:
diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb
index 93b4f9d4fa..cf8b5223bb 100644
--- a/ext/fiddle/extconf.rb
+++ b/ext/fiddle/extconf.rb
@@ -46,7 +46,7 @@ end
libffi_version = nil
have_libffi = false
-bundle = enable_config('bundled-libffi')
+bundle = with_config("libffi-source-dir")
unless bundle
dir_config 'libffi'
@@ -67,27 +67,11 @@ unless bundle
end
unless have_libffi
- # for https://github.com/ruby/fiddle
- extlibs_rb = File.expand_path("../../bin/extlibs.rb", $srcdir)
- if bundle && File.exist?(extlibs_rb)
- require "fileutils"
- require_relative "../../bin/extlibs"
- extlibs = ExtLibs.new
- cache_dir = File.expand_path("../../tmp/.download_cache", $srcdir)
- ext_dir = File.expand_path("../../ext", $srcdir)
- Dir.glob("#{$srcdir}/libffi-*/").each{|dir| FileUtils.rm_rf(dir)}
- extlibs.run(["--cache=#{cache_dir}", ext_dir])
- end
- if bundle != false
- libffi_package_name = Dir.glob("#{$srcdir}/libffi-*/")
- .map {|n| File.basename(n)}
- .max_by {|n| n.scan(/\d+/).map(&:to_i)}
- end
- unless libffi_package_name
- raise "missing libffi. Please install 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
-
- libffi_srcdir = "#{$srcdir}/#{libffi_package_name}"
ffi_header = 'ffi.h'
libffi = Struct.new(*%I[dir srcdir builddir include lib a cflags ldflags opt arch]).new
libffi.dir = libffi_package_name
@@ -167,7 +151,7 @@ if libffi_version
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 }})
- puts "libffi_version: #{libffi_version.join('.')}"
+ warn "libffi_version: #{libffi_version.join('.')}"
end
case
@@ -226,7 +210,7 @@ types.each do |type, signed|
end
if libffi
- $LOCAL_LIBS.prepend("./#{libffi.a} ").strip! # to exts.mk
+ $LOCAL_LIBS.prepend("#{libffi.a} ").strip! # to exts.mk
$INCFLAGS.gsub!(/-I#{libffi.dir}/, '-I$(LIBFFI_DIR)')
end
create_makefile 'fiddle' do |conf|
diff --git a/ext/fiddle/extlibs b/ext/fiddle/extlibs
deleted file mode 100644
index 68dac46a95..0000000000
--- a/ext/fiddle/extlibs
+++ /dev/null
@@ -1,13 +0,0 @@
-ver = 3.2.1
-pkg = libffi-$(ver)
-
-https://ftp.osuosl.org/pub/blfs/conglomeration/libffi/$(pkg).tar.gz \
- md5:83b89587607e3eb65c70d361f13bab43 \
- sha512:980ca30a8d76f963fca722432b1fe5af77d7a4e4d2eac5144fbc5374d4c596609a293440573f4294207e1bdd9fda80ad1e1cafb2ffb543df5a275bc3bd546483 \
- #
- win32/$(pkg)-mswin.patch -p0
-
-$(pkg)/config.guess -> /tool/config.guess
-$(pkg)/config.sub -> /tool/config.sub
-
-! chdir: $(pkg)| autoconf || exit 0
diff --git a/ext/fiddle/fiddle.c b/ext/fiddle/fiddle.c
index a8b5123269..c06cd5634a 100644
--- a/ext/fiddle/fiddle.c
+++ b/ext/fiddle/fiddle.c
@@ -164,137 +164,193 @@ Init_fiddle(void)
*/
rb_eFiddleDLError = rb_define_class_under(mFiddle, "DLError", rb_eFiddleError);
- /* Document-const: TYPE_VOID
+ VALUE mFiddleTypes = rb_define_module_under(mFiddle, "Types");
+
+ /* Document-const: Fiddle::Types::VOID
*
* C type - void
*/
- rb_define_const(mFiddle, "TYPE_VOID", INT2NUM(TYPE_VOID));
+ rb_define_const(mFiddleTypes, "VOID", INT2NUM(TYPE_VOID));
- /* Document-const: TYPE_VOIDP
+ /* Document-const: Fiddle::Types::VOIDP
*
* C type - void*
*/
- rb_define_const(mFiddle, "TYPE_VOIDP", INT2NUM(TYPE_VOIDP));
+ rb_define_const(mFiddleTypes, "VOIDP", INT2NUM(TYPE_VOIDP));
- /* Document-const: TYPE_CHAR
+ /* Document-const: Fiddle::Types::CHAR
*
* C type - char
*/
- rb_define_const(mFiddle, "TYPE_CHAR", INT2NUM(TYPE_CHAR));
+ rb_define_const(mFiddleTypes, "CHAR", INT2NUM(TYPE_CHAR));
- /* Document-const: TYPE_SHORT
+ /* 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(mFiddle, "TYPE_SHORT", INT2NUM(TYPE_SHORT));
+ rb_define_const(mFiddleTypes, "SHORT", INT2NUM(TYPE_SHORT));
- /* Document-const: TYPE_INT
+ /* 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(mFiddle, "TYPE_INT", INT2NUM(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: TYPE_LONG
+ /* Document-const: Fiddle::Types::ULONG
*
* C type - long
*/
- rb_define_const(mFiddle, "TYPE_LONG", INT2NUM(TYPE_LONG));
+ rb_define_const(mFiddleTypes, "ULONG", INT2NUM(TYPE_ULONG));
#if HAVE_LONG_LONG
- /* Document-const: TYPE_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(mFiddle, "TYPE_LONG_LONG", INT2NUM(TYPE_LONG_LONG));
+ rb_define_const(mFiddleTypes, "ULONG_LONG", INT2NUM(TYPE_ULONG_LONG));
#endif
#ifdef TYPE_INT8_T
- /* Document-const: TYPE_INT8_T
+ /* Document-const: Fiddle::Types::INT8_T
*
* C type - int8_t
*/
- rb_define_const(mFiddle, "TYPE_INT8_T", INT2NUM(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: TYPE_INT16_T
+ /* Document-const: Fiddle::Types::INT16_T
*
* C type - int16_t
*/
- rb_define_const(mFiddle, "TYPE_INT16_T", INT2NUM(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: TYPE_INT32_T
+ /* Document-const: Fiddle::Types::INT32_T
*
* C type - int32_t
*/
- rb_define_const(mFiddle, "TYPE_INT32_T", INT2NUM(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: TYPE_INT64_T
+ /* Document-const: Fiddle::Types::INT64_T
*
* C type - int64_t
*/
- rb_define_const(mFiddle, "TYPE_INT64_T", INT2NUM(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: TYPE_FLOAT
+ /* Document-const: Fiddle::Types::FLOAT
*
* C type - float
*/
- rb_define_const(mFiddle, "TYPE_FLOAT", INT2NUM(TYPE_FLOAT));
+ rb_define_const(mFiddleTypes, "FLOAT", INT2NUM(TYPE_FLOAT));
- /* Document-const: TYPE_DOUBLE
+ /* Document-const: Fiddle::Types::DOUBLE
*
* C type - double
*/
- rb_define_const(mFiddle, "TYPE_DOUBLE", INT2NUM(TYPE_DOUBLE));
+ rb_define_const(mFiddleTypes, "DOUBLE", INT2NUM(TYPE_DOUBLE));
#ifdef HAVE_FFI_PREP_CIF_VAR
- /* Document-const: TYPE_VARIADIC
+ /* Document-const: Fiddle::Types::VARIADIC
*
* C type - ...
*/
- rb_define_const(mFiddle, "TYPE_VARIADIC", INT2NUM(TYPE_VARIADIC));
+ rb_define_const(mFiddleTypes, "VARIADIC", INT2NUM(TYPE_VARIADIC));
#endif
- /* Document-const: TYPE_CONST_STRING
+ /* Document-const: Fiddle::Types::CONST_STRING
*
* C type - const char* ('\0' terminated const char*)
*/
- rb_define_const(mFiddle, "TYPE_CONST_STRING", INT2NUM(TYPE_CONST_STRING));
+ rb_define_const(mFiddleTypes, "CONST_STRING", INT2NUM(TYPE_CONST_STRING));
- /* Document-const: TYPE_SIZE_T
+ /* Document-const: Fiddle::Types::SIZE_T
*
* C type - size_t
*/
- rb_define_const(mFiddle, "TYPE_SIZE_T", INT2NUM(TYPE_SIZE_T));
+ rb_define_const(mFiddleTypes, "SIZE_T", INT2NUM(TYPE_SIZE_T));
- /* Document-const: TYPE_SSIZE_T
+ /* Document-const: Fiddle::Types::SSIZE_T
*
* C type - ssize_t
*/
- rb_define_const(mFiddle, "TYPE_SSIZE_T", INT2NUM(TYPE_SSIZE_T));
+ rb_define_const(mFiddleTypes, "SSIZE_T", INT2NUM(TYPE_SSIZE_T));
- /* Document-const: TYPE_PTRDIFF_T
+ /* Document-const: Fiddle::Types::PTRDIFF_T
*
* C type - ptrdiff_t
*/
- rb_define_const(mFiddle, "TYPE_PTRDIFF_T", INT2NUM(TYPE_PTRDIFF_T));
+ rb_define_const(mFiddleTypes, "PTRDIFF_T", INT2NUM(TYPE_PTRDIFF_T));
- /* Document-const: TYPE_INTPTR_T
+ /* Document-const: Fiddle::Types::INTPTR_T
*
* C type - intptr_t
*/
- rb_define_const(mFiddle, "TYPE_INTPTR_T", INT2NUM(TYPE_INTPTR_T));
+ rb_define_const(mFiddleTypes, "INTPTR_T", INT2NUM(TYPE_INTPTR_T));
- /* Document-const: TYPE_UINTPTR_T
+ /* Document-const: Fiddle::Types::UINTPTR_T
*
* C type - uintptr_t
*/
- rb_define_const(mFiddle, "TYPE_UINTPTR_T", INT2NUM(TYPE_UINTPTR_T));
+ rb_define_const(mFiddleTypes, "UINTPTR_T", INT2NUM(TYPE_UINTPTR_T));
/* Document-const: ALIGN_VOIDP
*
@@ -422,30 +478,60 @@ Init_fiddle(void)
*/
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
@@ -454,24 +540,48 @@ Init_fiddle(void)
*/
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
@@ -540,6 +650,30 @@ Init_fiddle(void)
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();
diff --git a/ext/fiddle/fiddle.gemspec b/ext/fiddle/fiddle.gemspec
index a9c0ec4026..878109395b 100644
--- a/ext/fiddle/fiddle.gemspec
+++ b/ext/fiddle/fiddle.gemspec
@@ -20,15 +20,12 @@ Gem::Specification.new do |spec|
"LICENSE.txt",
"README.md",
"Rakefile",
- "bin/downloader.rb",
- "bin/extlibs.rb",
"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/extlibs",
"ext/fiddle/fiddle.c",
"ext/fiddle/fiddle.h",
"ext/fiddle/function.c",
diff --git a/ext/fiddle/fiddle.h b/ext/fiddle/fiddle.h
index 9de62a58cc..10eb9ceedb 100644
--- a/ext/fiddle/fiddle.h
+++ b/ext/fiddle/fiddle.h
@@ -111,11 +111,16 @@
#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
#if HAVE_LONG_LONG
#define TYPE_LONG_LONG 6
+#define TYPE_ULONG_LONG -TYPE_LONG_LONG
#endif
#define TYPE_FLOAT 7
#define TYPE_DOUBLE 8
@@ -123,11 +128,18 @@
#define TYPE_CONST_STRING 10
#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
@@ -135,6 +147,11 @@
#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
@@ -143,6 +160,10 @@
# 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
diff --git a/ext/fiddle/handle.c b/ext/fiddle/handle.c
index 76b90909d3..ae8cc3a581 100644
--- a/ext/fiddle/handle.c
+++ b/ext/fiddle/handle.c
@@ -321,8 +321,10 @@ rb_fiddle_handle_s_sym(VALUE self, VALUE sym)
return fiddle_handle_sym(RTLD_NEXT, sym);
}
-static VALUE
-fiddle_handle_sym(void *handle, VALUE symbol)
+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;
@@ -330,13 +332,13 @@ fiddle_handle_sym(void *handle, VALUE symbol)
#else
# define CHECK_DLERROR
#endif
- void (*func)();
+ fiddle_void_func func;
const char *name = StringValueCStr(symbol);
#ifdef HAVE_DLERROR
dlerror();
#endif
- func = (void (*)())(VALUE)dlsym(handle, name);
+ func = (fiddle_void_func)(VALUE)dlsym(handle, name);
CHECK_DLERROR;
#if defined(FUNC_STDCALL)
if( !func ){
@@ -379,6 +381,53 @@ fiddle_handle_sym(void *handle, VALUE symbol)
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);
}
@@ -468,6 +517,7 @@ Init_fiddle_handle(void)
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
@@ -526,6 +576,7 @@ Init_fiddle_handle(void)
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);
diff --git a/ext/fiddle/lib/fiddle.rb b/ext/fiddle/lib/fiddle.rb
index 4512989310..6137c487c6 100644
--- a/ext/fiddle/lib/fiddle.rb
+++ b/ext/fiddle/lib/fiddle.rb
@@ -58,7 +58,36 @@ module Fiddle
#
# See Fiddle::Handle.new for more.
def dlopen library
- Fiddle::Handle.new 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
@@ -67,4 +96,8 @@ module Fiddle
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
index c865a63c20..7e0077ea52 100644
--- a/ext/fiddle/lib/fiddle/closure.rb
+++ b/ext/fiddle/lib/fiddle/closure.rb
@@ -1,6 +1,31 @@
# 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
diff --git a/ext/fiddle/lib/fiddle/cparser.rb b/ext/fiddle/lib/fiddle/cparser.rb
index 93a05513c9..9a70402953 100644
--- a/ext/fiddle/lib/fiddle/cparser.rb
+++ b/ext/fiddle/lib/fiddle/cparser.rb
@@ -164,23 +164,23 @@ module Fiddle
unless Fiddle.const_defined?(:TYPE_LONG_LONG)
raise(RuntimeError, "unsupported type: #{ty}")
end
- return -TYPE_LONG_LONG
+ return TYPE_ULONG_LONG
when /\A(?:signed\s+)?long(?:\s+int\s+)?(?:\s+\w+)?\z/
return TYPE_LONG
when /\Aunsigned\s+long(?:\s+int\s+)?(?:\s+\w+)?\z/
- return -TYPE_LONG
+ return TYPE_ULONG
when /\A(?:signed\s+)?int(?:\s+\w+)?\z/
return TYPE_INT
when /\A(?:unsigned\s+int|uint)(?:\s+\w+)?\z/
- return -TYPE_INT
+ return TYPE_UINT
when /\A(?:signed\s+)?short(?:\s+int\s+)?(?:\s+\w+)?\z/
return TYPE_SHORT
when /\Aunsigned\s+short(?:\s+int\s+)?(?:\s+\w+)?\z/
- return -TYPE_SHORT
+ return TYPE_USHORT
when /\A(?:signed\s+)?char(?:\s+\w+)?\z/
return TYPE_CHAR
when /\Aunsigned\s+char(?:\s+\w+)?\z/
- return -TYPE_CHAR
+ return TYPE_UCHAR
when /\Aint8_t(?:\s+\w+)?\z/
unless Fiddle.const_defined?(:TYPE_INT8_T)
raise(RuntimeError, "unsupported type: #{ty}")
@@ -190,7 +190,7 @@ module Fiddle
unless Fiddle.const_defined?(:TYPE_INT8_T)
raise(RuntimeError, "unsupported type: #{ty}")
end
- return -TYPE_INT8_T
+ return TYPE_UINT8_T
when /\Aint16_t(?:\s+\w+)?\z/
unless Fiddle.const_defined?(:TYPE_INT16_T)
raise(RuntimeError, "unsupported type: #{ty}")
@@ -200,7 +200,7 @@ module Fiddle
unless Fiddle.const_defined?(:TYPE_INT16_T)
raise(RuntimeError, "unsupported type: #{ty}")
end
- return -TYPE_INT16_T
+ return TYPE_UINT16_T
when /\Aint32_t(?:\s+\w+)?\z/
unless Fiddle.const_defined?(:TYPE_INT32_T)
raise(RuntimeError, "unsupported type: #{ty}")
@@ -210,7 +210,7 @@ module Fiddle
unless Fiddle.const_defined?(:TYPE_INT32_T)
raise(RuntimeError, "unsupported type: #{ty}")
end
- return -TYPE_INT32_T
+ return TYPE_UINT32_T
when /\Aint64_t(?:\s+\w+)?\z/
unless Fiddle.const_defined?(:TYPE_INT64_T)
raise(RuntimeError, "unsupported type: #{ty}")
@@ -220,7 +220,7 @@ module Fiddle
unless Fiddle.const_defined?(:TYPE_INT64_T)
raise(RuntimeError, "unsupported type: #{ty}")
end
- return -TYPE_INT64_T
+ return TYPE_UINT64_T
when /\Afloat(?:\s+\w+)?\z/
return TYPE_FLOAT
when /\Adouble(?:\s+\w+)?\z/
diff --git a/ext/fiddle/lib/fiddle/pack.rb b/ext/fiddle/lib/fiddle/pack.rb
index 22eccedb76..545b985d50 100644
--- a/ext/fiddle/lib/fiddle/pack.rb
+++ b/ext/fiddle/lib/fiddle/pack.rb
@@ -11,24 +11,24 @@ module Fiddle
TYPE_LONG => ALIGN_LONG,
TYPE_FLOAT => ALIGN_FLOAT,
TYPE_DOUBLE => ALIGN_DOUBLE,
- -TYPE_CHAR => ALIGN_CHAR,
- -TYPE_SHORT => ALIGN_SHORT,
- -TYPE_INT => ALIGN_INT,
- -TYPE_LONG => ALIGN_LONG,
+ TYPE_UCHAR => ALIGN_CHAR,
+ TYPE_USHORT => ALIGN_SHORT,
+ TYPE_UINT => ALIGN_INT,
+ TYPE_ULONG => ALIGN_LONG,
}
PACK_MAP = {
- TYPE_VOIDP => "l!",
+ TYPE_VOIDP => "L!",
TYPE_CHAR => "c",
TYPE_SHORT => "s!",
TYPE_INT => "i!",
TYPE_LONG => "l!",
TYPE_FLOAT => "f",
TYPE_DOUBLE => "d",
- -TYPE_CHAR => "c",
- -TYPE_SHORT => "s!",
- -TYPE_INT => "i!",
- -TYPE_LONG => "l!",
+ TYPE_UCHAR => "C",
+ TYPE_USHORT => "S!",
+ TYPE_UINT => "I!",
+ TYPE_ULONG => "L!",
}
SIZE_MAP = {
@@ -39,16 +39,17 @@ module Fiddle
TYPE_LONG => SIZEOF_LONG,
TYPE_FLOAT => SIZEOF_FLOAT,
TYPE_DOUBLE => SIZEOF_DOUBLE,
- -TYPE_CHAR => SIZEOF_CHAR,
- -TYPE_SHORT => SIZEOF_SHORT,
- -TYPE_INT => SIZEOF_INT,
- -TYPE_LONG => SIZEOF_LONG,
+ TYPE_UCHAR => SIZEOF_CHAR,
+ TYPE_USHORT => SIZEOF_SHORT,
+ TYPE_UINT => SIZEOF_INT,
+ TYPE_ULONG => SIZEOF_LONG,
}
if defined?(TYPE_LONG_LONG)
- ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG
- PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q"
- SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG
- PACK_MAP[TYPE_VOIDP] = "q" if SIZEOF_LONG_LONG == SIZEOF_VOIDP
+ 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)
diff --git a/ext/fiddle/lib/fiddle/version.rb b/ext/fiddle/lib/fiddle/version.rb
index db6504b650..719dc62e37 100644
--- a/ext/fiddle/lib/fiddle/version.rb
+++ b/ext/fiddle/lib/fiddle/version.rb
@@ -1,3 +1,3 @@
module Fiddle
- VERSION = "1.1.0"
+ VERSION = "1.1.1"
end
diff --git a/ext/io/console/console.c b/ext/io/console/console.c
index 4ec24178c4..21454a73fa 100644
--- a/ext/io/console/console.c
+++ b/ext/io/console/console.c
@@ -75,7 +75,7 @@ getattr(int fd, conmode *t)
#define SET_LAST_ERROR (0)
#endif
-static ID id_getc, id_console, id_close, id_min, id_time, id_intr;
+static ID id_getc, id_console, id_close;
#if ENABLE_IO_GETPASS
static ID id_gets, id_chomp_bang;
#endif
@@ -112,18 +112,34 @@ rb_f_send(int argc, VALUE *argv, VALUE recv)
}
#endif
+enum rawmode_opt_ids {
+ kwd_min,
+ kwd_time,
+ kwd_intr,
+ rawmode_opt_id_count
+};
+static ID rawmode_opt_ids[rawmode_opt_id_count];
+
typedef struct {
int vmin;
int vtime;
int intr;
} rawmode_arg_t;
+#ifndef UNDEF_P
+# define UNDEF_P(obj) ((obj) == Qundef)
+#endif
+#ifndef NIL_OR_UNDEF_P
+# define NIL_OR_UNDEF_P(obj) (NIL_P(obj) || UNDEF_P(obj))
+#endif
+
static rawmode_arg_t *
rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *opts)
{
int argc = *argcp;
rawmode_arg_t *optp = NULL;
VALUE vopts = Qnil;
+ VALUE optvals[rawmode_opt_id_count];
#ifdef RB_SCAN_ARGS_PASS_CALLED_KEYWORDS
argc = rb_scan_args(argc, argv, "*:", NULL, &vopts);
#else
@@ -138,19 +154,20 @@ rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *
}
#endif
rb_check_arity(argc, min_argc, max_argc);
- if (!NIL_P(vopts)) {
- VALUE vmin = rb_hash_aref(vopts, ID2SYM(id_min));
- VALUE vtime = rb_hash_aref(vopts, ID2SYM(id_time));
- VALUE intr = rb_hash_aref(vopts, ID2SYM(id_intr));
+ if (rb_get_kwargs(vopts, rawmode_opt_ids,
+ 0, rawmode_opt_id_count, optvals)) {
+ VALUE vmin = optvals[kwd_min];
+ VALUE vtime = optvals[kwd_time];
+ VALUE intr = optvals[kwd_intr];
/* default values by `stty raw` */
opts->vmin = 1;
opts->vtime = 0;
opts->intr = 0;
- if (!NIL_P(vmin)) {
+ if (!NIL_OR_UNDEF_P(vmin)) {
opts->vmin = NUM2INT(vmin);
optp = opts;
}
- if (!NIL_P(vtime)) {
+ if (!NIL_OR_UNDEF_P(vtime)) {
VALUE v10 = INT2FIX(10);
vtime = rb_funcall3(vtime, '*', 1, &v10);
opts->vtime = NUM2INT(vtime);
@@ -165,6 +182,7 @@ rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *
opts->intr = 0;
optp = opts;
break;
+ case Qundef:
case Qnil:
break;
default:
@@ -1633,9 +1651,11 @@ Init_console(void)
#endif
id_console = rb_intern("console");
id_close = rb_intern("close");
- id_min = rb_intern("min");
- id_time = rb_intern("time");
- id_intr = rb_intern("intr");
+#define init_rawmode_opt_id(name) \
+ rawmode_opt_ids[kwd_##name] = rb_intern(#name)
+ init_rawmode_opt_id(min);
+ init_rawmode_opt_id(time);
+ init_rawmode_opt_id(intr);
#ifndef HAVE_RB_F_SEND
id___send__ = rb_intern("__send__");
#endif
diff --git a/ext/io/console/depend b/ext/io/console/depend
index 06ccdde70d..36747ef583 100644
--- a/ext/io/console/depend
+++ b/ext/io/console/depend
@@ -185,7 +185,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/io-console.gemspec b/ext/io/console/io-console.gemspec
index aa57f8ac52..d26a757b01 100644
--- a/ext/io/console/io-console.gemspec
+++ b/ext/io/console/io-console.gemspec
@@ -1,5 +1,5 @@
# -*- ruby -*-
-_VERSION = "0.5.11"
+_VERSION = "0.6.0"
Gem::Specification.new do |s|
s.name = "io-console"
diff --git a/ext/io/console/win32_vk.inc b/ext/io/console/win32_vk.inc
index cbec7bef15..d15b1219fb 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,18 +509,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 "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 */
#ifndef GPERF_DOWNCASE
#define GPERF_DOWNCASE 1
-static unsigned char gperf_downcase[256] =
+static const 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,
@@ -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/io-nonblock.gemspec b/ext/io/nonblock/io-nonblock.gemspec
index f81d4fda0a..d6df21a84d 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.1.1"
+ spec.version = "0.2.0"
spec.authors = ["Nobu Nakada"]
spec.email = ["nobu@ruby-lang.org"]
diff --git a/ext/io/wait/extconf.rb b/ext/io/wait/extconf.rb
index eecdcce99f..c6230b7783 100644
--- a/ext/io/wait/extconf.rb
+++ b/ext/io/wait/extconf.rb
@@ -5,7 +5,7 @@ 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_wait", "ruby/io.h")
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|
diff --git a/ext/io/wait/io-wait.gemspec b/ext/io/wait/io-wait.gemspec
index 7b73d64200..ebc1f6f5c7 100644
--- a/ext/io/wait/io-wait.gemspec
+++ b/ext/io/wait/io-wait.gemspec
@@ -1,4 +1,4 @@
-_VERSION = "0.3.0.pre"
+_VERSION = "0.3.0"
Gem::Specification.new do |spec|
spec.name = "io-wait"
diff --git a/ext/io/wait/wait.c b/ext/io/wait/wait.c
index 30637da584..d74afb580b 100644
--- a/ext/io/wait/wait.c
+++ b/ext/io/wait/wait.c
@@ -47,11 +47,11 @@ 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;
+ return NULL;
}
else {
- *timerec = rb_time_interval(timeout);
- return timerec;
+ *timerec = rb_time_interval(timeout);
+ return timerec;
}
}
@@ -60,7 +60,7 @@ 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_sys_fail(0);
rb_io_check_closed(fptr);
return (i & events);
}
@@ -180,7 +180,7 @@ io_wait_readable(int argc, VALUE *argv, VALUE io)
#ifndef HAVE_RB_IO_WAIT
if (wait_for_single_fd(fptr, RB_WAITFD_IN, tv)) {
- return io;
+ return io;
}
return Qnil;
#else
@@ -216,7 +216,7 @@ io_wait_writable(int argc, VALUE *argv, VALUE io)
#ifndef HAVE_RB_IO_WAIT
tv = get_timeout(argc, argv, &timerec);
if (wait_for_single_fd(fptr, RB_WAITFD_OUT, tv)) {
- return io;
+ return io;
}
return Qnil;
#else
@@ -260,31 +260,31 @@ static int
wait_mode_sym(VALUE mode)
{
if (mode == ID2SYM(rb_intern("r"))) {
- return RB_WAITFD_IN;
+ return RB_WAITFD_IN;
}
if (mode == ID2SYM(rb_intern("read"))) {
- return RB_WAITFD_IN;
+ return RB_WAITFD_IN;
}
if (mode == ID2SYM(rb_intern("readable"))) {
- return RB_WAITFD_IN;
+ return RB_WAITFD_IN;
}
if (mode == ID2SYM(rb_intern("w"))) {
- return RB_WAITFD_OUT;
+ return RB_WAITFD_OUT;
}
if (mode == ID2SYM(rb_intern("write"))) {
- return RB_WAITFD_OUT;
+ return RB_WAITFD_OUT;
}
if (mode == ID2SYM(rb_intern("writable"))) {
- return RB_WAITFD_OUT;
+ return RB_WAITFD_OUT;
}
if (mode == ID2SYM(rb_intern("rw"))) {
- return RB_WAITFD_IN|RB_WAITFD_OUT;
+ return RB_WAITFD_IN|RB_WAITFD_OUT;
}
if (mode == ID2SYM(rb_intern("read_write"))) {
- return RB_WAITFD_IN|RB_WAITFD_OUT;
+ return RB_WAITFD_IN|RB_WAITFD_OUT;
}
if (mode == ID2SYM(rb_intern("readable_writable"))) {
- return RB_WAITFD_IN|RB_WAITFD_OUT;
+ return RB_WAITFD_IN|RB_WAITFD_OUT;
}
rb_raise(rb_eArgError, "unsupported mode: %"PRIsVALUE, mode);
return 0;
@@ -333,20 +333,20 @@ io_wait(int argc, VALUE *argv, VALUE io)
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]);
- }
+ 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;
+ return Qtrue;
if (wait_for_single_fd(fptr, event, tv))
- return io;
+ return io;
return Qnil;
#else
VALUE timeout = Qundef;
diff --git a/ext/json/VERSION b/ext/json/VERSION
index 097a15a2af..ec1cf33c3f 100644
--- a/ext/json/VERSION
+++ b/ext/json/VERSION
@@ -1 +1 @@
-2.6.2
+2.6.3
diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c
index e3a83472e1..98d0ea46c3 100644
--- a/ext/json/generator/generator.c
+++ b/ext/json/generator/generator.c
@@ -997,10 +997,10 @@ static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
if (!allow_nan) {
if (isinf(value)) {
fbuffer_free(buffer);
- rb_raise(eGeneratorError, "%u: %"PRIsVALUE" not allowed in JSON", __LINE__, RB_OBJ_STRING(tmp));
+ rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp));
} else if (isnan(value)) {
fbuffer_free(buffer);
- rb_raise(eGeneratorError, "%u: %"PRIsVALUE" not allowed in JSON", __LINE__, RB_OBJ_STRING(tmp));
+ rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp));
}
}
fbuffer_append_str(buffer, tmp);
diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb
index 9bedb65fa7..3d4326d836 100644
--- a/ext/json/lib/json/version.rb
+++ b/ext/json/lib/json/version.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
module JSON
# JSON version
- VERSION = '2.6.2'
+ VERSION = '2.6.3'
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
diff --git a/ext/json/parser/extconf.rb b/ext/json/parser/extconf.rb
index feb586e1b4..4723a02aee 100644
--- a/ext/json/parser/extconf.rb
+++ b/ext/json/parser/extconf.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: false
require 'mkmf'
-have_func("rb_enc_raise", "ruby.h")
-have_func("rb_enc_interned_str", "ruby.h")
+have_func("rb_enc_raise", "ruby/encoding.h")
+have_func("rb_enc_interned_str", "ruby/encoding.h")
# checking if String#-@ (str_uminus) dedupes... '
begin
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c
index 8b860c4101..9bd7f1971e 100644
--- a/ext/json/parser/parser.c
+++ b/ext/json/parser/parser.c
@@ -948,7 +948,7 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
{p = p - 1; } {p+= 1; cs = 29; goto _out;}
} else {
- rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p);
}
}
np = JSON_parse_float(json, p, pe, result);
@@ -990,7 +990,7 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
if (json->allow_nan) {
*result = CInfinity;
} else {
- rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8);
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 8);
}
}
@@ -1002,7 +1002,7 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
if (json->allow_nan) {
*result = CNaN;
} else {
- rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2);
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 2);
}
}
@@ -2348,7 +2348,7 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul
if(cs >= JSON_array_first_final) {
return p + 1;
} else {
- rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p);
return NULL;
}
}
@@ -2413,7 +2413,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int
}
rb_enc_raise(
EXC_ENCODING eParserError,
- "%u: incomplete unicode character escape sequence at '%s'", __LINE__, p
+ "incomplete unicode character escape sequence at '%s'", p
);
} else {
UTF32 ch = unescape_unicode((unsigned char *) ++pe);
@@ -2426,7 +2426,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int
}
rb_enc_raise(
EXC_ENCODING eParserError,
- "%u: incomplete surrogate pair at '%s'", __LINE__, p
+ "incomplete surrogate pair at '%s'", p
);
}
if (pe[0] == '\\' && pe[1] == 'u') {
@@ -3225,7 +3225,7 @@ static VALUE cParser_parse(VALUE self)
if (cs >= JSON_first_final && p == pe) {
return result;
} else {
- rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p);
return Qnil;
}
}
diff --git a/ext/json/parser/parser.rl b/ext/json/parser/parser.rl
index 2dee80ee3b..2dbdc7ef24 100644
--- a/ext/json/parser/parser.rl
+++ b/ext/json/parser/parser.rl
@@ -222,14 +222,14 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
if (json->allow_nan) {
*result = CNaN;
} else {
- rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2);
+ 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, "%u: unexpected token at '%s'", __LINE__, p - 8);
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 8);
}
}
action parse_string {
@@ -245,7 +245,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
fexec p + 10;
fhold; fbreak;
} else {
- rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p);
}
}
np = JSON_parse_float(json, fpc, pe, result);
@@ -447,7 +447,7 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul
if(cs >= JSON_array_first_final) {
return p + 1;
} else {
- rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p);
return NULL;
}
}
@@ -512,7 +512,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int
}
rb_enc_raise(
EXC_ENCODING eParserError,
- "%u: incomplete unicode character escape sequence at '%s'", __LINE__, p
+ "incomplete unicode character escape sequence at '%s'", p
);
} else {
UTF32 ch = unescape_unicode((unsigned char *) ++pe);
@@ -525,7 +525,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int
}
rb_enc_raise(
EXC_ENCODING eParserError,
- "%u: incomplete surrogate pair at '%s'", __LINE__, p
+ "incomplete surrogate pair at '%s'", p
);
}
if (pe[0] == '\\' && pe[1] == 'u') {
@@ -864,7 +864,7 @@ static VALUE cParser_parse(VALUE self)
if (cs >= JSON_first_final && p == pe) {
return result;
} else {
- rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p);
return Qnil;
}
}
diff --git a/ext/nkf/nkf.c b/ext/nkf/nkf.c
index a8fd937c31..c6ddee1976 100644
--- a/ext/nkf/nkf.c
+++ b/ext/nkf/nkf.c
@@ -65,11 +65,11 @@ rb_encoding* rb_nkf_enc_get(const char *name)
{
int idx = rb_enc_find_index(name);
if (idx < 0) {
- nkf_encoding *nkf_enc = nkf_enc_find(name);
- idx = rb_enc_find_index(nkf_enc_name(nkf_enc_to_base_encoding(nkf_enc)));
- if (idx < 0) {
- idx = rb_define_dummy_encoding(name);
- }
+ nkf_encoding *nkf_enc = nkf_enc_find(name);
+ idx = rb_enc_find_index(nkf_enc_name(nkf_enc_to_base_encoding(nkf_enc)));
+ if (idx < 0) {
+ idx = rb_define_dummy_encoding(name);
+ }
}
return rb_enc_from_index(idx);
}
@@ -83,40 +83,40 @@ int nkf_split_options(const char *arg)
int is_single_quoted = FALSE;
int is_double_quoted = FALSE;
for(i = 0; arg[i]; i++){
- if(j == 255){
- return -1;
- }else if(is_single_quoted){
- if(arg[i] == '\''){
- is_single_quoted = FALSE;
- }else{
- option[j++] = arg[i];
- }
- }else if(is_escaped){
- is_escaped = FALSE;
- option[j++] = arg[i];
- }else if(arg[i] == '\\'){
- is_escaped = TRUE;
- }else if(is_double_quoted){
- if(arg[i] == '"'){
- is_double_quoted = FALSE;
- }else{
- option[j++] = arg[i];
- }
- }else if(arg[i] == '\''){
- is_single_quoted = TRUE;
- }else if(arg[i] == '"'){
- is_double_quoted = TRUE;
- }else if(arg[i] == ' '){
- option[j] = '\0';
- options(option);
- j = 0;
- }else{
- option[j++] = arg[i];
- }
+ if(j == 255){
+ return -1;
+ }else if(is_single_quoted){
+ if(arg[i] == '\''){
+ is_single_quoted = FALSE;
+ }else{
+ option[j++] = arg[i];
+ }
+ }else if(is_escaped){
+ is_escaped = FALSE;
+ option[j++] = arg[i];
+ }else if(arg[i] == '\\'){
+ is_escaped = TRUE;
+ }else if(is_double_quoted){
+ if(arg[i] == '"'){
+ is_double_quoted = FALSE;
+ }else{
+ option[j++] = arg[i];
+ }
+ }else if(arg[i] == '\''){
+ is_single_quoted = TRUE;
+ }else if(arg[i] == '"'){
+ is_double_quoted = TRUE;
+ }else if(arg[i] == ' '){
+ option[j] = '\0';
+ options(option);
+ j = 0;
+ }else{
+ option[j++] = arg[i];
+ }
}
if(j){
- option[j] = '\0';
- options(option);
+ option[j] = '\0';
+ options(option);
}
return count;
}
@@ -170,9 +170,9 @@ rb_nkf_convert(VALUE obj, VALUE opt, VALUE src)
rb_str_set_len(tmp, output_ctr);
if (mimeout_f)
- rb_enc_associate(tmp, rb_usascii_encoding());
+ rb_enc_associate(tmp, rb_usascii_encoding());
else
- rb_enc_associate(tmp, rb_nkf_enc_get(nkf_enc_name(output_encoding)));
+ rb_enc_associate(tmp, rb_nkf_enc_get(nkf_enc_name(output_encoding)));
return tmp;
}
diff --git a/ext/nkf/nkf.gemspec b/ext/nkf/nkf.gemspec
index 2d77c71ff8..7f3bd4a4b1 100644
--- a/ext/nkf/nkf.gemspec
+++ b/ext/nkf/nkf.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "nkf"
- spec.version = "0.1.1"
+ spec.version = "0.1.2"
spec.authors = ["NARUSE Yui"]
spec.email = ["naruse@airemix.jp"]
diff --git a/ext/objspace/depend b/ext/objspace/depend
index c4da8031cc..52797664e0 100644
--- a/ext/objspace/depend
+++ b/ext/objspace/depend
@@ -158,12 +158,14 @@ object_tracing.o: $(hdrdir)/ruby/missing.h
object_tracing.o: $(hdrdir)/ruby/ruby.h
object_tracing.o: $(hdrdir)/ruby/st.h
object_tracing.o: $(hdrdir)/ruby/subst.h
+object_tracing.o: $(top_srcdir)/gc.h
object_tracing.o: $(top_srcdir)/internal.h
object_tracing.o: object_tracing.c
object_tracing.o: objspace.h
objspace.o: $(RUBY_EXTCONF_H)
objspace.o: $(arch_hdrdir)/ruby/config.h
objspace.o: $(hdrdir)/ruby/assert.h
+objspace.o: $(hdrdir)/ruby/atomic.h
objspace.o: $(hdrdir)/ruby/backward.h
objspace.o: $(hdrdir)/ruby/backward/2/assume.h
objspace.o: $(hdrdir)/ruby/backward/2/attributes.h
@@ -336,10 +338,16 @@ objspace.o: $(hdrdir)/ruby/regex.h
objspace.o: $(hdrdir)/ruby/ruby.h
objspace.o: $(hdrdir)/ruby/st.h
objspace.o: $(hdrdir)/ruby/subst.h
+objspace.o: $(hdrdir)/ruby/thread_native.h
+objspace.o: $(top_srcdir)/ccan/check_type/check_type.h
+objspace.o: $(top_srcdir)/ccan/container_of/container_of.h
+objspace.o: $(top_srcdir)/ccan/list/list.h
+objspace.o: $(top_srcdir)/ccan/str/str.h
objspace.o: $(top_srcdir)/gc.h
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/class.h
objspace.o: $(top_srcdir)/internal/compilers.h
objspace.o: $(top_srcdir)/internal/gc.h
@@ -348,9 +356,17 @@ 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/static_assert.h
+objspace.o: $(top_srcdir)/internal/vm.h
objspace.o: $(top_srcdir)/internal/warnings.h
+objspace.o: $(top_srcdir)/method.h
objspace.o: $(top_srcdir)/node.h
+objspace.o: $(top_srcdir)/ruby_assert.h
+objspace.o: $(top_srcdir)/ruby_atomic.h
+objspace.o: $(top_srcdir)/shape.h
objspace.o: $(top_srcdir)/symbol.h
+objspace.o: $(top_srcdir)/thread_pthread.h
+objspace.o: $(top_srcdir)/vm_core.h
+objspace.o: $(top_srcdir)/vm_opts.h
objspace.o: objspace.c
objspace.o: {$(VPATH)}id.h
objspace_dump.o: $(RUBY_EXTCONF_H)
@@ -533,9 +549,13 @@ objspace_dump.o: $(top_srcdir)/ccan/check_type/check_type.h
objspace_dump.o: $(top_srcdir)/ccan/container_of/container_of.h
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)/gc.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/class.h
objspace_dump.o: $(top_srcdir)/internal/compilers.h
objspace_dump.o: $(top_srcdir)/internal/gc.h
objspace_dump.o: $(top_srcdir)/internal/hash.h
@@ -544,12 +564,15 @@ objspace_dump.o: $(top_srcdir)/internal/sanitizers.h
objspace_dump.o: $(top_srcdir)/internal/serial.h
objspace_dump.o: $(top_srcdir)/internal/static_assert.h
objspace_dump.o: $(top_srcdir)/internal/string.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
objspace_dump.o: $(top_srcdir)/method.h
objspace_dump.o: $(top_srcdir)/node.h
objspace_dump.o: $(top_srcdir)/ruby_assert.h
objspace_dump.o: $(top_srcdir)/ruby_atomic.h
+objspace_dump.o: $(top_srcdir)/shape.h
+objspace_dump.o: $(top_srcdir)/symbol.h
objspace_dump.o: $(top_srcdir)/thread_pthread.h
objspace_dump.o: $(top_srcdir)/vm_core.h
objspace_dump.o: $(top_srcdir)/vm_opts.h
diff --git a/ext/objspace/lib/objspace.rb b/ext/objspace/lib/objspace.rb
index 0298b0646c..6865fdda4c 100644
--- a/ext/objspace/lib/objspace.rb
+++ b/ext/objspace/lib/objspace.rb
@@ -6,14 +6,15 @@ module ObjectSpace
class << self
private :_dump
private :_dump_all
+ private :_dump_shapes
end
module_function
# call-seq:
- # ObjectSpace.dump(obj[, output: :string]) # => "{ ... }"
- # ObjectSpace.dump(obj, output: :file) # => #<File:/tmp/rubyobj20131125-88733-1xkfmpv.json>
- # ObjectSpace.dump(obj, output: :stdout) # => nil
+ # ObjectSpace.dump(obj[, output: :string]) -> "{ ... }"
+ # ObjectSpace.dump(obj, output: :file) -> #<File:/tmp/rubyobj20131125-88733-1xkfmpv.json>
+ # ObjectSpace.dump(obj, output: :stdout) -> nil
#
# Dump the contents of a ruby object as JSON.
#
@@ -42,38 +43,88 @@ module ObjectSpace
end
- # call-seq:
- # ObjectSpace.dump_all([output: :file]) # => #<File:/tmp/rubyheap20131125-88469-laoj3v.json>
- # ObjectSpace.dump_all(output: :stdout) # => nil
- # ObjectSpace.dump_all(output: :string) # => "{...}\n{...}\n..."
- # ObjectSpace.dump_all(output:
- # File.open('heap.json','w')) # => #<File:heap.json>
- # ObjectSpace.dump_all(output: :string,
- # since: 42) # => "{...}\n{...}\n..."
+ # call-seq:
+ # ObjectSpace.dump_all([output: :file]) -> #<File:/tmp/rubyheap20131125-88469-laoj3v.json>
+ # ObjectSpace.dump_all(output: :stdout) -> nil
+ # ObjectSpace.dump_all(output: :string) -> "{...}\n{...}\n..."
+ # ObjectSpace.dump_all(output: File.open('heap.json','w')) -> #<File:heap.json>
+ # ObjectSpace.dump_all(output: :string, since: 42) -> "{...}\n{...}\n..."
+ #
+ # Dump the contents of the ruby heap as JSON.
+ #
+ # _full_ must be a boolean. If true all heap slots are dumped including the empty ones (T_NONE).
+ #
+ # _since_ must be a non-negative integer or +nil+.
#
- # Dump the contents of the ruby heap as JSON.
+ # If _since_ is a positive integer, only objects of that generation and
+ # newer generations are dumped. The current generation can be accessed using
+ # GC::count. Objects that were allocated without object allocation tracing enabled
+ # are ignored. See ::trace_object_allocations for more information and
+ # examples.
#
- # _since_ must be a non-negative integer or +nil+.
+ # If _since_ is omitted or is +nil+, all objects are dumped.
#
- # If _since_ is a positive integer, only objects of that generation and
- # newer generations are dumped. The current generation can be accessed using
- # GC::count.
+ # _shapes_ must be a boolean or a non-negative integer.
#
- # Objects that were allocated without object allocation tracing enabled
- # are ignored. See ::trace_object_allocations for more information and
- # examples.
+ # If _shapes_ is a positive integer, only shapes newer than the provided
+ # shape id are dumped. The current shape_id can be accessed using <tt>RubyVM.stat(:next_shape_id)</tt>.
#
- # If _since_ is omitted or is +nil+, all objects are dumped.
+ # If _shapes_ is +false+, no shapes are dumped.
+ #
+ # To only dump objects allocated past a certain point you can combine _since_ and _shapes_:
+ # ObjectSpace.trace_object_allocations
+ # GC.start
+ # gc_generation = GC.count
+ # shape_generation = RubyVM.stat(:next_shape_id)
+ # call_method_to_instrument
+ # ObjectSpace.dump_all(since: gc_generation, shapes: shape_generation)
+ #
+ # This method is only expected to work with C Ruby.
+ # This is an experimental method and is subject to change.
+ # In particular, the function signature and output format are
+ # not guaranteed to be compatible in future versions of ruby.
+ def dump_all(output: :file, full: false, since: nil, shapes: true)
+ out = case output
+ when :file, nil
+ require 'tempfile'
+ Tempfile.create(%w(rubyheap .json))
+ when :stdout
+ STDOUT
+ when :string
+ +''
+ when IO
+ output
+ else
+ raise ArgumentError, "wrong output option: #{output.inspect}"
+ end
+
+ shapes = 0 if shapes == true
+ ret = _dump_all(out, full, since, shapes)
+ return nil if output == :stdout
+ ret
+ end
+
+ # call-seq:
+ # ObjectSpace.dump_shapes([output: :file]) -> #<File:/tmp/rubyshapes20131125-88469-laoj3v.json>
+ # ObjectSpace.dump_shapes(output: :stdout) -> nil
+ # ObjectSpace.dump_shapes(output: :string) -> "{...}\n{...}\n..."
+ # ObjectSpace.dump_shapes(output: File.open('shapes.json','w')) -> #<File:shapes.json>
+ # ObjectSpace.dump_all(output: :string, since: 42) -> "{...}\n{...}\n..."
+ #
+ # Dump the contents of the ruby shape tree as JSON.
+ #
+ # If _shapes_ is a positive integer, only shapes newer than the provided
+ # shape id are dumped. The current shape_id can be accessed using <tt>RubyVM.stat(:next_shape_id)</tt>.
#
# This method is only expected to work with C Ruby.
# This is an experimental method and is subject to change.
# In particular, the function signature and output format are
# not guaranteed to be compatible in future versions of ruby.
- def dump_all(output: :file, full: false, since: nil)
+ def dump_shapes(output: :file, since: 0)
out = case output
when :file, nil
require 'tempfile'
- Tempfile.create(%w(rubyheap .json))
+ Tempfile.create(%w(rubyshapes .json))
when :stdout
STDOUT
when :string
@@ -84,7 +135,7 @@ module ObjectSpace
raise ArgumentError, "wrong output option: #{output.inspect}"
end
- ret = _dump_all(out, full, since)
+ ret = _dump_shapes(out, since)
return nil if output == :stdout
ret
end
diff --git a/ext/objspace/object_tracing.c b/ext/objspace/object_tracing.c
index 66d6baa491..8c54d51eab 100644
--- a/ext/objspace/object_tracing.c
+++ b/ext/objspace/object_tracing.c
@@ -13,6 +13,7 @@
**********************************************************************/
+#include "gc.h"
#include "internal.h"
#include "ruby/debug.h"
#include "objspace.h"
@@ -31,24 +32,24 @@ static const char *
make_unique_str(st_table *tbl, const char *str, long len)
{
if (!str) {
- return NULL;
+ return NULL;
}
else {
- st_data_t n;
- char *result;
-
- if (st_lookup(tbl, (st_data_t)str, &n)) {
- st_insert(tbl, (st_data_t)str, n+1);
- st_get_key(tbl, (st_data_t)str, &n);
- result = (char *)n;
- }
- else {
- result = (char *)ruby_xmalloc(len+1);
- strncpy(result, str, len);
- result[len] = 0;
- st_add_direct(tbl, (st_data_t)result, 1);
- }
- return result;
+ st_data_t n;
+ char *result;
+
+ if (st_lookup(tbl, (st_data_t)str, &n)) {
+ st_insert(tbl, (st_data_t)str, n+1);
+ st_get_key(tbl, (st_data_t)str, &n);
+ result = (char *)n;
+ }
+ else {
+ result = (char *)ruby_xmalloc(len+1);
+ strncpy(result, str, len);
+ result[len] = 0;
+ st_add_direct(tbl, (st_data_t)result, 1);
+ }
+ return result;
}
}
@@ -56,17 +57,17 @@ static void
delete_unique_str(st_table *tbl, const char *str)
{
if (str) {
- st_data_t n;
-
- st_lookup(tbl, (st_data_t)str, &n);
- if (n == 1) {
- n = (st_data_t)str;
- st_delete(tbl, &n, 0);
- ruby_xfree((char *)n);
- }
- else {
- st_insert(tbl, (st_data_t)str, n-1);
- }
+ st_data_t n;
+
+ st_lookup(tbl, (st_data_t)str, &n);
+ if (n == 1) {
+ n = (st_data_t)str;
+ st_delete(tbl, &n, 0);
+ ruby_xfree((char *)n);
+ }
+ else {
+ st_insert(tbl, (st_data_t)str, n-1);
+ }
}
}
@@ -87,18 +88,18 @@ newobj_i(VALUE tpval, void *data)
st_data_t v;
if (st_lookup(arg->object_table, (st_data_t)obj, &v)) {
- info = (struct allocation_info *)v;
- if (arg->keep_remains) {
- if (info->living) {
- /* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */
- }
- }
- /* reuse info */
- delete_unique_str(arg->str_table, info->path);
- delete_unique_str(arg->str_table, info->class_path);
+ info = (struct allocation_info *)v;
+ if (arg->keep_remains) {
+ if (info->living) {
+ /* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */
+ }
+ }
+ /* reuse info */
+ delete_unique_str(arg->str_table, info->path);
+ delete_unique_str(arg->str_table, info->class_path);
}
else {
- info = (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info));
+ info = (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info));
}
info->living = 1;
info->flags = RBASIC(obj)->flags;
@@ -121,20 +122,26 @@ freeobj_i(VALUE tpval, void *data)
st_data_t v;
struct allocation_info *info;
+ /* Modifying the st table can cause allocations, which can trigger GC.
+ * Since freeobj_i is called during GC, it must not trigger another GC. */
+ VALUE gc_disabled = rb_gc_disable_no_rest();
+
if (arg->keep_remains) {
- if (st_lookup(arg->object_table, obj, &v)) {
- info = (struct allocation_info *)v;
- info->living = 0;
- }
+ if (st_lookup(arg->object_table, obj, &v)) {
+ info = (struct allocation_info *)v;
+ info->living = 0;
+ }
}
else {
- if (st_delete(arg->object_table, &obj, &v)) {
- info = (struct allocation_info *)v;
- delete_unique_str(arg->str_table, info->path);
- delete_unique_str(arg->str_table, info->class_path);
- ruby_xfree(info);
- }
+ if (st_delete(arg->object_table, &obj, &v)) {
+ info = (struct allocation_info *)v;
+ delete_unique_str(arg->str_table, info->path);
+ delete_unique_str(arg->str_table, info->class_path);
+ ruby_xfree(info);
+ }
}
+
+ if (gc_disabled == Qfalse) rb_gc_enable();
}
static int
@@ -236,12 +243,12 @@ get_traceobj_arg(void)
VALUE obj = TypedData_Make_Struct(rb_cObject, struct traceobj_arg, &allocation_info_tracer_type, tmp_trace_arg);
traceobj_arg = obj;
rb_gc_register_mark_object(traceobj_arg);
- tmp_trace_arg->running = 0;
- tmp_trace_arg->keep_remains = tmp_keep_remains;
- tmp_trace_arg->newobj_trace = 0;
- tmp_trace_arg->freeobj_trace = 0;
- tmp_trace_arg->object_table = st_init_numtable();
- tmp_trace_arg->str_table = st_init_strtable();
+ tmp_trace_arg->running = 0;
+ tmp_trace_arg->keep_remains = tmp_keep_remains;
+ tmp_trace_arg->newobj_trace = 0;
+ tmp_trace_arg->freeobj_trace = 0;
+ tmp_trace_arg->object_table = st_init_numtable();
+ tmp_trace_arg->str_table = st_init_strtable();
}
return tmp_trace_arg;
}
@@ -258,15 +265,15 @@ trace_object_allocations_start(VALUE self)
struct traceobj_arg *arg = get_traceobj_arg();
if (arg->running++ > 0) {
- /* do nothing */
+ /* do nothing */
}
else {
- if (arg->newobj_trace == 0) {
- arg->newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg);
- arg->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg);
- }
- rb_tracepoint_enable(arg->newobj_trace);
- rb_tracepoint_enable(arg->freeobj_trace);
+ if (arg->newobj_trace == 0) {
+ arg->newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg);
+ arg->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg);
+ }
+ rb_tracepoint_enable(arg->newobj_trace);
+ rb_tracepoint_enable(arg->freeobj_trace);
}
return Qnil;
@@ -287,7 +294,7 @@ trace_object_allocations_stop(VALUE self)
struct traceobj_arg *arg = get_traceobj_arg();
if (arg->running > 0) {
- arg->running--;
+ arg->running--;
}
if (arg->running == 0) {
@@ -374,8 +381,8 @@ object_allocations_reporter_i(st_data_t key, st_data_t val, st_data_t ptr)
else fprintf(out, "C: %p", (void *)info->klass);
fprintf(out, "@%s:%lu", info->path ? info->path : "", info->line);
if (!NIL_P(info->mid)) {
- VALUE m = rb_sym2str(info->mid);
- fprintf(out, " (%s)", RSTRING_PTR(m));
+ VALUE m = rb_sym2str(info->mid);
+ fprintf(out, " (%s)", RSTRING_PTR(m));
}
fprintf(out, ")\n");
@@ -387,7 +394,7 @@ object_allocations_reporter(FILE *out, void *ptr)
{
fprintf(out, "== object_allocations_reporter: START\n");
if (tmp_trace_arg) {
- st_foreach(tmp_trace_arg->object_table, object_allocations_reporter_i, (st_data_t)out);
+ st_foreach(tmp_trace_arg->object_table, object_allocations_reporter_i, (st_data_t)out);
}
fprintf(out, "== object_allocations_reporter: END\n");
}
@@ -397,8 +404,8 @@ trace_object_allocations_debug_start(VALUE self)
{
tmp_keep_remains = 1;
if (object_allocations_reporter_registered == 0) {
- object_allocations_reporter_registered = 1;
- rb_bug_reporter_add(object_allocations_reporter, 0);
+ object_allocations_reporter_registered = 1;
+ rb_bug_reporter_add(object_allocations_reporter, 0);
}
return trace_object_allocations_start(self);
@@ -408,10 +415,10 @@ static struct allocation_info *
lookup_allocation_info(VALUE obj)
{
if (tmp_trace_arg) {
- st_data_t info;
- if (st_lookup(tmp_trace_arg->object_table, obj, &info)) {
- return (struct allocation_info *)info;
- }
+ st_data_t info;
+ if (st_lookup(tmp_trace_arg->object_table, obj, &info)) {
+ return (struct allocation_info *)info;
+ }
}
return NULL;
}
@@ -435,10 +442,10 @@ allocation_sourcefile(VALUE self, VALUE obj)
struct allocation_info *info = lookup_allocation_info(obj);
if (info && info->path) {
- return rb_str_new2(info->path);
+ return rb_str_new2(info->path);
}
else {
- return Qnil;
+ return Qnil;
}
}
@@ -455,10 +462,10 @@ allocation_sourceline(VALUE self, VALUE obj)
struct allocation_info *info = lookup_allocation_info(obj);
if (info) {
- return INT2FIX(info->line);
+ return INT2FIX(info->line);
}
else {
- return Qnil;
+ return Qnil;
}
}
@@ -486,10 +493,10 @@ allocation_class_path(VALUE self, VALUE obj)
struct allocation_info *info = lookup_allocation_info(obj);
if (info && info->class_path) {
- return rb_str_new2(info->class_path);
+ return rb_str_new2(info->class_path);
}
else {
- return Qnil;
+ return Qnil;
}
}
@@ -518,10 +525,10 @@ allocation_method_id(VALUE self, VALUE obj)
{
struct allocation_info *info = lookup_allocation_info(obj);
if (info) {
- return info->mid;
+ return info->mid;
}
else {
- return Qnil;
+ return Qnil;
}
}
@@ -550,10 +557,10 @@ allocation_generation(VALUE self, VALUE obj)
{
struct allocation_info *info = lookup_allocation_info(obj);
if (info) {
- return SIZET2NUM(info->generation);
+ return SIZET2NUM(info->generation);
}
else {
- return Qnil;
+ return Qnil;
}
}
diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c
index 9cc66bcfe8..ca08604c95 100644
--- a/ext/objspace/objspace.c
+++ b/ext/objspace/objspace.c
@@ -62,9 +62,9 @@ total_i(VALUE v, void *ptr)
struct total_data *data = (struct total_data *)ptr;
if (!rb_objspace_internal_object_p(v)) {
- if (data->klass == 0 || rb_obj_is_kind_of(v, data->klass)) {
- data->total += rb_obj_memsize_of(v);
- }
+ if (data->klass == 0 || rb_obj_is_kind_of(v, data->klass)) {
+ data->total += rb_obj_memsize_of(v);
+ }
}
}
@@ -140,7 +140,7 @@ memsize_of_all_m(int argc, VALUE *argv, VALUE self)
struct total_data data = {0, 0};
if (argc > 0) {
- rb_scan_args(argc, argv, "01", &data.klass);
+ rb_scan_args(argc, argv, "01", &data.klass);
}
each_object_with_flags(total_i, &data);
@@ -190,33 +190,33 @@ type2sym(enum ruby_value_type i)
VALUE type;
switch (i) {
#define CASE_TYPE(t) case t: type = ID2SYM(rb_intern(#t)); break;
- CASE_TYPE(T_NONE);
- CASE_TYPE(T_OBJECT);
- CASE_TYPE(T_CLASS);
- CASE_TYPE(T_MODULE);
- CASE_TYPE(T_FLOAT);
- CASE_TYPE(T_STRING);
- CASE_TYPE(T_REGEXP);
- CASE_TYPE(T_ARRAY);
- CASE_TYPE(T_HASH);
- CASE_TYPE(T_STRUCT);
- CASE_TYPE(T_BIGNUM);
- CASE_TYPE(T_FILE);
- CASE_TYPE(T_DATA);
- CASE_TYPE(T_MATCH);
- CASE_TYPE(T_COMPLEX);
- CASE_TYPE(T_RATIONAL);
- CASE_TYPE(T_NIL);
- CASE_TYPE(T_TRUE);
- CASE_TYPE(T_FALSE);
- CASE_TYPE(T_SYMBOL);
- CASE_TYPE(T_FIXNUM);
- CASE_TYPE(T_UNDEF);
- CASE_TYPE(T_IMEMO);
- CASE_TYPE(T_NODE);
- CASE_TYPE(T_ICLASS);
+ CASE_TYPE(T_NONE);
+ CASE_TYPE(T_OBJECT);
+ CASE_TYPE(T_CLASS);
+ CASE_TYPE(T_MODULE);
+ CASE_TYPE(T_FLOAT);
+ CASE_TYPE(T_STRING);
+ CASE_TYPE(T_REGEXP);
+ CASE_TYPE(T_ARRAY);
+ CASE_TYPE(T_HASH);
+ CASE_TYPE(T_STRUCT);
+ CASE_TYPE(T_BIGNUM);
+ CASE_TYPE(T_FILE);
+ CASE_TYPE(T_DATA);
+ CASE_TYPE(T_MATCH);
+ CASE_TYPE(T_COMPLEX);
+ CASE_TYPE(T_RATIONAL);
+ CASE_TYPE(T_NIL);
+ CASE_TYPE(T_TRUE);
+ CASE_TYPE(T_FALSE);
+ CASE_TYPE(T_SYMBOL);
+ CASE_TYPE(T_FIXNUM);
+ CASE_TYPE(T_UNDEF);
+ CASE_TYPE(T_IMEMO);
+ CASE_TYPE(T_NODE);
+ CASE_TYPE(T_ICLASS);
CASE_TYPE(T_MOVED);
- CASE_TYPE(T_ZOMBIE);
+ CASE_TYPE(T_ZOMBIE);
#undef CASE_TYPE
default: rb_bug("type2sym: unknown type (%d)", i);
}
@@ -255,17 +255,17 @@ count_objects_size(int argc, VALUE *argv, VALUE os)
VALUE hash = setup_hash(argc, argv);
for (i = 0; i <= T_MASK; i++) {
- counts[i] = 0;
+ counts[i] = 0;
}
each_object_with_flags(cos_i, &counts[0]);
for (i = 0; i <= T_MASK; i++) {
- if (counts[i]) {
- VALUE type = type2sym(i);
- total += counts[i];
- rb_hash_aset(hash, type, SIZET2NUM(counts[i]));
- }
+ if (counts[i]) {
+ VALUE type = type2sym(i);
+ total += counts[i];
+ rb_hash_aset(hash, type, SIZET2NUM(counts[i]));
+ }
}
rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
return hash;
@@ -379,127 +379,128 @@ count_nodes(int argc, VALUE *argv, VALUE os)
VALUE hash = setup_hash(argc, argv);
for (i = 0; i <= NODE_LAST; i++) {
- nodes[i] = 0;
+ nodes[i] = 0;
}
each_object_with_flags(cn_i, &nodes[0]);
for (i=0; i<NODE_LAST; i++) {
- if (nodes[i] != 0) {
- VALUE node;
- switch (i) {
+ if (nodes[i] != 0) {
+ VALUE node;
+ switch (i) {
#define COUNT_NODE(n) case n: node = ID2SYM(rb_intern(#n)); goto set
- COUNT_NODE(NODE_SCOPE);
- COUNT_NODE(NODE_BLOCK);
- COUNT_NODE(NODE_IF);
- COUNT_NODE(NODE_UNLESS);
- COUNT_NODE(NODE_CASE);
- COUNT_NODE(NODE_CASE2);
- COUNT_NODE(NODE_CASE3);
- COUNT_NODE(NODE_WHEN);
- COUNT_NODE(NODE_IN);
- COUNT_NODE(NODE_WHILE);
- COUNT_NODE(NODE_UNTIL);
- COUNT_NODE(NODE_ITER);
- COUNT_NODE(NODE_FOR);
- COUNT_NODE(NODE_FOR_MASGN);
- COUNT_NODE(NODE_BREAK);
- COUNT_NODE(NODE_NEXT);
- COUNT_NODE(NODE_REDO);
- COUNT_NODE(NODE_RETRY);
- COUNT_NODE(NODE_BEGIN);
- COUNT_NODE(NODE_RESCUE);
- COUNT_NODE(NODE_RESBODY);
- COUNT_NODE(NODE_ENSURE);
- COUNT_NODE(NODE_AND);
- COUNT_NODE(NODE_OR);
- COUNT_NODE(NODE_MASGN);
- COUNT_NODE(NODE_LASGN);
- COUNT_NODE(NODE_DASGN);
- COUNT_NODE(NODE_GASGN);
- COUNT_NODE(NODE_IASGN);
- COUNT_NODE(NODE_CDECL);
- COUNT_NODE(NODE_CVASGN);
- COUNT_NODE(NODE_OP_ASGN1);
- COUNT_NODE(NODE_OP_ASGN2);
- COUNT_NODE(NODE_OP_ASGN_AND);
- COUNT_NODE(NODE_OP_ASGN_OR);
- COUNT_NODE(NODE_OP_CDECL);
- COUNT_NODE(NODE_CALL);
- COUNT_NODE(NODE_OPCALL);
- COUNT_NODE(NODE_FCALL);
- COUNT_NODE(NODE_VCALL);
- COUNT_NODE(NODE_QCALL);
- COUNT_NODE(NODE_SUPER);
- COUNT_NODE(NODE_ZSUPER);
- COUNT_NODE(NODE_LIST);
- COUNT_NODE(NODE_ZLIST);
- COUNT_NODE(NODE_VALUES);
- COUNT_NODE(NODE_HASH);
- COUNT_NODE(NODE_RETURN);
- COUNT_NODE(NODE_YIELD);
- COUNT_NODE(NODE_LVAR);
- COUNT_NODE(NODE_DVAR);
- COUNT_NODE(NODE_GVAR);
- COUNT_NODE(NODE_IVAR);
- COUNT_NODE(NODE_CONST);
- COUNT_NODE(NODE_CVAR);
- COUNT_NODE(NODE_NTH_REF);
- COUNT_NODE(NODE_BACK_REF);
- COUNT_NODE(NODE_MATCH);
- COUNT_NODE(NODE_MATCH2);
- COUNT_NODE(NODE_MATCH3);
- COUNT_NODE(NODE_LIT);
- COUNT_NODE(NODE_STR);
- COUNT_NODE(NODE_DSTR);
- COUNT_NODE(NODE_XSTR);
- COUNT_NODE(NODE_DXSTR);
- COUNT_NODE(NODE_EVSTR);
- COUNT_NODE(NODE_DREGX);
- COUNT_NODE(NODE_ONCE);
- COUNT_NODE(NODE_ARGS);
- COUNT_NODE(NODE_ARGS_AUX);
- COUNT_NODE(NODE_OPT_ARG);
- COUNT_NODE(NODE_KW_ARG);
- COUNT_NODE(NODE_POSTARG);
- COUNT_NODE(NODE_ARGSCAT);
- COUNT_NODE(NODE_ARGSPUSH);
- COUNT_NODE(NODE_SPLAT);
- COUNT_NODE(NODE_BLOCK_PASS);
- COUNT_NODE(NODE_DEFN);
- COUNT_NODE(NODE_DEFS);
- COUNT_NODE(NODE_ALIAS);
- COUNT_NODE(NODE_VALIAS);
- COUNT_NODE(NODE_UNDEF);
- COUNT_NODE(NODE_CLASS);
- COUNT_NODE(NODE_MODULE);
- COUNT_NODE(NODE_SCLASS);
- COUNT_NODE(NODE_COLON2);
- COUNT_NODE(NODE_COLON3);
- COUNT_NODE(NODE_DOT2);
- COUNT_NODE(NODE_DOT3);
- COUNT_NODE(NODE_FLIP2);
- COUNT_NODE(NODE_FLIP3);
- COUNT_NODE(NODE_SELF);
- COUNT_NODE(NODE_NIL);
- COUNT_NODE(NODE_TRUE);
- COUNT_NODE(NODE_FALSE);
- COUNT_NODE(NODE_ERRINFO);
- COUNT_NODE(NODE_DEFINED);
- COUNT_NODE(NODE_POSTEXE);
- COUNT_NODE(NODE_DSYM);
- COUNT_NODE(NODE_ATTRASGN);
- COUNT_NODE(NODE_LAMBDA);
- COUNT_NODE(NODE_ARYPTN);
- COUNT_NODE(NODE_FNDPTN);
- COUNT_NODE(NODE_HSHPTN);
+ COUNT_NODE(NODE_SCOPE);
+ COUNT_NODE(NODE_BLOCK);
+ COUNT_NODE(NODE_IF);
+ COUNT_NODE(NODE_UNLESS);
+ COUNT_NODE(NODE_CASE);
+ COUNT_NODE(NODE_CASE2);
+ COUNT_NODE(NODE_CASE3);
+ COUNT_NODE(NODE_WHEN);
+ COUNT_NODE(NODE_IN);
+ COUNT_NODE(NODE_WHILE);
+ COUNT_NODE(NODE_UNTIL);
+ COUNT_NODE(NODE_ITER);
+ COUNT_NODE(NODE_FOR);
+ COUNT_NODE(NODE_FOR_MASGN);
+ COUNT_NODE(NODE_BREAK);
+ COUNT_NODE(NODE_NEXT);
+ COUNT_NODE(NODE_REDO);
+ COUNT_NODE(NODE_RETRY);
+ COUNT_NODE(NODE_BEGIN);
+ COUNT_NODE(NODE_RESCUE);
+ COUNT_NODE(NODE_RESBODY);
+ COUNT_NODE(NODE_ENSURE);
+ COUNT_NODE(NODE_AND);
+ COUNT_NODE(NODE_OR);
+ COUNT_NODE(NODE_MASGN);
+ COUNT_NODE(NODE_LASGN);
+ COUNT_NODE(NODE_DASGN);
+ COUNT_NODE(NODE_GASGN);
+ COUNT_NODE(NODE_IASGN);
+ COUNT_NODE(NODE_CDECL);
+ COUNT_NODE(NODE_CVASGN);
+ COUNT_NODE(NODE_OP_ASGN1);
+ COUNT_NODE(NODE_OP_ASGN2);
+ COUNT_NODE(NODE_OP_ASGN_AND);
+ COUNT_NODE(NODE_OP_ASGN_OR);
+ COUNT_NODE(NODE_OP_CDECL);
+ COUNT_NODE(NODE_CALL);
+ COUNT_NODE(NODE_OPCALL);
+ COUNT_NODE(NODE_FCALL);
+ COUNT_NODE(NODE_VCALL);
+ COUNT_NODE(NODE_QCALL);
+ COUNT_NODE(NODE_SUPER);
+ COUNT_NODE(NODE_ZSUPER);
+ COUNT_NODE(NODE_LIST);
+ COUNT_NODE(NODE_ZLIST);
+ COUNT_NODE(NODE_VALUES);
+ COUNT_NODE(NODE_HASH);
+ COUNT_NODE(NODE_RETURN);
+ COUNT_NODE(NODE_YIELD);
+ COUNT_NODE(NODE_LVAR);
+ COUNT_NODE(NODE_DVAR);
+ COUNT_NODE(NODE_GVAR);
+ COUNT_NODE(NODE_IVAR);
+ COUNT_NODE(NODE_CONST);
+ COUNT_NODE(NODE_CVAR);
+ COUNT_NODE(NODE_NTH_REF);
+ COUNT_NODE(NODE_BACK_REF);
+ COUNT_NODE(NODE_MATCH);
+ COUNT_NODE(NODE_MATCH2);
+ COUNT_NODE(NODE_MATCH3);
+ COUNT_NODE(NODE_LIT);
+ COUNT_NODE(NODE_STR);
+ COUNT_NODE(NODE_DSTR);
+ COUNT_NODE(NODE_XSTR);
+ COUNT_NODE(NODE_DXSTR);
+ COUNT_NODE(NODE_EVSTR);
+ COUNT_NODE(NODE_DREGX);
+ COUNT_NODE(NODE_ONCE);
+ COUNT_NODE(NODE_ARGS);
+ COUNT_NODE(NODE_ARGS_AUX);
+ COUNT_NODE(NODE_OPT_ARG);
+ COUNT_NODE(NODE_KW_ARG);
+ COUNT_NODE(NODE_POSTARG);
+ COUNT_NODE(NODE_ARGSCAT);
+ COUNT_NODE(NODE_ARGSPUSH);
+ COUNT_NODE(NODE_SPLAT);
+ COUNT_NODE(NODE_BLOCK_PASS);
+ COUNT_NODE(NODE_DEFN);
+ COUNT_NODE(NODE_DEFS);
+ COUNT_NODE(NODE_ALIAS);
+ COUNT_NODE(NODE_VALIAS);
+ COUNT_NODE(NODE_UNDEF);
+ COUNT_NODE(NODE_CLASS);
+ COUNT_NODE(NODE_MODULE);
+ COUNT_NODE(NODE_SCLASS);
+ COUNT_NODE(NODE_COLON2);
+ COUNT_NODE(NODE_COLON3);
+ COUNT_NODE(NODE_DOT2);
+ COUNT_NODE(NODE_DOT3);
+ COUNT_NODE(NODE_FLIP2);
+ COUNT_NODE(NODE_FLIP3);
+ COUNT_NODE(NODE_SELF);
+ COUNT_NODE(NODE_NIL);
+ COUNT_NODE(NODE_TRUE);
+ COUNT_NODE(NODE_FALSE);
+ COUNT_NODE(NODE_ERRINFO);
+ COUNT_NODE(NODE_DEFINED);
+ COUNT_NODE(NODE_POSTEXE);
+ COUNT_NODE(NODE_DSYM);
+ COUNT_NODE(NODE_ATTRASGN);
+ COUNT_NODE(NODE_LAMBDA);
+ COUNT_NODE(NODE_ARYPTN);
+ COUNT_NODE(NODE_FNDPTN);
+ COUNT_NODE(NODE_HSHPTN);
+ COUNT_NODE(NODE_ERROR);
#undef COUNT_NODE
- case NODE_LAST: break;
- }
- UNREACHABLE;
- set:
- rb_hash_aset(hash, node, SIZET2NUM(nodes[i]));
- }
+ case NODE_LAST: break;
+ }
+ UNREACHABLE;
+ set:
+ rb_hash_aset(hash, node, SIZET2NUM(nodes[i]));
+ }
}
return hash;
}
@@ -817,26 +818,26 @@ reachable_object_from_root_i(const char *category, VALUE obj, void *ptr)
VALUE category_objects;
if (category == data->last_category) {
- category_str = data->last_category_str;
- category_objects = data->last_category_objects;
+ category_str = data->last_category_str;
+ category_objects = data->last_category_objects;
}
else {
- data->last_category = category;
- category_str = data->last_category_str = rb_str_new2(category);
- category_objects = data->last_category_objects = rb_ident_hash_new();
- if (!NIL_P(rb_hash_lookup(data->categories, category_str))) {
- rb_bug("reachable_object_from_root_i: category should insert at once");
- }
- rb_hash_aset(data->categories, category_str, category_objects);
+ data->last_category = category;
+ category_str = data->last_category_str = rb_str_new2(category);
+ category_objects = data->last_category_objects = rb_ident_hash_new();
+ if (!NIL_P(rb_hash_lookup(data->categories, category_str))) {
+ rb_bug("reachable_object_from_root_i: category should insert at once");
+ }
+ rb_hash_aset(data->categories, category_str, category_objects);
}
if (rb_objspace_markable_object_p(obj) &&
- obj != data->categories &&
- obj != data->last_category_objects) {
- if (rb_objspace_internal_object_p(obj)) {
- obj = iow_newobj(obj);
- }
- rb_hash_aset(category_objects, obj, obj);
+ obj != data->categories &&
+ obj != data->last_category_objects) {
+ if (rb_objspace_internal_object_p(obj)) {
+ obj = iow_newobj(obj);
+ }
+ rb_hash_aset(category_objects, obj, obj);
}
}
@@ -872,14 +873,14 @@ static VALUE
wrap_klass_iow(VALUE klass)
{
if (!RTEST(klass)) {
- return Qnil;
+ return Qnil;
}
else if (RB_TYPE_P(klass, T_ICLASS) ||
CLASS_OF(klass) == Qfalse /* hidden object */) {
- return iow_newobj(klass);
+ return iow_newobj(klass);
}
else {
- return klass;
+ return klass;
}
}
@@ -898,7 +899,7 @@ objspace_internal_class_of(VALUE self, VALUE obj)
VALUE klass;
if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
- obj = (VALUE)DATA_PTR(obj);
+ obj = (VALUE)DATA_PTR(obj);
}
if (RB_TYPE_P(obj, T_IMEMO)) {
@@ -925,17 +926,17 @@ objspace_internal_super_of(VALUE self, VALUE obj)
VALUE super;
if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
- obj = (VALUE)DATA_PTR(obj);
+ obj = (VALUE)DATA_PTR(obj);
}
switch (OBJ_BUILTIN_TYPE(obj)) {
case T_MODULE:
case T_CLASS:
case T_ICLASS:
- super = RCLASS_SUPER(obj);
- break;
+ super = RCLASS_SUPER(obj);
+ break;
default:
- rb_raise(rb_eArgError, "class or module is expected");
+ rb_raise(rb_eArgError, "class or module is expected");
}
return wrap_klass_iow(super);
diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c
index c6c85aec99..c3cc9a1e7b 100644
--- a/ext/objspace/objspace_dump.c
+++ b/ext/objspace/objspace_dump.c
@@ -13,10 +13,15 @@
**********************************************************************/
#include "gc.h"
+#include "id_table.h"
#include "internal.h"
+#include "internal/array.h"
+#include "internal/class.h"
#include "internal/hash.h"
#include "internal/string.h"
#include "internal/sanitizers.h"
+#include "symbol.h"
+#include "shape.h"
#include "node.h"
#include "objspace.h"
#include "ruby/debug.h"
@@ -41,6 +46,7 @@ struct dump_config {
unsigned int full_heap: 1;
unsigned int partial_dump;
size_t since;
+ size_t shapes_since;
unsigned long buffer_len;
char buffer[BUFFER_CAPACITY];
};
@@ -76,7 +82,8 @@ buffer_ensure_capa(struct dump_config *dc, unsigned long requested)
}
}
-static void buffer_append(struct dump_config *dc, const char *cstr, unsigned long len)
+static void
+buffer_append(struct dump_config *dc, const char *cstr, unsigned long len)
{
if (LIKELY(len > 0)) {
buffer_ensure_capa(dc, len);
@@ -142,10 +149,10 @@ dump_append_sizet(struct dump_config *dc, const size_t number)
}
static void
-dump_append_c(struct dump_config *dc, char c)
+dump_append_c(struct dump_config *dc, unsigned char c)
{
if (c <= 0x1f) {
- const unsigned int width = (sizeof(c) * CHAR_BIT / 4) + 5;
+ const unsigned int width = rb_strlen_lit("\\u0000") + 1;
buffer_ensure_capa(dc, width);
unsigned long required = snprintf(dc->buffer + dc->buffer_len, width, "\\u00%02x", c);
RUBY_ASSERT(required <= width);
@@ -163,7 +170,7 @@ dump_append_ref(struct dump_config *dc, VALUE ref)
{
RUBY_ASSERT(ref > 0);
- char buffer[((sizeof(VALUE) * CHAR_BIT + 3) / 4) + 4];
+ char buffer[roomof(sizeof(VALUE) * CHAR_BIT, 4) + rb_strlen_lit("\"0x\"")];
char *buffer_start, *buffer_end;
buffer_start = buffer_end = &buffer[sizeof(buffer)];
@@ -235,32 +242,32 @@ obj_type(VALUE obj)
{
switch (BUILTIN_TYPE(obj)) {
#define CASE_TYPE(type) case T_##type: return #type
- CASE_TYPE(NONE);
- CASE_TYPE(NIL);
- CASE_TYPE(OBJECT);
- CASE_TYPE(CLASS);
- CASE_TYPE(ICLASS);
- CASE_TYPE(MODULE);
- CASE_TYPE(FLOAT);
- CASE_TYPE(STRING);
- CASE_TYPE(REGEXP);
- CASE_TYPE(ARRAY);
- CASE_TYPE(HASH);
- CASE_TYPE(STRUCT);
- CASE_TYPE(BIGNUM);
- CASE_TYPE(FILE);
- CASE_TYPE(FIXNUM);
- CASE_TYPE(TRUE);
- CASE_TYPE(FALSE);
- CASE_TYPE(DATA);
- CASE_TYPE(MATCH);
- CASE_TYPE(SYMBOL);
- CASE_TYPE(RATIONAL);
- CASE_TYPE(COMPLEX);
- CASE_TYPE(IMEMO);
- CASE_TYPE(UNDEF);
- CASE_TYPE(NODE);
- CASE_TYPE(ZOMBIE);
+ CASE_TYPE(NONE);
+ CASE_TYPE(NIL);
+ CASE_TYPE(OBJECT);
+ CASE_TYPE(CLASS);
+ CASE_TYPE(ICLASS);
+ CASE_TYPE(MODULE);
+ CASE_TYPE(FLOAT);
+ CASE_TYPE(STRING);
+ CASE_TYPE(REGEXP);
+ CASE_TYPE(ARRAY);
+ CASE_TYPE(HASH);
+ CASE_TYPE(STRUCT);
+ CASE_TYPE(BIGNUM);
+ CASE_TYPE(FILE);
+ CASE_TYPE(FIXNUM);
+ CASE_TYPE(TRUE);
+ CASE_TYPE(FALSE);
+ CASE_TYPE(DATA);
+ CASE_TYPE(MATCH);
+ CASE_TYPE(SYMBOL);
+ CASE_TYPE(RATIONAL);
+ CASE_TYPE(COMPLEX);
+ CASE_TYPE(IMEMO);
+ CASE_TYPE(UNDEF);
+ CASE_TYPE(NODE);
+ CASE_TYPE(ZOMBIE);
#undef CASE_TYPE
default: break;
}
@@ -348,6 +355,20 @@ dump_append_string_content(struct dump_config *dc, VALUE obj)
}
}
+static inline void
+dump_append_id(struct dump_config *dc, ID id)
+{
+ if (is_instance_id(id)) {
+ dump_append_string_value(dc, rb_sym2str(ID2SYM(id)));
+ }
+ else {
+ dump_append(dc, "\"ID_INTERNAL(");
+ dump_append_sizet(dc, rb_id_to_serial(id));
+ dump_append(dc, ")\"");
+ }
+}
+
+
static void
dump_object(VALUE obj, struct dump_config *dc)
{
@@ -364,7 +385,11 @@ dump_object(VALUE obj, struct dump_config *dc)
dc->cur_obj = obj;
dc->cur_obj_references = 0;
- dc->cur_obj_klass = BUILTIN_TYPE(obj) == T_NODE ? 0 : RBASIC_CLASS(obj);
+ if (BUILTIN_TYPE(obj) == T_NODE || BUILTIN_TYPE(obj) == T_IMEMO) {
+ dc->cur_obj_klass = 0;
+ } else {
+ dc->cur_obj_klass = RBASIC_CLASS(obj);
+ }
if (dc->partial_dump && (!ainfo || ainfo->generation < dc->since)) {
return;
@@ -380,6 +405,10 @@ 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);
+
dump_append(dc, ", \"slot_size\":");
dump_append_sizet(dc, dc->cur_page_slot_size);
@@ -455,7 +484,7 @@ dump_object(VALUE obj, struct dump_config *dc)
case T_ARRAY:
dump_append(dc, ", \"length\":");
dump_append_ld(dc, RARRAY_LEN(obj));
- if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, ELTS_SHARED))
+ if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, RARRAY_SHARED_FLAG))
dump_append(dc, ", \"shared\":true");
if (FL_TEST(obj, RARRAY_EMBED_FLAG))
dump_append(dc, ", \"embedded\":true");
@@ -469,6 +498,9 @@ dump_object(VALUE obj, struct dump_config *dc)
break;
case T_CLASS:
+ dump_append(dc, ", \"variation_count\":");
+ dump_append_d(dc, RCLASS_EXT(obj)->variation_count);
+
case T_MODULE:
if (rb_class_get_superclass(obj)) {
dump_append(dc, ", \"superclass\":");
@@ -481,7 +513,8 @@ dump_object(VALUE obj, struct dump_config *dc)
dump_append(dc, ", \"name\":\"");
dump_append(dc, RSTRING_PTR(mod_name));
dump_append(dc, "\"");
- } else {
+ }
+ else {
VALUE real_mod_name = rb_mod_name(rb_class_real(obj));
if (RTEST(real_mod_name)) {
dump_append(dc, ", \"real_class_name\":\"");
@@ -512,7 +545,10 @@ dump_object(VALUE obj, struct dump_config *dc)
case T_OBJECT:
dump_append(dc, ", \"ivars\":");
- dump_append_lu(dc, ROBJECT_NUMIV(obj));
+ dump_append_lu(dc, ROBJECT_IV_COUNT(obj));
+ if (rb_shape_obj_too_complex(obj)) {
+ dump_append(dc, ", \"too_complex_shape\":true");
+ }
break;
case T_FILE:
@@ -524,8 +560,8 @@ dump_object(VALUE obj, struct dump_config *dc)
break;
case T_ZOMBIE:
- dump_append(dc, "}\n");
- return;
+ dump_append(dc, "}\n");
+ return;
default:
break;
@@ -583,8 +619,8 @@ heap_i(void *vstart, void *vend, size_t stride, void *data)
asan_unpoison_object(v, false);
dc->cur_page_slot_size = stride;
- if (dc->full_heap || RBASIC(v)->flags)
- dump_object(v, dc);
+ if (dc->full_heap || RBASIC(v)->flags)
+ dump_object(v, dc);
if (ptr) {
asan_poison_object(v);
@@ -616,7 +652,7 @@ root_obj_i(const char *category, VALUE obj, void *data)
}
static void
-dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since)
+dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since, VALUE shapes)
{
dc->full_heap = 0;
@@ -625,7 +661,8 @@ dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since)
if (TYPE(output) == T_STRING) {
dc->stream = Qfalse;
dc->string = output;
- } else {
+ }
+ else {
dc->stream = output;
dc->string = Qfalse;
}
@@ -637,9 +674,12 @@ dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since)
if (RTEST(since)) {
dc->partial_dump = 1;
dc->since = NUM2SIZET(since);
- } else {
+ }
+ else {
dc->partial_dump = 0;
}
+
+ dc->shapes_since = RTEST(shapes) ? NUM2SIZET(shapes) : 0;
}
static VALUE
@@ -649,12 +689,14 @@ dump_result(struct dump_config *dc)
if (dc->string) {
return dc->string;
- } else {
+ }
+ else {
rb_io_flush(dc->stream);
return dc->stream;
}
}
+/* :nodoc: */
static VALUE
objspace_dump(VALUE os, VALUE obj, VALUE output)
{
@@ -663,18 +705,87 @@ objspace_dump(VALUE os, VALUE obj, VALUE output)
dc.cur_page_slot_size = rb_gc_obj_slot_size(obj);
}
- dump_output(&dc, output, Qnil, Qnil);
+ dump_output(&dc, output, Qnil, Qnil, Qnil);
dump_object(obj, &dc);
return dump_result(&dc);
}
+static void
+shape_i(rb_shape_t *shape, 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(dc, ", \"type\":\"SHAPE\", \"id\":");
+ dump_append_sizet(dc, shape_id);
+
+ if (shape->type != SHAPE_ROOT) {
+ dump_append(dc, ", \"parent_id\":");
+ dump_append_lu(dc, shape->parent_id);
+ }
+
+ dump_append(dc, ", \"depth\":");
+ dump_append_sizet(dc, rb_shape_depth(shape));
+
+ dump_append(dc, ", \"shape_type\":");
+ switch((enum shape_type)shape->type) {
+ case SHAPE_ROOT:
+ dump_append(dc, "\"ROOT\"");
+ break;
+ case SHAPE_IVAR:
+ dump_append(dc, "\"IVAR\"");
+
+ dump_append(dc, ",\"edge_name\":");
+ dump_append_id(dc, shape->edge_name);
+
+ break;
+ case SHAPE_FROZEN:
+ dump_append(dc, "\"FROZEN\"");
+ break;
+ case SHAPE_CAPACITY_CHANGE:
+ dump_append(dc, "\"CAPACITY_CHANGE\"");
+ dump_append(dc, ", \"capacity\":");
+ dump_append_sizet(dc, shape->capacity);
+ break;
+ case SHAPE_INITIAL_CAPACITY:
+ dump_append(dc, "\"INITIAL_CAPACITY\"");
+ dump_append(dc, ", \"capacity\":");
+ dump_append_sizet(dc, shape->capacity);
+ 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(dc, ", \"memsize\":");
+ dump_append_sizet(dc, rb_shape_memsize(shape));
+
+ dump_append(dc, "}\n");
+}
+
+/* :nodoc: */
static VALUE
-objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since)
+objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since, VALUE shapes)
{
struct dump_config dc = {0,};
- dump_output(&dc, output, full, since);
+ dump_output(&dc, output, full, since, shapes);
if (!dc.partial_dump || dc.since == 0) {
/* dump roots */
@@ -682,12 +793,29 @@ objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since)
if (dc.roots) dump_append(&dc, "]}\n");
}
+ if (RTEST(shapes)) {
+ rb_shape_each_shape(shape_i, &dc);
+ }
+
/* dump all objects */
rb_objspace_each_objects(heap_i, &dc);
return dump_result(&dc);
}
+/* :nodoc: */
+static VALUE
+objspace_dump_shapes(VALUE os, VALUE output, VALUE shapes)
+{
+ struct dump_config dc = {0,};
+ dump_output(&dc, output, Qfalse, Qnil, shapes);
+
+ if (RTEST(shapes)) {
+ rb_shape_each_shape(shape_i, &dc);
+ }
+ return dump_result(&dc);
+}
+
void
Init_objspace_dump(VALUE rb_mObjSpace)
{
@@ -697,7 +825,8 @@ Init_objspace_dump(VALUE rb_mObjSpace)
#endif
rb_define_module_function(rb_mObjSpace, "_dump", objspace_dump, 2);
- rb_define_module_function(rb_mObjSpace, "_dump_all", objspace_dump_all, 3);
+ 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 479ec3b4a2..1e0df7dd87 100644
--- a/ext/openssl/History.md
+++ b/ext/openssl/History.md
@@ -1,3 +1,77 @@
+Version 3.1.0
+=============
+
+Ruby/OpenSSL 3.1 will be maintained for the lifetime of Ruby 3.2.
+
+Merged bug fixes in 2.2.3 and 3.0.2. Among the new features and changes are:
+
+Notable changes
+---------------
+
+* Add `OpenSSL::SSL::SSLContext#ciphersuites=` to allow setting TLS 1.3 cipher
+ suites.
+ [[GitHub #493]](https://github.com/ruby/openssl/pull/493)
+* Add `OpenSSL::SSL::SSLSocket#export_keying_material` for exporting keying
+ material of the session, as defined in RFC 5705.
+ [[GitHub #530]](https://github.com/ruby/openssl/pull/530)
+* Add `OpenSSL::SSL::SSLContext#keylog_cb=` for setting the TLS key logging
+ callback, which is useful for supporting NSS's SSLKEYLOGFILE debugging output.
+ [[GitHub #536]](https://github.com/ruby/openssl/pull/536)
+* Remove the default digest algorithm from `OpenSSL::OCSP::BasicResponse#sign`
+ and `OpenSSL::OCSP::Request#sign`. Omitting the 5th parameter of these
+ methods used to be equivalent of specifying SHA-1. This default value is now
+ removed and we will let the underlying OpenSSL library decide instead.
+ [[GitHub #507]](https://github.com/ruby/openssl/pull/507)
+* Add `OpenSSL::BN#mod_sqrt`.
+ [[GitHub #553]](https://github.com/ruby/openssl/pull/553)
+* Allow calling `OpenSSL::Cipher#update` with an empty string. This was
+ prohibited to workaround an ancient bug in OpenSSL.
+ [[GitHub #568]](https://github.com/ruby/openssl/pull/568)
+* Fix build on platforms without socket support, such as WASI. `OpenSSL::SSL`
+ will not be defined if OpenSSL is compiled with `OPENSSL_NO_SOCK`.
+ [[GitHub #558]](https://github.com/ruby/openssl/pull/558)
+* Improve support for recent LibreSSL versions. This includes HKDF support in
+ LibreSSL 3.6 and Ed25519 support in LibreSSL 3.7.
+
+
+Version 3.0.2
+=============
+
+Merged changes in 2.2.3. Additionally, the following issues are fixed by this
+release.
+
+Bug fixes
+---------
+
+* Fix OpenSSL::PKey::EC#check_key not working correctly on OpenSSL 3.0.
+ [[GitHub #563]](https://github.com/ruby/openssl/issues/563)
+ [[GitHub #580]](https://github.com/ruby/openssl/pull/580)
+
+
+Version 3.0.1
+=============
+
+Merged changes in 2.1.4 and 2.2.2. Additionally, the following issues are fixed
+by this release.
+
+Bug fixes
+---------
+
+* Add missing type check in OpenSSL::PKey::PKey#sign's optional parameters.
+ [[GitHub #531]](https://github.com/ruby/openssl/pull/531)
+* Work around OpenSSL 3.0's HMAC issues with a zero-length key.
+ [[GitHub #538]](https://github.com/ruby/openssl/pull/538)
+* Fix a regression in OpenSSL::PKey::DSA.generate's default of 'q' size.
+ [[GitHub #483]](https://github.com/ruby/openssl/issues/483)
+ [[GitHub #539]](https://github.com/ruby/openssl/pull/539)
+* Restore OpenSSL::PKey.read's ability to decode "openssl ecparam -genkey"
+ output when linked against OpenSSL 3.0.
+ [[GitHub #535]](https://github.com/ruby/openssl/pull/535)
+ [[GitHub #540]](https://github.com/ruby/openssl/pull/540)
+* Restore error checks in OpenSSL::PKey::EC#{to_der,to_pem}.
+ [[GitHub #541]](https://github.com/ruby/openssl/pull/541)
+
+
Version 3.0.0
=============
@@ -100,6 +174,27 @@ Notable changes
[[GitHub #342]](https://github.com/ruby/openssl/issues/342)
+Version 2.2.3
+=============
+
+Bug fixes
+---------
+
+* Fix serveral methods in OpenSSL::PKey::EC::Point attempting to raise an error
+ with an incorrect class, which would end up with a TypeError.
+ [[GitHub #570]](https://github.com/ruby/openssl/pull/570)
+* Fix OpenSSL::PKey::EC::Point#eql? and OpenSSL::PKey::EC::Group#eql?
+ incorrectly treated OpenSSL's internal errors as "not equal".
+ [[GitHub #564]](https://github.com/ruby/openssl/pull/564)
+* Fix build with LibreSSL 3.5 or later.
+
+
+Version 2.2.2
+=============
+
+Merged changes in 2.1.4.
+
+
Version 2.2.1
=============
@@ -194,6 +289,16 @@ Notable changes
[[GitHub #297]](https://github.com/ruby/openssl/pull/297)
+Version 2.1.4
+=============
+
+Bug fixes
+---------
+
+* Do not use pkg-config if --with-openssl-dir option is specified.
+ [[GitHub #486]](https://github.com/ruby/openssl/pull/486)
+
+
Version 2.1.3
=============
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index cc2b1f8ba2..bc3e4d3a21 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -25,8 +25,9 @@ Logging::message "=== OpenSSL for Ruby configurator ===\n"
if with_config("debug") or enable_config("debug")
$defs.push("-DOSSL_DEBUG")
end
+$defs.push("-D""OPENSSL_SUPPRESS_DEPRECATED")
-have_func("rb_io_maybe_wait") # Ruby 3.1
+have_func("rb_io_maybe_wait(0, Qnil, Qnil, Qnil)", "ruby/io.h") # Ruby 3.1
Logging::message "=== Checking for system dependent stuff... ===\n"
have_library("nsl", "t_open")
@@ -120,8 +121,13 @@ if is_libressl && ($mswin || $mingw)
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")
+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|
@@ -132,65 +138,59 @@ engines.each { |name|
if !have_struct_member("SSL", "ctx", "openssl/ssl.h") || is_libressl
$defs.push("-DHAVE_OPAQUE_OPENSSL")
end
-have_func("EVP_MD_CTX_new")
-have_func("EVP_MD_CTX_free")
-have_func("EVP_MD_CTX_pkey_ctx")
-have_func("X509_STORE_get_ex_data")
-have_func("X509_STORE_set_ex_data")
-have_func("X509_STORE_get_ex_new_index")
-have_func("X509_CRL_get0_signature")
-have_func("X509_REQ_get0_signature")
-have_func("X509_REVOKED_get0_serialNumber")
-have_func("X509_REVOKED_get0_revocationDate")
-have_func("X509_get0_tbs_sigalg")
-have_func("X509_STORE_CTX_get0_untrusted")
-have_func("X509_STORE_CTX_get0_cert")
-have_func("X509_STORE_CTX_get0_chain")
-have_func("OCSP_SINGLERESP_get0_id")
-have_func("SSL_CTX_get_ciphers")
-have_func("X509_up_ref")
-have_func("X509_CRL_up_ref")
-have_func("X509_STORE_up_ref")
-have_func("SSL_SESSION_up_ref")
-have_func("EVP_PKEY_up_ref")
-have_func("SSL_CTX_set_min_proto_version(NULL, 0)", "openssl/ssl.h")
-have_func("SSL_CTX_get_security_level")
-have_func("X509_get0_notBefore")
-have_func("SSL_SESSION_get_protocol_version")
-have_func("TS_STATUS_INFO_get0_status")
-have_func("TS_STATUS_INFO_get0_text")
-have_func("TS_STATUS_INFO_get0_failure_info")
-have_func("TS_VERIFY_CTS_set_certs(NULL, NULL)", "openssl/ts.h")
-have_func("TS_VERIFY_CTX_set_store")
-have_func("TS_VERIFY_CTX_add_flags")
-have_func("TS_RESP_CTX_set_time_cb")
-have_func("EVP_PBE_scrypt")
-have_func("SSL_CTX_set_post_handshake_auth")
+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)
+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
-have_func("EVP_PKEY_check")
-have_func("SSL_CTX_set_ciphersuites")
+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
-openssl_3 =
-have_func("SSL_set0_tmp_dh_pkey")
-have_func("ERR_get_error_all")
-have_func("TS_VERIFY_CTX_set_certs(NULL, NULL)", "openssl/ts.h")
-have_func("SSL_CTX_load_verify_file")
-have_func("BN_check_prime")
-have_func("EVP_MD_CTX_get0_md")
-have_func("EVP_MD_CTX_get_pkey_ctx")
-have_func("EVP_PKEY_eq")
-have_func("EVP_PKEY_dup")
+have_func("SSL_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)
+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)
Logging::message "=== Checking done. ===\n"
-if openssl_3
- if $warnflags&.sub!(/-W\K(?=deprecated-declarations)/, 'no-')
- $warnflags << " -Wno-incompatible-pointer-types-discards-qualifiers"
- end
-end
-
create_header
create_makefile("openssl")
Logging::message "Done.\n"
diff --git a/ext/openssl/lib/openssl/pkey.rb b/ext/openssl/lib/openssl/pkey.rb
index c3e0629091..0414658a10 100644
--- a/ext/openssl/lib/openssl/pkey.rb
+++ b/ext/openssl/lib/openssl/pkey.rb
@@ -167,8 +167,16 @@ module OpenSSL::PKey
# +size+::
# The desired key size in bits.
def generate(size, &blk)
+ # FIPS 186-4 specifies four (L,N) pairs: (1024,160), (2048,224),
+ # (2048,256), and (3072,256).
+ #
+ # q size is derived here with compatibility with
+ # DSA_generator_parameters_ex() which previous versions of ruby/openssl
+ # used to call.
+ qsize = size >= 2048 ? 256 : 160
dsaparams = OpenSSL::PKey.generate_parameters("DSA", {
"dsa_paramgen_bits" => size,
+ "dsa_paramgen_q_bits" => qsize,
}, &blk)
OpenSSL::PKey.generate_key(dsaparams)
end
@@ -355,7 +363,8 @@ module OpenSSL::PKey
# rsa.private_encrypt(string, padding) -> String
#
# Encrypt +string+ with the private key. +padding+ defaults to
- # PKCS1_PADDING. The encrypted string output can be decrypted using
+ # PKCS1_PADDING, which is known to be insecure but is kept for backwards
+ # compatibility. The encrypted string output can be decrypted using
# #public_decrypt.
#
# <b>Deprecated in version 3.0</b>.
@@ -378,7 +387,8 @@ module OpenSSL::PKey
# rsa.public_decrypt(string, padding) -> String
#
# Decrypt +string+, which has been encrypted with the private key, with the
- # public key. +padding+ defaults to PKCS1_PADDING.
+ # public key. +padding+ defaults to PKCS1_PADDING which is known to be
+ # insecure but is kept for backwards compatibility.
#
# <b>Deprecated in version 3.0</b>.
# Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw, and
@@ -399,7 +409,8 @@ module OpenSSL::PKey
# rsa.public_encrypt(string, padding) -> String
#
# Encrypt +string+ with the public key. +padding+ defaults to
- # PKCS1_PADDING. The encrypted string output can be decrypted using
+ # PKCS1_PADDING, which is known to be insecure but is kept for backwards
+ # compatibility. The encrypted string output can be decrypted using
# #private_decrypt.
#
# <b>Deprecated in version 3.0</b>.
@@ -420,7 +431,8 @@ module OpenSSL::PKey
# rsa.private_decrypt(string, padding) -> String
#
# Decrypt +string+, which has been encrypted with the public key, with the
- # private key. +padding+ defaults to PKCS1_PADDING.
+ # private key. +padding+ defaults to PKCS1_PADDING, which is known to be
+ # insecure but is kept for backwards compatibility.
#
# <b>Deprecated in version 3.0</b>.
# Consider using PKey::PKey#encrypt and PKey::PKey#decrypt instead.
diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb
index a9103ecd27..ea8bb2a18e 100644
--- a/ext/openssl/lib/openssl/ssl.rb
+++ b/ext/openssl/lib/openssl/ssl.rb
@@ -11,6 +11,9 @@
=end
require "openssl/buffering"
+
+if defined?(OpenSSL::SSL)
+
require "io/nonblock"
require "ipaddr"
require "socket"
@@ -540,3 +543,5 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
end
end
end
+
+end
diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb
index 5e60604353..4163f55064 100644
--- a/ext/openssl/lib/openssl/version.rb
+++ b/ext/openssl/lib/openssl/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module OpenSSL
- VERSION = "3.0.0"
+ VERSION = "3.1.0"
end
diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec
index c6cd818336..8d83b69193 100644
--- a/ext/openssl/openssl.gemspec
+++ b/ext/openssl/openssl.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "openssl"
- spec.version = "3.0.0"
+ spec.version = "3.1.0"
spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"]
spec.email = ["ruby-core@ruby-lang.org"]
spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.}
diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h
index 2ab8aeaebb..facb80aa73 100644
--- a/ext/openssl/ossl.h
+++ b/ext/openssl/ossl.h
@@ -52,6 +52,12 @@
(LIBRESSL_VERSION_NUMBER >= ((maj << 28) | (min << 20) | (pat << 12)))
#endif
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+# define OSSL_3_const const
+#else
+# define OSSL_3_const /* const */
+#endif
+
#if !defined(OPENSSL_NO_ENGINE) && !OSSL_OPENSSL_PREREQ(3, 0, 0)
# define OSSL_USE_ENGINE
#endif
diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c
index 0d3fa9ad15..71c452c88a 100644
--- a/ext/openssl/ossl_asn1.c
+++ b/ext/openssl/ossl_asn1.c
@@ -509,7 +509,8 @@ ossl_asn1_get_asn1type(VALUE obj)
ASN1_TYPE *ret;
VALUE value, rflag;
void *ptr;
- void (*free_func)();
+ typedef void free_func_type(void *);
+ free_func_type *free_func;
int tag;
tag = ossl_asn1_default_tag(obj);
@@ -522,16 +523,16 @@ ossl_asn1_get_asn1type(VALUE obj)
case V_ASN1_INTEGER: /* FALLTHROUGH */
case V_ASN1_ENUMERATED:
ptr = obj_to_asn1int(value);
- free_func = ASN1_INTEGER_free;
+ 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 = ASN1_BIT_STRING_free;
+ free_func = (free_func_type *)ASN1_BIT_STRING_free;
break;
case V_ASN1_NULL:
ptr = obj_to_asn1null(value);
- free_func = ASN1_NULL_free;
+ free_func = (free_func_type *)ASN1_NULL_free;
break;
case V_ASN1_OCTET_STRING: /* FALLTHROUGH */
case V_ASN1_UTF8STRING: /* FALLTHROUGH */
@@ -546,24 +547,24 @@ ossl_asn1_get_asn1type(VALUE obj)
case V_ASN1_UNIVERSALSTRING: /* FALLTHROUGH */
case V_ASN1_BMPSTRING:
ptr = obj_to_asn1str(value);
- free_func = ASN1_STRING_free;
+ free_func = (free_func_type *)ASN1_STRING_free;
break;
case V_ASN1_OBJECT:
ptr = obj_to_asn1obj(value);
- free_func = ASN1_OBJECT_free;
+ free_func = (free_func_type *)ASN1_OBJECT_free;
break;
case V_ASN1_UTCTIME:
ptr = obj_to_asn1utime(value);
- free_func = ASN1_TIME_free;
+ free_func = (free_func_type *)ASN1_TIME_free;
break;
case V_ASN1_GENERALIZEDTIME:
ptr = obj_to_asn1gtime(value);
- free_func = ASN1_TIME_free;
+ 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 = ASN1_STRING_free;
+ free_func = (free_func_type *)ASN1_STRING_free;
break;
default:
ossl_raise(eASN1Error, "unsupported ASN.1 type");
diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c
index 56fa0ec302..bf2bac3679 100644
--- a/ext/openssl/ossl_bn.c
+++ b/ext/openssl/ossl_bn.c
@@ -577,22 +577,33 @@ 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; \
+ }
+
/*
+ * Document-method: OpenSSL::BN#mod_sqrt
+ * call-seq:
+ * bn.mod_sqrt(bn2) => aBN
+ */
+BIGNUM_2cr(mod_sqrt)
+
+/*
+ * Document-method: OpenSSL::BN#mod_inverse
* call-seq:
* bn.mod_inverse(bn2) => aBN
*/
-static VALUE
-ossl_bn_mod_inverse(VALUE self, VALUE other)
-{
- BIGNUM *bn1, *bn2 = GetBNPtr(other), *result;
- VALUE obj;
- GetBN(self, bn1);
- obj = NewBN(rb_obj_class(self));
- if (!(result = BN_mod_inverse(NULL, bn1, bn2, ossl_bn_ctx)))
- ossl_raise(eBNError, "BN_mod_inverse");
- SetBN(obj, result);
- return obj;
-}
+BIGNUM_2cr(mod_inverse)
/*
* call-seq:
@@ -1234,6 +1245,7 @@ Init_ossl_bn(void)
rb_define_method(cBN, "mod_sub", ossl_bn_mod_sub, 2);
rb_define_method(cBN, "mod_mul", ossl_bn_mod_mul, 2);
rb_define_method(cBN, "mod_sqr", ossl_bn_mod_sqr, 1);
+ rb_define_method(cBN, "mod_sqrt", ossl_bn_mod_sqrt, 1);
rb_define_method(cBN, "**", ossl_bn_exp, 1);
rb_define_method(cBN, "mod_exp", ossl_bn_mod_exp, 2);
rb_define_method(cBN, "gcd", ossl_bn_gcd, 1);
diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c
index d9c7891433..cb8fbc3ca2 100644
--- a/ext/openssl/ossl_cipher.c
+++ b/ext/openssl/ossl_cipher.c
@@ -384,8 +384,7 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self)
StringValue(data);
in = (unsigned char *)RSTRING_PTR(data);
- if ((in_len = RSTRING_LEN(data)) == 0)
- ossl_raise(rb_eArgError, "data must not be empty");
+ in_len = RSTRING_LEN(data);
GetCipher(self, ctx);
out_len = in_len+EVP_CIPHER_CTX_block_size(ctx);
if (out_len <= 0) {
diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c
index bfe3a74b12..1a5f471a27 100644
--- a/ext/openssl/ossl_hmac.c
+++ b/ext/openssl/ossl_hmac.c
@@ -97,11 +97,19 @@ ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest)
GetHMAC(self, ctx);
StringValue(key);
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+ 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) {
EVP_PKEY_free(pkey);
diff --git a/ext/openssl/ossl_kdf.c b/ext/openssl/ossl_kdf.c
index 7fa38b865e..0d25a7304b 100644
--- a/ext/openssl/ossl_kdf.c
+++ b/ext/openssl/ossl_kdf.c
@@ -3,7 +3,7 @@
* Copyright (C) 2007, 2017 Ruby/OpenSSL Project Authors
*/
#include "ossl.h"
-#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 6, 0)
# include <openssl/kdf.h>
#endif
@@ -141,7 +141,7 @@ kdf_scrypt(int argc, VALUE *argv, VALUE self)
}
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 6, 0)
/*
* call-seq:
* KDF.hkdf(ikm, salt:, info:, length:, hash:) -> String
@@ -305,7 +305,7 @@ Init_ossl_kdf(void)
#if defined(HAVE_EVP_PBE_SCRYPT)
rb_define_module_function(mKDF, "scrypt", kdf_scrypt, -1);
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+#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_pkey.c b/ext/openssl/ossl_pkey.c
index 24d0da4683..476256679b 100644
--- a/ext/openssl/ossl_pkey.c
+++ b/ext/openssl/ossl_pkey.c
@@ -99,17 +99,56 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass)
/* First check DER */
if (OSSL_DECODER_from_bio(dctx, bio) == 1)
goto out;
+ OSSL_BIO_reset(bio);
/* Then check PEM; multiple OSSL_DECODER_from_bio() calls may be needed */
- OSSL_BIO_reset(bio);
if (OSSL_DECODER_CTX_set_input_type(dctx, "PEM") != 1)
goto out;
- while (OSSL_DECODER_from_bio(dctx, bio) != 1) {
- if (BIO_eof(bio))
+ /*
+ * First check for private key formats. This is to keep compatibility with
+ * ruby/openssl < 3.0 which decoded the following as a private key.
+ *
+ * $ openssl ecparam -name prime256v1 -genkey -outform PEM
+ * -----BEGIN EC PARAMETERS-----
+ * BggqhkjOPQMBBw==
+ * -----END EC PARAMETERS-----
+ * -----BEGIN EC PRIVATE KEY-----
+ * MHcCAQEEIAG8ugBbA5MHkqnZ9ujQF93OyUfL9tk8sxqM5Wv5tKg5oAoGCCqGSM49
+ * AwEHoUQDQgAEVcjhJfkwqh5C7kGuhAf8XaAjVuG5ADwb5ayg/cJijCgs+GcXeedj
+ * 86avKpGH84DXUlB23C/kPt+6fXYlitUmXQ==
+ * -----END EC PRIVATE KEY-----
+ *
+ * While the first PEM block is a proper encoding of ECParameters, thus
+ * OSSL_DECODER_from_bio() would pick it up, ruby/openssl used to return
+ * the latter instead. Existing applications expect this behavior.
+ *
+ * Note that normally, the input is supposed to contain a single decodable
+ * PEM block only, so this special handling should not create a new problem.
+ */
+ OSSL_DECODER_CTX_set_selection(dctx, EVP_PKEY_KEYPAIR);
+ while (1) {
+ if (OSSL_DECODER_from_bio(dctx, bio) == 1)
goto out;
+ if (BIO_eof(bio))
+ break;
pos2 = BIO_tell(bio);
if (pos2 < 0 || pos2 <= pos)
+ break;
+ ossl_clear_error();
+ pos = pos2;
+ }
+
+ OSSL_BIO_reset(bio);
+ OSSL_DECODER_CTX_set_selection(dctx, 0);
+ while (1) {
+ if (OSSL_DECODER_from_bio(dctx, bio) == 1)
goto out;
+ if (BIO_eof(bio))
+ break;
+ pos2 = BIO_tell(bio);
+ if (pos2 < 0 || pos2 <= pos)
+ break;
+ ossl_clear_error();
pos = pos2;
}
@@ -200,6 +239,7 @@ static VALUE
pkey_ctx_apply_options0(VALUE args_v)
{
VALUE *args = (VALUE *)args_v;
+ Check_Type(args[1], T_HASH);
rb_block_call(args[1], rb_intern("each"), 0, NULL,
pkey_ctx_apply_options_i, args[0]);
@@ -911,7 +951,7 @@ ossl_pkey_sign(int argc, VALUE *argv, VALUE self)
rb_jump_tag(state);
}
}
-#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
+#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);
@@ -1016,7 +1056,7 @@ ossl_pkey_verify(int argc, VALUE *argv, VALUE self)
rb_jump_tag(state);
}
}
-#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
+#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));
diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h
index 38fb9fad10..10669b824c 100644
--- a/ext/openssl/ossl_pkey.h
+++ b/ext/openssl/ossl_pkey.h
@@ -92,7 +92,7 @@ void Init_ossl_ec(void);
*/ \
static VALUE ossl_##_keytype##_get_##_name(VALUE self) \
{ \
- _type *obj; \
+ const _type *obj; \
const BIGNUM *bn; \
\
Get##_type(self, obj); \
diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c
index 696455dcfd..83c41378fe 100644
--- a/ext/openssl/ossl_pkey_dh.c
+++ b/ext/openssl/ossl_pkey_dh.c
@@ -178,7 +178,7 @@ ossl_dh_initialize_copy(VALUE self, VALUE other)
static VALUE
ossl_dh_is_public(VALUE self)
{
- DH *dh;
+ OSSL_3_const DH *dh;
const BIGNUM *bn;
GetDH(self, dh);
@@ -197,14 +197,14 @@ ossl_dh_is_public(VALUE self)
static VALUE
ossl_dh_is_private(VALUE self)
{
- DH *dh;
+ OSSL_3_const DH *dh;
const BIGNUM *bn;
GetDH(self, dh);
DH_get0_key(dh, NULL, &bn);
#if !defined(OPENSSL_NO_ENGINE)
- return (bn || DH_get0_engine(dh)) ? Qtrue : Qfalse;
+ return (bn || DH_get0_engine((DH *)dh)) ? Qtrue : Qfalse;
#else
return bn ? Qtrue : Qfalse;
#endif
@@ -223,7 +223,7 @@ ossl_dh_is_private(VALUE self)
static VALUE
ossl_dh_export(VALUE self)
{
- DH *dh;
+ OSSL_3_const DH *dh;
BIO *out;
VALUE str;
@@ -252,7 +252,7 @@ ossl_dh_export(VALUE self)
static VALUE
ossl_dh_to_der(VALUE self)
{
- DH *dh;
+ OSSL_3_const DH *dh;
unsigned char *p;
long len;
VALUE str;
@@ -280,7 +280,7 @@ ossl_dh_to_der(VALUE self)
static VALUE
ossl_dh_get_params(VALUE self)
{
- DH *dh;
+ OSSL_3_const DH *dh;
VALUE hash;
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c
index 25404aa7f5..b097f8c9d2 100644
--- a/ext/openssl/ossl_pkey_dsa.c
+++ b/ext/openssl/ossl_pkey_dsa.c
@@ -24,7 +24,7 @@
} while (0)
static inline int
-DSA_HAS_PRIVATE(DSA *dsa)
+DSA_HAS_PRIVATE(OSSL_3_const DSA *dsa)
{
const BIGNUM *bn;
DSA_get0_key(dsa, NULL, &bn);
@@ -32,7 +32,7 @@ DSA_HAS_PRIVATE(DSA *dsa)
}
static inline int
-DSA_PRIVATE(VALUE obj, DSA *dsa)
+DSA_PRIVATE(VALUE obj, OSSL_3_const DSA *dsa)
{
return DSA_HAS_PRIVATE(dsa) || OSSL_PKEY_IS_PRIVATE(obj);
}
@@ -179,7 +179,7 @@ ossl_dsa_initialize_copy(VALUE self, VALUE other)
static VALUE
ossl_dsa_is_public(VALUE self)
{
- DSA *dsa;
+ const DSA *dsa;
const BIGNUM *bn;
GetDSA(self, dsa);
@@ -198,7 +198,7 @@ ossl_dsa_is_public(VALUE self)
static VALUE
ossl_dsa_is_private(VALUE self)
{
- DSA *dsa;
+ OSSL_3_const DSA *dsa;
GetDSA(self, dsa);
@@ -225,7 +225,7 @@ ossl_dsa_is_private(VALUE self)
static VALUE
ossl_dsa_export(int argc, VALUE *argv, VALUE self)
{
- DSA *dsa;
+ OSSL_3_const DSA *dsa;
GetDSA(self, dsa);
if (DSA_HAS_PRIVATE(dsa))
@@ -244,7 +244,7 @@ ossl_dsa_export(int argc, VALUE *argv, VALUE self)
static VALUE
ossl_dsa_to_der(VALUE self)
{
- DSA *dsa;
+ OSSL_3_const DSA *dsa;
GetDSA(self, dsa);
if (DSA_HAS_PRIVATE(dsa))
@@ -265,7 +265,7 @@ ossl_dsa_to_der(VALUE self)
static VALUE
ossl_dsa_get_params(VALUE self)
{
- DSA *dsa;
+ OSSL_3_const DSA *dsa;
VALUE hash;
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c
index dee215447d..92842f95ac 100644
--- a/ext/openssl/ossl_pkey_ec.c
+++ b/ext/openssl/ossl_pkey_ec.c
@@ -227,7 +227,7 @@ ossl_ec_key_initialize_copy(VALUE self, VALUE other)
static VALUE
ossl_ec_key_get_group(VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
const EC_GROUP *group;
GetEC(self, ec);
@@ -272,7 +272,7 @@ ossl_ec_key_set_group(VALUE self, VALUE group_v)
*/
static VALUE ossl_ec_key_get_private_key(VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
const BIGNUM *bn;
GetEC(self, ec);
@@ -323,7 +323,7 @@ static VALUE ossl_ec_key_set_private_key(VALUE self, VALUE private_key)
*/
static VALUE ossl_ec_key_get_public_key(VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
const EC_POINT *point;
GetEC(self, ec);
@@ -375,7 +375,7 @@ static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key)
*/
static VALUE ossl_ec_key_is_public(VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
GetEC(self, ec);
@@ -391,7 +391,7 @@ static VALUE ossl_ec_key_is_public(VALUE self)
*/
static VALUE ossl_ec_key_is_private(VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
GetEC(self, ec);
@@ -411,9 +411,11 @@ static VALUE ossl_ec_key_is_private(VALUE self)
static VALUE
ossl_ec_key_export(int argc, VALUE *argv, VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
GetEC(self, ec);
+ if (EC_KEY_get0_public_key(ec) == NULL)
+ ossl_raise(eECError, "can't export - no public key set");
if (EC_KEY_get0_private_key(ec))
return ossl_pkey_export_traditional(argc, argv, self, 0);
else
@@ -429,9 +431,11 @@ ossl_ec_key_export(int argc, VALUE *argv, VALUE self)
static VALUE
ossl_ec_key_to_der(VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
GetEC(self, ec);
+ if (EC_KEY_get0_public_key(ec) == NULL)
+ ossl_raise(eECError, "can't export - no public key set");
if (EC_KEY_get0_private_key(ec))
return ossl_pkey_export_traditional(0, NULL, self, 1);
else
@@ -479,16 +483,28 @@ static VALUE ossl_ec_key_check_key(VALUE self)
#ifdef HAVE_EVP_PKEY_CHECK
EVP_PKEY *pkey;
EVP_PKEY_CTX *pctx;
- int ret;
+ const EC_KEY *ec;
GetPKey(self, pkey);
+ GetEC(self, ec);
pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
if (!pctx)
- ossl_raise(eDHError, "EVP_PKEY_CTX_new");
- ret = EVP_PKEY_public_check(pctx);
+ ossl_raise(eECError, "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");
+ }
+ }
+ else {
+ if (EVP_PKEY_public_check(pctx) != 1) {
+ EVP_PKEY_CTX_free(pctx);
+ ossl_raise(eECError, "EVP_PKEY_public_check");
+ }
+ }
+
EVP_PKEY_CTX_free(pctx);
- if (ret != 1)
- ossl_raise(eECError, "EVP_PKEY_public_check");
#else
EC_KEY *ec;
@@ -664,10 +680,11 @@ static VALUE ossl_ec_group_eql(VALUE a, VALUE b)
GetECGroup(a, group1);
GetECGroup(b, group2);
- if (EC_GROUP_cmp(group1, group2, ossl_bn_ctx) == 1)
- return Qfalse;
-
- return Qtrue;
+ 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");
+ }
}
/*
@@ -1228,10 +1245,13 @@ static VALUE ossl_ec_point_eql(VALUE a, VALUE b)
GetECPoint(b, point2);
GetECGroup(group_v1, group);
- if (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx) == 1)
- return Qfalse;
+ 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");
+ }
- return Qtrue;
+ UNREACHABLE;
}
/*
@@ -1249,7 +1269,7 @@ static VALUE ossl_ec_point_is_at_infinity(VALUE self)
switch (EC_POINT_is_at_infinity(group, point)) {
case 1: return Qtrue;
case 0: return Qfalse;
- default: ossl_raise(cEC_POINT, "EC_POINT_is_at_infinity");
+ default: ossl_raise(eEC_POINT, "EC_POINT_is_at_infinity");
}
UNREACHABLE;
@@ -1270,7 +1290,7 @@ static VALUE ossl_ec_point_is_on_curve(VALUE self)
switch (EC_POINT_is_on_curve(group, point, ossl_bn_ctx)) {
case 1: return Qtrue;
case 0: return Qfalse;
- default: ossl_raise(cEC_POINT, "EC_POINT_is_on_curve");
+ default: ossl_raise(eEC_POINT, "EC_POINT_is_on_curve");
}
UNREACHABLE;
@@ -1293,7 +1313,7 @@ static VALUE ossl_ec_point_make_affine(VALUE self)
rb_warn("OpenSSL::PKey::EC::Point#make_affine! is deprecated");
#if !OSSL_OPENSSL_PREREQ(3, 0, 0)
if (EC_POINT_make_affine(group, point, ossl_bn_ctx) != 1)
- ossl_raise(cEC_POINT, "EC_POINT_make_affine");
+ ossl_raise(eEC_POINT, "EC_POINT_make_affine");
#endif
return self;
@@ -1312,7 +1332,7 @@ static VALUE ossl_ec_point_invert(VALUE self)
GetECPointGroup(self, group);
if (EC_POINT_invert(group, point, ossl_bn_ctx) != 1)
- ossl_raise(cEC_POINT, "EC_POINT_invert");
+ ossl_raise(eEC_POINT, "EC_POINT_invert");
return self;
}
@@ -1330,7 +1350,7 @@ static VALUE ossl_ec_point_set_to_infinity(VALUE self)
GetECPointGroup(self, group);
if (EC_POINT_set_to_infinity(group, point) != 1)
- ossl_raise(cEC_POINT, "EC_POINT_set_to_infinity");
+ ossl_raise(eEC_POINT, "EC_POINT_set_to_infinity");
return self;
}
diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c
index 4d66010f49..072adabe62 100644
--- a/ext/openssl/ossl_pkey_rsa.c
+++ b/ext/openssl/ossl_pkey_rsa.c
@@ -24,7 +24,7 @@
} while (0)
static inline int
-RSA_HAS_PRIVATE(RSA *rsa)
+RSA_HAS_PRIVATE(OSSL_3_const RSA *rsa)
{
const BIGNUM *e, *d;
@@ -33,7 +33,7 @@ RSA_HAS_PRIVATE(RSA *rsa)
}
static inline int
-RSA_PRIVATE(VALUE obj, RSA *rsa)
+RSA_PRIVATE(VALUE obj, OSSL_3_const RSA *rsa)
{
return RSA_HAS_PRIVATE(rsa) || OSSL_PKEY_IS_PRIVATE(obj);
}
@@ -174,7 +174,7 @@ ossl_rsa_initialize_copy(VALUE self, VALUE other)
static VALUE
ossl_rsa_is_public(VALUE self)
{
- RSA *rsa;
+ OSSL_3_const RSA *rsa;
GetRSA(self, rsa);
/*
@@ -193,7 +193,7 @@ ossl_rsa_is_public(VALUE self)
static VALUE
ossl_rsa_is_private(VALUE self)
{
- RSA *rsa;
+ OSSL_3_const RSA *rsa;
GetRSA(self, rsa);
@@ -203,7 +203,7 @@ ossl_rsa_is_private(VALUE self)
static int
can_export_rsaprivatekey(VALUE self)
{
- RSA *rsa;
+ OSSL_3_const RSA *rsa;
const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
GetRSA(self, rsa);
@@ -453,7 +453,7 @@ ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self)
static VALUE
ossl_rsa_get_params(VALUE self)
{
- RSA *rsa;
+ OSSL_3_const RSA *rsa;
VALUE hash;
const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index af262d9f56..f63992664a 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -11,11 +11,15 @@
*/
#include "ossl.h"
+#ifndef OPENSSL_NO_SOCK
#define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0]))
+#if !defined(OPENSSL_NO_NEXTPROTONEG) && !OSSL_IS_LIBRESSL
+# define OSSL_USE_NEXTPROTONEG
+#endif
+
#if !defined(TLS1_3_VERSION) && \
- defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER >= 0x3020000fL
+ OSSL_LIBRESSL_PREREQ(3, 2, 0) && !OSSL_LIBRESSL_PREREQ(3, 4, 0)
# define TLS1_3_VERSION 0x0304
#endif
@@ -30,7 +34,6 @@
} while (0)
VALUE mSSL;
-static VALUE mSSLExtConfig;
static VALUE eSSLError;
VALUE cSSLContext;
VALUE cSSLSocket;
@@ -49,7 +52,7 @@ static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
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_verify_hostname, id_i_keylog_cb;
static ID id_i_io, id_i_context, id_i_hostname;
static int ossl_ssl_ex_vcb_idx;
@@ -291,7 +294,7 @@ ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength)
if (!pkey)
return NULL;
- return EVP_PKEY_get0_DH(pkey);
+ return (DH *)EVP_PKEY_get0_DH(pkey);
}
#endif /* OPENSSL_NO_DH */
@@ -441,6 +444,54 @@ ossl_sslctx_session_new_cb(SSL *ssl, SSL_SESSION *sess)
return 0;
}
+#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
+/*
+ * 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
+ * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
+ */
+
+struct ossl_call_keylog_cb_args {
+ VALUE ssl_obj;
+ const char * line;
+};
+
+static VALUE
+ossl_call_keylog_cb(VALUE args_v)
+{
+ VALUE sslctx_obj, cb, line_v;
+ struct ossl_call_keylog_cb_args *args = (struct ossl_call_keylog_cb_args *) args_v;
+
+ sslctx_obj = rb_attr_get(args->ssl_obj, id_i_context);
+
+ cb = rb_attr_get(sslctx_obj, id_i_keylog_cb);
+ if (NIL_P(cb)) return Qnil;
+
+ line_v = rb_str_new_cstr(args->line);
+
+ return rb_funcall(cb, id_call, 2, args->ssl_obj, line_v);
+}
+
+static void
+ossl_sslctx_keylog_cb(const SSL *ssl, const char *line)
+{
+ VALUE ssl_obj;
+ struct ossl_call_keylog_cb_args args;
+ int state = 0;
+
+ OSSL_Debug("SSL keylog callback entered");
+
+ ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
+ args.ssl_obj = ssl_obj;
+ args.line = line;
+
+ rb_protect(ossl_call_keylog_cb, (VALUE)&args, &state);
+ if (state) {
+ rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state));
+ }
+}
+#endif
+
static VALUE
ossl_call_session_remove_cb(VALUE ary)
{
@@ -655,7 +706,7 @@ ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out,
return SSL_TLSEXT_ERR_OK;
}
-#ifndef OPENSSL_NO_NEXTPROTONEG
+#ifdef OSSL_USE_NEXTPROTONEG
static int
ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen,
void *arg)
@@ -852,7 +903,7 @@ ossl_sslctx_setup(VALUE self)
val = rb_attr_get(self, id_i_verify_depth);
if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2INT(val));
-#ifndef OPENSSL_NO_NEXTPROTONEG
+#ifdef OSSL_USE_NEXTPROTONEG
val = rb_attr_get(self, id_i_npn_protocols);
if (!NIL_P(val)) {
VALUE encoded = ssl_encode_npn_protocols(val);
@@ -911,6 +962,18 @@ ossl_sslctx_setup(VALUE self)
OSSL_Debug("SSL TLSEXT servername callback added");
}
+#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
+ /*
+ * 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
+ * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
+ */
+ if (RTEST(rb_attr_get(self, id_i_keylog_cb))) {
+ SSL_CTX_set_keylog_callback(ctx, ossl_sslctx_keylog_cb);
+ OSSL_Debug("SSL keylog callback added");
+ }
+#endif
+
return Qtrue;
}
@@ -1478,7 +1541,6 @@ ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE self)
/*
* SSLSocket class
*/
-#ifndef OPENSSL_NO_SOCK
static inline int
ssl_started(SSL *ssl)
{
@@ -1641,11 +1703,16 @@ no_exception_p(VALUE opts)
return 0;
}
+// Provided by Ruby 3.2.0 and later in order to support the default IO#timeout.
+#ifndef RUBY_IO_TIMEOUT_DEFAULT
+#define RUBY_IO_TIMEOUT_DEFAULT Qnil
+#endif
+
static void
io_wait_writable(rb_io_t *fptr)
{
#ifdef HAVE_RB_IO_MAYBE_WAIT
- rb_io_maybe_wait_writable(errno, fptr->self, Qnil);
+ rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT);
#else
rb_io_wait_writable(fptr->fd);
#endif
@@ -1655,14 +1722,14 @@ static void
io_wait_readable(rb_io_t *fptr)
{
#ifdef HAVE_RB_IO_MAYBE_WAIT
- rb_io_maybe_wait_readable(errno, fptr->self, Qnil);
+ rb_io_maybe_wait_readable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT);
#else
rb_io_wait_readable(fptr->fd);
#endif
}
static VALUE
-ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts)
+ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts)
{
SSL *ssl;
rb_io_t *fptr;
@@ -2381,7 +2448,7 @@ ossl_ssl_get_client_ca_list(VALUE self)
return ossl_x509name_sk2ary(ca);
}
-# ifndef OPENSSL_NO_NEXTPROTONEG
+# ifdef OSSL_USE_NEXTPROTONEG
/*
* call-seq:
* ssl.npn_protocol => String | nil
@@ -2431,6 +2498,49 @@ ossl_ssl_alpn_protocol(VALUE self)
/*
* call-seq:
+ * session.export_keying_material(label, length) -> String
+ *
+ * Enables use of shared session key material in accordance with RFC 5705.
+ */
+static VALUE
+ossl_ssl_export_keying_material(int argc, VALUE *argv, VALUE self)
+{
+ SSL *ssl;
+ VALUE str;
+ VALUE label;
+ VALUE length;
+ VALUE context;
+ unsigned char *p;
+ size_t len;
+ int use_ctx = 0;
+ unsigned char *ctx = NULL;
+ size_t ctx_len = 0;
+ int ret;
+
+ rb_scan_args(argc, argv, "21", &label, &length, &context);
+ StringValue(label);
+
+ GetSSL(self, ssl);
+
+ len = (size_t)NUM2LONG(length);
+ 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);
+ }
+ ret = SSL_export_keying_material(ssl, p, len, (char *)RSTRING_PTR(label),
+ RSTRING_LENINT(label), ctx, ctx_len, use_ctx);
+ if (ret == 0 || ret == -1) {
+ ossl_raise(eSSLError, "SSL_export_keying_material");
+ }
+ return str;
+}
+
+/*
+ * call-seq:
* ssl.tmp_key => PKey or nil
*
* Returns the ephemeral key used in case of forward secrecy cipher.
@@ -2458,6 +2568,7 @@ Init_ossl_ssl(void)
rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable");
#endif
+#ifndef OPENSSL_NO_SOCK
id_call = rb_intern_const("call");
ID_callback_state = rb_intern_const("callback_state");
@@ -2480,16 +2591,6 @@ Init_ossl_ssl(void)
*/
mSSL = rb_define_module_under(mOSSL, "SSL");
- /* Document-module: OpenSSL::ExtConfig
- *
- * This module contains configuration information about the SSL extension,
- * for example if socket support is enabled, or the host name TLS extension
- * is enabled. Constants in this module will always be defined, but contain
- * +true+ or +false+ values depending on the configuration of your OpenSSL
- * installation.
- */
- mSSLExtConfig = rb_define_module_under(mOSSL, "ExtConfig");
-
/* Document-class: OpenSSL::SSL::SSLError
*
* Generic error class raised by SSLSocket and SSLContext.
@@ -2652,8 +2753,6 @@ Init_ossl_ssl(void)
*/
rb_attr(cSSLContext, rb_intern_const("session_remove_cb"), 1, 1, Qfalse);
- rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qtrue);
-
/*
* A callback invoked whenever a new handshake is initiated on an
* established connection. May be used to disable renegotiation entirely.
@@ -2674,7 +2773,7 @@ Init_ossl_ssl(void)
* end
*/
rb_attr(cSSLContext, rb_intern_const("renegotiation_cb"), 1, 1, Qfalse);
-#ifndef OPENSSL_NO_NEXTPROTONEG
+#ifdef OSSL_USE_NEXTPROTONEG
/*
* An Enumerable of Strings. Each String represents a protocol to be
* advertised as the list of supported protocols for Next Protocol
@@ -2736,6 +2835,29 @@ Init_ossl_ssl(void)
*/
rb_attr(cSSLContext, rb_intern_const("alpn_select_cb"), 1, 1, Qfalse);
+ /*
+ * A callback invoked when TLS key material is generated or received, in
+ * order to allow applications to store this keying material for debugging
+ * purposes.
+ *
+ * The callback is invoked with an SSLSocket and a string containing the
+ * key material in the format used by NSS for its SSLKEYLOGFILE debugging
+ * output.
+ *
+ * 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
+ * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
+ *
+ * === Example
+ *
+ * context.keylog_cb = proc do |_sock, line|
+ * File.open('ssl_keylog_file', "a") do |f|
+ * f.write("#{line}\n")
+ * end
+ * end
+ */
+ rb_attr(cSSLContext, rb_intern_const("keylog_cb"), 1, 1, Qfalse);
+
rb_define_alias(cSSLContext, "ssl_timeout", "timeout");
rb_define_alias(cSSLContext, "ssl_timeout=", "timeout=");
rb_define_private_method(cSSLContext, "set_minmax_proto_version",
@@ -2821,11 +2943,6 @@ Init_ossl_ssl(void)
* Document-class: OpenSSL::SSL::SSLSocket
*/
cSSLSocket = rb_define_class_under(mSSL, "SSLSocket", rb_cObject);
-#ifdef OPENSSL_NO_SOCK
- rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qtrue);
- rb_define_method(cSSLSocket, "initialize", rb_f_notimplement, -1);
-#else
- rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qfalse);
rb_define_alloc_func(cSSLSocket, ossl_ssl_s_alloc);
rb_define_method(cSSLSocket, "initialize", ossl_ssl_initialize, -1);
rb_undef_method(cSSLSocket, "initialize_copy");
@@ -2856,10 +2973,10 @@ Init_ossl_ssl(void)
rb_define_method(cSSLSocket, "peer_finished_message", ossl_ssl_get_peer_finished, 0);
rb_define_method(cSSLSocket, "tmp_key", ossl_ssl_tmp_key, 0);
rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0);
-# ifndef OPENSSL_NO_NEXTPROTONEG
+ rb_define_method(cSSLSocket, "export_keying_material", ossl_ssl_export_keying_material, -1);
+# ifdef OSSL_USE_NEXTPROTONEG
rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0);
# endif
-#endif
rb_define_const(mSSL, "VERIFY_NONE", INT2NUM(SSL_VERIFY_NONE));
rb_define_const(mSSL, "VERIFY_PEER", INT2NUM(SSL_VERIFY_PEER));
@@ -3016,8 +3133,10 @@ Init_ossl_ssl(void)
DefIVarID(alpn_select_cb);
DefIVarID(servername_cb);
DefIVarID(verify_hostname);
+ DefIVarID(keylog_cb);
DefIVarID(io);
DefIVarID(context);
DefIVarID(hostname);
+#endif /* !defined(OPENSSL_NO_SOCK) */
}
diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c
index 92eb1365fe..139a474b04 100644
--- a/ext/openssl/ossl_ssl_session.c
+++ b/ext/openssl/ossl_ssl_session.c
@@ -4,6 +4,7 @@
#include "ossl.h"
+#ifndef OPENSSL_NO_SOCK
VALUE cSSLSession;
static VALUE eSSLSession;
@@ -299,6 +300,7 @@ static VALUE ossl_ssl_session_to_text(VALUE self)
return ossl_membio2str(out);
}
+#endif /* !defined(OPENSSL_NO_SOCK) */
void Init_ossl_ssl_session(void)
{
@@ -307,6 +309,7 @@ void Init_ossl_ssl_session(void)
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);
@@ -324,4 +327,5 @@ void Init_ossl_ssl_session(void)
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_x509cert.c b/ext/openssl/ossl_x509cert.c
index 996f184170..9443541645 100644
--- a/ext/openssl/ossl_x509cert.c
+++ b/ext/openssl/ossl_x509cert.c
@@ -642,12 +642,12 @@ ossl_x509_set_extensions(VALUE self, VALUE ary)
OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext);
}
GetX509(self, x509);
- while ((ext = X509_delete_ext(x509, 0)))
- X509_EXTENSION_free(ext);
+ 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, NULL);
+ ossl_raise(eX509CertError, "X509_add_ext");
}
}
diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c
index 863f0286c0..6c1d915370 100644
--- a/ext/openssl/ossl_x509crl.c
+++ b/ext/openssl/ossl_x509crl.c
@@ -474,12 +474,12 @@ ossl_x509crl_set_extensions(VALUE self, VALUE ary)
OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext);
}
GetX509CRL(self, crl);
- while ((ext = X509_CRL_delete_ext(crl, 0)))
- X509_EXTENSION_free(ext);
+ 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, NULL);
+ ossl_raise(eX509CRLError, "X509_CRL_add_ext");
}
}
diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c
index 6eb91e9c2f..77a7d3f2ff 100644
--- a/ext/openssl/ossl_x509req.c
+++ b/ext/openssl/ossl_x509req.c
@@ -380,13 +380,13 @@ ossl_x509req_set_attributes(VALUE self, VALUE ary)
OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Attr);
}
GetX509Req(self, req);
- while ((attr = X509_REQ_delete_attr(req, 0)))
- X509_ATTRIBUTE_free(attr);
+ 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, NULL);
+ ossl_raise(eX509ReqError, "X509_REQ_add1_attr");
}
}
return ary;
diff --git a/ext/openssl/ossl_x509revoked.c b/ext/openssl/ossl_x509revoked.c
index 5fe6853430..10b8aa4ad6 100644
--- a/ext/openssl/ossl_x509revoked.c
+++ b/ext/openssl/ossl_x509revoked.c
@@ -223,13 +223,13 @@ ossl_x509revoked_set_extensions(VALUE self, VALUE ary)
OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext);
}
GetX509Rev(self, rev);
- while ((ext = X509_REVOKED_delete_ext(rev, 0)))
- X509_EXTENSION_free(ext);
+ 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, NULL);
+ ossl_raise(eX509RevError, "X509_REVOKED_add_ext");
}
}
diff --git a/ext/pathname/lib/pathname.rb b/ext/pathname/lib/pathname.rb
index 41e5c171a7..7bdfd0eb39 100644
--- a/ext/pathname/lib/pathname.rb
+++ b/ext/pathname/lib/pathname.rb
@@ -338,6 +338,8 @@ class Pathname
#
# 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
@@ -399,6 +401,8 @@ class Pathname
#
# 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
@@ -574,9 +578,9 @@ class Pathname # * Find *
end
-class Pathname # * FileUtils *
- autoload(:FileUtils, 'fileutils')
+autoload(:FileUtils, 'fileutils')
+class Pathname # * FileUtils *
# Creates a full path, including any intermediate directories that don't yet
# exist.
#
diff --git a/ext/pathname/pathname.c b/ext/pathname/pathname.c
index e2c3c36dbf..8ee4bcec5b 100644
--- a/ext/pathname/pathname.c
+++ b/ext/pathname/pathname.c
@@ -1226,7 +1226,7 @@ path_entries(VALUE 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);
+ VALUE elt = RARRAY_AREF(ary, i);
elt = rb_class_new_instance(1, &elt, klass);
rb_ary_store(ary, i, elt);
}
diff --git a/ext/pathname/pathname.gemspec b/ext/pathname/pathname.gemspec
index c9c0b84e69..92bc02b0db 100644
--- a/ext/pathname/pathname.gemspec
+++ b/ext/pathname/pathname.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "pathname"
- spec.version = "0.2.0"
+ spec.version = "0.2.1"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/ext/psych/extconf.rb b/ext/psych/extconf.rb
index 6d03870436..41daf8c238 100644
--- a/ext/psych/extconf.rb
+++ b/ext/psych/extconf.rb
@@ -6,39 +6,9 @@ if $mswin or $mingw or $cygwin
$CPPFLAGS << " -DYAML_DECLARE_STATIC"
end
-yaml_source = with_config("libyaml-source-dir") || enable_config("bundled-libyaml", false)
-unless yaml_source # default to pre-installed libyaml
- pkg_config('yaml-0.1')
- dir_config('libyaml')
- unless find_header('yaml.h') && find_library('yaml', 'yaml_get_version')
- yaml_source = true # fallback to the bundled source if exists
- end
-end
-
-if yaml_source == true
- # search the latest libyaml source under $srcdir
- yaml_source = Dir.glob("#{$srcdir}/yaml{,-*}/").max_by {|n| File.basename(n).scan(/\d+/).map(&:to_i)}
- unless yaml_source
- download_failure = "failed to download libyaml source. Try manually installing libyaml?"
- begin
- require_relative '../../tool/extlibs.rb'
- rescue LoadError
- # When running in ruby/ruby, we use miniruby and don't have stdlib.
- # Avoid LoadError because it aborts the whole build. Usually when
- # stdlib extension fail to configure we skip it and continue.
- raise download_failure
- end
- extlibs = ExtLibs.new(cache_dir: File.expand_path("../../tmp/download_cache", $srcdir))
- unless extlibs.process_under($srcdir)
- raise download_failure
- end
- yaml_source, = Dir.glob("#{$srcdir}/yaml-*/")
- raise "libyaml not found" unless yaml_source
- end
-elsif yaml_source
- yaml_source = yaml_source.gsub(/\$\((\w+)\)|\$\{(\w+)\}/) {ENV[$1||$2]}
-end
+yaml_source = with_config("libyaml-source-dir")
if yaml_source
+ yaml_source = yaml_source.gsub(/\$\((\w+)\)|\$\{(\w+)\}/) {ENV[$1||$2]}
yaml_source = yaml_source.chomp("/")
yaml_configure = "#{File.expand_path(yaml_source)}/configure"
unless File.exist?(yaml_configure)
@@ -66,6 +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')
+ dir_config('libyaml')
+ find_header('yaml.h') or abort "yaml.h not found"
+ find_library('yaml', 'yaml_get_version') or abort "libyaml not found"
end
create_makefile 'psych' do |mk|
diff --git a/ext/psych/extlibs b/ext/psych/extlibs
deleted file mode 100644
index 108aad42af..0000000000
--- a/ext/psych/extlibs
+++ /dev/null
@@ -1,11 +0,0 @@
-ver = 0.2.5
-pkg = yaml-$(ver)
-
-https://github.com/yaml/libyaml/releases/download/$(ver)/$(pkg).tar.gz \
- rmd160:cc175ed640046722fb7790de828002633407b6b9 \
- sha256:c642ae9b75fee120b2d96c712538bd2cf283228d2337df2cf2988e3c02678ef4 \
- sha512:dadd7d8e0d88b5ebab005e5d521d56d541580198aa497370966b98c904586e642a1cd4f3881094eb57624f218d50db77417bbfd0ffdce50340f011e35e8c4c02 \
- #
-
-$(pkg)/config/config.guess -> /tool/config.guess
-$(pkg)/config/config.sub -> /tool/config.sub
diff --git a/ext/psych/lib/psych.rb b/ext/psych/lib/psych.rb
index 42d79efb83..4a2ab58514 100644
--- a/ext/psych/lib/psych.rb
+++ b/ext/psych/lib/psych.rb
@@ -307,7 +307,7 @@ module Psych
# A Psych::DisallowedClass exception will be raised if the yaml contains a
# class that isn't in the +permitted_classes+ list.
#
- # A Psych::BadAlias exception will be raised if the yaml contains aliases
+ # A Psych::AliasesNotEnabled exception will be raised if the yaml contains aliases
# but the +aliases+ keyword argument is set to false.
#
# +filename+ will be used in the exception message if any exception is raised
diff --git a/ext/psych/lib/psych/exception.rb b/ext/psych/lib/psych/exception.rb
index f473b95a3b..d7469a4b30 100644
--- a/ext/psych/lib/psych/exception.rb
+++ b/ext/psych/lib/psych/exception.rb
@@ -6,6 +6,20 @@ module Psych
class BadAlias < Exception
end
+ # Subclasses `BadAlias` for backwards compatibility
+ class AliasesNotEnabled < BadAlias
+ def initialize
+ super "Alias parsing was not enabled. To enable it, pass `aliases: true` to `Psych::load` or `Psych::safe_load`."
+ end
+ end
+
+ # Subclasses `BadAlias` for backwards compatibility
+ class AnchorNotDefined < BadAlias
+ def initialize anchor_name
+ super "An alias referenced an unknown anchor: #{anchor_name}"
+ end
+ end
+
class DisallowedClass < Exception
def initialize action, klass_name
super "Tried to #{action} unspecified class: #{klass_name}"
diff --git a/ext/psych/lib/psych/parser.rb b/ext/psych/lib/psych/parser.rb
index 39bc8289be..2181c730e5 100644
--- a/ext/psych/lib/psych/parser.rb
+++ b/ext/psych/lib/psych/parser.rb
@@ -48,5 +48,18 @@ module Psych
@handler = handler
@external_encoding = ANY
end
+
+ ###
+ # call-seq:
+ # parser.parse(yaml)
+ #
+ # Parse the YAML document contained in +yaml+. Events will be called on
+ # the handler set on the parser instance.
+ #
+ # See Psych::Parser and Psych::Parser#handler
+
+ def parse yaml, path = yaml.respond_to?(:path) ? yaml.path : "<unknown>"
+ _native_parse @handler, yaml, path
+ end
end
end
diff --git a/ext/psych/lib/psych/scalar_scanner.rb b/ext/psych/lib/psych/scalar_scanner.rb
index b50667c315..3cb4bf3c7e 100644
--- a/ext/psych/lib/psych/scalar_scanner.rb
+++ b/ext/psych/lib/psych/scalar_scanner.rb
@@ -63,7 +63,7 @@ module Psych
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, '%Y-%m-%d')
+ class_loader.date.strptime(string, '%F', Date::GREGORIAN)
rescue ArgumentError
string
end
diff --git a/ext/psych/lib/psych/versions.rb b/ext/psych/lib/psych/versions.rb
index 0fdead154c..a592a6916c 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.0.0.dev'
+ VERSION = '5.0.1'
if RUBY_ENGINE == 'jruby'
- DEFAULT_SNAKEYAML_VERSION = '1.28'.freeze
+ DEFAULT_SNAKEYAML_VERSION = '1.33'.freeze
end
end
diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb
index 935bc74f21..8614251ca9 100644
--- a/ext/psych/lib/psych/visitors/to_ruby.rb
+++ b/ext/psych/lib/psych/visitors/to_ruby.rb
@@ -80,7 +80,9 @@ module Psych
when "!ruby/object:DateTime"
class_loader.date_time
require 'date' unless defined? DateTime
- @ss.parse_time(o.value).to_datetime
+ t = @ss.parse_time(o.value)
+ DateTime.civil(*t.to_a[0, 6].reverse, Rational(t.utc_offset, 86400)) +
+ (t.subsec/86400)
when '!ruby/encoding'
::Encoding.find o.value
when "!ruby/object:Complex"
@@ -323,7 +325,7 @@ module Psych
end
def visit_Psych_Nodes_Alias o
- @st.fetch(o.anchor) { raise BadAlias, "Unknown alias: #{o.anchor}" }
+ @st.fetch(o.anchor) { raise AnchorNotDefined, o.anchor }
end
private
@@ -427,7 +429,7 @@ module Psych
class NoAliasRuby < ToRuby
def visit_Psych_Nodes_Alias o
- raise BadAlias, "Unknown alias: #{o.anchor}"
+ raise AliasesNotEnabled
end
end
end
diff --git a/ext/psych/lib/psych/visitors/yaml_tree.rb b/ext/psych/lib/psych/visitors/yaml_tree.rb
index 316a3a9496..31858798e4 100644
--- a/ext/psych/lib/psych/visitors/yaml_tree.rb
+++ b/ext/psych/lib/psych/visitors/yaml_tree.rb
@@ -192,12 +192,13 @@ module Psych
register o, @emitter.scalar(o.inspect, nil, '!ruby/regexp', false, false, Nodes::Scalar::ANY)
end
+ def visit_Date o
+ register o, visit_Integer(o.gregorian)
+ end
+
def visit_DateTime o
- formatted = if o.offset.zero?
- o.strftime("%Y-%m-%d %H:%M:%S.%9N Z".freeze)
- else
- o.strftime("%Y-%m-%d %H:%M:%S.%9N %:z".freeze)
- end
+ t = o.italy
+ formatted = format_time t, t.offset.zero?
tag = '!ruby/object:DateTime'
register o, @emitter.scalar(formatted, nil, tag, false, false, Nodes::Scalar::ANY)
end
@@ -235,7 +236,6 @@ module Psych
end
alias :visit_TrueClass :visit_Integer
alias :visit_FalseClass :visit_Integer
- alias :visit_Date :visit_Integer
def visit_Float o
if o.nan?
@@ -482,8 +482,8 @@ module Psych
@emitter.end_mapping
end
- def format_time time
- if time.utc?
+ def format_time time, utc = time.utc?
+ if utc
time.strftime("%Y-%m-%d %H:%M:%S.%9N Z")
else
time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z")
diff --git a/ext/psych/psych_parser.c b/ext/psych/psych_parser.c
index f91475b835..9c5179cc44 100644
--- a/ext/psych/psych_parser.c
+++ b/ext/psych/psych_parser.c
@@ -245,18 +245,8 @@ static VALUE protected_event_location(VALUE pointer)
return rb_funcall3(args[0], id_event_location, 4, args + 1);
}
-/*
- * call-seq:
- * parser.parse(yaml)
- *
- * Parse the YAML document contained in +yaml+. Events will be called on
- * the handler set on the parser instance.
- *
- * See Psych::Parser and Psych::Parser#handler
- */
-static VALUE parse(int argc, VALUE *argv, VALUE self)
+static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path)
{
- VALUE yaml, path;
yaml_parser_t * parser;
yaml_event_t event;
int done = 0;
@@ -264,14 +254,6 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
int parser_encoding = YAML_ANY_ENCODING;
int encoding = rb_utf8_encindex();
rb_encoding * internal_enc = rb_default_internal_encoding();
- VALUE handler = rb_iv_get(self, "@handler");
-
- if (rb_scan_args(argc, argv, "11", &yaml, &path) == 1) {
- if(rb_respond_to(yaml, id_path))
- path = rb_funcall(yaml, id_path, 0);
- else
- path = rb_str_new2("<unknown>");
- }
TypedData_Get_Struct(self, yaml_parser_t, &psych_parser_type, parser);
@@ -562,7 +544,7 @@ void Init_psych_parser(void)
rb_require("psych/syntax_error");
- rb_define_method(cPsychParser, "parse", parse, -1);
+ rb_define_private_method(cPsychParser, "_native_parse", parse, 3);
rb_define_method(cPsychParser, "mark", mark, 0);
id_read = rb_intern("read");
diff --git a/ext/pty/depend b/ext/pty/depend
index c43d3dcf9a..f251caae3f 100644
--- a/ext/pty/depend
+++ b/ext/pty/depend
@@ -181,5 +181,6 @@ pty.o: $(top_srcdir)/internal/process.h
pty.o: $(top_srcdir)/internal/signal.h
pty.o: $(top_srcdir)/internal/static_assert.h
pty.o: $(top_srcdir)/internal/warnings.h
+pty.o: $(top_srcdir)/shape.h
pty.o: pty.c
# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/pty/extconf.rb b/ext/pty/extconf.rb
index 038bdf4d2c..ba0c4286fd 100644
--- a/ext/pty/extconf.rb
+++ b/ext/pty/extconf.rb
@@ -7,10 +7,12 @@ if /mswin|mingw|bccwin/ !~ RUBY_PLATFORM
have_header("sys/stropts.h")
have_func("setresuid")
have_header("libutil.h")
- have_header("util.h") # OpenBSD openpty
have_header("pty.h")
have_header("pwd.h")
- util = have_library("util", "openpty")
+ if /openbsd/ =~ RUBY_PLATFORM
+ have_header("util.h") # OpenBSD openpty
+ util = have_library("util", "openpty")
+ end
if have_func("posix_openpt") or
(util or have_func("openpty")) or
have_func("_getpty") or
diff --git a/ext/pty/lib/expect.rb b/ext/pty/lib/expect.rb
index 5dbfa09ae9..22cbf54115 100644
--- a/ext/pty/lib/expect.rb
+++ b/ext/pty/lib/expect.rb
@@ -1,19 +1,19 @@
# frozen_string_literal: true
$expect_verbose = false
-# Expect library adds the IO instance method #expect, which does similar act to
-# tcl's expect extension.
-#
-# In order to use this method, you must require expect:
-#
-# require 'expect'
-#
-# Please see #expect for usage.
class IO
# call-seq:
# IO#expect(pattern,timeout=9999999) -> Array
# IO#expect(pattern,timeout=9999999) { |result| ... } -> nil
#
+ # The +expect+ library adds instance method IO#expect,
+ # which is similar to the
+ # {TCL expect extension}[https://www.tcl.tk/man/expect5.31/expect.1.html].
+ #
+ # To use this method, you must require +expect+:
+ #
+ # require 'expect'
+ #
# Reads from the IO until the given +pattern+ matches or the +timeout+ is over.
#
# It returns an array with the read buffer, followed by the matches.
diff --git a/ext/pty/pty.c b/ext/pty/pty.c
index c85f1dcae3..acec33f9bf 100644
--- a/ext/pty/pty.c
+++ b/ext/pty/pty.c
@@ -107,8 +107,8 @@ chfunc(void *data, char *errbuf, size_t errbuf_len)
int slave = carg->slave;
#define ERROR_EXIT(str) do { \
- strlcpy(errbuf, (str), errbuf_len); \
- return -1; \
+ strlcpy(errbuf, (str), errbuf_len); \
+ return -1; \
} while (0)
/*
@@ -166,32 +166,32 @@ chfunc(void *data, char *errbuf, size_t errbuf_len)
static void
establishShell(int argc, VALUE *argv, struct pty_info *info,
- char SlaveName[DEVICELEN])
+ char SlaveName[DEVICELEN])
{
int master, slave, status = 0;
rb_pid_t pid;
- char *p, *getenv();
+ char *p;
VALUE v;
struct child_info carg;
char errbuf[32];
if (argc == 0) {
- const char *shellname = "/bin/sh";
+ const char *shellname = "/bin/sh";
- if ((p = getenv("SHELL")) != NULL) {
- shellname = p;
- }
- else {
+ if ((p = getenv("SHELL")) != NULL) {
+ shellname = p;
+ }
+ 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;
+ const char *username = getenv("USER");
+ struct passwd *pwent = getpwnam(username ? username : getlogin());
+ if (pwent && pwent->pw_shell)
+ shellname = pwent->pw_shell;
#endif
- }
- v = rb_str_new2(shellname);
- argc = 1;
- argv = &v;
+ }
+ v = rb_str_new2(shellname);
+ argc = 1;
+ argv = &v;
}
carg.execarg_obj = rb_execarg_new(argc, argv, 1, 0);
@@ -207,13 +207,13 @@ establishShell(int argc, VALUE *argv, struct pty_info *info,
pid = rb_fork_async_signal_safe(&status, chfunc, &carg, Qnil, errbuf, sizeof(errbuf));
if (pid < 0) {
- int e = errno;
- close(master);
- close(slave);
+ int e = errno;
+ close(master);
+ close(slave);
rb_execarg_parent_end(carg.execarg_obj);
- errno = e;
- if (status) rb_jump_tag(status);
- rb_sys_fail(errbuf[0] ? errbuf : "fork failed");
+ errno = e;
+ if (status) rb_jump_tag(status);
+ rb_sys_fail(errbuf[0] ? errbuf : "fork failed");
}
close(slave);
@@ -268,14 +268,14 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
rb_fd_fix_cloexec(masterfd);
#else
{
- int flags = O_RDWR|O_NOCTTY;
+ int flags = O_RDWR|O_NOCTTY;
# if defined(O_CLOEXEC)
- /* glibc posix_openpt() in GNU/Linux calls open("/dev/ptmx", flags) internally.
- * So version dependency on GNU/Linux is the same as O_CLOEXEC with open().
- * O_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */
- flags |= O_CLOEXEC;
+ /* glibc posix_openpt() in GNU/Linux calls open("/dev/ptmx", flags) internally.
+ * So version dependency on GNU/Linux is the same as O_CLOEXEC with open().
+ * O_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */
+ flags |= O_CLOEXEC;
# endif
- if ((masterfd = posix_openpt(flags)) == -1) goto error;
+ if ((masterfd = posix_openpt(flags)) == -1) goto error;
}
rb_fd_fix_cloexec(masterfd);
if (rb_grantpt(masterfd) == -1) goto error;
@@ -310,15 +310,15 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
* or the same interface function.
*/
if (openpty(master, slave, SlaveName,
- (struct termios *)0, (struct winsize *)0) == -1) {
- if (!fail) return -1;
- rb_raise(rb_eRuntimeError, "openpty() failed");
+ (struct termios *)0, (struct winsize *)0) == -1) {
+ if (!fail) return -1;
+ rb_raise(rb_eRuntimeError, "openpty() failed");
}
rb_fd_fix_cloexec(*master);
rb_fd_fix_cloexec(*slave);
if (no_mesg(SlaveName, nomesg) == -1) {
- if (!fail) return -1;
- rb_raise(rb_eRuntimeError, "can't chmod slave pty");
+ if (!fail) return -1;
+ rb_raise(rb_eRuntimeError, "can't chmod slave pty");
}
return 0;
@@ -329,8 +329,8 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
mode_t mode = nomesg ? 0600 : 0622;
if (!(name = _getpty(master, O_RDWR, mode, 0))) {
- if (!fail) return -1;
- rb_raise(rb_eRuntimeError, "_getpty() failed");
+ if (!fail) return -1;
+ rb_raise(rb_eRuntimeError, "_getpty() failed");
}
rb_fd_fix_cloexec(*master);
@@ -386,42 +386,42 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
char MasterName[DEVICELEN];
#define HEX1(c) \
- c"0",c"1",c"2",c"3",c"4",c"5",c"6",c"7", \
- c"8",c"9",c"a",c"b",c"c",c"d",c"e",c"f"
+ c"0",c"1",c"2",c"3",c"4",c"5",c"6",c"7", \
+ c"8",c"9",c"a",c"b",c"c",c"d",c"e",c"f"
#if defined(_IBMESA) /* AIX/ESA */
static const char MasterDevice[] = "/dev/ptyp%s";
static const char SlaveDevice[] = "/dev/ttyp%s";
static const char deviceNo[][3] = {
- HEX1("0"), HEX1("1"), HEX1("2"), HEX1("3"),
- HEX1("4"), HEX1("5"), HEX1("6"), HEX1("7"),
- HEX1("8"), HEX1("9"), HEX1("a"), HEX1("b"),
- HEX1("c"), HEX1("d"), HEX1("e"), HEX1("f"),
+ HEX1("0"), HEX1("1"), HEX1("2"), HEX1("3"),
+ HEX1("4"), HEX1("5"), HEX1("6"), HEX1("7"),
+ HEX1("8"), HEX1("9"), HEX1("a"), HEX1("b"),
+ HEX1("c"), HEX1("d"), HEX1("e"), HEX1("f"),
};
#else /* 4.2BSD */
static const char MasterDevice[] = "/dev/pty%s";
static const char SlaveDevice[] = "/dev/tty%s";
static const char deviceNo[][3] = {
- HEX1("p"), HEX1("q"), HEX1("r"), HEX1("s"),
+ HEX1("p"), HEX1("q"), HEX1("r"), HEX1("s"),
};
#endif
#undef HEX1
for (i = 0; i < numberof(deviceNo); i++) {
- const char *const devno = deviceNo[i];
- snprintf(MasterName, sizeof MasterName, MasterDevice, devno);
- if ((masterfd = rb_cloexec_open(MasterName,O_RDWR,0)) >= 0) {
+ const char *const devno = deviceNo[i];
+ snprintf(MasterName, sizeof MasterName, MasterDevice, devno);
+ if ((masterfd = rb_cloexec_open(MasterName,O_RDWR,0)) >= 0) {
rb_update_max_fd(masterfd);
- *master = masterfd;
- snprintf(SlaveName, DEVICELEN, SlaveDevice, devno);
- if ((slavefd = rb_cloexec_open(SlaveName,O_RDWR,0)) >= 0) {
+ *master = masterfd;
+ snprintf(SlaveName, DEVICELEN, SlaveDevice, devno);
+ if ((slavefd = rb_cloexec_open(SlaveName,O_RDWR,0)) >= 0) {
rb_update_max_fd(slavefd);
- *slave = slavefd;
- if (chown(SlaveName, getuid(), getgid()) != 0) goto error;
- if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error;
- return 0;
- }
- close(masterfd);
- }
+ *slave = slavefd;
+ if (chown(SlaveName, getuid(), getgid()) != 0) goto error;
+ if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error;
+ return 0;
+ }
+ close(masterfd);
+ }
}
error:
if (slavefd != -1) close(slavefd);
@@ -435,8 +435,8 @@ static void
getDevice(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg)
{
if (get_device_once(master, slave, SlaveName, nomesg, 0)) {
- rb_gc();
- get_device_once(master, slave, SlaveName, nomesg, 1);
+ rb_gc();
+ get_device_once(master, slave, SlaveName, nomesg, 1);
}
}
@@ -519,7 +519,7 @@ pty_open(VALUE klass)
assoc = rb_assoc_new(master_io, slave_file);
if (rb_block_given_p()) {
- return rb_ensure(rb_yield, assoc, pty_close_pty, assoc);
+ return rb_ensure(rb_yield, assoc, pty_close_pty, assoc);
}
return assoc;
}
@@ -531,7 +531,7 @@ pty_detach_process(VALUE v)
#ifdef WNOHANG
int st;
if (rb_waitpid(info->child_pid, &st, WNOHANG) <= 0)
- return Qnil;
+ return Qnil;
#endif
rb_detach_process(info->child_pid);
return Qnil;
@@ -604,8 +604,8 @@ pty_getpty(int argc, VALUE *argv, VALUE self)
rb_ary_store(res,2,PIDT2NUM(info.child_pid));
if (rb_block_given_p()) {
- rb_ensure(rb_yield, res, pty_detach_process, (VALUE)&info);
- return Qnil;
+ rb_ensure(rb_yield, res, pty_detach_process, (VALUE)&info);
+ return Qnil;
}
return res;
}
@@ -625,13 +625,13 @@ raise_from_check(rb_pid_t pid, int status)
---->> Either IF_STOPPED or WIFSTOPPED is needed <<----
#endif /* WIFSTOPPED | IF_STOPPED */
if (WIFSTOPPED(status)) { /* suspend */
- state = "stopped";
+ state = "stopped";
}
else if (kill(pid, 0) == 0) {
- state = "changed";
+ state = "changed";
}
else {
- state = "exited";
+ state = "exited";
}
msg = rb_sprintf("pty - %s: %ld", state, (long)pid);
exc = rb_exc_new_str(eChildExited, msg);
@@ -664,12 +664,12 @@ pty_check(int argc, VALUE *argv, VALUE self)
int status;
const int flag =
#ifdef WNOHANG
- WNOHANG|
+ WNOHANG|
#endif
#ifdef WUNTRACED
- WUNTRACED|
+ WUNTRACED|
#endif
- 0;
+ 0;
rb_scan_args(argc, argv, "11", &pid, &exc);
cpid = rb_waitpid(NUM2PIDT(pid), &status, flag);
diff --git a/ext/racc/cparse/cparse.c b/ext/racc/cparse/cparse.c
index f71ed2bba9..f752eb7749 100644
--- a/ext/racc/cparse/cparse.c
+++ b/ext/racc/cparse/cparse.c
@@ -7,8 +7,6 @@
This library is free software.
You can distribute/modify this program under the same terms of ruby.
- $originalId: cparse.c,v 1.8 2006/07/06 11:39:46 aamine Exp $
-
*/
#include <ruby.h>
@@ -24,7 +22,7 @@
Important Constants
----------------------------------------------------------------------- */
-#define RACC_VERSION "1.4.15"
+#define RACC_VERSION "1.6.2"
#define DEFAULT_TOKEN -1
#define ERROR_TOKEN 1
diff --git a/ext/readline/readline-ext.gemspec b/ext/readline/readline-ext.gemspec
index 0c6f70ba91..1e16edbfe6 100644
--- a/ext/readline/readline-ext.gemspec
+++ b/ext/readline/readline-ext.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "readline-ext"
- spec.version = "0.1.4"
+ spec.version = "0.1.5"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
diff --git a/ext/readline/readline.c b/ext/readline/readline.c
index 5ae037dd41..fc254ce315 100644
--- a/ext/readline/readline.c
+++ b/ext/readline/readline.c
@@ -376,8 +376,8 @@ prepare_readline(void)
{
static int initialized = 0;
if (!initialized) {
- rl_initialize();
- initialized = 1;
+ rl_initialize();
+ initialized = 1;
}
if (readline_instream) {
diff --git a/ext/ripper/depend b/ext/ripper/depend
index 80a6d62346..856283e177 100644
--- a/ext/ripper/depend
+++ b/ext/ripper/depend
@@ -17,12 +17,9 @@ ripper.o: ripper.c
all: check
static: check
-ripper.y: $(srcdir)/tools/preproc.rb $(srcdir)/tools/dsl.rb $(top_srcdir)/parse.y {$(VPATH)}id.h
+ripper.y: $(srcdir)/tools/preproc.rb $(srcdir)/tools/dsl.rb $(top_srcdir)/parse.y $(top_srcdir)/defs/id.def
$(ECHO) extracting $@ from $(top_srcdir)/parse.y
- $(Q) $(RUBY) $(top_srcdir)/tool/id2token.rb --path-separator=.$(PATH_SEPARATOR)./ \
- --vpath=$(VPATH)$(PATH_SEPARATOR)$(top_srcdir) id.h $(top_srcdir)/parse.y > ripper.tmp.y
- $(Q) $(RUBY) $(top_srcdir)/tool/pure_parser.rb ripper.tmp.y $(BISON)
- $(Q) $(RM) ripper.tmp.y.bak
+ $(Q) $(RUBY) $(top_srcdir)/tool/id2token.rb $(top_srcdir)/parse.y > ripper.tmp.y
$(Q) $(RUBY) $(srcdir)/tools/preproc.rb ripper.tmp.y --output=$@
$(Q) $(RM) ripper.tmp.y
@@ -234,6 +231,7 @@ ripper.o: $(top_srcdir)/internal/bits.h
ripper.o: $(top_srcdir)/internal/compile.h
ripper.o: $(top_srcdir)/internal/compilers.h
ripper.o: $(top_srcdir)/internal/complex.h
+ripper.o: $(top_srcdir)/internal/encoding.h
ripper.o: $(top_srcdir)/internal/error.h
ripper.o: $(top_srcdir)/internal/fixnum.h
ripper.o: $(top_srcdir)/internal/gc.h
@@ -255,6 +253,7 @@ ripper.o: $(top_srcdir)/internal/warnings.h
ripper.o: $(top_srcdir)/node.h
ripper.o: $(top_srcdir)/regenc.h
ripper.o: $(top_srcdir)/ruby_assert.h
+ripper.o: $(top_srcdir)/shape.h
ripper.o: $(top_srcdir)/symbol.h
ripper.o: ../../probes.h
ripper.o: eventids2.c
diff --git a/ext/ripper/eventids2.c b/ext/ripper/eventids2.c
index ac38663f2d..05687497ac 100644
--- a/ext/ripper/eventids2.c
+++ b/ext/ripper/eventids2.c
@@ -1,22 +1,3 @@
-enum {
- tIGNORED_NL = tLAST_TOKEN + 1,
-# define tIGNORED_NL ((enum yytokentype)tIGNORED_NL)
- tCOMMENT,
-# define tCOMMENT ((enum yytokentype)tCOMMENT)
- tEMBDOC_BEG,
-# define tEMBDOC_BEG ((enum yytokentype)tEMBDOC_BEG)
- tEMBDOC,
-# define tEMBDOC ((enum yytokentype)tEMBDOC)
- tEMBDOC_END,
-# define tEMBDOC_END ((enum yytokentype)tEMBDOC_END)
- tHEREDOC_BEG,
-# define tHEREDOC_BEG ((enum yytokentype)tHEREDOC_BEG)
- tHEREDOC_END,
-# define tHEREDOC_END ((enum yytokentype)tHEREDOC_END)
- k__END__,
-# define k__END__ ((enum yytokentype)k__END__)
-};
-
typedef struct {
ID ripper_id_backref;
ID ripper_id_backtick;
diff --git a/ext/ripper/lib/ripper/lexer.rb b/ext/ripper/lib/ripper/lexer.rb
index 19c59e2ccc..a0f1cbeaa8 100644
--- a/ext/ripper/lib/ripper/lexer.rb
+++ b/ext/ripper/lib/ripper/lexer.rb
@@ -228,7 +228,7 @@ class Ripper
def on_heredoc_end(tok)
@buf.push Elem.new([lineno(), column()], __callee__, tok, state())
- @buf = @stack.pop
+ @buf = @stack.pop unless @stack.empty?
end
def _push_token(tok)
@@ -242,7 +242,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)
diff --git a/ext/ripper/tools/preproc.rb b/ext/ripper/tools/preproc.rb
index b838a78db7..cd85a5da61 100644
--- a/ext/ripper/tools/preproc.rb
+++ b/ext/ripper/tools/preproc.rb
@@ -47,7 +47,7 @@ def prelude(f, out)
when /\A%%/
out << "%%\n"
return
- when /\A%token/
+ when /\A%token/, /\A} <node>/
out << line.sub(/<\w+>/, '<val>')
when /\A%type/
out << line.sub(/<\w+>/, '<val>')
diff --git a/ext/socket/addrinfo.h b/ext/socket/addrinfo.h
index f0b977d79c..eb9eb8ae0e 100644
--- a/ext/socket/addrinfo.h
+++ b/ext/socket/addrinfo.h
@@ -129,14 +129,14 @@
#ifndef HAVE_TYPE_STRUCT_ADDRINFO
struct addrinfo {
- int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
- int ai_family; /* PF_xxx */
- int ai_socktype; /* SOCK_xxx */
- int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
- size_t ai_addrlen; /* length of ai_addr */
- char *ai_canonname; /* canonical name for hostname */
- struct sockaddr *ai_addr; /* binary address */
- struct addrinfo *ai_next; /* next structure in linked list */
+ int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
+ int ai_family; /* PF_xxx */
+ int ai_socktype; /* SOCK_xxx */
+ int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+ size_t ai_addrlen; /* length of ai_addr */
+ char *ai_canonname; /* canonical name for hostname */
+ struct sockaddr *ai_addr; /* binary address */
+ struct addrinfo *ai_next; /* next structure in linked list */
};
#endif
@@ -158,18 +158,18 @@ struct addrinfo {
#endif
extern int getaddrinfo __P((
- const char *hostname, const char *servname,
- const struct addrinfo *hints,
- struct addrinfo **res));
+ const char *hostname, const char *servname,
+ const struct addrinfo *hints,
+ struct addrinfo **res));
extern int getnameinfo __P((
- const struct sockaddr *sa,
- socklen_t salen,
- char *host,
- socklen_t hostlen,
- char *serv,
- socklen_t servlen,
- int flags));
+ const struct sockaddr *sa,
+ socklen_t salen,
+ char *host,
+ socklen_t hostlen,
+ char *serv,
+ socklen_t servlen,
+ int flags));
extern void freehostent __P((struct hostent *));
extern void freeaddrinfo __P((struct addrinfo *));
diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c
index aa62cab0ec..7406177de2 100644
--- a/ext/socket/ancdata.c
+++ b/ext/socket/ancdata.c
@@ -333,11 +333,11 @@ ancillary_timestamp(VALUE self)
if (level == SOL_SOCKET && type == SCM_BINTIME &&
RSTRING_LEN(data) == sizeof(struct bintime)) {
struct bintime bt;
- VALUE d, timev;
+ VALUE d, timev;
memcpy((char*)&bt, RSTRING_PTR(data), sizeof(bt));
- d = ULL2NUM(0x100000000ULL);
- d = mul(d,d);
- timev = add(TIMET2NUM(bt.sec), quo(ULL2NUM(bt.frac), d));
+ d = ULL2NUM(0x100000000ULL);
+ d = mul(d,d);
+ timev = add(TIMET2NUM(bt.sec), quo(ULL2NUM(bt.frac), d));
result = rb_time_num_new(timev, Qnil);
}
# endif
@@ -697,7 +697,7 @@ anc_inspect_passcred_credentials(int level, int type, VALUE data, VALUE ret)
struct ucred cred;
memcpy(&cred, RSTRING_PTR(data), sizeof(struct ucred));
rb_str_catf(ret, " pid=%u uid=%u gid=%u", cred.pid, cred.uid, cred.gid);
- rb_str_cat2(ret, " (ucred)");
+ rb_str_cat2(ret, " (ucred)");
return 1;
}
else {
@@ -712,7 +712,7 @@ static int
anc_inspect_socket_creds(int level, int type, VALUE data, VALUE ret)
{
if (level != SOL_SOCKET && type != SCM_CREDS)
- return 0;
+ return 0;
/*
* FreeBSD has struct cmsgcred and struct sockcred.
@@ -727,46 +727,46 @@ anc_inspect_socket_creds(int level, int type, VALUE data, VALUE ret)
#if defined(HAVE_TYPE_STRUCT_CMSGCRED) /* FreeBSD */
if (RSTRING_LEN(data) == sizeof(struct cmsgcred)) {
- struct cmsgcred cred;
+ struct cmsgcred cred;
memcpy(&cred, RSTRING_PTR(data), sizeof(struct cmsgcred));
rb_str_catf(ret, " pid=%u", cred.cmcred_pid);
rb_str_catf(ret, " uid=%u", cred.cmcred_uid);
rb_str_catf(ret, " euid=%u", cred.cmcred_euid);
rb_str_catf(ret, " gid=%u", cred.cmcred_gid);
- if (cred.cmcred_ngroups) {
- int i;
- const char *sep = " groups=";
- for (i = 0; i < cred.cmcred_ngroups; i++) {
- rb_str_catf(ret, "%s%u", sep, cred.cmcred_groups[i]);
- sep = ",";
- }
- }
- rb_str_cat2(ret, " (cmsgcred)");
+ if (cred.cmcred_ngroups) {
+ int i;
+ const char *sep = " groups=";
+ for (i = 0; i < cred.cmcred_ngroups; i++) {
+ rb_str_catf(ret, "%s%u", sep, cred.cmcred_groups[i]);
+ sep = ",";
+ }
+ }
+ rb_str_cat2(ret, " (cmsgcred)");
return 1;
}
#endif
#if defined(HAVE_TYPE_STRUCT_SOCKCRED) /* FreeBSD, NetBSD */
if ((size_t)RSTRING_LEN(data) >= SOCKCREDSIZE(0)) {
- struct sockcred cred0, *cred;
+ struct sockcred cred0, *cred;
memcpy(&cred0, RSTRING_PTR(data), SOCKCREDSIZE(0));
- if ((size_t)RSTRING_LEN(data) == SOCKCREDSIZE(cred0.sc_ngroups)) {
- cred = (struct sockcred *)ALLOCA_N(char, SOCKCREDSIZE(cred0.sc_ngroups));
- memcpy(cred, RSTRING_PTR(data), SOCKCREDSIZE(cred0.sc_ngroups));
- rb_str_catf(ret, " uid=%u", cred->sc_uid);
- rb_str_catf(ret, " euid=%u", cred->sc_euid);
- rb_str_catf(ret, " gid=%u", cred->sc_gid);
- rb_str_catf(ret, " egid=%u", cred->sc_egid);
- if (cred0.sc_ngroups) {
- int i;
- const char *sep = " groups=";
- for (i = 0; i < cred0.sc_ngroups; i++) {
- rb_str_catf(ret, "%s%u", sep, cred->sc_groups[i]);
- sep = ",";
- }
- }
- rb_str_cat2(ret, " (sockcred)");
- return 1;
- }
+ if ((size_t)RSTRING_LEN(data) == SOCKCREDSIZE(cred0.sc_ngroups)) {
+ cred = (struct sockcred *)ALLOCA_N(char, SOCKCREDSIZE(cred0.sc_ngroups));
+ memcpy(cred, RSTRING_PTR(data), SOCKCREDSIZE(cred0.sc_ngroups));
+ rb_str_catf(ret, " uid=%u", cred->sc_uid);
+ rb_str_catf(ret, " euid=%u", cred->sc_euid);
+ rb_str_catf(ret, " gid=%u", cred->sc_gid);
+ rb_str_catf(ret, " egid=%u", cred->sc_egid);
+ if (cred0.sc_ngroups) {
+ int i;
+ const char *sep = " groups=";
+ for (i = 0; i < cred0.sc_ngroups; i++) {
+ rb_str_catf(ret, "%s%u", sep, cred->sc_groups[i]);
+ sep = ",";
+ }
+ }
+ rb_str_cat2(ret, " (sockcred)");
+ return 1;
+ }
}
#endif
return 0;
@@ -906,37 +906,37 @@ inspect_bintime_as_abstime(int level, int optname, VALUE data, VALUE ret)
if (RSTRING_LEN(data) == sizeof(struct bintime)) {
struct bintime bt;
struct tm tm;
- uint64_t frac_h, frac_l;
- uint64_t scale_h, scale_l;
- uint64_t tmp1, tmp2;
- uint64_t res_h, res_l;
+ uint64_t frac_h, frac_l;
+ uint64_t scale_h, scale_l;
+ uint64_t tmp1, tmp2;
+ uint64_t res_h, res_l;
char buf[32];
memcpy((char*)&bt, RSTRING_PTR(data), sizeof(bt));
LOCALTIME(bt.sec, tm);
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
- /* res_h = frac * 10**19 / 2**64 */
+ /* res_h = frac * 10**19 / 2**64 */
- frac_h = bt.frac >> 32;
- frac_l = bt.frac & 0xffffffff;
+ frac_h = bt.frac >> 32;
+ frac_l = bt.frac & 0xffffffff;
- scale_h = 0x8ac72304; /* 0x8ac7230489e80000 == 10**19 */
- scale_l = 0x89e80000;
+ scale_h = 0x8ac72304; /* 0x8ac7230489e80000 == 10**19 */
+ scale_l = 0x89e80000;
- res_h = frac_h * scale_h;
- res_l = frac_l * scale_l;
+ res_h = frac_h * scale_h;
+ res_l = frac_l * scale_l;
- tmp1 = frac_h * scale_l;
- res_h += tmp1 >> 32;
- tmp2 = res_l;
- res_l += tmp1 & 0xffffffff;
- if (res_l < tmp2) res_h++;
+ tmp1 = frac_h * scale_l;
+ res_h += tmp1 >> 32;
+ tmp2 = res_l;
+ res_l += tmp1 & 0xffffffff;
+ if (res_l < tmp2) res_h++;
- tmp1 = frac_l * scale_h;
- res_h += tmp1 >> 32;
- tmp2 = res_l;
- res_l += tmp1 & 0xffffffff;
- if (res_l < tmp2) res_h++;
+ tmp1 = frac_l * scale_h;
+ res_h += tmp1 >> 32;
+ tmp2 = res_l;
+ res_l += tmp1 & 0xffffffff;
+ if (res_l < tmp2) res_h++;
rb_str_catf(ret, " %s.%019"PRIu64, buf, res_h);
return 1;
@@ -1136,8 +1136,8 @@ rb_sendmsg(int fd, const struct msghdr *msg, int flags)
static VALUE
bsock_sendmsg_internal(VALUE sock, VALUE data, VALUE vflags,
- VALUE dest_sockaddr, VALUE controls, VALUE ex,
- int nonblock)
+ VALUE dest_sockaddr, VALUE controls, VALUE ex,
+ int nonblock)
{
rb_io_t *fptr;
struct msghdr mh;
@@ -1160,15 +1160,15 @@ bsock_sendmsg_internal(VALUE sock, VALUE data, VALUE vflags,
tmp = rb_str_tmp_frozen_acquire(data);
if (!RB_TYPE_P(controls, T_ARRAY)) {
- controls = rb_ary_new();
+ controls = rb_ary_new();
}
controls_num = RARRAY_LENINT(controls);
if (controls_num) {
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
- int i;
- size_t last_pad = 0;
- const VALUE *controls_ptr = RARRAY_CONST_PTR(controls);
+ int i;
+ size_t last_pad = 0;
+ const VALUE *controls_ptr = RARRAY_CONST_PTR(controls);
#if defined(__NetBSD__)
int last_level = 0;
int last_type = 0;
@@ -1215,9 +1215,9 @@ bsock_sendmsg_internal(VALUE sock, VALUE data, VALUE vflags,
last_level = cmh.cmsg_level;
last_type = cmh.cmsg_type;
#endif
- last_pad = cspace - cmh.cmsg_len;
+ last_pad = cspace - cmh.cmsg_len;
}
- if (last_pad) {
+ if (last_pad) {
/*
* This code removes the last padding from msg_controllen.
*
@@ -1242,10 +1242,10 @@ bsock_sendmsg_internal(VALUE sock, VALUE data, VALUE vflags,
if (last_level == SOL_SOCKET && last_type == SCM_RIGHTS)
rb_str_set_len(controls_str, RSTRING_LEN(controls_str)-last_pad);
#endif
- }
- RB_GC_GUARD(controls);
+ }
+ RB_GC_GUARD(controls);
#else
- rb_raise(rb_eNotImpError, "control message for sendmsg is unimplemented");
+ rb_raise(rb_eNotImpError, "control message for sendmsg is unimplemented");
#endif
}
@@ -1256,7 +1256,7 @@ bsock_sendmsg_internal(VALUE sock, VALUE data, VALUE vflags,
#endif
if (!NIL_P(dest_sockaddr))
- SockAddrStringValue(dest_sockaddr);
+ SockAddrStringValue(dest_sockaddr);
rb_io_check_closed(fptr);
@@ -1284,20 +1284,20 @@ bsock_sendmsg_internal(VALUE sock, VALUE data, VALUE vflags,
ss = rb_sendmsg(fptr->fd, &mh, flags);
if (ss == -1) {
- int e;
- if (!nonblock && rb_io_maybe_wait_writable(errno, fptr->self, Qnil)) {
+ int e;
+ if (!nonblock && rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
rb_io_check_closed(fptr);
goto retry;
}
- e = errno;
- if (nonblock && (e == EWOULDBLOCK || e == EAGAIN)) {
- if (ex == Qfalse) {
- return sym_wait_writable;
- }
- rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e,
- "sendmsg(2) would block");
- }
- rb_syserr_fail(e, "sendmsg(2)");
+ e = errno;
+ if (nonblock && (e == EWOULDBLOCK || e == EAGAIN)) {
+ if (ex == Qfalse) {
+ return sym_wait_writable;
+ }
+ rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e,
+ "sendmsg(2) would block");
+ }
+ rb_syserr_fail(e, "sendmsg(2)");
}
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
RB_GC_GUARD(controls_str);
@@ -1311,20 +1311,20 @@ bsock_sendmsg_internal(VALUE sock, VALUE data, VALUE vflags,
#if defined(HAVE_SENDMSG)
VALUE
rsock_bsock_sendmsg(VALUE sock, VALUE data, VALUE flags, VALUE dest_sockaddr,
- VALUE controls)
+ VALUE controls)
{
return bsock_sendmsg_internal(sock, data, flags, dest_sockaddr, controls,
- Qtrue, 0);
+ Qtrue, 0);
}
#endif
#if defined(HAVE_SENDMSG)
VALUE
rsock_bsock_sendmsg_nonblock(VALUE sock, VALUE data, VALUE flags,
- VALUE dest_sockaddr, VALUE controls, VALUE ex)
+ VALUE dest_sockaddr, VALUE controls, VALUE ex)
{
return bsock_sendmsg_internal(sock, data, flags, dest_sockaddr,
- controls, ex, 1);
+ controls, ex, 1);
}
#endif
@@ -1422,12 +1422,12 @@ make_io_for_unix_rights(VALUE ctl, struct cmsghdr *cmh, char *msg_end)
{
if (cmh->cmsg_level == SOL_SOCKET && cmh->cmsg_type == SCM_RIGHTS) {
int *fdp, *end;
- VALUE ary = rb_ary_new();
- rb_ivar_set(ctl, rb_intern("unix_rights"), ary);
+ VALUE ary = rb_ary_new();
+ rb_ivar_set(ctl, rb_intern("unix_rights"), ary);
fdp = (int *)CMSG_DATA(cmh);
end = (int *)((char *)cmh + cmh->cmsg_len);
while ((char *)fdp + sizeof(int) <= (char *)end &&
- (char *)fdp + sizeof(int) <= msg_end) {
+ (char *)fdp + sizeof(int) <= msg_end) {
int fd = *fdp;
struct stat stbuf;
VALUE io;
@@ -1443,15 +1443,15 @@ make_io_for_unix_rights(VALUE ctl, struct cmsghdr *cmh, char *msg_end)
rb_ary_push(ary, io);
fdp++;
}
- OBJ_FREEZE(ary);
+ OBJ_FREEZE(ary);
}
}
#endif
static VALUE
bsock_recvmsg_internal(VALUE sock,
- VALUE vmaxdatlen, VALUE vflags, VALUE vmaxctllen,
- VALUE scm_rights, VALUE ex, int nonblock)
+ VALUE vmaxdatlen, VALUE vflags, VALUE vmaxctllen,
+ VALUE scm_rights, VALUE ex, int nonblock)
{
rb_io_t *fptr;
int grow_buffer;
@@ -1505,28 +1505,28 @@ bsock_recvmsg_internal(VALUE sock,
#if !defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
if (grow_buffer) {
- int socktype;
- socklen_t optlen = (socklen_t)sizeof(socktype);
+ int socktype;
+ socklen_t optlen = (socklen_t)sizeof(socktype);
if (getsockopt(fptr->fd, SOL_SOCKET, SO_TYPE, (void*)&socktype, &optlen) == -1) {
- rb_sys_fail("getsockopt(SO_TYPE)");
- }
- if (socktype == SOCK_STREAM)
- grow_buffer = 0;
+ rb_sys_fail("getsockopt(SO_TYPE)");
+ }
+ if (socktype == SOCK_STREAM)
+ grow_buffer = 0;
}
#endif
retry:
if (NIL_P(dat_str))
- dat_str = rb_str_tmp_new(maxdatlen);
+ dat_str = rb_str_tmp_new(maxdatlen);
else
- rb_str_resize(dat_str, maxdatlen);
+ rb_str_resize(dat_str, maxdatlen);
datbuf = RSTRING_PTR(dat_str);
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
if (NIL_P(ctl_str))
- ctl_str = rb_str_tmp_new(maxctllen);
+ ctl_str = rb_str_tmp_new(maxctllen);
else
- rb_str_resize(ctl_str, maxctllen);
+ rb_str_resize(ctl_str, maxctllen);
ctlbuf = RSTRING_PTR(ctl_str);
#endif
@@ -1556,20 +1556,20 @@ bsock_recvmsg_internal(VALUE sock,
ss = rb_recvmsg(fptr->fd, &mh, flags);
if (ss == -1) {
- int e;
- if (!nonblock && rb_io_maybe_wait_readable(errno, fptr->self, Qnil)) {
+ int e;
+ if (!nonblock && rb_io_maybe_wait_readable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
rb_io_check_closed(fptr);
goto retry;
}
- e = errno;
- if (nonblock && (e == EWOULDBLOCK || e == EAGAIN)) {
+ e = errno;
+ if (nonblock && (e == EWOULDBLOCK || e == EAGAIN)) {
if (ex == Qfalse) {
return sym_wait_readable;
}
- rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE, e, "recvmsg(2) would block");
+ rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE, e, "recvmsg(2) would block");
}
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
- if (!gc_done && (e == EMFILE || e == EMSGSIZE)) {
+ if (!gc_done && (e == EMFILE || e == EMSGSIZE)) {
/*
* When SCM_RIGHTS hit the file descriptors limit:
* - Linux 2.6.18 causes success with MSG_CTRUNC
@@ -1579,24 +1579,24 @@ bsock_recvmsg_internal(VALUE sock,
gc_and_retry:
rb_gc();
gc_done = 1;
- goto retry;
+ goto retry;
}
#else
- if (NIL_P(vmaxdatlen) && grow_buffer && e == EMSGSIZE)
- ss = (ssize_t)iov.iov_len;
- else
+ if (NIL_P(vmaxdatlen) && grow_buffer && e == EMSGSIZE)
+ ss = (ssize_t)iov.iov_len;
+ else
#endif
- rb_syserr_fail(e, "recvmsg(2)");
+ rb_syserr_fail(e, "recvmsg(2)");
}
if (grow_buffer) {
- int grown = 0;
- if (NIL_P(vmaxdatlen) && ss != -1 && ss == (ssize_t)iov.iov_len) {
+ int grown = 0;
+ if (NIL_P(vmaxdatlen) && ss != -1 && ss == (ssize_t)iov.iov_len) {
if (SIZE_MAX/2 < maxdatlen)
rb_raise(rb_eArgError, "max data length too big");
- maxdatlen *= 2;
- grown = 1;
- }
+ maxdatlen *= 2;
+ grown = 1;
+ }
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
if (NIL_P(vmaxctllen) && (mh.msg_flags & MSG_CTRUNC)) {
#define BIG_ENOUGH_SPACE 65536
@@ -1605,9 +1605,9 @@ bsock_recvmsg_internal(VALUE sock,
/* there are big space bug truncated.
* file descriptors limit? */
if (!gc_done) {
- rsock_discard_cmsg_resource(&mh, (flags & MSG_PEEK) != 0);
+ rsock_discard_cmsg_resource(&mh, (flags & MSG_PEEK) != 0);
goto gc_and_retry;
- }
+ }
}
else {
if (SIZE_MAX/2 < maxctllen)
@@ -1616,13 +1616,13 @@ bsock_recvmsg_internal(VALUE sock,
grown = 1;
}
#undef BIG_ENOUGH_SPACE
- }
+ }
#endif
- if (grown) {
+ if (grown) {
rsock_discard_cmsg_resource(&mh, (flags & MSG_PEEK) != 0);
- goto retry;
- }
- else {
+ goto retry;
+ }
+ else {
grow_buffer = 0;
if (flags != orig_flags) {
rsock_discard_cmsg_resource(&mh, (flags & MSG_PEEK) != 0);
@@ -1636,31 +1636,31 @@ bsock_recvmsg_internal(VALUE sock,
dat_str = rb_str_new(datbuf, ss);
else {
rb_str_resize(dat_str, ss);
- rb_obj_reveal(dat_str, rb_cString);
+ 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)
+ INT2NUM(mh.msg_flags)
#else
- Qnil
+ Qnil
#endif
- );
+ );
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
family = rsock_getfamily(fptr);
if (mh.msg_controllen) {
- char *msg_end = (char *)mh.msg_control + mh.msg_controllen;
+ char *msg_end = (char *)mh.msg_control + mh.msg_controllen;
for (cmh = CMSG_FIRSTHDR(&mh); cmh != NULL; cmh = CMSG_NXTHDR(&mh, cmh)) {
VALUE ctl;
- char *ctl_end;
+ char *ctl_end;
size_t clen;
if (cmh->cmsg_len == 0) {
rb_raise(rb_eTypeError, "invalid control message (cmsg_len == 0)");
}
ctl_end = (char*)cmh + cmh->cmsg_len;
- clen = (ctl_end <= msg_end ? ctl_end : msg_end) - (char*)CMSG_DATA(cmh);
+ clen = (ctl_end <= msg_end ? ctl_end : msg_end) - (char*)CMSG_DATA(cmh);
ctl = ancdata_new(family, cmh->cmsg_level, cmh->cmsg_type, rb_str_new((char*)CMSG_DATA(cmh), clen));
if (request_scm_rights)
make_io_for_unix_rights(ctl, cmh, msg_end);
@@ -1679,7 +1679,7 @@ bsock_recvmsg_internal(VALUE sock,
#if defined(HAVE_RECVMSG)
VALUE
rsock_bsock_recvmsg(VALUE sock, VALUE dlen, VALUE flags, VALUE clen,
- VALUE scm_rights)
+ VALUE scm_rights)
{
VALUE ex = Qtrue;
return bsock_recvmsg_internal(sock, dlen, flags, clen, scm_rights, ex, 0);
@@ -1689,7 +1689,7 @@ rsock_bsock_recvmsg(VALUE sock, VALUE dlen, VALUE flags, VALUE clen,
#if defined(HAVE_RECVMSG)
VALUE
rsock_bsock_recvmsg_nonblock(VALUE sock, VALUE dlen, VALUE flags, VALUE clen,
- VALUE scm_rights, VALUE ex)
+ VALUE scm_rights, VALUE ex)
{
return bsock_recvmsg_internal(sock, dlen, flags, clen, scm_rights, ex, 1);
}
diff --git a/ext/socket/basicsocket.c b/ext/socket/basicsocket.c
index 44fb7a4eb7..54c369f6fc 100644
--- a/ext/socket/basicsocket.c
+++ b/ext/socket/basicsocket.c
@@ -94,16 +94,16 @@ bsock_shutdown(int argc, VALUE *argv, VALUE sock)
rb_scan_args(argc, argv, "01", &howto);
if (howto == Qnil)
- how = SHUT_RDWR;
+ how = SHUT_RDWR;
else {
- how = rsock_shutdown_how_arg(howto);
+ how = rsock_shutdown_how_arg(howto);
if (how != SHUT_WR && how != SHUT_RD && how != SHUT_RDWR) {
- rb_raise(rb_eArgError, "`how' should be either :SHUT_RD, :SHUT_WR, :SHUT_RDWR");
- }
+ rb_raise(rb_eArgError, "`how' should be either :SHUT_RD, :SHUT_WR, :SHUT_RDWR");
+ }
}
GetOpenFile(sock, fptr);
if (shutdown(fptr->fd, how) == -1)
- rb_sys_fail("shutdown(2)");
+ rb_sys_fail("shutdown(2)");
return INT2FIX(0);
}
@@ -126,7 +126,7 @@ bsock_close_read(VALUE sock)
GetOpenFile(sock, fptr);
shutdown(fptr->fd, 0);
if (!(fptr->mode & FMODE_WRITABLE)) {
- return rb_io_close(sock);
+ return rb_io_close(sock);
}
fptr->mode &= ~FMODE_READABLE;
@@ -155,7 +155,7 @@ bsock_close_write(VALUE sock)
GetOpenFile(sock, fptr);
if (!(fptr->mode & FMODE_READABLE)) {
- return rb_io_close(sock);
+ return rb_io_close(sock);
}
shutdown(fptr->fd, 1);
fptr->mode &= ~FMODE_WRITABLE;
@@ -246,21 +246,21 @@ bsock_setsockopt(int argc, VALUE *argv, VALUE sock)
switch (TYPE(val)) {
case T_FIXNUM:
- i = FIX2INT(val);
- goto numval;
+ i = FIX2INT(val);
+ goto numval;
case T_FALSE:
- i = 0;
- goto numval;
+ i = 0;
+ goto numval;
case T_TRUE:
- i = 1;
+ i = 1;
numval:
- v = (char*)&i; vlen = (int)sizeof(i);
- break;
+ v = (char*)&i; vlen = (int)sizeof(i);
+ break;
default:
- StringValue(val);
- v = RSTRING_PTR(val);
- vlen = RSTRING_SOCKLEN(val);
- break;
+ StringValue(val);
+ v = RSTRING_PTR(val);
+ vlen = RSTRING_SOCKLEN(val);
+ break;
}
rb_io_check_closed(fptr);
@@ -357,7 +357,7 @@ bsock_getsockopt(VALUE sock, VALUE lev, VALUE optname)
rb_io_check_closed(fptr);
if (getsockopt(fptr->fd, level, option, buf, &len) < 0)
- rsock_sys_fail_path("getsockopt(2)", fptr->pathv);
+ rsock_sys_fail_path("getsockopt(2)", fptr->pathv);
return rsock_sockopt_new(family, level, option, rb_str_new(buf, len));
}
@@ -385,7 +385,7 @@ bsock_getsockname(VALUE sock)
GetOpenFile(sock, fptr);
if (getsockname(fptr->fd, &buf.addr, &len) < 0)
- rb_sys_fail("getsockname(2)");
+ rb_sys_fail("getsockname(2)");
if (len0 < len) len = len0;
return rb_str_new((char*)&buf, len);
}
@@ -416,7 +416,7 @@ bsock_getpeername(VALUE sock)
GetOpenFile(sock, fptr);
if (getpeername(fptr->fd, &buf.addr, &len) < 0)
- rb_sys_fail("getpeername(2)");
+ rb_sys_fail("getpeername(2)");
if (len0 < len) len = len0;
return rb_str_new((char*)&buf, len);
}
@@ -453,7 +453,7 @@ bsock_getpeereid(VALUE self)
gid_t egid;
GetOpenFile(self, fptr);
if (getpeereid(fptr->fd, &euid, &egid) == -1)
- rb_sys_fail("getpeereid(3)");
+ rb_sys_fail("getpeereid(3)");
return rb_assoc_new(UIDT2NUM(euid), GIDT2NUM(egid));
#elif defined(SO_PEERCRED) /* GNU/Linux */
rb_io_t *fptr;
@@ -461,7 +461,7 @@ bsock_getpeereid(VALUE self)
socklen_t len = sizeof(cred);
GetOpenFile(self, fptr);
if (getsockopt(fptr->fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1)
- rb_sys_fail("getsockopt(SO_PEERCRED)");
+ rb_sys_fail("getsockopt(SO_PEERCRED)");
return rb_assoc_new(UIDT2NUM(cred.uid), GIDT2NUM(cred.gid));
#elif defined(HAVE_GETPEERUCRED) /* Solaris */
rb_io_t *fptr;
@@ -469,7 +469,7 @@ bsock_getpeereid(VALUE self)
VALUE ret;
GetOpenFile(self, fptr);
if (getpeerucred(fptr->fd, &uc) == -1)
- rb_sys_fail("getpeerucred(3C)");
+ rb_sys_fail("getpeerucred(3C)");
ret = rb_assoc_new(UIDT2NUM(ucred_geteuid(uc)), GIDT2NUM(ucred_getegid(uc)));
ucred_free(uc);
return ret;
@@ -506,7 +506,7 @@ bsock_local_address(VALUE sock)
GetOpenFile(sock, fptr);
if (getsockname(fptr->fd, &buf.addr, &len) < 0)
- rb_sys_fail("getsockname(2)");
+ rb_sys_fail("getsockname(2)");
if (len0 < len) len = len0;
return rsock_fd_socket_addrinfo(fptr->fd, &buf.addr, len);
}
@@ -540,7 +540,7 @@ bsock_remote_address(VALUE sock)
GetOpenFile(sock, fptr);
if (getpeername(fptr->fd, &buf.addr, &len) < 0)
- rb_sys_fail("getpeername(2)");
+ rb_sys_fail("getpeername(2)");
if (len0 < len) len = len0;
return rsock_fd_socket_addrinfo(fptr->fd, &buf.addr, len);
}
@@ -601,7 +601,7 @@ rsock_bsock_send(int argc, VALUE *argv, VALUE socket)
if (n >= 0) return SSIZET2NUM(n);
- if (rb_io_maybe_wait_writable(errno, socket, Qnil)) {
+ if (rb_io_maybe_wait_writable(errno, socket, RUBY_IO_TIMEOUT_DEFAULT)) {
continue;
}
@@ -656,10 +656,10 @@ bsock_do_not_reverse_lookup_set(VALUE sock, VALUE state)
GetOpenFile(sock, fptr);
if (RTEST(state)) {
- fptr->mode |= FMODE_NOREVLOOKUP;
+ fptr->mode |= FMODE_NOREVLOOKUP;
}
else {
- fptr->mode &= ~FMODE_NOREVLOOKUP;
+ fptr->mode &= ~FMODE_NOREVLOOKUP;
}
return sock;
}
@@ -747,9 +747,9 @@ rsock_init_basicsocket(void)
rb_undef_method(rb_cBasicSocket, "initialize");
rb_define_singleton_method(rb_cBasicSocket, "do_not_reverse_lookup",
- bsock_do_not_rev_lookup, 0);
+ bsock_do_not_rev_lookup, 0);
rb_define_singleton_method(rb_cBasicSocket, "do_not_reverse_lookup=",
- bsock_do_not_rev_lookup_set, 1);
+ bsock_do_not_rev_lookup_set, 1);
rb_define_singleton_method(rb_cBasicSocket, "for_fd", bsock_s_for_fd, 1);
rb_define_method(rb_cBasicSocket, "close_read", bsock_close_read, 0);
@@ -770,23 +770,23 @@ rsock_init_basicsocket(void)
/* for ext/socket/lib/socket.rb use only: */
rb_define_private_method(rb_cBasicSocket,
- "__recv_nonblock", bsock_recv_nonblock, 4);
+ "__recv_nonblock", bsock_recv_nonblock, 4);
#if MSG_DONTWAIT_RELIABLE
rb_define_private_method(rb_cBasicSocket,
- "__read_nonblock", rsock_read_nonblock, 3);
+ "__read_nonblock", rsock_read_nonblock, 3);
rb_define_private_method(rb_cBasicSocket,
- "__write_nonblock", rsock_write_nonblock, 2);
+ "__write_nonblock", rsock_write_nonblock, 2);
#endif
/* in ancdata.c */
rb_define_private_method(rb_cBasicSocket, "__sendmsg",
- rsock_bsock_sendmsg, 4);
+ rsock_bsock_sendmsg, 4);
rb_define_private_method(rb_cBasicSocket, "__sendmsg_nonblock",
- rsock_bsock_sendmsg_nonblock, 5);
+ rsock_bsock_sendmsg_nonblock, 5);
rb_define_private_method(rb_cBasicSocket, "__recvmsg",
- rsock_bsock_recvmsg, 4);
+ rsock_bsock_recvmsg, 4);
rb_define_private_method(rb_cBasicSocket, "__recvmsg_nonblock",
- rsock_bsock_recvmsg_nonblock, 5);
+ rsock_bsock_recvmsg_nonblock, 5);
}
diff --git a/ext/socket/constants.c b/ext/socket/constants.c
index 1bbb53b173..1213f2ae17 100644
--- a/ext/socket/constants.c
+++ b/ext/socket/constants.c
@@ -26,14 +26,14 @@ constant_arg(VALUE arg, int (*str_to_int)(const char*, long, int*), const char *
goto str;
}
else if (!NIL_P(tmp = rb_check_string_type(arg))) {
- arg = tmp;
+ arg = tmp;
str:
ptr = RSTRING_PTR(arg);
if (str_to_int(ptr, RSTRING_LEN(arg), &ret) == -1)
- rb_raise(rb_eSocket, "%s: %s", errmsg, ptr);
+ rb_raise(rb_eSocket, "%s: %s", errmsg, ptr);
}
else {
- ret = NUM2INT(arg);
+ ret = NUM2INT(arg);
}
return ret;
}
diff --git a/ext/socket/depend b/ext/socket/depend
index ffe2fce844..28c5540cd6 100644
--- a/ext/socket/depend
+++ b/ext/socket/depend
@@ -197,6 +197,7 @@ ancdata.o: $(top_srcdir)/internal/string.h
ancdata.o: $(top_srcdir)/internal/thread.h
ancdata.o: $(top_srcdir)/internal/vm.h
ancdata.o: $(top_srcdir)/internal/warnings.h
+ancdata.o: $(top_srcdir)/shape.h
ancdata.o: ancdata.c
ancdata.o: constdefs.h
ancdata.o: rubysocket.h
@@ -388,6 +389,7 @@ basicsocket.o: $(top_srcdir)/internal/string.h
basicsocket.o: $(top_srcdir)/internal/thread.h
basicsocket.o: $(top_srcdir)/internal/vm.h
basicsocket.o: $(top_srcdir)/internal/warnings.h
+basicsocket.o: $(top_srcdir)/shape.h
basicsocket.o: basicsocket.c
basicsocket.o: constdefs.h
basicsocket.o: rubysocket.h
@@ -579,6 +581,7 @@ constants.o: $(top_srcdir)/internal/string.h
constants.o: $(top_srcdir)/internal/thread.h
constants.o: $(top_srcdir)/internal/vm.h
constants.o: $(top_srcdir)/internal/warnings.h
+constants.o: $(top_srcdir)/shape.h
constants.o: constants.c
constants.o: constdefs.c
constants.o: constdefs.h
@@ -771,6 +774,7 @@ ifaddr.o: $(top_srcdir)/internal/string.h
ifaddr.o: $(top_srcdir)/internal/thread.h
ifaddr.o: $(top_srcdir)/internal/vm.h
ifaddr.o: $(top_srcdir)/internal/warnings.h
+ifaddr.o: $(top_srcdir)/shape.h
ifaddr.o: constdefs.h
ifaddr.o: ifaddr.c
ifaddr.o: rubysocket.h
@@ -962,6 +966,7 @@ init.o: $(top_srcdir)/internal/string.h
init.o: $(top_srcdir)/internal/thread.h
init.o: $(top_srcdir)/internal/vm.h
init.o: $(top_srcdir)/internal/warnings.h
+init.o: $(top_srcdir)/shape.h
init.o: constdefs.h
init.o: init.c
init.o: rubysocket.h
@@ -1153,6 +1158,7 @@ ipsocket.o: $(top_srcdir)/internal/string.h
ipsocket.o: $(top_srcdir)/internal/thread.h
ipsocket.o: $(top_srcdir)/internal/vm.h
ipsocket.o: $(top_srcdir)/internal/warnings.h
+ipsocket.o: $(top_srcdir)/shape.h
ipsocket.o: constdefs.h
ipsocket.o: ipsocket.c
ipsocket.o: rubysocket.h
@@ -1344,6 +1350,7 @@ option.o: $(top_srcdir)/internal/string.h
option.o: $(top_srcdir)/internal/thread.h
option.o: $(top_srcdir)/internal/vm.h
option.o: $(top_srcdir)/internal/warnings.h
+option.o: $(top_srcdir)/shape.h
option.o: constdefs.h
option.o: option.c
option.o: rubysocket.h
@@ -1535,6 +1542,7 @@ raddrinfo.o: $(top_srcdir)/internal/string.h
raddrinfo.o: $(top_srcdir)/internal/thread.h
raddrinfo.o: $(top_srcdir)/internal/vm.h
raddrinfo.o: $(top_srcdir)/internal/warnings.h
+raddrinfo.o: $(top_srcdir)/shape.h
raddrinfo.o: constdefs.h
raddrinfo.o: raddrinfo.c
raddrinfo.o: rubysocket.h
@@ -1726,6 +1734,7 @@ socket.o: $(top_srcdir)/internal/string.h
socket.o: $(top_srcdir)/internal/thread.h
socket.o: $(top_srcdir)/internal/vm.h
socket.o: $(top_srcdir)/internal/warnings.h
+socket.o: $(top_srcdir)/shape.h
socket.o: constdefs.h
socket.o: rubysocket.h
socket.o: socket.c
@@ -1917,6 +1926,7 @@ sockssocket.o: $(top_srcdir)/internal/string.h
sockssocket.o: $(top_srcdir)/internal/thread.h
sockssocket.o: $(top_srcdir)/internal/vm.h
sockssocket.o: $(top_srcdir)/internal/warnings.h
+sockssocket.o: $(top_srcdir)/shape.h
sockssocket.o: constdefs.h
sockssocket.o: rubysocket.h
sockssocket.o: sockport.h
@@ -2108,6 +2118,7 @@ tcpserver.o: $(top_srcdir)/internal/string.h
tcpserver.o: $(top_srcdir)/internal/thread.h
tcpserver.o: $(top_srcdir)/internal/vm.h
tcpserver.o: $(top_srcdir)/internal/warnings.h
+tcpserver.o: $(top_srcdir)/shape.h
tcpserver.o: constdefs.h
tcpserver.o: rubysocket.h
tcpserver.o: sockport.h
@@ -2299,6 +2310,7 @@ tcpsocket.o: $(top_srcdir)/internal/string.h
tcpsocket.o: $(top_srcdir)/internal/thread.h
tcpsocket.o: $(top_srcdir)/internal/vm.h
tcpsocket.o: $(top_srcdir)/internal/warnings.h
+tcpsocket.o: $(top_srcdir)/shape.h
tcpsocket.o: constdefs.h
tcpsocket.o: rubysocket.h
tcpsocket.o: sockport.h
@@ -2490,6 +2502,7 @@ udpsocket.o: $(top_srcdir)/internal/string.h
udpsocket.o: $(top_srcdir)/internal/thread.h
udpsocket.o: $(top_srcdir)/internal/vm.h
udpsocket.o: $(top_srcdir)/internal/warnings.h
+udpsocket.o: $(top_srcdir)/shape.h
udpsocket.o: constdefs.h
udpsocket.o: rubysocket.h
udpsocket.o: sockport.h
@@ -2681,6 +2694,7 @@ unixserver.o: $(top_srcdir)/internal/string.h
unixserver.o: $(top_srcdir)/internal/thread.h
unixserver.o: $(top_srcdir)/internal/vm.h
unixserver.o: $(top_srcdir)/internal/warnings.h
+unixserver.o: $(top_srcdir)/shape.h
unixserver.o: constdefs.h
unixserver.o: rubysocket.h
unixserver.o: sockport.h
@@ -2872,6 +2886,7 @@ unixsocket.o: $(top_srcdir)/internal/string.h
unixsocket.o: $(top_srcdir)/internal/thread.h
unixsocket.o: $(top_srcdir)/internal/vm.h
unixsocket.o: $(top_srcdir)/internal/warnings.h
+unixsocket.o: $(top_srcdir)/shape.h
unixsocket.o: constdefs.h
unixsocket.o: rubysocket.h
unixsocket.o: sockport.h
diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb
index b70a862414..37ff216560 100644
--- a/ext/socket/extconf.rb
+++ b/ext/socket/extconf.rb
@@ -316,6 +316,7 @@ end
netpacket/packet.h
net/ethernet.h
sys/un.h
+ afunix.h
ifaddrs.h
sys/ioctl.h
sys/sockio.h
@@ -551,7 +552,7 @@ EOS
end
if !have_macro("IPPROTO_IPV6", headers) && have_const("IPPROTO_IPV6", headers)
- IO.read(File.join(File.dirname(__FILE__), "mkconstants.rb")).sub(/\A.*^__END__$/m, '').split(/\r?\n/).grep(/\AIPPROTO_\w*/){$&}.each {|name|
+ File.read(File.join(File.dirname(__FILE__), "mkconstants.rb")).sub(/\A.*^__END__$/m, '').split(/\r?\n/).grep(/\AIPPROTO_\w*/){$&}.each {|name|
have_const(name, headers) unless $defs.include?("-DHAVE_CONST_#{name.upcase}")
}
end
@@ -655,12 +656,20 @@ EOS
end
hdr = "netinet6/in6.h"
- if /darwin/ =~ RUBY_PLATFORM and !try_compile(<<"SRC", nil, :werror=>true)
+ /darwin/ =~ RUBY_PLATFORM and
+ checking_for("if apple's #{hdr} needs s6_addr patch") {!try_compile(<<"SRC", nil, :werror=>true)} and
#include <netinet/in.h>
int t(struct in6_addr *addr) {return IN6_IS_ADDR_UNSPECIFIED(addr);}
SRC
- print "fixing apple's netinet6/in6.h ..."; $stdout.flush
- in6 = File.read("/usr/include/#{hdr}")
+ checking_for("fixing apple's #{hdr}", "%s") do
+ file = xpopen(%w"clang -include netinet/in.h -E -xc -", in: IO::NULL) do |f|
+ re = %r[^# *\d+ *"(.*/netinet/in\.h)"]
+ Logging.message " grep(#{re})\n"
+ f.read[re, 1]
+ end
+ Logging.message "Substitute from #{file}\n"
+
+ in6 = File.read(file)
if in6.gsub!(/\*\(const\s+__uint32_t\s+\*\)\(const\s+void\s+\*\)\(&(\(\w+\))->s6_addr\[(\d+)\]\)/) do
i, r = $2.to_i.divmod(4)
if r.zero?
@@ -670,12 +679,12 @@ SRC
end
end
FileUtils.mkdir_p(File.dirname(hdr))
- open(hdr, "w") {|f| f.write(in6)}
+ File.write(hdr, in6)
$distcleanfiles << hdr
$distcleandirs << File.dirname(hdr)
- puts "done"
+ "done"
else
- puts "not needed"
+ "not needed"
end
end
create_makefile("socket")
diff --git a/ext/socket/getaddrinfo.c b/ext/socket/getaddrinfo.c
index ce6dc40478..95a2feb3be 100644
--- a/ext/socket/getaddrinfo.c
+++ b/ext/socket/getaddrinfo.c
@@ -98,42 +98,42 @@ static struct in6_addr faith_prefix = IN6ADDR_ANY_INIT;
static const char in_addrany[] = { 0, 0, 0, 0 };
static const char in6_addrany[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 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 const char in_loopback[] = { 127, 0, 0, 1 };
static const char in6_loopback[] = {
- 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, 1
};
struct sockinet {
- u_char si_len;
- u_char si_family;
- u_short si_port;
+ u_char si_len;
+ u_char si_family;
+ u_short si_port;
};
static const struct afd {
- int a_af;
- int a_addrlen;
- int a_socklen;
- int a_off;
- const char *a_addrany;
- const char *a_loopback;
+ int a_af;
+ int a_addrlen;
+ int a_socklen;
+ int a_off;
+ const char *a_addrany;
+ const char *a_loopback;
} afdl [] = {
#ifdef INET6
#define N_INET6 0
- {PF_INET6, sizeof(struct in6_addr),
- sizeof(struct sockaddr_in6),
- offsetof(struct sockaddr_in6, sin6_addr),
- in6_addrany, in6_loopback},
+ {PF_INET6, sizeof(struct in6_addr),
+ sizeof(struct sockaddr_in6),
+ offsetof(struct sockaddr_in6, sin6_addr),
+ in6_addrany, in6_loopback},
#define N_INET 1
#else
#define N_INET 0
#endif
- {PF_INET, sizeof(struct in_addr),
- sizeof(struct sockaddr_in),
- offsetof(struct sockaddr_in, sin_addr),
- in_addrany, in_loopback},
- {0, 0, 0, 0, NULL, NULL},
+ {PF_INET, sizeof(struct in_addr),
+ sizeof(struct sockaddr_in),
+ offsetof(struct sockaddr_in, sin_addr),
+ in_addrany, in_loopback},
+ {0, 0, 0, 0, NULL, NULL},
};
#ifdef INET6
@@ -143,58 +143,58 @@ static const struct afd {
#endif
static int get_name __P((const char *, const struct afd *,
- struct addrinfo **, char *, struct addrinfo *,
- int));
+ struct addrinfo **, char *, struct addrinfo *,
+ int));
static int get_addr __P((const char *, int, struct addrinfo **,
- struct addrinfo *, int));
+ struct addrinfo *, int));
static int str_isnumber __P((const char *));
#ifndef HAVE_GAI_STRERROR
static const char *const ai_errlist[] = {
- "success.",
- "address family for hostname not supported.", /* EAI_ADDRFAMILY */
- "temporary failure in name resolution.", /* EAI_AGAIN */
- "invalid value for ai_flags.", /* EAI_BADFLAGS */
- "non-recoverable failure in name resolution.", /* EAI_FAIL */
- "ai_family not supported.", /* EAI_FAMILY */
- "memory allocation failure.", /* EAI_MEMORY */
- "no address associated with hostname.", /* EAI_NODATA */
- "hostname nor servname provided, or not known.",/* EAI_NONAME */
- "servname not supported for ai_socktype.", /* EAI_SERVICE */
- "ai_socktype not supported.", /* EAI_SOCKTYPE */
- "system error returned in errno.", /* EAI_SYSTEM */
- "invalid value for hints.", /* EAI_BADHINTS */
- "resolved protocol is unknown.", /* EAI_PROTOCOL */
- "unknown error.", /* EAI_MAX */
+ "success.",
+ "address family for hostname not supported.", /* EAI_ADDRFAMILY */
+ "temporary failure in name resolution.", /* EAI_AGAIN */
+ "invalid value for ai_flags.", /* EAI_BADFLAGS */
+ "non-recoverable failure in name resolution.", /* EAI_FAIL */
+ "ai_family not supported.", /* EAI_FAMILY */
+ "memory allocation failure.", /* EAI_MEMORY */
+ "no address associated with hostname.", /* EAI_NODATA */
+ "hostname nor servname provided, or not known.",/* EAI_NONAME */
+ "servname not supported for ai_socktype.", /* EAI_SERVICE */
+ "ai_socktype not supported.", /* EAI_SOCKTYPE */
+ "system error returned in errno.", /* EAI_SYSTEM */
+ "invalid value for hints.", /* EAI_BADHINTS */
+ "resolved protocol is unknown.", /* EAI_PROTOCOL */
+ "unknown error.", /* EAI_MAX */
};
#endif
#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 {\
- error = EAI_MEMORY;\
- goto free;\
- }\
+ if (((ai)->ai_canonname = (char *)malloc(strlen(str) + 1)) != NULL) {\
+ strcpy((ai)->ai_canonname, (str));\
+ } else {\
+ error = EAI_MEMORY;\
+ goto free;\
+ }\
}
#define GET_AI(ai, afd, addr, port) {\
- char *p;\
- if (((ai) = (struct addrinfo *)malloc(sizeof(struct addrinfo) +\
- ((afd)->a_socklen)))\
- == NULL) {\
- error = EAI_MEMORY;\
- goto free;\
- }\
- memcpy((ai), pai, sizeof(struct addrinfo));\
- (ai)->ai_addr = (struct sockaddr *)((ai) + 1);\
- (ai)->ai_family = (afd)->a_af;\
- (ai)->ai_addrlen = (afd)->a_socklen;\
- INIT_SOCKADDR((ai)->ai_addr, (afd)->a_af, (afd)->a_socklen);\
- ((struct sockinet *)(ai)->ai_addr)->si_port = (port);\
- p = (char *)((ai)->ai_addr);\
- memcpy(p + (afd)->a_off, (addr), (afd)->a_addrlen);\
+ char *p;\
+ if (((ai) = (struct addrinfo *)malloc(sizeof(struct addrinfo) +\
+ ((afd)->a_socklen)))\
+ == NULL) {\
+ error = EAI_MEMORY;\
+ goto free;\
+ }\
+ memcpy((ai), pai, sizeof(struct addrinfo));\
+ (ai)->ai_addr = (struct sockaddr *)((ai) + 1);\
+ (ai)->ai_family = (afd)->a_af;\
+ (ai)->ai_addrlen = (afd)->a_socklen;\
+ INIT_SOCKADDR((ai)->ai_addr, (afd)->a_af, (afd)->a_socklen);\
+ ((struct sockinet *)(ai)->ai_addr)->si_port = (port);\
+ p = (char *)((ai)->ai_addr);\
+ memcpy(p + (afd)->a_off, (addr), (afd)->a_addrlen);\
}
#define ERR(err) { error = (err); goto bad; }
@@ -206,36 +206,36 @@ const
char *
gai_strerror(int ecode)
{
- if (ecode < 0 || ecode > EAI_MAX)
- ecode = EAI_MAX;
- return (char *)ai_errlist[ecode];
+ if (ecode < 0 || ecode > EAI_MAX)
+ ecode = EAI_MAX;
+ return (char *)ai_errlist[ecode];
}
#endif
void
freeaddrinfo(struct addrinfo *ai)
{
- struct addrinfo *next;
-
- do {
- next = ai->ai_next;
- if (ai->ai_canonname)
- free(ai->ai_canonname);
- /* no need to free(ai->ai_addr) */
- free(ai);
- } while ((ai = next) != NULL);
+ struct addrinfo *next;
+
+ do {
+ next = ai->ai_next;
+ if (ai->ai_canonname)
+ free(ai->ai_canonname);
+ /* no need to free(ai->ai_addr) */
+ free(ai);
+ } while ((ai = next) != NULL);
}
static int
str_isnumber(const char *p)
{
- char *q = (char *)p;
- while (*q) {
- if (! isdigit(*q))
- return NO;
- q++;
- }
- return YES;
+ char *q = (char *)p;
+ while (*q) {
+ if (! isdigit(*q))
+ return NO;
+ q++;
+ }
+ return YES;
}
#ifndef HAVE_INET_PTON
@@ -243,435 +243,435 @@ str_isnumber(const char *p)
static int
inet_pton(int af, const char *hostname, void *pton)
{
- struct in_addr in;
+ struct in_addr in;
#ifdef HAVE_INET_ATON
- if (!inet_aton(hostname, &in))
- return 0;
+ if (!inet_aton(hostname, &in))
+ return 0;
#else
- int d1, d2, d3, d4;
- char ch;
-
- if (sscanf(hostname, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 &&
- 0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 &&
- 0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) {
- in.s_addr = htonl(
- ((long) d1 << 24) | ((long) d2 << 16) |
- ((long) d3 << 8) | ((long) d4 << 0));
- }
- else {
- return 0;
- }
-#endif
- memcpy(pton, &in, sizeof(in));
- return 1;
+ int d1, d2, d3, d4;
+ char ch;
+
+ if (sscanf(hostname, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 &&
+ 0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 &&
+ 0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) {
+ in.s_addr = htonl(
+ ((long) d1 << 24) | ((long) d2 << 16) |
+ ((long) d3 << 8) | ((long) d4 << 0));
+ }
+ else {
+ return 0;
+ }
+#endif
+ memcpy(pton, &in, sizeof(in));
+ return 1;
}
#endif
int
getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res)
{
- struct addrinfo sentinel;
- struct addrinfo *top = NULL;
- struct addrinfo *cur;
- int i, error = 0;
- char pton[PTON_MAX];
- struct addrinfo ai;
- struct addrinfo *pai;
- u_short port;
+ struct addrinfo sentinel;
+ struct addrinfo *top = NULL;
+ struct addrinfo *cur;
+ int i, error = 0;
+ char pton[PTON_MAX];
+ struct addrinfo ai;
+ struct addrinfo *pai;
+ u_short port;
#ifdef FAITH
- static int firsttime = 1;
-
- if (firsttime) {
- /* translator hack */
- {
- char *q = getenv("GAI");
- if (q && inet_pton(AF_INET6, q, &faith_prefix) == 1)
- translate = YES;
- }
- firsttime = 0;
- }
-#endif
-
- /* initialize file static vars */
- sentinel.ai_next = NULL;
- cur = &sentinel;
- pai = &ai;
- pai->ai_flags = 0;
- pai->ai_family = PF_UNSPEC;
- pai->ai_socktype = ANY;
- pai->ai_protocol = ANY;
- pai->ai_addrlen = 0;
- pai->ai_canonname = NULL;
- pai->ai_addr = NULL;
- pai->ai_next = NULL;
- port = ANY;
-
- if (hostname == NULL && servname == NULL)
- return EAI_NONAME;
- if (hints) {
- /* error check for hints */
- if (hints->ai_addrlen || hints->ai_canonname ||
- hints->ai_addr || hints->ai_next)
- ERR(EAI_BADHINTS); /* xxx */
- if (hints->ai_flags & ~AI_MASK)
- ERR(EAI_BADFLAGS);
- switch (hints->ai_family) {
- case PF_UNSPEC:
- case PF_INET:
+ static int firsttime = 1;
+
+ if (firsttime) {
+ /* translator hack */
+ {
+ char *q = getenv("GAI");
+ if (q && inet_pton(AF_INET6, q, &faith_prefix) == 1)
+ translate = YES;
+ }
+ firsttime = 0;
+ }
+#endif
+
+ /* initialize file static vars */
+ sentinel.ai_next = NULL;
+ cur = &sentinel;
+ pai = &ai;
+ pai->ai_flags = 0;
+ pai->ai_family = PF_UNSPEC;
+ pai->ai_socktype = ANY;
+ pai->ai_protocol = ANY;
+ pai->ai_addrlen = 0;
+ pai->ai_canonname = NULL;
+ pai->ai_addr = NULL;
+ pai->ai_next = NULL;
+ port = ANY;
+
+ if (hostname == NULL && servname == NULL)
+ return EAI_NONAME;
+ if (hints) {
+ /* error check for hints */
+ if (hints->ai_addrlen || hints->ai_canonname ||
+ hints->ai_addr || hints->ai_next)
+ ERR(EAI_BADHINTS); /* xxx */
+ if (hints->ai_flags & ~AI_MASK)
+ ERR(EAI_BADFLAGS);
+ switch (hints->ai_family) {
+ case PF_UNSPEC:
+ case PF_INET:
#ifdef INET6
- case PF_INET6:
-#endif
- break;
- default:
- ERR(EAI_FAMILY);
- }
- memcpy(pai, hints, sizeof(*pai));
- switch (pai->ai_socktype) {
- case ANY:
- switch (pai->ai_protocol) {
- case ANY:
- break;
- case IPPROTO_UDP:
- pai->ai_socktype = SOCK_DGRAM;
- break;
- case IPPROTO_TCP:
- pai->ai_socktype = SOCK_STREAM;
- break;
- default:
+ case PF_INET6:
+#endif
+ break;
+ default:
+ ERR(EAI_FAMILY);
+ }
+ memcpy(pai, hints, sizeof(*pai));
+ switch (pai->ai_socktype) {
+ case ANY:
+ switch (pai->ai_protocol) {
+ case ANY:
+ break;
+ case IPPROTO_UDP:
+ pai->ai_socktype = SOCK_DGRAM;
+ break;
+ case IPPROTO_TCP:
+ pai->ai_socktype = SOCK_STREAM;
+ break;
+ default:
#if defined(SOCK_RAW)
- pai->ai_socktype = SOCK_RAW;
+ pai->ai_socktype = SOCK_RAW;
#endif
- break;
- }
- break;
+ break;
+ }
+ break;
#if defined(SOCK_RAW)
- case SOCK_RAW:
- break;
-#endif
- case SOCK_DGRAM:
- if (pai->ai_protocol != IPPROTO_UDP &&
- pai->ai_protocol != ANY)
- ERR(EAI_BADHINTS); /*xxx*/
- pai->ai_protocol = IPPROTO_UDP;
- break;
- case SOCK_STREAM:
- if (pai->ai_protocol != IPPROTO_TCP &&
- pai->ai_protocol != ANY)
- ERR(EAI_BADHINTS); /*xxx*/
- pai->ai_protocol = IPPROTO_TCP;
- break;
- default:
- ERR(EAI_SOCKTYPE);
- break;
- }
- }
-
- /*
- * service port
- */
- if (servname) {
- if (str_isnumber(servname)) {
- if (pai->ai_socktype == ANY) {
- /* caller accept *ANY* socktype */
- pai->ai_socktype = SOCK_DGRAM;
- pai->ai_protocol = IPPROTO_UDP;
- }
- port = htons((unsigned short)atoi(servname));
+ case SOCK_RAW:
+ break;
+#endif
+ case SOCK_DGRAM:
+ if (pai->ai_protocol != IPPROTO_UDP &&
+ pai->ai_protocol != ANY)
+ ERR(EAI_BADHINTS); /*xxx*/
+ pai->ai_protocol = IPPROTO_UDP;
+ break;
+ case SOCK_STREAM:
+ if (pai->ai_protocol != IPPROTO_TCP &&
+ pai->ai_protocol != ANY)
+ ERR(EAI_BADHINTS); /*xxx*/
+ pai->ai_protocol = IPPROTO_TCP;
+ break;
+ default:
+ ERR(EAI_SOCKTYPE);
+ break;
+ }
+ }
+
+ /*
+ * service port
+ */
+ if (servname) {
+ if (str_isnumber(servname)) {
+ if (pai->ai_socktype == ANY) {
+ /* caller accept *ANY* socktype */
+ pai->ai_socktype = SOCK_DGRAM;
+ pai->ai_protocol = IPPROTO_UDP;
+ }
+ port = htons((unsigned short)atoi(servname));
} else if (pai->ai_flags & AI_NUMERICSERV) {
ERR(EAI_NONAME);
- } else {
- struct servent *sp;
- const char *proto;
-
- proto = NULL;
- switch (pai->ai_socktype) {
- case ANY:
- proto = NULL;
- break;
- case SOCK_DGRAM:
- proto = "udp";
- break;
- case SOCK_STREAM:
- proto = "tcp";
- break;
- default:
- fprintf(stderr, "panic!\n");
- break;
- }
- if ((sp = getservbyname((char*)servname, proto)) == NULL)
- ERR(EAI_SERVICE);
- port = sp->s_port;
- if (pai->ai_socktype == ANY)
- if (strcmp(sp->s_proto, "udp") == 0) {
- pai->ai_socktype = SOCK_DGRAM;
- pai->ai_protocol = IPPROTO_UDP;
- } else if (strcmp(sp->s_proto, "tcp") == 0) {
- pai->ai_socktype = SOCK_STREAM;
- pai->ai_protocol = IPPROTO_TCP;
- } else
- ERR(EAI_PROTOCOL); /*xxx*/
- }
- }
-
- /*
- * hostname == NULL.
- * passive socket -> anyaddr (0.0.0.0 or ::)
- * non-passive socket -> localhost (127.0.0.1 or ::1)
- */
- if (hostname == NULL) {
- const struct afd *afd;
- int s;
-
- for (afd = &afdl[0]; afd->a_af; afd++) {
- if (!(pai->ai_family == PF_UNSPEC
- || pai->ai_family == afd->a_af)) {
- continue;
- }
-
- /*
- * filter out AFs that are not supported by the kernel
- * XXX errno?
- */
- s = socket(afd->a_af, SOCK_DGRAM, 0);
- if (s < 0)
- continue;
-
- close(s);
-
- if (pai->ai_flags & AI_PASSIVE) {
- GET_AI(cur->ai_next, afd, afd->a_addrany, port);
- /* xxx meaningless?
- * GET_CANONNAME(cur->ai_next, "anyaddr");
- */
- } else {
- GET_AI(cur->ai_next, afd, afd->a_loopback,
- port);
- /* xxx meaningless?
- * GET_CANONNAME(cur->ai_next, "localhost");
- */
- }
- cur = cur->ai_next;
- }
- top = sentinel.ai_next;
- if (top)
- goto good;
- else
- ERR(EAI_FAMILY);
- }
-
- /* hostname as numeric name */
- for (i = 0; afdl[i].a_af; i++) {
- if (inet_pton(afdl[i].a_af, hostname, pton)) {
- u_long v4a;
+ } else {
+ struct servent *sp;
+ const char *proto;
+
+ proto = NULL;
+ switch (pai->ai_socktype) {
+ case ANY:
+ proto = NULL;
+ break;
+ case SOCK_DGRAM:
+ proto = "udp";
+ break;
+ case SOCK_STREAM:
+ proto = "tcp";
+ break;
+ default:
+ fprintf(stderr, "panic!\n");
+ break;
+ }
+ if ((sp = getservbyname((char*)servname, proto)) == NULL)
+ ERR(EAI_SERVICE);
+ port = sp->s_port;
+ if (pai->ai_socktype == ANY)
+ if (strcmp(sp->s_proto, "udp") == 0) {
+ pai->ai_socktype = SOCK_DGRAM;
+ pai->ai_protocol = IPPROTO_UDP;
+ } else if (strcmp(sp->s_proto, "tcp") == 0) {
+ pai->ai_socktype = SOCK_STREAM;
+ pai->ai_protocol = IPPROTO_TCP;
+ } else
+ ERR(EAI_PROTOCOL); /*xxx*/
+ }
+ }
+
+ /*
+ * hostname == NULL.
+ * passive socket -> anyaddr (0.0.0.0 or ::)
+ * non-passive socket -> localhost (127.0.0.1 or ::1)
+ */
+ if (hostname == NULL) {
+ const struct afd *afd;
+ int s;
+
+ for (afd = &afdl[0]; afd->a_af; afd++) {
+ if (!(pai->ai_family == PF_UNSPEC
+ || pai->ai_family == afd->a_af)) {
+ continue;
+ }
+
+ /*
+ * filter out AFs that are not supported by the kernel
+ * XXX errno?
+ */
+ s = socket(afd->a_af, SOCK_DGRAM, 0);
+ if (s < 0)
+ continue;
+
+ close(s);
+
+ if (pai->ai_flags & AI_PASSIVE) {
+ GET_AI(cur->ai_next, afd, afd->a_addrany, port);
+ /* xxx meaningless?
+ * GET_CANONNAME(cur->ai_next, "anyaddr");
+ */
+ } else {
+ GET_AI(cur->ai_next, afd, afd->a_loopback,
+ port);
+ /* xxx meaningless?
+ * GET_CANONNAME(cur->ai_next, "localhost");
+ */
+ }
+ cur = cur->ai_next;
+ }
+ top = sentinel.ai_next;
+ if (top)
+ goto good;
+ else
+ ERR(EAI_FAMILY);
+ }
+
+ /* hostname as numeric name */
+ for (i = 0; afdl[i].a_af; i++) {
+ if (inet_pton(afdl[i].a_af, hostname, pton)) {
+ u_long v4a;
#ifdef INET6
- u_char pfx;
-#endif
-
- switch (afdl[i].a_af) {
- case AF_INET:
- v4a = ((struct in_addr *)pton)->s_addr;
- if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
- pai->ai_flags &= ~AI_CANONNAME;
- v4a >>= IN_CLASSA_NSHIFT;
- if (v4a == 0 || v4a == IN_LOOPBACKNET)
- pai->ai_flags &= ~AI_CANONNAME;
- break;
+ u_char pfx;
+#endif
+
+ switch (afdl[i].a_af) {
+ case AF_INET:
+ v4a = ((struct in_addr *)pton)->s_addr;
+ if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
+ pai->ai_flags &= ~AI_CANONNAME;
+ v4a >>= IN_CLASSA_NSHIFT;
+ if (v4a == 0 || v4a == IN_LOOPBACKNET)
+ pai->ai_flags &= ~AI_CANONNAME;
+ break;
#ifdef INET6
- case AF_INET6:
- pfx = ((struct in6_addr *)pton)->s6_addr[0];
- if (pfx == 0 || pfx == 0xfe || pfx == 0xff)
- pai->ai_flags &= ~AI_CANONNAME;
- break;
-#endif
- }
-
- if (pai->ai_family == afdl[i].a_af ||
- pai->ai_family == PF_UNSPEC) {
- if (! (pai->ai_flags & AI_CANONNAME)) {
- GET_AI(top, &afdl[i], pton, port);
- goto good;
- }
- /*
- * if AI_CANONNAME and if reverse lookup
- * fail, return ai anyway to pacify
- * calling application.
- *
- * XXX getaddrinfo() is a name->address
- * translation function, and it looks strange
- * that we do addr->name translation here.
- */
- get_name(pton, &afdl[i], &top, pton, pai, port);
- goto good;
- } else
- ERR(EAI_FAMILY); /*xxx*/
- }
- }
-
- if (pai->ai_flags & AI_NUMERICHOST)
- ERR(EAI_NONAME);
-
- /* hostname as alphabetical name */
- error = get_addr(hostname, pai->ai_family, &top, pai, port);
- if (error == 0) {
- if (top) {
+ case AF_INET6:
+ pfx = ((struct in6_addr *)pton)->s6_addr[0];
+ if (pfx == 0 || pfx == 0xfe || pfx == 0xff)
+ pai->ai_flags &= ~AI_CANONNAME;
+ break;
+#endif
+ }
+
+ if (pai->ai_family == afdl[i].a_af ||
+ pai->ai_family == PF_UNSPEC) {
+ if (! (pai->ai_flags & AI_CANONNAME)) {
+ GET_AI(top, &afdl[i], pton, port);
+ goto good;
+ }
+ /*
+ * if AI_CANONNAME and if reverse lookup
+ * fail, return ai anyway to pacify
+ * calling application.
+ *
+ * XXX getaddrinfo() is a name->address
+ * translation function, and it looks strange
+ * that we do addr->name translation here.
+ */
+ get_name(pton, &afdl[i], &top, pton, pai, port);
+ goto good;
+ } else
+ ERR(EAI_FAMILY); /*xxx*/
+ }
+ }
+
+ if (pai->ai_flags & AI_NUMERICHOST)
+ ERR(EAI_NONAME);
+
+ /* hostname as alphabetical name */
+ error = get_addr(hostname, pai->ai_family, &top, pai, port);
+ if (error == 0) {
+ if (top) {
good:
- *res = top;
- return SUCCESS;
- } else
- error = EAI_FAIL;
- }
+ *res = top;
+ return SUCCESS;
+ } else
+ error = EAI_FAIL;
+ }
free:
- if (top)
- freeaddrinfo(top);
+ if (top)
+ freeaddrinfo(top);
bad:
- *res = NULL;
- return error;
+ *res = NULL;
+ return error;
}
static int
get_name(const char *addr, const struct afd *afd, struct addrinfo **res, char *numaddr, struct addrinfo *pai, int port0)
{
- u_short port = port0 & 0xffff;
- struct hostent *hp;
- struct addrinfo *cur;
- int error = 0;
+ u_short port = port0 & 0xffff;
+ struct hostent *hp;
+ struct addrinfo *cur;
+ int error = 0;
#ifdef INET6
- int h_error;
+ int h_error;
#endif
#ifdef INET6
- hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
+ hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
#else
- hp = gethostbyaddr((char*)addr, afd->a_addrlen, AF_INET);
+ hp = gethostbyaddr((char*)addr, afd->a_addrlen, AF_INET);
#endif
- if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
- GET_AI(cur, afd, hp->h_addr_list[0], port);
- GET_CANONNAME(cur, hp->h_name);
- } else
- GET_AI(cur, afd, numaddr, port);
+ if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
+ GET_AI(cur, afd, hp->h_addr_list[0], port);
+ GET_CANONNAME(cur, hp->h_name);
+ } else
+ GET_AI(cur, afd, numaddr, port);
#ifdef INET6
- if (hp)
- freehostent(hp);
+ if (hp)
+ freehostent(hp);
#endif
- *res = cur;
- return SUCCESS;
+ *res = cur;
+ return SUCCESS;
free:
- if (cur)
- freeaddrinfo(cur);
+ if (cur)
+ freeaddrinfo(cur);
#ifdef INET6
- if (hp)
- freehostent(hp);
+ if (hp)
+ freehostent(hp);
#endif
/* bad: */
- *res = NULL;
- return error;
+ *res = NULL;
+ return error;
}
static int
get_addr(const char *hostname, int af, struct addrinfo **res, struct addrinfo *pai, int port0)
{
- u_short port = port0 & 0xffff;
- struct addrinfo sentinel;
- struct hostent *hp;
- struct addrinfo *top, *cur;
- const struct afd *afd;
- int i, error = 0, h_error;
- char *ap;
-
- top = NULL;
- sentinel.ai_next = NULL;
- cur = &sentinel;
+ u_short port = port0 & 0xffff;
+ struct addrinfo sentinel;
+ struct hostent *hp;
+ struct addrinfo *top, *cur;
+ const struct afd *afd;
+ int i, error = 0, h_error;
+ char *ap;
+
+ top = NULL;
+ sentinel.ai_next = NULL;
+ cur = &sentinel;
#ifdef INET6
- if (af == AF_UNSPEC) {
- hp = getipnodebyname(hostname, AF_INET6,
- AI_ADDRCONFIG|AI_ALL|AI_V4MAPPED, &h_error);
- } else
- hp = getipnodebyname(hostname, af, AI_ADDRCONFIG, &h_error);
+ if (af == AF_UNSPEC) {
+ hp = getipnodebyname(hostname, AF_INET6,
+ AI_ADDRCONFIG|AI_ALL|AI_V4MAPPED, &h_error);
+ } else
+ hp = getipnodebyname(hostname, af, AI_ADDRCONFIG, &h_error);
#else
- if (strlen(hostname) >= NI_MAXHOST) ERR(EAI_NODATA);
- hp = gethostbyname((char*)hostname);
- h_error = h_errno;
-#endif
- if (hp == NULL) {
- switch (h_error) {
- case HOST_NOT_FOUND:
- case NO_DATA:
- error = EAI_NODATA;
- break;
- case TRY_AGAIN:
- error = EAI_AGAIN;
- break;
- case NO_RECOVERY:
- default:
- error = EAI_FAIL;
- break;
- }
- goto bad;
- }
-
- if ((hp->h_name == NULL) || (hp->h_name[0] == 0) ||
- (hp->h_addr_list[0] == NULL))
- ERR(EAI_FAIL);
-
- for (i = 0; (ap = hp->h_addr_list[i]) != NULL; i++) {
- switch (af) {
+ if (strlen(hostname) >= NI_MAXHOST) ERR(EAI_NODATA);
+ hp = gethostbyname((char*)hostname);
+ h_error = h_errno;
+#endif
+ if (hp == NULL) {
+ switch (h_error) {
+ case HOST_NOT_FOUND:
+ case NO_DATA:
+ error = EAI_NODATA;
+ break;
+ case TRY_AGAIN:
+ error = EAI_AGAIN;
+ break;
+ case NO_RECOVERY:
+ default:
+ error = EAI_FAIL;
+ break;
+ }
+ goto bad;
+ }
+
+ if ((hp->h_name == NULL) || (hp->h_name[0] == 0) ||
+ (hp->h_addr_list[0] == NULL))
+ ERR(EAI_FAIL);
+
+ for (i = 0; (ap = hp->h_addr_list[i]) != NULL; i++) {
+ switch (af) {
#ifdef INET6
- case AF_INET6:
- afd = &afdl[N_INET6];
- break;
+ case AF_INET6:
+ afd = &afdl[N_INET6];
+ break;
#endif
#ifndef INET6
- default: /* AF_UNSPEC */
+ default: /* AF_UNSPEC */
#endif
- case AF_INET:
- afd = &afdl[N_INET];
- break;
+ case AF_INET:
+ afd = &afdl[N_INET];
+ break;
#ifdef INET6
- default: /* AF_UNSPEC */
- if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) {
- ap += sizeof(struct in6_addr) -
- sizeof(struct in_addr);
- afd = &afdl[N_INET];
- } else
- afd = &afdl[N_INET6];
- break;
-#endif
- }
+ default: /* AF_UNSPEC */
+ if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) {
+ ap += sizeof(struct in6_addr) -
+ sizeof(struct in_addr);
+ afd = &afdl[N_INET];
+ } else
+ afd = &afdl[N_INET6];
+ break;
+#endif
+ }
#ifdef FAITH
- if (translate && afd->a_af == AF_INET) {
- struct in6_addr *in6;
-
- GET_AI(cur->ai_next, &afdl[N_INET6], ap, port);
- in6 = &((struct sockaddr_in6 *)cur->ai_next->ai_addr)->sin6_addr;
- memcpy(&in6->s6_addr, &faith_prefix,
- sizeof(struct in6_addr) - sizeof(struct in_addr));
- memcpy(&in6->s6_addr + sizeof(struct in_addr), ap,
- sizeof(struct in_addr));
- } else
+ if (translate && afd->a_af == AF_INET) {
+ struct in6_addr *in6;
+
+ GET_AI(cur->ai_next, &afdl[N_INET6], ap, port);
+ in6 = &((struct sockaddr_in6 *)cur->ai_next->ai_addr)->sin6_addr;
+ memcpy(&in6->s6_addr, &faith_prefix,
+ sizeof(struct in6_addr) - sizeof(struct in_addr));
+ memcpy(&in6->s6_addr + sizeof(struct in_addr), ap,
+ sizeof(struct in_addr));
+ } else
#endif /* FAITH */
- GET_AI(cur->ai_next, afd, ap, port);
- if (cur == &sentinel) {
- top = cur->ai_next;
- GET_CANONNAME(top, hp->h_name);
- }
- cur = cur->ai_next;
- }
+ GET_AI(cur->ai_next, afd, ap, port);
+ if (cur == &sentinel) {
+ top = cur->ai_next;
+ GET_CANONNAME(top, hp->h_name);
+ }
+ cur = cur->ai_next;
+ }
#ifdef INET6
- freehostent(hp);
+ freehostent(hp);
#endif
- *res = top;
- return SUCCESS;
+ *res = top;
+ return SUCCESS;
free:
- if (top)
- freeaddrinfo(top);
+ if (top)
+ freeaddrinfo(top);
#ifdef INET6
- if (hp)
- freehostent(hp);
+ if (hp)
+ freehostent(hp);
#endif
bad:
- *res = NULL;
- return error;
+ *res = NULL;
+ return error;
}
diff --git a/ext/socket/getnameinfo.c b/ext/socket/getnameinfo.c
index 94a5eb9439..ae5284fab6 100644
--- a/ext/socket/getnameinfo.c
+++ b/ext/socket/getnameinfo.c
@@ -84,30 +84,30 @@ typedef int socklen_t;
#define NO 0
struct sockinet {
- u_char si_len;
- u_char si_family;
- u_short si_port;
+ u_char si_len;
+ u_char si_family;
+ u_short si_port;
};
static struct afd {
- int a_af;
- int a_addrlen;
- int a_socklen;
- int a_off;
+ int a_af;
+ int a_addrlen;
+ int a_socklen;
+ int a_off;
} afdl [] = {
#ifdef INET6
#define N_INET6 0
- {PF_INET6, sizeof(struct in6_addr),
- sizeof(struct sockaddr_in6),
- offsetof(struct sockaddr_in6, sin6_addr)},
+ {PF_INET6, sizeof(struct in6_addr),
+ sizeof(struct sockaddr_in6),
+ offsetof(struct sockaddr_in6, sin6_addr)},
#define N_INET 1
#else
#define N_INET 0
#endif
- {PF_INET, sizeof(struct in_addr),
- sizeof(struct sockaddr_in),
- offsetof(struct sockaddr_in, sin_addr)},
- {0, 0, 0, 0},
+ {PF_INET, sizeof(struct in_addr),
+ sizeof(struct sockaddr_in),
+ offsetof(struct sockaddr_in, sin_addr)},
+ {0, 0, 0, 0},
};
#define ENI_NOSOCKET 0
@@ -121,123 +121,123 @@ static struct afd {
int
getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags)
{
- struct afd *afd;
- struct hostent *hp;
- u_short port;
- int family, len, i;
- char *addr, *p;
- u_long v4a;
+ struct afd *afd;
+ struct hostent *hp;
+ u_short port;
+ int family, len, i;
+ char *addr, *p;
+ u_long v4a;
#ifdef INET6
- u_char pfx;
+ u_char pfx;
#endif
- int h_error;
- char numserv[512];
- char numaddr[512];
+ int h_error;
+ char numserv[512];
+ char numaddr[512];
- if (sa == NULL)
- return ENI_NOSOCKET;
+ if (sa == NULL)
+ return ENI_NOSOCKET;
- if (!VALIDATE_SOCKLEN(sa, salen)) return ENI_SALEN;
+ if (!VALIDATE_SOCKLEN(sa, salen)) return ENI_SALEN;
len = salen;
- family = sa->sa_family;
- for (i = 0; afdl[i].a_af; i++)
- if (afdl[i].a_af == family) {
- afd = &afdl[i];
- goto found;
- }
- return ENI_FAMILY;
+ family = sa->sa_family;
+ for (i = 0; afdl[i].a_af; i++)
+ if (afdl[i].a_af == family) {
+ afd = &afdl[i];
+ goto found;
+ }
+ return ENI_FAMILY;
found:
- if (len != afd->a_socklen) return ENI_SALEN;
-
- port = ((struct sockinet *)sa)->si_port; /* network byte order */
- addr = (char *)sa + afd->a_off;
-
- if (serv == NULL || servlen == 0) {
- /* what we should do? */
- } else if (flags & NI_NUMERICSERV) {
- snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
- if (strlen(numserv) + 1 > servlen)
- return ENI_MEMORY;
- strcpy(serv, numserv);
- } else {
+ if (len != afd->a_socklen) return ENI_SALEN;
+
+ port = ((struct sockinet *)sa)->si_port; /* network byte order */
+ addr = (char *)sa + afd->a_off;
+
+ if (serv == NULL || servlen == 0) {
+ /* what we should do? */
+ } else if (flags & NI_NUMERICSERV) {
+ snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
+ if (strlen(numserv) + 1 > 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)
- return ENI_MEMORY;
- strcpy(serv, sp->s_name);
- } else
- return ENI_NOSERVNAME;
+ struct servent *sp = getservbyport(port, (flags & NI_DGRAM) ? "udp" : "tcp");
+ if (sp) {
+ if (strlen(sp->s_name) + 1 > servlen)
+ return ENI_MEMORY;
+ strcpy(serv, sp->s_name);
+ } else
+ return ENI_NOSERVNAME;
#else
- return ENI_NOSERVNAME;
-#endif
- }
-
- switch (sa->sa_family) {
- case AF_INET:
- v4a = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
- if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
- flags |= NI_NUMERICHOST;
- v4a >>= IN_CLASSA_NSHIFT;
- if (v4a == 0)
- flags |= NI_NUMERICHOST;
- break;
+ return ENI_NOSERVNAME;
+#endif
+ }
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ v4a = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
+ if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
+ flags |= NI_NUMERICHOST;
+ v4a >>= IN_CLASSA_NSHIFT;
+ if (v4a == 0)
+ flags |= NI_NUMERICHOST;
+ break;
#ifdef INET6
- case AF_INET6:
+ case AF_INET6:
#ifdef HAVE_ADDR8
- pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr8[0];
+ pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr8[0];
#else
- pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[0];
-#endif
- if (pfx == 0 || pfx == 0xfe || pfx == 0xff)
- flags |= NI_NUMERICHOST;
- break;
-#endif
- }
- if (host == NULL || hostlen == 0) {
- /* what should we do? */
- } else if (flags & NI_NUMERICHOST) {
- if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
- == NULL)
- return ENI_SYSTEM;
- if (strlen(numaddr) > hostlen)
- return ENI_MEMORY;
- strcpy(host, numaddr);
- } else {
+ pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[0];
+#endif
+ if (pfx == 0 || pfx == 0xfe || pfx == 0xff)
+ flags |= NI_NUMERICHOST;
+ break;
+#endif
+ }
+ if (host == NULL || hostlen == 0) {
+ /* what should we do? */
+ } else if (flags & NI_NUMERICHOST) {
+ if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
+ == NULL)
+ return ENI_SYSTEM;
+ if (strlen(numaddr) > hostlen)
+ return ENI_MEMORY;
+ strcpy(host, numaddr);
+ } else {
#ifdef INET6
- hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
+ hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
#else
- hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
- h_error = h_errno;
+ hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
+ h_error = h_errno;
#endif
- if (hp) {
- if (flags & NI_NOFQDN) {
- p = strchr(hp->h_name, '.');
- if (p) *p = '\0';
- }
- if (strlen(hp->h_name) + 1 > hostlen) {
+ if (hp) {
+ if (flags & NI_NOFQDN) {
+ p = strchr(hp->h_name, '.');
+ if (p) *p = '\0';
+ }
+ if (strlen(hp->h_name) + 1 > hostlen) {
#ifdef INET6
- freehostent(hp);
+ freehostent(hp);
#endif
- return ENI_MEMORY;
- }
- strcpy(host, hp->h_name);
+ return ENI_MEMORY;
+ }
+ strcpy(host, hp->h_name);
#ifdef INET6
- freehostent(hp);
-#endif
- } else {
- if (flags & NI_NAMEREQD)
- return ENI_NOHOSTNAME;
- if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
- == NULL)
- return ENI_NOHOSTNAME;
- if (strlen(numaddr) > hostlen)
- return ENI_MEMORY;
- strcpy(host, numaddr);
- }
- }
- return SUCCESS;
+ freehostent(hp);
+#endif
+ } else {
+ if (flags & NI_NAMEREQD)
+ return ENI_NOHOSTNAME;
+ if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
+ == NULL)
+ return ENI_NOHOSTNAME;
+ if (strlen(numaddr) > hostlen)
+ return ENI_MEMORY;
+ strcpy(host, numaddr);
+ }
+ }
+ return SUCCESS;
}
diff --git a/ext/socket/ifaddr.c b/ext/socket/ifaddr.c
index 1da259bd6f..ab163dcc8f 100644
--- a/ext/socket/ifaddr.c
+++ b/ext/socket/ifaddr.c
@@ -104,7 +104,7 @@ rsock_getifaddrs(void)
rb_sys_fail("getifaddrs");
if (!ifaddrs) {
- return rb_ary_new();
+ return rb_ary_new();
}
numifaddrs = 0;
@@ -128,9 +128,9 @@ rsock_getifaddrs(void)
result = rb_ary_new2(numifaddrs);
rb_ary_push(result, addr);
for (i = 1; i < numifaddrs; i++) {
- addr = TypedData_Wrap_Struct(rb_cSockIfaddr, &ifaddr_type, &root->ary[i]);
- root->refcount++;
- rb_ary_push(result, addr);
+ addr = TypedData_Wrap_Struct(rb_cSockIfaddr, &ifaddr_type, &root->ary[i]);
+ root->refcount++;
+ rb_ary_push(result, addr);
}
return result;
diff --git a/ext/socket/init.c b/ext/socket/init.c
index 359696e626..557d4374a5 100644
--- a/ext/socket/init.c
+++ b/ext/socket/init.c
@@ -41,7 +41,7 @@ rsock_raise_socket_error(const char *reason, int error)
#ifdef EAI_SYSTEM
int e;
if (error == EAI_SYSTEM && (e = errno) != 0)
- rb_syserr_fail(e, reason);
+ rb_syserr_fail(e, reason);
#endif
#ifdef _WIN32
rb_encoding *enc = rb_default_internal_encoding();
@@ -71,7 +71,7 @@ rsock_init_sock(VALUE sock, int fd)
fp->mode = FMODE_READWRITE|FMODE_DUPLEX;
rb_io_ascii8bit_binmode(sock);
if (rsock_do_not_reverse_lookup) {
- fp->mode |= FMODE_NOREVLOOKUP;
+ fp->mode |= FMODE_NOREVLOOKUP;
}
rb_io_synchronized(fp);
@@ -85,7 +85,7 @@ rsock_sendto_blocking(void *data)
VALUE mesg = arg->mesg;
ssize_t ret;
do_write_retry(sendto(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg),
- arg->flags, arg->to, arg->tolen));
+ arg->flags, arg->to, arg->tolen));
return (VALUE)ret;
}
@@ -96,7 +96,7 @@ rsock_send_blocking(void *data)
VALUE mesg = arg->mesg;
ssize_t ret;
do_write_retry(send(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg),
- arg->flags));
+ arg->flags));
return (VALUE)ret;
}
@@ -132,9 +132,9 @@ rsock_strbuf(VALUE str, long buflen)
StringValue(str);
len = RSTRING_LEN(str);
if (len >= buflen) {
- rb_str_modify(str);
+ rb_str_modify(str);
} else {
- rb_str_modify_expand(str, buflen - len);
+ rb_str_modify_expand(str, buflen - len);
}
return str;
}
@@ -189,7 +189,7 @@ rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from)
if (slen >= 0) break;
- if (!rb_io_maybe_wait_readable(errno, socket, Qnil))
+ if (!rb_io_maybe_wait_readable(errno, socket, RUBY_IO_TIMEOUT_DEFAULT))
rb_sys_fail("recvfrom(2)");
}
@@ -197,32 +197,32 @@ rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from)
rb_str_set_len(str, slen);
switch (from) {
case RECV_RECV:
- return str;
+ return str;
case RECV_IP:
#if 0
- if (arg.alen != sizeof(struct sockaddr_in)) {
- rb_raise(rb_eTypeError, "sockaddr size differs - should not happen");
- }
+ if (arg.alen != sizeof(struct sockaddr_in)) {
+ rb_raise(rb_eTypeError, "sockaddr size differs - should not happen");
+ }
#endif
- if (arg.alen && arg.alen != sizeof(arg.buf)) /* OSX doesn't return a from result for connection-oriented sockets */
- return rb_assoc_new(str, rsock_ipaddr(&arg.buf.addr, arg.alen, fptr->mode & FMODE_NOREVLOOKUP));
- else
- return rb_assoc_new(str, Qnil);
+ if (arg.alen && arg.alen != sizeof(arg.buf)) /* OSX doesn't return a from result for connection-oriented sockets */
+ return rb_assoc_new(str, rsock_ipaddr(&arg.buf.addr, arg.alen, fptr->mode & FMODE_NOREVLOOKUP));
+ else
+ return rb_assoc_new(str, Qnil);
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case RECV_UNIX:
return rb_assoc_new(str, rsock_unixaddr(&arg.buf.un, arg.alen));
#endif
case RECV_SOCKET:
- return rb_assoc_new(str, rsock_io_socket_addrinfo(socket, &arg.buf.addr, arg.alen));
+ return rb_assoc_new(str, rsock_io_socket_addrinfo(socket, &arg.buf.addr, arg.alen));
default:
- rb_bug("rsock_s_recvfrom called with bad value");
+ rb_bug("rsock_s_recvfrom called with bad value");
}
}
VALUE
rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str,
- VALUE ex, enum sock_recv_type from)
+ VALUE ex, enum sock_recv_type from)
{
rb_io_t *fptr;
union_sockaddr buf;
@@ -245,14 +245,14 @@ rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str,
GetOpenFile(sock, fptr);
if (rb_io_read_pending(fptr)) {
- rb_raise(rb_eIOError, "recvfrom for buffered IO");
+ rb_raise(rb_eIOError, "recvfrom for buffered IO");
}
fd = fptr->fd;
rb_io_check_closed(fptr);
if (!MSG_DONTWAIT_RELIABLE)
- rb_io_set_nonblock(fptr);
+ rb_io_set_nonblock(fptr);
len0 = alen;
slen = recvfrom(fd, RSTRING_PTR(str), buflen, flags, &buf.addr, &alen);
@@ -260,20 +260,20 @@ rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str,
alen = len0;
if (slen < 0) {
- int e = errno;
- switch (e) {
- case EAGAIN:
+ int e = errno;
+ switch (e) {
+ case EAGAIN:
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK:
+ case EWOULDBLOCK:
#endif
if (ex == Qfalse)
- return sym_wait_readable;
+ return sym_wait_readable;
rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE, e, "recvfrom(2) would block");
- }
- rb_syserr_fail(e, "recvfrom(2)");
+ }
+ rb_syserr_fail(e, "recvfrom(2)");
}
if (slen != RSTRING_LEN(str)) {
- rb_str_set_len(str, slen);
+ rb_str_set_len(str, slen);
}
switch (from) {
case RECV_RECV:
@@ -324,31 +324,31 @@ rsock_read_nonblock(VALUE sock, VALUE length, VALUE buf, VALUE ex)
GetOpenFile(sock, fptr);
if (len == 0) {
- rb_str_set_len(str, 0);
- return str;
+ rb_str_set_len(str, 0);
+ return str;
}
ptr = RSTRING_PTR(str);
n = read_buffered_data(ptr, len, fptr);
if (n <= 0) {
- n = (long)recv(fptr->fd, ptr, len, MSG_DONTWAIT);
- if (n < 0) {
- int e = errno;
- if ((e == EWOULDBLOCK || e == EAGAIN)) {
- if (ex == Qfalse) return sym_wait_readable;
- rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE,
- e, "read would block");
- }
- rb_syserr_fail_path(e, fptr->pathv);
- }
+ n = (long)recv(fptr->fd, ptr, len, MSG_DONTWAIT);
+ if (n < 0) {
+ int e = errno;
+ if ((e == EWOULDBLOCK || e == EAGAIN)) {
+ if (ex == Qfalse) return sym_wait_readable;
+ rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE,
+ e, "read would block");
+ }
+ rb_syserr_fail_path(e, fptr->pathv);
+ }
}
if (n != RSTRING_LEN(str)) {
- rb_str_modify(str);
- rb_str_set_len(str, n);
+ rb_str_modify(str);
+ rb_str_set_len(str, n);
}
if (n == 0) {
- if (ex == Qfalse) return Qnil;
- rb_eof_error();
+ if (ex == Qfalse) return Qnil;
+ rb_eof_error();
}
return str;
@@ -362,7 +362,7 @@ rsock_write_nonblock(VALUE sock, VALUE str, VALUE ex)
long n;
if (!RB_TYPE_P(str, T_STRING))
- str = rb_obj_as_string(str);
+ str = rb_obj_as_string(str);
sock = rb_io_get_write_io(sock);
GetOpenFile(sock, fptr);
@@ -374,7 +374,7 @@ rsock_write_nonblock(VALUE sock, VALUE str, VALUE ex)
* are not userspace-buffered in Ruby by default.
*/
if (fptr->wbuf.len > 0) {
- rb_io_flush(sock);
+ rb_io_flush(sock);
}
#ifdef __APPLE__
@@ -382,19 +382,19 @@ rsock_write_nonblock(VALUE sock, VALUE str, VALUE ex)
#endif
n = (long)send(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str), MSG_DONTWAIT);
if (n < 0) {
- int e = errno;
+ int e = errno;
#ifdef __APPLE__
- if (e == EPROTOTYPE) {
- goto again;
- }
-#endif
- if (e == EWOULDBLOCK || e == EAGAIN) {
- if (ex == Qfalse) return sym_wait_writable;
- rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e,
- "write would block");
- }
- rb_syserr_fail_path(e, fptr->pathv);
+ if (e == EPROTOTYPE) {
+ goto again;
+ }
+#endif
+ if (e == EWOULDBLOCK || e == EAGAIN) {
+ if (ex == Qfalse) return sym_wait_writable;
+ rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e,
+ "write would block");
+ }
+ rb_syserr_fail_path(e, fptr->pathv);
}
return LONG2FIX(n);
@@ -497,11 +497,11 @@ wait_connectable(int fd, struct timeval *timeout)
* interrupted connect()
*/
- /* when the connection timed out, no errno is set and revents is 0. */
- if (timeout && revents == 0) {
- errno = ETIMEDOUT;
- return -1;
- }
+ /* 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:
@@ -516,7 +516,7 @@ wait_connectable(int fd, struct timeval *timeout)
#ifdef EISCONN
case EISCONN:
#endif
- return 0; /* success */
+ return 0; /* success */
default:
/* likely (but not limited to): ECONNREFUSED, ETIMEDOUT, EHOSTUNREACH */
errno = sockerr;
@@ -634,27 +634,27 @@ cloexec_accept(int socket, struct sockaddr *address, socklen_t *address_len)
VALUE
rsock_s_accept_nonblock(VALUE klass, VALUE ex, rb_io_t *fptr,
- struct sockaddr *sockaddr, socklen_t *len)
+ struct sockaddr *sockaddr, socklen_t *len)
{
int fd2;
rb_io_set_nonblock(fptr);
fd2 = cloexec_accept(fptr->fd, (struct sockaddr*)sockaddr, len);
if (fd2 < 0) {
- int e = errno;
- switch (e) {
- case EAGAIN:
+ int e = errno;
+ switch (e) {
+ case EAGAIN:
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK:
+ case EWOULDBLOCK:
#endif
- case ECONNABORTED:
+ case ECONNABORTED:
#if defined EPROTO
- case EPROTO:
+ case EPROTO:
#endif
if (ex == Qfalse)
- return sym_wait_readable;
+ return sym_wait_readable;
rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE, e, "accept(2) would block");
- }
+ }
rb_syserr_fail(e, "accept(2)");
}
rb_update_max_fd(fd2);
@@ -705,7 +705,7 @@ rsock_s_accept(VALUE klass, VALUE io, struct sockaddr *sockaddr, socklen_t *len)
retry = 1;
goto retry;
default:
- if (!rb_io_maybe_wait_readable(error, io, Qnil)) break;
+ if (!rb_io_maybe_wait_readable(error, io, RUBY_IO_TIMEOUT_DEFAULT)) break;
retry = 0;
goto retry;
}
@@ -730,11 +730,11 @@ rsock_getfamily(rb_io_t *fptr)
if (cached) {
switch (cached) {
#ifdef AF_UNIX
- case FMODE_UNIX: return AF_UNIX;
+ case FMODE_UNIX: return AF_UNIX;
#endif
- case FMODE_INET: return AF_INET;
- case FMODE_INET6: return AF_INET6;
- }
+ case FMODE_INET: return AF_INET;
+ case FMODE_INET6: return AF_INET6;
+ }
}
ss.addr.sa_family = AF_UNSPEC;
diff --git a/ext/socket/ipsocket.c b/ext/socket/ipsocket.c
index b5cdc60080..0c13620258 100644
--- a/ext/socket/ipsocket.c
+++ b/ext/socket/ipsocket.c
@@ -14,8 +14,8 @@ struct inetsock_arg
{
VALUE sock;
struct {
- VALUE host, serv;
- struct rb_addrinfo *res;
+ VALUE host, serv;
+ struct rb_addrinfo *res;
} remote, local;
int type;
int fd;
@@ -28,15 +28,15 @@ inetsock_cleanup(VALUE v)
{
struct inetsock_arg *arg = (void *)v;
if (arg->remote.res) {
- rb_freeaddrinfo(arg->remote.res);
- arg->remote.res = 0;
+ rb_freeaddrinfo(arg->remote.res);
+ arg->remote.res = 0;
}
if (arg->local.res) {
- rb_freeaddrinfo(arg->local.res);
- arg->local.res = 0;
+ rb_freeaddrinfo(arg->local.res);
+ arg->local.res = 0;
}
if (arg->fd >= 0) {
- close(arg->fd);
+ close(arg->fd);
}
return Qnil;
}
@@ -61,8 +61,8 @@ init_inetsock_internal(VALUE v)
}
arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv,
- family, SOCK_STREAM,
- (type == INET_SERVER) ? AI_PASSIVE : 0);
+ family, SOCK_STREAM,
+ (type == INET_SERVER) ? AI_PASSIVE : 0);
/*
@@ -70,15 +70,15 @@ 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);
+ arg->local.res = rsock_addrinfo(arg->local.host, arg->local.serv,
+ family, SOCK_STREAM, 0);
}
arg->fd = fd = -1;
for (res = arg->remote.res->ai; res; res = res->ai_next) {
#if !defined(INET6) && defined(AF_INET6)
- if (res->ai_family == AF_INET6)
- continue;
+ if (res->ai_family == AF_INET6)
+ continue;
#endif
lres = NULL;
if (arg->local.res) {
@@ -94,73 +94,73 @@ init_inetsock_internal(VALUE v)
lres = arg->local.res->ai;
}
}
- status = rsock_socket(res->ai_family,res->ai_socktype,res->ai_protocol);
- syscall = "socket(2)";
- fd = status;
- if (fd < 0) {
- error = errno;
- continue;
- }
- arg->fd = fd;
- if (type == INET_SERVER) {
+ status = rsock_socket(res->ai_family,res->ai_socktype,res->ai_protocol);
+ syscall = "socket(2)";
+ fd = status;
+ if (fd < 0) {
+ error = errno;
+ continue;
+ }
+ arg->fd = fd;
+ if (type == INET_SERVER) {
#if !defined(_WIN32) && !defined(__CYGWIN__)
- status = 1;
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
- (char*)&status, (socklen_t)sizeof(status));
+ status = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char*)&status, (socklen_t)sizeof(status));
#endif
- status = bind(fd, res->ai_addr, res->ai_addrlen);
- syscall = "bind(2)";
- }
- else {
- if (lres) {
+ status = bind(fd, res->ai_addr, res->ai_addrlen);
+ syscall = "bind(2)";
+ }
+ else {
+ if (lres) {
#if !defined(_WIN32) && !defined(__CYGWIN__)
status = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
(char*)&status, (socklen_t)sizeof(status));
#endif
- status = bind(fd, lres->ai_addr, lres->ai_addrlen);
- local = status;
- syscall = "bind(2)";
- }
-
- if (status >= 0) {
- status = rsock_connect(fd, res->ai_addr, res->ai_addrlen,
- (type == INET_SOCKS), tv);
- syscall = "connect(2)";
- }
- }
-
- if (status < 0) {
- error = errno;
- close(fd);
- arg->fd = fd = -1;
- continue;
- } else
- break;
+ status = bind(fd, lres->ai_addr, lres->ai_addrlen);
+ local = status;
+ syscall = "bind(2)";
+ }
+
+ if (status >= 0) {
+ status = rsock_connect(fd, res->ai_addr, res->ai_addrlen,
+ (type == INET_SOCKS), tv);
+ syscall = "connect(2)";
+ }
+ }
+
+ if (status < 0) {
+ error = errno;
+ close(fd);
+ arg->fd = fd = -1;
+ continue;
+ } else
+ break;
}
if (status < 0) {
- VALUE host, port;
-
- if (local < 0) {
- host = arg->local.host;
- port = arg->local.serv;
- } else {
- host = arg->remote.host;
- port = arg->remote.serv;
- }
+ VALUE host, port;
+
+ if (local < 0) {
+ host = arg->local.host;
+ port = arg->local.serv;
+ } else {
+ host = arg->remote.host;
+ port = arg->remote.serv;
+ }
- rsock_syserr_fail_host_port(error, syscall, host, port);
+ rsock_syserr_fail_host_port(error, syscall, host, port);
}
arg->fd = -1;
if (type == INET_SERVER) {
- status = listen(fd, SOMAXCONN);
- if (status < 0) {
- error = errno;
- close(fd);
- rb_syserr_fail(error, "listen(2)");
- }
+ status = listen(fd, SOMAXCONN);
+ if (status < 0) {
+ error = errno;
+ close(fd);
+ rb_syserr_fail(error, "listen(2)");
+ }
}
/* create new instance */
@@ -169,8 +169,8 @@ init_inetsock_internal(VALUE v)
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 local_host, VALUE local_serv, int type,
+ VALUE resolv_timeout, VALUE connect_timeout)
{
struct inetsock_arg arg;
arg.sock = sock;
@@ -185,7 +185,7 @@ rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv,
arg.resolv_timeout = resolv_timeout;
arg.connect_timeout = connect_timeout;
return rb_ensure(init_inetsock_internal, (VALUE)&arg,
- inetsock_cleanup, (VALUE)&arg);
+ inetsock_cleanup, (VALUE)&arg);
}
static ID id_numeric, id_hostname;
@@ -201,11 +201,11 @@ rsock_revlookup_flag(VALUE revlookup, int *norevlookup)
case Qfalse: return_norevlookup(1);
case Qnil: break;
default:
- Check_Type(revlookup, T_SYMBOL);
- id = SYM2ID(revlookup);
- if (id == id_numeric) return_norevlookup(1);
- if (id == id_hostname) return_norevlookup(0);
- rb_raise(rb_eArgError, "invalid reverse_lookup flag: :%s", rb_id2name(id));
+ Check_Type(revlookup, T_SYMBOL);
+ id = SYM2ID(revlookup);
+ if (id == id_numeric) return_norevlookup(1);
+ if (id == id_hostname) return_norevlookup(0);
+ rb_raise(rb_eArgError, "invalid reverse_lookup flag: :%s", rb_id2name(id));
}
return 0;
#undef return_norevlookup
@@ -226,24 +226,24 @@ ip_inspect(VALUE sock)
socklen_t len = (socklen_t)sizeof addr;
ID id;
if (fptr && fptr->fd >= 0 &&
- getsockname(fptr->fd, &addr.addr, &len) >= 0 &&
- (id = rsock_intern_family(addr.addr.sa_family)) != 0) {
- VALUE family = rb_id2str(id);
- char hbuf[1024], pbuf[1024];
- long slen = RSTRING_LEN(str);
- const char last = (slen > 1 && RSTRING_PTR(str)[slen - 1] == '>') ?
- (--slen, '>') : 0;
- str = rb_str_subseq(str, 0, slen);
- rb_str_cat_cstr(str, ", ");
- rb_str_append(str, family);
- if (!rb_getnameinfo(&addr.addr, len, hbuf, sizeof(hbuf),
- pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV)) {
- rb_str_cat_cstr(str, ", ");
- rb_str_cat_cstr(str, hbuf);
- rb_str_cat_cstr(str, ", ");
- rb_str_cat_cstr(str, pbuf);
- }
- if (last) rb_str_cat(str, &last, 1);
+ getsockname(fptr->fd, &addr.addr, &len) >= 0 &&
+ (id = rsock_intern_family(addr.addr.sa_family)) != 0) {
+ VALUE family = rb_id2str(id);
+ char hbuf[1024], pbuf[1024];
+ long slen = RSTRING_LEN(str);
+ const char last = (slen > 1 && RSTRING_PTR(str)[slen - 1] == '>') ?
+ (--slen, '>') : 0;
+ str = rb_str_subseq(str, 0, slen);
+ rb_str_cat_cstr(str, ", ");
+ rb_str_append(str, family);
+ if (!rb_getnameinfo(&addr.addr, len, hbuf, sizeof(hbuf),
+ pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV)) {
+ rb_str_cat_cstr(str, ", ");
+ rb_str_cat_cstr(str, hbuf);
+ rb_str_cat_cstr(str, ", ");
+ rb_str_cat_cstr(str, pbuf);
+ }
+ if (last) rb_str_cat(str, &last, 1);
}
return str;
}
@@ -282,9 +282,9 @@ ip_addr(int argc, VALUE *argv, VALUE sock)
GetOpenFile(sock, fptr);
if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup))
- norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
+ norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
if (getsockname(fptr->fd, &addr.addr, &len) < 0)
- rb_sys_fail("getsockname(2)");
+ rb_sys_fail("getsockname(2)");
return rsock_ipaddr(&addr.addr, len, norevlookup);
}
@@ -323,9 +323,9 @@ ip_peeraddr(int argc, VALUE *argv, VALUE sock)
GetOpenFile(sock, fptr);
if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup))
- norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
+ norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
if (getpeername(fptr->fd, &addr.addr, &len) < 0)
- rb_sys_fail("getpeername(2)");
+ rb_sys_fail("getpeername(2)");
return rsock_ipaddr(&addr.addr, len, norevlookup);
}
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb
index d756a32a5a..eecdc7d4b8 100644
--- a/ext/socket/lib/socket.rb
+++ b/ext/socket/lib/socket.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
require 'socket.so'
-require 'io/wait'
+
+unless IO.method_defined?(:wait_writable, false)
+ # It's only required on older Rubies < v3.2:
+ require 'io/wait'
+end
class Addrinfo
# creates an Addrinfo object from the arguments.
@@ -197,7 +201,7 @@ class Addrinfo
sock = Socket.new(self.pfamily, self.socktype, self.protocol)
begin
sock.ipv6only! if self.ipv6?
- sock.setsockopt(:SOCKET, :REUSEADDR, 1)
+ sock.setsockopt(:SOCKET, :REUSEADDR, 1) unless self.pfamily == Socket::PF_UNIX
sock.bind(self)
sock.listen(backlog)
rescue Exception
@@ -606,7 +610,6 @@ class Socket < BasicSocket
# _opts_ may have following options:
#
# [:connect_timeout] specify the timeout in seconds.
- # [:resolv_timeout] specify the name resolution timeout in seconds.
#
# If a block is given, the block is called with the socket.
# The value of the block is returned.
diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb
index 577958a358..5e6c0668f6 100644
--- a/ext/socket/mkconstants.rb
+++ b/ext/socket/mkconstants.rb
@@ -626,6 +626,7 @@ SO_SNDTIMEO nil Send timeout
SO_ACCEPTCONN nil Socket has had listen() called on it
SO_USELOOPBACK nil Bypass hardware when possible
SO_ACCEPTFILTER nil There is an accept filter
+SO_USER_COOKIE nil Setting an identifier for ipfw purpose mainly
SO_DONTTRUNC nil Retain unread data
SO_WANTMORE nil Give a hint when more data is ready
SO_WANTOOBFLAG nil OOB data is wanted in MSG_FLAG on receive
@@ -661,6 +662,10 @@ SO_SELECT_ERR_QUEUE nil Make select() detect socket error queue with err
SO_BUSY_POLL nil Set the threshold in microseconds for low latency polling (Linux 3.11)
SO_MAX_PACING_RATE nil Cap the rate computed by transport layer. [bytes per second] (Linux 3.13)
SO_BPF_EXTENSIONS nil Query supported BPF extensions (Linux 3.14)
+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)
SOPRI_INTERACTIVE nil Interactive socket priority
SOPRI_NORMAL nil Normal socket priority
@@ -670,9 +675,11 @@ IPX_TYPE
TCP_NODELAY nil Don't delay sending to coalesce packets
TCP_MAXSEG nil Set maximum segment size
+TCP_CONNECTION_INFO nil Retrieve information about this socket (macOS)
TCP_CORK nil Don't send partial frames (Linux 2.2, glibc 2.2)
TCP_DEFER_ACCEPT nil Don't notify a listening socket until data is ready (Linux 2.4, glibc 2.2)
TCP_INFO nil Retrieve information about this socket (Linux 2.4, glibc 2.2)
+TCP_KEEPALIVE nil Idle time before keepalive probes are sent (macOS)
TCP_KEEPCNT nil Maximum number of keepalive probes allowed before dropping a connection (Linux 2.4, glibc 2.2)
TCP_KEEPIDLE nil Idle time before keepalive probes are sent (Linux 2.4, glibc 2.2)
TCP_KEEPINTVL nil Time between keepalive probes (Linux 2.4, glibc 2.2)
diff --git a/ext/socket/option.c b/ext/socket/option.c
index 4b33b3f1d3..0d818d0c70 100644
--- a/ext/socket/option.c
+++ b/ext/socket/option.c
@@ -31,7 +31,7 @@ VALUE rb_cSockOpt;
((len) == (size) ? \
(void)0 : \
rb_raise(rb_eTypeError, "size differ. expected as "#size"=%d but %ld", \
- (int)size, (long)(len)))
+ (int)size, (long)(len)))
static VALUE
sockopt_pack_byte(VALUE value)
@@ -309,7 +309,7 @@ sockopt_bool(VALUE self)
StringValue(data);
len = RSTRING_LEN(data);
if (len == 1) {
- return *RSTRING_PTR(data) == 0 ? Qfalse : Qtrue;
+ return *RSTRING_PTR(data) == 0 ? Qfalse : Qtrue;
}
check_size(len, sizeof(int));
memcpy((char*)&i, RSTRING_PTR(data), len);
@@ -420,7 +420,7 @@ sockopt_ipv4_multicast_loop(VALUE self)
#if defined(IPPROTO_IP) && defined(IP_MULTICAST_LOOP)
if (family == AF_INET && level == IPPROTO_IP && optname == IP_MULTICAST_LOOP) {
- return XCAT(sockopt_,TYPE_IP_MULTICAST_LOOP)(self);
+ return XCAT(sockopt_,TYPE_IP_MULTICAST_LOOP)(self);
}
#endif
rb_raise(rb_eTypeError, "ipv4_multicast_loop socket option expected");
@@ -471,7 +471,7 @@ sockopt_ipv4_multicast_ttl(VALUE self)
#if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL)
if (family == AF_INET && level == IPPROTO_IP && optname == IP_MULTICAST_TTL) {
- return XCAT(sockopt_,TYPE_IP_MULTICAST_TTL)(self);
+ return XCAT(sockopt_,TYPE_IP_MULTICAST_TTL)(self);
}
#endif
rb_raise(rb_eTypeError, "ipv4_multicast_ttl socket option expected");
@@ -657,8 +657,8 @@ inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
#else
unsigned long x = ntohl(*(unsigned long*)addr);
snprintf(numaddr, numaddr_len, "%d.%d.%d.%d",
- (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
- (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff);
+ (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
+ (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff);
#endif
return numaddr;
}
@@ -670,10 +670,10 @@ rb_if_indextoname(const char *succ_prefix, const char *fail_prefix, unsigned int
{
#if defined(HAVE_IF_INDEXTONAME)
char ifbuf[IFNAMSIZ];
- if (if_indextoname(ifindex, ifbuf) == NULL)
- return snprintf(buf, len, "%s%u", fail_prefix, ifindex);
- else
+ if (if_indextoname(ifindex, ifbuf))
return snprintf(buf, len, "%s%s", succ_prefix, ifbuf);
+ else
+ return snprintf(buf, len, "%s%u", fail_prefix, ifindex);
#else
# ifndef IFNAMSIZ
# define IFNAMSIZ (sizeof(unsigned int)*3+1)
@@ -1059,16 +1059,16 @@ inspect_tcp_info(int level, int optname, VALUE data, VALUE ret)
rb_str_catf(ret, " fackets=%u", s.tcpi_fackets);
#endif
#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_SENT
- inspect_tcpi_last_data_sent(ret, s.tcpi_last_data_sent);
+ inspect_tcpi_last_data_sent(ret, s.tcpi_last_data_sent);
#endif
#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_SENT
- inspect_tcpi_last_ack_sent(ret, s.tcpi_last_ack_sent);
+ inspect_tcpi_last_ack_sent(ret, s.tcpi_last_ack_sent);
#endif
#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_RECV
- inspect_tcpi_last_data_recv(ret, s.tcpi_last_data_recv);
+ inspect_tcpi_last_data_recv(ret, s.tcpi_last_data_recv);
#endif
#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_RECV
- inspect_tcpi_last_ack_recv(ret, s.tcpi_last_ack_recv);
+ inspect_tcpi_last_ack_recv(ret, s.tcpi_last_ack_recv);
#endif
#ifdef HAVE_STRUCT_TCP_INFO_TCPI_PMTU
rb_str_catf(ret, " pmtu=%u", s.tcpi_pmtu);
@@ -1077,10 +1077,10 @@ inspect_tcp_info(int level, int optname, VALUE data, VALUE ret)
rb_str_catf(ret, " rcv_ssthresh=%u", s.tcpi_rcv_ssthresh);
#endif
#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RTT
- inspect_tcpi_rtt(ret, s.tcpi_rtt);
+ inspect_tcpi_rtt(ret, s.tcpi_rtt);
#endif
#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RTTVAR
- inspect_tcpi_rttvar(ret, s.tcpi_rttvar);
+ inspect_tcpi_rttvar(ret, s.tcpi_rttvar);
#endif
#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_SSTHRESH
rb_str_catf(ret, " snd_ssthresh=%u", s.tcpi_snd_ssthresh);
@@ -1150,7 +1150,7 @@ inspect_peercred(int level, int optname, VALUE data, VALUE ret)
RUBY_SOCK_PEERCRED cred;
memcpy(&cred, RSTRING_PTR(data), sizeof(RUBY_SOCK_PEERCRED));
rb_str_catf(ret, " pid=%u euid=%u egid=%u",
- (unsigned)cred.pid, (unsigned)cred.uid, (unsigned)cred.gid);
+ (unsigned)cred.pid, (unsigned)cred.uid, (unsigned)cred.gid);
rb_str_cat2(ret, " (ucred)");
return 1;
}
@@ -1171,14 +1171,14 @@ inspect_local_peercred(int level, int optname, VALUE data, VALUE ret)
return 0;
rb_str_catf(ret, " version=%u", cred.cr_version);
rb_str_catf(ret, " euid=%u", cred.cr_uid);
- if (cred.cr_ngroups) {
- int i;
- const char *sep = " groups=";
- for (i = 0; i < cred.cr_ngroups; i++) {
- rb_str_catf(ret, "%s%u", sep, cred.cr_groups[i]);
- sep = ",";
- }
- }
+ if (cred.cr_ngroups) {
+ int i;
+ const char *sep = " groups=";
+ for (i = 0; i < cred.cr_ngroups; i++) {
+ rb_str_catf(ret, "%s%u", sep, cred.cr_groups[i]);
+ sep = ",";
+ }
+ }
rb_str_cat2(ret, " (xucred)");
return 1;
}
@@ -1216,42 +1216,42 @@ sockopt_inspect(VALUE self)
family_id = rsock_intern_family_noprefix(family);
if (family_id)
- rb_str_catf(ret, " %s", rb_id2name(family_id));
+ rb_str_catf(ret, " %s", rb_id2name(family_id));
else
rb_str_catf(ret, " family:%d", family);
if (level == SOL_SOCKET) {
rb_str_cat2(ret, " SOCKET");
- optname_id = rsock_intern_so_optname(optname);
- if (optname_id)
- rb_str_catf(ret, " %s", rb_id2name(optname_id));
- else
- rb_str_catf(ret, " optname:%d", optname);
+ optname_id = rsock_intern_so_optname(optname);
+ if (optname_id)
+ rb_str_catf(ret, " %s", rb_id2name(optname_id));
+ else
+ rb_str_catf(ret, " optname:%d", optname);
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
else if (family == AF_UNIX) {
- rb_str_catf(ret, " level:%d", level);
+ rb_str_catf(ret, " level:%d", level);
- optname_id = rsock_intern_local_optname(optname);
- if (optname_id)
- rb_str_catf(ret, " %s", rb_id2name(optname_id));
- else
- rb_str_catf(ret, " optname:%d", optname);
+ optname_id = rsock_intern_local_optname(optname);
+ if (optname_id)
+ rb_str_catf(ret, " %s", rb_id2name(optname_id));
+ else
+ rb_str_catf(ret, " optname:%d", optname);
}
#endif
else if (IS_IP_FAMILY(family)) {
- level_id = rsock_intern_iplevel(level);
- if (level_id)
- rb_str_catf(ret, " %s", rb_id2name(level_id));
- else
- rb_str_catf(ret, " level:%d", level);
-
- v = optname_to_sym(level, optname);
- if (SYMBOL_P(v))
- rb_str_catf(ret, " %"PRIsVALUE, rb_sym2str(v));
- else
- rb_str_catf(ret, " optname:%d", optname);
+ level_id = rsock_intern_iplevel(level);
+ if (level_id)
+ rb_str_catf(ret, " %s", rb_id2name(level_id));
+ else
+ rb_str_catf(ret, " level:%d", level);
+
+ v = optname_to_sym(level, optname);
+ if (SYMBOL_P(v))
+ rb_str_catf(ret, " %"PRIsVALUE, rb_sym2str(v));
+ else
+ rb_str_catf(ret, " optname:%d", optname);
}
else {
rb_str_catf(ret, " level:%d", level);
@@ -1393,7 +1393,7 @@ sockopt_inspect(VALUE self)
}
break;
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX:
switch (level) {
case 0:
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c
index 8772572d2e..45b4cad38f 100644
--- a/ext/socket/raddrinfo.c
+++ b/ext/socket/raddrinfo.c
@@ -24,28 +24,28 @@ static const int lookup_order_table[] = {
static int
ruby_getaddrinfo(const char *nodename, const char *servname,
- const struct addrinfo *hints, struct addrinfo **res)
+ const struct addrinfo *hints, struct addrinfo **res)
{
struct addrinfo tmp_hints;
int i, af, error;
if (hints->ai_family != PF_UNSPEC) {
- return getaddrinfo(nodename, servname, hints, res);
+ return getaddrinfo(nodename, servname, hints, res);
}
for (i = 0; i < LOOKUP_ORDERS; i++) {
- af = lookup_order_table[i];
- MEMCPY(&tmp_hints, hints, struct addrinfo, 1);
- tmp_hints.ai_family = af;
- error = getaddrinfo(nodename, servname, &tmp_hints, res);
- if (error) {
- if (tmp_hints.ai_family == PF_UNSPEC) {
- break;
- }
- }
- else {
- break;
- }
+ af = lookup_order_table[i];
+ MEMCPY(&tmp_hints, hints, struct addrinfo, 1);
+ tmp_hints.ai_family = af;
+ error = getaddrinfo(nodename, servname, &tmp_hints, res);
+ if (error) {
+ if (tmp_hints.ai_family == PF_UNSPEC) {
+ break;
+ }
+ }
+ else {
+ break;
+ }
}
return error;
@@ -56,17 +56,17 @@ ruby_getaddrinfo(const char *nodename, const char *servname,
#if defined(_AIX)
static int
ruby_getaddrinfo__aix(const char *nodename, const char *servname,
- const struct addrinfo *hints, struct addrinfo **res)
+ const struct addrinfo *hints, struct addrinfo **res)
{
int error = getaddrinfo(nodename, servname, hints, res);
struct addrinfo *r;
if (error)
- return error;
+ return error;
for (r = *res; r != NULL; r = r->ai_next) {
- if (r->ai_addr->sa_family == 0)
- r->ai_addr->sa_family = r->ai_family;
- if (r->ai_addr->sa_len == 0)
- r->ai_addr->sa_len = r->ai_addrlen;
+ if (r->ai_addr->sa_family == 0)
+ r->ai_addr->sa_family = r->ai_family;
+ if (r->ai_addr->sa_len == 0)
+ r->ai_addr->sa_len = r->ai_addrlen;
}
return 0;
}
@@ -74,21 +74,21 @@ ruby_getaddrinfo__aix(const char *nodename, const char *servname,
#define getaddrinfo(node,serv,hints,res) ruby_getaddrinfo__aix((node),(serv),(hints),(res))
static int
ruby_getnameinfo__aix(const struct sockaddr *sa, size_t salen,
- char *host, size_t hostlen,
- char *serv, size_t servlen, int flags)
+ char *host, size_t hostlen,
+ char *serv, size_t servlen, int flags)
{
struct sockaddr_in6 *sa6;
u_int32_t *a6;
if (sa->sa_family == AF_INET6) {
- sa6 = (struct sockaddr_in6 *)sa;
- a6 = sa6->sin6_addr.u6_addr.u6_addr32;
+ sa6 = (struct sockaddr_in6 *)sa;
+ a6 = sa6->sin6_addr.u6_addr.u6_addr32;
- if (a6[0] == 0 && a6[1] == 0 && a6[2] == 0 && a6[3] == 0) {
- strncpy(host, "::", hostlen);
- snprintf(serv, servlen, "%d", sa6->sin6_port);
- return 0;
- }
+ if (a6[0] == 0 && a6[1] == 0 && a6[2] == 0 && a6[3] == 0) {
+ strncpy(host, "::", hostlen);
+ snprintf(serv, servlen, "%d", sa6->sin6_port);
+ return 0;
+ }
}
return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
}
@@ -102,7 +102,7 @@ static int str_is_number(const char *);
#if defined(__APPLE__)
static int
ruby_getaddrinfo__darwin(const char *nodename, const char *servname,
- const struct addrinfo *hints, struct addrinfo **res)
+ const struct addrinfo *hints, struct addrinfo **res)
{
/* fix [ruby-core:29427] */
const char *tmp_servname;
@@ -112,12 +112,12 @@ ruby_getaddrinfo__darwin(const char *nodename, const char *servname,
tmp_servname = servname;
MEMCPY(&tmp_hints, hints, struct addrinfo, 1);
if (nodename && servname) {
- if (str_is_number(tmp_servname) && atoi(servname) == 0) {
- tmp_servname = NULL;
+ if (str_is_number(tmp_servname) && atoi(servname) == 0) {
+ tmp_servname = NULL;
#ifdef AI_NUMERICSERV
- if (tmp_hints.ai_flags) tmp_hints.ai_flags &= ~AI_NUMERICSERV;
+ if (tmp_hints.ai_flags) tmp_hints.ai_flags &= ~AI_NUMERICSERV;
#endif
- }
+ }
}
error = getaddrinfo(nodename, tmp_servname, &tmp_hints, res);
@@ -193,7 +193,7 @@ nogvl_getaddrinfo(void *arg)
* it cause getaddrinfo to return EAI_SYSTEM/ENOENT. [ruby-list:49420]
*/
if (ret == EAI_SYSTEM && errno == ENOENT)
- ret = EAI_NONAME;
+ ret = EAI_NONAME;
#endif
return (void *)(VALUE)ret;
}
@@ -212,15 +212,15 @@ numeric_getaddrinfo(const char *node, const char *service,
int port;
if (node && parse_numeric_port(service, &port)) {
- static const struct {
- int socktype;
- int protocol;
- } list[] = {
- { SOCK_STREAM, IPPROTO_TCP },
- { SOCK_DGRAM, IPPROTO_UDP },
- { SOCK_RAW, 0 }
- };
- struct addrinfo *ai = NULL;
+ static const struct {
+ int socktype;
+ int protocol;
+ } list[] = {
+ { SOCK_STREAM, IPPROTO_TCP },
+ { SOCK_DGRAM, IPPROTO_UDP },
+ { SOCK_RAW, 0 }
+ };
+ struct addrinfo *ai = NULL;
int hint_family = hints ? hints->ai_family : PF_UNSPEC;
int hint_socktype = hints ? hints->ai_socktype : 0;
int hint_protocol = hints ? hints->ai_protocol : 0;
@@ -287,8 +287,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;
@@ -319,9 +320,9 @@ nogvl_getnameinfo(void *arg)
{
struct getnameinfo_arg *ptr = arg;
return (void *)(VALUE)getnameinfo(ptr->sa, ptr->salen,
- ptr->host, (socklen_t)ptr->hostlen,
- ptr->serv, (socklen_t)ptr->servlen,
- ptr->flags);
+ ptr->host, (socklen_t)ptr->hostlen,
+ ptr->serv, (socklen_t)ptr->servlen,
+ ptr->flags);
}
#endif
@@ -587,7 +588,7 @@ rsock_fd_family(int fd)
if (fd < 0 || getsockname(fd, &sa, &sa_len) != 0 ||
(size_t)sa_len < offsetof(struct sockaddr, sa_family) + sizeof(sa.sa_family)) {
- return AF_UNSPEC;
+ return AF_UNSPEC;
}
return sa.sa_family;
}
@@ -618,8 +619,7 @@ rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup)
family = rb_str_dup(rb_id2str(id));
}
else {
- sprintf(pbuf, "unknown:%d", sockaddr->sa_family);
- family = rb_str_new2(pbuf);
+ family = rb_sprintf("unknown:%d", sockaddr->sa_family);
}
addr1 = Qnil;
@@ -645,7 +645,7 @@ rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup)
return ary;
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
static long
unixsocket_len(const struct sockaddr_un *su, socklen_t socklen)
{
@@ -677,19 +677,19 @@ rsock_unix_sockaddr_len(VALUE path)
{
#ifdef __linux__
if (RSTRING_LEN(path) == 0) {
- /* autobind; see unix(7) for details. */
- return (socklen_t) sizeof(sa_family_t);
+ /* autobind; see unix(7) for details. */
+ return (socklen_t) sizeof(sa_family_t);
}
else if (RSTRING_PTR(path)[0] == '\0') {
- /* abstract namespace; see unix(7) for details. */
+ /* abstract namespace; see unix(7) for details. */
if (SOCKLEN_MAX - offsetof(struct sockaddr_un, sun_path) < (size_t)RSTRING_LEN(path))
rb_raise(rb_eArgError, "Linux abstract socket too long");
- return (socklen_t) offsetof(struct sockaddr_un, sun_path) +
- RSTRING_SOCKLEN(path);
+ return (socklen_t) offsetof(struct sockaddr_un, sun_path) +
+ RSTRING_SOCKLEN(path);
}
else {
#endif
- return (socklen_t) sizeof(struct sockaddr_un);
+ return (socklen_t) sizeof(struct sockaddr_un);
#ifdef __linux__
}
#endif
@@ -727,7 +727,7 @@ make_hostent_internal(VALUE v)
rb_ary_push(ary, rb_str_new2(hostp));
if (addr->ai_canonname && strlen(addr->ai_canonname) < NI_MAXHOST &&
- (h = gethostbyname(addr->ai_canonname))) {
+ (h = gethostbyname(addr->ai_canonname))) {
names = rb_ary_new();
if (h->h_aliases != NULL) {
for (pch = h->h_aliases; *pch; pch++) {
@@ -875,19 +875,19 @@ call_getaddrinfo(VALUE node, VALUE service,
hints.ai_family = NIL_P(family) ? PF_UNSPEC : rsock_family_arg(family);
if (!NIL_P(socktype)) {
- hints.ai_socktype = rsock_socktype_arg(socktype);
+ hints.ai_socktype = rsock_socktype_arg(socktype);
}
if (!NIL_P(protocol)) {
- hints.ai_protocol = NUM2INT(protocol);
+ hints.ai_protocol = NUM2INT(protocol);
}
if (!NIL_P(flags)) {
- hints.ai_flags = NUM2INT(flags);
+ hints.ai_flags = NUM2INT(flags);
}
res = rsock_getaddrinfo(node, service, &hints, socktype_hack);
if (res == NULL)
- rb_raise(rb_eSocket, "host not found");
+ rb_raise(rb_eSocket, "host not found");
return res;
}
@@ -1018,7 +1018,7 @@ addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
static void
init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype)
{
@@ -1037,7 +1037,7 @@ init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype)
len = rsock_unix_sockaddr_len(path);
init_addrinfo(rai, (struct sockaddr *)&un, len,
- PF_UNIX, socktype, 0, Qnil, Qnil);
+ PF_UNIX, socktype, 0, Qnil, Qnil);
}
static long
@@ -1119,7 +1119,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self)
int af;
StringValue(afamily);
if (rsock_family_to_int(RSTRING_PTR(afamily), RSTRING_LEN(afamily), &af) == -1)
- rb_raise(rb_eSocket, "unknown address family: %s", StringValueCStr(afamily));
+ rb_raise(rb_eSocket, "unknown address family: %s", StringValueCStr(afamily));
switch (af) {
case AF_INET: /* ["AF_INET", 46102, "localhost.localdomain", "127.0.0.1"] */
#ifdef INET6
@@ -1147,7 +1147,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self)
break;
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX: /* ["AF_UNIX", "/tmp/sock"] */
{
VALUE path = rb_ary_entry(sockaddr_ary, 1);
@@ -1209,45 +1209,45 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r
else {
switch (sockaddr->addr.sa_family) {
case AF_UNSPEC:
- {
- rb_str_cat2(ret, "UNSPEC");
+ {
+ rb_str_cat2(ret, "UNSPEC");
break;
- }
+ }
case AF_INET:
{
struct sockaddr_in *addr;
int port;
- addr = &sockaddr->in;
- if ((socklen_t)(((char*)&addr->sin_addr)-(char*)addr+0+1) <= socklen)
- rb_str_catf(ret, "%d", ((unsigned char*)&addr->sin_addr)[0]);
- else
- rb_str_cat2(ret, "?");
- if ((socklen_t)(((char*)&addr->sin_addr)-(char*)addr+1+1) <= socklen)
- rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[1]);
- else
- rb_str_cat2(ret, ".?");
- if ((socklen_t)(((char*)&addr->sin_addr)-(char*)addr+2+1) <= socklen)
- rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[2]);
- else
- rb_str_cat2(ret, ".?");
- if ((socklen_t)(((char*)&addr->sin_addr)-(char*)addr+3+1) <= socklen)
- rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[3]);
- else
- rb_str_cat2(ret, ".?");
-
- if ((socklen_t)(((char*)&addr->sin_port)-(char*)addr+(int)sizeof(addr->sin_port)) < socklen) {
- port = ntohs(addr->sin_port);
- if (port)
- rb_str_catf(ret, ":%d", port);
- }
- else {
- rb_str_cat2(ret, ":?");
- }
- if ((socklen_t)sizeof(struct sockaddr_in) != socklen)
- rb_str_catf(ret, " (%d bytes for %d bytes sockaddr_in)",
- (int)socklen,
- (int)sizeof(struct sockaddr_in));
+ addr = &sockaddr->in;
+ if ((socklen_t)(((char*)&addr->sin_addr)-(char*)addr+0+1) <= socklen)
+ rb_str_catf(ret, "%d", ((unsigned char*)&addr->sin_addr)[0]);
+ else
+ rb_str_cat2(ret, "?");
+ if ((socklen_t)(((char*)&addr->sin_addr)-(char*)addr+1+1) <= socklen)
+ rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[1]);
+ else
+ rb_str_cat2(ret, ".?");
+ if ((socklen_t)(((char*)&addr->sin_addr)-(char*)addr+2+1) <= socklen)
+ rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[2]);
+ else
+ rb_str_cat2(ret, ".?");
+ if ((socklen_t)(((char*)&addr->sin_addr)-(char*)addr+3+1) <= socklen)
+ rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[3]);
+ else
+ rb_str_cat2(ret, ".?");
+
+ if ((socklen_t)(((char*)&addr->sin_port)-(char*)addr+(int)sizeof(addr->sin_port)) < socklen) {
+ port = ntohs(addr->sin_port);
+ if (port)
+ rb_str_catf(ret, ":%d", port);
+ }
+ else {
+ rb_str_cat2(ret, ":?");
+ }
+ if ((socklen_t)sizeof(struct sockaddr_in) != socklen)
+ rb_str_catf(ret, " (%d bytes for %d bytes sockaddr_in)",
+ (int)socklen,
+ (int)sizeof(struct sockaddr_in));
break;
}
@@ -1287,7 +1287,7 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r
}
#endif
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX:
{
struct sockaddr_un *addr = &sockaddr->un;
@@ -1398,20 +1398,20 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r
#endif
#if defined(AF_LINK) && defined(HAVE_TYPE_STRUCT_SOCKADDR_DL)
- /* AF_LINK is defined in 4.4BSD derivations since Net2.
- link_ntoa is also defined at Net2.
+ /* AF_LINK is defined in 4.4BSD derivations since Net2.
+ link_ntoa is also defined at Net2.
However Debian GNU/kFreeBSD defines AF_LINK but
don't have link_ntoa. */
case AF_LINK:
- {
- /*
- * Simple implementation using link_ntoa():
- * This doesn't work on Debian GNU/kFreeBSD 6.0.7 (squeeze).
+ {
+ /*
+ * Simple implementation using link_ntoa():
+ * This doesn't work on Debian GNU/kFreeBSD 6.0.7 (squeeze).
* Also, the format is bit different.
- *
- * rb_str_catf(ret, "LINK %s", link_ntoa(&sockaddr->dl));
- * break;
- */
+ *
+ * rb_str_catf(ret, "LINK %s", link_ntoa(&sockaddr->dl));
+ * break;
+ */
struct sockaddr_dl *addr = &sockaddr->dl;
char *np = NULL, *ap = NULL, *endp;
int nlen = 0, alen = 0;
@@ -1438,14 +1438,14 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r
alen = (int)(endp - ap);
}
- CATSEP;
+ CATSEP;
if (np)
rb_str_catf(ret, "%.*s", nlen, np);
else
rb_str_cat2(ret, "?");
if (ap && 0 < alen) {
- CATSEP;
+ CATSEP;
for (i = 0; i < alen; i++)
rb_str_catf(ret, "%s%02x", i == 0 ? "" : ":", (unsigned char)ap[i]);
}
@@ -1456,10 +1456,10 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r
/* longer length is possible behavior because struct sockaddr_dl has "minimum work area, can be larger" as the last field.
* cf. Net2:/usr/src/sys/net/if_dl.h. */
socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_data) + addr->sdl_nlen + addr->sdl_alen + addr->sdl_slen)) {
- CATSEP;
+ CATSEP;
rb_str_catf(ret, "(%d bytes for %d bytes sockaddr_dl)",
(int)socklen, (int)sizeof(struct sockaddr_dl));
- }
+ }
rb_str_cat2(ret, "]");
#undef CATSEP
@@ -1623,7 +1623,7 @@ addrinfo_mdump(VALUE self)
afamily = rb_id2str(id);
switch(afamily_int) {
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX:
{
sockaddr = rb_str_new(rai->addr.un.sun_path, rai_unixsocket_len(rai));
@@ -1716,7 +1716,7 @@ addrinfo_mload(VALUE self, VALUE ary)
v = rb_ary_entry(ary, 1);
switch(afamily) {
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX:
{
struct sockaddr_un uaddr;
@@ -2009,7 +2009,7 @@ addrinfo_ip_unpack(VALUE self)
VALUE ret, portstr;
if (!IS_IP_FAMILY(family))
- rb_raise(rb_eSocket, "need IPv4 or IPv6 address");
+ rb_raise(rb_eSocket, "need IPv4 or IPv6 address");
vflags = INT2NUM(NI_NUMERICHOST|NI_NUMERICSERV);
ret = addrinfo_getnameinfo(1, &vflags, self);
@@ -2036,7 +2036,7 @@ addrinfo_ip_address(VALUE self)
VALUE ret;
if (!IS_IP_FAMILY(family))
- rb_raise(rb_eSocket, "need IPv4 or IPv6 address");
+ rb_raise(rb_eSocket, "need IPv4 or IPv6 address");
vflags = INT2NUM(NI_NUMERICHOST|NI_NUMERICSERV);
ret = addrinfo_getnameinfo(1, &vflags, self);
@@ -2062,9 +2062,9 @@ addrinfo_ip_port(VALUE self)
if (!IS_IP_FAMILY(family)) {
bad_family:
#ifdef AF_INET6
- rb_raise(rb_eSocket, "need IPv4 or IPv6 address");
+ rb_raise(rb_eSocket, "need IPv4 or IPv6 address");
#else
- rb_raise(rb_eSocket, "need IPv4 address");
+ rb_raise(rb_eSocket, "need IPv4 address");
#endif
}
@@ -2084,7 +2084,7 @@ addrinfo_ip_port(VALUE self)
#endif
default:
- goto bad_family;
+ goto bad_family;
}
return INT2NUM(port);
@@ -2192,7 +2192,7 @@ addrinfo_ipv6_multicast_p(VALUE self)
}
/*
- * Returns true for IPv6 link local address (ff80::/10).
+ * Returns true for IPv6 link local address (fe80::/10).
* It returns false otherwise.
*/
static VALUE
@@ -2204,7 +2204,7 @@ addrinfo_ipv6_linklocal_p(VALUE self)
}
/*
- * Returns true for IPv6 site local address (ffc0::/10).
+ * Returns true for IPv6 site local address (fec0::/10).
* It returns false otherwise.
*/
static VALUE
@@ -2344,7 +2344,7 @@ addrinfo_ipv6_to_ipv4(VALUE self)
#endif
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* call-seq:
* addrinfo.unix_path => path
@@ -2362,7 +2362,7 @@ addrinfo_unix_path(VALUE self)
long n;
if (family != AF_UNIX)
- rb_raise(rb_eSocket, "need AF_UNIX address");
+ rb_raise(rb_eSocket, "need AF_UNIX address");
addr = &rai->addr.un;
@@ -2429,10 +2429,10 @@ addrinfo_s_getaddrinfo(int argc, VALUE *argv, VALUE self)
VALUE node, service, family, socktype, protocol, flags, opts, timeout;
rb_scan_args(argc, argv, "24:", &node, &service, &family, &socktype,
- &protocol, &flags, &opts);
+ &protocol, &flags, &opts);
rb_get_kwargs(opts, &id_timeout, 0, 1, &timeout);
if (timeout == Qundef) {
- timeout = Qnil;
+ timeout = Qnil;
}
return addrinfo_list_new(node, service, family, socktype, protocol, flags, timeout);
@@ -2492,7 +2492,7 @@ addrinfo_s_udp(VALUE self, VALUE host, VALUE port)
INT2NUM(PF_UNSPEC), INT2NUM(SOCK_DGRAM), INT2NUM(IPPROTO_UDP), INT2FIX(0));
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* call-seq:
@@ -2630,7 +2630,7 @@ rsock_init_addrinfo(void)
rb_define_singleton_method(rb_cAddrinfo, "ip", addrinfo_s_ip, 1);
rb_define_singleton_method(rb_cAddrinfo, "tcp", addrinfo_s_tcp, 2);
rb_define_singleton_method(rb_cAddrinfo, "udp", addrinfo_s_udp, 2);
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
rb_define_singleton_method(rb_cAddrinfo, "unix", addrinfo_s_unix, -1);
#endif
@@ -2671,7 +2671,7 @@ rsock_init_addrinfo(void)
rb_define_method(rb_cAddrinfo, "ipv6_to_ipv4", addrinfo_ipv6_to_ipv4, 0);
#endif
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
rb_define_method(rb_cAddrinfo, "unix_path", addrinfo_unix_path, 0);
#endif
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index c0d40addca..5f803ba0da 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -33,6 +33,9 @@
#endif
#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include <iphlpapi.h>
# if defined(_MSC_VER)
# undef HAVE_TYPE_STRUCT_SOCKADDR_DL
# endif
@@ -69,6 +72,11 @@
# include <sys/un.h>
#endif
+#ifdef HAVE_AFUNIX_H
+// Windows doesn't have sys/un.h, but it does have afunix.h just to be special:
+# include <afunix.h>
+#endif
+
#if defined(HAVE_FCNTL)
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
@@ -268,7 +276,7 @@ extern VALUE rb_cIPSocket;
extern VALUE rb_cTCPSocket;
extern VALUE rb_cTCPServer;
extern VALUE rb_cUDPSocket;
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
extern VALUE rb_cUNIXSocket;
extern VALUE rb_cUNIXServer;
#endif
@@ -336,7 +344,7 @@ VALUE rsock_sockaddr_obj(struct sockaddr *addr, socklen_t len);
int rsock_revlookup_flag(VALUE revlookup, int *norevlookup);
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
VALUE rsock_unixpath_str(struct sockaddr_un *sockaddr, socklen_t len);
VALUE rsock_unixaddr(struct sockaddr_un *sockaddr, socklen_t len);
socklen_t rsock_unix_sockaddr_len(VALUE path);
@@ -368,23 +376,23 @@ enum sock_recv_type {
};
VALUE rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str,
- VALUE ex, enum sock_recv_type from);
+ 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);
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,
- struct sockaddr *sockaddr, socklen_t *len);
+ struct sockaddr *sockaddr, socklen_t *len);
VALUE rsock_sock_listen(VALUE sock, VALUE log);
VALUE rsock_sockopt_new(int family, int level, int optname, VALUE data);
#if defined(HAVE_SENDMSG)
VALUE rsock_bsock_sendmsg(VALUE sock, VALUE data, VALUE flags,
- VALUE dest_sockaddr, VALUE controls);
+ VALUE dest_sockaddr, VALUE controls);
VALUE rsock_bsock_sendmsg_nonblock(VALUE sock, VALUE data, VALUE flags,
- VALUE dest_sockaddr, VALUE controls, VALUE ex);
+ VALUE dest_sockaddr, VALUE controls, VALUE ex);
#else
#define rsock_bsock_sendmsg rb_f_notimplement
#define rsock_bsock_sendmsg_nonblock rb_f_notimplement
@@ -392,9 +400,9 @@ VALUE rsock_bsock_sendmsg_nonblock(VALUE sock, VALUE data, VALUE flags,
#if defined(HAVE_RECVMSG)
VALUE rsock_bsock_recvmsg(VALUE sock, VALUE dlen, VALUE clen, VALUE flags,
- VALUE scm_rights);
+ VALUE scm_rights);
VALUE rsock_bsock_recvmsg_nonblock(VALUE sock, VALUE dlen, VALUE clen,
- VALUE flags, VALUE scm_rights, VALUE ex);
+ VALUE flags, VALUE scm_rights, VALUE ex);
ssize_t rsock_recvmsg(int socket, struct msghdr *message, int flags);
#else
#define rsock_bsock_recvmsg rb_f_notimplement
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 4ba1c6cd61..eb74f7a936 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -26,7 +26,11 @@ rsock_syserr_fail_host_port(int err, const char *mesg, VALUE host, VALUE port)
VALUE message;
message = rb_sprintf("%s for %+"PRIsVALUE" port % "PRIsVALUE"",
- mesg, host, port);
+ mesg, host, port);
+
+ if (err == ETIMEDOUT) {
+ rb_exc_raise(rb_exc_new3(rb_eIOTimeoutError, message));
+ }
rb_syserr_fail_str(err, message);
}
@@ -43,11 +47,11 @@ rsock_syserr_fail_path(int err, const char *mesg, VALUE path)
VALUE message;
if (RB_TYPE_P(path, T_STRING)) {
- message = rb_sprintf("%s for % "PRIsVALUE"", mesg, path);
- rb_syserr_fail_str(err, message);
+ message = rb_sprintf("%s for % "PRIsVALUE"", mesg, path);
+ rb_syserr_fail_str(err, message);
}
else {
- rb_syserr_fail(err, mesg);
+ rb_syserr_fail(err, mesg);
}
}
@@ -96,12 +100,12 @@ rsock_syserr_fail_raddrinfo_or_sockaddr(int err, const char *mesg, VALUE addr, V
if (NIL_P(rai)) {
StringValue(addr);
- rsock_syserr_fail_sockaddr(err, mesg,
+ rsock_syserr_fail_sockaddr(err, mesg,
(struct sockaddr *)RSTRING_PTR(addr),
(socklen_t)RSTRING_LEN(addr)); /* overflow should be checked already */
}
else
- rsock_syserr_fail_raddrinfo(err, mesg, rai);
+ rsock_syserr_fail_raddrinfo(err, mesg, rai);
}
static void
@@ -256,7 +260,7 @@ rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
p = NUM2INT(protocol);
ret = rsock_socketpair(d, t, p, sp);
if (ret < 0) {
- rb_sys_fail("socketpair(2)");
+ rb_sys_fail("socketpair(2)");
}
s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
@@ -395,7 +399,7 @@ sock_connect(VALUE sock, VALUE addr)
fd = fptr->fd;
n = rsock_connect(fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, NULL);
if (n < 0) {
- rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai);
+ rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai);
}
return INT2FIX(n);
@@ -415,19 +419,19 @@ sock_connect_nonblock(VALUE sock, VALUE addr, VALUE ex)
rb_io_set_nonblock(fptr);
n = connect(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr));
if (n < 0) {
- int e = errno;
- if (e == EINPROGRESS) {
+ int e = errno;
+ if (e == EINPROGRESS) {
if (ex == Qfalse) {
return sym_wait_writable;
}
rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e, "connect(2) would block");
- }
- if (e == EISCONN) {
+ }
+ if (e == EISCONN) {
if (ex == Qfalse) {
return INT2FIX(0);
}
- }
- rsock_syserr_fail_raddrinfo_or_sockaddr(e, "connect(2)", addr, rai);
+ }
+ rsock_syserr_fail_raddrinfo_or_sockaddr(e, "connect(2)", addr, rai);
}
return INT2FIX(n);
@@ -528,7 +532,7 @@ sock_bind(VALUE sock, VALUE addr)
SockAddrStringValueWithAddrinfo(addr, rai);
GetOpenFile(sock, fptr);
if (bind(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr)) < 0)
- rsock_sys_fail_raddrinfo_or_sockaddr("bind(2)", addr, rai);
+ rsock_sys_fail_raddrinfo_or_sockaddr("bind(2)", addr, rai);
return INT2FIX(0);
}
@@ -612,7 +616,7 @@ rsock_sock_listen(VALUE sock, VALUE log)
backlog = NUM2INT(log);
GetOpenFile(sock, fptr);
if (listen(fptr->fd, backlog) < 0)
- rb_sys_fail("listen(2)");
+ rb_sys_fail("listen(2)");
return INT2FIX(0);
}
@@ -774,7 +778,7 @@ sock_accept_nonblock(VALUE sock, VALUE ex)
sock2 = rsock_s_accept_nonblock(rb_cSocket, ex, fptr, addr, &len);
if (SYMBOL_P(sock2)) /* :wait_readable */
- return sock2;
+ return sock2;
return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, &buf.addr, len));
}
@@ -855,19 +859,19 @@ sock_gethostname(VALUE obj)
name = rb_str_new(0, len);
while (gethostname(RSTRING_PTR(name), len) < 0) {
- int e = errno;
- switch (e) {
- case ENAMETOOLONG:
+ int e = errno;
+ switch (e) {
+ case ENAMETOOLONG:
#ifdef __linux__
- case EINVAL:
- /* glibc before version 2.1 uses EINVAL instead of ENAMETOOLONG */
+ case EINVAL:
+ /* glibc before version 2.1 uses EINVAL instead of ENAMETOOLONG */
#endif
- break;
- default:
- rb_syserr_fail(e, "gethostname(3)");
- }
- rb_str_modify_expand(name, len);
- len += len;
+ break;
+ default:
+ rb_syserr_fail(e, "gethostname(3)");
+ }
+ rb_str_modify_expand(name, len);
+ len += len;
}
rb_str_resize(name, strlen(RSTRING_PTR(name)));
return name;
@@ -897,18 +901,18 @@ make_addrinfo(struct rb_addrinfo *res0, int norevlookup)
struct addrinfo *res;
if (res0 == NULL) {
- rb_raise(rb_eSocket, "host not found");
+ rb_raise(rb_eSocket, "host not found");
}
base = rb_ary_new();
for (res = res0->ai; res; res = res->ai_next) {
- ary = rsock_ipaddr(res->ai_addr, res->ai_addrlen, norevlookup);
- if (res->ai_canonname) {
- RARRAY_ASET(ary, 2, rb_str_new2(res->ai_canonname));
- }
- rb_ary_push(ary, INT2FIX(res->ai_family));
- rb_ary_push(ary, INT2FIX(res->ai_socktype));
- rb_ary_push(ary, INT2FIX(res->ai_protocol));
- rb_ary_push(base, ary);
+ ary = rsock_ipaddr(res->ai_addr, res->ai_addrlen, norevlookup);
+ if (res->ai_canonname) {
+ RARRAY_ASET(ary, 2, rb_str_new2(res->ai_canonname));
+ }
+ rb_ary_push(ary, INT2FIX(res->ai_family));
+ rb_ary_push(ary, INT2FIX(res->ai_socktype));
+ rb_ary_push(ary, INT2FIX(res->ai_protocol));
+ rb_ary_push(base, ary);
}
return base;
}
@@ -920,18 +924,18 @@ sock_sockaddr(struct sockaddr *addr, socklen_t len)
switch (addr->sa_family) {
case AF_INET:
- ptr = (char*)&((struct sockaddr_in*)addr)->sin_addr.s_addr;
- len = (socklen_t)sizeof(((struct sockaddr_in*)addr)->sin_addr.s_addr);
- break;
+ ptr = (char*)&((struct sockaddr_in*)addr)->sin_addr.s_addr;
+ len = (socklen_t)sizeof(((struct sockaddr_in*)addr)->sin_addr.s_addr);
+ break;
#ifdef AF_INET6
case AF_INET6:
- ptr = (char*)&((struct sockaddr_in6*)addr)->sin6_addr.s6_addr;
- len = (socklen_t)sizeof(((struct sockaddr_in6*)addr)->sin6_addr.s6_addr);
- break;
+ ptr = (char*)&((struct sockaddr_in6*)addr)->sin6_addr.s6_addr;
+ len = (socklen_t)sizeof(((struct sockaddr_in6*)addr)->sin6_addr.s6_addr);
+ break;
#endif
default:
rb_raise(rb_eSocket, "unknown socket family:%d", addr->sa_family);
- break;
+ break;
}
return rb_str_new(ptr, len);
}
@@ -961,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);
return rsock_make_hostent(host, res, sock_sockaddr);
}
@@ -1004,20 +1008,20 @@ sock_s_gethostbyaddr(int argc, VALUE *argv, VALUE _)
rb_scan_args(argc, argv, "11", &addr, &family);
StringValue(addr);
if (!NIL_P(family)) {
- t = rsock_family_arg(family);
+ t = rsock_family_arg(family);
}
#ifdef AF_INET6
else if (RSTRING_LEN(addr) == 16) {
- t = AF_INET6;
+ t = AF_INET6;
}
#endif
h = gethostbyaddr(RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), t);
if (h == NULL) {
#ifdef HAVE_HSTRERROR
- extern int h_errno;
- rb_raise(rb_eSocket, "%s", (char*)hstrerror(h_errno));
+ extern int h_errno;
+ rb_raise(rb_eSocket, "%s", (char*)hstrerror(h_errno));
#else
- rb_raise(rb_eSocket, "host not found");
+ rb_raise(rb_eSocket, "host not found");
#endif
}
ary = rb_ary_new();
@@ -1025,14 +1029,14 @@ sock_s_gethostbyaddr(int argc, VALUE *argv, VALUE _)
names = rb_ary_new();
rb_ary_push(ary, names);
if (h->h_aliases != NULL) {
- for (pch = h->h_aliases; *pch; pch++) {
- rb_ary_push(names, rb_str_new2(*pch));
- }
+ for (pch = h->h_aliases; *pch; pch++) {
+ rb_ary_push(names, rb_str_new2(*pch));
+ }
}
rb_ary_push(ary, INT2NUM(h->h_addrtype));
#ifdef h_addr
for (pch = h->h_addr_list; *pch; pch++) {
- rb_ary_push(ary, rb_str_new(*pch, h->h_length));
+ rb_ary_push(ary, rb_str_new(*pch, h->h_length));
}
#else
rb_ary_push(ary, rb_str_new(h->h_addr, h->h_length));
@@ -1069,15 +1073,15 @@ sock_s_getservbyname(int argc, VALUE *argv, VALUE _)
if (!NIL_P(proto)) protoname = StringValueCStr(proto);
sp = getservbyname(servicename, protoname);
if (sp) {
- port = ntohs(sp->s_port);
+ port = ntohs(sp->s_port);
}
else {
- char *end;
+ char *end;
- port = STRTOUL(servicename, &end, 0);
- if (*end != '\0') {
- rb_raise(rb_eSocket, "no such service %s/%s", servicename, protoname);
- }
+ port = STRTOUL(servicename, &end, 0);
+ if (*end != '\0') {
+ rb_raise(rb_eSocket, "no such service %s/%s", servicename, protoname);
+ }
}
return INT2FIX(port);
}
@@ -1106,14 +1110,14 @@ sock_s_getservbyport(int argc, VALUE *argv, VALUE _)
rb_scan_args(argc, argv, "11", &port, &proto);
portnum = NUM2LONG(port);
if (portnum != (uint16_t)portnum) {
- const char *s = portnum > 0 ? "big" : "small";
- rb_raise(rb_eRangeError, "integer %ld too %s to convert into `int16_t'", portnum, s);
+ const char *s = portnum > 0 ? "big" : "small";
+ rb_raise(rb_eRangeError, "integer %ld too %s to convert into `int16_t'", portnum, s);
}
if (!NIL_P(proto)) protoname = StringValueCStr(proto);
sp = getservbyport((int)htons((uint16_t)portnum), protoname);
if (!sp) {
- rb_raise(rb_eSocket, "no such service for port %d/%s", (int)portnum, protoname);
+ rb_raise(rb_eSocket, "no such service for port %d/%s", (int)portnum, protoname);
}
return rb_str_new2(sp->s_name);
}
@@ -1167,16 +1171,16 @@ sock_s_getaddrinfo(int argc, VALUE *argv, VALUE _)
hints.ai_family = NIL_P(family) ? PF_UNSPEC : rsock_family_arg(family);
if (!NIL_P(socktype)) {
- hints.ai_socktype = rsock_socktype_arg(socktype);
+ hints.ai_socktype = rsock_socktype_arg(socktype);
}
if (!NIL_P(protocol)) {
- hints.ai_protocol = NUM2INT(protocol);
+ hints.ai_protocol = NUM2INT(protocol);
}
if (!NIL_P(flags)) {
- hints.ai_flags = NUM2INT(flags);
+ hints.ai_flags = NUM2INT(flags);
}
if (NIL_P(revlookup) || !rsock_revlookup_flag(revlookup, &norevlookup)) {
- norevlookup = rsock_do_not_reverse_lookup;
+ norevlookup = rsock_do_not_reverse_lookup;
}
res = rsock_getaddrinfo(host, port, &hints, 0);
@@ -1226,82 +1230,82 @@ sock_s_getnameinfo(int argc, VALUE *argv, VALUE _)
fl = 0;
if (!NIL_P(flags)) {
- fl = NUM2INT(flags);
+ fl = NUM2INT(flags);
}
tmp = rb_check_sockaddr_string_type(sa);
if (!NIL_P(tmp)) {
- sa = tmp;
- if (sizeof(ss) < (size_t)RSTRING_LEN(sa)) {
- rb_raise(rb_eTypeError, "sockaddr length too big");
- }
- memcpy(&ss, RSTRING_PTR(sa), RSTRING_LEN(sa));
+ sa = tmp;
+ if (sizeof(ss) < (size_t)RSTRING_LEN(sa)) {
+ rb_raise(rb_eTypeError, "sockaddr length too big");
+ }
+ memcpy(&ss, RSTRING_PTR(sa), RSTRING_LEN(sa));
if (!VALIDATE_SOCKLEN(&ss.addr, RSTRING_LEN(sa))) {
- rb_raise(rb_eTypeError, "sockaddr size differs - should not happen");
- }
- sap = &ss.addr;
+ rb_raise(rb_eTypeError, "sockaddr size differs - should not happen");
+ }
+ sap = &ss.addr;
salen = RSTRING_SOCKLEN(sa);
- goto call_nameinfo;
+ goto call_nameinfo;
}
tmp = rb_check_array_type(sa);
if (!NIL_P(tmp)) {
- sa = tmp;
- MEMZERO(&hints, struct addrinfo, 1);
- if (RARRAY_LEN(sa) == 3) {
- af = RARRAY_AREF(sa, 0);
- port = RARRAY_AREF(sa, 1);
- host = RARRAY_AREF(sa, 2);
- }
- else if (RARRAY_LEN(sa) >= 4) {
- af = RARRAY_AREF(sa, 0);
- port = RARRAY_AREF(sa, 1);
- host = RARRAY_AREF(sa, 3);
- if (NIL_P(host)) {
- host = RARRAY_AREF(sa, 2);
- }
- else {
- /*
- * 4th element holds numeric form, don't resolve.
- * see rsock_ipaddr().
- */
+ sa = tmp;
+ MEMZERO(&hints, struct addrinfo, 1);
+ if (RARRAY_LEN(sa) == 3) {
+ af = RARRAY_AREF(sa, 0);
+ port = RARRAY_AREF(sa, 1);
+ host = RARRAY_AREF(sa, 2);
+ }
+ else if (RARRAY_LEN(sa) >= 4) {
+ af = RARRAY_AREF(sa, 0);
+ port = RARRAY_AREF(sa, 1);
+ host = RARRAY_AREF(sa, 3);
+ if (NIL_P(host)) {
+ host = RARRAY_AREF(sa, 2);
+ }
+ else {
+ /*
+ * 4th element holds numeric form, don't resolve.
+ * see rsock_ipaddr().
+ */
#ifdef AI_NUMERICHOST /* AIX 4.3.3 doesn't have AI_NUMERICHOST. */
- hints.ai_flags |= AI_NUMERICHOST;
+ hints.ai_flags |= AI_NUMERICHOST;
#endif
- }
- }
- else {
- rb_raise(rb_eArgError, "array size should be 3 or 4, %ld given",
- RARRAY_LEN(sa));
- }
- hints.ai_socktype = (fl & NI_DGRAM) ? SOCK_DGRAM : SOCK_STREAM;
- /* af */
+ }
+ }
+ else {
+ rb_raise(rb_eArgError, "array size should be 3 or 4, %ld given",
+ RARRAY_LEN(sa));
+ }
+ 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);
- sap = res->ai->ai_addr;
+ res = rsock_getaddrinfo(host, port, &hints, 0);
+ sap = res->ai->ai_addr;
salen = res->ai->ai_addrlen;
}
else {
- rb_raise(rb_eTypeError, "expecting String or Array");
+ rb_raise(rb_eTypeError, "expecting String or Array");
}
call_nameinfo:
error = rb_getnameinfo(sap, salen, hbuf, sizeof(hbuf),
- pbuf, sizeof(pbuf), fl);
+ pbuf, sizeof(pbuf), fl);
if (error) goto error_exit_name;
if (res) {
- for (r = res->ai->ai_next; r; r = r->ai_next) {
- char hbuf2[1024], pbuf2[1024];
+ for (r = res->ai->ai_next; r; r = r->ai_next) {
+ char hbuf2[1024], pbuf2[1024];
- sap = r->ai_addr;
+ sap = r->ai_addr;
salen = r->ai_addrlen;
- error = rb_getnameinfo(sap, salen, hbuf2, sizeof(hbuf2),
- pbuf2, sizeof(pbuf2), fl);
- if (error) goto error_exit_name;
- if (strcmp(hbuf, hbuf2) != 0|| strcmp(pbuf, pbuf2) != 0) {
- rb_freeaddrinfo(res);
- rb_raise(rb_eSocket, "sockaddr resolved to multiple nodename");
- }
- }
- rb_freeaddrinfo(res);
+ error = rb_getnameinfo(sap, salen, hbuf2, sizeof(hbuf2),
+ pbuf2, sizeof(pbuf2), fl);
+ if (error) goto error_exit_name;
+ if (strcmp(hbuf, hbuf2) != 0|| strcmp(pbuf, pbuf2) != 0) {
+ rb_freeaddrinfo(res);
+ rb_raise(rb_eSocket, "sockaddr resolved to multiple nodename");
+ }
+ }
+ rb_freeaddrinfo(res);
}
return rb_assoc_new(rb_str_new2(hbuf), rb_str_new2(pbuf));
@@ -1379,7 +1383,7 @@ sock_s_unpack_sockaddr_in(VALUE self, VALUE addr)
return rb_assoc_new(INT2NUM(ntohs(sockaddr->sin_port)), host);
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* call-seq:
@@ -1437,8 +1441,8 @@ sock_s_unpack_sockaddr_un(VALUE self, VALUE addr)
rb_raise(rb_eArgError, "not an AF_UNIX sockaddr");
}
if (sizeof(struct sockaddr_un) < (size_t)RSTRING_LEN(addr)) {
- rb_raise(rb_eTypeError, "too long sockaddr_un - %ld longer than %d",
- RSTRING_LEN(addr), (int)sizeof(struct sockaddr_un));
+ rb_raise(rb_eTypeError, "too long sockaddr_un - %ld longer than %d",
+ RSTRING_LEN(addr), (int)sizeof(struct sockaddr_un));
}
path = rsock_unixpath_str(sockaddr, RSTRING_SOCKLEN(addr));
return path;
@@ -1467,7 +1471,7 @@ sockaddr_len(struct sockaddr *addr)
return (socklen_t)sizeof(struct sockaddr_in6);
#endif
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX:
return (socklen_t)sizeof(struct sockaddr_un);
#endif
@@ -1502,19 +1506,19 @@ sockaddr_obj(struct sockaddr *addr, socklen_t len)
#if defined(__KAME__) && defined(AF_INET6)
if (addr->sa_family == AF_INET6) {
- /* KAME uses the 2nd 16bit word of link local IPv6 address as interface index internally */
+ /* KAME uses the 2nd 16bit word of link local IPv6 address as interface index internally */
/* http://orange.kame.net/dev/cvsweb.cgi/kame/IMPLEMENTATION */
- /* convert fe80:1::1 to fe80::1%1 */
+ /* convert fe80:1::1 to fe80::1%1 */
len = (socklen_t)sizeof(struct sockaddr_in6);
- memcpy(&addr6, addr, len);
- addr = (struct sockaddr *)&addr6;
- if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) &&
- addr6.sin6_scope_id == 0 &&
- (addr6.sin6_addr.s6_addr[2] || addr6.sin6_addr.s6_addr[3])) {
- addr6.sin6_scope_id = (addr6.sin6_addr.s6_addr[2] << 8) | addr6.sin6_addr.s6_addr[3];
- addr6.sin6_addr.s6_addr[2] = 0;
- addr6.sin6_addr.s6_addr[3] = 0;
- }
+ memcpy(&addr6, addr, len);
+ addr = (struct sockaddr *)&addr6;
+ if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) &&
+ addr6.sin6_scope_id == 0 &&
+ (addr6.sin6_addr.s6_addr[2] || addr6.sin6_addr.s6_addr[3])) {
+ addr6.sin6_scope_id = (addr6.sin6_addr.s6_addr[2] << 8) | addr6.sin6_addr.s6_addr[3];
+ addr6.sin6_addr.s6_addr[2] = 0;
+ addr6.sin6_addr.s6_addr[3] = 0;
+ }
}
#endif
@@ -1612,8 +1616,8 @@ socket_s_ip_address_list(VALUE self)
ret = ioctl(fd, SIOCGLIFNUM, &ln);
if (ret == -1) {
- reason = "SIOCGLIFNUM";
- goto finish;
+ reason = "SIOCGLIFNUM";
+ goto finish;
}
memset(&lc, 0, sizeof(lc));
@@ -1624,13 +1628,13 @@ socket_s_ip_address_list(VALUE self)
ret = ioctl(fd, SIOCGLIFCONF, &lc);
if (ret == -1) {
- reason = "SIOCGLIFCONF";
- goto finish;
+ reason = "SIOCGLIFCONF";
+ goto finish;
}
list = rb_ary_new();
for (i = 0; i < ln.lifn_count; i++) {
- struct lifreq *req = &lc.lifc_req[i];
+ struct lifreq *req = &lc.lifc_req[i];
if (IS_IP_FAMILY(req->lifr_addr.ss_family)) {
if (req->lifr_addr.ss_family == AF_INET6 &&
IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_addr) &&
@@ -1651,13 +1655,13 @@ socket_s_ip_address_list(VALUE self)
finish:
save_errno = errno;
if (lc.lifc_buf != NULL)
- xfree(lc.lifc_req);
+ xfree(lc.lifc_req);
if (fd != -1)
- close(fd);
+ close(fd);
errno = save_errno;
if (reason)
- rb_syserr_fail(save_errno, reason);
+ rb_syserr_fail(save_errno, reason);
return list;
#elif defined(SIOCGIFCONF)
@@ -1695,17 +1699,17 @@ socket_s_ip_address_list(VALUE self)
/* fprintf(stderr, "conf.ifc_len: %d\n", conf.ifc_len); */
if (bufsize - EXTRA_SPACE < conf.ifc_len) {
- if (bufsize < conf.ifc_len) {
- /* NetBSD returns required size for all interfaces. */
- bufsize = conf.ifc_len + EXTRA_SPACE;
- }
- else {
- bufsize = bufsize << 1;
- }
- if (buf == initbuf)
- buf = NULL;
- buf = xrealloc(buf, bufsize);
- goto retry;
+ if (bufsize < conf.ifc_len) {
+ /* NetBSD returns required size for all interfaces. */
+ bufsize = conf.ifc_len + EXTRA_SPACE;
+ }
+ else {
+ bufsize = bufsize << 1;
+ }
+ if (buf == initbuf)
+ buf = NULL;
+ buf = xrealloc(buf, bufsize);
+ goto retry;
}
close(fd);
@@ -1714,10 +1718,10 @@ socket_s_ip_address_list(VALUE self)
list = rb_ary_new();
req = conf.ifc_req;
while ((char*)req < (char*)conf.ifc_req + conf.ifc_len) {
- struct sockaddr *addr = &req->ifr_addr;
+ struct sockaddr *addr = &req->ifr_addr;
if (IS_IP_FAMILY(addr->sa_family)) {
- rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
- }
+ rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
+ }
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
# ifndef _SIZEOF_ADDR_IFREQ
# define _SIZEOF_ADDR_IFREQ(r) \
@@ -1726,9 +1730,9 @@ socket_s_ip_address_list(VALUE self)
(r).ifr_addr.sa_len - sizeof(struct sockaddr) : \
0))
# endif
- req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req));
+ req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req));
#else
- req = (struct ifreq *)((char*)req + sizeof(struct ifreq));
+ req = (struct ifreq *)((char*)req + sizeof(struct ifreq));
#endif
}
@@ -1738,57 +1742,57 @@ socket_s_ip_address_list(VALUE self)
if (buf != initbuf)
xfree(buf);
if (fd != -1)
- close(fd);
+ close(fd);
errno = save_errno;
if (reason)
- rb_syserr_fail(save_errno, reason);
+ rb_syserr_fail(save_errno, reason);
return list;
#undef EXTRA_SPACE
#elif defined(_WIN32)
typedef struct ip_adapter_unicast_address_st {
- unsigned LONG_LONG dummy0;
- struct ip_adapter_unicast_address_st *Next;
- struct {
- struct sockaddr *lpSockaddr;
- int iSockaddrLength;
- } Address;
- int dummy1;
- int dummy2;
- int dummy3;
- long dummy4;
- long dummy5;
- long dummy6;
+ unsigned LONG_LONG dummy0;
+ struct ip_adapter_unicast_address_st *Next;
+ struct {
+ struct sockaddr *lpSockaddr;
+ int iSockaddrLength;
+ } Address;
+ int dummy1;
+ int dummy2;
+ int dummy3;
+ long dummy4;
+ long dummy5;
+ long dummy6;
} ip_adapter_unicast_address_t;
typedef struct ip_adapter_anycast_address_st {
- unsigned LONG_LONG dummy0;
- struct ip_adapter_anycast_address_st *Next;
- struct {
- struct sockaddr *lpSockaddr;
- int iSockaddrLength;
- } Address;
+ unsigned LONG_LONG dummy0;
+ struct ip_adapter_anycast_address_st *Next;
+ struct {
+ struct sockaddr *lpSockaddr;
+ int iSockaddrLength;
+ } Address;
} ip_adapter_anycast_address_t;
typedef struct ip_adapter_addresses_st {
- unsigned LONG_LONG dummy0;
- struct ip_adapter_addresses_st *Next;
- void *dummy1;
- ip_adapter_unicast_address_t *FirstUnicastAddress;
- ip_adapter_anycast_address_t *FirstAnycastAddress;
- void *dummy2;
- void *dummy3;
- void *dummy4;
- void *dummy5;
- void *dummy6;
- BYTE dummy7[8];
- DWORD dummy8;
- DWORD dummy9;
- DWORD dummy10;
- DWORD IfType;
- int OperStatus;
- DWORD dummy12;
- DWORD dummy13[16];
- void *dummy14;
+ unsigned LONG_LONG dummy0;
+ struct ip_adapter_addresses_st *Next;
+ void *dummy1;
+ ip_adapter_unicast_address_t *FirstUnicastAddress;
+ ip_adapter_anycast_address_t *FirstAnycastAddress;
+ void *dummy2;
+ void *dummy3;
+ void *dummy4;
+ void *dummy5;
+ void *dummy6;
+ BYTE dummy7[8];
+ DWORD dummy8;
+ DWORD dummy9;
+ DWORD dummy10;
+ DWORD IfType;
+ int OperStatus;
+ DWORD dummy12;
+ DWORD dummy13[16];
+ void *dummy14;
} ip_adapter_addresses_t;
typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG, ULONG, PVOID, ip_adapter_addresses_t *, PULONG);
HMODULE h;
@@ -1800,49 +1804,49 @@ socket_s_ip_address_list(VALUE self)
h = LoadLibrary("iphlpapi.dll");
if (!h)
- rb_notimplement();
+ rb_notimplement();
pGetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress(h, "GetAdaptersAddresses");
if (!pGetAdaptersAddresses) {
- FreeLibrary(h);
- rb_notimplement();
+ FreeLibrary(h);
+ rb_notimplement();
}
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &len);
if (ret != ERROR_SUCCESS && ret != ERROR_BUFFER_OVERFLOW) {
- errno = rb_w32_map_errno(ret);
- FreeLibrary(h);
- rb_sys_fail("GetAdaptersAddresses");
+ errno = rb_w32_map_errno(ret);
+ FreeLibrary(h);
+ rb_sys_fail("GetAdaptersAddresses");
}
adapters = (ip_adapter_addresses_t *)ALLOCA_N(BYTE, len);
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapters, &len);
if (ret != ERROR_SUCCESS) {
- errno = rb_w32_map_errno(ret);
- FreeLibrary(h);
- rb_sys_fail("GetAdaptersAddresses");
+ errno = rb_w32_map_errno(ret);
+ FreeLibrary(h);
+ rb_sys_fail("GetAdaptersAddresses");
}
list = rb_ary_new();
for (; adapters; adapters = adapters->Next) {
- ip_adapter_unicast_address_t *uni;
- ip_adapter_anycast_address_t *any;
- if (adapters->OperStatus != 1) /* 1 means IfOperStatusUp */
- continue;
- for (uni = adapters->FirstUnicastAddress; uni; uni = uni->Next) {
+ ip_adapter_unicast_address_t *uni;
+ ip_adapter_anycast_address_t *any;
+ if (adapters->OperStatus != 1) /* 1 means IfOperStatusUp */
+ continue;
+ for (uni = adapters->FirstUnicastAddress; uni; uni = uni->Next) {
#ifndef INET6
- if (uni->Address.lpSockaddr->sa_family == AF_INET)
+ if (uni->Address.lpSockaddr->sa_family == AF_INET)
#else
- if (IS_IP_FAMILY(uni->Address.lpSockaddr->sa_family))
+ if (IS_IP_FAMILY(uni->Address.lpSockaddr->sa_family))
#endif
- rb_ary_push(list, sockaddr_obj(uni->Address.lpSockaddr, uni->Address.iSockaddrLength));
- }
- for (any = adapters->FirstAnycastAddress; any; any = any->Next) {
+ rb_ary_push(list, sockaddr_obj(uni->Address.lpSockaddr, uni->Address.iSockaddrLength));
+ }
+ for (any = adapters->FirstAnycastAddress; any; any = any->Next) {
#ifndef INET6
- if (any->Address.lpSockaddr->sa_family == AF_INET)
+ if (any->Address.lpSockaddr->sa_family == AF_INET)
#else
- if (IS_IP_FAMILY(any->Address.lpSockaddr->sa_family))
+ if (IS_IP_FAMILY(any->Address.lpSockaddr->sa_family))
#endif
- rb_ary_push(list, sockaddr_obj(any->Address.lpSockaddr, any->Address.iSockaddrLength));
- }
+ rb_ary_push(list, sockaddr_obj(any->Address.lpSockaddr, any->Address.iSockaddrLength));
+ }
}
FreeLibrary(h);
@@ -1986,7 +1990,7 @@ Init_socket(void)
/* for ext/socket/lib/socket.rb use only: */
rb_define_private_method(rb_cSocket,
- "__connect_nonblock", sock_connect_nonblock, 2);
+ "__connect_nonblock", sock_connect_nonblock, 2);
rb_define_method(rb_cSocket, "bind", sock_bind, 1);
rb_define_method(rb_cSocket, "listen", rsock_sock_listen, 1);
@@ -1994,7 +1998,7 @@ Init_socket(void)
/* for ext/socket/lib/socket.rb use only: */
rb_define_private_method(rb_cSocket,
- "__accept_nonblock", sock_accept_nonblock, 1);
+ "__accept_nonblock", sock_accept_nonblock, 1);
rb_define_method(rb_cSocket, "sysaccept", sock_sysaccept, 0);
@@ -2002,7 +2006,7 @@ Init_socket(void)
/* for ext/socket/lib/socket.rb use only: */
rb_define_private_method(rb_cSocket,
- "__recvfrom_nonblock", sock_recvfrom_nonblock, 4);
+ "__recvfrom_nonblock", sock_recvfrom_nonblock, 4);
rb_define_singleton_method(rb_cSocket, "socketpair", rsock_sock_s_socketpair, -1);
rb_define_singleton_method(rb_cSocket, "pair", rsock_sock_s_socketpair, -1);
@@ -2016,7 +2020,7 @@ Init_socket(void)
rb_define_singleton_method(rb_cSocket, "sockaddr_in", sock_s_pack_sockaddr_in, 2);
rb_define_singleton_method(rb_cSocket, "pack_sockaddr_in", sock_s_pack_sockaddr_in, 2);
rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_in", sock_s_unpack_sockaddr_in, 1);
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
rb_define_singleton_method(rb_cSocket, "sockaddr_un", sock_s_pack_sockaddr_un, 1);
rb_define_singleton_method(rb_cSocket, "pack_sockaddr_un", sock_s_pack_sockaddr_un, 1);
rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_un", sock_s_unpack_sockaddr_un, 1);
diff --git a/ext/socket/sockssocket.c b/ext/socket/sockssocket.c
index b8b7e12998..f263ac3804 100644
--- a/ext/socket/sockssocket.c
+++ b/ext/socket/sockssocket.c
@@ -30,8 +30,8 @@ socks_init(VALUE sock, VALUE host, VALUE port)
static int init = 0;
if (init == 0) {
- SOCKSinit("ruby");
- init = 1;
+ SOCKSinit("ruby");
+ init = 1;
}
return rsock_init_inetsock(sock, host, port, Qnil, Qnil, INET_SOCKS, Qnil, Qnil);
diff --git a/ext/socket/tcpserver.c b/ext/socket/tcpserver.c
index 675733c6f9..04e5a0bb51 100644
--- a/ext/socket/tcpserver.c
+++ b/ext/socket/tcpserver.c
@@ -133,7 +133,7 @@ rsock_init_tcpserver(void)
rb_cTCPServer = rb_define_class("TCPServer", rb_cTCPSocket);
rb_define_method(rb_cTCPServer, "accept", tcp_accept, 0);
rb_define_private_method(rb_cTCPServer,
- "__accept_nonblock", tcp_accept_nonblock, 1);
+ "__accept_nonblock", tcp_accept_nonblock, 1);
rb_define_method(rb_cTCPServer, "sysaccept", tcp_sysaccept, 0);
rb_define_method(rb_cTCPServer, "initialize", tcp_svr_init, -1);
rb_define_method(rb_cTCPServer, "listen", rsock_sock_listen, 1); /* in socket.c */
diff --git a/ext/socket/tcpsocket.c b/ext/socket/tcpsocket.c
index 51e77a0de9..03787272f3 100644
--- a/ext/socket/tcpsocket.c
+++ b/ext/socket/tcpsocket.c
@@ -32,22 +32,22 @@ tcp_init(int argc, VALUE *argv, VALUE sock)
VALUE connect_timeout = Qnil;
if (!keyword_ids[0]) {
- CONST_ID(keyword_ids[0], "resolv_timeout");
- CONST_ID(keyword_ids[1], "connect_timeout");
+ CONST_ID(keyword_ids[0], "resolv_timeout");
+ CONST_ID(keyword_ids[1], "connect_timeout");
}
rb_scan_args(argc, argv, "22:", &remote_host, &remote_serv,
- &local_host, &local_serv, &opt);
+ &local_host, &local_serv, &opt);
if (!NIL_P(opt)) {
- rb_get_kwargs(opt, keyword_ids, 0, 2, kwargs);
- if (kwargs[0] != Qundef) { resolv_timeout = kwargs[0]; }
- if (kwargs[1] != Qundef) { connect_timeout = kwargs[1]; }
+ rb_get_kwargs(opt, keyword_ids, 0, 2, kwargs);
+ if (kwargs[0] != Qundef) { resolv_timeout = kwargs[0]; }
+ if (kwargs[1] != Qundef) { connect_timeout = kwargs[1]; }
}
return rsock_init_inetsock(sock, remote_host, remote_serv,
- local_host, local_serv, INET_CLIENT,
- resolv_timeout, connect_timeout);
+ local_host, local_serv, INET_CLIENT,
+ resolv_timeout, connect_timeout);
}
static VALUE
@@ -80,7 +80,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);
return rsock_make_hostent(host, res, tcp_sockaddr);
}
diff --git a/ext/socket/udpsocket.c b/ext/socket/udpsocket.c
index 2bfd7c8560..5224e48a96 100644
--- a/ext/socket/udpsocket.c
+++ b/ext/socket/udpsocket.c
@@ -33,11 +33,11 @@ udp_init(int argc, VALUE *argv, VALUE sock)
int fd;
if (rb_scan_args(argc, argv, "01", &arg) == 1) {
- family = rsock_family_arg(arg);
+ family = rsock_family_arg(arg);
}
fd = rsock_socket(family, SOCK_DGRAM, 0);
if (fd < 0) {
- rb_sys_fail("socket(2) - udp");
+ rb_sys_fail("socket(2) - udp");
}
return rsock_init_sock(sock, fd);
@@ -60,9 +60,9 @@ udp_connect_internal(VALUE v)
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) {
- return Qtrue;
- }
+ if (rsock_connect(fd, res->ai_addr, res->ai_addrlen, 0, NULL) >= 0) {
+ return Qtrue;
+ }
}
return Qfalse;
}
@@ -92,7 +92,7 @@ udp_connect(VALUE sock, VALUE host, VALUE 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);
+ rsock_freeaddrinfo, (VALUE)arg.res);
if (!ret) rsock_sys_fail_host_port("connect(2)", host, port);
return INT2FIX(0);
}
@@ -108,10 +108,10 @@ udp_bind_internal(VALUE v)
rb_io_check_closed(fptr = arg->fptr);
fd = fptr->fd;
for (res = arg->res->ai; res; res = res->ai_next) {
- if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) {
- continue;
- }
- return Qtrue;
+ if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) {
+ continue;
+ }
+ return Qtrue;
}
return Qfalse;
}
@@ -137,7 +137,7 @@ udp_bind(VALUE sock, VALUE host, VALUE 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);
+ rsock_freeaddrinfo, (VALUE)arg.res);
if (!ret) rsock_sys_fail_host_port("bind(2)", host, port);
return INT2FIX(0);
}
@@ -170,7 +170,7 @@ udp_send_internal(VALUE v)
if (n >= 0) return RB_SSIZE2NUM(n);
- if (rb_io_maybe_wait_writable(errno, fptr->self, Qnil)) {
+ if (rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
goto retry;
}
}
@@ -207,7 +207,7 @@ udp_send(int argc, VALUE *argv, VALUE sock)
VALUE ret;
if (argc == 2 || argc == 3) {
- return rsock_bsock_send(argc, argv, sock);
+ return rsock_bsock_send(argc, argv, sock);
}
rb_scan_args(argc, argv, "4", &arg.sarg.mesg, &flags, &host, &port);
@@ -217,7 +217,7 @@ udp_send(int argc, VALUE *argv, VALUE sock)
arg.sarg.flags = NUM2INT(flags);
arg.res = rsock_addrinfo(host, port, rsock_fd_family(arg.fptr->fd), SOCK_DGRAM, 0);
ret = rb_ensure(udp_send_internal, (VALUE)&arg,
- rsock_freeaddrinfo, (VALUE)arg.res);
+ rsock_freeaddrinfo, (VALUE)arg.res);
if (!ret) rsock_sys_fail_host_port("sendto(2)", host, port);
return ret;
}
@@ -246,5 +246,5 @@ rsock_init_udpsocket(void)
/* for ext/socket/lib/socket.rb use only: */
rb_define_private_method(rb_cUDPSocket,
- "__recvfrom_nonblock", udp_recvfrom_nonblock, 4);
+ "__recvfrom_nonblock", udp_recvfrom_nonblock, 4);
}
diff --git a/ext/socket/unixserver.c b/ext/socket/unixserver.c
index 890f9d3fae..0ea5ac083c 100644
--- a/ext/socket/unixserver.c
+++ b/ext/socket/unixserver.c
@@ -10,7 +10,7 @@
#include "rubysocket.h"
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* call-seq:
* UNIXServer.new(path) => unixserver
@@ -66,7 +66,7 @@ unix_accept_nonblock(VALUE sock, VALUE ex)
GetOpenFile(sock, fptr);
fromlen = (socklen_t)sizeof(from);
return rsock_s_accept_nonblock(rb_cUNIXSocket, ex, fptr,
- (struct sockaddr *)&from, &fromlen);
+ (struct sockaddr *)&from, &fromlen);
}
/*
@@ -101,7 +101,7 @@ unix_sysaccept(VALUE server)
void
rsock_init_unixserver(void)
{
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* Document-class: UNIXServer < UNIXSocket
*
@@ -113,7 +113,7 @@ rsock_init_unixserver(void)
rb_define_method(rb_cUNIXServer, "accept", unix_accept, 0);
rb_define_private_method(rb_cUNIXServer,
- "__accept_nonblock", unix_accept_nonblock, 1);
+ "__accept_nonblock", unix_accept_nonblock, 1);
rb_define_method(rb_cUNIXServer, "sysaccept", unix_sysaccept, 0);
rb_define_method(rb_cUNIXServer, "listen", rsock_sock_listen, 1); /* in socket.c */
diff --git a/ext/socket/unixsocket.c b/ext/socket/unixsocket.c
index 857cfa6002..26ab76fc9f 100644
--- a/ext/socket/unixsocket.c
+++ b/ext/socket/unixsocket.c
@@ -10,7 +10,7 @@
#include "rubysocket.h"
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
struct unixsock_arg {
struct sockaddr_un *sockaddr;
socklen_t sockaddrlen;
@@ -22,7 +22,7 @@ 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);
+ arg->sockaddrlen, 0, NULL);
}
static VALUE
@@ -43,6 +43,10 @@ unixsock_path_value(VALUE path)
}
}
#endif
+#ifdef _WIN32
+ /* UNIXSocket requires UTF-8 per spec. */
+ path = rb_str_export_to_enc(path, rb_utf8_encoding());
+#endif
return rb_get_path(path);
}
@@ -66,42 +70,42 @@ rsock_init_unixsock(VALUE sock, VALUE path, int server)
fd = rsock_socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
- rsock_sys_fail_path("socket(2)", path);
+ rsock_sys_fail_path("socket(2)", path);
}
if (server) {
status = bind(fd, (struct sockaddr*)&sockaddr, sockaddrlen);
}
else {
- int prot;
- struct unixsock_arg arg;
- arg.sockaddr = &sockaddr;
- arg.sockaddrlen = sockaddrlen;
- arg.fd = fd;
+ int prot;
+ 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);
- }
+ if (prot) {
+ close(fd);
+ rb_jump_tag(prot);
+ }
}
if (status < 0) {
- int e = errno;
- close(fd);
- rsock_syserr_fail_path(e, "connect(2)", path);
+ int e = errno;
+ close(fd);
+ rsock_syserr_fail_path(e, "connect(2)", path);
}
if (server) {
- if (listen(fd, SOMAXCONN) < 0) {
- int e = errno;
- close(fd);
- rsock_syserr_fail_path(e, "listen(2)", path);
- }
+ if (listen(fd, SOMAXCONN) < 0) {
+ int e = errno;
+ close(fd);
+ rsock_syserr_fail_path(e, "listen(2)", path);
+ }
}
rsock_init_sock(sock, fd);
if (server) {
- GetOpenFile(sock, fptr);
+ GetOpenFile(sock, fptr);
fptr->pathv = rb_str_new_frozen(path);
}
@@ -143,13 +147,13 @@ unix_path(VALUE sock)
GetOpenFile(sock, fptr);
if (NIL_P(fptr->pathv)) {
- struct sockaddr_un addr;
- socklen_t len = (socklen_t)sizeof(addr);
- socklen_t len0 = len;
- if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
+ struct sockaddr_un addr;
+ socklen_t len = (socklen_t)sizeof(addr);
+ socklen_t len0 = len;
+ if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
rsock_sys_fail_path("getsockname(2)", fptr->pathv);
if (len0 < len) len = len0;
- fptr->pathv = rb_obj_freeze(rsock_unixpath_str(&addr, len));
+ fptr->pathv = rb_obj_freeze(rsock_unixpath_str(&addr, len));
}
return rb_str_dup(fptr->pathv);
}
@@ -240,21 +244,21 @@ unix_send_io(VALUE sock, VALUE val)
#if FD_PASSING_BY_MSG_CONTROL
union {
- struct cmsghdr hdr;
- char pad[sizeof(struct cmsghdr)+8+sizeof(int)+8];
+ struct cmsghdr hdr;
+ char pad[sizeof(struct cmsghdr)+8+sizeof(int)+8];
} cmsg;
#endif
if (rb_obj_is_kind_of(val, rb_cIO)) {
rb_io_t *valfptr;
- GetOpenFile(val, valfptr);
- fd = valfptr->fd;
+ GetOpenFile(val, valfptr);
+ fd = valfptr->fd;
}
else if (FIXNUM_P(val)) {
fd = FIX2INT(val);
}
else {
- rb_raise(rb_eTypeError, "neither IO nor file descriptor");
+ rb_raise(rb_eTypeError, "neither IO nor file descriptor");
}
GetOpenFile(sock, fptr);
@@ -285,8 +289,8 @@ unix_send_io(VALUE sock, VALUE val)
arg.fd = fptr->fd;
while ((int)BLOCKING_REGION_FD(sendmsg_blocking, &arg) == -1) {
- if (!rb_io_wait_writable(arg.fd))
- rsock_sys_fail_path("sendmsg(2)", fptr->pathv);
+ if (!rb_io_wait_writable(arg.fd))
+ rsock_sys_fail_path("sendmsg(2)", fptr->pathv);
}
return Qnil;
@@ -348,16 +352,16 @@ unix_recv_io(int argc, VALUE *argv, VALUE sock)
int fd;
#if FD_PASSING_BY_MSG_CONTROL
union {
- struct cmsghdr hdr;
- char pad[sizeof(struct cmsghdr)+8+sizeof(int)+8];
+ struct cmsghdr hdr;
+ char pad[sizeof(struct cmsghdr)+8+sizeof(int)+8];
} cmsg;
#endif
rb_scan_args(argc, argv, "02", &klass, &mode);
if (argc == 0)
- klass = rb_cIO;
+ klass = rb_cIO;
if (argc <= 1)
- mode = Qnil;
+ mode = Qnil;
retry:
GetOpenFile(sock, fptr);
@@ -400,8 +404,8 @@ retry:
rb_gc_for_fd(e);
goto retry;
}
- if (!rb_io_wait_readable(arg.fd))
- rsock_syserr_fail_path(e, "recvmsg(2)", fptr->pathv);
+ if (!rb_io_wait_readable(arg.fd))
+ rsock_syserr_fail_path(e, "recvmsg(2)", fptr->pathv);
}
#if FD_PASSING_BY_MSG_CONTROL
@@ -412,41 +416,41 @@ retry:
rb_gc_for_fd(EMFILE);
goto retry;
}
- rb_raise(rb_eSocket,
- "file descriptor was not passed (msg_controllen=%d smaller than sizeof(struct cmsghdr)=%d)",
- (int)arg.msg.msg_controllen, (int)sizeof(struct cmsghdr));
+ rb_raise(rb_eSocket,
+ "file descriptor was not passed (msg_controllen=%d smaller than sizeof(struct cmsghdr)=%d)",
+ (int)arg.msg.msg_controllen, (int)sizeof(struct cmsghdr));
}
if (cmsg.hdr.cmsg_level != SOL_SOCKET) {
- rb_raise(rb_eSocket,
- "file descriptor was not passed (cmsg_level=%d, %d expected)",
- cmsg.hdr.cmsg_level, SOL_SOCKET);
+ rb_raise(rb_eSocket,
+ "file descriptor was not passed (cmsg_level=%d, %d expected)",
+ cmsg.hdr.cmsg_level, SOL_SOCKET);
}
if (cmsg.hdr.cmsg_type != SCM_RIGHTS) {
- rb_raise(rb_eSocket,
- "file descriptor was not passed (cmsg_type=%d, %d expected)",
- cmsg.hdr.cmsg_type, SCM_RIGHTS);
+ rb_raise(rb_eSocket,
+ "file descriptor was not passed (cmsg_type=%d, %d expected)",
+ cmsg.hdr.cmsg_type, SCM_RIGHTS);
}
if (arg.msg.msg_controllen < (socklen_t)CMSG_LEN(sizeof(int))) {
- rb_raise(rb_eSocket,
- "file descriptor was not passed (msg_controllen=%d smaller than CMSG_LEN(sizeof(int))=%d)",
- (int)arg.msg.msg_controllen, (int)CMSG_LEN(sizeof(int)));
+ rb_raise(rb_eSocket,
+ "file descriptor was not passed (msg_controllen=%d smaller than CMSG_LEN(sizeof(int))=%d)",
+ (int)arg.msg.msg_controllen, (int)CMSG_LEN(sizeof(int)));
}
if ((socklen_t)CMSG_SPACE(sizeof(int)) < arg.msg.msg_controllen) {
- rb_raise(rb_eSocket,
- "file descriptor was not passed (msg_controllen=%d bigger than CMSG_SPACE(sizeof(int))=%d)",
- (int)arg.msg.msg_controllen, (int)CMSG_SPACE(sizeof(int)));
+ rb_raise(rb_eSocket,
+ "file descriptor was not passed (msg_controllen=%d bigger than CMSG_SPACE(sizeof(int))=%d)",
+ (int)arg.msg.msg_controllen, (int)CMSG_SPACE(sizeof(int)));
}
if (cmsg.hdr.cmsg_len != CMSG_LEN(sizeof(int))) {
- rsock_discard_cmsg_resource(&arg.msg, 0);
- rb_raise(rb_eSocket,
- "file descriptor was not passed (cmsg_len=%d, %d expected)",
- (int)cmsg.hdr.cmsg_len, (int)CMSG_LEN(sizeof(int)));
+ rsock_discard_cmsg_resource(&arg.msg, 0);
+ rb_raise(rb_eSocket,
+ "file descriptor was not passed (cmsg_len=%d, %d expected)",
+ (int)cmsg.hdr.cmsg_len, (int)CMSG_LEN(sizeof(int)));
}
#else
if (arg.msg.msg_accrightslen != sizeof(fd)) {
- rb_raise(rb_eSocket,
- "file descriptor was not passed (accrightslen=%d, %d expected)",
- arg.msg.msg_accrightslen, (int)sizeof(fd));
+ rb_raise(rb_eSocket,
+ "file descriptor was not passed (accrightslen=%d, %d expected)",
+ arg.msg.msg_accrightslen, (int)sizeof(fd));
}
#endif
@@ -458,15 +462,15 @@ retry:
rb_maygvl_fd_fix_cloexec(fd);
if (klass == Qnil)
- return INT2FIX(fd);
+ return INT2FIX(fd);
else {
- ID for_fd;
- int ff_argc;
- VALUE ff_argv[2];
- CONST_ID(for_fd, "for_fd");
- ff_argc = mode == Qnil ? 1 : 2;
- ff_argv[0] = INT2FIX(fd);
- ff_argv[1] = mode;
+ ID for_fd;
+ int ff_argc;
+ VALUE ff_argv[2];
+ CONST_ID(for_fd, "for_fd");
+ ff_argc = mode == Qnil ? 1 : 2;
+ ff_argv[0] = INT2FIX(fd);
+ ff_argv[1] = mode;
return rb_funcallv(klass, for_fd, ff_argc, ff_argv);
}
}
@@ -556,9 +560,9 @@ unix_s_socketpair(int argc, VALUE *argv, VALUE klass)
domain = INT2FIX(PF_UNIX);
rb_scan_args(argc, argv, "02", &type, &protocol);
if (argc == 0)
- type = INT2FIX(SOCK_STREAM);
+ type = INT2FIX(SOCK_STREAM);
if (argc <= 1)
- protocol = INT2FIX(0);
+ protocol = INT2FIX(0);
args[0] = domain;
args[1] = type;
@@ -571,7 +575,7 @@ unix_s_socketpair(int argc, VALUE *argv, VALUE klass)
void
rsock_init_unixsocket(void)
{
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* Document-class: UNIXSocket < BasicSocket
*
diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c
index 9ba2cd92a1..0054766dac 100644
--- a/ext/stringio/stringio.c
+++ b/ext/stringio/stringio.c
@@ -12,7 +12,7 @@
**********************************************************************/
-#define STRINGIO_VERSION "3.0.3"
+#define STRINGIO_VERSION "3.0.4"
#include "ruby.h"
#include "ruby/io.h"
@@ -257,9 +257,20 @@ strio_s_allocate(VALUE klass)
}
/*
- * call-seq: StringIO.new(string=""[, mode])
+ * 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]:
+ *
+ * strio = StringIO.new # => #<StringIO>
+ * strio.close
+ *
+ * The instance should be closed when no longer needed.
*
- * Creates new StringIO instance from with _string_ and _mode_.
+ * Related: StringIO.open (accepts block; closes automatically).
*/
static VALUE
strio_initialize(int argc, VALUE *argv, VALUE self)
@@ -392,11 +403,26 @@ strio_finalize(VALUE self)
}
/*
- * call-seq: StringIO.open(string=""[, mode]) {|strio| ...}
+ * call-seq:
+ * StringIO.open(string = '', mode = 'r+') {|strio| ... }
+ *
+ * Note that +mode+ defaults to <tt>'r'</tt> if +string+ is frozen.
+ *
+ * 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:
+ *
+ * strio = StringIO.open # => #<StringIO>
*
- * Equivalent to StringIO.new except that when it is called with a block, it
- * yields with the new instance and closes it, and returns the result which
- * returned from the block.
+ * With a block, calls the block with the new instance
+ * and returns the block's value;
+ * closes the instance on block exit.
+ *
+ * StringIO.open {|strio| p strio }
+ * # => #<StringIO>
+ *
+ * Related: StringIO.new.
*/
static VALUE
strio_s_open(int argc, VALUE *argv, VALUE klass)
@@ -482,9 +508,23 @@ strio_unimpl(int argc, VALUE *argv, VALUE self)
}
/*
- * call-seq: strio.string -> string
+ * call-seq:
+ * string -> string
+ *
+ * Returns underlying string:
+ *
+ * StringIO.open('foo') do |strio|
+ * p strio.string
+ * strio.string = 'bar'
+ * p strio.string
+ * end
*
- * Returns underlying String object, the subject of IO.
+ * Output:
+ *
+ * "foo"
+ * "bar"
+ *
+ * Related: StringIO#string= (assigns the underlying string).
*/
static VALUE
strio_get_string(VALUE self)
@@ -494,9 +534,23 @@ strio_get_string(VALUE self)
/*
* call-seq:
- * strio.string = string -> string
+ * string = other_string -> other_string
+ *
+ * Assigns the underlying string as +other_string+, and sets position to zero;
+ * returns +other_string+:
+ *
+ * StringIO.open('foo') do |strio|
+ * p strio.string
+ * strio.string = 'bar'
+ * p strio.string
+ * end
*
- * Changes underlying String object, the subject of IO.
+ * Output:
+ *
+ * "foo"
+ * "bar"
+ *
+ * Related: StringIO#string (returns the underlying string).
*/
static VALUE
strio_set_string(VALUE self, VALUE string)
@@ -514,10 +568,13 @@ strio_set_string(VALUE self, VALUE string)
/*
* call-seq:
- * strio.close -> nil
+ * close -> nil
+ *
+ * Closes +self+ for both reading and writing.
*
- * Closes a StringIO. The stream is unavailable for any further data
- * operations; an +IOError+ is raised if such an attempt is made.
+ * Raises IOError if reading or writing is attempted.
+ *
+ * Related: StringIO#close_read, StringIO#close_write.
*/
static VALUE
strio_close(VALUE self)
@@ -529,10 +586,13 @@ strio_close(VALUE self)
/*
* call-seq:
- * strio.close_read -> nil
+ * close_read -> nil
+ *
+ * Closes +self+ for reading; closed-write setting remains unchanged.
*
- * Closes the read end of a StringIO. Will raise an +IOError+ if the
- * receiver is not readable.
+ * Raises IOError if reading is attempted.
+ *
+ * Related: StringIO#close, StringIO#close_write.
*/
static VALUE
strio_close_read(VALUE self)
@@ -547,10 +607,13 @@ strio_close_read(VALUE self)
/*
* call-seq:
- * strio.close_write -> nil
+ * close_write -> nil
+ *
+ * Closes +self+ for writing; closed-read setting remains unchanged.
+ *
+ * Raises IOError if writing is attempted.
*
- * Closes the write end of a StringIO. Will raise an +IOError+ if the
- * receiver is not writeable.
+ * Related: StringIO#close, StringIO#close_read.
*/
static VALUE
strio_close_write(VALUE self)
@@ -565,9 +628,10 @@ strio_close_write(VALUE self)
/*
* call-seq:
- * strio.closed? -> true or false
+ * closed? -> true or false
*
- * Returns +true+ if the stream is completely closed, +false+ otherwise.
+ * Returns +true+ if +self+ is closed for both reading and writing,
+ * +false+ otherwise.
*/
static VALUE
strio_closed(VALUE self)
@@ -579,9 +643,9 @@ strio_closed(VALUE self)
/*
* call-seq:
- * strio.closed_read? -> true or false
+ * closed_read? -> true or false
*
- * Returns +true+ if the stream is not readable, +false+ otherwise.
+ * Returns +true+ if +self+ is closed for reading, +false+ otherwise.
*/
static VALUE
strio_closed_read(VALUE self)
@@ -593,9 +657,9 @@ strio_closed_read(VALUE self)
/*
* call-seq:
- * strio.closed_write? -> true or false
+ * closed_write? -> true or false
*
- * Returns +true+ if the stream is not writable, +false+ otherwise.
+ * Returns +true+ if +self+ is closed for writing, +false+ otherwise.
*/
static VALUE
strio_closed_write(VALUE self)
@@ -615,11 +679,14 @@ strio_to_read(VALUE self)
/*
* call-seq:
- * strio.eof -> true or false
- * strio.eof? -> true or false
+ * eof? -> true or false
+ *
+ * Returns +true+ if positioned at end-of-stream, +false+ otherwise;
+ * see {Position}[rdoc-ref:File@Position].
+ *
+ * Raises IOError if the stream is not opened for reading.
*
- * Returns true if the stream is at the end of the data (underlying string).
- * The stream must be opened for reading or an +IOError+ will be raised.
+ * StreamIO#eof is an alias for StreamIO#eof?.
*/
static VALUE
strio_eof(VALUE self)
@@ -649,13 +716,10 @@ strio_copy(VALUE copy, VALUE orig)
/*
* call-seq:
- * strio.lineno -> integer
+ * lineno -> current_line_number
*
- * Returns the current line number. The stream must be
- * opened for reading. +lineno+ counts the number of times +gets+ is
- * called, rather than the number of newlines encountered. The two
- * values will differ if +gets+ is called with a separator other than
- * newline. See also the <code>$.</code> variable.
+ * Returns the current line number in +self+;
+ * see {Line Number}[rdoc-ref:IO@Line+Number].
*/
static VALUE
strio_get_lineno(VALUE self)
@@ -665,10 +729,10 @@ strio_get_lineno(VALUE self)
/*
* call-seq:
- * strio.lineno = integer -> integer
+ * lineno = new_line_number -> new_line_number
*
- * Manually sets the current line number to the given value.
- * <code>$.</code> is updated only on the next read.
+ * Sets the current line number in +self+ to the given +new_line_number+;
+ * see {Line Number}[rdoc-ref:IO@Line+Number].
*/
static VALUE
strio_set_lineno(VALUE self, VALUE lineno)
@@ -679,9 +743,10 @@ strio_set_lineno(VALUE self, VALUE lineno)
/*
* call-seq:
- * strio.binmode -> stringio
+ * binmode -> self
*
- * Puts stream into binary mode. See IO#binmode.
+ * Sets the data mode in +self+ to binary mode;
+ * see {Data Mode}[rdoc-ref:File@Data+Mode].
*
*/
static VALUE
@@ -705,11 +770,27 @@ strio_binmode(VALUE self)
/*
* call-seq:
- * strio.reopen(other_StrIO) -> strio
- * strio.reopen(string, mode) -> strio
+ * reopen(other, mode = 'r+') -> self
+ *
+ * Reinitializes the stream with the given +other+ (string or StringIO) and +mode+;
+ * see IO.new:
+ *
+ * StringIO.open('foo') do |strio|
+ * p strio.string
+ * strio.reopen('bar')
+ * p strio.string
+ * other_strio = StringIO.new('baz')
+ * strio.reopen(other_strio)
+ * p strio.string
+ * other_strio.close
+ * end
+ *
+ * Output:
+ *
+ * "foo"
+ * "bar"
+ * "baz"
*
- * Reinitializes the stream with the given <i>other_StrIO</i> or _string_
- * and _mode_ (see StringIO#new).
*/
static VALUE
strio_reopen(int argc, VALUE *argv, VALUE self)
@@ -723,10 +804,12 @@ strio_reopen(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * strio.pos -> integer
- * strio.tell -> integer
+ * pos -> stream_position
+ *
+ * Returns the current position (in bytes);
+ * see {Position}[rdoc-ref:IO@Position].
*
- * Returns the current offset (in bytes).
+ * StringIO#tell is an alias for StringIO#pos.
*/
static VALUE
strio_get_pos(VALUE self)
@@ -736,9 +819,10 @@ strio_get_pos(VALUE self)
/*
* call-seq:
- * strio.pos = integer -> integer
+ * pos = new_position -> new_position
*
- * Seeks to the given position (in bytes).
+ * Sets the current position (in bytes);
+ * see {Position}[rdoc-ref:IO@Position].
*/
static VALUE
strio_set_pos(VALUE self, VALUE pos)
@@ -754,10 +838,11 @@ strio_set_pos(VALUE self, VALUE pos)
/*
* call-seq:
- * strio.rewind -> 0
+ * rewind -> 0
*
- * Positions the stream to the beginning of input, resetting
- * +lineno+ to zero.
+ * Sets the current position and line number to zero;
+ * see {Position}[rdoc-ref:IO@Position]
+ * and {Line Number}[rdoc-ref:IO@Line+Number].
*/
static VALUE
strio_rewind(VALUE self)
@@ -770,10 +855,11 @@ strio_rewind(VALUE self)
/*
* call-seq:
- * strio.seek(amount, whence=SEEK_SET) -> 0
+ * seek(offset, whence = SEEK_SET) -> 0
*
- * Seeks to a given offset _amount_ in the stream according to
- * the value of _whence_ (see IO#seek).
+ * Sets the current position to the given integer +offset+ (in bytes),
+ * with respect to a given constant +whence+;
+ * see {Position}[rdoc-ref:IO@Position].
*/
static VALUE
strio_seek(int argc, VALUE *argv, VALUE self)
@@ -809,9 +895,9 @@ strio_seek(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * strio.sync -> true
+ * sync -> true
*
- * Returns +true+ always.
+ * Returns +true+; implemented only for compatibility with other stream classes.
*/
static VALUE
strio_get_sync(VALUE self)
@@ -826,10 +912,12 @@ strio_get_sync(VALUE self)
/*
* call-seq:
- * strio.each_byte {|byte| block } -> strio
- * strio.each_byte -> anEnumerator
+ * each_byte {|byte| ... } -> self
*
- * See IO#each_byte.
+ * With a block given, calls the block with each remaining byte in the stream;
+ * see {Byte IO}[rdoc-ref:IO@Byte+IO].
+ *
+ * With no block given, returns an enumerator.
*/
static VALUE
strio_each_byte(VALUE self)
@@ -847,9 +935,10 @@ strio_each_byte(VALUE self)
/*
* call-seq:
- * strio.getc -> string or nil
+ * getc -> character or nil
*
- * See IO#getc.
+ * Reads and returns the next character from the stream;
+ * see {Character IO}[rdoc-ref:IO@Character+IO].
*/
static VALUE
strio_getc(VALUE self)
@@ -872,9 +961,10 @@ strio_getc(VALUE self)
/*
* call-seq:
- * strio.getbyte -> fixnum or nil
+ * getbyte -> byte or nil
*
- * See IO#getbyte.
+ * 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)
@@ -910,12 +1000,10 @@ strio_extend(struct StringIO *ptr, long pos, long len)
/*
* call-seq:
- * strio.ungetc(string) -> nil
+ * ungetc(character) -> nil
*
- * Pushes back one character (passed as a parameter)
- * such that a subsequent buffered read will return it. There is no
- * limitation for multiple pushbacks including pushing back behind the
- * beginning of the buffer string.
+ * Pushes back ("unshifts") a character or integer onto the stream;
+ * see {Character IO}[rdoc-ref:IO@Character+IO].
*/
static VALUE
strio_ungetc(VALUE self, VALUE c)
@@ -950,9 +1038,10 @@ strio_ungetc(VALUE self, VALUE c)
/*
* call-seq:
- * strio.ungetbyte(fixnum) -> nil
+ * ungetbyte(byte) -> nil
*
- * See IO#ungetbyte
+ * Pushes back ("unshifts") an 8-bit byte onto the stream;
+ * see {Byte IO}[rdoc-ref:IO@Byte+IO].
*/
static VALUE
strio_ungetbyte(VALUE self, VALUE c)
@@ -1012,9 +1101,10 @@ strio_unget_bytes(struct StringIO *ptr, const char *cp, long cl)
/*
* call-seq:
- * strio.readchar -> string
+ * readchar -> string
*
- * See IO#readchar.
+ * Like +getc+, but raises an exception if already at end-of-stream;
+ * see {Character IO}[rdoc-ref:IO@Character+IO].
*/
static VALUE
strio_readchar(VALUE self)
@@ -1026,9 +1116,10 @@ strio_readchar(VALUE self)
/*
* call-seq:
- * strio.readbyte -> fixnum
+ * readbyte -> byte
*
- * See IO#readbyte.
+ * Like +getbyte+, but raises an exception if already at end-of-stream;
+ * see {Byte IO}[rdoc-ref:IO@Byte+IO].
*/
static VALUE
strio_readbyte(VALUE self)
@@ -1040,10 +1131,12 @@ strio_readbyte(VALUE self)
/*
* call-seq:
- * strio.each_char {|char| block } -> strio
- * strio.each_char -> anEnumerator
+ * each_char {|c| ... } -> self
+ *
+ * With a block given, calls the block with each remaining character in the stream;
+ * see {Character IO}[rdoc-ref:IO@Character+IO].
*
- * See IO#each_char.
+ * With no block given, returns an enumerator.
*/
static VALUE
strio_each_char(VALUE self)
@@ -1060,10 +1153,12 @@ strio_each_char(VALUE self)
/*
* call-seq:
- * strio.each_codepoint {|c| block } -> strio
- * strio.each_codepoint -> anEnumerator
+ * each_codepoint {|codepoint| ... } -> self
*
- * See IO#each_codepoint.
+ * With a block given, calls the block with each remaining codepoint in the stream;
+ * see {Codepoint IO}[rdoc-ref:IO@Codepoint+IO].
+ *
+ * With no block given, returns an enumerator.
*/
static VALUE
strio_each_codepoint(VALUE self)
@@ -1245,8 +1340,9 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
str = strio_substr(ptr, ptr->pos, e - s - w, enc);
}
else {
- if (n < e - s) {
- if (e - s < 1024) {
+ if (n < e - s + arg->chomp) {
+ /* unless chomping, RS at the end does not matter */
+ if (e - s < 1024 || n == e - s) {
for (p = s; p + n <= e; ++p) {
if (MEMCMP(p, RSTRING_PTR(str), char, n) == 0) {
e = p + n;
@@ -1273,11 +1369,13 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
/*
* call-seq:
- * strio.gets(sep=$/, chomp: false) -> string or nil
- * strio.gets(limit, chomp: false) -> string or nil
- * strio.gets(sep, limit, chomp: false) -> string or nil
+ * gets(sep = $/, chomp: false) -> string or nil
+ * gets(limit, chomp: false) -> string or nil
+ * gets(sep, limit, chomp: false) -> string or nil
*
- * See IO#gets.
+ * Reads and returns a line from the stream;
+ * assigns the return value to <tt>$_</tt>;
+ * see {Line IO}[rdoc-ref:IO@Line+IO].
*/
static VALUE
strio_gets(int argc, VALUE *argv, VALUE self)
@@ -1297,11 +1395,12 @@ strio_gets(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * strio.readline(sep=$/, chomp: false) -> string
- * strio.readline(limit, chomp: false) -> string or nil
- * strio.readline(sep, limit, chomp: false) -> string or nil
+ * readline(sep = $/, chomp: false) -> string
+ * readline(limit, chomp: false) -> string
+ * readline(sep, limit, chomp: false) -> string
*
- * See IO#readline.
+ * Reads a line as with IO#gets, but raises EOFError if already at end-of-file;
+ * see {Line IO}[rdoc-ref:IO@Line+IO].
*/
static VALUE
strio_readline(int argc, VALUE *argv, VALUE self)
@@ -1313,17 +1412,16 @@ strio_readline(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * strio.each(sep=$/, chomp: false) {|line| block } -> strio
- * strio.each(limit, chomp: false) {|line| block } -> strio
- * strio.each(sep, limit, chomp: false) {|line| block } -> strio
- * strio.each(...) -> anEnumerator
+ * each_line(sep = $/, chomp: false) {|line| ... } -> self
+ * each_line(limit, chomp: false) {|line| ... } -> self
+ * each_line(sep, limit, chomp: false) {|line| ... } -> self
*
- * strio.each_line(sep=$/, chomp: false) {|line| block } -> strio
- * strio.each_line(limit, chomp: false) {|line| block } -> strio
- * strio.each_line(sep, limit, chomp: false) {|line| block } -> strio
- * strio.each_line(...) -> anEnumerator
+ * 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].
*
- * See IO#each.
+ * StringIO#each is an alias for StringIO#each_line.
*/
static VALUE
strio_each(int argc, VALUE *argv, VALUE self)
@@ -1751,24 +1849,16 @@ strio_set_encoding_by_bom(VALUE self)
}
/*
- * Pseudo I/O on String object, with interface corresponding to IO.
+ * \IO streams for strings, with access similar to
+ * {IO}[rdoc-ref:IO];
+ * see {IO}[rdoc-ref:IO].
*
- * Commonly used to simulate <code>$stdio</code> or <code>$stderr</code>
+ * === About the Examples
*
- * === Examples
+ * Examples on this page assume that \StringIO has been required:
*
* require 'stringio'
*
- * # Writing stream emulation
- * io = StringIO.new
- * io.puts "Hello World"
- * io.string #=> "Hello World\n"
- *
- * # Reading stream emulation
- * io = StringIO.new "first\nsecond\nlast\n"
- * io.getc #=> "f"
- * io.gets #=> "irst\n"
- * io.read #=> "second\nlast\n"
*/
void
Init_stringio(void)
diff --git a/ext/strscan/extconf.rb b/ext/strscan/extconf.rb
index f0ecbf85d8..3c311d2364 100644
--- a/ext/strscan/extconf.rb
+++ b/ext/strscan/extconf.rb
@@ -1,5 +1,10 @@
# frozen_string_literal: true
require 'mkmf'
-$INCFLAGS << " -I$(top_srcdir)" if $extmk
-have_func("onig_region_memsize", "ruby.h")
-create_makefile 'strscan'
+if RUBY_ENGINE == 'ruby'
+ $INCFLAGS << " -I$(top_srcdir)" if $extmk
+ have_func("onig_region_memsize(NULL)")
+ have_func("rb_reg_onig_match", "ruby/re.h")
+ create_makefile 'strscan'
+else
+ File.write('Makefile', dummy_makefile("").join)
+end
diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c
index e1426380b4..16d669d8a5 100644
--- a/ext/strscan/strscan.c
+++ b/ext/strscan/strscan.c
@@ -22,7 +22,7 @@ extern size_t onig_region_memsize(const struct re_registers *regs);
#include <stdbool.h>
-#define STRSCAN_VERSION "3.0.1"
+#define STRSCAN_VERSION "3.0.7"
/* =======================================================================
Data Type Definitions
@@ -435,11 +435,11 @@ strscan_get_pos(VALUE self)
*
* In short, it's a 0-based index into the string.
*
- * s = StringScanner.new("abcädeföghi")
- * s.charpos # -> 0
- * s.scan_until(/ä/) # -> "abcä"
- * s.pos # -> 5
- * s.charpos # -> 4
+ * s = StringScanner.new("abc\u00e4def\u00f6ghi")
+ * s.charpos # -> 0
+ * s.scan_until(/\u00e4/) # -> "abc\u00E4"
+ * s.pos # -> 5
+ * s.charpos # -> 4
*/
static VALUE
strscan_get_charpos(VALUE self)
@@ -539,6 +539,68 @@ adjust_register_position(struct strscanner *p, long position)
}
}
+/* rb_reg_onig_match is available in Ruby 3.3 and later. */
+#ifndef HAVE_RB_REG_ONIG_MATCH
+static OnigPosition
+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)
+{
+ 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);
+
+ if (!tmpreg) RREGEXP(re)->usecnt--;
+ if (tmpreg) {
+ if (RREGEXP(re)->usecnt) {
+ onig_free(reg);
+ }
+ else {
+ onig_free(RREGEXP_PTR(re));
+ RREGEXP_PTR(re) = reg;
+ }
+ }
+
+ if (result < 0) {
+ if (result != ONIG_MISMATCH) {
+ rb_raise(ScanError, "regexp buffer overflow");
+ }
+ }
+
+ return result;
+}
+#endif
+
+static OnigPosition
+strscan_match(regex_t *reg, VALUE str, struct re_registers *regs, void *args_ptr)
+{
+ struct strscanner *p = (struct strscanner *)args_ptr;
+
+ return onig_match(reg,
+ match_target(p),
+ (UChar* )(CURPTR(p) + S_RESTLEN(p)),
+ (UChar* )CURPTR(p),
+ regs,
+ ONIG_OPTION_NONE);
+}
+
+static OnigPosition
+strscan_search(regex_t *reg, VALUE str, struct re_registers *regs, void *args_ptr)
+{
+ struct strscanner *p = (struct strscanner *)args_ptr;
+
+ return onig_search(reg,
+ match_target(p),
+ (UChar *)(CURPTR(p) + S_RESTLEN(p)),
+ (UChar *)CURPTR(p),
+ (UChar *)(CURPTR(p) + S_RESTLEN(p)),
+ regs,
+ ONIG_OPTION_NONE);
+}
+
static VALUE
strscan_do_scan(VALUE self, VALUE pattern, int succptr, int getstr, int headonly)
{
@@ -560,47 +622,14 @@ strscan_do_scan(VALUE self, VALUE pattern, int succptr, int getstr, int headonly
}
if (RB_TYPE_P(pattern, T_REGEXP)) {
- regex_t *rb_reg_prepare_re(VALUE re, VALUE str);
- regex_t *re;
- long ret;
- int tmpreg;
-
p->regex = pattern;
- re = rb_reg_prepare_re(pattern, p->str);
- tmpreg = re != RREGEXP_PTR(pattern);
- if (!tmpreg) RREGEXP(pattern)->usecnt++;
-
- if (headonly) {
- ret = onig_match(re,
- match_target(p),
- (UChar* )(CURPTR(p) + S_RESTLEN(p)),
- (UChar* )CURPTR(p),
- &(p->regs),
- ONIG_OPTION_NONE);
- }
- else {
- ret = onig_search(re,
- match_target(p),
- (UChar* )(CURPTR(p) + S_RESTLEN(p)),
- (UChar* )CURPTR(p),
- (UChar* )(CURPTR(p) + S_RESTLEN(p)),
- &(p->regs),
- ONIG_OPTION_NONE);
- }
- if (!tmpreg) RREGEXP(pattern)->usecnt--;
- if (tmpreg) {
- if (RREGEXP(pattern)->usecnt) {
- onig_free(re);
- }
- else {
- onig_free(RREGEXP_PTR(pattern));
- RREGEXP_PTR(pattern) = re;
- }
- }
+ OnigPosition ret = rb_reg_onig_match(pattern,
+ p->str,
+ headonly ? strscan_match : strscan_search,
+ (void *)p,
+ &(p->regs));
- if (ret == -2) rb_raise(ScanError, "regexp buffer overflow");
- if (ret < 0) {
- /* not matched */
+ if (ret == ONIG_MISMATCH) {
return Qnil;
}
}
@@ -1038,8 +1067,9 @@ strscan_empty_p(VALUE self)
* This method is obsolete; use #eos? instead.
*
* s = StringScanner.new('test string')
- * s.eos? # These two
- * s.rest? # are opposites.
+ * # These two are opposites
+ * s.eos? # => false
+ * s.rest? # => true
*/
static VALUE
strscan_rest_p(VALUE self)
@@ -1458,6 +1488,56 @@ strscan_fixed_anchor_p(VALUE self)
return p->fixed_anchor_p ? Qtrue : Qfalse;
}
+typedef struct {
+ VALUE self;
+ VALUE captures;
+} named_captures_data;
+
+static int
+named_captures_iter(const OnigUChar *name,
+ const OnigUChar *name_end,
+ int back_num,
+ int *back_refs,
+ OnigRegex regex,
+ void *arg)
+{
+ named_captures_data *data = arg;
+
+ VALUE key = rb_str_new((const char *)name, name_end - name);
+ VALUE value = RUBY_Qnil;
+ int i;
+ for (i = 0; i < back_num; i++) {
+ value = strscan_aref(data->self, INT2NUM(back_refs[i]));
+ }
+ rb_hash_aset(data->captures, key, value);
+ return 0;
+}
+
+/*
+ * call-seq:
+ * scanner.named_captures -> hash
+ *
+ * Returns a hash of string variables matching the regular expression.
+ *
+ * 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;
+ data.self = self;
+ data.captures = rb_hash_new();
+ if (!RB_NIL_P(p->regex)) {
+ onig_foreach_name(RREGEXP_PTR(p->regex), named_captures_iter, &data);
+ }
+
+ return data.captures;
+}
+
/* =======================================================================
Ruby Interface
======================================================================= */
@@ -1468,6 +1548,8 @@ strscan_fixed_anchor_p(VALUE self)
* 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
*
@@ -1650,4 +1732,6 @@ Init_strscan(void)
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);
}
diff --git a/ext/strscan/strscan.gemspec b/ext/strscan/strscan.gemspec
index 5d8119ea4c..8a61c7abe6 100644
--- a/ext/strscan/strscan.gemspec
+++ b/ext/strscan/strscan.gemspec
@@ -16,13 +16,26 @@ Gem::Specification.new do |s|
s.summary = "Provides lexical scanning operations on a String."
s.description = "Provides lexical scanning operations on a String."
- s.require_path = %w{lib}
- s.files = %w{ext/strscan/extconf.rb ext/strscan/strscan.c}
- s.extensions = %w{ext/strscan/extconf.rb}
+ files = [
+ "COPYING",
+ "LICENSE.txt",
+ ]
+ if RUBY_ENGINE == "jruby"
+ s.require_paths = %w{ext/jruby/lib lib}
+ files << "ext/jruby/lib/strscan.rb"
+ files << "lib/strscan.jar"
+ s.platform = "java"
+ else
+ s.require_paths = %w{lib}
+ files << "ext/strscan/extconf.rb"
+ files << "ext/strscan/strscan.c"
+ s.extensions = %w{ext/strscan/extconf.rb}
+ end
+ s.files = files
s.required_ruby_version = ">= 2.4.0"
- s.authors = ["Minero Aoki", "Sutou Kouhei"]
- s.email = [nil, "kou@cozmixng.org"]
+ s.authors = ["Minero Aoki", "Sutou Kouhei", "Charles Oliver Nutter"]
+ s.email = [nil, "kou@cozmixng.org", "headius@headius.com"]
s.homepage = "https://github.com/ruby/strscan"
s.licenses = ["Ruby", "BSD-2-Clause"]
end
diff --git a/ext/syslog/syslog.c b/ext/syslog/syslog.c
index 4c540fc9c7..8f3674aa8d 100644
--- a/ext/syslog/syslog.c
+++ b/ext/syslog/syslog.c
@@ -165,15 +165,15 @@ static VALUE mSyslog_open(int argc, VALUE *argv, VALUE self)
syslog_ident = strdup(ident_ptr);
if (NIL_P(opt)) {
- syslog_options = LOG_PID | LOG_CONS;
+ syslog_options = LOG_PID | LOG_CONS;
} else {
- syslog_options = NUM2INT(opt);
+ syslog_options = NUM2INT(opt);
}
if (NIL_P(fac)) {
- syslog_facility = LOG_USER;
+ syslog_facility = LOG_USER;
} else {
- syslog_facility = NUM2INT(fac);
+ syslog_facility = NUM2INT(fac);
}
openlog(syslog_ident, syslog_options, syslog_facility);
@@ -307,7 +307,7 @@ static VALUE mSyslog_log(int argc, VALUE *argv, VALUE self)
pri = *argv++;
if (!FIXNUM_P(pri)) {
- rb_raise(rb_eTypeError, "type mismatch: %"PRIsVALUE" given", rb_obj_class(pri));
+ rb_raise(rb_eTypeError, "type mismatch: %"PRIsVALUE" given", rb_obj_class(pri));
}
syslog_write(FIX2INT(pri), argc, argv);
@@ -322,14 +322,14 @@ static VALUE mSyslog_inspect(VALUE self)
Check_Type(self, T_MODULE);
if (!syslog_opened)
- return rb_sprintf("<#%"PRIsVALUE": opened=false>", self);
+ return rb_sprintf("<#%"PRIsVALUE": opened=false>", self);
return rb_sprintf("<#%"PRIsVALUE": opened=true, ident=\"%s\", options=%d, facility=%d, mask=%d>",
- self,
- syslog_ident,
- syslog_options,
- syslog_facility,
- syslog_mask);
+ self,
+ syslog_ident,
+ syslog_options,
+ syslog_facility,
+ syslog_mask);
}
/* Returns self, for backward compatibility.
diff --git a/ext/syslog/syslog.gemspec b/ext/syslog/syslog.gemspec
index 8f73f5ad0d..6aa2e9570d 100644
--- a/ext/syslog/syslog.gemspec
+++ b/ext/syslog/syslog.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "syslog"
- spec.version = "0.1.0"
+ spec.version = "0.1.1"
spec.authors = ["Akinori MUSHA"]
spec.email = ["knu@idaemons.org"]
diff --git a/ext/win32/lib/win32/registry.rb b/ext/win32/lib/win32/registry.rb
index b5b99ff684..bda8bb012f 100644
--- a/ext/win32/lib/win32/registry.rb
+++ b/ext/win32/lib/win32/registry.rb
@@ -69,7 +69,11 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
WCHAR_NUL = "\0".encode(WCHAR).freeze
WCHAR_CR = "\r".encode(WCHAR).freeze
WCHAR_SIZE = WCHAR_NUL.bytesize
- LOCALE = Encoding.find(Encoding.locale_charmap)
+ begin
+ LOCALE = Encoding.find(Encoding.locale_charmap)
+ rescue ArgumentError
+ LOCALE = Encoding::UTF_8
+ end
class Registry
@@ -740,14 +744,11 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
# method returns.
#
def write(name, type, data)
- termsize = 0
case type
when REG_SZ, REG_EXPAND_SZ
- data = data.encode(WCHAR)
- termsize = WCHAR_SIZE
+ data = data.encode(WCHAR) << WCHAR_NUL
when REG_MULTI_SZ
data = data.to_a.map {|s| s.encode(WCHAR)}.join(WCHAR_NUL) << WCHAR_NUL
- termsize = WCHAR_SIZE
when REG_BINARY, REG_NONE
data = data.to_s
when REG_DWORD
@@ -759,7 +760,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
else
raise TypeError, "Unsupported type #{Registry.type2name(type)}"
end
- API.SetValue(@hkey, name, type, data, data.bytesize + termsize)
+ API.SetValue(@hkey, name, type, data, data.bytesize)
end
#
diff --git a/ext/win32/resolv/resolv.c b/ext/win32/resolv/resolv.c
index f19243ccc1..8a50ef7824 100644
--- a/ext/win32/resolv/resolv.c
+++ b/ext/win32/resolv/resolv.c
@@ -29,19 +29,19 @@ get_dns_server_list(VALUE self)
ret = GetNetworkParams(NULL, &buflen);
if (ret != NO_ERROR && ret != ERROR_BUFFER_OVERFLOW) {
- w32error_raise(ret);
+ w32error_raise(ret);
}
fixedinfo = ALLOCV(buf, buflen);
ret = GetNetworkParams(fixedinfo, &buflen);
if (ret == NO_ERROR) {
- const IP_ADDR_STRING *ipaddr = &fixedinfo->DnsServerList;
- nameservers = rb_ary_new();
- do {
- const char *s = ipaddr->IpAddress.String;
- if (!*s) continue;
- if (strcmp(s, "0.0.0.0") == 0) continue;
- rb_ary_push(nameservers, rb_str_new_cstr(s));
- } while ((ipaddr = ipaddr->Next) != NULL);
+ const IP_ADDR_STRING *ipaddr = &fixedinfo->DnsServerList;
+ nameservers = rb_ary_new();
+ do {
+ const char *s = ipaddr->IpAddress.String;
+ if (!*s) continue;
+ if (strcmp(s, "0.0.0.0") == 0) continue;
+ rb_ary_push(nameservers, rb_str_new_cstr(s));
+ } while ((ipaddr = ipaddr->Next) != NULL);
}
ALLOCV_END(buf);
if (ret != NO_ERROR) w32error_raise(ret);
diff --git a/ext/win32ole/win32ole.c b/ext/win32ole/win32ole.c
index d77347b3a3..3f083bb12d 100644
--- a/ext/win32ole/win32ole.c
+++ b/ext/win32ole/win32ole.c
@@ -454,12 +454,12 @@ vtdate2rbtime(double date)
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));
+ 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));
@@ -568,16 +568,16 @@ load_conv_function51932(void)
void *p;
if (!pIMultiLanguage) {
#if defined(HAVE_TYPE_IMULTILANGUAGE2)
- hr = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER,
- &IID_IMultiLanguage2, &p);
+ 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);
+ hr = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IMultiLanguage, &p);
#endif
- if (FAILED(hr)) {
- failed_load_conv51932();
- }
- pIMultiLanguage = p;
+ if (FAILED(hr)) {
+ failed_load_conv51932();
+ }
+ pIMultiLanguage = p;
}
}
#define need_conv_function51932() (load_conv_function51932(), 1)
@@ -624,7 +624,7 @@ ole_init_cp(void)
rb_encoding *encdef;
encdef = rb_default_internal_encoding();
if (!encdef) {
- encdef = rb_default_external_encoding();
+ encdef = rb_default_external_encoding();
}
cp = ole_encoding2cp(encdef);
set_ole_codepage(cp);
@@ -650,38 +650,38 @@ ole_cp2encoding(UINT cp)
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:
+ 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;
}
@@ -690,7 +690,7 @@ ole_cp2encoding(UINT cp)
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);
+ idx = rb_define_dummy_encoding(enc_cstr);
return rb_enc_from_index(idx);
}
@@ -700,14 +700,14 @@ 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);
+ &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); \
- } \
+ 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
@@ -720,11 +720,11 @@ ole_wc2mb_alloc(LPWSTR pw, char *(alloc)(UINT size, void *arg), void *arg)
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;
+ 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);
@@ -816,8 +816,8 @@ 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;
+ rb_add_event_hook(ole_uninitialize_hook, RUBY_EVENT_THREAD_END, Qnil);
+ g_uninitialize_hooked = TRUE;
}
if(g_ole_initialized == FALSE) {
@@ -911,21 +911,21 @@ ole_mb2wc(char *pm, int len, UINT cp)
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)) {
+ 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)) {
+ }
+ 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;
+ }
+ return pw;
#endif
}
size = MultiByteToWideChar(cp, 0, pm, len, NULL, 0);
@@ -1737,11 +1737,11 @@ reg_get_val(HKEY hkey, const char *subkey)
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);
+ 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);
}
@@ -2555,7 +2555,7 @@ hash2named_arg(VALUE key, VALUE val, VALUE pop)
rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)");
}
if (RB_TYPE_P(key, T_SYMBOL)) {
- key = rb_sym2str(key);
+ key = rb_sym2str(key);
}
/* pNamedArgs[0] is <method name>, so "index + 1" */
@@ -2619,10 +2619,10 @@ ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket)
rb_scan_args(argc, argv, "1*", &cmd, &paramS);
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)");
+ rb_raise(rb_eTypeError, "method is wrong type (expected String or Symbol)");
}
if (RB_TYPE_P(cmd, T_SYMBOL)) {
- cmd = rb_sym2str(cmd);
+ cmd = rb_sym2str(cmd);
}
pole = oledata_get_struct(self);
if(!pole->pDispatch) {
@@ -2631,7 +2631,7 @@ ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket)
if (is_bracket) {
DispID = DISPID_VALUE;
argc += 1;
- rb_ary_unshift(paramS, cmd);
+ rb_ary_unshift(paramS, cmd);
} else {
wcmdname = ole_vstr2wc(cmd);
hr = pole->pDispatch->lpVtbl->GetIDsOfNames( pole->pDispatch, &IID_NULL,
@@ -3639,7 +3639,7 @@ fole_respond_to(VALUE self, VALUE method)
pole = oledata_get_struct(self);
wcmdname = ole_vstr2wc(method);
hr = pole->pDispatch->lpVtbl->GetIDsOfNames( pole->pDispatch, &IID_NULL,
- &wcmdname, 1, cWIN32OLE_lcid, &DispID);
+ &wcmdname, 1, cWIN32OLE_lcid, &DispID);
SysFreeString(wcmdname);
return SUCCEEDED(hr) ? Qtrue : Qfalse;
}
diff --git a/ext/win32ole/win32ole.gemspec b/ext/win32ole/win32ole.gemspec
index 977555c98d..b6ea8e8a55 100644
--- a/ext/win32ole/win32ole.gemspec
+++ b/ext/win32ole/win32ole.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "win32ole"
- spec.version = "1.8.8"
+ spec.version = "1.8.9"
spec.authors = ["Masaki Suketa"]
spec.email = ["suke@ruby-lang.org"]
diff --git a/ext/win32ole/win32ole_event.c b/ext/win32ole/win32ole_event.c
index 93e4d6bad5..45ebf13433 100644
--- a/ext/win32ole/win32ole_event.c
+++ b/ext/win32ole/win32ole_event.c
@@ -200,7 +200,7 @@ STDMETHODIMP EVENTSINK_Invoke(
}
outargv = Qnil;
if (is_outarg == Qtrue) {
- outargv = rb_ary_new();
+ outargv = rb_ary_new();
rb_ary_push(args, outargv);
}
@@ -413,15 +413,15 @@ hash2ptr_dispparams(VALUE hash, ITypeInfo *pTypeInfo, DISPID dispid, DISPPARAMS
bstrs, pdispparams->cArgs + 1,
&len);
if (FAILED(hr))
- return;
+ return;
for (i = 0; i < len - 1; i++) {
- key = WC2VSTR(bstrs[i + 1]);
+ 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));
+ 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);
}
@@ -433,7 +433,7 @@ 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")));
+ ret = rb_hash_aref(hash, rb_str_intern(rb_str_new2("return")));
return ret;
}
@@ -610,7 +610,7 @@ find_coclass(
hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, NULL);
if (FAILED(hr)) {
- return hr;
+ return hr;
}
count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib);
for (i = 0; i < count && !found; i++) {
diff --git a/ext/win32ole/win32ole_typelib.c b/ext/win32ole/win32ole_typelib.c
index 651dc2d75f..fb68bebda8 100644
--- a/ext/win32ole/win32ole_typelib.c
+++ b/ext/win32ole/win32ole_typelib.c
@@ -285,7 +285,7 @@ oletypelib_get_libattr(ITypeLib *pTypeLib, TLIBATTR **ppTLibAttr)
hr = pTypeLib->lpVtbl->GetLibAttr(pTypeLib, ppTLibAttr);
if (FAILED(hr)) {
ole_raise(hr, eWIN32OLERuntimeError,
- "failed to get library attribute(TLIBATTR) from ITypeLib");
+ "failed to get library attribute(TLIBATTR) from ITypeLib");
}
}
@@ -588,13 +588,13 @@ foletypelib_path(VALUE self)
pTypeLib = itypelib(self);
oletypelib_get_libattr(pTypeLib, &pTLibAttr);
hr = QueryPathOfRegTypeLib(&pTLibAttr->guid,
- pTLibAttr->wMajorVerNum,
- pTLibAttr->wMinorVerNum,
- lcid,
- &bstr);
+ 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);
+ ole_raise(hr, eWIN32OLERuntimeError, "failed to QueryPathOfRegTypeTypeLib");
}
pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr);
@@ -722,7 +722,7 @@ typelib_file_from_typelib(VALUE ole)
if (ver == Qnil)
break;
err = reg_open_vkey(hclsid, ver, &hversion);
- if (err != ERROR_SUCCESS || fver > atof(StringValuePtr(ver)))
+ if (err != ERROR_SUCCESS || fver > atof(StringValuePtr(ver)))
continue;
fver = atof(StringValuePtr(ver));
typelib = reg_get_val(hversion, NULL);
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index 20079a5d4c..aefdba0ebd 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 "2.1.1"
+#define RUBY_ZLIB_VERSION "3.0.0"
#ifndef RB_PASS_CALLED_KEYWORDS
# define rb_class_new_instance_kw(argc, argv, klass, kw_splat) rb_class_new_instance(argc, argv, klass)
diff --git a/file.c b/file.c
index 417bfc5814..3a8439ef07 100644
--- a/file.c
+++ b/file.c
@@ -115,6 +115,8 @@ int flock(int, int);
# define link(f, t) rb_w32_ulink((f), (t))
# undef unlink
# define unlink(p) rb_w32_uunlink(p)
+# undef readlink
+# define readlink(f, t, l) rb_w32_ureadlink((f), (t), (l))
# undef rename
# define rename(f, t) rb_w32_urename((f), (t))
# undef symlink
@@ -182,15 +184,15 @@ file_path_convert(VALUE name)
int fname_encidx = ENCODING_GET(name);
int fs_encidx;
if (ENCINDEX_US_ASCII != fname_encidx &&
- ENCINDEX_ASCII_8BIT != fname_encidx &&
- (fs_encidx = rb_filesystem_encindex()) != fname_encidx &&
- rb_default_internal_encoding() &&
- !rb_enc_str_asciionly_p(name)) {
- /* Don't call rb_filesystem_encoding() before US-ASCII and ASCII-8BIT */
- /* fs_encoding should be ascii compatible */
- rb_encoding *fname_encoding = rb_enc_from_index(fname_encidx);
- rb_encoding *fs_encoding = rb_enc_from_index(fs_encidx);
- name = rb_str_conv_enc(name, fname_encoding, fs_encoding);
+ ENCINDEX_ASCII_8BIT != fname_encidx &&
+ (fs_encidx = rb_filesystem_encindex()) != fname_encidx &&
+ rb_default_internal_encoding() &&
+ !rb_enc_str_asciionly_p(name)) {
+ /* Don't call rb_filesystem_encoding() before US-ASCII and ASCII-8BIT */
+ /* fs_encoding should be ascii compatible */
+ rb_encoding *fname_encoding = rb_enc_from_index(fname_encidx);
+ rb_encoding *fs_encoding = rb_enc_from_index(fs_encidx);
+ name = rb_str_conv_enc(name, fname_encoding, fs_encoding);
}
#endif
return name;
@@ -201,8 +203,8 @@ check_path_encoding(VALUE str)
{
rb_encoding *enc = rb_enc_get(str);
if (!rb_enc_asciicompat(enc)) {
- rb_raise(rb_eEncCompatError, "path name must be ASCII-compatible (%s): %"PRIsVALUE,
- rb_enc_name(enc), rb_str_inspect(str));
+ rb_raise(rb_eEncCompatError, "path name must be ASCII-compatible (%s): %"PRIsVALUE,
+ rb_enc_name(enc), rb_str_inspect(str));
}
return enc;
}
@@ -214,7 +216,7 @@ rb_get_path_check_to_string(VALUE obj)
ID to_path;
if (RB_TYPE_P(obj, T_STRING)) {
- return obj;
+ return obj;
}
CONST_ID(to_path, "to_path");
tmp = rb_check_funcall_default(obj, to_path, 0, 0, obj);
@@ -229,7 +231,7 @@ rb_get_path_check_convert(VALUE obj)
check_path_encoding(obj);
if (!rb_str_to_cstr(obj)) {
- rb_raise(rb_eArgError, "path name contains null byte");
+ rb_raise(rb_eArgError, "path name contains null byte");
}
return rb_str_new4(obj);
@@ -254,13 +256,13 @@ rb_str_encode_ospath(VALUE path)
int encidx = ENCODING_GET(path);
#if 0 && defined _WIN32
if (encidx == ENCINDEX_ASCII_8BIT) {
- encidx = rb_filesystem_encindex();
+ encidx = rb_filesystem_encindex();
}
#endif
if (encidx != ENCINDEX_ASCII_8BIT && encidx != ENCINDEX_UTF_8) {
- rb_encoding *enc = rb_enc_from_index(encidx);
- rb_encoding *utf8 = rb_utf8_encoding();
- path = rb_str_conv_enc(path, enc, utf8);
+ rb_encoding *enc = rb_enc_from_index(encidx);
+ rb_encoding *utf8 = rb_utf8_encoding();
+ path = rb_str_conv_enc(path, enc, utf8);
}
#endif
return path;
@@ -268,15 +270,55 @@ rb_str_encode_ospath(VALUE path)
#ifdef __APPLE__
# define NORMALIZE_UTF8PATH 1
+
+# ifdef HAVE_WORKING_FORK
+static void
+rb_CFString_class_initialize_before_fork(void)
+{
+ /*
+ * Since macOS 13, CFString family API used in
+ * rb_str_append_normalized_ospath may internally use Objective-C classes
+ * (NSTaggedPointerString and NSPlaceholderMutableString) for small strings.
+ *
+ * On the other hand, Objective-C classes should not be used for the first
+ * time in a fork()'ed but not exec()'ed process. Violations for this rule
+ * can result deadlock during class initialization, so Objective-C runtime
+ * conservatively crashes on such cases by default.
+ *
+ * Therefore, we need to use CFString API to initialize Objective-C classes
+ * used internally *before* fork().
+ *
+ * For future changes, please note that this initialization process cannot
+ * be done in ctor because NSTaggedPointerString in CoreFoundation is enabled
+ * after CFStringInitializeTaggedStrings(), which is called during loading
+ * Objective-C runtime after ctor.
+ * For more details, see https://bugs.ruby-lang.org/issues/18912
+ */
+
+ /* Enough small but non-empty ASCII string to fit in NSTaggedPointerString. */
+ const char small_str[] = "/";
+ long len = sizeof(small_str) - 1;
+
+ const CFAllocatorRef alloc = kCFAllocatorDefault;
+ CFStringRef s = CFStringCreateWithBytesNoCopy(alloc,
+ (const UInt8 *)small_str,
+ len, kCFStringEncodingUTF8,
+ FALSE, kCFAllocatorNull);
+ CFMutableStringRef m = CFStringCreateMutableCopy(alloc, len, s);
+ CFRelease(m);
+ CFRelease(s);
+}
+# endif
+
static VALUE
rb_str_append_normalized_ospath(VALUE str, const char *ptr, long len)
{
CFIndex buflen = 0;
CFRange all;
CFStringRef s = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
- (const UInt8 *)ptr, len,
- kCFStringEncodingUTF8, FALSE,
- kCFAllocatorNull);
+ (const UInt8 *)ptr, len,
+ kCFStringEncodingUTF8, FALSE,
+ kCFAllocatorNull);
CFMutableStringRef m = CFStringCreateMutableCopy(kCFAllocatorDefault, len, s);
long oldlen = RSTRING_LEN(str);
@@ -285,7 +327,7 @@ rb_str_append_normalized_ospath(VALUE str, const char *ptr, long len)
CFStringGetBytes(m, all, kCFStringEncodingUTF8, '?', FALSE, NULL, 0, &buflen);
rb_str_modify_expand(str, buflen);
CFStringGetBytes(m, all, kCFStringEncodingUTF8, '?', FALSE,
- (UInt8 *)(RSTRING_PTR(str) + oldlen), buflen, &buflen);
+ (UInt8 *)(RSTRING_PTR(str) + oldlen), buflen, &buflen);
rb_str_set_len(str, oldlen + buflen);
CFRelease(m);
CFRelease(s);
@@ -303,34 +345,34 @@ rb_str_normalize_ospath(const char *ptr, long len)
rb_enc_associate(str, enc);
while (p < e) {
- int l, c;
- int r = rb_enc_precise_mbclen(p, e, enc);
- if (!MBCLEN_CHARFOUND_P(r)) {
- /* invalid byte shall not happen but */
- static const char invalid[3] = "\xEF\xBF\xBD";
- rb_str_append_normalized_ospath(str, p1, p-p1);
- rb_str_cat(str, invalid, sizeof(invalid));
- p += 1;
- p1 = p;
- continue;
- }
- l = MBCLEN_CHARFOUND_LEN(r);
- c = rb_enc_mbc_to_codepoint(p, e, enc);
- if ((0x2000 <= c && c <= 0x2FFF) || (0xF900 <= c && c <= 0xFAFF) ||
- (0x2F800 <= c && c <= 0x2FAFF)) {
- if (p - p1 > 0) {
- rb_str_append_normalized_ospath(str, p1, p-p1);
- }
- rb_str_cat(str, p, l);
- p += l;
- p1 = p;
- }
- else {
- p += l;
- }
+ int l, c;
+ int r = rb_enc_precise_mbclen(p, e, enc);
+ if (!MBCLEN_CHARFOUND_P(r)) {
+ /* invalid byte shall not happen but */
+ static const char invalid[3] = "\xEF\xBF\xBD";
+ rb_str_append_normalized_ospath(str, p1, p-p1);
+ rb_str_cat(str, invalid, sizeof(invalid));
+ p += 1;
+ p1 = p;
+ continue;
+ }
+ l = MBCLEN_CHARFOUND_LEN(r);
+ c = rb_enc_mbc_to_codepoint(p, e, enc);
+ if ((0x2000 <= c && c <= 0x2FFF) || (0xF900 <= c && c <= 0xFAFF) ||
+ (0x2F800 <= c && c <= 0x2FAFF)) {
+ if (p - p1 > 0) {
+ rb_str_append_normalized_ospath(str, p1, p-p1);
+ }
+ rb_str_cat(str, p, l);
+ p += l;
+ p1 = p;
+ }
+ else {
+ p += l;
+ }
}
if (p - p1 > 0) {
- rb_str_append_normalized_ospath(str, p1, p-p1);
+ rb_str_append_normalized_ospath(str, p1, p-p1);
}
return str;
@@ -343,27 +385,27 @@ ignored_char_p(const char *p, const char *e, rb_encoding *enc)
if (p+3 > e) return 0;
switch ((unsigned char)*p) {
case 0xe2:
- switch ((unsigned char)p[1]) {
- case 0x80:
- c = (unsigned char)p[2];
- /* c >= 0x200c && c <= 0x200f */
- if (c >= 0x8c && c <= 0x8f) return 3;
- /* c >= 0x202a && c <= 0x202e */
- if (c >= 0xaa && c <= 0xae) return 3;
- return 0;
- case 0x81:
- c = (unsigned char)p[2];
- /* c >= 0x206a && c <= 0x206f */
- if (c >= 0xaa && c <= 0xaf) return 3;
- return 0;
- }
- break;
+ switch ((unsigned char)p[1]) {
+ case 0x80:
+ c = (unsigned char)p[2];
+ /* c >= 0x200c && c <= 0x200f */
+ if (c >= 0x8c && c <= 0x8f) return 3;
+ /* c >= 0x202a && c <= 0x202e */
+ if (c >= 0xaa && c <= 0xae) return 3;
+ return 0;
+ case 0x81:
+ c = (unsigned char)p[2];
+ /* c >= 0x206a && c <= 0x206f */
+ if (c >= 0xaa && c <= 0xaf) return 3;
+ return 0;
+ }
+ break;
case 0xef:
- /* c == 0xfeff */
- if ((unsigned char)p[1] == 0xbb &&
- (unsigned char)p[2] == 0xbf)
- return 3;
- break;
+ /* c == 0xfeff */
+ if ((unsigned char)p[1] == 0xbb &&
+ (unsigned char)p[2] == 0xbf)
+ return 3;
+ break;
}
return 0;
}
@@ -393,10 +435,10 @@ no_gvl_apply2files(void *ptr)
struct apply_arg *aa = ptr;
for (aa->i = 0; aa->i < aa->argc; aa->i++) {
- if (aa->func(aa->fn[aa->i].ptr, aa->arg) < 0) {
- aa->errnum = errno;
- break;
- }
+ if (aa->func(aa->fn[aa->i].ptr, aa->arg) < 0) {
+ aa->errnum = errno;
+ break;
+ }
}
return 0;
}
@@ -420,63 +462,28 @@ apply2files(int (*func)(const char *, void *), int argc, VALUE *argv, void *arg)
aa->func = func;
for (aa->i = 0; aa->i < argc; aa->i++) {
- VALUE path = rb_get_path(argv[aa->i]);
+ VALUE path = rb_get_path(argv[aa->i]);
- path = rb_str_encode_ospath(path);
- aa->fn[aa->i].ptr = RSTRING_PTR(path);
- aa->fn[aa->i].path = path;
+ path = rb_str_encode_ospath(path);
+ aa->fn[aa->i].ptr = RSTRING_PTR(path);
+ aa->fn[aa->i].path = path;
}
rb_thread_call_without_gvl(no_gvl_apply2files, aa, RUBY_UBF_IO, 0);
if (aa->errnum) {
#ifdef UTIME_EINVAL
- if (func == utime_internal) {
- utime_failed(aa);
- }
+ if (func == utime_internal) {
+ utime_failed(aa);
+ }
#endif
- rb_syserr_fail_path(aa->errnum, aa->fn[aa->i].path);
+ rb_syserr_fail_path(aa->errnum, aa->fn[aa->i].path);
}
if (v) {
- ALLOCV_END(v);
+ ALLOCV_END(v);
}
return LONG2FIX(argc);
}
-/*
- * call-seq:
- * path -> filepath
- *
- * Returns the string filepath used to create +self+:
- *
- * f = File.new('t.txt') # => #<File:t.txt>
- f.path # => "t.txt"
- *
- * Does not normalize the returned filepath:
- *
- * f = File.new('../files/t.txt') # => #<File:../files/t.txt>
- f.path # => "../files/t.txt"
- *
- * Raises IOError for a file created using File::Constants::TMPFILE, because it has no filename.
- *
- * File#to_path is an alias for File#path.
- *
- */
-
-static VALUE
-rb_file_path(VALUE obj)
-{
- rb_io_t *fptr;
-
- fptr = RFILE(rb_io_taint_check(obj))->fptr;
- rb_io_check_initialized(fptr);
-
- if (NIL_P(fptr->pathv)) {
- rb_raise(rb_eIOError, "File is unnamed (TMPFILE?)");
- }
-
- return rb_str_dup(fptr->pathv);
-}
-
static size_t
stat_memsize(const void *p)
{
@@ -496,9 +503,9 @@ stat_new_0(VALUE klass, const struct stat *st)
VALUE obj = TypedData_Wrap_Struct(klass, &stat_data_type, 0);
if (st) {
- nst = ALLOC(struct stat);
- *nst = *st;
- RTYPEDDATA_DATA(obj) = nst;
+ nst = ALLOC(struct stat);
+ *nst = *st;
+ RTYPEDDATA_DATA(obj) = nst;
}
return obj;
}
@@ -1065,24 +1072,24 @@ rb_stat_inspect(VALUE self)
VALUE str;
size_t i;
static const struct {
- const char *name;
- VALUE (*func)(VALUE);
+ const char *name;
+ VALUE (*func)(VALUE);
} member[] = {
- {"dev", rb_stat_dev},
- {"ino", rb_stat_ino},
- {"mode", rb_stat_mode},
- {"nlink", rb_stat_nlink},
- {"uid", rb_stat_uid},
- {"gid", rb_stat_gid},
- {"rdev", rb_stat_rdev},
- {"size", rb_stat_size},
- {"blksize", rb_stat_blksize},
- {"blocks", rb_stat_blocks},
- {"atime", rb_stat_atime},
- {"mtime", rb_stat_mtime},
- {"ctime", rb_stat_ctime},
+ {"dev", rb_stat_dev},
+ {"ino", rb_stat_ino},
+ {"mode", rb_stat_mode},
+ {"nlink", rb_stat_nlink},
+ {"uid", rb_stat_uid},
+ {"gid", rb_stat_gid},
+ {"rdev", rb_stat_rdev},
+ {"size", rb_stat_size},
+ {"blksize", rb_stat_blksize},
+ {"blocks", rb_stat_blocks},
+ {"atime", rb_stat_atime},
+ {"mtime", rb_stat_mtime},
+ {"ctime", rb_stat_ctime},
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
- {"birthtime", rb_stat_birthtime},
+ {"birthtime", rb_stat_birthtime},
#endif
};
@@ -1097,23 +1104,23 @@ rb_stat_inspect(VALUE self)
rb_str_buf_cat2(str, " ");
for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
- VALUE v;
-
- if (i > 0) {
- rb_str_buf_cat2(str, ", ");
- }
- rb_str_buf_cat2(str, member[i].name);
- rb_str_buf_cat2(str, "=");
- v = (*member[i].func)(self);
- if (i == 2) { /* mode */
- rb_str_catf(str, "0%lo", (unsigned long)NUM2ULONG(v));
- }
- else if (i == 0 || i == 6) { /* dev/rdev */
- rb_str_catf(str, "0x%"PRI_DEVT_PREFIX"x", NUM2DEVT(v));
- }
- else {
- rb_str_append(str, rb_inspect(v));
- }
+ VALUE v;
+
+ if (i > 0) {
+ rb_str_buf_cat2(str, ", ");
+ }
+ rb_str_buf_cat2(str, member[i].name);
+ rb_str_buf_cat2(str, "=");
+ v = (*member[i].func)(self);
+ if (i == 2) { /* mode */
+ rb_str_catf(str, "0%lo", (unsigned long)NUM2ULONG(v));
+ }
+ else if (i == 0 || i == 6) { /* dev/rdev */
+ rb_str_catf(str, "0x%"PRI_DEVT_PREFIX"x", NUM2DEVT(v));
+ }
+ else {
+ rb_str_append(str, rb_inspect(v));
+ }
}
rb_str_buf_cat2(str, ">");
@@ -1123,8 +1130,8 @@ rb_stat_inspect(VALUE self)
typedef struct no_gvl_stat_data {
struct stat *st;
union {
- const char *path;
- int fd;
+ const char *path;
+ int fd;
} file;
} no_gvl_stat_data;
@@ -1162,7 +1169,7 @@ stat_without_gvl(const char *path, struct stat *st)
data.st = st;
return (int)(VALUE)rb_thread_call_without_gvl(no_gvl_stat, &data,
- RUBY_UBF_IO, NULL);
+ RUBY_UBF_IO, NULL);
}
#if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC) && \
@@ -1292,16 +1299,16 @@ rb_stat(VALUE file, struct stat *st)
tmp = rb_check_convert_type_with_id(file, T_FILE, "IO", idTo_io);
if (!NIL_P(tmp)) {
- rb_io_t *fptr;
+ rb_io_t *fptr;
- GetOpenFile(tmp, fptr);
- result = fstat_without_gvl(fptr->fd, st);
- file = tmp;
+ GetOpenFile(tmp, fptr);
+ result = fstat_without_gvl(fptr->fd, st);
+ file = tmp;
}
else {
- FilePathValue(file);
- file = rb_str_encode_ospath(file);
- result = stat_without_gvl(RSTRING_PTR(file), st);
+ FilePathValue(file);
+ file = rb_str_encode_ospath(file);
+ result = stat_without_gvl(RSTRING_PTR(file), st);
}
RB_GC_GUARD(file);
return result;
@@ -1325,7 +1332,7 @@ rb_file_s_stat(VALUE klass, VALUE fname)
FilePathValue(fname);
fname = rb_str_encode_ospath(fname);
if (stat_without_gvl(RSTRING_PTR(fname), &st) < 0) {
- rb_sys_fail_path(fname);
+ rb_sys_fail_path(fname);
}
return rb_stat_new(&st);
}
@@ -1353,7 +1360,7 @@ rb_io_stat(VALUE obj)
GetOpenFile(obj, fptr);
if (fstat(fptr->fd, &st) == -1) {
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
}
return rb_stat_new(&st);
}
@@ -1375,7 +1382,7 @@ lstat_without_gvl(const char *path, struct stat *st)
data.st = st;
return (int)(VALUE)rb_thread_call_without_gvl(no_gvl_lstat, &data,
- RUBY_UBF_IO, NULL);
+ RUBY_UBF_IO, NULL);
}
#endif /* HAVE_LSTAT */
@@ -1401,7 +1408,7 @@ rb_file_s_lstat(VALUE klass, VALUE fname)
FilePathValue(fname);
fname = rb_str_encode_ospath(fname);
if (lstat_without_gvl(StringValueCStr(fname), &st) == -1) {
- rb_sys_fail_path(fname);
+ rb_sys_fail_path(fname);
}
return rb_stat_new(&st);
#else
@@ -1435,7 +1442,7 @@ rb_file_lstat(VALUE obj)
if (NIL_P(fptr->pathv)) return Qnil;
path = rb_str_encode_ospath(fptr->pathv);
if (lstat_without_gvl(RSTRING_PTR(path), &st) == -1) {
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
}
return rb_stat_new(&st);
#else
@@ -1456,19 +1463,19 @@ rb_group_member(GETGROUPS_T gid)
int anum = -1;
if (getgid() == gid || getegid() == gid)
- return TRUE;
+ return TRUE;
groups = getgroups(0, NULL);
gary = ALLOCV_N(GETGROUPS_T, v, groups);
anum = getgroups(groups, gary);
while (--anum >= 0) {
- if (gary[anum] == gid) {
- rv = TRUE;
- break;
- }
+ if (gary[anum] == gid) {
+ rv = TRUE;
+ break;
+ }
}
if (v)
- ALLOCV_END(v);
+ ALLOCV_END(v);
return rv;
#endif
@@ -1494,28 +1501,28 @@ eaccess(const char *path, int mode)
/* no setuid nor setgid. run shortcut. */
if (getuid() == euid && getgid() == getegid())
- return access(path, mode);
+ return access(path, mode);
if (STAT(path, &st) < 0)
- return -1;
+ return -1;
if (euid == 0) {
- /* Root can read or write any file. */
- if (!(mode & X_OK))
- return 0;
+ /* Root can read or write any file. */
+ if (!(mode & X_OK))
+ return 0;
- /* Root can execute any file that has any one of the execute
- bits set. */
- if (st.st_mode & S_IXUGO)
- return 0;
+ /* Root can execute any file that has any one of the execute
+ bits set. */
+ if (st.st_mode & S_IXUGO)
+ return 0;
- return -1;
+ return -1;
}
if (st.st_uid == euid) /* owner */
- mode <<= 6;
+ mode <<= 6;
else if (rb_group_member(st.st_gid))
- mode <<= 3;
+ mode <<= 3;
if ((int)(st.st_mode & mode) == mode) return 0;
@@ -1550,7 +1557,7 @@ rb_eaccess(VALUE fname, int mode)
aa.mode = mode;
return (int)(VALUE)rb_thread_call_without_gvl(nogvl_eaccess, &aa,
- RUBY_UBF_IO, 0);
+ RUBY_UBF_IO, 0);
}
static void *
@@ -1572,7 +1579,7 @@ rb_access(VALUE fname, int mode)
aa.mode = mode;
return (int)(VALUE)rb_thread_call_without_gvl(nogvl_access, &aa,
- RUBY_UBF_IO, 0);
+ RUBY_UBF_IO, 0);
}
/*
@@ -1723,8 +1730,8 @@ rb_file_socket_p(VALUE obj, VALUE fname)
if (rb_stat(fname, &st) < 0) return Qfalse;
if (S_ISSOCK(st.st_mode)) return Qtrue;
-
#endif
+
return Qfalse;
}
@@ -1871,7 +1878,7 @@ rb_file_world_readable_p(VALUE obj, VALUE fname)
if (rb_stat(fname, &st) < 0) return Qnil;
if ((st.st_mode & (S_IROTH)) == S_IROTH) {
- return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
+ return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
}
#endif
return Qnil;
@@ -1935,7 +1942,7 @@ rb_file_world_writable_p(VALUE obj, VALUE fname)
if (rb_stat(fname, &st) < 0) return Qnil;
if ((st.st_mode & (S_IWOTH)) == S_IWOTH) {
- return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
+ return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
}
#endif
return Qnil;
@@ -2218,9 +2225,9 @@ rb_file_s_size(VALUE klass, VALUE fname)
struct stat st;
if (rb_stat(fname, &st) < 0) {
- int e = errno;
- FilePathValue(fname);
- rb_syserr_fail_path(e, fname);
+ int e = errno;
+ FilePathValue(fname);
+ rb_syserr_fail_path(e, fname);
}
return OFFT2NUM(st.st_size);
}
@@ -2231,36 +2238,36 @@ rb_file_ftype(const struct stat *st)
const char *t;
if (S_ISREG(st->st_mode)) {
- t = "file";
+ t = "file";
}
else if (S_ISDIR(st->st_mode)) {
- t = "directory";
+ t = "directory";
}
else if (S_ISCHR(st->st_mode)) {
- t = "characterSpecial";
+ t = "characterSpecial";
}
#ifdef S_ISBLK
else if (S_ISBLK(st->st_mode)) {
- t = "blockSpecial";
+ t = "blockSpecial";
}
#endif
#ifdef S_ISFIFO
else if (S_ISFIFO(st->st_mode)) {
- t = "fifo";
+ t = "fifo";
}
#endif
#ifdef S_ISLNK
else if (S_ISLNK(st->st_mode)) {
- t = "link";
+ t = "link";
}
#endif
#ifdef S_ISSOCK
else if (S_ISSOCK(st->st_mode)) {
- t = "socket";
+ t = "socket";
}
#endif
else {
- t = "unknown";
+ t = "unknown";
}
return rb_usascii_str_new2(t);
@@ -2289,7 +2296,7 @@ rb_file_s_ftype(VALUE klass, VALUE fname)
FilePathValue(fname);
fname = rb_str_encode_ospath(fname);
if (lstat_without_gvl(StringValueCStr(fname), &st) == -1) {
- rb_sys_fail_path(fname);
+ rb_sys_fail_path(fname);
}
return rb_file_ftype(&st);
@@ -2313,9 +2320,9 @@ rb_file_s_atime(VALUE klass, VALUE fname)
struct stat st;
if (rb_stat(fname, &st) < 0) {
- int e = errno;
- FilePathValue(fname);
- rb_syserr_fail_path(e, fname);
+ int e = errno;
+ FilePathValue(fname);
+ rb_syserr_fail_path(e, fname);
}
return stat_atime(&st);
}
@@ -2339,7 +2346,7 @@ rb_file_atime(VALUE obj)
GetOpenFile(obj, fptr);
if (fstat(fptr->fd, &st) == -1) {
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
}
return stat_atime(&st);
}
@@ -2362,9 +2369,9 @@ rb_file_s_mtime(VALUE klass, VALUE fname)
struct stat st;
if (rb_stat(fname, &st) < 0) {
- int e = errno;
- FilePathValue(fname);
- rb_syserr_fail_path(e, fname);
+ int e = errno;
+ FilePathValue(fname);
+ rb_syserr_fail_path(e, fname);
}
return stat_mtime(&st);
}
@@ -2387,7 +2394,7 @@ rb_file_mtime(VALUE obj)
GetOpenFile(obj, fptr);
if (fstat(fptr->fd, &st) == -1) {
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
}
return stat_mtime(&st);
}
@@ -2414,9 +2421,9 @@ rb_file_s_ctime(VALUE klass, VALUE fname)
struct stat st;
if (rb_stat(fname, &st) < 0) {
- int e = errno;
- FilePathValue(fname);
- rb_syserr_fail_path(e, fname);
+ int e = errno;
+ FilePathValue(fname);
+ rb_syserr_fail_path(e, fname);
}
return stat_ctime(&st);
}
@@ -2442,7 +2449,7 @@ rb_file_ctime(VALUE obj)
GetOpenFile(obj, fptr);
if (fstat(fptr->fd, &st) == -1) {
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
}
return stat_ctime(&st);
}
@@ -2468,9 +2475,9 @@ rb_file_s_birthtime(VALUE klass, VALUE fname)
statx_data st;
if (rb_statx(fname, &st, STATX_BTIME) < 0) {
- int e = errno;
- FilePathValue(fname);
- rb_syserr_fail_path(e, fname);
+ int e = errno;
+ FilePathValue(fname);
+ rb_syserr_fail_path(e, fname);
}
return statx_birthtime(&st, fname);
}
@@ -2499,7 +2506,7 @@ rb_file_birthtime(VALUE obj)
GetOpenFile(obj, fptr);
if (fstatx_without_gvl(fptr->fd, &st, STATX_BTIME) == -1) {
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
}
return statx_birthtime(&st, fptr->pathv);
}
@@ -2517,7 +2524,7 @@ rb_file_birthtime(VALUE obj)
*
*/
-off_t
+rb_off_t
rb_file_size(VALUE file)
{
if (RB_TYPE_P(file, T_FILE)) {
@@ -2603,18 +2610,18 @@ rb_file_chmod(VALUE obj, VALUE vmode)
GetOpenFile(obj, fptr);
#ifdef HAVE_FCHMOD
if (fchmod(fptr->fd, mode) == -1) {
- if (HAVE_FCHMOD || errno != ENOSYS)
- rb_sys_fail_path(fptr->pathv);
+ if (HAVE_FCHMOD || errno != ENOSYS)
+ rb_sys_fail_path(fptr->pathv);
}
else {
- if (!HAVE_FCHMOD) return INT2FIX(0);
+ if (!HAVE_FCHMOD) return INT2FIX(0);
}
#endif
#if !defined HAVE_FCHMOD || !HAVE_FCHMOD
if (NIL_P(fptr->pathv)) return Qnil;
path = rb_str_encode_ospath(fptr->pathv);
if (chmod(RSTRING_PTR(path), mode) == -1)
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
#endif
return INT2FIX(0);
@@ -2655,7 +2662,7 @@ static inline rb_uid_t
to_uid(VALUE u)
{
if (NIL_P(u)) {
- return (rb_uid_t)-1;
+ return (rb_uid_t)-1;
}
return NUM2UIDT(u);
}
@@ -2664,7 +2671,7 @@ static inline rb_gid_t
to_gid(VALUE g)
{
if (NIL_P(g)) {
- return (rb_gid_t)-1;
+ return (rb_gid_t)-1;
}
return NUM2GIDT(g);
}
@@ -2740,10 +2747,10 @@ rb_file_chown(VALUE obj, VALUE owner, VALUE group)
if (NIL_P(fptr->pathv)) return Qnil;
path = rb_str_encode_ospath(fptr->pathv);
if (chown(RSTRING_PTR(path), o, g) == -1)
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
#else
if (fchown(fptr->fd, o, g) == -1)
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
#endif
return INT2FIX(0);
@@ -2800,32 +2807,32 @@ utime_failed(struct apply_arg *aa)
struct utime_args *ua = aa->arg;
if (ua->tsp && e == EINVAL) {
- VALUE e[2], a = Qnil, m = Qnil;
- int d = 0;
- VALUE atime = ua->atime;
- VALUE mtime = ua->mtime;
-
- if (!NIL_P(atime)) {
- a = rb_inspect(atime);
- }
- if (!NIL_P(mtime) && mtime != atime && !rb_equal(atime, mtime)) {
- m = rb_inspect(mtime);
- }
- if (NIL_P(a)) e[0] = m;
- else if (NIL_P(m) || rb_str_cmp(a, m) == 0) e[0] = a;
- else {
- e[0] = rb_str_plus(a, rb_str_new_cstr(" or "));
- rb_str_append(e[0], m);
- d = 1;
- }
- if (!NIL_P(e[0])) {
- if (path) {
- if (!d) e[0] = rb_str_dup(e[0]);
- rb_str_append(rb_str_cat2(e[0], " for "), path);
- }
- e[1] = INT2FIX(EINVAL);
- rb_exc_raise(rb_class_new_instance(2, e, rb_eSystemCallError));
- }
+ VALUE e[2], a = Qnil, m = Qnil;
+ int d = 0;
+ VALUE atime = ua->atime;
+ VALUE mtime = ua->mtime;
+
+ if (!NIL_P(atime)) {
+ a = rb_inspect(atime);
+ }
+ if (!NIL_P(mtime) && mtime != atime && !rb_equal(atime, mtime)) {
+ m = rb_inspect(mtime);
+ }
+ if (NIL_P(a)) e[0] = m;
+ else if (NIL_P(m) || rb_str_cmp(a, m) == 0) e[0] = a;
+ else {
+ e[0] = rb_str_plus(a, rb_str_new_cstr(" or "));
+ rb_str_append(e[0], m);
+ d = 1;
+ }
+ if (!NIL_P(e[0])) {
+ if (path) {
+ if (!d) e[0] = rb_str_dup(e[0]);
+ rb_str_append(rb_str_cat2(e[0], " for "), path);
+ }
+ e[1] = INT2FIX(EINVAL);
+ rb_exc_raise(rb_class_new_instance(2, e, rb_eSystemCallError));
+ }
}
rb_syserr_fail_path(e, path);
}
@@ -2833,29 +2840,27 @@ utime_failed(struct apply_arg *aa)
#if defined(HAVE_UTIMES)
-# if defined(__APPLE__) && \
+# if !defined(HAVE_UTIMENSAT)
+/* utimensat() is not found, runtime check is not needed */
+# elif defined(__APPLE__) && \
(!defined(MAC_OS_X_VERSION_13_0) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_13_0))
# if defined(__has_attribute) && __has_attribute(availability)
typedef int utimensat_func(int, const char *, const struct timespec [2], int);
-RBIMPL_WARNING_PUSH();
-RBIMPL_WARNING_IGNORED(-Wunguarded-availability-new);
+RBIMPL_WARNING_PUSH()
+RBIMPL_WARNING_IGNORED(-Wunguarded-availability-new)
static inline utimensat_func *
rb_utimensat(void)
{
return &utimensat;
}
-RBIMPL_WARNING_POP();
+RBIMPL_WARNING_POP()
# define utimensat rb_utimensat()
# else /* __API_AVAILABLE macro does nothing on gcc */
__attribute__((weak)) int utimensat(int, const char *, const struct timespec [2], int);
# endif
-
-# define utimensat_available_p() (utimensat != NULL)
-# else
-# define utimensat_available_p() 1
# endif
static int
@@ -2882,14 +2887,14 @@ utime_internal(const char *path, void *arg)
if (v->follow ? try_utimensat_follow : try_utimensat) {
# ifdef AT_SYMLINK_NOFOLLOW
- if (v->follow) {
- flags = AT_SYMLINK_NOFOLLOW;
- }
+ if (v->follow) {
+ flags = AT_SYMLINK_NOFOLLOW;
+ }
# endif
- int result = utimensat(AT_FDCWD, path, tsp, flags);
+ int result = utimensat(AT_FDCWD, path, tsp, flags);
# ifdef TRY_UTIMENSAT
- if (result < 0 && errno == ENOSYS) {
+ if (result < 0 && errno == ENOSYS) {
# ifdef AT_SYMLINK_NOFOLLOW
try_utimensat_follow = 0;
# endif
@@ -2953,12 +2958,12 @@ utime_internal_i(int argc, VALUE *argv, int follow)
args.follow = follow;
if (!NIL_P(args.atime) || !NIL_P(args.mtime)) {
- tsp = tss;
- tsp[0] = rb_time_timespec(args.atime);
- if (args.atime == args.mtime)
- tsp[1] = tsp[0];
- else
- tsp[1] = rb_time_timespec(args.mtime);
+ tsp = tss;
+ tsp[0] = rb_time_timespec(args.atime);
+ if (args.atime == args.mtime)
+ tsp[1] = tsp[0];
+ else
+ tsp[1] = rb_time_timespec(args.mtime);
}
args.tsp = tsp;
@@ -3022,7 +3027,7 @@ syserr_fail2_in(const char *func, int e, VALUE s1, VALUE s2)
#endif
if (e == EEXIST) {
- rb_syserr_fail_path(e, rb_str_ellipsize(s2, max_pathlen));
+ rb_syserr_fail_path(e, rb_str_ellipsize(s2, max_pathlen));
}
str = rb_str_new_cstr("(");
rb_str_append(str, rb_str_ellipsize(s1, max_pathlen));
@@ -3058,7 +3063,7 @@ rb_file_s_link(VALUE klass, VALUE from, VALUE to)
to = rb_str_encode_ospath(to);
if (link(StringValueCStr(from), StringValueCStr(to)) < 0) {
- sys_fail2(from, to);
+ sys_fail2(from, to);
}
return INT2FIX(0);
}
@@ -3088,7 +3093,7 @@ rb_file_s_symlink(VALUE klass, VALUE from, VALUE to)
to = rb_str_encode_ospath(to);
if (symlink(StringValueCStr(from), StringValueCStr(to)) < 0) {
- sys_fail2(from, to);
+ sys_fail2(from, to);
}
return INT2FIX(0);
}
@@ -3114,7 +3119,6 @@ rb_file_s_readlink(VALUE klass, VALUE path)
return rb_readlink(path, rb_filesystem_encoding());
}
-#ifndef _WIN32
struct readlink_arg {
const char *path;
char *buf;
@@ -3139,7 +3143,7 @@ readlink_without_gvl(VALUE path, VALUE buf, size_t size)
ra.size = size;
return (ssize_t)rb_thread_call_without_gvl(nogvl_readlink, &ra,
- RUBY_UBF_IO, 0);
+ RUBY_UBF_IO, 0);
}
VALUE
@@ -3154,23 +3158,22 @@ rb_readlink(VALUE path, rb_encoding *enc)
v = rb_enc_str_new(0, size, enc);
while ((rv = readlink_without_gvl(path, v, size)) == size
#ifdef _AIX
- || (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */
+ || (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */
#endif
- ) {
- rb_str_modify_expand(v, size);
- size *= 2;
- rb_str_set_len(v, size);
+ ) {
+ rb_str_modify_expand(v, size);
+ size *= 2;
+ rb_str_set_len(v, size);
}
if (rv < 0) {
- int e = errno;
- rb_str_resize(v, 0);
- rb_syserr_fail_path(e, path);
+ int e = errno;
+ rb_str_resize(v, 0);
+ rb_syserr_fail_path(e, path);
}
rb_str_resize(v, rv);
return v;
}
-#endif
#else
#define rb_file_s_readlink rb_f_notimplement
#endif
@@ -3242,18 +3245,18 @@ rb_file_s_rename(VALUE klass, VALUE from, VALUE to)
errno = 0;
#endif
if ((int)(VALUE)rb_thread_call_without_gvl(no_gvl_rename, &ra,
- RUBY_UBF_IO, 0) < 0) {
- int e = errno;
+ RUBY_UBF_IO, 0) < 0) {
+ int e = errno;
#if defined DOSISH
- switch (e) {
- case EEXIST:
- if (chmod(ra.dst, 0666) == 0 &&
- unlink(ra.dst) == 0 &&
- rename(ra.src, ra.dst) == 0)
- return INT2FIX(0);
- }
+ switch (e) {
+ case EEXIST:
+ if (chmod(ra.dst, 0666) == 0 &&
+ unlink(ra.dst) == 0 &&
+ rename(ra.src, ra.dst) == 0)
+ return INT2FIX(0);
+ }
#endif
- syserr_fail2(e, from, to);
+ syserr_fail2(e, from, to);
}
return INT2FIX(0);
@@ -3281,11 +3284,11 @@ rb_file_s_umask(int argc, VALUE *argv, VALUE _)
switch (argc) {
case 0:
- omask = umask(0);
- umask(omask);
+ omask = umask(0);
+ umask(omask);
break;
case 1:
- omask = umask(NUM2MODET(argv[0]));
+ omask = umask(NUM2MODET(argv[0]));
break;
default:
rb_error_arity(argc, 0, 1);
@@ -3350,10 +3353,10 @@ static inline int
has_drive_letter(const char *buf)
{
if (ISALPHA(buf[0]) && buf[1] == ':') {
- return 1;
+ return 1;
}
else {
- return 0;
+ return 0;
}
}
@@ -3374,13 +3377,13 @@ getcwdofdrv(int drv)
*/
oldcwd = ruby_getcwd();
if (chdir(drive) == 0) {
- drvcwd = ruby_getcwd();
- chdir(oldcwd);
- xfree(oldcwd);
+ drvcwd = ruby_getcwd();
+ chdir(oldcwd);
+ xfree(oldcwd);
}
else {
- /* perhaps the drive is not exist. we return only drive letter */
- drvcwd = strdup(drive);
+ /* perhaps the drive is not exist. we return only drive letter */
+ drvcwd = strdup(drive);
}
return drvcwd;
}
@@ -3391,10 +3394,10 @@ not_same_drive(VALUE path, int drive)
const char *p = RSTRING_PTR(path);
if (RSTRING_LEN(path) < 2) return 0;
if (has_drive_letter(p)) {
- return TOLOWER(p[0]) != TOLOWER(drive);
+ return TOLOWER(p[0]) != TOLOWER(drive);
}
else {
- return has_unc(p);
+ return has_unc(p);
}
}
#endif
@@ -3415,7 +3418,7 @@ char *
rb_enc_path_next(const char *s, const char *e, rb_encoding *enc)
{
while (s < e && !isdirsep(*s)) {
- Inc(s, e, enc);
+ Inc(s, e, enc);
}
return (char *)s;
}
@@ -3431,16 +3434,16 @@ rb_enc_path_skip_prefix(const char *path, const char *end, rb_encoding *enc)
#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
#ifdef DOSISH_UNC
if (path + 2 <= end && isdirsep(path[0]) && isdirsep(path[1])) {
- path += 2;
- while (path < end && isdirsep(*path)) path++;
- if ((path = rb_enc_path_next(path, end, enc)) < end && path[0] && path[1] && !isdirsep(path[1]))
- path = rb_enc_path_next(path + 1, end, enc);
- return (char *)path;
+ path += 2;
+ while (path < end && isdirsep(*path)) path++;
+ if ((path = rb_enc_path_next(path, end, enc)) < end && path[0] && path[1] && !isdirsep(path[1]))
+ path = rb_enc_path_next(path + 1, end, enc);
+ return (char *)path;
}
#endif
#ifdef DOSISH_DRIVE_LETTER
if (has_drive_letter(path))
- return (char *)(path + 2);
+ return (char *)(path + 2);
#endif
#endif
return (char *)path;
@@ -3464,15 +3467,15 @@ rb_enc_path_last_separator(const char *path, const char *end, rb_encoding *enc)
{
char *last = NULL;
while (path < end) {
- if (isdirsep(*path)) {
- const char *tmp = path++;
- while (path < end && isdirsep(*path)) path++;
- if (path >= end) break;
- last = (char *)tmp;
- }
- else {
- Inc(path, end, enc);
- }
+ if (isdirsep(*path)) {
+ const char *tmp = path++;
+ while (path < end && isdirsep(*path)) path++;
+ if (path >= end) break;
+ last = (char *)tmp;
+ }
+ else {
+ Inc(path, end, enc);
+ }
}
return last;
}
@@ -3481,14 +3484,14 @@ static char *
chompdirsep(const char *path, const char *end, rb_encoding *enc)
{
while (path < end) {
- if (isdirsep(*path)) {
- const char *last = path++;
- while (path < end && isdirsep(*path)) path++;
- if (path >= end) return (char *)last;
- }
- else {
- Inc(path, end, enc);
- }
+ if (isdirsep(*path)) {
+ const char *last = path++;
+ while (path < end && isdirsep(*path)) path++;
+ if (path >= end) return (char *)last;
+ }
+ else {
+ Inc(path, end, enc);
+ }
}
return (char *)path;
}
@@ -3520,20 +3523,20 @@ ntfs_tail(const char *path, const char *end, rb_encoding *enc)
{
while (path < end && *path == '.') path++;
while (path < end && !isADS(*path)) {
- if (istrailinggarbage(*path)) {
- const char *last = path++;
- while (path < end && istrailinggarbage(*path)) path++;
- if (path >= end || isADS(*path)) return (char *)last;
- }
- else if (isdirsep(*path)) {
- const char *last = path++;
- while (path < end && isdirsep(*path)) path++;
- if (path >= end) return (char *)last;
- if (isADS(*path)) path++;
- }
- else {
- Inc(path, end, enc);
- }
+ if (istrailinggarbage(*path)) {
+ const char *last = path++;
+ while (path < end && istrailinggarbage(*path)) path++;
+ if (path >= end || isADS(*path)) return (char *)last;
+ }
+ else if (isdirsep(*path)) {
+ const char *last = path++;
+ while (path < end && isdirsep(*path)) path++;
+ if (path >= end) return (char *)last;
+ if (isADS(*path)) path++;
+ }
+ else {
+ Inc(path, end, enc);
+ }
}
return (char *)path;
}
@@ -3542,11 +3545,11 @@ ntfs_tail(const char *path, const char *end, rb_encoding *enc)
#define BUFCHECK(cond) do {\
bdiff = p - buf;\
if (cond) {\
- do {buflen *= 2;} while (cond);\
- rb_str_resize(result, buflen);\
- buf = RSTRING_PTR(result);\
- p = buf + bdiff;\
- pend = buf + buflen;\
+ do {buflen *= 2;} while (cond);\
+ rb_str_resize(result, buflen);\
+ buf = RSTRING_PTR(result);\
+ p = buf + bdiff;\
+ pend = buf + buflen;\
}\
} while (0)
@@ -3595,9 +3598,9 @@ copy_home_path(VALUE result, const char *dir)
#if defined DOSISH || defined __CYGWIN__
enc = rb_enc_from_index(encidx);
for (bend = (p = buf) + dirlen; p < bend; Inc(p, bend, enc)) {
- if (*p == '\\') {
- *p = '/';
- }
+ if (*p == '\\') {
+ *p = '/';
+ }
}
#endif
return result;
@@ -3628,11 +3631,11 @@ rb_home_dir_of(VALUE user, VALUE result)
pwPtr = getpwnam(username);
#else
if (strcasecmp(username, getlogin()) == 0)
- dir = pwPtr = getenv("HOME");
+ dir = pwPtr = getenv("HOME");
#endif
if (!pwPtr) {
- endpwent();
- rb_raise(rb_eArgError, "user %"PRIsVALUE" doesn't exist", user);
+ endpwent();
+ rb_raise(rb_eArgError, "user %"PRIsVALUE" doesn't exist", user);
}
#ifdef HAVE_PWD_H
dir = pwPtr->pw_dir;
@@ -3689,7 +3692,7 @@ rb_default_home_dir(VALUE result)
}
#endif
if (!dir) {
- rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
+ rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
}
return copy_home_path(result, dir);
}
@@ -3714,15 +3717,15 @@ append_fspath(VALUE result, VALUE fname, char *dir, rb_encoding **enc, rb_encodi
size_t dirlen = strlen(dir), buflen = rb_str_capacity(result);
if (NORMALIZE_UTF8PATH || *enc != fsenc) {
- rb_encoding *direnc = fs_enc_check(fname, dirname = ospath_new(dir, dirlen, fsenc));
- if (direnc != fsenc) {
- dirname = rb_str_conv_enc(dirname, fsenc, direnc);
- RSTRING_GETMEM(dirname, cwdp, dirlen);
- }
- else if (NORMALIZE_UTF8PATH) {
- RSTRING_GETMEM(dirname, cwdp, dirlen);
- }
- *enc = direnc;
+ rb_encoding *direnc = fs_enc_check(fname, dirname = ospath_new(dir, dirlen, fsenc));
+ if (direnc != fsenc) {
+ dirname = rb_str_conv_enc(dirname, fsenc, direnc);
+ RSTRING_GETMEM(dirname, cwdp, dirlen);
+ }
+ else if (NORMALIZE_UTF8PATH) {
+ RSTRING_GETMEM(dirname, cwdp, dirlen);
+ }
+ *enc = direnc;
}
do {buflen *= 2;} while (dirlen > buflen);
rb_str_resize(result, buflen);
@@ -3748,115 +3751,115 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
BUFINIT();
if (s[0] == '~' && abs_mode == 0) { /* execute only if NOT absolute_path() */
- long userlen = 0;
- if (isdirsep(s[1]) || s[1] == '\0') {
- buf = 0;
- b = 0;
- rb_str_set_len(result, 0);
- if (*++s) ++s;
- rb_default_home_dir(result);
- }
- else {
- s = nextdirsep(b = s, fend, enc);
- b++; /* b[0] is '~' */
- userlen = s - b;
- BUFCHECK(bdiff + userlen >= buflen);
- memcpy(p, b, userlen);
- ENC_CODERANGE_CLEAR(result);
- rb_str_set_len(result, userlen);
- rb_enc_associate(result, enc);
- rb_home_dir_of(result, result);
- buf = p + 1;
- p += userlen;
- }
- if (!rb_is_absolute_path(RSTRING_PTR(result))) {
- if (userlen) {
- rb_enc_raise(enc, rb_eArgError, "non-absolute home of %.*s%.0"PRIsVALUE,
- (int)userlen, b, fname);
- }
- else {
- rb_raise(rb_eArgError, "non-absolute home");
- }
- }
- BUFINIT();
- p = pend;
+ long userlen = 0;
+ if (isdirsep(s[1]) || s[1] == '\0') {
+ buf = 0;
+ b = 0;
+ rb_str_set_len(result, 0);
+ if (*++s) ++s;
+ rb_default_home_dir(result);
+ }
+ else {
+ s = nextdirsep(b = s, fend, enc);
+ b++; /* b[0] is '~' */
+ userlen = s - b;
+ BUFCHECK(bdiff + userlen >= buflen);
+ memcpy(p, b, userlen);
+ ENC_CODERANGE_CLEAR(result);
+ rb_str_set_len(result, userlen);
+ rb_enc_associate(result, enc);
+ rb_home_dir_of(result, result);
+ buf = p + 1;
+ p += userlen;
+ }
+ if (!rb_is_absolute_path(RSTRING_PTR(result))) {
+ if (userlen) {
+ rb_enc_raise(enc, rb_eArgError, "non-absolute home of %.*s%.0"PRIsVALUE,
+ (int)userlen, b, fname);
+ }
+ else {
+ rb_raise(rb_eArgError, "non-absolute home");
+ }
+ }
+ BUFINIT();
+ p = pend;
}
#ifdef DOSISH_DRIVE_LETTER
/* skip drive letter */
else if (has_drive_letter(s)) {
- if (isdirsep(s[2])) {
- /* specified drive letter, and full path */
- /* skip drive letter */
- BUFCHECK(bdiff + 2 >= buflen);
- memcpy(p, s, 2);
- p += 2;
- s += 2;
- rb_enc_copy(result, fname);
- }
- else {
- /* specified drive, but not full path */
- int same = 0;
- if (!NIL_P(dname) && !not_same_drive(dname, s[0])) {
- rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
- BUFINIT();
- if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
- /* ok, same drive */
- same = 1;
- }
- }
- if (!same) {
- char *e = append_fspath(result, fname, getcwdofdrv(*s), &enc, fsenc);
- BUFINIT();
- p = e;
- }
- else {
- rb_enc_associate(result, enc = fs_enc_check(result, fname));
- p = pend;
- }
- p = chompdirsep(skiproot(buf, p, enc), p, enc);
- s += 2;
- }
+ if (isdirsep(s[2])) {
+ /* specified drive letter, and full path */
+ /* skip drive letter */
+ BUFCHECK(bdiff + 2 >= buflen);
+ memcpy(p, s, 2);
+ p += 2;
+ s += 2;
+ rb_enc_copy(result, fname);
+ }
+ else {
+ /* specified drive, but not full path */
+ int same = 0;
+ if (!NIL_P(dname) && !not_same_drive(dname, s[0])) {
+ rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
+ BUFINIT();
+ if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
+ /* ok, same drive */
+ same = 1;
+ }
+ }
+ if (!same) {
+ char *e = append_fspath(result, fname, getcwdofdrv(*s), &enc, fsenc);
+ BUFINIT();
+ p = e;
+ }
+ else {
+ rb_enc_associate(result, enc = fs_enc_check(result, fname));
+ p = pend;
+ }
+ p = chompdirsep(skiproot(buf, p, enc), p, enc);
+ s += 2;
+ }
}
#endif
else if (!rb_is_absolute_path(s)) {
- if (!NIL_P(dname)) {
- rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
- rb_enc_associate(result, fs_enc_check(result, fname));
- BUFINIT();
- p = pend;
- }
- else {
- char *e = append_fspath(result, fname, ruby_getcwd(), &enc, fsenc);
- BUFINIT();
- p = e;
- }
+ if (!NIL_P(dname)) {
+ rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
+ rb_enc_associate(result, fs_enc_check(result, fname));
+ BUFINIT();
+ p = pend;
+ }
+ else {
+ char *e = append_fspath(result, fname, ruby_getcwd(), &enc, fsenc);
+ BUFINIT();
+ p = e;
+ }
#if defined DOSISH || defined __CYGWIN__
- if (isdirsep(*s)) {
- /* specified full path, but not drive letter nor UNC */
- /* we need to get the drive letter or UNC share name */
- p = skipprefix(buf, p, enc);
- }
- else
+ if (isdirsep(*s)) {
+ /* specified full path, but not drive letter nor UNC */
+ /* we need to get the drive letter or UNC share name */
+ p = skipprefix(buf, p, enc);
+ }
+ else
#endif
- p = chompdirsep(skiproot(buf, p, enc), p, enc);
+ p = chompdirsep(skiproot(buf, p, enc), p, enc);
}
else {
- size_t len;
- b = s;
- do s++; while (isdirsep(*s));
- len = s - b;
- p = buf + len;
- BUFCHECK(bdiff >= buflen);
- memset(buf, '/', len);
- rb_str_set_len(result, len);
- rb_enc_associate(result, fs_enc_check(result, fname));
+ size_t len;
+ b = s;
+ do s++; while (isdirsep(*s));
+ len = s - b;
+ p = buf + len;
+ BUFCHECK(bdiff >= buflen);
+ memset(buf, '/', len);
+ rb_str_set_len(result, len);
+ rb_enc_associate(result, fs_enc_check(result, fname));
}
if (p > buf && p[-1] == '/')
- --p;
+ --p;
else {
- rb_str_set_len(result, p-buf);
- BUFCHECK(bdiff + 1 >= buflen);
- *p = '/';
+ rb_str_set_len(result, p-buf);
+ BUFCHECK(bdiff + 1 >= buflen);
+ *p = '/';
}
rb_str_set_len(result, p-buf+1);
@@ -3866,213 +3869,213 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
b = s;
while (*s) {
- switch (*s) {
- case '.':
- if (b == s++) { /* beginning of path element */
- switch (*s) {
- case '\0':
- b = s;
- break;
- case '.':
- if (*(s+1) == '\0' || isdirsep(*(s+1))) {
- /* We must go back to the parent */
- char *n;
- *p = '\0';
- if (!(n = strrdirsep(root, p, enc))) {
- *p = '/';
- }
- else {
- p = n;
- }
- b = ++s;
- }
+ switch (*s) {
+ case '.':
+ if (b == s++) { /* beginning of path element */
+ switch (*s) {
+ case '\0':
+ b = s;
+ break;
+ case '.':
+ if (*(s+1) == '\0' || isdirsep(*(s+1))) {
+ /* We must go back to the parent */
+ char *n;
+ *p = '\0';
+ if (!(n = strrdirsep(root, p, enc))) {
+ *p = '/';
+ }
+ else {
+ p = n;
+ }
+ b = ++s;
+ }
#if USE_NTFS
- else {
- do ++s; while (istrailinggarbage(*s));
- }
+ else {
+ do ++s; while (istrailinggarbage(*s));
+ }
#endif
- break;
- case '/':
+ break;
+ case '/':
#if defined DOSISH || defined __CYGWIN__
- case '\\':
-#endif
- b = ++s;
- break;
- default:
- /* ordinary path element, beginning don't move */
- break;
- }
- }
+ case '\\':
+#endif
+ b = ++s;
+ break;
+ default:
+ /* ordinary path element, beginning don't move */
+ break;
+ }
+ }
#if USE_NTFS
- else {
- --s;
- case ' ': {
- const char *e = s;
- while (s < fend && istrailinggarbage(*s)) s++;
- if (s >= fend) {
- s = e;
- goto endpath;
- }
- }
- }
-#endif
- break;
- case '/':
+ else {
+ --s;
+ case ' ': {
+ const char *e = s;
+ while (s < fend && istrailinggarbage(*s)) s++;
+ if (s >= fend) {
+ s = e;
+ goto endpath;
+ }
+ }
+ }
+#endif
+ break;
+ case '/':
#if defined DOSISH || defined __CYGWIN__
- case '\\':
-#endif
- if (s > b) {
- WITH_ROOTDIFF(BUFCOPY(b, s-b));
- *p = '/';
- }
- b = ++s;
- break;
- default:
+ case '\\':
+#endif
+ if (s > b) {
+ WITH_ROOTDIFF(BUFCOPY(b, s-b));
+ *p = '/';
+ }
+ b = ++s;
+ break;
+ default:
#ifdef __APPLE__
- {
- int n = ignored_char_p(s, fend, enc);
- if (n) {
- if (s > b) {
- WITH_ROOTDIFF(BUFCOPY(b, s-b));
- *p = '\0';
- }
- b = s += n;
- break;
- }
- }
-#endif
- Inc(s, fend, enc);
- break;
- }
+ {
+ int n = ignored_char_p(s, fend, enc);
+ if (n) {
+ if (s > b) {
+ WITH_ROOTDIFF(BUFCOPY(b, s-b));
+ *p = '\0';
+ }
+ b = s += n;
+ break;
+ }
+ }
+#endif
+ Inc(s, fend, enc);
+ break;
+ }
}
if (s > b) {
#if USE_NTFS
# if USE_NTFS_ADS
- static const char prime[] = ":$DATA";
- enum {prime_len = sizeof(prime) -1};
+ static const char prime[] = ":$DATA";
+ enum {prime_len = sizeof(prime) -1};
# endif
endpath:
# if USE_NTFS_ADS
- if (s > b + prime_len && strncasecmp(s - prime_len, prime, prime_len) == 0) {
- /* alias of stream */
- /* get rid of a bug of x64 VC++ */
- if (isADS(*(s - (prime_len+1)))) {
- s -= prime_len + 1; /* prime */
- }
- else if (memchr(b, ':', s - prime_len - b)) {
- s -= prime_len; /* alternative */
- }
- }
+ if (s > b + prime_len && strncasecmp(s - prime_len, prime, prime_len) == 0) {
+ /* alias of stream */
+ /* get rid of a bug of x64 VC++ */
+ if (isADS(*(s - (prime_len+1)))) {
+ s -= prime_len + 1; /* prime */
+ }
+ else if (memchr(b, ':', s - prime_len - b)) {
+ s -= prime_len; /* alternative */
+ }
+ }
# endif
#endif
- BUFCOPY(b, s-b);
- rb_str_set_len(result, p-buf);
+ BUFCOPY(b, s-b);
+ rb_str_set_len(result, p-buf);
}
if (p == skiproot(buf, p + !!*p, enc) - 1) p++;
#if USE_NTFS
*p = '\0';
if ((s = strrdirsep(b = buf, p, enc)) != 0 && !strpbrk(s, "*?")) {
- VALUE tmp, v;
- size_t len;
- int encidx;
- WCHAR *wstr;
- WIN32_FIND_DATAW wfd;
- HANDLE h;
+ VALUE tmp, v;
+ size_t len;
+ int encidx;
+ WCHAR *wstr;
+ WIN32_FIND_DATAW wfd;
+ HANDLE h;
#ifdef __CYGWIN__
#ifdef HAVE_CYGWIN_CONV_PATH
- char *w32buf = NULL;
- const int flags = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
+ char *w32buf = NULL;
+ const int flags = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
#else
- char w32buf[MAXPATHLEN];
-#endif
- const char *path;
- ssize_t bufsize;
- int lnk_added = 0, is_symlink = 0;
- struct stat st;
- p = (char *)s;
- len = strlen(p);
- if (lstat_without_gvl(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
- is_symlink = 1;
- if (len > 4 && STRCASECMP(p + len - 4, ".lnk") != 0) {
- lnk_added = 1;
- }
- }
- path = *buf ? buf : "/";
+ char w32buf[MAXPATHLEN];
+#endif
+ const char *path;
+ ssize_t bufsize;
+ int lnk_added = 0, is_symlink = 0;
+ struct stat st;
+ p = (char *)s;
+ len = strlen(p);
+ if (lstat_without_gvl(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
+ is_symlink = 1;
+ if (len > 4 && STRCASECMP(p + len - 4, ".lnk") != 0) {
+ lnk_added = 1;
+ }
+ }
+ path = *buf ? buf : "/";
#ifdef HAVE_CYGWIN_CONV_PATH
- bufsize = cygwin_conv_path(flags, path, NULL, 0);
- if (bufsize > 0) {
- bufsize += len;
- if (lnk_added) bufsize += 4;
- w32buf = ALLOCA_N(char, bufsize);
- if (cygwin_conv_path(flags, path, w32buf, bufsize) == 0) {
- b = w32buf;
- }
- }
+ bufsize = cygwin_conv_path(flags, path, NULL, 0);
+ if (bufsize > 0) {
+ bufsize += len;
+ if (lnk_added) bufsize += 4;
+ w32buf = ALLOCA_N(char, bufsize);
+ if (cygwin_conv_path(flags, path, w32buf, bufsize) == 0) {
+ b = w32buf;
+ }
+ }
#else
- bufsize = MAXPATHLEN;
- if (cygwin_conv_to_win32_path(path, w32buf) == 0) {
- b = w32buf;
- }
-#endif
- if (is_symlink && b == w32buf) {
- *p = '\\';
- strlcat(w32buf, p, bufsize);
- if (lnk_added) {
- strlcat(w32buf, ".lnk", bufsize);
- }
- }
- else {
- lnk_added = 0;
- }
- *p = '/';
-#endif
- rb_str_set_len(result, p - buf + strlen(p));
- encidx = ENCODING_GET(result);
- tmp = result;
- if (encidx != ENCINDEX_UTF_8 && !is_ascii_string(result)) {
- tmp = rb_str_encode_ospath(result);
- }
- len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
- wstr = ALLOCV_N(WCHAR, v, len);
- MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, wstr, len);
- if (tmp != result) rb_str_set_len(tmp, 0);
- h = FindFirstFileW(wstr, &wfd);
- ALLOCV_END(v);
- if (h != INVALID_HANDLE_VALUE) {
- size_t wlen;
- FindClose(h);
- len = lstrlenW(wfd.cFileName);
+ bufsize = MAXPATHLEN;
+ if (cygwin_conv_to_win32_path(path, w32buf) == 0) {
+ b = w32buf;
+ }
+#endif
+ if (is_symlink && b == w32buf) {
+ *p = '\\';
+ strlcat(w32buf, p, bufsize);
+ if (lnk_added) {
+ strlcat(w32buf, ".lnk", bufsize);
+ }
+ }
+ else {
+ lnk_added = 0;
+ }
+ *p = '/';
+#endif
+ rb_str_set_len(result, p - buf + strlen(p));
+ encidx = ENCODING_GET(result);
+ tmp = result;
+ if (encidx != ENCINDEX_UTF_8 && !is_ascii_string(result)) {
+ tmp = rb_str_encode_ospath(result);
+ }
+ len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
+ wstr = ALLOCV_N(WCHAR, v, len);
+ MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, wstr, len);
+ if (tmp != result) rb_str_set_len(tmp, 0);
+ h = FindFirstFileW(wstr, &wfd);
+ ALLOCV_END(v);
+ if (h != INVALID_HANDLE_VALUE) {
+ size_t wlen;
+ FindClose(h);
+ len = lstrlenW(wfd.cFileName);
#ifdef __CYGWIN__
- if (lnk_added && len > 4 &&
- wcscasecmp(wfd.cFileName + len - 4, L".lnk") == 0) {
- wfd.cFileName[len -= 4] = L'\0';
- }
+ if (lnk_added && len > 4 &&
+ wcscasecmp(wfd.cFileName + len - 4, L".lnk") == 0) {
+ wfd.cFileName[len -= 4] = L'\0';
+ }
#else
- p = (char *)s;
-#endif
- ++p;
- wlen = (int)len;
- len = WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, NULL, 0, NULL, NULL);
- if (tmp == result) {
- BUFCHECK(bdiff + len >= buflen);
- WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, p, len + 1, NULL, NULL);
- }
- else {
- rb_str_modify_expand(tmp, len);
- WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, RSTRING_PTR(tmp), len + 1, NULL, NULL);
- rb_str_cat_conv_enc_opts(result, bdiff, RSTRING_PTR(tmp), len,
- rb_utf8_encoding(), 0, Qnil);
- BUFINIT();
- rb_str_resize(tmp, 0);
- }
- p += len;
- }
+ p = (char *)s;
+#endif
+ ++p;
+ wlen = (int)len;
+ len = WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, NULL, 0, NULL, NULL);
+ if (tmp == result) {
+ BUFCHECK(bdiff + len >= buflen);
+ WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, p, len + 1, NULL, NULL);
+ }
+ else {
+ rb_str_modify_expand(tmp, len);
+ WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, RSTRING_PTR(tmp), len + 1, NULL, NULL);
+ rb_str_cat_conv_enc_opts(result, bdiff, RSTRING_PTR(tmp), len,
+ rb_utf8_encoding(), 0, Qnil);
+ BUFINIT();
+ rb_str_resize(tmp, 0);
+ }
+ p += len;
+ }
#ifdef __CYGWIN__
- else {
- p += strlen(p);
- }
+ else {
+ p += strlen(p);
+ }
#endif
}
#endif
@@ -4221,27 +4224,27 @@ enum rb_realpath_mode {
static int
realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE fallback,
- VALUE loopcheck, enum rb_realpath_mode mode, int last)
+ VALUE loopcheck, enum rb_realpath_mode mode, int last)
{
const char *pend = unresolved + strlen(unresolved);
rb_encoding *enc = rb_enc_get(*resolvedp);
ID resolving;
CONST_ID(resolving, "resolving");
while (unresolved < pend) {
- const char *testname = unresolved;
- const char *unresolved_firstsep = rb_enc_path_next(unresolved, pend, enc);
- long testnamelen = unresolved_firstsep - unresolved;
- const char *unresolved_nextname = unresolved_firstsep;
+ const char *testname = unresolved;
+ const char *unresolved_firstsep = rb_enc_path_next(unresolved, pend, enc);
+ long testnamelen = unresolved_firstsep - unresolved;
+ const char *unresolved_nextname = unresolved_firstsep;
while (unresolved_nextname < pend && isdirsep(*unresolved_nextname))
- unresolved_nextname++;
+ unresolved_nextname++;
unresolved = unresolved_nextname;
if (testnamelen == 1 && testname[0] == '.') {
}
else if (testnamelen == 2 && testname[0] == '.' && testname[1] == '.') {
if (*prefixlenp < RSTRING_LEN(*resolvedp)) {
- const char *resolved_str = RSTRING_PTR(*resolvedp);
- const char *resolved_names = resolved_str + *prefixlenp;
- const char *lastsep = strrdirsep(resolved_names, resolved_str + RSTRING_LEN(*resolvedp), enc);
+ const char *resolved_str = RSTRING_PTR(*resolvedp);
+ const char *resolved_names = resolved_str + *prefixlenp;
+ const char *lastsep = strrdirsep(resolved_names, resolved_str + RSTRING_LEN(*resolvedp), enc);
long len = lastsep ? lastsep - resolved_names : 0;
rb_str_resize(*resolvedp, *prefixlenp + len);
}
@@ -4252,20 +4255,20 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE f
if (*prefixlenp < RSTRING_LEN(testpath))
rb_str_cat2(testpath, "/");
#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
- if (*prefixlenp > 1 && *prefixlenp == RSTRING_LEN(testpath)) {
- const char *prefix = RSTRING_PTR(testpath);
- const char *last = rb_enc_left_char_head(prefix, prefix + *prefixlenp - 1, prefix + *prefixlenp, enc);
- if (!isdirsep(*last)) rb_str_cat2(testpath, "/");
- }
+ if (*prefixlenp > 1 && *prefixlenp == RSTRING_LEN(testpath)) {
+ const char *prefix = RSTRING_PTR(testpath);
+ const char *last = rb_enc_left_char_head(prefix, prefix + *prefixlenp - 1, prefix + *prefixlenp, enc);
+ if (!isdirsep(*last)) rb_str_cat2(testpath, "/");
+ }
#endif
rb_str_cat(testpath, testname, testnamelen);
checkval = rb_hash_aref(loopcheck, testpath);
if (!NIL_P(checkval)) {
if (checkval == ID2SYM(resolving)) {
- if (mode == RB_REALPATH_CHECK) {
- errno = ELOOP;
- return -1;
- }
+ if (mode == RB_REALPATH_CHECK) {
+ errno = ELOOP;
+ return -1;
+ }
rb_syserr_fail_path(ELOOP, testpath);
}
else {
@@ -4277,49 +4280,49 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE f
int ret;
ret = lstat_without_gvl(RSTRING_PTR(testpath), &sbuf);
if (ret == -1) {
- int e = errno;
- if (e == ENOENT && !NIL_P(fallback)) {
- if (stat_without_gvl(RSTRING_PTR(fallback), &sbuf) == 0) {
- rb_str_replace(*resolvedp, fallback);
- return 0;
- }
- }
- if (mode == RB_REALPATH_CHECK) return -1;
- if (e == ENOENT) {
- if (mode == RB_REALPATH_STRICT || !last || *unresolved_firstsep)
- rb_syserr_fail_path(e, testpath);
+ int e = errno;
+ if (e == ENOENT && !NIL_P(fallback)) {
+ if (stat_without_gvl(RSTRING_PTR(fallback), &sbuf) == 0) {
+ rb_str_replace(*resolvedp, fallback);
+ return 0;
+ }
+ }
+ if (mode == RB_REALPATH_CHECK) return -1;
+ if (e == ENOENT) {
+ if (mode == RB_REALPATH_STRICT || !last || *unresolved_firstsep)
+ rb_syserr_fail_path(e, testpath);
*resolvedp = testpath;
break;
}
else {
- rb_syserr_fail_path(e, testpath);
+ rb_syserr_fail_path(e, testpath);
}
}
#ifdef HAVE_READLINK
if (S_ISLNK(sbuf.st_mode)) {
- VALUE link;
- VALUE link_orig = Qnil;
- const char *link_prefix, *link_names;
+ VALUE link;
+ VALUE link_orig = Qnil;
+ const char *link_prefix, *link_names;
long link_prefixlen;
rb_hash_aset(loopcheck, testpath, ID2SYM(resolving));
- link = rb_readlink(testpath, enc);
+ link = rb_readlink(testpath, enc);
link_prefix = RSTRING_PTR(link);
- link_names = skipprefixroot(link_prefix, link_prefix + RSTRING_LEN(link), rb_enc_get(link));
- link_prefixlen = link_names - link_prefix;
- if (link_prefixlen > 0) {
- rb_encoding *tmpenc, *linkenc = rb_enc_get(link);
- link_orig = link;
- link = rb_str_subseq(link, 0, link_prefixlen);
- tmpenc = fs_enc_check(*resolvedp, link);
- if (tmpenc != linkenc) link = rb_str_conv_enc(link, linkenc, tmpenc);
- *resolvedp = link;
- *prefixlenp = link_prefixlen;
- }
- if (realpath_rec(prefixlenp, resolvedp, link_names, testpath,
- loopcheck, mode, !*unresolved_firstsep))
- return -1;
- RB_GC_GUARD(link_orig);
- rb_hash_aset(loopcheck, testpath, rb_str_dup_frozen(*resolvedp));
+ link_names = skipprefixroot(link_prefix, link_prefix + RSTRING_LEN(link), rb_enc_get(link));
+ link_prefixlen = link_names - link_prefix;
+ if (link_prefixlen > 0) {
+ rb_encoding *tmpenc, *linkenc = rb_enc_get(link);
+ link_orig = link;
+ link = rb_str_subseq(link, 0, link_prefixlen);
+ tmpenc = fs_enc_check(*resolvedp, link);
+ if (tmpenc != linkenc) link = rb_str_conv_enc(link, linkenc, tmpenc);
+ *resolvedp = link;
+ *prefixlenp = link_prefixlen;
+ }
+ if (realpath_rec(prefixlenp, resolvedp, link_names, testpath,
+ loopcheck, mode, !*unresolved_firstsep))
+ return -1;
+ RB_GC_GUARD(link_orig);
+ rb_hash_aset(loopcheck, testpath, rb_str_dup_frozen(*resolvedp));
}
else
#endif
@@ -4365,11 +4368,11 @@ rb_check_realpath_emulate(VALUE basedir, VALUE path, rb_encoding *origenc, enum
}
if (!NIL_P(basedir)) {
- RSTRING_GETMEM(basedir, ptr, len);
- basedir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(basedir));
+ RSTRING_GETMEM(basedir, ptr, len);
+ basedir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(basedir));
if (ptr != basedir_names) {
- resolved = rb_str_subseq(basedir, 0, basedir_names - ptr);
- goto root_found;
+ resolved = rb_str_subseq(basedir, 0, basedir_names - ptr);
+ goto root_found;
}
}
@@ -4388,38 +4391,38 @@ rb_check_realpath_emulate(VALUE basedir, VALUE path, rb_encoding *origenc, enum
}
#ifdef FILE_ALT_SEPARATOR
while (prefixptr < ptr) {
- if (*prefixptr == FILE_ALT_SEPARATOR) {
- *prefixptr = '/';
- }
- Inc(prefixptr, pend, enc);
+ if (*prefixptr == FILE_ALT_SEPARATOR) {
+ *prefixptr = '/';
+ }
+ Inc(prefixptr, pend, enc);
}
#endif
switch (rb_enc_to_index(enc)) {
case ENCINDEX_ASCII_8BIT:
case ENCINDEX_US_ASCII:
- rb_enc_associate_index(resolved, rb_filesystem_encindex());
+ rb_enc_associate_index(resolved, rb_filesystem_encindex());
}
loopcheck = rb_hash_new();
if (curdir_names) {
- if (realpath_rec(&prefixlen, &resolved, curdir_names, Qnil, loopcheck, mode, 0))
- return Qnil;
+ if (realpath_rec(&prefixlen, &resolved, curdir_names, Qnil, loopcheck, mode, 0))
+ return Qnil;
}
if (basedir_names) {
- if (realpath_rec(&prefixlen, &resolved, basedir_names, Qnil, loopcheck, mode, 0))
- return Qnil;
+ if (realpath_rec(&prefixlen, &resolved, basedir_names, Qnil, loopcheck, mode, 0))
+ return Qnil;
}
if (realpath_rec(&prefixlen, &resolved, path_names, Qnil, loopcheck, mode, 1))
- return Qnil;
+ return Qnil;
if (origenc && origenc != rb_enc_get(resolved)) {
- if (rb_enc_str_asciionly_p(resolved)) {
- rb_enc_associate(resolved, origenc);
- }
- else {
- resolved = rb_str_conv_enc(resolved, NULL, origenc);
- }
+ if (rb_enc_str_asciionly_p(resolved)) {
+ rb_enc_associate(resolved, origenc);
+ }
+ else {
+ resolved = rb_str_conv_enc(resolved, NULL, origenc);
+ }
}
RB_GC_GUARD(unresolved_path);
@@ -4500,9 +4503,9 @@ rb_check_realpath_internal(VALUE basedir, VALUE path, rb_encoding *origenc, enum
rb_enc_associate(resolved, origenc);
}
- if (rb_enc_str_coderange(resolved) == ENC_CODERANGE_BROKEN) {
+ if (is_broken_string(resolved)) {
rb_enc_associate(resolved, rb_filesystem_encoding());
- if (rb_enc_str_coderange(resolved) == ENC_CODERANGE_BROKEN) {
+ if (is_broken_string(resolved)) {
rb_enc_associate(resolved, rb_ascii8bit_encoding());
}
}
@@ -4529,7 +4532,7 @@ VALUE
rb_realpath_internal(VALUE basedir, VALUE path, int strict)
{
const enum rb_realpath_mode mode =
- strict ? RB_REALPATH_STRICT : RB_REALPATH_DIR;
+ strict ? RB_REALPATH_STRICT : RB_REALPATH_DIR;
return rb_check_realpath_internal(basedir, path, rb_enc_get(path), mode);
}
@@ -4593,15 +4596,15 @@ rmext(const char *p, long l0, long l1, const char *e, long l2, rb_encoding *enc)
c = rb_enc_codepoint_len(e, e + l2, &len1, enc);
if (rb_enc_ascget(e + len1, e + l2, &len2, enc) == '*' && len1 + len2 == l2) {
- if (c == '.') return l0;
- s = p;
- e = p + l1;
- last = e;
- while (s < e) {
- if (rb_enc_codepoint_len(s, e, &len1, enc) == c) last = s;
- s += len1;
- }
- return last - p;
+ if (c == '.') return l0;
+ s = p;
+ e = p + l1;
+ last = e;
+ while (s < e) {
+ if (rb_enc_codepoint_len(s, e, &len1, enc) == c) last = s;
+ s += len1;
+ }
+ return last - p;
}
if (l1 < l2) return l1;
@@ -4613,7 +4616,7 @@ rmext(const char *p, long l0, long l1, const char *e, long l2, rb_encoding *enc)
#define fncomp strncmp
#endif
if (fncomp(s, e, l2) == 0) {
- return l1-l2;
+ return l1-l2;
}
return 0;
}
@@ -4633,51 +4636,51 @@ ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encodin
root = name;
#endif
while (isdirsep(*name))
- name++;
+ name++;
if (!*name) {
- p = name - 1;
- f = 1;
+ p = name - 1;
+ f = 1;
#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
- if (name != root) {
- /* has slashes */
- }
+ if (name != root) {
+ /* has slashes */
+ }
#ifdef DOSISH_DRIVE_LETTER
- else if (*p == ':') {
- p++;
- f = 0;
- }
+ else if (*p == ':') {
+ p++;
+ f = 0;
+ }
#endif
#ifdef DOSISH_UNC
- else {
- p = "/";
- }
+ else {
+ p = "/";
+ }
#endif
#endif
}
else {
- if (!(p = strrdirsep(name, end, enc))) {
- p = name;
- }
- else {
- while (isdirsep(*p)) p++; /* skip last / */
- }
+ if (!(p = strrdirsep(name, end, enc))) {
+ p = name;
+ }
+ else {
+ while (isdirsep(*p)) p++; /* skip last / */
+ }
#if USE_NTFS
- n = ntfs_tail(p, end, enc) - p;
+ n = ntfs_tail(p, end, enc) - p;
#else
- n = chompdirsep(p, end, enc) - p;
+ n = chompdirsep(p, end, enc) - p;
#endif
- for (q = p; q - p < n && *q == '.'; q++);
- for (e = 0; q - p < n; Inc(q, end, enc)) {
- if (*q == '.') e = q;
- }
- if (e) f = e - p;
- else f = n;
+ for (q = p; q - p < n && *q == '.'; q++);
+ for (e = 0; q - p < n; Inc(q, end, enc)) {
+ if (*q == '.') e = q;
+ }
+ if (e) f = e - p;
+ else f = n;
}
if (baselen)
- *baselen = f;
+ *baselen = f;
if (alllen)
- *alllen = n;
+ *alllen = n;
return p;
}
@@ -4708,33 +4711,33 @@ rb_file_s_basename(int argc, VALUE *argv, VALUE _)
fext = Qnil;
if (rb_check_arity(argc, 1, 2) == 2) {
- fext = argv[1];
- StringValue(fext);
- enc = check_path_encoding(fext);
+ fext = argv[1];
+ StringValue(fext);
+ enc = check_path_encoding(fext);
}
fname = argv[0];
FilePathStringValue(fname);
if (NIL_P(fext) || !(enc = rb_enc_compatible(fname, fext))) {
- enc = rb_enc_get(fname);
- fext = Qnil;
+ enc = rb_enc_get(fname);
+ fext = Qnil;
}
if ((n = RSTRING_LEN(fname)) == 0 || !*(name = RSTRING_PTR(fname)))
- return rb_str_new_shared(fname);
+ return rb_str_new_shared(fname);
p = ruby_enc_find_basename(name, &f, &n, enc);
if (n >= 0) {
- if (NIL_P(fext)) {
- f = n;
- }
- else {
- const char *fp;
- fp = StringValueCStr(fext);
- if (!(f = rmext(p, f, n, fp, RSTRING_LEN(fext), enc))) {
- f = n;
- }
- RB_GC_GUARD(fext);
- }
- if (f == RSTRING_LEN(fname)) return rb_str_new_shared(fname);
+ if (NIL_P(fext)) {
+ f = n;
+ }
+ else {
+ const char *fp;
+ fp = StringValueCStr(fext);
+ if (!(f = rmext(p, f, n, fp, RSTRING_LEN(fext), enc))) {
+ f = n;
+ }
+ RB_GC_GUARD(fext);
+ }
+ if (f == RSTRING_LEN(fname)) return rb_str_new_shared(fname);
}
basename = rb_str_new(p, f);
@@ -4768,7 +4771,7 @@ rb_file_s_dirname(int argc, VALUE *argv, VALUE klass)
{
int n = 1;
if ((argc = rb_check_arity(argc, 1, 2)) > 1) {
- n = NUM2INT(argv[1]);
+ n = NUM2INT(argv[1]);
}
return rb_file_dirname_n(argv[0], n);
}
@@ -4796,58 +4799,58 @@ rb_file_dirname_n(VALUE fname, int n)
root = skiproot(name, end, enc);
#ifdef DOSISH_UNC
if (root > name + 1 && isdirsep(*name))
- root = skipprefix(name = root - 2, end, enc);
+ root = skipprefix(name = root - 2, end, enc);
#else
if (root > name + 1)
- name = root - 1;
+ name = root - 1;
#endif
if (n > (end - root + 1) / 2) {
- p = root;
+ p = root;
}
else {
- int i;
- switch (n) {
- case 0:
- p = end;
- break;
- case 1:
- if (!(p = strrdirsep(root, end, enc))) p = root;
- break;
- default:
- seps = ALLOCV_N(const char *, sepsv, n);
- for (i = 0; i < n; ++i) seps[i] = root;
- i = 0;
- for (p = root; p < end; ) {
- if (isdirsep(*p)) {
- const char *tmp = p++;
- while (p < end && isdirsep(*p)) p++;
- if (p >= end) break;
- seps[i++] = tmp;
- if (i == n) i = 0;
- }
- else {
- Inc(p, end, enc);
- }
- }
- p = seps[i];
- ALLOCV_END(sepsv);
- break;
- }
+ int i;
+ switch (n) {
+ case 0:
+ p = end;
+ break;
+ case 1:
+ if (!(p = strrdirsep(root, end, enc))) p = root;
+ break;
+ default:
+ seps = ALLOCV_N(const char *, sepsv, n);
+ for (i = 0; i < n; ++i) seps[i] = root;
+ i = 0;
+ for (p = root; p < end; ) {
+ if (isdirsep(*p)) {
+ const char *tmp = p++;
+ while (p < end && isdirsep(*p)) p++;
+ if (p >= end) break;
+ seps[i++] = tmp;
+ if (i == n) i = 0;
+ }
+ else {
+ Inc(p, end, enc);
+ }
+ }
+ p = seps[i];
+ ALLOCV_END(sepsv);
+ break;
+ }
}
if (p == name)
- return rb_usascii_str_new2(".");
+ return rb_usascii_str_new2(".");
#ifdef DOSISH_DRIVE_LETTER
if (has_drive_letter(name) && isdirsep(*(name + 2))) {
- const char *top = skiproot(name + 2, end, enc);
- dirname = rb_str_new(name, 3);
- rb_str_cat(dirname, top, p - top);
+ const char *top = skiproot(name + 2, end, enc);
+ dirname = rb_str_new(name, 3);
+ rb_str_cat(dirname, top, p - top);
}
else
#endif
dirname = rb_str_new(name, p - name);
#ifdef DOSISH_DRIVE_LETTER
if (has_drive_letter(name) && root == name + 2 && p - name == 2)
- rb_str_cat(dirname, ".", 1);
+ rb_str_cat(dirname, ".", 1);
#endif
rb_enc_copy(dirname, fname);
return dirname;
@@ -4862,7 +4865,7 @@ rb_file_dirname_n(VALUE fname, int n)
* dotfile top 0
* end with dot dot 1
* .ext dot len of .ext
- * .ext:stream dot len of .ext without :stream (NT only)
+ * .ext:stream dot len of .ext without :stream (NTFS only)
*
*/
const char *
@@ -4872,48 +4875,48 @@ ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc)
p = strrdirsep(name, end, enc); /* get the last path component */
if (!p)
- p = name;
+ p = name;
else
- do name = ++p; while (isdirsep(*p));
+ do name = ++p; while (isdirsep(*p));
e = 0;
while (*p && *p == '.') p++;
while (*p) {
- if (*p == '.' || istrailinggarbage(*p)) {
+ if (*p == '.' || istrailinggarbage(*p)) {
#if USE_NTFS
- const char *last = p++, *dot = last;
- while (istrailinggarbage(*p)) {
- if (*p == '.') dot = p;
- p++;
- }
- if (!*p || isADS(*p)) {
- p = last;
- break;
- }
- if (*last == '.' || dot > last) e = dot;
- continue;
+ const char *last = p++, *dot = last;
+ while (istrailinggarbage(*p)) {
+ if (*p == '.') dot = p;
+ p++;
+ }
+ if (!*p || isADS(*p)) {
+ p = last;
+ break;
+ }
+ if (*last == '.' || dot > last) e = dot;
+ continue;
#else
- e = p; /* get the last dot of the last component */
+ e = p; /* get the last dot of the last component */
#endif
- }
+ }
#if USE_NTFS
- else if (isADS(*p)) {
- break;
- }
+ else if (isADS(*p)) {
+ break;
+ }
#endif
- else if (isdirsep(*p))
- break;
- Inc(p, end, enc);
+ else if (isdirsep(*p))
+ break;
+ Inc(p, end, enc);
}
if (len) {
- /* no dot, or the only dot is first or end? */
- if (!e || e == name)
- *len = 0;
- else if (e+1 == p)
- *len = 1;
- else
- *len = p - e;
+ /* no dot, or the only dot is first or end? */
+ if (!e || e == name)
+ *len = 0;
+ else if (e+1 == p)
+ *len = 1;
+ else
+ *len = p - e;
}
return e;
}
@@ -4956,7 +4959,7 @@ rb_file_s_extname(VALUE klass, VALUE fname)
len = RSTRING_LEN(fname);
e = ruby_enc_find_extname(name, &len, rb_enc_get(fname));
if (len < 1)
- return rb_str_new(0, 0);
+ return rb_str_new(0, 0);
extname = rb_str_subseq(fname, e - name, len); /* keep the dot, too! */
return extname;
}
@@ -5016,53 +5019,53 @@ rb_file_join(VALUE ary)
len = 1;
for (i=0; i<RARRAY_LEN(ary); i++) {
- tmp = RARRAY_AREF(ary, i);
- if (RB_TYPE_P(tmp, T_STRING)) {
- check_path_encoding(tmp);
- len += RSTRING_LEN(tmp);
- }
- else {
- len += 10;
- }
+ tmp = RARRAY_AREF(ary, i);
+ if (RB_TYPE_P(tmp, T_STRING)) {
+ check_path_encoding(tmp);
+ len += RSTRING_LEN(tmp);
+ }
+ else {
+ len += 10;
+ }
}
len += RARRAY_LEN(ary) - 1;
result = rb_str_buf_new(len);
RBASIC_CLEAR_CLASS(result);
for (i=0; i<RARRAY_LEN(ary); i++) {
- tmp = RARRAY_AREF(ary, i);
- switch (OBJ_BUILTIN_TYPE(tmp)) {
- case T_STRING:
- if (!checked) check_path_encoding(tmp);
- StringValueCStr(tmp);
- break;
- case T_ARRAY:
- if (ary == tmp) {
- rb_raise(rb_eArgError, "recursive array");
- }
- else {
- tmp = rb_exec_recursive(file_inspect_join, ary, tmp);
- }
- break;
- default:
- FilePathStringValue(tmp);
- checked = FALSE;
- }
- RSTRING_GETMEM(result, name, len);
- if (i == 0) {
- rb_enc_copy(result, tmp);
- }
- else {
- tail = chompdirsep(name, name + len, rb_enc_get(result));
- if (RSTRING_PTR(tmp) && isdirsep(RSTRING_PTR(tmp)[0])) {
- rb_str_set_len(result, tail - name);
- }
- else if (!*tail) {
- rb_str_cat(result, "/", 1);
- }
- }
- enc = fs_enc_check(result, tmp);
- rb_str_buf_append(result, tmp);
- rb_enc_associate(result, enc);
+ tmp = RARRAY_AREF(ary, i);
+ switch (OBJ_BUILTIN_TYPE(tmp)) {
+ case T_STRING:
+ if (!checked) check_path_encoding(tmp);
+ StringValueCStr(tmp);
+ break;
+ case T_ARRAY:
+ if (ary == tmp) {
+ rb_raise(rb_eArgError, "recursive array");
+ }
+ else {
+ tmp = rb_exec_recursive(file_inspect_join, ary, tmp);
+ }
+ break;
+ default:
+ FilePathStringValue(tmp);
+ checked = FALSE;
+ }
+ RSTRING_GETMEM(result, name, len);
+ if (i == 0) {
+ rb_enc_copy(result, tmp);
+ }
+ else {
+ tail = chompdirsep(name, name + len, rb_enc_get(result));
+ if (RSTRING_PTR(tmp) && isdirsep(RSTRING_PTR(tmp)[0])) {
+ rb_str_set_len(result, tail - name);
+ }
+ else if (!*tail) {
+ rb_str_cat(result, "/", 1);
+ }
+ }
+ enc = fs_enc_check(result, tmp);
+ rb_str_buf_append(result, tmp);
+ rb_enc_associate(result, enc);
}
RBASIC_SET_CLASS_RAW(result, rb_cString);
@@ -5086,41 +5089,17 @@ rb_file_s_join(VALUE klass, VALUE args)
return rb_file_join(args);
}
-#if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE)
+#if defined(HAVE_TRUNCATE)
struct truncate_arg {
const char *path;
-#if defined(HAVE_TRUNCATE)
-#define NUM2POS(n) NUM2OFFT(n)
- off_t pos;
-#else
-#define NUM2POS(n) NUM2LONG(n)
- long pos;
-#endif
+ rb_off_t pos;
};
static void *
nogvl_truncate(void *ptr)
{
struct truncate_arg *ta = ptr;
-#ifdef HAVE_TRUNCATE
return (void *)(VALUE)truncate(ta->path, ta->pos);
-#else /* defined(HAVE_CHSIZE) */
- {
- int tmpfd = rb_cloexec_open(ta->path, 0, 0);
-
- if (tmpfd < 0)
- return (void *)-1;
- rb_update_max_fd(tmpfd);
- if (chsize(tmpfd, ta->pos) < 0) {
- int e = errno;
- close(tmpfd);
- errno = e;
- return (void *)-1;
- }
- close(tmpfd);
- return 0;
- }
-#endif
}
/*
@@ -5144,32 +5123,25 @@ rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
struct truncate_arg ta;
int r;
- ta.pos = NUM2POS(len);
+ ta.pos = NUM2OFFT(len);
FilePathValue(path);
path = rb_str_encode_ospath(path);
ta.path = StringValueCStr(path);
r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_truncate, &ta,
- RUBY_UBF_IO, NULL);
+ RUBY_UBF_IO, NULL);
if (r < 0)
- rb_sys_fail_path(path);
+ rb_sys_fail_path(path);
return INT2FIX(0);
-#undef NUM2POS
}
#else
#define rb_file_s_truncate rb_f_notimplement
#endif
-#if defined(HAVE_FTRUNCATE) || defined(HAVE_CHSIZE)
+#if defined(HAVE_FTRUNCATE)
struct ftruncate_arg {
int fd;
-#if defined(HAVE_FTRUNCATE)
-#define NUM2POS(n) NUM2OFFT(n)
- off_t pos;
-#else
-#define NUM2POS(n) NUM2LONG(n)
- long pos;
-#endif
+ rb_off_t pos;
};
static VALUE
@@ -5177,11 +5149,7 @@ nogvl_ftruncate(void *ptr)
{
struct ftruncate_arg *fa = ptr;
-#ifdef HAVE_FTRUNCATE
return (VALUE)ftruncate(fa->fd, fa->pos);
-#else /* defined(HAVE_CHSIZE) */
- return (VALUE)chsize(fa->fd, fa->pos);
-#endif
}
/*
@@ -5204,18 +5172,17 @@ rb_file_truncate(VALUE obj, VALUE len)
rb_io_t *fptr;
struct ftruncate_arg fa;
- fa.pos = NUM2POS(len);
+ fa.pos = NUM2OFFT(len);
GetOpenFile(obj, fptr);
if (!(fptr->mode & FMODE_WRITABLE)) {
- rb_raise(rb_eIOError, "not opened for writing");
+ rb_raise(rb_eIOError, "not opened for writing");
}
rb_io_flush_raw(obj, 0);
fa.fd = fptr->fd;
if ((int)rb_thread_io_blocking_region(nogvl_ftruncate, &fa, fa.fd) < 0) {
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
}
return INT2FIX(0);
-#undef NUM2POS
}
#else
#define rb_file_truncate rb_f_notimplement
@@ -5248,8 +5215,8 @@ rb_thread_flock(void *data)
#ifdef __CYGWIN__
if (GetLastError() == ERROR_NOT_LOCKED) {
- ret = 0;
- errno = old_errno;
+ ret = 0;
+ errno = old_errno;
}
#endif
return (VALUE)ret;
@@ -5311,33 +5278,33 @@ rb_file_flock(VALUE obj, VALUE operation)
op[0] = fptr->fd;
if (fptr->mode & FMODE_WRITABLE) {
- rb_io_flush_raw(obj, 0);
+ rb_io_flush_raw(obj, 0);
}
while ((int)rb_thread_io_blocking_region(rb_thread_flock, op, fptr->fd) < 0) {
- int e = errno;
- switch (e) {
- case EAGAIN:
- case EACCES:
+ int e = errno;
+ switch (e) {
+ case EAGAIN:
+ case EACCES:
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK:
+ case EWOULDBLOCK:
#endif
- if (op1 & LOCK_NB) return Qfalse;
+ if (op1 & LOCK_NB) return Qfalse;
- time.tv_sec = 0;
- time.tv_usec = 100 * 1000; /* 0.1 sec */
- rb_thread_wait_for(time);
- rb_io_check_closed(fptr);
- continue;
+ time.tv_sec = 0;
+ time.tv_usec = 100 * 1000; /* 0.1 sec */
+ rb_thread_wait_for(time);
+ rb_io_check_closed(fptr);
+ continue;
- case EINTR:
+ case EINTR:
#if defined(ERESTART)
- case ERESTART:
+ case ERESTART:
#endif
- break;
+ break;
- default:
- rb_syserr_fail_path(e, fptr->pathv);
- }
+ default:
+ rb_syserr_fail_path(e, fptr->pathv);
+ }
}
return INT2FIX(0);
}
@@ -5350,9 +5317,9 @@ test_check(int n, int argc, VALUE *argv)
n+=1;
rb_check_arity(argc, n, n);
for (i=1; i<n; i++) {
- if (!RB_TYPE_P(argv[i], T_FILE)) {
- FilePathValue(argv[i]);
- }
+ if (!RB_TYPE_P(argv[i], T_FILE)) {
+ FilePathValue(argv[i]);
+ }
}
}
@@ -5375,8 +5342,7 @@ test_check(int n, int argc, VALUE *argv)
* "d" | boolean | True if file1 exists and is a directory
* "e" | boolean | True if file1 exists
* "f" | boolean | True if file1 exists and is a regular file
- * "g" | boolean | True if file1 has the \CF{setgid} bit
- * | | set (false under NT)
+ * "g" | boolean | True if file1 has the setgid bit set
* "G" | boolean | True if file1 exists and has a group
* | | ownership equal to the caller's group
* "k" | boolean | True if file1 exists and has the sticky bit set
@@ -5427,128 +5393,128 @@ rb_f_test(int argc, VALUE *argv, VALUE _)
goto unknown;
}
if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
- CHECK(1);
- switch (cmd) {
- case 'b':
- return rb_file_blockdev_p(0, argv[1]);
+ CHECK(1);
+ switch (cmd) {
+ case 'b':
+ return rb_file_blockdev_p(0, argv[1]);
- case 'c':
- return rb_file_chardev_p(0, argv[1]);
+ case 'c':
+ return rb_file_chardev_p(0, argv[1]);
- case 'd':
- return rb_file_directory_p(0, argv[1]);
+ case 'd':
+ return rb_file_directory_p(0, argv[1]);
- case 'e':
- return rb_file_exist_p(0, argv[1]);
+ case 'e':
+ return rb_file_exist_p(0, argv[1]);
- case 'f':
- return rb_file_file_p(0, argv[1]);
+ case 'f':
+ return rb_file_file_p(0, argv[1]);
- case 'g':
- return rb_file_sgid_p(0, argv[1]);
+ case 'g':
+ return rb_file_sgid_p(0, argv[1]);
- case 'G':
- return rb_file_grpowned_p(0, argv[1]);
+ case 'G':
+ return rb_file_grpowned_p(0, argv[1]);
- case 'k':
- return rb_file_sticky_p(0, argv[1]);
+ case 'k':
+ return rb_file_sticky_p(0, argv[1]);
- case 'l':
- return rb_file_symlink_p(0, argv[1]);
+ case 'l':
+ return rb_file_symlink_p(0, argv[1]);
- case 'o':
- return rb_file_owned_p(0, argv[1]);
+ case 'o':
+ return rb_file_owned_p(0, argv[1]);
- case 'O':
- return rb_file_rowned_p(0, argv[1]);
+ case 'O':
+ return rb_file_rowned_p(0, argv[1]);
- case 'p':
- return rb_file_pipe_p(0, argv[1]);
+ case 'p':
+ return rb_file_pipe_p(0, argv[1]);
- case 'r':
- return rb_file_readable_p(0, argv[1]);
+ case 'r':
+ return rb_file_readable_p(0, argv[1]);
- case 'R':
- return rb_file_readable_real_p(0, argv[1]);
+ case 'R':
+ return rb_file_readable_real_p(0, argv[1]);
- case 's':
- return rb_file_size_p(0, argv[1]);
+ case 's':
+ return rb_file_size_p(0, argv[1]);
- case 'S':
- return rb_file_socket_p(0, argv[1]);
+ case 'S':
+ return rb_file_socket_p(0, argv[1]);
- case 'u':
- return rb_file_suid_p(0, argv[1]);
+ case 'u':
+ return rb_file_suid_p(0, argv[1]);
- case 'w':
- return rb_file_writable_p(0, argv[1]);
+ case 'w':
+ return rb_file_writable_p(0, argv[1]);
- case 'W':
- return rb_file_writable_real_p(0, argv[1]);
+ case 'W':
+ return rb_file_writable_real_p(0, argv[1]);
- case 'x':
- return rb_file_executable_p(0, argv[1]);
+ case 'x':
+ return rb_file_executable_p(0, argv[1]);
- case 'X':
- return rb_file_executable_real_p(0, argv[1]);
+ case 'X':
+ return rb_file_executable_real_p(0, argv[1]);
- case 'z':
- return rb_file_zero_p(0, argv[1]);
- }
+ case 'z':
+ return rb_file_zero_p(0, argv[1]);
+ }
}
if (strchr("MAC", cmd)) {
- struct stat st;
- VALUE fname = argv[1];
-
- CHECK(1);
- if (rb_stat(fname, &st) == -1) {
- int e = errno;
- FilePathValue(fname);
- rb_syserr_fail_path(e, fname);
- }
-
- switch (cmd) {
- case 'A':
- return stat_atime(&st);
- case 'M':
- return stat_mtime(&st);
- case 'C':
- return stat_ctime(&st);
- }
+ struct stat st;
+ VALUE fname = argv[1];
+
+ CHECK(1);
+ if (rb_stat(fname, &st) == -1) {
+ int e = errno;
+ FilePathValue(fname);
+ rb_syserr_fail_path(e, fname);
+ }
+
+ switch (cmd) {
+ case 'A':
+ return stat_atime(&st);
+ case 'M':
+ return stat_mtime(&st);
+ case 'C':
+ return stat_ctime(&st);
+ }
}
if (cmd == '-') {
- CHECK(2);
- return rb_file_identical_p(0, argv[1], argv[2]);
+ CHECK(2);
+ return rb_file_identical_p(0, argv[1], argv[2]);
}
if (strchr("=<>", cmd)) {
- struct stat st1, st2;
+ struct stat st1, st2;
struct timespec t1, t2;
- CHECK(2);
- if (rb_stat(argv[1], &st1) < 0) return Qfalse;
- if (rb_stat(argv[2], &st2) < 0) return Qfalse;
+ CHECK(2);
+ if (rb_stat(argv[1], &st1) < 0) return Qfalse;
+ if (rb_stat(argv[2], &st2) < 0) return Qfalse;
t1 = stat_mtimespec(&st1);
t2 = stat_mtimespec(&st2);
- switch (cmd) {
- case '=':
- if (t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec) return Qtrue;
- return Qfalse;
+ switch (cmd) {
+ case '=':
+ if (t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec) return Qtrue;
+ return Qfalse;
- case '>':
- if (t1.tv_sec > t2.tv_sec) return Qtrue;
- if (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec) return Qtrue;
- return Qfalse;
+ case '>':
+ if (t1.tv_sec > t2.tv_sec) return Qtrue;
+ if (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec) return Qtrue;
+ return Qfalse;
- case '<':
- if (t1.tv_sec < t2.tv_sec) return Qtrue;
- if (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec) return Qtrue;
- return Qfalse;
- }
+ case '<':
+ if (t1.tv_sec < t2.tv_sec) return Qtrue;
+ if (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec) return Qtrue;
+ return Qfalse;
+ }
}
unknown:
/* unknown command */
@@ -5597,11 +5563,12 @@ rb_stat_init(VALUE obj, VALUE fname)
FilePathValue(fname);
fname = rb_str_encode_ospath(fname);
if (STAT(StringValueCStr(fname), &st) == -1) {
- rb_sys_fail_path(fname);
+ rb_sys_fail_path(fname);
}
+
if (DATA_PTR(obj)) {
- xfree(DATA_PTR(obj));
- DATA_PTR(obj) = NULL;
+ xfree(DATA_PTR(obj));
+ DATA_PTR(obj) = NULL;
}
nst = ALLOC(struct stat);
*nst = st;
@@ -5618,13 +5585,13 @@ rb_stat_init_copy(VALUE copy, VALUE orig)
if (!OBJ_INIT_COPY(copy, orig)) return copy;
if (DATA_PTR(copy)) {
- xfree(DATA_PTR(copy));
- DATA_PTR(copy) = 0;
+ xfree(DATA_PTR(copy));
+ DATA_PTR(copy) = 0;
}
if (DATA_PTR(orig)) {
- nst = ALLOC(struct stat);
- *nst = *(struct stat*)DATA_PTR(orig);
- DATA_PTR(copy) = nst;
+ nst = ALLOC(struct stat);
+ *nst = *(struct stat*)DATA_PTR(orig);
+ DATA_PTR(copy) = nst;
}
return copy;
@@ -5807,7 +5774,7 @@ rb_stat_rowned(VALUE obj)
* stat.grpowned? -> true or false
*
* Returns true if the effective group id of the process is the same as
- * the group id of <i>stat</i>. On Windows NT, returns <code>false</code>.
+ * the group id of <i>stat</i>. On Windows, returns <code>false</code>.
*
* File.stat("testfile").grpowned? #=> true
* File.stat("/etc/passwd").grpowned? #=> false
@@ -5844,11 +5811,11 @@ rb_stat_r(VALUE obj)
#endif
#ifdef S_IRUSR
if (rb_stat_owned(obj))
- return RBOOL(st->st_mode & S_IRUSR);
+ return RBOOL(st->st_mode & S_IRUSR);
#endif
#ifdef S_IRGRP
if (rb_stat_grpowned(obj))
- return RBOOL(st->st_mode & S_IRGRP);
+ return RBOOL(st->st_mode & S_IRGRP);
#endif
#ifdef S_IROTH
if (!(st->st_mode & S_IROTH)) return Qfalse;
@@ -5877,11 +5844,11 @@ rb_stat_R(VALUE obj)
#endif
#ifdef S_IRUSR
if (rb_stat_rowned(obj))
- return RBOOL(st->st_mode & S_IRUSR);
+ return RBOOL(st->st_mode & S_IRUSR);
#endif
#ifdef S_IRGRP
if (rb_group_member(get_stat(obj)->st_gid))
- return RBOOL(st->st_mode & S_IRGRP);
+ return RBOOL(st->st_mode & S_IRGRP);
#endif
#ifdef S_IROTH
if (!(st->st_mode & S_IROTH)) return Qfalse;
@@ -5908,10 +5875,10 @@ rb_stat_wr(VALUE obj)
#ifdef S_IROTH
struct stat *st = get_stat(obj);
if ((st->st_mode & (S_IROTH)) == S_IROTH) {
- return UINT2NUM(st->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
+ return UINT2NUM(st->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
}
else {
- return Qnil;
+ return Qnil;
}
#endif
}
@@ -5937,11 +5904,11 @@ rb_stat_w(VALUE obj)
#endif
#ifdef S_IWUSR
if (rb_stat_owned(obj))
- return RBOOL(st->st_mode & S_IWUSR);
+ return RBOOL(st->st_mode & S_IWUSR);
#endif
#ifdef S_IWGRP
if (rb_stat_grpowned(obj))
- return RBOOL(st->st_mode & S_IWGRP);
+ return RBOOL(st->st_mode & S_IWGRP);
#endif
#ifdef S_IWOTH
if (!(st->st_mode & S_IWOTH)) return Qfalse;
@@ -5970,11 +5937,11 @@ rb_stat_W(VALUE obj)
#endif
#ifdef S_IWUSR
if (rb_stat_rowned(obj))
- return RBOOL(st->st_mode & S_IWUSR);
+ return RBOOL(st->st_mode & S_IWUSR);
#endif
#ifdef S_IWGRP
if (rb_group_member(get_stat(obj)->st_gid))
- return RBOOL(st->st_mode & S_IWGRP);
+ return RBOOL(st->st_mode & S_IWGRP);
#endif
#ifdef S_IWOTH
if (!(st->st_mode & S_IWOTH)) return Qfalse;
@@ -6001,10 +5968,10 @@ rb_stat_ww(VALUE obj)
#ifdef S_IROTH
struct stat *st = get_stat(obj);
if ((st->st_mode & (S_IWOTH)) == S_IWOTH) {
- return UINT2NUM(st->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
+ return UINT2NUM(st->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
}
else {
- return Qnil;
+ return Qnil;
}
#endif
}
@@ -6029,16 +5996,16 @@ rb_stat_x(VALUE obj)
#ifdef USE_GETEUID
if (geteuid() == 0) {
- return RBOOL(st->st_mode & S_IXUGO);
+ return RBOOL(st->st_mode & S_IXUGO);
}
#endif
#ifdef S_IXUSR
if (rb_stat_owned(obj))
- return RBOOL(st->st_mode & S_IXUSR);
+ return RBOOL(st->st_mode & S_IXUSR);
#endif
#ifdef S_IXGRP
if (rb_stat_grpowned(obj))
- return RBOOL(st->st_mode & S_IXGRP);
+ return RBOOL(st->st_mode & S_IXGRP);
#endif
#ifdef S_IXOTH
if (!(st->st_mode & S_IXOTH)) return Qfalse;
@@ -6061,16 +6028,16 @@ rb_stat_X(VALUE obj)
#ifdef USE_GETEUID
if (getuid() == 0) {
- return RBOOL(st->st_mode & S_IXUGO);
+ return RBOOL(st->st_mode & S_IXUGO);
}
#endif
#ifdef S_IXUSR
if (rb_stat_rowned(obj))
- return RBOOL(st->st_mode & S_IXUSR);
+ return RBOOL(st->st_mode & S_IXUSR);
#endif
#ifdef S_IXGRP
if (rb_group_member(get_stat(obj)->st_gid))
- return RBOOL(st->st_mode & S_IXGRP);
+ return RBOOL(st->st_mode & S_IXGRP);
#endif
#ifdef S_IXOTH
if (!(st->st_mode & S_IXOTH)) return Qfalse;
@@ -6129,7 +6096,7 @@ rb_stat_z(VALUE obj)
static VALUE
rb_stat_s(VALUE obj)
{
- off_t size = get_stat(obj)->st_size;
+ rb_off_t size = get_stat(obj)->st_size;
if (size == 0) return Qnil;
return OFFT2NUM(size);
@@ -6235,14 +6202,14 @@ rb_file_s_mkfifo(int argc, VALUE *argv, VALUE _)
ma.mode = 0666;
rb_check_arity(argc, 1, 2);
if (argc > 1) {
- ma.mode = NUM2MODET(argv[1]);
+ ma.mode = NUM2MODET(argv[1]);
}
path = argv[0];
FilePathValue(path);
path = rb_str_encode_ospath(path);
ma.path = RSTRING_PTR(path);
if (rb_thread_call_without_gvl(nogvl_mkfifo, &ma, RUBY_UBF_IO, 0)) {
- rb_sys_fail_path(path);
+ rb_sys_fail_path(path);
}
return INT2FIX(0);
}
@@ -6292,16 +6259,16 @@ path_check_0(VALUE path)
char *p = 0, *s;
if (!rb_is_absolute_path(p0)) {
- char *buf = ruby_getcwd();
- VALUE newpath;
+ char *buf = ruby_getcwd();
+ VALUE newpath;
- newpath = rb_str_new2(buf);
- xfree(buf);
+ newpath = rb_str_new2(buf);
+ xfree(buf);
- rb_str_cat2(newpath, "/");
- rb_str_cat2(newpath, p0);
- path = newpath;
- p0 = RSTRING_PTR(path);
+ rb_str_cat2(newpath, "/");
+ rb_str_cat2(newpath, p0);
+ path = newpath;
+ p0 = RSTRING_PTR(path);
}
e0 = p0 + RSTRING_LEN(path);
enc = rb_enc_get(path);
@@ -6309,28 +6276,28 @@ path_check_0(VALUE path)
#ifndef S_IWOTH
# define S_IWOTH 002
#endif
- if (STAT(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
+ if (STAT(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
#ifdef S_ISVTX
- && !(p && (st.st_mode & S_ISVTX))
+ && !(p && (st.st_mode & S_ISVTX))
#endif
- && !access(p0, W_OK)) {
- rb_enc_warn(enc, "Insecure world writable dir %s in PATH, mode 0%"
+ && !access(p0, W_OK)) {
+ rb_enc_warn(enc, "Insecure world writable dir %s in PATH, mode 0%"
#if SIZEOF_DEV_T > SIZEOF_INT
- PRI_MODET_PREFIX"o",
+ PRI_MODET_PREFIX"o",
#else
- "o",
+ "o",
#endif
- p0, st.st_mode);
- if (p) *p = '/';
- RB_GC_GUARD(path);
- return 0;
- }
- s = strrdirsep(p0, e0, enc);
- if (p) *p = '/';
- if (!s || s == p0) return 1;
- p = s;
- e0 = p;
- *p = '\0';
+ p0, st.st_mode);
+ if (p) *p = '/';
+ RB_GC_GUARD(path);
+ return 0;
+ }
+ s = strrdirsep(p0, e0, enc);
+ if (p) *p = '/';
+ if (!s || s == p0) return 1;
+ p = s;
+ e0 = p;
+ *p = '\0';
}
}
#endif
@@ -6350,13 +6317,13 @@ rb_path_check(const char *path)
if (!p) p = pend;
for (;;) {
- if (!path_check_0(rb_str_new(p0, p - p0))) {
- return 0; /* not safe */
- }
- p0 = p + 1;
- if (p0 > pend) break;
- p = strchr(p0, sep);
- if (!p) p = pend;
+ if (!path_check_0(rb_str_new(p0, p - p0))) {
+ return 0; /* not safe */
+ }
+ p0 = p + 1;
+ if (p0 > pend) break;
+ p = strchr(p0, sep);
+ if (!p) p = pend;
}
#endif
return 1;
@@ -6371,18 +6338,18 @@ ruby_is_fd_loadable(int fd)
struct stat st;
if (fstat(fd, &st) < 0)
- return 0;
+ return 0;
if (S_ISREG(st.st_mode))
- return 1;
+ return 1;
if (S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode))
- return -1;
+ return -1;
if (S_ISDIR(st.st_mode))
- errno = EISDIR;
+ errno = EISDIR;
else
- errno = ENXIO;
+ errno = ENXIO;
return 0;
#endif
@@ -6399,11 +6366,11 @@ rb_file_load_ok(const char *path)
*/
int mode = (O_RDONLY |
#if defined O_NONBLOCK
- O_NONBLOCK |
+ O_NONBLOCK |
#elif defined O_NDELAY
- O_NDELAY |
+ O_NDELAY |
#endif
- 0);
+ 0);
int fd = rb_cloexec_open(path, mode, 0);
if (fd == -1) return 0;
rb_update_max_fd(fd);
@@ -6445,24 +6412,24 @@ rb_find_file_ext(VALUE *filep, const char *const *ext)
if (!ext[0]) return 0;
if (f[0] == '~') {
- fname = file_expand_path_1(fname);
- f = RSTRING_PTR(fname);
- *filep = fname;
- expanded = 1;
+ fname = file_expand_path_1(fname);
+ f = RSTRING_PTR(fname);
+ *filep = fname;
+ expanded = 1;
}
if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
- if (!expanded) fname = file_expand_path_1(fname);
- fnlen = RSTRING_LEN(fname);
- for (i=0; ext[i]; i++) {
- rb_str_cat2(fname, ext[i]);
- if (rb_file_load_ok(RSTRING_PTR(fname))) {
- *filep = copy_path_class(fname, *filep);
- return (int)(i+1);
- }
- rb_str_set_len(fname, fnlen);
- }
- return 0;
+ if (!expanded) fname = file_expand_path_1(fname);
+ fnlen = RSTRING_LEN(fname);
+ for (i=0; ext[i]; i++) {
+ rb_str_cat2(fname, ext[i]);
+ if (rb_file_load_ok(RSTRING_PTR(fname))) {
+ *filep = copy_path_class(fname, *filep);
+ return (int)(i+1);
+ }
+ rb_str_set_len(fname, fnlen);
+ }
+ return 0;
}
RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
@@ -6474,19 +6441,19 @@ rb_find_file_ext(VALUE *filep, const char *const *ext)
tmp = rb_str_tmp_new(MAXPATHLEN + 2);
rb_enc_associate_index(tmp, rb_usascii_encindex());
for (j=0; ext[j]; j++) {
- rb_str_cat2(fname, ext[j]);
- for (i = 0; i < RARRAY_LEN(load_path); i++) {
- VALUE str = RARRAY_AREF(load_path, i);
+ rb_str_cat2(fname, ext[j]);
+ for (i = 0; i < RARRAY_LEN(load_path); i++) {
+ VALUE str = RARRAY_AREF(load_path, i);
RB_GC_GUARD(str) = rb_get_path(str);
- if (RSTRING_LEN(str) == 0) continue;
- rb_file_expand_path_internal(fname, str, 0, 0, tmp);
- if (rb_file_load_ok(RSTRING_PTR(tmp))) {
- *filep = copy_path_class(tmp, *filep);
- return (int)(j+1);
- }
- }
- rb_str_set_len(fname, fnlen);
+ if (RSTRING_LEN(str) == 0) continue;
+ rb_file_expand_path_internal(fname, str, 0, 0, tmp);
+ if (rb_file_load_ok(RSTRING_PTR(tmp))) {
+ *filep = copy_path_class(tmp, *filep);
+ return (int)(j+1);
+ }
+ }
+ rb_str_set_len(fname, fnlen);
}
rb_str_resize(tmp, 0);
RB_GC_GUARD(load_path);
@@ -6501,51 +6468,49 @@ rb_find_file(VALUE path)
int expanded = 0;
if (f[0] == '~') {
- tmp = file_expand_path_1(path);
- path = copy_path_class(tmp, path);
- f = RSTRING_PTR(path);
- expanded = 1;
+ tmp = file_expand_path_1(path);
+ path = copy_path_class(tmp, path);
+ f = RSTRING_PTR(path);
+ expanded = 1;
}
if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
- if (!rb_file_load_ok(f)) return 0;
- if (!expanded)
- path = copy_path_class(file_expand_path_1(path), path);
- return path;
+ if (!rb_file_load_ok(f)) return 0;
+ if (!expanded)
+ path = copy_path_class(file_expand_path_1(path), path);
+ return path;
}
RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
if (load_path) {
- long i;
+ long i;
- tmp = rb_str_tmp_new(MAXPATHLEN + 2);
- rb_enc_associate_index(tmp, rb_usascii_encindex());
- for (i = 0; i < RARRAY_LEN(load_path); i++) {
- VALUE str = RARRAY_AREF(load_path, i);
+ tmp = rb_str_tmp_new(MAXPATHLEN + 2);
+ rb_enc_associate_index(tmp, rb_usascii_encindex());
+ for (i = 0; i < RARRAY_LEN(load_path); i++) {
+ VALUE str = RARRAY_AREF(load_path, i);
RB_GC_GUARD(str) = rb_get_path(str);
- if (RSTRING_LEN(str) > 0) {
- rb_file_expand_path_internal(path, str, 0, 0, tmp);
- f = RSTRING_PTR(tmp);
- if (rb_file_load_ok(f)) goto found;
- }
- }
- rb_str_resize(tmp, 0);
- return 0;
+ if (RSTRING_LEN(str) > 0) {
+ rb_file_expand_path_internal(path, str, 0, 0, tmp);
+ f = RSTRING_PTR(tmp);
+ if (rb_file_load_ok(f)) goto found;
+ }
+ }
+ rb_str_resize(tmp, 0);
+ return 0;
}
else {
- return 0; /* no path, no load */
+ return 0; /* no path, no load */
}
found:
return copy_path_class(tmp, path);
}
-static void
-define_filetest_function(const char *name, VALUE (*func)(ANYARGS), int argc)
-{
- rb_define_module_function(rb_mFileTest, name, func, argc);
- rb_define_singleton_method(rb_cFile, name, func, argc);
-}
+#define define_filetest_function(name, func, argc) do { \
+ rb_define_module_function(rb_mFileTest, name, func, argc); \
+ rb_define_singleton_method(rb_cFile, name, func, argc); \
+} while(false)
const char ruby_null_device[] =
#if defined DOSISH
@@ -6565,6 +6530,602 @@ const char ruby_null_device[] =
* \Class \File extends module FileTest, supporting such singleton methods
* as <tt>File.exist?</tt>.
*
+ * === About the Examples
+ *
+ * Many examples here use these variables:
+ *
+ * :include: doc/examples/files.rdoc
+ *
+ * == Access Modes
+ *
+ * \Methods File.new and File.open each create a \File object for a given file path.
+ *
+ * === \String Access Modes
+ *
+ * \Methods File.new and File.open each may take string argument +mode+, which:
+ *
+ * - Begins with a 1- or 2-character
+ * {read/write mode}[rdoc-ref:File@Read-2FWrite+Mode].
+ * - May also contain a 1-character {data mode}[rdoc-ref:File@Data+Mode].
+ * - May also contain a 1-character
+ * {file-create mode}[rdoc-ref:File@File-Create+Mode].
+ *
+ * ==== Read/Write Mode
+ *
+ * The read/write +mode+ determines:
+ *
+ * - Whether the file is to be initially truncated.
+ *
+ * - Whether reading is allowed, and if so:
+ *
+ * - The initial read position in the file.
+ * - Where in the file reading can occur.
+ *
+ * - Whether writing is allowed, and if so:
+ *
+ * - The initial write position in the file.
+ * - Where in the file writing can occur.
+ *
+ * These tables summarize:
+ *
+ * Read/Write Modes for Existing File
+ *
+ * |------|-----------|----------|----------|----------|-----------|
+ * | R/W | Initial | | Initial | | Initial |
+ * | Mode | Truncate? | Read | Read Pos | Write | Write Pos |
+ * |------|-----------|----------|----------|----------|-----------|
+ * | 'r' | No | Anywhere | 0 | Error | - |
+ * | 'w' | Yes | Error | - | Anywhere | 0 |
+ * | 'a' | No | Error | - | End only | End |
+ * | 'r+' | No | Anywhere | 0 | Anywhere | 0 |
+ * | 'w+' | Yes | Anywhere | 0 | Anywhere | 0 |
+ * | 'a+' | No | Anywhere | End | End only | End |
+ * |------|-----------|----------|----------|----------|-----------|
+ *
+ * Read/Write Modes for \File To Be Created
+ *
+ * |------|----------|----------|----------|-----------|
+ * | R/W | | Initial | | Initial |
+ * | Mode | Read | Read Pos | Write | Write Pos |
+ * |------|----------|----------|----------|-----------|
+ * | 'w' | Error | - | Anywhere | 0 |
+ * | 'a' | Error | - | End only | 0 |
+ * | 'w+' | Anywhere | 0 | Anywhere | 0 |
+ * | 'a+' | Anywhere | 0 | End only | End |
+ * |------|----------|----------|----------|-----------|
+ *
+ * Note that modes <tt>'r'</tt> and <tt>'r+'</tt> are not allowed
+ * for a non-existent file (exception raised).
+ *
+ * In the tables:
+ *
+ * - +Anywhere+ means that methods IO#rewind, IO#pos=, and IO#seek
+ * may be used to change the file's position,
+ * so that allowed reading or writing may occur anywhere in the file.
+ * - <tt>End only</tt> means that writing can occur only at end-of-file,
+ * and that methods IO#rewind, IO#pos=, and IO#seek do not affect writing.
+ * - +Error+ means that an exception is raised if disallowed reading or writing
+ * is attempted.
+ *
+ * ===== Read/Write Modes for Existing \File
+ *
+ * - <tt>'r'</tt>:
+ *
+ * - File is not initially truncated:
+ *
+ * f = File.new('t.txt') # => #<File:t.txt>
+ * f.size == 0 # => false
+ *
+ * - File's initial read position is 0:
+ *
+ * f.pos # => 0
+ *
+ * - File may be read anywhere; see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.readline # => "First line\n"
+ * f.readline # => "Second line\n"
+ *
+ * f.rewind
+ * f.readline # => "First line\n"
+ *
+ * f.pos = 1
+ * f.readline # => "irst line\n"
+ *
+ * f.seek(1, :CUR)
+ * f.readline # => "econd line\n"
+ *
+ * - Writing is not allowed:
+ *
+ * f.write('foo') # Raises IOError.
+ *
+ * - <tt>'w'</tt>:
+ *
+ * - File is initially truncated:
+ *
+ * path = 't.tmp'
+ * File.write(path, text)
+ * f = File.new(path, 'w')
+ * f.size == 0 # => true
+ *
+ * - File's initial write position is 0:
+ *
+ * f.pos # => 0
+ *
+ * - File may be written anywhere (even past end-of-file);
+ * see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.write('foo')
+ * f.flush
+ * File.read(path) # => "foo"
+ * f.pos # => 3
+ *
+ * f.write('bar')
+ * f.flush
+ * File.read(path) # => "foobar"
+ * f.pos # => 6
+ *
+ * f.rewind
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "bazbar"
+ * f.pos # => 3
+ *
+ * f.pos = 3
+ * f.write('foo')
+ * f.flush
+ * File.read(path) # => "bazfoo"
+ * f.pos # => 6
+ *
+ * f.seek(-3, :END)
+ * f.write('bam')
+ * f.flush
+ * File.read(path) # => "bazbam"
+ * f.pos # => 6
+ *
+ * f.pos = 8
+ * f.write('bah') # Zero padding as needed.
+ * f.flush
+ * File.read(path) # => "bazbam\u0000\u0000bah"
+ * f.pos # => 11
+ *
+ * - Reading is not allowed:
+ *
+ * f.read # Raises IOError.
+ *
+ * - <tt>'a'</tt>:
+ *
+ * - File is not initially truncated:
+ *
+ * path = 't.tmp'
+ * File.write(path, 'foo')
+ * f = File.new(path, 'a')
+ * f.size == 0 # => false
+ *
+ * - File's initial position is 0 (but is ignored):
+ *
+ * f.pos # => 0
+ *
+ * - File may be written only at end-of-file;
+ * IO#rewind, IO#pos=, IO#seek do not affect writing:
+ *
+ * f.write('bar')
+ * f.flush
+ * File.read(path) # => "foobar"
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "foobarbaz"
+ *
+ * f.rewind
+ * f.write('bat')
+ * f.flush
+ * File.read(path) # => "foobarbazbat"
+ *
+ * - Reading is not allowed:
+ *
+ * f.read # Raises IOError.
+ *
+ * - <tt>'r+'</tt>:
+ *
+ * - File is not initially truncated:
+ *
+ * path = 't.tmp'
+ * File.write(path, text)
+ * f = File.new(path, 'r+')
+ * f.size == 0 # => false
+ *
+ * - File's initial read position is 0:
+ *
+ * f.pos # => 0
+ *
+ * - File may be read or written anywhere (even past end-of-file);
+ * see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.readline # => "First line\n"
+ * f.readline # => "Second line\n"
+ *
+ * f.rewind
+ * f.readline # => "First line\n"
+ *
+ * f.pos = 1
+ * f.readline # => "irst line\n"
+ *
+ * f.seek(1, :CUR)
+ * f.readline # => "econd line\n"
+ *
+ * f.rewind
+ * f.write('WWW')
+ * f.flush
+ * File.read(path)
+ * # => "WWWst line\nSecond line\nFourth line\nFifth line\n"
+ *
+ * f.pos = 10
+ * f.write('XXX')
+ * f.flush
+ * File.read(path)
+ * # => "WWWst lineXXXecond line\nFourth line\nFifth line\n"
+ *
+ * f.seek(-6, :END)
+ * # => 0
+ * f.write('YYY')
+ * # => 3
+ * f.flush
+ * # => #<File:t.tmp>
+ * File.read(path)
+ * # => "WWWst lineXXXecond line\nFourth line\nFifth YYYe\n"
+ *
+ * f.seek(2, :END)
+ * f.write('ZZZ') # Zero padding as needed.
+ * f.flush
+ * File.read(path)
+ * # => "WWWst lineXXXecond line\nFourth line\nFifth YYYe\n\u0000\u0000ZZZ"
+ *
+ *
+ * - <tt>'a+'</tt>:
+ *
+ * - File is not initially truncated:
+ *
+ * path = 't.tmp'
+ * File.write(path, 'foo')
+ * f = File.new(path, 'a+')
+ * f.size == 0 # => false
+ *
+ * - File's initial read position is 0:
+ *
+ * f.pos # => 0
+ *
+ * - File may be written only at end-of-file;
+ * IO#rewind, IO#pos=, IO#seek do not affect writing:
+ *
+ * f.write('bar')
+ * f.flush
+ * File.read(path) # => "foobar"
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "foobarbaz"
+ *
+ * f.rewind
+ * f.write('bat')
+ * f.flush
+ * File.read(path) # => "foobarbazbat"
+ *
+ * - File may be read anywhere; see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.rewind
+ * f.read # => "foobarbazbat"
+ *
+ * f.pos = 3
+ * f.read # => "barbazbat"
+ *
+ * f.seek(-3, :END)
+ * f.read # => "bat"
+ *
+ * ===== Read/Write Modes for \File To Be Created
+ *
+ * Note that modes <tt>'r'</tt> and <tt>'r+'</tt> are not allowed
+ * for a non-existent file (exception raised).
+ *
+ * - <tt>'w'</tt>:
+ *
+ * - File's initial write position is 0:
+ *
+ * path = 't.tmp'
+ * FileUtils.rm_f(path)
+ * f = File.new(path, 'w')
+ * f.pos # => 0
+ *
+ * - File may be written anywhere (even past end-of-file);
+ * see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.write('foo')
+ * f.flush
+ * File.read(path) # => "foo"
+ * f.pos # => 3
+ *
+ * f.write('bar')
+ * f.flush
+ * File.read(path) # => "foobar"
+ * f.pos # => 6
+ *
+ * f.rewind
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "bazbar"
+ * f.pos # => 3
+ *
+ * f.pos = 3
+ * f.write('foo')
+ * f.flush
+ * File.read(path) # => "bazfoo"
+ * f.pos # => 6
+ *
+ * f.seek(-3, :END)
+ * f.write('bam')
+ * f.flush
+ * File.read(path) # => "bazbam"
+ * f.pos # => 6
+ *
+ * f.pos = 8
+ * f.write('bah') # Zero padding as needed.
+ * f.flush
+ * File.read(path) # => "bazbam\u0000\u0000bah"
+ * f.pos # => 11
+ *
+ * - Reading is not allowed:
+ *
+ * f.read # Raises IOError.
+ *
+ * - <tt>'a'</tt>:
+ *
+ * - File's initial write position is 0:
+ *
+ * path = 't.tmp'
+ * FileUtils.rm_f(path)
+ * f = File.new(path, 'a')
+ * f.pos # => 0
+ *
+ * - Writing occurs only at end-of-file:
+ *
+ * f.write('foo')
+ * f.pos # => 3
+ * f.write('bar')
+ * f.pos # => 6
+ * f.flush
+ * File.read(path) # => "foobar"
+ *
+ * f.rewind
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "foobarbaz"
+ *
+ * - Reading is not allowed:
+ *
+ * f.read # Raises IOError.
+ *
+ * - <tt>'w+'</tt>:
+ *
+ * - File's initial position is 0:
+ *
+ * path = 't.tmp'
+ * FileUtils.rm_f(path)
+ * f = File.new(path, 'w+')
+ * f.pos # => 0
+ *
+ * - File may be written anywhere (even past end-of-file);
+ * see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.write('foo')
+ * f.flush
+ * File.read(path) # => "foo"
+ * f.pos # => 3
+ *
+ * f.write('bar')
+ * f.flush
+ * File.read(path) # => "foobar"
+ * f.pos # => 6
+ *
+ * f.rewind
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "bazbar"
+ * f.pos # => 3
+ *
+ * f.pos = 3
+ * f.write('foo')
+ * f.flush
+ * File.read(path) # => "bazfoo"
+ * f.pos # => 6
+ *
+ * f.seek(-3, :END)
+ * f.write('bam')
+ * f.flush
+ * File.read(path) # => "bazbam"
+ * f.pos # => 6
+ *
+ * f.pos = 8
+ * f.write('bah') # Zero padding as needed.
+ * f.flush
+ * File.read(path) # => "bazbam\u0000\u0000bah"
+ * f.pos # => 11
+ *
+ * - File may be read anywhere (even past end-of-file);
+ * see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.rewind
+ * # => 0
+ * f.read
+ * # => "bazbam\u0000\u0000bah"
+ *
+ * f.pos = 3
+ * # => 3
+ * f.read
+ * # => "bam\u0000\u0000bah"
+ *
+ * f.seek(-3, :END)
+ * # => 0
+ * f.read
+ * # => "bah"
+ *
+ * - <tt>'a+'</tt>:
+ *
+ * - File's initial write position is 0:
+ *
+ * path = 't.tmp'
+ * FileUtils.rm_f(path)
+ * f = File.new(path, 'a+')
+ * f.pos # => 0
+ *
+ * - Writing occurs only at end-of-file:
+ *
+ * f.write('foo')
+ * f.pos # => 3
+ * f.write('bar')
+ * f.pos # => 6
+ * f.flush
+ * File.read(path) # => "foobar"
+ *
+ * f.rewind
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "foobarbaz"
+ *
+ * - File may be read anywhere (even past end-of-file);
+ * see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.rewind
+ * f.read # => "foobarbaz"
+ *
+ * f.pos = 3
+ * f.read # => "barbaz"
+ *
+ * f.seek(-3, :END)
+ * f.read # => "baz"
+ *
+ * f.pos = 800
+ * f.read # => ""
+ *
+ * ==== \Data Mode
+ *
+ * To specify whether data is to be treated as text or as binary data,
+ * either of the following may be suffixed to any of the string read/write modes
+ * above:
+ *
+ * - <tt>'t'</tt>: Text data; sets the default external encoding
+ * to <tt>Encoding::UTF_8</tt>;
+ * on Windows, enables conversion between EOL and CRLF
+ * and enables interpreting <tt>0x1A</tt> as an end-of-file marker.
+ * - <tt>'b'</tt>: Binary data; sets the default external encoding
+ * to <tt>Encoding::ASCII_8BIT</tt>;
+ * on Windows, suppresses conversion between EOL and CRLF
+ * and disables interpreting <tt>0x1A</tt> as an end-of-file marker.
+ *
+ * If neither is given, the stream defaults to text data.
+ *
+ * Examples:
+ *
+ * File.new('t.txt', 'rt')
+ * File.new('t.dat', 'rb')
+ *
+ * When the data mode is specified, the read/write mode may not be omitted,
+ * and the data mode must precede the file-create mode, if given:
+ *
+ * File.new('t.dat', 'b') # Raises an exception.
+ * File.new('t.dat', 'rxb') # Raises an exception.
+ *
+ * ==== \File-Create Mode
+ *
+ * The following may be suffixed to any writable string mode above:
+ *
+ * - <tt>'x'</tt>: Creates the file if it does not exist;
+ * raises an exception if the file exists.
+ *
+ * Example:
+ *
+ * File.new('t.tmp', 'wx')
+ *
+ * When the file-create mode is specified, the read/write mode may not be omitted,
+ * and the file-create mode must follow the data mode:
+ *
+ * File.new('t.dat', 'x') # Raises an exception.
+ * File.new('t.dat', 'rxb') # Raises an exception.
+ *
+ * === \Integer Access Modes
+ *
+ * When mode is an integer it must be one or more of the following constants,
+ * which may be combined by the bitwise OR operator <tt>|</tt>:
+ *
+ * - +File::RDONLY+: Open for reading only.
+ * - +File::WRONLY+: Open for writing only.
+ * - +File::RDWR+: Open for reading and writing.
+ * - +File::APPEND+: Open for appending only.
+ *
+ * Examples:
+ *
+ * File.new('t.txt', File::RDONLY)
+ * File.new('t.tmp', File::RDWR | File::CREAT | File::EXCL)
+ *
+ * Note: Method IO#set_encoding does not allow the mode to be specified as an integer.
+ *
+ * === File-Create Mode Specified as an \Integer
+ *
+ * These constants may also be ORed into the integer mode:
+ *
+ * - +File::CREAT+: Create file if it does not exist.
+ * - +File::EXCL+: Raise an exception if +File::CREAT+ is given and the file exists.
+ *
+ * === \Data Mode Specified as an \Integer
+ *
+ * \Data mode cannot be specified as an integer.
+ * When the stream access mode is given as an integer,
+ * the data mode is always text, never binary.
+ *
+ * Note that although there is a constant +File::BINARY+,
+ * setting its value in an integer stream mode has no effect;
+ * this is because, as documented in File::Constants,
+ * the +File::BINARY+ value disables line code conversion,
+ * but does not change the external encoding.
+ *
+ * === Encodings
+ *
+ * Any of the string modes above may specify encodings -
+ * either external encoding only or both external and internal encodings -
+ * by appending one or both encoding names, separated by colons:
+ *
+ * f = File.new('t.dat', 'rb')
+ * f.external_encoding # => #<Encoding:ASCII-8BIT>
+ * f.internal_encoding # => nil
+ * f = File.new('t.dat', 'rb:UTF-16')
+ * f.external_encoding # => #<Encoding:UTF-16 (dummy)>
+ * f.internal_encoding # => nil
+ * f = File.new('t.dat', 'rb:UTF-16:UTF-16')
+ * f.external_encoding # => #<Encoding:UTF-16 (dummy)>
+ * f.internal_encoding # => #<Encoding:UTF-16>
+ * f.close
+ *
+ * The numerous encoding names are available in array Encoding.name_list:
+ *
+ * Encoding.name_list.take(3) # => ["ASCII-8BIT", "UTF-8", "US-ASCII"]
+ *
+ * When the external encoding is set, strings read are tagged by that encoding
+ * when reading, and strings written are converted to that encoding when
+ * writing.
+ *
+ * When both external and internal encodings are set,
+ * strings read are converted from external to internal encoding,
+ * and strings written are converted from internal to external encoding.
+ * For further details about transcoding input and output,
+ * see {Encodings}[rdoc-ref:encodings.rdoc@Encodings].
+ *
+ * If the external encoding is <tt>'BOM|UTF-8'</tt>, <tt>'BOM|UTF-16LE'</tt>
+ * or <tt>'BOM|UTF16-BE'</tt>,
+ * Ruby checks for a Unicode BOM in the input document
+ * to help determine the encoding.
+ * For UTF-16 encodings the file open mode must be binary.
+ * If the BOM is found,
+ * it is stripped and the external encoding from the BOM is used.
+ *
+ * Note that the BOM-style encoding option is case insensitive,
+ * so <tt>'bom|utf-8'</tt> is also valid.
+ *
* == \File Permissions
*
* A \File object has _permissions_, an octal integer representing
@@ -6616,40 +7177,12 @@ const char ruby_null_device[] =
* f.chmod(0644)
* f.chmod(0444)
*
- * == \File Constants
+ * == \File \Constants
*
* Various constants for use in \File and \IO methods
* may be found in module File::Constants;
* an array of their names is returned by <tt>File::Constants.constants</tt>.
*
- * == Example Files
- *
- * Many examples here use these filenames and their corresponding files:
- *
- * - <tt>t.txt</tt>: A text-only file that is assumed to exist via:
- *
- * text = <<~EOT
- * First line
- * Second line
- *
- * Fourth line
- * Fifth line
- * EOT
- * File.write('t.txt', text)
- *
- * - <tt>t.dat</tt>: A data file that is assumed to exist via:
- *
- * data = "\u9990\u9991\u9992\u9993\u9994"
- * f = File.open('t.dat', 'wb:UTF-16')
- * f.write(data)
- * f.close
- *
- * - <tt>t.rus</tt>: A Russian-language text file that is assumed to exist via:
- *
- * File.write('t.rus', "\u{442 435 441 442}")
- *
- * - <tt>t.tmp</tt>: A file that is assumed _not_ to exist.
- *
* == What's Here
*
* First, what's elsewhere. \Class \File:
@@ -6716,7 +7249,7 @@ const char ruby_null_device[] =
*
* - ::blockdev?: Returns whether the file at the given path is a block device.
* - ::chardev?: Returns whether the file at the given path is a character device.
- * - ::directory?: Returns whether the file at the given path is a diretory.
+ * - ::directory?: Returns whether the file at the given path is a directory.
* - ::executable?: Returns whether the file at the given path is executable
* by the effective user and group of the current process.
* - ::executable_real?: Returns whether the file at the given path is executable
@@ -6787,6 +7320,10 @@ const char ruby_null_device[] =
void
Init_File(void)
{
+#if defined(__APPLE__) && defined(HAVE_WORKING_FORK)
+ rb_CFString_class_initialize_before_fork();
+#endif
+
VALUE separator;
rb_mFileTest = rb_define_module("FileTest");
@@ -6983,8 +7520,6 @@ Init_File(void)
/* Name of the null device */
rb_define_const(rb_mFConst, "NULL", rb_fstring_cstr(ruby_null_device));
- rb_define_method(rb_cFile, "path", rb_file_path, 0);
- rb_define_method(rb_cFile, "to_path", rb_file_path, 0);
rb_define_global_function("test", rb_f_test, -1);
rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
diff --git a/gc.c b/gc.c
index 4f92c371be..919d57989a 100644
--- a/gc.c
+++ b/gc.c
@@ -138,6 +138,7 @@
#include "ractor_core.h"
#include "builtin.h"
+#include "shape.h"
#define rb_setjmp(env) RUBY_SETJMP(env)
#define rb_jmp_buf rb_jmpbuf_t
@@ -573,64 +574,77 @@ struct RMoved {
VALUE flags;
VALUE dummy;
VALUE destination;
+ shape_id_t original_shape_id;
};
#define RMOVED(obj) ((struct RMoved *)(obj))
typedef struct RVALUE {
union {
- struct {
- VALUE flags; /* always 0 for freed obj */
- struct RVALUE *next;
- } free;
+ struct {
+ VALUE flags; /* always 0 for freed obj */
+ struct RVALUE *next;
+ } free;
struct RMoved moved;
- struct RBasic basic;
- struct RObject object;
- struct RClass klass;
- struct RFloat flonum;
- struct RString string;
- struct RArray array;
- struct RRegexp regexp;
- struct RHash hash;
- struct RData data;
- struct RTypedData typeddata;
- struct RStruct rstruct;
- struct RBignum bignum;
- struct RFile file;
- struct RMatch match;
- struct RRational rational;
- struct RComplex complex;
+ struct RBasic basic;
+ struct RObject object;
+ struct RClass klass;
+ struct RFloat flonum;
+ struct RString string;
+ struct RArray array;
+ struct RRegexp regexp;
+ struct RHash hash;
+ struct RData data;
+ struct RTypedData typeddata;
+ struct RStruct rstruct;
+ struct RBignum bignum;
+ struct RFile file;
+ struct RMatch match;
+ struct RRational rational;
+ struct RComplex complex;
struct RSymbol symbol;
- union {
- rb_cref_t cref;
- struct vm_svar svar;
- struct vm_throw_data throw_data;
- struct vm_ifunc ifunc;
- struct MEMO memo;
- struct rb_method_entry_struct ment;
- const rb_iseq_t iseq;
- rb_env_t env;
- struct rb_imemo_tmpbuf_struct alloc;
- rb_ast_t ast;
- } imemo;
- struct {
- struct RBasic basic;
- VALUE v1;
- VALUE v2;
- VALUE v3;
- } values;
+ union {
+ rb_cref_t cref;
+ struct vm_svar svar;
+ struct vm_throw_data throw_data;
+ struct vm_ifunc ifunc;
+ struct MEMO memo;
+ struct rb_method_entry_struct ment;
+ const rb_iseq_t iseq;
+ rb_env_t env;
+ struct rb_imemo_tmpbuf_struct alloc;
+ rb_ast_t ast;
+ } imemo;
+ struct {
+ struct RBasic basic;
+ VALUE v1;
+ VALUE v2;
+ VALUE v3;
+ } values;
} as;
+
+ /* Start of RVALUE_OVERHEAD.
+ * Do not directly read these members from the RVALUE as they're located
+ * at the end of the slot (which may differ in size depending on the size
+ * pool). */
+#if RACTOR_CHECK_MODE
+ uint32_t _ractor_belonging_id;
+#endif
#if GC_DEBUG
const char *file;
int line;
#endif
} RVALUE;
-#if GC_DEBUG
-STATIC_ASSERT(sizeof_rvalue, offsetof(RVALUE, file) == SIZEOF_VALUE * 5);
+#if RACTOR_CHECK_MODE
+# define RVALUE_OVERHEAD (sizeof(RVALUE) - offsetof(RVALUE, _ractor_belonging_id))
+#elif GC_DEBUG
+# define RVALUE_OVERHEAD (sizeof(RVALUE) - offsetof(RVALUE, file))
#else
-STATIC_ASSERT(sizeof_rvalue, sizeof(RVALUE) == SIZEOF_VALUE * 5);
+# define RVALUE_OVERHEAD 0
#endif
+
+STATIC_ASSERT(sizeof_rvalue, sizeof(RVALUE) == (SIZEOF_VALUE * 5) + RVALUE_OVERHEAD);
STATIC_ASSERT(alignof_rvalue, RUBY_ALIGNOF(RVALUE) == SIZEOF_VALUE);
typedef uintptr_t bits_t;
@@ -716,27 +730,27 @@ enum gc_mode {
typedef struct rb_objspace {
struct {
- size_t limit;
- size_t increase;
+ size_t limit;
+ size_t increase;
#if MALLOC_ALLOCATED_SIZE
- size_t allocated_size;
- size_t allocations;
+ size_t allocated_size;
+ size_t allocations;
#endif
} malloc_params;
struct {
- unsigned int mode : 2;
- unsigned int immediate_sweep : 1;
- unsigned int dont_gc : 1;
- unsigned int dont_incremental : 1;
- unsigned int during_gc : 1;
+ unsigned int mode : 2;
+ unsigned int immediate_sweep : 1;
+ unsigned int dont_gc : 1;
+ unsigned int dont_incremental : 1;
+ unsigned int during_gc : 1;
unsigned int during_compacting : 1;
- unsigned int gc_stressful: 1;
- unsigned int has_hook: 1;
- unsigned int during_minor_gc : 1;
+ unsigned int gc_stressful: 1;
+ unsigned int has_hook: 1;
+ unsigned int during_minor_gc : 1;
#if GC_ENABLE_INCREMENTAL_MARK
- unsigned int during_incremental_marking : 1;
+ unsigned int during_incremental_marking : 1;
#endif
unsigned int measure_gc : 1;
} flags;
@@ -748,70 +762,70 @@ typedef struct rb_objspace {
rb_size_pool_t size_pools[SIZE_POOL_COUNT];
struct {
- rb_atomic_t finalizing;
+ rb_atomic_t finalizing;
} atomic_flags;
mark_stack_t mark_stack;
size_t marked_slots;
struct {
- struct heap_page **sorted;
- size_t allocated_pages;
- size_t allocatable_pages;
- size_t sorted_length;
- uintptr_t range[2];
- size_t freeable_pages;
-
- /* final */
- size_t final_slots;
- VALUE deferred_final;
+ struct heap_page **sorted;
+ size_t allocated_pages;
+ size_t allocatable_pages;
+ size_t sorted_length;
+ uintptr_t range[2];
+ size_t freeable_pages;
+
+ /* final */
+ size_t final_slots;
+ VALUE deferred_final;
} heap_pages;
st_table *finalizer_table;
struct {
- int run;
- unsigned int latest_gc_info;
- gc_profile_record *records;
- gc_profile_record *current_record;
- size_t next_index;
- size_t size;
+ int run;
+ unsigned int latest_gc_info;
+ gc_profile_record *records;
+ gc_profile_record *current_record;
+ size_t next_index;
+ size_t size;
#if GC_PROFILE_MORE_DETAIL
- double prepare_time;
+ double prepare_time;
#endif
- double invoke_time;
+ double invoke_time;
- size_t minor_gc_count;
- size_t major_gc_count;
- size_t compact_count;
- size_t read_barrier_faults;
+ size_t minor_gc_count;
+ size_t major_gc_count;
+ size_t compact_count;
+ size_t read_barrier_faults;
#if RGENGC_PROFILE > 0
- size_t total_generated_normal_object_count;
- size_t total_generated_shady_object_count;
- size_t total_shade_operation_count;
- size_t total_promoted_count;
- size_t total_remembered_normal_object_count;
- size_t total_remembered_shady_object_count;
+ size_t total_generated_normal_object_count;
+ size_t total_generated_shady_object_count;
+ size_t total_shade_operation_count;
+ size_t total_promoted_count;
+ size_t total_remembered_normal_object_count;
+ size_t total_remembered_shady_object_count;
#if RGENGC_PROFILE >= 2
- size_t generated_normal_object_count_types[RUBY_T_MASK];
- size_t generated_shady_object_count_types[RUBY_T_MASK];
- size_t shade_operation_count_types[RUBY_T_MASK];
- size_t promoted_types[RUBY_T_MASK];
- size_t remembered_normal_object_count_types[RUBY_T_MASK];
- size_t remembered_shady_object_count_types[RUBY_T_MASK];
+ size_t generated_normal_object_count_types[RUBY_T_MASK];
+ size_t generated_shady_object_count_types[RUBY_T_MASK];
+ size_t shade_operation_count_types[RUBY_T_MASK];
+ size_t promoted_types[RUBY_T_MASK];
+ size_t remembered_normal_object_count_types[RUBY_T_MASK];
+ size_t remembered_shady_object_count_types[RUBY_T_MASK];
#endif
#endif /* RGENGC_PROFILE */
- /* temporary profiling space */
- double gc_sweep_start_time;
- size_t total_allocated_objects_at_gc_start;
- size_t heap_used_at_gc_start;
+ /* temporary profiling space */
+ double gc_sweep_start_time;
+ size_t total_allocated_objects_at_gc_start;
+ size_t heap_used_at_gc_start;
- /* basic statistics */
- size_t count;
- size_t total_freed_objects;
+ /* basic statistics */
+ size_t count;
+ size_t total_freed_objects;
uint64_t total_time_ns;
struct timespec start_time;
} profile;
@@ -820,22 +834,22 @@ typedef struct rb_objspace {
VALUE gc_stress_mode;
struct {
- VALUE parent_object;
- int need_major_gc;
- size_t last_major_gc;
- size_t uncollectible_wb_unprotected_objects;
- size_t uncollectible_wb_unprotected_objects_limit;
- size_t old_objects;
- size_t old_objects_limit;
+ VALUE parent_object;
+ int need_major_gc;
+ size_t last_major_gc;
+ size_t uncollectible_wb_unprotected_objects;
+ size_t uncollectible_wb_unprotected_objects_limit;
+ size_t old_objects;
+ size_t old_objects_limit;
#if RGENGC_ESTIMATE_OLDMALLOC
- size_t oldmalloc_increase;
- size_t oldmalloc_increase_limit;
+ size_t oldmalloc_increase;
+ size_t oldmalloc_increase_limit;
#endif
#if RGENGC_CHECK_MODE >= 2
- struct st_table *allrefs_table;
- size_t error_count;
+ struct st_table *allrefs_table;
+ size_t error_count;
#endif
} rgengc;
@@ -849,8 +863,8 @@ typedef struct rb_objspace {
#if GC_ENABLE_INCREMENTAL_MARK
struct {
- size_t pooled_slots;
- size_t step_slots;
+ size_t pooled_slots;
+ size_t step_slots;
} rincgc;
#endif
@@ -870,7 +884,7 @@ typedef struct rb_objspace {
#define BASE_SLOT_SIZE sizeof(RVALUE)
-#define CEILDIV(i, mod) (((i) + (mod) - 1)/(mod))
+#define CEILDIV(i, mod) roomof(i, mod)
enum {
HEAP_PAGE_ALIGN = (1UL << HEAP_PAGE_ALIGN_LOG),
HEAP_PAGE_ALIGN_MASK = (~(~0UL << HEAP_PAGE_ALIGN_LOG)),
@@ -932,13 +946,12 @@ struct heap_page {
short slot_size;
short total_slots;
short free_slots;
- short pinned_slots;
short final_slots;
struct {
- unsigned int before_sweep : 1;
- unsigned int has_remembered_objects : 1;
- unsigned int has_uncollectible_shady_objects : 1;
- unsigned int in_tomb : 1;
+ unsigned int before_sweep : 1;
+ unsigned int has_remembered_objects : 1;
+ unsigned int has_uncollectible_shady_objects : 1;
+ unsigned int in_tomb : 1;
} flags;
rb_size_pool_t *size_pool;
@@ -958,6 +971,24 @@ struct heap_page {
bits_t pinned_bits[HEAP_PAGE_BITMAP_LIMIT];
};
+/*
+ * When asan is enabled, this will prohibit writing to the freelist until it is unlocked
+ */
+static void
+asan_lock_freelist(struct heap_page *page)
+{
+ asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
+}
+
+/*
+ * When asan is enabled, this will enable the ability to write to the freelist
+ */
+static void
+asan_unlock_freelist(struct heap_page *page)
+{
+ asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
+}
+
#define GET_PAGE_BODY(x) ((struct heap_page_body *)((bits_t)(x) & ~(HEAP_PAGE_ALIGN_MASK)))
#define GET_PAGE_HEADER(x) (&GET_PAGE_BODY(x)->header)
#define GET_HEAP_PAGE(x) (GET_PAGE_HEADER(x)->page)
@@ -1034,9 +1065,9 @@ gc_mode_verify(enum gc_mode mode)
case gc_mode_marking:
case gc_mode_sweeping:
case gc_mode_compacting:
- break;
+ break;
default:
- rb_bug("gc_mode_verify: unreachable (%d)", (int)mode);
+ rb_bug("gc_mode_verify: unreachable (%d)", (int)mode);
}
#endif
return mode;
@@ -1341,6 +1372,27 @@ tick(void)
return val;
}
+/* Implementation for macOS PPC by @nobu
+ * See: https://github.com/ruby/ruby/pull/5975#discussion_r890045558
+ */
+#elif defined(__POWERPC__) && defined(__APPLE__)
+typedef unsigned long long tick_t;
+#define PRItick "llu"
+
+static __inline__ tick_t
+tick(void)
+{
+ unsigned long int upper, lower, tmp;
+ # define mftbu(r) __asm__ volatile("mftbu %0" : "=r"(r))
+ # define mftb(r) __asm__ volatile("mftb %0" : "=r"(r))
+ do {
+ mftbu(upper);
+ mftb(lower);
+ mftbu(tmp);
+ } while (tmp != upper);
+ return ((tick_t)upper << 32) | lower;
+}
+
#elif defined(__aarch64__) && defined(__GNUC__)
typedef unsigned long tick_t;
#define PRItick "lu"
@@ -1698,14 +1750,14 @@ RVALUE_AGE_INC(rb_objspace_t *objspace, VALUE obj)
int age = RVALUE_FLAGS_AGE(flags);
if (RGENGC_CHECK_MODE && age == RVALUE_OLD_AGE) {
- rb_bug("RVALUE_AGE_INC: can not increment age of OLD object %s.", obj_info(obj));
+ rb_bug("RVALUE_AGE_INC: can not increment age of OLD object %s.", obj_info(obj));
}
age++;
RBASIC(obj)->flags = RVALUE_FLAGS_AGE_SET(flags, age);
if (age == RVALUE_OLD_AGE) {
- RVALUE_OLD_UNCOLLECTIBLE_SET(objspace, obj);
+ RVALUE_OLD_UNCOLLECTIBLE_SET(objspace, obj);
}
check_rvalue_consistency(obj);
}
@@ -1749,13 +1801,13 @@ RVALUE_DEMOTE(rb_objspace_t *objspace, VALUE obj)
GC_ASSERT(RVALUE_OLD_P(obj));
if (!is_incremental_marking(objspace) && RVALUE_REMEMBERED(obj)) {
- CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj);
+ CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj);
}
RVALUE_DEMOTE_RAW(objspace, obj);
if (RVALUE_MARKED(obj)) {
- objspace->rgengc.old_objects--;
+ objspace->rgengc.old_objects--;
}
check_rvalue_consistency(obj);
@@ -1836,30 +1888,30 @@ void
rb_objspace_free(rb_objspace_t *objspace)
{
if (is_lazy_sweeping(objspace))
- rb_bug("lazy sweeping underway when freeing object space");
+ rb_bug("lazy sweeping underway when freeing object space");
if (objspace->profile.records) {
- free(objspace->profile.records);
- objspace->profile.records = 0;
+ free(objspace->profile.records);
+ objspace->profile.records = 0;
}
if (global_list) {
- struct gc_list *list, *next;
- for (list = global_list; list; list = next) {
- next = list->next;
- xfree(list);
- }
+ struct gc_list *list, *next;
+ for (list = global_list; list; list = next) {
+ next = list->next;
+ xfree(list);
+ }
}
if (heap_pages_sorted) {
- size_t i;
- for (i = 0; i < heap_allocated_pages; ++i) {
- heap_page_free(objspace, heap_pages_sorted[i]);
- }
- free(heap_pages_sorted);
- heap_allocated_pages = 0;
- heap_pages_sorted_length = 0;
- heap_pages_lomem = 0;
- heap_pages_himem = 0;
+ size_t i;
+ for (i = 0; i < heap_allocated_pages; ++i) {
+ heap_page_free(objspace, heap_pages_sorted[i]);
+ }
+ free(heap_pages_sorted);
+ heap_allocated_pages = 0;
+ heap_pages_sorted_length = 0;
+ heap_pages_lomem = 0;
+ heap_pages_himem = 0;
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
rb_size_pool_t *size_pool = &size_pools[i];
@@ -1886,15 +1938,15 @@ heap_pages_expand_sorted_to(rb_objspace_t *objspace, size_t next_length)
next_length, size);
if (heap_pages_sorted_length > 0) {
- sorted = (struct heap_page **)realloc(heap_pages_sorted, size);
- if (sorted) heap_pages_sorted = sorted;
+ sorted = (struct heap_page **)realloc(heap_pages_sorted, size);
+ if (sorted) heap_pages_sorted = sorted;
}
else {
- sorted = heap_pages_sorted = (struct heap_page **)malloc(size);
+ sorted = heap_pages_sorted = (struct heap_page **)malloc(size);
}
if (sorted == 0) {
- rb_memerror();
+ rb_memerror();
}
heap_pages_sorted_length = next_length;
@@ -1916,7 +1968,7 @@ heap_pages_expand_sorted(rb_objspace_t *objspace)
}
if (next_length > heap_pages_sorted_length) {
- heap_pages_expand_sorted_to(objspace, next_length);
+ heap_pages_expand_sorted_to(objspace, next_length);
}
GC_ASSERT(heap_allocatable_pages(objspace) + heap_eden_total_pages(objspace) <= heap_pages_sorted_length);
@@ -1939,12 +1991,12 @@ heap_page_add_freeobj(rb_objspace_t *objspace, struct heap_page *page, VALUE obj
asan_unpoison_object(obj, false);
- asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
+ asan_unlock_freelist(page);
p->as.free.flags = 0;
p->as.free.next = page->freelist;
page->freelist = p;
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
+ asan_lock_freelist(page);
if (RGENGC_CHECK_MODE &&
/* obj should belong to page */
@@ -1961,7 +2013,7 @@ heap_page_add_freeobj(rb_objspace_t *objspace, struct heap_page *page, VALUE obj
static inline void
heap_add_freepage(rb_heap_t *heap, struct heap_page *page)
{
- asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
+ asan_unlock_freelist(page);
GC_ASSERT(page->free_slots != 0);
GC_ASSERT(page->freelist != NULL);
@@ -1970,14 +2022,14 @@ heap_add_freepage(rb_heap_t *heap, struct heap_page *page)
RUBY_DEBUG_LOG("page:%p freelist:%p", (void *)page, (void *)page->freelist);
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
+ asan_lock_freelist(page);
}
#if GC_ENABLE_INCREMENTAL_MARK
static inline void
heap_add_poolpage(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *page)
{
- asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
+ asan_unlock_freelist(page);
GC_ASSERT(page->free_slots != 0);
GC_ASSERT(page->freelist != NULL);
@@ -1985,7 +2037,7 @@ heap_add_poolpage(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *pa
heap->pooled_pages = page;
objspace->rincgc.pooled_slots += page->free_slots;
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
+ asan_lock_freelist(page);
}
#endif
@@ -2060,7 +2112,7 @@ heap_pages_free_unused_pages(rb_objspace_t *objspace)
GC_ASSERT(himem <= heap_pages_himem);
heap_pages_himem = himem;
- GC_ASSERT(j == heap_allocated_pages);
+ GC_ASSERT(j == heap_allocated_pages);
}
}
@@ -2124,14 +2176,14 @@ heap_page_allocate(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
/* assign heap_page body (contains heap_page_header and RVALUEs) */
struct heap_page_body *page_body = heap_page_body_allocate();
if (page_body == 0) {
- rb_memerror();
+ rb_memerror();
}
/* assign heap_page entry */
page = calloc1(sizeof(struct heap_page));
if (page == 0) {
heap_page_body_free(page_body);
- rb_memerror();
+ rb_memerror();
}
/* adjust obj_limit (object number available in this page) */
@@ -2152,7 +2204,7 @@ heap_page_allocate(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
GC_ASSERT(NUM_IN_PAGE(start) * BASE_SLOT_SIZE % stride == 0);
- limit = (HEAP_PAGE_SIZE - (int)(start - (uintptr_t)page_body))/(int)stride;
+ limit = (HEAP_PAGE_SIZE - (int)(start - (uintptr_t)page_body))/(int)stride;
}
end = start + (limit * (int)stride);
@@ -2160,23 +2212,23 @@ heap_page_allocate(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
lo = 0;
hi = (uintptr_t)heap_allocated_pages;
while (lo < hi) {
- struct heap_page *mid_page;
+ struct heap_page *mid_page;
- mid = (lo + hi) / 2;
- mid_page = heap_pages_sorted[mid];
- if ((uintptr_t)mid_page->start < start) {
- lo = mid + 1;
- }
- else if ((uintptr_t)mid_page->start > start) {
- hi = mid;
- }
- else {
- rb_bug("same heap page is allocated: %p at %"PRIuVALUE, (void *)page_body, (VALUE)mid);
- }
+ mid = (lo + hi) / 2;
+ mid_page = heap_pages_sorted[mid];
+ if ((uintptr_t)mid_page->start < start) {
+ lo = mid + 1;
+ }
+ else if ((uintptr_t)mid_page->start > start) {
+ hi = mid;
+ }
+ else {
+ rb_bug("same heap page is allocated: %p at %"PRIuVALUE, (void *)page_body, (VALUE)mid);
+ }
}
if (hi < (uintptr_t)heap_allocated_pages) {
- MEMMOVE(&heap_pages_sorted[hi+1], &heap_pages_sorted[hi], struct heap_page_header*, heap_allocated_pages - hi);
+ MEMMOVE(&heap_pages_sorted[hi+1], &heap_pages_sorted[hi], struct heap_page_header*, heap_allocated_pages - hi);
}
heap_pages_sorted[hi] = page;
@@ -2190,8 +2242,8 @@ heap_page_allocate(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
size_pool->total_allocated_pages++;
if (heap_allocated_pages > heap_pages_sorted_length) {
- rb_bug("heap_page_allocate: allocated(%"PRIdSIZE") > sorted(%"PRIdSIZE")",
- heap_allocated_pages, heap_pages_sorted_length);
+ rb_bug("heap_page_allocate: allocated(%"PRIdSIZE") > sorted(%"PRIdSIZE")",
+ heap_allocated_pages, heap_pages_sorted_length);
}
if (heap_pages_lomem == 0 || heap_pages_lomem > start) heap_pages_lomem = start;
@@ -2204,12 +2256,12 @@ heap_page_allocate(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
page_body->header.page = page;
for (p = start; p != end; p += stride) {
- gc_report(3, objspace, "assign_heap_page: %p is added to freelist\n", (void *)p);
- heap_page_add_freeobj(objspace, page, (VALUE)p);
+ gc_report(3, objspace, "assign_heap_page: %p is added to freelist\n", (void *)p);
+ heap_page_add_freeobj(objspace, page, (VALUE)p);
}
page->free_slots = limit;
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
+ asan_lock_freelist(page);
return page;
}
@@ -2219,12 +2271,12 @@ heap_page_resurrect(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
struct heap_page *page = 0, *next;
ccan_list_for_each_safe(&SIZE_POOL_TOMB_HEAP(size_pool)->pages, page, next, page_node) {
- asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
- if (page->freelist != NULL) {
- heap_unlink_page(objspace, &size_pool->tomb_heap, page);
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
- return page;
- }
+ asan_unlock_freelist(page);
+ if (page->freelist != NULL) {
+ heap_unlink_page(objspace, &size_pool->tomb_heap, page);
+ asan_lock_freelist(page);
+ return page;
+ }
}
return NULL;
@@ -2241,8 +2293,8 @@ heap_page_create(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
page = heap_page_resurrect(objspace, size_pool);
if (page == NULL) {
- page = heap_page_allocate(objspace, size_pool);
- method = "allocate";
+ page = heap_page_allocate(objspace, size_pool);
+ method = "allocate";
}
if (0) fprintf(stderr, "heap_page_create: %s - %p, "
"heap_allocated_pages: %"PRIdSIZE", "
@@ -2279,7 +2331,7 @@ heap_add_pages(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *he
size_pool_allocatable_pages_set(objspace, size_pool, add);
for (i = 0; i < add; i++) {
- heap_assign_page(objspace, size_pool, heap);
+ heap_assign_page(objspace, size_pool, heap);
}
GC_ASSERT(size_pool->allocatable_pages == 0);
@@ -2292,36 +2344,36 @@ heap_extend_pages(rb_objspace_t *objspace, rb_size_pool_t *size_pool, size_t fre
size_t next_used;
if (goal_ratio == 0.0) {
- next_used = (size_t)(used * gc_params.growth_factor);
+ next_used = (size_t)(used * gc_params.growth_factor);
}
else if (total_slots == 0) {
int multiple = size_pool->slot_size / BASE_SLOT_SIZE;
next_used = (gc_params.heap_init_slots * multiple) / HEAP_PAGE_OBJ_LIMIT;
}
else {
- /* Find `f' where free_slots = f * total_slots * goal_ratio
- * => f = (total_slots - free_slots) / ((1 - goal_ratio) * total_slots)
- */
- double f = (double)(total_slots - free_slots) / ((1 - goal_ratio) * total_slots);
+ /* Find `f' where free_slots = f * total_slots * goal_ratio
+ * => f = (total_slots - free_slots) / ((1 - goal_ratio) * total_slots)
+ */
+ double f = (double)(total_slots - free_slots) / ((1 - goal_ratio) * total_slots);
- if (f > gc_params.growth_factor) f = gc_params.growth_factor;
- if (f < 1.0) f = 1.1;
+ if (f > gc_params.growth_factor) f = gc_params.growth_factor;
+ if (f < 1.0) f = 1.1;
- next_used = (size_t)(f * used);
+ next_used = (size_t)(f * used);
- if (0) {
- fprintf(stderr,
- "free_slots(%8"PRIuSIZE")/total_slots(%8"PRIuSIZE")=%1.2f,"
- " G(%1.2f), f(%1.2f),"
- " used(%8"PRIuSIZE") => next_used(%8"PRIuSIZE")\n",
- free_slots, total_slots, free_slots/(double)total_slots,
- goal_ratio, f, used, next_used);
- }
+ if (0) {
+ fprintf(stderr,
+ "free_slots(%8"PRIuSIZE")/total_slots(%8"PRIuSIZE")=%1.2f,"
+ " G(%1.2f), f(%1.2f),"
+ " used(%8"PRIuSIZE") => next_used(%8"PRIuSIZE")\n",
+ free_slots, total_slots, free_slots/(double)total_slots,
+ goal_ratio, f, used, next_used);
+ }
}
if (gc_params.growth_max_slots > 0) {
- size_t max_used = (size_t)(used + gc_params.growth_max_slots/HEAP_PAGE_OBJ_LIMIT);
- if (next_used > max_used) next_used = max_used;
+ size_t max_used = (size_t)(used + gc_params.growth_max_slots/HEAP_PAGE_OBJ_LIMIT);
+ if (next_used > max_used) next_used = max_used;
}
size_t extend_page_count = next_used - used;
@@ -2335,15 +2387,15 @@ static int
heap_increment(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap)
{
if (size_pool->allocatable_pages > 0) {
- gc_report(1, objspace, "heap_increment: heap_pages_sorted_length: %"PRIdSIZE", "
+ gc_report(1, objspace, "heap_increment: heap_pages_sorted_length: %"PRIdSIZE", "
"heap_pages_inc: %"PRIdSIZE", heap->total_pages: %"PRIdSIZE"\n",
- heap_pages_sorted_length, size_pool->allocatable_pages, heap->total_pages);
+ heap_pages_sorted_length, size_pool->allocatable_pages, heap->total_pages);
- GC_ASSERT(heap_allocatable_pages(objspace) + heap_eden_total_pages(objspace) <= heap_pages_sorted_length);
- GC_ASSERT(heap_allocated_pages <= heap_pages_sorted_length);
+ GC_ASSERT(heap_allocatable_pages(objspace) + heap_eden_total_pages(objspace) <= heap_pages_sorted_length);
+ GC_ASSERT(heap_allocated_pages <= heap_pages_sorted_length);
- heap_assign_page(objspace, size_pool, heap);
- return TRUE;
+ heap_assign_page(objspace, size_pool, heap);
+ return TRUE;
}
return FALSE;
}
@@ -2353,7 +2405,7 @@ gc_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap)
{
/* Continue marking if in incremental marking. */
if (heap->free_pages == NULL && is_incremental_marking(objspace)) {
- gc_marks_continue(objspace, size_pool, heap);
+ gc_marks_continue(objspace, size_pool, heap);
}
/* Continue sweeping if in lazy sweeping or the previous incremental
@@ -2422,6 +2474,7 @@ rb_objspace_set_event_hook(const rb_event_flag_t event)
static void
gc_event_hook_body(rb_execution_context_t *ec, rb_objspace_t *objspace, const rb_event_flag_t event, VALUE data)
{
+ if (UNLIKELY(!ec->cfp)) return;
const VALUE *pc = ec->cfp->pc;
if (pc && VM_FRAME_RUBYFRAME_P(ec->cfp)) {
/* increment PC because source line is calculated with PC-1 */
@@ -2437,7 +2490,7 @@ gc_event_hook_body(rb_execution_context_t *ec, rb_objspace_t *objspace, const rb
#define gc_event_hook_prep(objspace, event, data, prep) do { \
if (UNLIKELY(gc_event_hook_needed_p(objspace, event))) { \
prep; \
- gc_event_hook_body(GET_EC(), (objspace), (event), (data)); \
+ gc_event_hook_body(GET_EC(), (objspace), (event), (data)); \
} \
} while (0)
@@ -2536,7 +2589,7 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
size_t
rb_gc_obj_slot_size(VALUE obj)
{
- return GET_HEAP_PAGE(obj)->slot_size;
+ return GET_HEAP_PAGE(obj)->slot_size - RVALUE_OVERHEAD;
}
static inline size_t
@@ -2551,9 +2604,17 @@ size_pool_slot_size(unsigned char pool_id)
GC_ASSERT(size_pools[pool_id].slot_size == (short)slot_size);
#endif
+ slot_size -= RVALUE_OVERHEAD;
+
return slot_size;
}
+size_t
+rb_size_pool_slot_size(unsigned char pool_id)
+{
+ return size_pool_slot_size(pool_id);
+}
+
bool
rb_gc_size_allocatable_p(size_t size)
{
@@ -2618,7 +2679,7 @@ heap_next_free_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_
GC_ASSERT(page->free_slots != 0);
RUBY_DEBUG_LOG("page:%p freelist:%p cnt:%d", (void *)page, (void *)page->freelist, page->free_slots);
- asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
+ asan_unlock_freelist(page);
return page;
}
@@ -2659,6 +2720,8 @@ static inline size_t
size_pool_idx_for_size(size_t size)
{
#if USE_RVARGC
+ size += RVALUE_OVERHEAD;
+
size_t slot_count = CEILDIV(size, BASE_SLOT_SIZE);
/* size_pool_idx is ceil(log2(slot_count)) */
@@ -2733,6 +2796,12 @@ newobj_alloc(rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx, boo
return obj;
}
+static void
+newobj_zero_slot(VALUE obj)
+{
+ memset((char *)obj + sizeof(struct RBasic), 0, rb_gc_obj_slot_size(obj) - sizeof(struct RBasic));
+}
+
ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, int wb_protected, size_t size_pool_idx));
static inline VALUE
@@ -2758,9 +2827,12 @@ newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *
}
obj = newobj_alloc(objspace, cr, size_pool_idx, true);
+#if SHAPE_IN_BASIC_FLAGS
+ flags |= (VALUE)(size_pool_idx) << SHAPE_FLAG_SHIFT;
+#endif
newobj_init(klass, flags, wb_protected, objspace, obj);
- gc_event_hook_prep(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj, newobj_fill(obj, 0, 0, 0));
+ gc_event_hook_prep(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj, newobj_zero_slot(obj));
}
RB_VM_LOCK_LEAVE_CR_LEV(cr, &lev);
@@ -2809,6 +2881,9 @@ newobj_of0(VALUE klass, VALUE flags, int wb_protected, rb_ractor_t *cr, size_t a
gc_event_hook_available_p(objspace)) &&
wb_protected) {
obj = newobj_alloc(objspace, cr, size_pool_idx, false);
+#if SHAPE_IN_BASIC_FLAGS
+ flags |= (VALUE)size_pool_idx << SHAPE_FLAG_SHIFT;
+#endif
newobj_init(klass, flags, wb_protected, objspace, obj);
}
else {
@@ -2862,7 +2937,7 @@ rb_ec_wb_protected_newobj_of(rb_execution_context_t *ec, VALUE klass, VALUE flag
VALUE
rb_newobj(void)
{
- return newobj_of(0, T_NONE, 0, 0, 0, FALSE, sizeof(RVALUE));
+ return newobj_of(0, T_NONE, 0, 0, 0, FALSE, RVALUE_SIZE);
}
static size_t
@@ -2877,44 +2952,33 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected)
GC_ASSERT((flags & RUBY_T_MASK) == T_OBJECT);
GC_ASSERT(flags & ROBJECT_EMBED);
- st_table *index_tbl = RCLASS_IV_INDEX_TBL(klass);
- uint32_t index_tbl_num_entries = index_tbl == NULL ? 0 : (uint32_t)index_tbl->num_entries;
-
size_t size;
- bool embed = true;
#if USE_RVARGC
+ uint32_t index_tbl_num_entries = RCLASS_EXT(klass)->max_iv_count;
+
size = rb_obj_embedded_size(index_tbl_num_entries);
if (!rb_gc_size_allocatable_p(size)) {
size = sizeof(struct RObject);
- embed = false;
}
#else
size = sizeof(struct RObject);
- if (index_tbl_num_entries > ROBJECT_EMBED_LEN_MAX) {
- embed = false;
- }
#endif
-#if USE_RVARGC
VALUE obj = newobj_of(klass, flags, 0, 0, 0, wb_protected, size);
-#else
- VALUE obj = newobj_of(klass, flags, Qundef, Qundef, Qundef, wb_protected, size);
-#endif
+ RUBY_ASSERT(rb_shape_get_shape(obj)->type == SHAPE_ROOT ||
+ rb_shape_get_shape(obj)->type == SHAPE_INITIAL_CAPACITY);
- if (embed) {
-#if USE_RVARGC
- uint32_t capa = (uint32_t)((rb_gc_obj_slot_size(obj) - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
- GC_ASSERT(capa >= index_tbl_num_entries);
+ // Set the shape to the specific T_OBJECT shape which is always
+ // SIZE_POOL_COUNT away from the root shape.
+ ROBJECT_SET_SHAPE_ID(obj, ROBJECT_SHAPE_ID(obj) + SIZE_POOL_COUNT);
- ROBJECT(obj)->numiv = capa;
- for (size_t i = 0; i < capa; i++) {
- ROBJECT(obj)->as.ary[i] = Qundef;
- }
-#endif
- }
- else {
- rb_init_iv_list(obj);
+#if RUBY_DEBUG
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
+ VALUE *ptr = ROBJECT_IVPTR(obj);
+ for (size_t i = 0; i < ROBJECT_IV_CAPACITY(obj); i++) {
+ ptr[i] = Qundef;
}
+#endif
return obj;
}
@@ -2926,13 +2990,13 @@ rb_newobj_of(VALUE klass, VALUE flags)
return rb_class_instance_allocate_internal(klass, (flags | ROBJECT_EMBED) & ~FL_WB_PROTECTED, flags & FL_WB_PROTECTED);
}
else {
- return newobj_of(klass, flags & ~FL_WB_PROTECTED, 0, 0, 0, flags & FL_WB_PROTECTED, sizeof(RVALUE));
+ return newobj_of(klass, flags & ~FL_WB_PROTECTED, 0, 0, 0, flags & FL_WB_PROTECTED, RVALUE_SIZE);
}
}
#define UNEXPECTED_NODE(func) \
rb_bug(#func"(): GC does not handle T_NODE 0x%x(%p) 0x%"PRIxVALUE, \
- BUILTIN_TYPE(obj), (void*)(obj), RBASIC(obj)->flags)
+ BUILTIN_TYPE(obj), (void*)(obj), RBASIC(obj)->flags)
const char *
rb_imemo_name(enum imemo_type type)
@@ -2964,7 +3028,7 @@ rb_imemo_name(enum imemo_type type)
VALUE
rb_imemo_new(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0)
{
- size_t size = sizeof(RVALUE);
+ size_t size = RVALUE_SIZE;
VALUE flags = T_IMEMO | (type << FL_USHIFT);
return newobj_of(v0, flags, v1, v2, v3, TRUE, size);
}
@@ -2972,7 +3036,7 @@ rb_imemo_new(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0)
static VALUE
rb_imemo_tmpbuf_new(VALUE v1, VALUE v2, VALUE v3, VALUE v0)
{
- size_t size = sizeof(RVALUE);
+ size_t size = sizeof(struct rb_imemo_tmpbuf_struct);
VALUE flags = T_IMEMO | (imemo_tmpbuf << FL_USHIFT);
return newobj_of(v0, flags, v1, v2, v3, FALSE, size);
}
@@ -3033,7 +3097,7 @@ rb_imemo_new_debug(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0,
}
#endif
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_class_allocate_instance(VALUE klass)
{
return rb_class_instance_allocate_internal(klass, T_OBJECT | ROBJECT_EMBED, RGENGC_WB_PROTECTED_OBJECT);
@@ -3053,7 +3117,7 @@ rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FU
{
RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1);
if (klass) rb_data_object_check(klass);
- return newobj_of(klass, T_DATA, (VALUE)dmark, (VALUE)dfree, (VALUE)datap, FALSE, sizeof(RVALUE));
+ return newobj_of(klass, T_DATA, (VALUE)dmark, (VALUE)dfree, (VALUE)datap, FALSE, sizeof(struct RTypedData));
}
VALUE
@@ -3069,7 +3133,7 @@ rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type)
{
RBIMPL_NONNULL_ARG(type);
if (klass) rb_data_object_check(klass);
- return newobj_of(klass, T_DATA, (VALUE)type, (VALUE)1, (VALUE)datap, type->flags & RUBY_FL_WB_PROTECTED, sizeof(RVALUE));
+ return newobj_of(klass, T_DATA, (VALUE)type, (VALUE)1, (VALUE)datap, type->flags & RUBY_FL_WB_PROTECTED, sizeof(struct RTypedData));
}
VALUE
@@ -3084,11 +3148,11 @@ size_t
rb_objspace_data_type_memsize(VALUE obj)
{
if (RTYPEDDATA_P(obj)) {
- const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
- const void *ptr = RTYPEDDATA_DATA(obj);
- if (ptr && type->function.dsize) {
- return type->function.dsize(ptr);
- }
+ const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
+ const void *ptr = RTYPEDDATA_DATA(obj);
+ if (ptr && type->function.dsize) {
+ return type->function.dsize(ptr);
+ }
}
return 0;
}
@@ -3097,10 +3161,10 @@ const char *
rb_objspace_data_type_name(VALUE obj)
{
if (RTYPEDDATA_P(obj)) {
- return RTYPEDDATA_TYPE(obj)->wrap_struct_name;
+ return RTYPEDDATA_TYPE(obj)->wrap_struct_name;
}
else {
- return 0;
+ return 0;
}
}
@@ -3188,20 +3252,6 @@ rb_free_const_table(struct rb_id_table *tbl)
rb_id_table_free(tbl);
}
-static int
-free_iv_index_tbl_free_i(st_data_t key, st_data_t value, st_data_t data)
-{
- xfree((void *)value);
- return ST_CONTINUE;
-}
-
-static void
-iv_index_tbl_free(struct st_table *tbl)
-{
- st_foreach(tbl, free_iv_index_tbl_free_i, 0);
- st_free_table(tbl);
-}
-
// alive: if false, target pointers can be freed already.
// To check it, we need objspace parameter.
static void
@@ -3382,15 +3432,15 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
case T_FIXNUM:
case T_TRUE:
case T_FALSE:
- rb_bug("obj_free() called for broken object");
- break;
+ rb_bug("obj_free() called for broken object");
+ break;
default:
break;
}
if (FL_TEST(obj, FL_EXIVAR)) {
- rb_free_generic_ivar((VALUE)obj);
- FL_UNSET(obj, FL_EXIVAR);
+ rb_free_generic_ivar((VALUE)obj);
+ FL_UNSET(obj, FL_EXIVAR);
}
if (FL_TEST(obj, FL_SEEN_OBJ_ID) && !FL_TEST(obj, FL_FINALIZE)) {
@@ -3401,16 +3451,20 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
#if RGENGC_CHECK_MODE
#define CHECK(x) if (x(obj) != FALSE) rb_bug("obj_free: " #x "(%s) != FALSE", obj_info(obj))
- CHECK(RVALUE_WB_UNPROTECTED);
- CHECK(RVALUE_MARKED);
- CHECK(RVALUE_MARKING);
- CHECK(RVALUE_UNCOLLECTIBLE);
+ CHECK(RVALUE_WB_UNPROTECTED);
+ CHECK(RVALUE_MARKED);
+ CHECK(RVALUE_MARKING);
+ CHECK(RVALUE_UNCOLLECTIBLE);
#undef CHECK
#endif
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
- if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) {
+ if (rb_shape_obj_too_complex(obj)) {
+ RB_DEBUG_COUNTER_INC(obj_obj_too_complex);
+ st_free_table(ROBJECT_IV_HASH(obj));
+ }
+ else if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) {
RB_DEBUG_COUNTER_INC(obj_obj_embed);
}
else if (ROBJ_TRANSIENT_P(obj)) {
@@ -3423,45 +3477,39 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
break;
case T_MODULE:
case T_CLASS:
- rb_id_table_free(RCLASS_M_TBL(obj));
+ rb_id_table_free(RCLASS_M_TBL(obj));
cc_table_free(objspace, obj, FALSE);
- if (RCLASS_IV_TBL(obj)) {
- st_free_table(RCLASS_IV_TBL(obj));
- }
- if (RCLASS_CONST_TBL(obj)) {
- rb_free_const_table(RCLASS_CONST_TBL(obj));
- }
- if (RCLASS_IV_INDEX_TBL(obj)) {
- iv_index_tbl_free(RCLASS_IV_INDEX_TBL(obj));
- }
- if (RCLASS_CVC_TBL(obj)) {
+ if (RCLASS_IVPTR(obj)) {
+ xfree(RCLASS_IVPTR(obj));
+ }
+ if (RCLASS_CONST_TBL(obj)) {
+ rb_free_const_table(RCLASS_CONST_TBL(obj));
+ }
+ if (RCLASS_CVC_TBL(obj)) {
rb_id_table_foreach_values(RCLASS_CVC_TBL(obj), cvar_table_free_i, NULL);
rb_id_table_free(RCLASS_CVC_TBL(obj));
- }
+ }
rb_class_remove_subclass_head(obj);
- rb_class_remove_from_module_subclasses(obj);
- rb_class_remove_from_super_subclasses(obj);
+ rb_class_remove_from_module_subclasses(obj);
+ rb_class_remove_from_super_subclasses(obj);
if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) {
xfree(RCLASS_SUPERCLASSES(obj));
}
-#if SIZEOF_SERIAL_T != SIZEOF_VALUE && USE_RVARGC
- xfree(RCLASS(obj)->class_serial_ptr);
-#endif
-#if !USE_RVARGC
- if (RCLASS_EXT(obj))
+#if SIZE_POOL_COUNT == 1
+ if (RCLASS_EXT(obj))
xfree(RCLASS_EXT(obj));
#endif
(void)RB_DEBUG_COUNTER_INC_IF(obj_module_ptr, BUILTIN_TYPE(obj) == T_MODULE);
(void)RB_DEBUG_COUNTER_INC_IF(obj_class_ptr, BUILTIN_TYPE(obj) == T_CLASS);
- break;
+ break;
case T_STRING:
- rb_str_free(obj);
- break;
+ rb_str_free(obj);
+ break;
case T_ARRAY:
rb_ary_free(obj);
- break;
+ break;
case T_HASH:
#if USE_DEBUG_COUNTER
switch (RHASH_SIZE(obj)) {
@@ -3519,53 +3567,53 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
GC_ASSERT(RHASH_ST_TABLE_P(obj));
st_free_table(RHASH(obj)->as.st);
}
- break;
+ break;
case T_REGEXP:
- if (RANY(obj)->as.regexp.ptr) {
- onig_free(RANY(obj)->as.regexp.ptr);
+ if (RANY(obj)->as.regexp.ptr) {
+ onig_free(RANY(obj)->as.regexp.ptr);
RB_DEBUG_COUNTER_INC(obj_regexp_ptr);
- }
- break;
+ }
+ break;
case T_DATA:
- if (DATA_PTR(obj)) {
- int free_immediately = FALSE;
- void (*dfree)(void *);
- void *data = DATA_PTR(obj);
-
- if (RTYPEDDATA_P(obj)) {
- free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
- dfree = RANY(obj)->as.typeddata.type->function.dfree;
- if (0 && free_immediately == 0) {
- /* to expose non-free-immediate T_DATA */
- fprintf(stderr, "not immediate -> %s\n", RANY(obj)->as.typeddata.type->wrap_struct_name);
- }
- }
- else {
- dfree = RANY(obj)->as.data.dfree;
- }
-
- if (dfree) {
- if (dfree == RUBY_DEFAULT_FREE) {
- xfree(data);
+ if (DATA_PTR(obj)) {
+ int free_immediately = FALSE;
+ void (*dfree)(void *);
+ void *data = DATA_PTR(obj);
+
+ if (RTYPEDDATA_P(obj)) {
+ free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
+ dfree = RANY(obj)->as.typeddata.type->function.dfree;
+ if (0 && free_immediately == 0) {
+ /* to expose non-free-immediate T_DATA */
+ fprintf(stderr, "not immediate -> %s\n", RANY(obj)->as.typeddata.type->wrap_struct_name);
+ }
+ }
+ else {
+ dfree = RANY(obj)->as.data.dfree;
+ }
+
+ if (dfree) {
+ if (dfree == RUBY_DEFAULT_FREE) {
+ xfree(data);
RB_DEBUG_COUNTER_INC(obj_data_xfree);
- }
- else if (free_immediately) {
- (*dfree)(data);
+ }
+ else if (free_immediately) {
+ (*dfree)(data);
RB_DEBUG_COUNTER_INC(obj_data_imm_free);
- }
- else {
- make_zombie(objspace, obj, dfree, data);
+ }
+ else {
+ make_zombie(objspace, obj, dfree, data);
RB_DEBUG_COUNTER_INC(obj_data_zombie);
- return FALSE;
- }
- }
+ return FALSE;
+ }
+ }
else {
RB_DEBUG_COUNTER_INC(obj_data_empty);
}
- }
- break;
+ }
+ break;
case T_MATCH:
- if (RANY(obj)->as.match.rmatch) {
+ if (RANY(obj)->as.match.rmatch) {
struct rmatch *rm = RANY(obj)->as.match.rmatch;
#if USE_DEBUG_COUNTER
if (rm->regs.num_regs >= 8) {
@@ -3578,21 +3626,21 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
RB_DEBUG_COUNTER_INC(obj_match_under4);
}
#endif
- onig_region_free(&rm->regs, 0);
+ onig_region_free(&rm->regs, 0);
if (rm->char_offset)
- xfree(rm->char_offset);
- xfree(rm);
+ xfree(rm->char_offset);
+ xfree(rm);
RB_DEBUG_COUNTER_INC(obj_match_ptr);
- }
- break;
+ }
+ break;
case T_FILE:
- if (RANY(obj)->as.file.fptr) {
- make_io_zombie(objspace, obj);
+ if (RANY(obj)->as.file.fptr) {
+ make_io_zombie(objspace, obj);
RB_DEBUG_COUNTER_INC(obj_file_ptr);
- return FALSE;
- }
- break;
+ return FALSE;
+ }
+ break;
case T_RATIONAL:
RB_DEBUG_COUNTER_INC(obj_rational);
break;
@@ -3600,44 +3648,44 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
RB_DEBUG_COUNTER_INC(obj_complex);
break;
case T_MOVED:
- break;
+ break;
case T_ICLASS:
- /* Basically , T_ICLASS shares table with the module */
+ /* Basically , T_ICLASS shares table with the module */
if (RICLASS_OWNS_M_TBL_P(obj)) {
/* Method table is not shared for origin iclasses of classes */
rb_id_table_free(RCLASS_M_TBL(obj));
}
- if (RCLASS_CALLABLE_M_TBL(obj) != NULL) {
- rb_id_table_free(RCLASS_CALLABLE_M_TBL(obj));
- }
+ if (RCLASS_CALLABLE_M_TBL(obj) != NULL) {
+ rb_id_table_free(RCLASS_CALLABLE_M_TBL(obj));
+ }
rb_class_remove_subclass_head(obj);
cc_table_free(objspace, obj, FALSE);
- rb_class_remove_from_module_subclasses(obj);
- rb_class_remove_from_super_subclasses(obj);
-#if !USE_RVARGC
+ rb_class_remove_from_module_subclasses(obj);
+ rb_class_remove_from_super_subclasses(obj);
+#if !RCLASS_EXT_EMBEDDED
xfree(RCLASS_EXT(obj));
#endif
RB_DEBUG_COUNTER_INC(obj_iclass_ptr);
- break;
+ break;
case T_FLOAT:
RB_DEBUG_COUNTER_INC(obj_float);
- break;
+ break;
case T_BIGNUM:
- if (!BIGNUM_EMBED_P(obj) && BIGNUM_DIGITS(obj)) {
- xfree(BIGNUM_DIGITS(obj));
+ if (!BIGNUM_EMBED_P(obj) && BIGNUM_DIGITS(obj)) {
+ xfree(BIGNUM_DIGITS(obj));
RB_DEBUG_COUNTER_INC(obj_bignum_ptr);
- }
+ }
else {
RB_DEBUG_COUNTER_INC(obj_bignum_embed);
}
- break;
+ break;
case T_NODE:
- UNEXPECTED_NODE(obj_free);
- break;
+ UNEXPECTED_NODE(obj_free);
+ break;
case T_STRUCT:
if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) ||
@@ -3650,39 +3698,39 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
else {
xfree((void *)RANY(obj)->as.rstruct.as.heap.ptr);
RB_DEBUG_COUNTER_INC(obj_struct_ptr);
- }
- break;
+ }
+ break;
case T_SYMBOL:
- {
+ {
rb_gc_free_dsymbol(obj);
RB_DEBUG_COUNTER_INC(obj_symbol);
- }
- break;
+ }
+ break;
case T_IMEMO:
- switch (imemo_type(obj)) {
- case imemo_ment:
- rb_free_method_entry(&RANY(obj)->as.imemo.ment);
+ switch (imemo_type(obj)) {
+ case imemo_ment:
+ rb_free_method_entry(&RANY(obj)->as.imemo.ment);
RB_DEBUG_COUNTER_INC(obj_imemo_ment);
- break;
- case imemo_iseq:
- rb_iseq_free(&RANY(obj)->as.imemo.iseq);
+ break;
+ case imemo_iseq:
+ rb_iseq_free(&RANY(obj)->as.imemo.iseq);
RB_DEBUG_COUNTER_INC(obj_imemo_iseq);
- break;
- case imemo_env:
- GC_ASSERT(VM_ENV_ESCAPED_P(RANY(obj)->as.imemo.env.ep));
- xfree((VALUE *)RANY(obj)->as.imemo.env.env);
+ break;
+ case imemo_env:
+ GC_ASSERT(VM_ENV_ESCAPED_P(RANY(obj)->as.imemo.env.ep));
+ xfree((VALUE *)RANY(obj)->as.imemo.env.env);
RB_DEBUG_COUNTER_INC(obj_imemo_env);
- break;
- case imemo_tmpbuf:
- xfree(RANY(obj)->as.imemo.alloc.ptr);
+ break;
+ case imemo_tmpbuf:
+ xfree(RANY(obj)->as.imemo.alloc.ptr);
RB_DEBUG_COUNTER_INC(obj_imemo_tmpbuf);
- break;
- case imemo_ast:
- rb_ast_free(&RANY(obj)->as.imemo.ast);
+ break;
+ case imemo_ast:
+ rb_ast_free(&RANY(obj)->as.imemo.ast);
RB_DEBUG_COUNTER_INC(obj_imemo_ast);
- break;
+ break;
case imemo_cref:
RB_DEBUG_COUNTER_INC(obj_imemo_cref);
break;
@@ -3710,20 +3758,20 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
case imemo_constcache:
RB_DEBUG_COUNTER_INC(obj_imemo_constcache);
break;
- }
- return TRUE;
+ }
+ return TRUE;
default:
- rb_bug("gc_sweep(): unknown data type 0x%x(%p) 0x%"PRIxVALUE,
- BUILTIN_TYPE(obj), (void*)obj, RBASIC(obj)->flags);
+ rb_bug("gc_sweep(): unknown data type 0x%x(%p) 0x%"PRIxVALUE,
+ BUILTIN_TYPE(obj), (void*)obj, RBASIC(obj)->flags);
}
if (FL_TEST(obj, FL_FINALIZE)) {
make_zombie(objspace, obj, 0, 0);
- return FALSE;
+ return FALSE;
}
else {
- return TRUE;
+ return TRUE;
}
}
@@ -3886,7 +3934,8 @@ objspace_each_objects_try(VALUE arg)
uintptr_t pstart = (uintptr_t)page->start;
uintptr_t pend = pstart + (page->total_slots * size_pool->slot_size);
- if ((*data->callback)((void *)pstart, (void *)pend, size_pool->slot_size, data->data)) {
+ if (!__asan_region_is_poisoned((void *)pstart, pend - pstart) &&
+ (*data->callback)((void *)pstart, (void *)pend, size_pool->slot_size, data->data)) {
break;
}
@@ -3985,25 +4034,25 @@ internal_object_p(VALUE obj)
if (used_p) {
switch (BUILTIN_TYPE(obj)) {
- case T_NODE:
- UNEXPECTED_NODE(internal_object_p);
- break;
- case T_NONE:
+ case T_NODE:
+ UNEXPECTED_NODE(internal_object_p);
+ break;
+ case T_NONE:
case T_MOVED:
- case T_IMEMO:
- case T_ICLASS:
- case T_ZOMBIE:
- break;
- case T_CLASS:
- if (!p->as.basic.klass) break;
- if (FL_TEST(obj, FL_SINGLETON)) {
- return rb_singleton_class_internal_p(obj);
- }
- return 0;
- default:
- if (!p->as.basic.klass) break;
- return 0;
- }
+ case T_IMEMO:
+ case T_ICLASS:
+ case T_ZOMBIE:
+ break;
+ case T_CLASS:
+ if (!p->as.basic.klass) break;
+ if (FL_TEST(obj, FL_SINGLETON)) {
+ return rb_singleton_class_internal_p(obj);
+ }
+ return 0;
+ default:
+ if (!p->as.basic.klass) break;
+ return 0;
+ }
}
if (ptr || ! used_p) {
asan_poison_object(obj);
@@ -4024,14 +4073,14 @@ os_obj_of_i(void *vstart, void *vend, size_t stride, void *data)
VALUE v = (VALUE)vstart;
for (; v != (VALUE)vend; v += stride) {
- if (!internal_object_p(v)) {
- if (!oes->of || rb_obj_is_kind_of(v, oes->of)) {
+ if (!internal_object_p(v)) {
+ if (!oes->of || rb_obj_is_kind_of(v, oes->of)) {
if (!rb_multi_ractor_p() || rb_ractor_shareable_p(v)) {
rb_yield(v);
oes->num++;
}
- }
- }
+ }
+ }
}
return 0;
@@ -4123,8 +4172,8 @@ static void
should_be_callable(VALUE block)
{
if (!rb_obj_respond_to(block, idCall, TRUE)) {
- rb_raise(rb_eArgError, "wrong type argument %"PRIsVALUE" (should be callable)",
- rb_obj_class(block));
+ rb_raise(rb_eArgError, "wrong type argument %"PRIsVALUE" (should be callable)",
+ rb_obj_class(block));
}
}
@@ -4132,8 +4181,8 @@ static void
should_be_finalizable(VALUE obj)
{
if (!FL_ABLE(obj)) {
- rb_raise(rb_eArgError, "cannot define finalizer for %s",
- rb_obj_classname(obj));
+ rb_raise(rb_eArgError, "cannot define finalizer for %s",
+ rb_obj_classname(obj));
}
rb_check_frozen(obj);
}
@@ -4208,10 +4257,10 @@ define_final(int argc, VALUE *argv, VALUE os)
rb_scan_args(argc, argv, "11", &obj, &block);
should_be_finalizable(obj);
if (argc == 1) {
- block = rb_block_proc();
+ block = rb_block_proc();
}
else {
- should_be_callable(block);
+ should_be_callable(block);
}
if (rb_callable_receiver(block) == obj) {
@@ -4231,28 +4280,28 @@ define_final0(VALUE obj, VALUE block)
RBASIC(obj)->flags |= FL_FINALIZE;
if (st_lookup(finalizer_table, obj, &data)) {
- table = (VALUE)data;
+ table = (VALUE)data;
- /* avoid duplicate block, table is usually small */
- {
- long len = RARRAY_LEN(table);
- long i;
+ /* avoid duplicate block, table is usually small */
+ {
+ long len = RARRAY_LEN(table);
+ long i;
for (i = 0; i < len; i++) {
VALUE recv = RARRAY_AREF(table, i);
if (rb_equal(recv, block)) {
block = recv;
goto end;
- }
- }
- }
+ }
+ }
+ }
- rb_ary_push(table, block);
+ rb_ary_push(table, block);
}
else {
- table = rb_ary_new3(1, block);
- RBASIC_CLEAR_CLASS(table);
- st_add_direct(finalizer_table, obj, table);
+ table = rb_ary_new3(1, block);
+ RBASIC_CLEAR_CLASS(table);
+ st_add_direct(finalizer_table, obj, table);
}
end:
block = rb_ary_new3(2, INT2FIX(0), block);
@@ -4277,8 +4326,8 @@ rb_gc_copy_finalizer(VALUE dest, VALUE obj)
if (!FL_TEST(obj, FL_FINALIZE)) return;
if (st_lookup(finalizer_table, obj, &data)) {
- table = (VALUE)data;
- st_insert(finalizer_table, dest, table);
+ table = (VALUE)data;
+ st_insert(finalizer_table, dest, table);
}
FL_SET(dest, FL_FINALIZE);
}
@@ -4292,10 +4341,10 @@ run_single_final(VALUE cmd, VALUE objid)
static void
warn_exception_in_finalizer(rb_execution_context_t *ec, VALUE final)
{
- if (final != Qundef && !NIL_P(ruby_verbose)) {
- VALUE errinfo = ec->errinfo;
- rb_warn("Exception in finalizer %+"PRIsVALUE, final);
- rb_ec_error_print(ec, errinfo);
+ if (!UNDEF_P(final) && !NIL_P(ruby_verbose)) {
+ VALUE errinfo = ec->errinfo;
+ rb_warn("Exception in finalizer %+"PRIsVALUE, final);
+ rb_ec_error_print(ec, errinfo);
}
}
@@ -4305,33 +4354,37 @@ run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table)
long i;
enum ruby_tag_type state;
volatile struct {
- VALUE errinfo;
- VALUE objid;
- VALUE final;
- rb_control_frame_t *cfp;
- long finished;
+ VALUE errinfo;
+ VALUE objid;
+ VALUE final;
+ rb_control_frame_t *cfp;
+ VALUE *sp;
+ long finished;
} saved;
+
rb_execution_context_t * volatile ec = GET_EC();
#define RESTORE_FINALIZER() (\
- ec->cfp = saved.cfp, \
- ec->errinfo = saved.errinfo)
+ ec->cfp = saved.cfp, \
+ ec->cfp->sp = saved.sp, \
+ ec->errinfo = saved.errinfo)
saved.errinfo = ec->errinfo;
saved.objid = rb_obj_id(obj);
saved.cfp = ec->cfp;
+ saved.sp = ec->cfp->sp;
saved.finished = 0;
saved.final = Qundef;
EC_PUSH_TAG(ec);
state = EC_EXEC_TAG();
if (state != TAG_NONE) {
- ++saved.finished; /* skip failed finalizer */
- warn_exception_in_finalizer(ec, ATOMIC_VALUE_EXCHANGE(saved.final, Qundef));
+ ++saved.finished; /* skip failed finalizer */
+ warn_exception_in_finalizer(ec, ATOMIC_VALUE_EXCHANGE(saved.final, Qundef));
}
for (i = saved.finished;
- RESTORE_FINALIZER(), i<RARRAY_LEN(table);
- saved.finished = ++i) {
- run_single_final(saved.final = RARRAY_AREF(table, i), saved.objid);
+ RESTORE_FINALIZER(), i<RARRAY_LEN(table);
+ saved.finished = ++i) {
+ run_single_final(saved.final = RARRAY_AREF(table, i), saved.objid);
}
EC_POP_TAG();
#undef RESTORE_FINALIZER
@@ -4343,12 +4396,12 @@ run_final(rb_objspace_t *objspace, VALUE zombie)
st_data_t key, table;
if (RZOMBIE(zombie)->dfree) {
- RZOMBIE(zombie)->dfree(RZOMBIE(zombie)->data);
+ RZOMBIE(zombie)->dfree(RZOMBIE(zombie)->data);
}
key = (st_data_t)zombie;
if (st_delete(finalizer_table, &key, &table)) {
- run_finalizer(objspace, zombie, (VALUE)table);
+ run_finalizer(objspace, zombie, (VALUE)table);
}
}
@@ -4362,7 +4415,7 @@ finalize_list(rb_objspace_t *objspace, VALUE zombie)
next_zombie = RZOMBIE(zombie)->next;
page = GET_HEAP_PAGE(zombie);
- run_final(objspace, zombie);
+ run_final(objspace, zombie);
RB_VM_LOCK_ENTER();
{
@@ -4391,7 +4444,7 @@ finalize_deferred_heap_pages(rb_objspace_t *objspace)
{
VALUE zombie;
while ((zombie = ATOMIC_VALUE_EXCHANGE(heap_pages_deferred_final, 0)) != 0) {
- finalize_list(objspace, zombie);
+ finalize_list(objspace, zombie);
}
}
@@ -4418,7 +4471,7 @@ static void
gc_finalize_deferred_register(rb_objspace_t *objspace)
{
if (rb_postponed_job_register_one(0, gc_finalize_deferred, objspace) == 0) {
- rb_bug("gc_finalize_deferred_register: can't register finalizer.");
+ rb_bug("gc_finalize_deferred_register: can't register finalizer.");
}
}
@@ -4464,16 +4517,16 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
/* force to run finalizer */
while (finalizer_table->num_entries) {
- struct force_finalize_list *list = 0;
- st_foreach(finalizer_table, force_chain_object, (st_data_t)&list);
- while (list) {
- struct force_finalize_list *curr = list;
- st_data_t obj = (st_data_t)curr->obj;
- run_finalizer(objspace, curr->obj, curr->table);
- st_delete(finalizer_table, &obj, 0);
- list = curr->next;
- xfree(curr);
- }
+ struct force_finalize_list *list = 0;
+ st_foreach(finalizer_table, force_chain_object, (st_data_t)&list);
+ while (list) {
+ struct force_finalize_list *curr = list;
+ st_data_t obj = (st_data_t)curr->obj;
+ run_finalizer(objspace, curr->obj, curr->table);
+ st_delete(finalizer_table, &obj, 0);
+ list = curr->next;
+ xfree(curr);
+ }
}
/* prohibit GC because force T_DATA finalizers can break an object graph consistency */
@@ -4494,36 +4547,36 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
VALUE vp = (VALUE)p;
void *poisoned = asan_unpoison_object_temporary(vp);
switch (BUILTIN_TYPE(vp)) {
- case T_DATA:
- if (!DATA_PTR(p) || !RANY(p)->as.data.dfree) break;
+ case T_DATA:
+ if (!DATA_PTR(p) || !RANY(p)->as.data.dfree) break;
if (rb_obj_is_thread(vp)) break;
if (rb_obj_is_mutex(vp)) break;
if (rb_obj_is_fiber(vp)) break;
if (rb_obj_is_main_ractor(vp)) break;
if (RTYPEDDATA_P(vp)) {
- RDATA(p)->dfree = RANY(p)->as.typeddata.type->function.dfree;
- }
+ RDATA(p)->dfree = RANY(p)->as.typeddata.type->function.dfree;
+ }
RANY(p)->as.free.flags = 0;
- if (RANY(p)->as.data.dfree == RUBY_DEFAULT_FREE) {
- xfree(DATA_PTR(p));
- }
- else if (RANY(p)->as.data.dfree) {
+ if (RANY(p)->as.data.dfree == RUBY_DEFAULT_FREE) {
+ xfree(DATA_PTR(p));
+ }
+ else if (RANY(p)->as.data.dfree) {
make_zombie(objspace, vp, RANY(p)->as.data.dfree, RANY(p)->as.data.data);
- }
- break;
- case T_FILE:
- if (RANY(p)->as.file.fptr) {
+ }
+ break;
+ case T_FILE:
+ if (RANY(p)->as.file.fptr) {
make_io_zombie(objspace, vp);
- }
- break;
+ }
+ break;
default:
break;
- }
+ }
if (poisoned) {
GC_ASSERT(BUILTIN_TYPE(vp) == T_NONE);
asan_poison_object(vp);
}
- }
+ }
}
gc_exit(objspace, gc_enter_event_finalizer, &lock_lev);
@@ -4547,13 +4600,13 @@ static inline int
is_garbage_object(rb_objspace_t *objspace, VALUE ptr)
{
if (!is_lazy_sweeping(objspace) ||
- is_swept_object(objspace, ptr) ||
- MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(ptr), ptr)) {
+ is_swept_object(objspace, ptr) ||
+ MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(ptr), ptr)) {
- return FALSE;
+ return FALSE;
}
else {
- return TRUE;
+ return TRUE;
}
}
@@ -4564,16 +4617,16 @@ is_live_object(rb_objspace_t *objspace, VALUE ptr)
case T_NONE:
case T_MOVED:
case T_ZOMBIE:
- return FALSE;
+ return FALSE;
default:
break;
}
if (!is_garbage_object(objspace, ptr)) {
- return TRUE;
+ return TRUE;
}
else {
- return FALSE;
+ return FALSE;
}
}
@@ -4658,7 +4711,7 @@ id2ref(VALUE objid)
}
}
- if ((orig = id2ref_obj_tbl(objspace, objid)) != Qundef &&
+ if (!UNDEF_P(orig = id2ref_obj_tbl(objspace, objid)) &&
is_live_object(objspace, orig)) {
if (!rb_multi_ractor_p() || rb_ractor_shareable_p(orig)) {
@@ -4677,6 +4730,7 @@ id2ref(VALUE objid)
}
}
+/* :nodoc: */
static VALUE
os_id2ref(VALUE os, VALUE objid)
{
@@ -4829,139 +4883,134 @@ obj_memsize_of(VALUE obj, int use_all_types)
size_t size = 0;
if (SPECIAL_CONST_P(obj)) {
- return 0;
+ return 0;
}
if (FL_TEST(obj, FL_EXIVAR)) {
- size += rb_generic_ivar_memsize(obj);
+ size += rb_generic_ivar_memsize(obj);
}
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
- if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
- size += ROBJECT_NUMIV(obj) * sizeof(VALUE);
- }
- break;
+ if (rb_shape_obj_too_complex(obj)) {
+ size += rb_st_memsize(ROBJECT_IV_HASH(obj));
+ }
+ else if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
+ size += ROBJECT_IV_CAPACITY(obj) * sizeof(VALUE);
+ }
+ break;
case T_MODULE:
case T_CLASS:
- if (RCLASS_EXT(obj)) {
+ if (RCLASS_EXT(obj)) {
if (RCLASS_M_TBL(obj)) {
size += rb_id_table_memsize(RCLASS_M_TBL(obj));
}
- if (RCLASS_IV_TBL(obj)) {
- size += st_memsize(RCLASS_IV_TBL(obj));
- }
- if (RCLASS_CVC_TBL(obj)) {
- size += rb_id_table_memsize(RCLASS_CVC_TBL(obj));
- }
- if (RCLASS_IV_INDEX_TBL(obj)) {
- // TODO: more correct value
- size += st_memsize(RCLASS_IV_INDEX_TBL(obj));
- }
- if (RCLASS_EXT(obj)->iv_tbl) {
- size += st_memsize(RCLASS_EXT(obj)->iv_tbl);
- }
+ // class IV sizes are allocated as powers of two
+ size += SIZEOF_VALUE << bit_length(RCLASS_IV_COUNT(obj));
+ if (RCLASS_CVC_TBL(obj)) {
+ size += rb_id_table_memsize(RCLASS_CVC_TBL(obj));
+ }
if (RCLASS_EXT(obj)->const_tbl) {
size += rb_id_table_memsize(RCLASS_EXT(obj)->const_tbl);
- }
+ }
if (RCLASS_CC_TBL(obj)) {
size += cc_table_memsize(RCLASS_CC_TBL(obj));
}
if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) {
size += (RCLASS_SUPERCLASS_DEPTH(obj) + 1) * sizeof(VALUE);
}
-#if !USE_RVARGC
- size += sizeof(rb_classext_t);
+#if SIZE_POOL_COUNT == 1
+ size += sizeof(rb_classext_t);
#endif
- }
- break;
+ }
+ break;
case T_ICLASS:
if (RICLASS_OWNS_M_TBL_P(obj)) {
- if (RCLASS_M_TBL(obj)) {
- size += rb_id_table_memsize(RCLASS_M_TBL(obj));
- }
- }
+ if (RCLASS_M_TBL(obj)) {
+ size += rb_id_table_memsize(RCLASS_M_TBL(obj));
+ }
+ }
if (RCLASS_EXT(obj) && RCLASS_CC_TBL(obj)) {
size += cc_table_memsize(RCLASS_CC_TBL(obj));
}
- break;
+ break;
case T_STRING:
- size += rb_str_memsize(obj);
- break;
+ size += rb_str_memsize(obj);
+ break;
case T_ARRAY:
- size += rb_ary_memsize(obj);
- break;
+ size += rb_ary_memsize(obj);
+ break;
case T_HASH:
if (RHASH_AR_TABLE_P(obj)) {
if (RHASH_AR_TABLE(obj) != NULL) {
size_t rb_hash_ar_table_size(void);
size += rb_hash_ar_table_size();
}
- }
+ }
else {
VM_ASSERT(RHASH_ST_TABLE(obj) != NULL);
size += st_memsize(RHASH_ST_TABLE(obj));
}
- break;
+ break;
case T_REGEXP:
- if (RREGEXP_PTR(obj)) {
- size += onig_memsize(RREGEXP_PTR(obj));
- }
- break;
+ if (RREGEXP_PTR(obj)) {
+ size += onig_memsize(RREGEXP_PTR(obj));
+ }
+ break;
case T_DATA:
- if (use_all_types) size += rb_objspace_data_type_memsize(obj);
- break;
+ if (use_all_types) size += rb_objspace_data_type_memsize(obj);
+ break;
case T_MATCH:
- if (RMATCH(obj)->rmatch) {
+ if (RMATCH(obj)->rmatch) {
struct rmatch *rm = RMATCH(obj)->rmatch;
- size += onig_region_memsize(&rm->regs);
- size += sizeof(struct rmatch_offset) * rm->char_offset_num_allocated;
- size += sizeof(struct rmatch);
- }
- break;
+ size += onig_region_memsize(&rm->regs);
+ size += sizeof(struct rmatch_offset) * rm->char_offset_num_allocated;
+ size += sizeof(struct rmatch);
+ }
+ break;
case T_FILE:
- if (RFILE(obj)->fptr) {
- size += rb_io_memsize(RFILE(obj)->fptr);
- }
- break;
+ if (RFILE(obj)->fptr) {
+ size += rb_io_memsize(RFILE(obj)->fptr);
+ }
+ break;
case T_RATIONAL:
case T_COMPLEX:
break;
case T_IMEMO:
size += imemo_memsize(obj);
- break;
+ break;
case T_FLOAT:
case T_SYMBOL:
- break;
+ break;
case T_BIGNUM:
- if (!(RBASIC(obj)->flags & BIGNUM_EMBED_FLAG) && BIGNUM_DIGITS(obj)) {
- size += BIGNUM_LEN(obj) * sizeof(BDIGIT);
- }
- break;
+ if (!(RBASIC(obj)->flags & BIGNUM_EMBED_FLAG) && BIGNUM_DIGITS(obj)) {
+ size += BIGNUM_LEN(obj) * sizeof(BDIGIT);
+ }
+ break;
case T_NODE:
- UNEXPECTED_NODE(obj_memsize_of);
- break;
+ UNEXPECTED_NODE(obj_memsize_of);
+ break;
case T_STRUCT:
- if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 &&
- RSTRUCT(obj)->as.heap.ptr) {
- size += sizeof(VALUE) * RSTRUCT_LEN(obj);
- }
- break;
+ if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 &&
+ RSTRUCT(obj)->as.heap.ptr) {
+ size += sizeof(VALUE) * RSTRUCT_LEN(obj);
+ }
+ break;
case T_ZOMBIE:
case T_MOVED:
- break;
+ break;
default:
- rb_bug("objspace/memsize_of(): unknown data type 0x%x(%p)",
- BUILTIN_TYPE(obj), (void*)obj);
+ rb_bug("objspace/memsize_of(): unknown data type 0x%x(%p)",
+ BUILTIN_TYPE(obj), (void*)obj);
}
- return size + GET_HEAP_PAGE(obj)->slot_size;
+ return size + rb_gc_obj_slot_size(obj);
}
size_t
@@ -5072,7 +5121,7 @@ count_objects(int argc, VALUE *argv, VALUE os)
}
for (i = 0; i < heap_allocated_pages; i++) {
- struct heap_page *page = heap_pages_sorted[i];
+ struct heap_page *page = heap_pages_sorted[i];
short stride = page->slot_size;
uintptr_t p = (uintptr_t)page->start;
@@ -5084,16 +5133,16 @@ count_objects(int argc, VALUE *argv, VALUE os)
void *poisoned = asan_unpoison_object_temporary(vp);
if (RANY(p)->as.basic.flags) {
counts[BUILTIN_TYPE(vp)]++;
- }
- else {
- freed++;
- }
+ }
+ else {
+ freed++;
+ }
if (poisoned) {
GC_ASSERT(BUILTIN_TYPE(vp) == T_NONE);
asan_poison_object(vp);
}
- }
- total += page->total_slots;
+ }
+ total += page->total_slots;
}
if (NIL_P(hash)) {
@@ -5193,6 +5242,8 @@ unlock_page_body(rb_objspace_t *objspace, struct heap_page_body *body)
static bool
try_move(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *free_page, VALUE src)
{
+ GC_ASSERT(gc_is_moveable_obj(objspace, src));
+
struct heap_page *src_page = GET_HEAP_PAGE(src);
if (!free_page) {
return false;
@@ -5201,33 +5252,33 @@ try_move(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *free_page,
/* We should return true if either src is successfully moved, or src is
* unmoveable. A false return will cause the sweeping cursor to be
* incremented to the next page, and src will attempt to move again */
- if (gc_is_moveable_obj(objspace, src)) {
- GC_ASSERT(MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(src), src));
-
- VALUE dest = (VALUE)free_page->freelist;
- asan_unpoison_object(dest, false);
- if (!dest) {
- /* if we can't get something from the freelist then the page must be
- * full */
- return false;
- }
- free_page->freelist = RANY(dest)->as.free.next;
-
- GC_ASSERT(RB_BUILTIN_TYPE(dest) == T_NONE);
+ GC_ASSERT(MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(src), src));
+
+ asan_unlock_freelist(free_page);
+ VALUE dest = (VALUE)free_page->freelist;
+ asan_lock_freelist(free_page);
+ asan_unpoison_object(dest, false);
+ if (!dest) {
+ /* if we can't get something from the freelist then the page must be
+ * full */
+ return false;
+ }
+ free_page->freelist = RANY(dest)->as.free.next;
- if (src_page->slot_size > free_page->slot_size) {
- objspace->rcompactor.moved_down_count_table[BUILTIN_TYPE(src)]++;
- }
- else if (free_page->slot_size > src_page->slot_size) {
- objspace->rcompactor.moved_up_count_table[BUILTIN_TYPE(src)]++;
- }
- objspace->rcompactor.moved_count_table[BUILTIN_TYPE(src)]++;
- objspace->rcompactor.total_moved++;
+ GC_ASSERT(RB_BUILTIN_TYPE(dest) == T_NONE);
- gc_move(objspace, src, dest, src_page->slot_size, free_page->slot_size);
- gc_pin(objspace, src);
- free_page->free_slots--;
+ if (src_page->slot_size > free_page->slot_size) {
+ objspace->rcompactor.moved_down_count_table[BUILTIN_TYPE(src)]++;
+ }
+ else if (free_page->slot_size > src_page->slot_size) {
+ objspace->rcompactor.moved_up_count_table[BUILTIN_TYPE(src)]++;
}
+ objspace->rcompactor.moved_count_table[BUILTIN_TYPE(src)]++;
+ objspace->rcompactor.total_moved++;
+
+ gc_move(objspace, src, dest, src_page->slot_size, free_page->slot_size);
+ gc_pin(objspace, src);
+ free_page->free_slots--;
return true;
}
@@ -5279,7 +5330,7 @@ read_barrier_handler(uintptr_t original_address)
/* If the page_body is NULL, then mprotect cannot handle it and will crash
* with "Cannot allocate memory". */
if (page_body == NULL) {
- rb_bug("read_barrier_handler: segmentation fault at 0x%lx", original_address);
+ rb_bug("read_barrier_handler: segmentation fault at %p", (void *)original_address);
}
RB_VM_LOCK_ENTER();
@@ -5637,15 +5688,15 @@ gc_sweep_page(rb_objspace_t *objspace, rb_heap_t *heap, struct gc_sweep_context
#if GC_PROFILE_MORE_DETAIL
if (gc_prof_enabled(objspace)) {
- gc_profile_record *record = gc_prof_record(objspace);
- record->removing_objects += ctx->final_slots + ctx->freed_slots;
- record->empty_objects += ctx->empty_slots;
+ gc_profile_record *record = gc_prof_record(objspace);
+ record->removing_objects += ctx->final_slots + ctx->freed_slots;
+ record->empty_objects += ctx->empty_slots;
}
#endif
if (0) fprintf(stderr, "gc_sweep_page(%"PRIdSIZE"): total_slots: %d, freed_slots: %d, empty_slots: %d, final_slots: %d\n",
- rb_gc_count(),
- sweep_page->total_slots,
- ctx->freed_slots, ctx->empty_slots, ctx->final_slots);
+ rb_gc_count(),
+ sweep_page->total_slots,
+ ctx->freed_slots, ctx->empty_slots, ctx->final_slots);
sweep_page->free_slots += ctx->freed_slots + ctx->empty_slots;
objspace->profile.total_freed_objects += ctx->freed_slots;
@@ -5653,19 +5704,19 @@ gc_sweep_page(rb_objspace_t *objspace, rb_heap_t *heap, struct gc_sweep_context
if (heap_pages_deferred_final && !finalizing) {
rb_thread_t *th = GET_THREAD();
if (th) {
- gc_finalize_deferred_register(objspace);
+ gc_finalize_deferred_register(objspace);
}
}
#if RGENGC_CHECK_MODE
short freelist_len = 0;
- asan_unpoison_memory_region(&sweep_page->freelist, sizeof(RVALUE*), false);
+ asan_unlock_freelist(sweep_page);
RVALUE *ptr = sweep_page->freelist;
while (ptr) {
freelist_len++;
ptr = ptr->as.free.next;
}
- asan_poison_memory_region(&sweep_page->freelist, sizeof(RVALUE*));
+ asan_lock_freelist(sweep_page);
if (freelist_len != sweep_page->free_slots) {
rb_bug("inconsistent freelist length: expected %d but was %d", sweep_page->free_slots, freelist_len);
}
@@ -5723,7 +5774,7 @@ static void
heap_page_freelist_append(struct heap_page *page, RVALUE *freelist)
{
if (freelist) {
- asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
+ asan_unlock_freelist(page);
if (page->freelist) {
RVALUE *p = page->freelist;
asan_unpoison_object((VALUE)p, false);
@@ -5739,7 +5790,7 @@ heap_page_freelist_append(struct heap_page *page, RVALUE *freelist)
else {
page->freelist = freelist;
}
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
+ asan_lock_freelist(page);
}
}
@@ -5804,10 +5855,29 @@ gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
size_t swept_slots = size_pool->freed_slots + size_pool->empty_slots;
size_t min_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_min_ratio);
+
+ /* If we don't have enough slots and we have pages on the tomb heap, move
+ * pages from the tomb heap to the eden heap. This may prevent page
+ * creation thrashing (frequently allocating and deallocting pages) and
+ * GC thrashing (running GC more frequently than required). */
+ struct heap_page *resurrected_page;
+ while ((swept_slots < min_free_slots || swept_slots < gc_params.heap_init_slots) &&
+ (resurrected_page = heap_page_resurrect(objspace, size_pool))) {
+ swept_slots += resurrected_page->free_slots;
+
+ heap_add_page(objspace, size_pool, heap, resurrected_page);
+ heap_add_freepage(heap, resurrected_page);
+ }
+
/* Some size pools may have very few pages (or even no pages). These size pools
* should still have allocatable pages. */
- if (min_free_slots < gc_params.heap_init_slots) {
- min_free_slots = gc_params.heap_init_slots;
+ if (min_free_slots < gc_params.heap_init_slots && swept_slots < gc_params.heap_init_slots) {
+ int multiple = size_pool->slot_size / BASE_SLOT_SIZE;
+ size_t extra_slots = gc_params.heap_init_slots - swept_slots;
+ size_t extend_page_count = CEILDIV(extra_slots * multiple, HEAP_PAGE_OBJ_LIMIT);
+ if (extend_page_count > size_pool->allocatable_pages) {
+ size_pool_allocatable_pages_set(objspace, size_pool, extend_page_count);
+ }
}
if (swept_slots < min_free_slots) {
@@ -5923,41 +5993,41 @@ gc_sweep_step(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *hea
heap->sweeping_page = ccan_list_next(&heap->pages, sweep_page, page_node);
- if (sweep_page->final_slots + free_slots == sweep_page->total_slots &&
- heap_pages_freeable_pages > 0 &&
- unlink_limit > 0) {
- heap_pages_freeable_pages--;
- unlink_limit--;
- /* there are no living objects -> move this page to tomb heap */
- heap_unlink_page(objspace, heap, sweep_page);
- heap_add_page(objspace, size_pool, SIZE_POOL_TOMB_HEAP(size_pool), sweep_page);
- }
- else if (free_slots > 0) {
+ if (sweep_page->final_slots + free_slots == sweep_page->total_slots &&
+ heap_pages_freeable_pages > 0 &&
+ unlink_limit > 0) {
+ heap_pages_freeable_pages--;
+ unlink_limit--;
+ /* there are no living objects -> move this page to tomb heap */
+ heap_unlink_page(objspace, heap, sweep_page);
+ heap_add_page(objspace, size_pool, SIZE_POOL_TOMB_HEAP(size_pool), sweep_page);
+ }
+ else if (free_slots > 0) {
#if USE_RVARGC
size_pool->freed_slots += ctx.freed_slots;
size_pool->empty_slots += ctx.empty_slots;
#endif
#if GC_ENABLE_INCREMENTAL_MARK
- if (need_pool) {
+ if (need_pool) {
heap_add_poolpage(objspace, heap, sweep_page);
need_pool = FALSE;
- }
- else {
+ }
+ else {
heap_add_freepage(heap, sweep_page);
swept_slots += free_slots;
if (swept_slots > GC_INCREMENTAL_SWEEP_SLOT_COUNT) {
break;
}
- }
+ }
#else
heap_add_freepage(heap, sweep_page);
break;
#endif
- }
- else {
- sweep_page->free_next = NULL;
- }
+ }
+ else {
+ sweep_page->free_next = NULL;
+ }
} while ((sweep_page = heap->sweeping_page));
if (!heap->sweeping_page) {
@@ -6037,9 +6107,19 @@ invalidate_moved_plane(rb_objspace_t *objspace, struct heap_page *page, uintptr_
object = rb_gc_location(forwarding_object);
+ shape_id_t original_shape_id = 0;
+ if (RB_TYPE_P(object, T_OBJECT)) {
+ original_shape_id = RMOVED(forwarding_object)->original_shape_id;
+ }
+
gc_move(objspace, object, forwarding_object, GET_HEAP_PAGE(object)->slot_size, page->slot_size);
/* forwarding_object is now our actual object, and "object"
* is the free slot for the original page */
+
+ if (original_shape_id) {
+ ROBJECT_SET_SHAPE_ID(forwarding_object, original_shape_id);
+ }
+
struct heap_page *orig_page = GET_HEAP_PAGE(object);
orig_page->free_slots++;
heap_page_add_freeobj(objspace, orig_page, object);
@@ -6129,11 +6209,11 @@ gc_sweep(rb_objspace_t *objspace)
if (immediate_sweep) {
#if !GC_ENABLE_LAZY_SWEEP
- gc_prof_sweep_timer_start(objspace);
+ gc_prof_sweep_timer_start(objspace);
#endif
- gc_sweep_rest(objspace);
+ gc_sweep_rest(objspace);
#if !GC_ENABLE_LAZY_SWEEP
- gc_prof_sweep_timer_stop(objspace);
+ gc_prof_sweep_timer_stop(objspace);
#endif
}
else {
@@ -6178,8 +6258,8 @@ mark_stack_size(mark_stack_t *stack)
stack_chunk_t *chunk = stack->chunk ? stack->chunk->next : NULL;
while (chunk) {
- size += stack->limit;
- chunk = chunk->next;
+ size += stack->limit;
+ chunk = chunk->next;
}
return size;
}
@@ -6304,11 +6384,11 @@ push_mark_stack(mark_stack_t *stack, VALUE data)
case T_ZOMBIE:
case T_UNDEF:
case T_MASK:
- rb_bug("push_mark_stack() called for broken object");
- break;
+ rb_bug("push_mark_stack() called for broken object");
+ break;
case T_NODE:
- UNEXPECTED_NODE(push_mark_stack);
+ UNEXPECTED_NODE(push_mark_stack);
break;
}
@@ -6328,7 +6408,7 @@ pop_mark_stack(mark_stack_t *stack, VALUE *data)
pop_mark_stack_chunk(stack);
}
else {
- *data = stack->chunk->data[--stack->index];
+ *data = stack->chunk->data[--stack->index];
}
return TRUE;
}
@@ -6361,7 +6441,7 @@ init_mark_stack(mark_stack_t *stack)
# define STACK_LENGTH (size_t)(STACK_END - STACK_START + 1)
#else
# define STACK_LENGTH ((STACK_END < STACK_START) ? (size_t)(STACK_START - STACK_END) \
- : (size_t)(STACK_END - STACK_START + 1))
+ : (size_t)(STACK_END - STACK_START + 1))
#endif
#if !STACK_GROW_DIRECTION
int ruby_stack_grow_direction;
@@ -6430,7 +6510,7 @@ each_location(rb_objspace_t *objspace, register const VALUE *x, register long n,
while (n--) {
v = *x;
cb(objspace, v);
- x++;
+ x++;
}
}
@@ -6629,7 +6709,7 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
gc_mark(objspace, me->defined_class);
if (def) {
- switch (def->type) {
+ switch (def->type) {
case VM_METHOD_TYPE_ISEQ:
if (def->body.iseq.iseqptr) gc_mark(objspace, (VALUE)def->body.iseq.iseqptr);
gc_mark(objspace, (VALUE)def->body.iseq.cref);
@@ -6640,29 +6720,29 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
gc_mark_and_pin(objspace, (VALUE)me);
}
break;
- case VM_METHOD_TYPE_ATTRSET:
- case VM_METHOD_TYPE_IVAR:
- gc_mark(objspace, def->body.attr.location);
- break;
- case VM_METHOD_TYPE_BMETHOD:
+ case VM_METHOD_TYPE_ATTRSET:
+ case VM_METHOD_TYPE_IVAR:
+ gc_mark(objspace, def->body.attr.location);
+ break;
+ case VM_METHOD_TYPE_BMETHOD:
gc_mark(objspace, def->body.bmethod.proc);
if (def->body.bmethod.hooks) rb_hook_list_mark(def->body.bmethod.hooks);
- break;
- case VM_METHOD_TYPE_ALIAS:
- gc_mark(objspace, (VALUE)def->body.alias.original_me);
- return;
- case VM_METHOD_TYPE_REFINED:
- gc_mark(objspace, (VALUE)def->body.refined.orig_me);
- gc_mark(objspace, (VALUE)def->body.refined.owner);
- break;
- case VM_METHOD_TYPE_CFUNC:
- case VM_METHOD_TYPE_ZSUPER:
- case VM_METHOD_TYPE_MISSING:
- case VM_METHOD_TYPE_OPTIMIZED:
- case VM_METHOD_TYPE_UNDEF:
- case VM_METHOD_TYPE_NOTIMPLEMENTED:
- break;
- }
+ break;
+ case VM_METHOD_TYPE_ALIAS:
+ gc_mark(objspace, (VALUE)def->body.alias.original_me);
+ return;
+ case VM_METHOD_TYPE_REFINED:
+ gc_mark(objspace, (VALUE)def->body.refined.orig_me);
+ gc_mark(objspace, (VALUE)def->body.refined.owner);
+ break;
+ case VM_METHOD_TYPE_CFUNC:
+ case VM_METHOD_TYPE_ZSUPER:
+ case VM_METHOD_TYPE_MISSING:
+ case VM_METHOD_TYPE_OPTIMIZED:
+ case VM_METHOD_TYPE_UNDEF:
+ case VM_METHOD_TYPE_NOTIMPLEMENTED:
+ break;
+ }
}
}
@@ -6679,7 +6759,7 @@ static void
mark_m_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl)
{
if (tbl) {
- rb_id_table_foreach_values(tbl, mark_method_entry_i, objspace);
+ rb_id_table_foreach_values(tbl, mark_method_entry_i, objspace);
}
}
@@ -6742,8 +6822,10 @@ mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec
static void
mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec)
{
- rb_wasm_scan_stack(rb_mark_locations);
- each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], gc_mark_maybe);
+ VALUE *stack_start, *stack_end;
+ SET_STACK_END;
+ GET_STACK_BOUNDS(stack_start, stack_end, 1);
+ each_stack_location(objspace, ec, stack_start, stack_end, gc_mark_maybe);
rb_wasm_scan_locals(rb_mark_locations);
each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], gc_mark_maybe);
@@ -6757,8 +6839,8 @@ static void
mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec)
{
union {
- rb_jmp_buf j;
- VALUE v[sizeof(rb_jmp_buf) / (sizeof(VALUE))];
+ rb_jmp_buf j;
+ VALUE v[sizeof(rb_jmp_buf) / (sizeof(VALUE))];
} save_regs_gc_mark;
VALUE *stack_start, *stack_end;
@@ -6797,15 +6879,15 @@ rb_gc_mark_machine_stack(const rb_execution_context_t *ec)
static void
each_stack_location(rb_objspace_t *objspace, const rb_execution_context_t *ec,
- const VALUE *stack_start, const VALUE *stack_end, void (*cb)(rb_objspace_t *, VALUE))
+ const VALUE *stack_start, const VALUE *stack_end, void (*cb)(rb_objspace_t *, VALUE))
{
gc_mark_locations(objspace, stack_start, stack_end, cb);
#if defined(__mc68000__)
gc_mark_locations(objspace,
- (VALUE*)((char*)stack_start + 2),
- (VALUE*)((char*)stack_end - 2), cb);
+ (VALUE*)((char*)stack_start + 2),
+ (VALUE*)((char*)stack_end - 2), cb);
#endif
}
@@ -6868,20 +6950,20 @@ gc_remember_unprotected(rb_objspace_t *objspace, VALUE obj)
bits_t *uncollectible_bits = &page->uncollectible_bits[0];
if (!MARKED_IN_BITMAP(uncollectible_bits, obj)) {
- page->flags.has_uncollectible_shady_objects = TRUE;
- MARK_IN_BITMAP(uncollectible_bits, obj);
- objspace->rgengc.uncollectible_wb_unprotected_objects++;
+ page->flags.has_uncollectible_shady_objects = TRUE;
+ MARK_IN_BITMAP(uncollectible_bits, obj);
+ objspace->rgengc.uncollectible_wb_unprotected_objects++;
#if RGENGC_PROFILE > 0
- objspace->profile.total_remembered_shady_object_count++;
+ objspace->profile.total_remembered_shady_object_count++;
#if RGENGC_PROFILE >= 2
- objspace->profile.remembered_shady_object_count_types[BUILTIN_TYPE(obj)]++;
+ objspace->profile.remembered_shady_object_count_types[BUILTIN_TYPE(obj)]++;
#endif
#endif
- return TRUE;
+ return TRUE;
}
else {
- return FALSE;
+ return FALSE;
}
}
@@ -6891,32 +6973,32 @@ rgengc_check_relation(rb_objspace_t *objspace, VALUE obj)
const VALUE old_parent = objspace->rgengc.parent_object;
if (old_parent) { /* parent object is old */
- if (RVALUE_WB_UNPROTECTED(obj)) {
- if (gc_remember_unprotected(objspace, obj)) {
- gc_report(2, objspace, "relation: (O->S) %s -> %s\n", obj_info(old_parent), obj_info(obj));
- }
- }
- else {
- if (!RVALUE_OLD_P(obj)) {
- if (RVALUE_MARKED(obj)) {
- /* An object pointed from an OLD object should be OLD. */
- gc_report(2, objspace, "relation: (O->unmarked Y) %s -> %s\n", obj_info(old_parent), obj_info(obj));
- RVALUE_AGE_SET_OLD(objspace, obj);
- if (is_incremental_marking(objspace)) {
- if (!RVALUE_MARKING(obj)) {
- gc_grey(objspace, obj);
- }
- }
- else {
- rgengc_remember(objspace, obj);
- }
- }
- else {
- gc_report(2, objspace, "relation: (O->Y) %s -> %s\n", obj_info(old_parent), obj_info(obj));
- RVALUE_AGE_SET_CANDIDATE(objspace, obj);
- }
- }
- }
+ if (RVALUE_WB_UNPROTECTED(obj)) {
+ if (gc_remember_unprotected(objspace, obj)) {
+ gc_report(2, objspace, "relation: (O->S) %s -> %s\n", obj_info(old_parent), obj_info(obj));
+ }
+ }
+ else {
+ if (!RVALUE_OLD_P(obj)) {
+ if (RVALUE_MARKED(obj)) {
+ /* An object pointed from an OLD object should be OLD. */
+ gc_report(2, objspace, "relation: (O->unmarked Y) %s -> %s\n", obj_info(old_parent), obj_info(obj));
+ RVALUE_AGE_SET_OLD(objspace, obj);
+ if (is_incremental_marking(objspace)) {
+ if (!RVALUE_MARKING(obj)) {
+ gc_grey(objspace, obj);
+ }
+ }
+ else {
+ rgengc_remember(objspace, obj);
+ }
+ }
+ else {
+ gc_report(2, objspace, "relation: (O->Y) %s -> %s\n", obj_info(old_parent), obj_info(obj));
+ RVALUE_AGE_SET_CANDIDATE(objspace, obj);
+ }
+ }
+ }
}
GC_ASSERT(old_parent == objspace->rgengc.parent_object);
@@ -6932,7 +7014,7 @@ gc_grey(rb_objspace_t *objspace, VALUE obj)
#if GC_ENABLE_INCREMENTAL_MARK
if (is_incremental_marking(objspace)) {
- MARK_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj);
+ MARK_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj);
}
#endif
@@ -6948,14 +7030,14 @@ gc_aging(rb_objspace_t *objspace, VALUE obj)
check_rvalue_consistency(obj);
if (!RVALUE_PAGE_WB_UNPROTECTED(page, obj)) {
- if (!RVALUE_OLD_P(obj)) {
- gc_report(3, objspace, "gc_aging: YOUNG: %s\n", obj_info(obj));
- RVALUE_AGE_INC(objspace, obj);
- }
- else if (is_full_marking(objspace)) {
- GC_ASSERT(RVALUE_PAGE_UNCOLLECTIBLE(page, obj) == FALSE);
- RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(objspace, page, obj);
- }
+ if (!RVALUE_OLD_P(obj)) {
+ gc_report(3, objspace, "gc_aging: YOUNG: %s\n", obj_info(obj));
+ RVALUE_AGE_INC(objspace, obj);
+ }
+ else if (is_full_marking(objspace)) {
+ GC_ASSERT(RVALUE_PAGE_UNCOLLECTIBLE(page, obj) == FALSE);
+ RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(objspace, page, obj);
+ }
}
check_rvalue_consistency(obj);
@@ -6969,8 +7051,8 @@ static void
gc_mark_ptr(rb_objspace_t *objspace, VALUE obj)
{
if (LIKELY(during_gc)) {
- rgengc_check_relation(objspace, obj);
- if (!gc_mark_set(objspace, obj)) return; /* already marked */
+ rgengc_check_relation(objspace, obj);
+ if (!gc_mark_set(objspace, obj)) return; /* already marked */
if (0) { // for debug GC marking miss
if (objspace->rgengc.parent_object) {
@@ -6987,8 +7069,8 @@ gc_mark_ptr(rb_objspace_t *objspace, VALUE obj)
rp(obj);
rb_bug("try to mark T_NONE object"); /* check here will help debugging */
}
- gc_aging(objspace, obj);
- gc_grey(objspace, obj);
+ gc_aging(objspace, obj);
+ gc_grey(objspace, obj);
}
else {
reachable_objects_from_callback(obj);
@@ -7047,10 +7129,10 @@ static inline void
gc_mark_set_parent(rb_objspace_t *objspace, VALUE obj)
{
if (RVALUE_OLD_P(obj)) {
- objspace->rgengc.parent_object = obj;
+ objspace->rgengc.parent_object = obj;
}
else {
- objspace->rgengc.parent_object = Qfalse;
+ objspace->rgengc.parent_object = Qfalse;
}
}
@@ -7059,8 +7141,8 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj)
{
switch (imemo_type(obj)) {
case imemo_env:
- {
- const rb_env_t *env = (const rb_env_t *)obj;
+ {
+ const rb_env_t *env = (const rb_env_t *)obj;
if (LIKELY(env->ep)) {
// just after newobj() can be NULL here.
@@ -7071,50 +7153,50 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj)
gc_mark(objspace, (VALUE)rb_vm_env_prev_env(env));
gc_mark(objspace, (VALUE)env->iseq);
}
- }
- return;
+ }
+ return;
case imemo_cref:
- gc_mark(objspace, RANY(obj)->as.imemo.cref.klass_or_self);
- gc_mark(objspace, (VALUE)RANY(obj)->as.imemo.cref.next);
- gc_mark(objspace, RANY(obj)->as.imemo.cref.refinements);
- return;
+ gc_mark(objspace, RANY(obj)->as.imemo.cref.klass_or_self);
+ gc_mark(objspace, (VALUE)RANY(obj)->as.imemo.cref.next);
+ gc_mark(objspace, RANY(obj)->as.imemo.cref.refinements);
+ return;
case imemo_svar:
- gc_mark(objspace, RANY(obj)->as.imemo.svar.cref_or_me);
- gc_mark(objspace, RANY(obj)->as.imemo.svar.lastline);
- gc_mark(objspace, RANY(obj)->as.imemo.svar.backref);
- gc_mark(objspace, RANY(obj)->as.imemo.svar.others);
- return;
+ gc_mark(objspace, RANY(obj)->as.imemo.svar.cref_or_me);
+ gc_mark(objspace, RANY(obj)->as.imemo.svar.lastline);
+ gc_mark(objspace, RANY(obj)->as.imemo.svar.backref);
+ gc_mark(objspace, RANY(obj)->as.imemo.svar.others);
+ return;
case imemo_throw_data:
- gc_mark(objspace, RANY(obj)->as.imemo.throw_data.throw_obj);
- return;
+ gc_mark(objspace, RANY(obj)->as.imemo.throw_data.throw_obj);
+ return;
case imemo_ifunc:
- gc_mark_maybe(objspace, (VALUE)RANY(obj)->as.imemo.ifunc.data);
- return;
+ gc_mark_maybe(objspace, (VALUE)RANY(obj)->as.imemo.ifunc.data);
+ return;
case imemo_memo:
- gc_mark(objspace, RANY(obj)->as.imemo.memo.v1);
- gc_mark(objspace, RANY(obj)->as.imemo.memo.v2);
- gc_mark_maybe(objspace, RANY(obj)->as.imemo.memo.u3.value);
- return;
+ gc_mark(objspace, RANY(obj)->as.imemo.memo.v1);
+ gc_mark(objspace, RANY(obj)->as.imemo.memo.v2);
+ gc_mark_maybe(objspace, RANY(obj)->as.imemo.memo.u3.value);
+ return;
case imemo_ment:
- mark_method_entry(objspace, &RANY(obj)->as.imemo.ment);
- return;
+ mark_method_entry(objspace, &RANY(obj)->as.imemo.ment);
+ return;
case imemo_iseq:
- rb_iseq_mark((rb_iseq_t *)obj);
- return;
+ rb_iseq_mark((rb_iseq_t *)obj);
+ return;
case imemo_tmpbuf:
- {
- const rb_imemo_tmpbuf_t *m = &RANY(obj)->as.imemo.alloc;
- do {
- rb_gc_mark_locations(m->ptr, m->ptr + m->cnt);
- } while ((m = m->next) != NULL);
- }
- return;
+ {
+ const rb_imemo_tmpbuf_t *m = &RANY(obj)->as.imemo.alloc;
+ do {
+ rb_gc_mark_locations(m->ptr, m->ptr + m->cnt);
+ } while ((m = m->next) != NULL);
+ }
+ return;
case imemo_ast:
- rb_ast_mark(&RANY(obj)->as.imemo.ast);
- return;
+ rb_ast_mark(&RANY(obj)->as.imemo.ast);
+ return;
case imemo_parser_strterm:
- rb_strterm_mark(obj);
- return;
+ rb_strterm_mark(obj);
+ return;
case imemo_callinfo:
return;
case imemo_callcache:
@@ -7132,11 +7214,13 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj)
return;
#if VM_CHECK_MODE > 0
default:
- VM_UNREACHABLE(gc_mark_imemo);
+ VM_UNREACHABLE(gc_mark_imemo);
#endif
}
}
+static void mark_cvc_tbl(rb_objspace_t *objspace, VALUE klass);
+
static void
gc_mark_children(rb_objspace_t *objspace, VALUE obj)
{
@@ -7144,7 +7228,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
gc_mark_set_parent(objspace, obj);
if (FL_TEST(obj, FL_EXIVAR)) {
- rb_mark_generic_ivar(obj);
+ rb_mark_generic_ivar(obj);
}
switch (BUILTIN_TYPE(obj)) {
@@ -7157,16 +7241,16 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
case T_NIL:
case T_FIXNUM:
- rb_bug("rb_gc_mark() called for broken object");
- break;
+ rb_bug("rb_gc_mark() called for broken object");
+ break;
case T_NODE:
- UNEXPECTED_NODE(rb_gc_mark);
- break;
+ UNEXPECTED_NODE(rb_gc_mark);
+ break;
case T_IMEMO:
- gc_mark_imemo(objspace, obj);
- return;
+ gc_mark_imemo(objspace, obj);
+ return;
default:
break;
@@ -7180,41 +7264,44 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
if (RCLASS_SUPER(obj)) {
gc_mark(objspace, RCLASS_SUPER(obj));
}
- if (!RCLASS_EXT(obj)) break;
+ if (!RCLASS_EXT(obj)) break;
mark_m_tbl(objspace, RCLASS_M_TBL(obj));
+ mark_cvc_tbl(objspace, obj);
cc_table_mark(objspace, obj);
- mark_tbl_no_pin(objspace, RCLASS_IV_TBL(obj));
- mark_const_tbl(objspace, RCLASS_CONST_TBL(obj));
- break;
+ for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) {
+ gc_mark(objspace, RCLASS_IVPTR(obj)[i]);
+ }
+ mark_const_tbl(objspace, RCLASS_CONST_TBL(obj));
+ break;
case T_ICLASS:
if (RICLASS_OWNS_M_TBL_P(obj)) {
- mark_m_tbl(objspace, RCLASS_M_TBL(obj));
- }
+ mark_m_tbl(objspace, RCLASS_M_TBL(obj));
+ }
if (RCLASS_SUPER(obj)) {
gc_mark(objspace, RCLASS_SUPER(obj));
}
- if (!RCLASS_EXT(obj)) break;
+ if (!RCLASS_EXT(obj)) break;
if (RCLASS_INCLUDER(obj)) {
gc_mark(objspace, RCLASS_INCLUDER(obj));
}
- mark_m_tbl(objspace, RCLASS_CALLABLE_M_TBL(obj));
+ mark_m_tbl(objspace, RCLASS_CALLABLE_M_TBL(obj));
cc_table_mark(objspace, obj);
- break;
+ break;
case T_ARRAY:
if (ARY_SHARED_P(obj)) {
VALUE root = ARY_SHARED_ROOT(obj);
gc_mark(objspace, root);
}
- else {
- long i, len = RARRAY_LEN(obj);
+ else {
+ long i, len = RARRAY_LEN(obj);
const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(obj);
- for (i=0; i < len; i++) {
+ for (i=0; i < len; i++) {
gc_mark(objspace, ptr[i]);
- }
+ }
if (LIKELY(during_gc)) {
if (!ARY_EMBED_P(obj) && RARRAY_TRANSIENT_P(obj)) {
@@ -7222,45 +7309,60 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
}
}
}
- break;
+ break;
case T_HASH:
mark_hash(objspace, obj);
- break;
+ break;
case T_STRING:
- if (STR_SHARED_P(obj)) {
- gc_mark(objspace, any->as.string.as.heap.aux.shared);
- }
- break;
+ if (STR_SHARED_P(obj)) {
+ gc_mark(objspace, any->as.string.as.heap.aux.shared);
+ }
+ break;
case T_DATA:
- {
- void *const ptr = DATA_PTR(obj);
- if (ptr) {
- RUBY_DATA_FUNC mark_func = RTYPEDDATA_P(obj) ?
- any->as.typeddata.type->function.dmark :
- any->as.data.dmark;
- if (mark_func) (*mark_func)(ptr);
- }
- }
- break;
+ {
+ void *const ptr = DATA_PTR(obj);
+ if (ptr) {
+ RUBY_DATA_FUNC mark_func = RTYPEDDATA_P(obj) ?
+ any->as.typeddata.type->function.dmark :
+ any->as.data.dmark;
+ if (mark_func) (*mark_func)(ptr);
+ }
+ }
+ break;
case T_OBJECT:
{
- const VALUE * const ptr = ROBJECT_IVPTR(obj);
+ rb_shape_t *shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj));
+ if (rb_shape_obj_too_complex(obj)) {
+ mark_tbl_no_pin(objspace, ROBJECT_IV_HASH(obj));
+ }
+ else {
+ const VALUE * const ptr = ROBJECT_IVPTR(obj);
- uint32_t i, len = ROBJECT_NUMIV(obj);
- for (i = 0; i < len; i++) {
- gc_mark(objspace, ptr[i]);
+ uint32_t i, len = ROBJECT_IV_COUNT(obj);
+ for (i = 0; i < len; i++) {
+ gc_mark(objspace, ptr[i]);
+ }
+
+ if (LIKELY(during_gc) &&
+ ROBJ_TRANSIENT_P(obj)) {
+ rb_transient_heap_mark(obj, ptr);
+ }
}
+ if (shape) {
+ VALUE klass = RBASIC_CLASS(obj);
- if (LIKELY(during_gc) &&
- ROBJ_TRANSIENT_P(obj)) {
- rb_transient_heap_mark(obj, ptr);
+ // Increment max_iv_count if applicable, used to determine size pool allocation
+ uint32_t num_of_ivs = shape->next_iv_index;
+ if (RCLASS_EXT(klass)->max_iv_count < num_of_ivs) {
+ RCLASS_EXT(klass)->max_iv_count = num_of_ivs;
+ }
}
}
- break;
+ break;
case T_FILE:
if (any->as.file.fptr) {
@@ -7271,32 +7373,33 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
gc_mark(objspace, any->as.file.fptr->writeconv_pre_ecopts);
gc_mark(objspace, any->as.file.fptr->encs.ecopts);
gc_mark(objspace, any->as.file.fptr->write_lock);
+ gc_mark(objspace, any->as.file.fptr->timeout);
}
break;
case T_REGEXP:
gc_mark(objspace, any->as.regexp.src);
- break;
+ break;
case T_MATCH:
- gc_mark(objspace, any->as.match.regexp);
- if (any->as.match.str) {
- gc_mark(objspace, any->as.match.str);
- }
- break;
+ gc_mark(objspace, any->as.match.regexp);
+ if (any->as.match.str) {
+ gc_mark(objspace, any->as.match.str);
+ }
+ break;
case T_RATIONAL:
- gc_mark(objspace, any->as.rational.num);
- gc_mark(objspace, any->as.rational.den);
- break;
+ gc_mark(objspace, any->as.rational.num);
+ gc_mark(objspace, any->as.rational.den);
+ break;
case T_COMPLEX:
- gc_mark(objspace, any->as.complex.real);
- gc_mark(objspace, any->as.complex.imag);
- break;
+ gc_mark(objspace, any->as.complex.real);
+ gc_mark(objspace, any->as.complex.imag);
+ break;
case T_STRUCT:
- {
+ {
long i;
const long len = RSTRUCT_LEN(obj);
const VALUE * const ptr = RSTRUCT_CONST_PTR(obj);
@@ -7309,19 +7412,19 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
RSTRUCT_TRANSIENT_P(obj)) {
rb_transient_heap_mark(obj, ptr);
}
- }
- break;
+ }
+ break;
default:
#if GC_DEBUG
- rb_gcdebug_print_obj_condition((VALUE)obj);
+ rb_gcdebug_print_obj_condition((VALUE)obj);
#endif
if (BUILTIN_TYPE(obj) == T_MOVED) rb_bug("rb_gc_mark(): %p is T_MOVED", (void *)obj);
- if (BUILTIN_TYPE(obj) == T_NONE) rb_bug("rb_gc_mark(): %p is T_NONE", (void *)obj);
- if (BUILTIN_TYPE(obj) == T_ZOMBIE) rb_bug("rb_gc_mark(): %p is T_ZOMBIE", (void *)obj);
- rb_bug("rb_gc_mark(): unknown data type 0x%x(%p) %s",
- BUILTIN_TYPE(obj), (void *)any,
- is_pointer_to_heap(objspace, any) ? "corrupted object" : "non object");
+ if (BUILTIN_TYPE(obj) == T_NONE) rb_bug("rb_gc_mark(): %p is T_NONE", (void *)obj);
+ if (BUILTIN_TYPE(obj) == T_ZOMBIE) rb_bug("rb_gc_mark(): %p is T_ZOMBIE", (void *)obj);
+ rb_bug("rb_gc_mark(): unknown data type 0x%x(%p) %s",
+ BUILTIN_TYPE(obj), (void *)any,
+ is_pointer_to_heap(objspace, any) ? "corrupted object" : "non object");
}
}
@@ -7340,39 +7443,39 @@ gc_mark_stacked_objects(rb_objspace_t *objspace, int incremental, size_t count)
#endif
while (pop_mark_stack(mstack, &obj)) {
- if (obj == Qundef) continue; /* skip */
+ if (UNDEF_P(obj)) continue; /* skip */
- if (RGENGC_CHECK_MODE && !RVALUE_MARKED(obj)) {
- rb_bug("gc_mark_stacked_objects: %s is not marked.", obj_info(obj));
- }
+ if (RGENGC_CHECK_MODE && !RVALUE_MARKED(obj)) {
+ rb_bug("gc_mark_stacked_objects: %s is not marked.", obj_info(obj));
+ }
gc_mark_children(objspace, obj);
#if GC_ENABLE_INCREMENTAL_MARK
- if (incremental) {
- if (RGENGC_CHECK_MODE && !RVALUE_MARKING(obj)) {
- rb_bug("gc_mark_stacked_objects: incremental, but marking bit is 0");
- }
- CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj);
- popped_count++;
+ if (incremental) {
+ if (RGENGC_CHECK_MODE && !RVALUE_MARKING(obj)) {
+ rb_bug("gc_mark_stacked_objects: incremental, but marking bit is 0");
+ }
+ CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj);
+ popped_count++;
- if (popped_count + (objspace->marked_slots - marked_slots_at_the_beginning) > count) {
- break;
- }
- }
- else {
- /* just ignore marking bits */
- }
+ if (popped_count + (objspace->marked_slots - marked_slots_at_the_beginning) > count) {
+ break;
+ }
+ }
+ else {
+ /* just ignore marking bits */
+ }
#endif
}
if (RGENGC_CHECK_MODE >= 3) gc_verify_internal_consistency(objspace);
if (is_mark_stack_empty(mstack)) {
- shrink_stack_chunk_cache(mstack);
- return TRUE;
+ shrink_stack_chunk_cache(mstack);
+ return TRUE;
}
else {
- return FALSE;
+ return FALSE;
}
}
@@ -7399,13 +7502,13 @@ show_mark_ticks(void)
int i;
fprintf(stderr, "mark ticks result:\n");
for (i=0; i<MAX_TICKS; i++) {
- const char *category = mark_ticks_categories[i];
- if (category) {
- fprintf(stderr, "%s\t%8lu\n", category, (unsigned long)mark_ticks[i]);
- }
- else {
- break;
- }
+ const char *category = mark_ticks_categories[i];
+ if (category) {
+ fprintf(stderr, "%s\t%8lu\n", category, (unsigned long)mark_ticks[i]);
+ }
+ else {
+ break;
+ }
}
}
@@ -7424,7 +7527,7 @@ gc_mark_roots(rb_objspace_t *objspace, const char **categoryp)
const char *prev_category = 0;
if (mark_ticks_categories[0] == 0) {
- atexit(show_mark_ticks);
+ atexit(show_mark_ticks);
}
#endif
@@ -7435,10 +7538,10 @@ gc_mark_roots(rb_objspace_t *objspace, const char **categoryp)
#if PRINT_ROOT_TICKS
#define MARK_CHECKPOINT_PRINT_TICK(category) do { \
if (prev_category) { \
- tick_t t = tick(); \
- mark_ticks[tick_count] = t - start_tick; \
- mark_ticks_categories[tick_count] = prev_category; \
- tick_count++; \
+ tick_t t = tick(); \
+ mark_ticks[tick_count] = t - start_tick; \
+ mark_ticks_categories[tick_count] = prev_category; \
+ tick_count++; \
} \
prev_category = category; \
start_tick = tick(); \
@@ -7519,8 +7622,8 @@ static void
reflist_add(struct reflist *refs, VALUE obj)
{
if (refs->pos == refs->size) {
- refs->size *= 2;
- SIZED_REALLOC_N(refs->list, VALUE, refs->size, refs->size/2);
+ refs->size *= 2;
+ SIZED_REALLOC_N(refs->list, VALUE, refs->size, refs->size/2);
}
refs->list[refs->pos++] = obj;
@@ -7531,14 +7634,14 @@ reflist_dump(struct reflist *refs)
{
int i;
for (i=0; i<refs->pos; i++) {
- VALUE obj = refs->list[i];
- if (IS_ROOTSIG(obj)) { /* root */
- fprintf(stderr, "<root@%s>", GET_ROOTSIG(obj));
- }
- else {
- fprintf(stderr, "<%s>", obj_info(obj));
- }
- if (i+1 < refs->pos) fprintf(stderr, ", ");
+ VALUE obj = refs->list[i];
+ if (IS_ROOTSIG(obj)) { /* root */
+ fprintf(stderr, "<root@%s>", GET_ROOTSIG(obj));
+ }
+ else {
+ fprintf(stderr, "<%s>", obj_info(obj));
+ }
+ if (i+1 < refs->pos) fprintf(stderr, ", ");
}
}
@@ -7547,8 +7650,8 @@ reflist_referred_from_machine_context(struct reflist *refs)
{
int i;
for (i=0; i<refs->pos; i++) {
- VALUE obj = refs->list[i];
- if (IS_ROOTSIG(obj) && strcmp(GET_ROOTSIG(obj), "machine_context") == 0) return 1;
+ VALUE obj = refs->list[i];
+ if (IS_ROOTSIG(obj) && strcmp(GET_ROOTSIG(obj), "machine_context") == 0) return 1;
}
return 0;
}
@@ -7576,13 +7679,13 @@ allrefs_add(struct allrefs *data, VALUE obj)
if (st_lookup(data->references, obj, &r)) {
refs = (struct reflist *)r;
- reflist_add(refs, data->root_obj);
- return 0;
+ reflist_add(refs, data->root_obj);
+ return 0;
}
else {
- refs = reflist_create(data->root_obj);
- st_insert(data->references, obj, (st_data_t)refs);
- return 1;
+ refs = reflist_create(data->root_obj);
+ st_insert(data->references, obj, (st_data_t)refs);
+ return 1;
}
}
@@ -7592,7 +7695,7 @@ allrefs_i(VALUE obj, void *ptr)
struct allrefs *data = (struct allrefs *)ptr;
if (allrefs_add(data, obj)) {
- push_mark_stack(&data->mark_stack, obj);
+ push_mark_stack(&data->mark_stack, obj);
}
}
@@ -7604,7 +7707,7 @@ allrefs_roots_i(VALUE obj, void *ptr)
data->root_obj = MAKE_ROOTSIG(data->category);
if (allrefs_add(data, obj)) {
- push_mark_stack(&data->mark_stack, obj);
+ push_mark_stack(&data->mark_stack, obj);
}
}
#define PUSH_MARK_FUNC_DATA(v) do { \
@@ -7637,7 +7740,7 @@ objspace_allrefs(rb_objspace_t *objspace)
/* traverse rest objects reachable from root objects */
while (pop_mark_stack(&data.mark_stack, &obj)) {
- rb_objspace_reachable_objects_from(data.root_obj = obj, allrefs_i, &data);
+ rb_objspace_reachable_objects_from(data.root_obj = obj, allrefs_i, &data);
}
free_stack_chunks(&data.mark_stack);
@@ -7690,18 +7793,18 @@ gc_check_after_marks_i(st_data_t k, st_data_t v, st_data_t ptr)
/* object should be marked or oldgen */
if (!MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(obj), obj)) {
- fprintf(stderr, "gc_check_after_marks_i: %s is not marked and not oldgen.\n", obj_info(obj));
- fprintf(stderr, "gc_check_after_marks_i: %p is referred from ", (void *)obj);
- reflist_dump(refs);
-
- if (reflist_referred_from_machine_context(refs)) {
- fprintf(stderr, " (marked from machine stack).\n");
- /* marked from machine context can be false positive */
- }
- else {
- objspace->rgengc.error_count++;
- fprintf(stderr, "\n");
- }
+ fprintf(stderr, "gc_check_after_marks_i: %s is not marked and not oldgen.\n", obj_info(obj));
+ fprintf(stderr, "gc_check_after_marks_i: %p is referred from ", (void *)obj);
+ reflist_dump(refs);
+
+ if (reflist_referred_from_machine_context(refs)) {
+ fprintf(stderr, " (marked from machine stack).\n");
+ /* marked from machine context can be false positive */
+ }
+ else {
+ objspace->rgengc.error_count++;
+ fprintf(stderr, "\n");
+ }
}
return ST_CONTINUE;
}
@@ -7718,14 +7821,14 @@ gc_marks_check(rb_objspace_t *objspace, st_foreach_callback_func *checker_func,
objspace->rgengc.allrefs_table = objspace_allrefs(objspace);
if (checker_func) {
- st_foreach(objspace->rgengc.allrefs_table, checker_func, (st_data_t)objspace);
+ st_foreach(objspace->rgengc.allrefs_table, checker_func, (st_data_t)objspace);
}
if (objspace->rgengc.error_count > 0) {
#if RGENGC_CHECK_MODE >= 5
- allrefs_dump(objspace);
+ allrefs_dump(objspace);
#endif
- if (checker_name) rb_bug("%s: GC has problem.", checker_name);
+ if (checker_name) rb_bug("%s: GC has problem.", checker_name);
}
objspace_allrefs_destruct(objspace->rgengc.allrefs_table);
@@ -7759,12 +7862,12 @@ check_generation_i(const VALUE child, void *ptr)
if (RGENGC_CHECK_MODE) GC_ASSERT(RVALUE_OLD_P(parent));
if (!RVALUE_OLD_P(child)) {
- if (!RVALUE_REMEMBERED(parent) &&
- !RVALUE_REMEMBERED(child) &&
- !RVALUE_UNCOLLECTIBLE(child)) {
- fprintf(stderr, "verify_internal_consistency_reachable_i: WB miss (O->Y) %s -> %s\n", obj_info(parent), obj_info(child));
- data->err_count++;
- }
+ if (!RVALUE_REMEMBERED(parent) &&
+ !RVALUE_REMEMBERED(child) &&
+ !RVALUE_UNCOLLECTIBLE(child)) {
+ fprintf(stderr, "verify_internal_consistency_reachable_i: WB miss (O->Y) %s -> %s\n", obj_info(parent), obj_info(child));
+ data->err_count++;
+ }
}
}
@@ -7775,9 +7878,9 @@ check_color_i(const VALUE child, void *ptr)
const VALUE parent = data->parent;
if (!RVALUE_WB_UNPROTECTED(parent) && RVALUE_WHITE_P(child)) {
- fprintf(stderr, "verify_internal_consistency_reachable_i: WB miss (B->W) - %s -> %s\n",
- obj_info(parent), obj_info(child));
- data->err_count++;
+ fprintf(stderr, "verify_internal_consistency_reachable_i: WB miss (B->W) - %s -> %s\n",
+ obj_info(parent), obj_info(child));
+ data->err_count++;
}
}
@@ -7804,9 +7907,9 @@ verify_internal_consistency_i(void *page_start, void *page_end, size_t stride,
for (obj = (VALUE)page_start; obj != (VALUE)page_end; obj += stride) {
void *poisoned = asan_unpoison_object_temporary(obj);
- if (is_live_object(objspace, obj)) {
- /* count objects */
- data->live_object_count++;
+ if (is_live_object(objspace, obj)) {
+ /* count objects */
+ data->live_object_count++;
data->parent = obj;
/* Normally, we don't expect T_MOVED objects to be in the heap.
@@ -7816,30 +7919,30 @@ verify_internal_consistency_i(void *page_start, void *page_end, size_t stride,
rb_objspace_reachable_objects_from(obj, check_children_i, (void *)data);
}
- /* check health of children */
- if (RVALUE_OLD_P(obj)) data->old_object_count++;
- if (RVALUE_WB_UNPROTECTED(obj) && RVALUE_UNCOLLECTIBLE(obj)) data->remembered_shady_count++;
-
- if (!is_marking(objspace) && RVALUE_OLD_P(obj)) {
- /* reachable objects from an oldgen object should be old or (young with remember) */
- data->parent = obj;
- rb_objspace_reachable_objects_from(obj, check_generation_i, (void *)data);
- }
-
- if (is_incremental_marking(objspace)) {
- if (RVALUE_BLACK_P(obj)) {
- /* reachable objects from black objects should be black or grey objects */
- data->parent = obj;
- rb_objspace_reachable_objects_from(obj, check_color_i, (void *)data);
- }
- }
- }
- else {
- if (BUILTIN_TYPE(obj) == T_ZOMBIE) {
- GC_ASSERT((RBASIC(obj)->flags & ~FL_SEEN_OBJ_ID) == T_ZOMBIE);
- data->zombie_object_count++;
- }
- }
+ /* check health of children */
+ if (RVALUE_OLD_P(obj)) data->old_object_count++;
+ if (RVALUE_WB_UNPROTECTED(obj) && RVALUE_UNCOLLECTIBLE(obj)) data->remembered_shady_count++;
+
+ if (!is_marking(objspace) && RVALUE_OLD_P(obj)) {
+ /* reachable objects from an oldgen object should be old or (young with remember) */
+ data->parent = obj;
+ rb_objspace_reachable_objects_from(obj, check_generation_i, (void *)data);
+ }
+
+ if (is_incremental_marking(objspace)) {
+ if (RVALUE_BLACK_P(obj)) {
+ /* reachable objects from black objects should be black or grey objects */
+ data->parent = obj;
+ rb_objspace_reachable_objects_from(obj, check_color_i, (void *)data);
+ }
+ }
+ }
+ else {
+ if (BUILTIN_TYPE(obj) == T_ZOMBIE) {
+ GC_ASSERT((RBASIC(obj)->flags & ~FL_SEEN_OBJ_ID) == T_ZOMBIE);
+ data->zombie_object_count++;
+ }
+ }
if (poisoned) {
GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE);
asan_poison_object(obj);
@@ -7867,15 +7970,15 @@ gc_verify_heap_page(rb_objspace_t *objspace, struct heap_page *page, VALUE obj)
void *poisoned = asan_unpoison_object_temporary(val);
enum ruby_value_type type = BUILTIN_TYPE(val);
- if (type == T_NONE) free_objects++;
- if (type == T_ZOMBIE) zombie_objects++;
- if (RVALUE_PAGE_UNCOLLECTIBLE(page, val) && RVALUE_PAGE_WB_UNPROTECTED(page, val)) {
- has_remembered_shady = TRUE;
- }
- if (RVALUE_PAGE_MARKING(page, val)) {
- has_remembered_old = TRUE;
- remembered_old_objects++;
- }
+ if (type == T_NONE) free_objects++;
+ if (type == T_ZOMBIE) zombie_objects++;
+ if (RVALUE_PAGE_UNCOLLECTIBLE(page, val) && RVALUE_PAGE_WB_UNPROTECTED(page, val)) {
+ has_remembered_shady = TRUE;
+ }
+ if (RVALUE_PAGE_MARKING(page, val)) {
+ has_remembered_old = TRUE;
+ remembered_old_objects++;
+ }
if (poisoned) {
GC_ASSERT(BUILTIN_TYPE(val) == T_NONE);
@@ -7884,31 +7987,31 @@ gc_verify_heap_page(rb_objspace_t *objspace, struct heap_page *page, VALUE obj)
}
if (!is_incremental_marking(objspace) &&
- page->flags.has_remembered_objects == FALSE && has_remembered_old == TRUE) {
+ page->flags.has_remembered_objects == FALSE && has_remembered_old == TRUE) {
for (uintptr_t ptr = start; ptr < end; ptr += slot_size) {
VALUE val = (VALUE)ptr;
- if (RVALUE_PAGE_MARKING(page, val)) {
- fprintf(stderr, "marking -> %s\n", obj_info(val));
- }
- }
- rb_bug("page %p's has_remembered_objects should be false, but there are remembered old objects (%d). %s",
- (void *)page, remembered_old_objects, obj ? obj_info(obj) : "");
+ if (RVALUE_PAGE_MARKING(page, val)) {
+ fprintf(stderr, "marking -> %s\n", obj_info(val));
+ }
+ }
+ rb_bug("page %p's has_remembered_objects should be false, but there are remembered old objects (%d). %s",
+ (void *)page, remembered_old_objects, obj ? obj_info(obj) : "");
}
if (page->flags.has_uncollectible_shady_objects == FALSE && has_remembered_shady == TRUE) {
- rb_bug("page %p's has_remembered_shady should be false, but there are remembered shady objects. %s",
- (void *)page, obj ? obj_info(obj) : "");
+ rb_bug("page %p's has_remembered_shady should be false, but there are remembered shady objects. %s",
+ (void *)page, obj ? obj_info(obj) : "");
}
if (0) {
- /* free_slots may not equal to free_objects */
- if (page->free_slots != free_objects) {
- rb_bug("page %p's free_slots should be %d, but %d\n", (void *)page, page->free_slots, free_objects);
- }
+ /* free_slots may not equal to free_objects */
+ if (page->free_slots != free_objects) {
+ rb_bug("page %p's free_slots should be %d, but %d\n", (void *)page, page->free_slots, free_objects);
+ }
}
if (page->final_slots != zombie_objects) {
- rb_bug("page %p's final_slots should be %d, but %d\n", (void *)page, page->final_slots, zombie_objects);
+ rb_bug("page %p's final_slots should be %d, but %d\n", (void *)page, page->final_slots, zombie_objects);
}
return remembered_old_objects;
@@ -7921,7 +8024,7 @@ gc_verify_heap_pages_(rb_objspace_t *objspace, struct ccan_list_head *head)
struct heap_page *page = 0;
ccan_list_for_each(head, page, page_node) {
- asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
+ asan_unlock_freelist(page);
RVALUE *p = page->freelist;
while (p) {
VALUE vp = (VALUE)p;
@@ -7933,11 +8036,11 @@ gc_verify_heap_pages_(rb_objspace_t *objspace, struct ccan_list_head *head)
p = p->as.free.next;
asan_poison_object(prev);
}
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
+ asan_lock_freelist(page);
- if (page->flags.has_remembered_objects == FALSE) {
- remembered_old_objects += gc_verify_heap_page(objspace, page, Qfalse);
- }
+ if (page->flags.has_remembered_objects == FALSE) {
+ remembered_old_objects += gc_verify_heap_page(objspace, page, Qfalse);
+ }
}
return remembered_old_objects;
@@ -7992,11 +8095,11 @@ gc_verify_internal_consistency_(rb_objspace_t *objspace)
if (data.err_count != 0) {
#if RGENGC_CHECK_MODE >= 5
- objspace->rgengc.error_count = data.err_count;
- gc_marks_check(objspace, NULL, NULL);
- allrefs_dump(objspace);
+ objspace->rgengc.error_count = data.err_count;
+ gc_marks_check(objspace, NULL, NULL);
+ allrefs_dump(objspace);
#endif
- rb_bug("gc_verify_internal_consistency: found internal inconsistency.");
+ rb_bug("gc_verify_internal_consistency: found internal inconsistency.");
}
/* check heap_page status */
@@ -8007,39 +8110,39 @@ gc_verify_internal_consistency_(rb_objspace_t *objspace)
if (!is_lazy_sweeping(objspace) &&
!finalizing &&
ruby_single_main_ractor != NULL) {
- if (objspace_live_slots(objspace) != data.live_object_count) {
- fprintf(stderr, "heap_pages_final_slots: %"PRIdSIZE", "
+ if (objspace_live_slots(objspace) != data.live_object_count) {
+ fprintf(stderr, "heap_pages_final_slots: %"PRIdSIZE", "
"objspace->profile.total_freed_objects: %"PRIdSIZE"\n",
- heap_pages_final_slots, objspace->profile.total_freed_objects);
- rb_bug("inconsistent live slot number: expect %"PRIuSIZE", but %"PRIuSIZE".",
+ heap_pages_final_slots, objspace->profile.total_freed_objects);
+ rb_bug("inconsistent live slot number: expect %"PRIuSIZE", but %"PRIuSIZE".",
objspace_live_slots(objspace), data.live_object_count);
- }
+ }
}
if (!is_marking(objspace)) {
- if (objspace->rgengc.old_objects != data.old_object_count) {
- rb_bug("inconsistent old slot number: expect %"PRIuSIZE", but %"PRIuSIZE".",
+ if (objspace->rgengc.old_objects != data.old_object_count) {
+ rb_bug("inconsistent old slot number: expect %"PRIuSIZE", but %"PRIuSIZE".",
objspace->rgengc.old_objects, data.old_object_count);
- }
- if (objspace->rgengc.uncollectible_wb_unprotected_objects != data.remembered_shady_count) {
+ }
+ if (objspace->rgengc.uncollectible_wb_unprotected_objects != data.remembered_shady_count) {
rb_bug("inconsistent number of wb unprotected objects: expect %"PRIuSIZE", but %"PRIuSIZE".",
objspace->rgengc.uncollectible_wb_unprotected_objects, data.remembered_shady_count);
- }
+ }
}
if (!finalizing) {
- size_t list_count = 0;
+ size_t list_count = 0;
- {
- VALUE z = heap_pages_deferred_final;
- while (z) {
- list_count++;
- z = RZOMBIE(z)->next;
- }
- }
+ {
+ VALUE z = heap_pages_deferred_final;
+ while (z) {
+ list_count++;
+ z = RZOMBIE(z)->next;
+ }
+ }
- if (heap_pages_final_slots != data.zombie_object_count ||
- heap_pages_final_slots != list_count) {
+ if (heap_pages_final_slots != data.zombie_object_count ||
+ heap_pages_final_slots != list_count) {
rb_bug("inconsistent finalizing object count:\n"
" expect %"PRIuSIZE"\n"
@@ -8048,7 +8151,7 @@ gc_verify_internal_consistency_(rb_objspace_t *objspace)
heap_pages_final_slots,
data.zombie_object_count,
list_count);
- }
+ }
}
gc_report(5, objspace, "gc_verify_internal_consistency: OK\n");
@@ -8119,20 +8222,20 @@ gc_marks_start(rb_objspace_t *objspace, int full_mark)
size_t incremental_marking_steps = (objspace->rincgc.pooled_slots / INCREMENTAL_MARK_STEP_ALLOCATIONS) + 1;
objspace->rincgc.step_slots = (objspace->marked_slots * 2) / incremental_marking_steps;
- if (0) fprintf(stderr, "objspace->marked_slots: %"PRIdSIZE", "
+ if (0) fprintf(stderr, "objspace->marked_slots: %"PRIdSIZE", "
"objspace->rincgc.pooled_page_num: %"PRIdSIZE", "
"objspace->rincgc.step_slots: %"PRIdSIZE", \n",
objspace->marked_slots, objspace->rincgc.pooled_slots, objspace->rincgc.step_slots);
#endif
- objspace->flags.during_minor_gc = FALSE;
+ objspace->flags.during_minor_gc = FALSE;
if (ruby_enable_autocompact) {
objspace->flags.during_compacting |= TRUE;
}
- objspace->profile.major_gc_count++;
- objspace->rgengc.uncollectible_wb_unprotected_objects = 0;
- objspace->rgengc.old_objects = 0;
- objspace->rgengc.last_major_gc = objspace->profile.count;
- objspace->marked_slots = 0;
+ objspace->profile.major_gc_count++;
+ objspace->rgengc.uncollectible_wb_unprotected_objects = 0;
+ objspace->rgengc.old_objects = 0;
+ objspace->rgengc.last_major_gc = objspace->profile.count;
+ objspace->marked_slots = 0;
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
rb_size_pool_t *size_pool = &size_pools[i];
@@ -8142,10 +8245,10 @@ gc_marks_start(rb_objspace_t *objspace, int full_mark)
}
}
else {
- objspace->flags.during_minor_gc = TRUE;
- objspace->marked_slots =
- objspace->rgengc.old_objects + objspace->rgengc.uncollectible_wb_unprotected_objects; /* uncollectible objects are marked already */
- objspace->profile.minor_gc_count++;
+ objspace->flags.during_minor_gc = TRUE;
+ objspace->marked_slots =
+ objspace->rgengc.old_objects + objspace->rgengc.uncollectible_wb_unprotected_objects; /* uncollectible objects are marked already */
+ objspace->profile.minor_gc_count++;
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
rgengc_rememberset_mark(objspace, SIZE_POOL_EDEN_HEAP(&size_pools[i]));
@@ -8182,22 +8285,22 @@ gc_marks_wb_unprotected_objects(rb_objspace_t *objspace, rb_heap_t *heap)
struct heap_page *page = 0;
ccan_list_for_each(&heap->pages, page, page_node) {
- bits_t *mark_bits = page->mark_bits;
- bits_t *wbun_bits = page->wb_unprotected_bits;
+ bits_t *mark_bits = page->mark_bits;
+ bits_t *wbun_bits = page->wb_unprotected_bits;
uintptr_t p = page->start;
- size_t j;
+ size_t j;
bits_t bits = mark_bits[0] & wbun_bits[0];
bits >>= NUM_IN_PAGE(p);
gc_marks_wb_unprotected_objects_plane(objspace, p, bits);
p += (BITS_BITLENGTH - NUM_IN_PAGE(p)) * BASE_SLOT_SIZE;
- for (j=1; j<HEAP_PAGE_BITMAP_LIMIT; j++) {
- bits_t bits = mark_bits[j] & wbun_bits[j];
+ for (j=1; j<HEAP_PAGE_BITMAP_LIMIT; j++) {
+ bits_t bits = mark_bits[j] & wbun_bits[j];
gc_marks_wb_unprotected_objects_plane(objspace, p, bits);
p += BITS_BITLENGTH * BASE_SLOT_SIZE;
- }
+ }
}
gc_mark_stacked_objects_all(objspace);
@@ -8210,22 +8313,22 @@ gc_marks_finish(rb_objspace_t *objspace)
#if GC_ENABLE_INCREMENTAL_MARK
/* finish incremental GC */
if (is_incremental_marking(objspace)) {
- if (RGENGC_CHECK_MODE && is_mark_stack_empty(&objspace->mark_stack) == 0) {
- rb_bug("gc_marks_finish: mark stack is not empty (%"PRIdSIZE").",
+ if (RGENGC_CHECK_MODE && is_mark_stack_empty(&objspace->mark_stack) == 0) {
+ rb_bug("gc_marks_finish: mark stack is not empty (%"PRIdSIZE").",
mark_stack_size(&objspace->mark_stack));
- }
+ }
- gc_mark_roots(objspace, 0);
+ gc_mark_roots(objspace, 0);
while (gc_mark_stacked_objects_incremental(objspace, INT_MAX) == false);
#if RGENGC_CHECK_MODE >= 2
- if (gc_verify_heap_pages(objspace) != 0) {
- rb_bug("gc_marks_finish (incremental): there are remembered old objects.");
- }
+ if (gc_verify_heap_pages(objspace) != 0) {
+ rb_bug("gc_marks_finish (incremental): there are remembered old objects.");
+ }
#endif
- objspace->flags.during_incremental_marking = FALSE;
- /* check children of all marked wb-unprotected objects */
+ objspace->flags.during_incremental_marking = FALSE;
+ /* check children of all marked wb-unprotected objects */
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
gc_marks_wb_unprotected_objects(objspace, SIZE_POOL_EDEN_HEAP(&size_pools[i]));
}
@@ -8237,10 +8340,10 @@ gc_marks_finish(rb_objspace_t *objspace)
#endif
if (is_full_marking(objspace)) {
- /* See the comment about RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR */
- const double r = gc_params.oldobject_limit_factor;
- objspace->rgengc.uncollectible_wb_unprotected_objects_limit = (size_t)(objspace->rgengc.uncollectible_wb_unprotected_objects * r);
- objspace->rgengc.old_objects_limit = (size_t)(objspace->rgengc.old_objects * r);
+ /* See the comment about RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR */
+ const double r = gc_params.oldobject_limit_factor;
+ objspace->rgengc.uncollectible_wb_unprotected_objects_limit = (size_t)(objspace->rgengc.uncollectible_wb_unprotected_objects * r);
+ objspace->rgengc.old_objects_limit = (size_t)(objspace->rgengc.old_objects * r);
}
#if RGENGC_CHECK_MODE >= 4
@@ -8250,81 +8353,81 @@ gc_marks_finish(rb_objspace_t *objspace)
#endif
{
- /* decide full GC is needed or not */
+ /* decide full GC is needed or not */
size_t total_slots = heap_allocatable_slots(objspace) + heap_eden_total_slots(objspace);
- size_t sweep_slots = total_slots - objspace->marked_slots; /* will be swept slots */
- size_t max_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_max_ratio);
- size_t min_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_min_ratio);
- int full_marking = is_full_marking(objspace);
+ size_t sweep_slots = total_slots - objspace->marked_slots; /* will be swept slots */
+ size_t max_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_max_ratio);
+ size_t min_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_min_ratio);
+ int full_marking = is_full_marking(objspace);
const int r_cnt = GET_VM()->ractor.cnt;
const int r_mul = r_cnt > 8 ? 8 : r_cnt; // upto 8
GC_ASSERT(heap_eden_total_slots(objspace) >= objspace->marked_slots);
- /* setup free-able page counts */
+ /* setup free-able page counts */
if (max_free_slots < gc_params.heap_init_slots * r_mul) {
max_free_slots = gc_params.heap_init_slots * r_mul;
}
- if (sweep_slots > max_free_slots) {
- heap_pages_freeable_pages = (sweep_slots - max_free_slots) / HEAP_PAGE_OBJ_LIMIT;
- }
- else {
- heap_pages_freeable_pages = 0;
- }
+ if (sweep_slots > max_free_slots) {
+ heap_pages_freeable_pages = (sweep_slots - max_free_slots) / HEAP_PAGE_OBJ_LIMIT;
+ }
+ else {
+ heap_pages_freeable_pages = 0;
+ }
/* check free_min */
if (min_free_slots < gc_params.heap_free_slots * r_mul) {
min_free_slots = gc_params.heap_free_slots * r_mul;
}
- if (sweep_slots < min_free_slots) {
- if (!full_marking) {
- if (objspace->profile.count - objspace->rgengc.last_major_gc < RVALUE_OLD_AGE) {
- full_marking = TRUE;
- /* do not update last_major_gc, because full marking is not done. */
+ if (sweep_slots < min_free_slots) {
+ if (!full_marking) {
+ if (objspace->profile.count - objspace->rgengc.last_major_gc < RVALUE_OLD_AGE) {
+ full_marking = TRUE;
+ /* do not update last_major_gc, because full marking is not done. */
/* goto increment; */
- }
- else {
- gc_report(1, objspace, "gc_marks_finish: next is full GC!!)\n");
- objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_NOFREE;
- }
- }
+ }
+ else {
+ gc_report(1, objspace, "gc_marks_finish: next is full GC!!)\n");
+ objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_NOFREE;
+ }
+ }
#if !USE_RVARGC
if (full_marking) {
/* increment: */
- gc_report(1, objspace, "gc_marks_finish: heap_set_increment!!\n");
+ gc_report(1, objspace, "gc_marks_finish: heap_set_increment!!\n");
rb_size_pool_t *size_pool = &size_pools[0];
size_pool_allocatable_pages_set(objspace, size_pool, heap_extend_pages(objspace, size_pool, sweep_slots, total_slots, heap_allocated_pages + heap_allocatable_pages(objspace)));
heap_increment(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool));
- }
-#endif
- }
-
- if (full_marking) {
- /* See the comment about RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR */
- const double r = gc_params.oldobject_limit_factor;
- objspace->rgengc.uncollectible_wb_unprotected_objects_limit = (size_t)(objspace->rgengc.uncollectible_wb_unprotected_objects * r);
- objspace->rgengc.old_objects_limit = (size_t)(objspace->rgengc.old_objects * r);
- }
-
- if (objspace->rgengc.uncollectible_wb_unprotected_objects > objspace->rgengc.uncollectible_wb_unprotected_objects_limit) {
- objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_SHADY;
- }
- if (objspace->rgengc.old_objects > objspace->rgengc.old_objects_limit) {
- objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_OLDGEN;
- }
- if (RGENGC_FORCE_MAJOR_GC) {
- objspace->rgengc.need_major_gc = GPR_FLAG_MAJOR_BY_FORCE;
- }
-
- gc_report(1, objspace, "gc_marks_finish (marks %"PRIdSIZE" objects, "
+ }
+#endif
+ }
+
+ if (full_marking) {
+ /* See the comment about RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR */
+ const double r = gc_params.oldobject_limit_factor;
+ objspace->rgengc.uncollectible_wb_unprotected_objects_limit = (size_t)(objspace->rgengc.uncollectible_wb_unprotected_objects * r);
+ objspace->rgengc.old_objects_limit = (size_t)(objspace->rgengc.old_objects * r);
+ }
+
+ if (objspace->rgengc.uncollectible_wb_unprotected_objects > objspace->rgengc.uncollectible_wb_unprotected_objects_limit) {
+ objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_SHADY;
+ }
+ if (objspace->rgengc.old_objects > objspace->rgengc.old_objects_limit) {
+ objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_OLDGEN;
+ }
+ if (RGENGC_FORCE_MAJOR_GC) {
+ objspace->rgengc.need_major_gc = GPR_FLAG_MAJOR_BY_FORCE;
+ }
+
+ gc_report(1, objspace, "gc_marks_finish (marks %"PRIdSIZE" objects, "
"old %"PRIdSIZE" objects, total %"PRIdSIZE" slots, "
"sweep %"PRIdSIZE" slots, increment: %"PRIdSIZE", next GC: %s)\n",
objspace->marked_slots, objspace->rgengc.old_objects, heap_eden_total_slots(objspace), sweep_slots, heap_allocatable_pages(objspace),
- objspace->rgengc.need_major_gc ? "major" : "minor");
+ objspace->rgengc.need_major_gc ? "major" : "minor");
}
rb_transient_heap_finish_marking();
@@ -8357,42 +8460,64 @@ static rb_size_pool_t *
gc_compact_destination_pool(rb_objspace_t *objspace, rb_size_pool_t *src_pool, VALUE src)
{
size_t obj_size;
+ size_t idx = 0;
switch (BUILTIN_TYPE(src)) {
- case T_ARRAY:
- obj_size = rb_ary_size_as_embedded(src);
- break;
+ case T_ARRAY:
+ obj_size = rb_ary_size_as_embedded(src);
+ break;
- case T_OBJECT:
- obj_size = rb_obj_embedded_size(ROBJECT_NUMIV(src));
- break;
+ case T_OBJECT:
+ if (rb_shape_obj_too_complex(src)) {
+ return &size_pools[0];
+ }
+ else {
+ obj_size = rb_obj_embedded_size(ROBJECT_IV_CAPACITY(src));
+ }
+ break;
- case T_STRING:
- obj_size = rb_str_size_as_embedded(src);
- break;
+ case T_STRING:
+ obj_size = rb_str_size_as_embedded(src);
+ break;
- default:
- return src_pool;
+ default:
+ return src_pool;
}
if (rb_gc_size_allocatable_p(obj_size)){
- return &size_pools[size_pool_idx_for_size(obj_size)];
- }
- else {
- return &size_pools[0];
+ idx = size_pool_idx_for_size(obj_size);
}
+ return &size_pools[idx];
}
static bool
gc_compact_move(rb_objspace_t *objspace, rb_heap_t *heap, rb_size_pool_t *size_pool, VALUE src)
{
GC_ASSERT(BUILTIN_TYPE(src) != T_MOVED);
- rb_heap_t *dheap = SIZE_POOL_EDEN_HEAP(gc_compact_destination_pool(objspace, size_pool, src));
+ GC_ASSERT(gc_is_moveable_obj(objspace, src));
+
+ rb_size_pool_t *dest_pool = gc_compact_destination_pool(objspace, size_pool, src);
+ rb_heap_t *dheap = SIZE_POOL_EDEN_HEAP(dest_pool);
+ rb_shape_t *new_shape = NULL;
+ rb_shape_t *orig_shape = NULL;
if (gc_compact_heap_cursors_met_p(dheap)) {
return dheap != heap;
}
+ if (RB_TYPE_P(src, T_OBJECT)) {
+ orig_shape = rb_shape_get_shape(src);
+ if (dheap != heap && !rb_shape_obj_too_complex(src)) {
+ rb_shape_t *initial_shape = rb_shape_get_shape_by_id((shape_id_t)((dest_pool - size_pools) + SIZE_POOL_COUNT));
+ new_shape = rb_shape_traverse_from_new_root(initial_shape, orig_shape);
+
+ if (!new_shape) {
+ dest_pool = size_pool;
+ dheap = heap;
+ }
+ }
+ }
+
while (!try_move(objspace, dheap, dheap->free_pages, src)) {
struct gc_sweep_context ctx = {
.page = dheap->sweeping_page,
@@ -8414,14 +8539,24 @@ gc_compact_move(rb_objspace_t *objspace, rb_heap_t *heap, rb_size_pool_t *size_p
dheap->sweeping_page = ccan_list_next(&dheap->pages, dheap->sweeping_page, page_node);
if (gc_compact_heap_cursors_met_p(dheap)) {
- return false;
+ return dheap != heap;
+ }
+ }
+
+ if (orig_shape) {
+ if (new_shape) {
+ VALUE dest = rb_gc_location(src);
+ rb_shape_set_shape(dest, new_shape);
}
+ RMOVED(src)->original_shape_id = rb_shape_id(orig_shape);
}
+
return true;
}
static bool
-gc_compact_plane(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, uintptr_t p, bits_t bitset, struct heap_page *page) {
+gc_compact_plane(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, uintptr_t p, bits_t bitset, struct heap_page *page)
+{
short slot_size = page->slot_size;
short slot_bits = slot_size / BASE_SLOT_SIZE;
GC_ASSERT(slot_bits > 0);
@@ -8433,9 +8568,11 @@ gc_compact_plane(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *
if (bitset & 1) {
objspace->rcompactor.considered_count_table[BUILTIN_TYPE(vp)]++;
- if (!gc_compact_move(objspace, heap, size_pool, vp)) {
- //the cursors met. bubble up
- return false;
+ if (gc_is_moveable_obj(objspace, vp)) {
+ if (!gc_compact_move(objspace, heap, size_pool, vp)) {
+ //the cursors met. bubble up
+ return false;
+ }
}
}
p += slot_size;
@@ -8480,7 +8617,8 @@ gc_compact_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *h
}
static bool
-gc_compact_all_compacted_p(rb_objspace_t *objspace) {
+gc_compact_all_compacted_p(rb_objspace_t *objspace)
+{
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
rb_size_pool_t *size_pool = &size_pools[i];
rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool);
@@ -8548,7 +8686,7 @@ gc_marks_rest(rb_objspace_t *objspace)
while (gc_mark_stacked_objects_incremental(objspace, INT_MAX) == FALSE);
}
else {
- gc_mark_stacked_objects_all(objspace);
+ gc_mark_stacked_objects_all(objspace);
}
gc_marks_finish(objspace);
@@ -8607,29 +8745,29 @@ static void
gc_report_body(int level, rb_objspace_t *objspace, const char *fmt, ...)
{
if (level <= RGENGC_DEBUG) {
- char buf[1024];
- FILE *out = stderr;
- va_list args;
- const char *status = " ";
+ char buf[1024];
+ FILE *out = stderr;
+ va_list args;
+ const char *status = " ";
- if (during_gc) {
- status = is_full_marking(objspace) ? "+" : "-";
- }
- else {
- if (is_lazy_sweeping(objspace)) {
- status = "S";
- }
- if (is_incremental_marking(objspace)) {
- status = "M";
- }
- }
+ if (during_gc) {
+ status = is_full_marking(objspace) ? "+" : "-";
+ }
+ else {
+ if (is_lazy_sweeping(objspace)) {
+ status = "S";
+ }
+ if (is_incremental_marking(objspace)) {
+ status = "M";
+ }
+ }
- va_start(args, fmt);
- vsnprintf(buf, 1024, fmt, args);
- va_end(args);
+ va_start(args, fmt);
+ vsnprintf(buf, 1024, fmt, args);
+ va_end(args);
- fprintf(out, "%s|", status);
- fputs(buf, out);
+ fprintf(out, "%s|", status);
+ fputs(buf, out);
}
}
@@ -8650,12 +8788,12 @@ rgengc_remembersetbits_set(rb_objspace_t *objspace, VALUE obj)
GC_ASSERT(!is_incremental_marking(objspace));
if (MARKED_IN_BITMAP(bits, obj)) {
- return FALSE;
+ return FALSE;
}
else {
- page->flags.has_remembered_objects = TRUE;
- MARK_IN_BITMAP(bits, obj);
- return TRUE;
+ page->flags.has_remembered_objects = TRUE;
+ MARK_IN_BITMAP(bits, obj);
+ return TRUE;
}
}
@@ -8666,22 +8804,22 @@ static int
rgengc_remember(rb_objspace_t *objspace, VALUE obj)
{
gc_report(6, objspace, "rgengc_remember: %s %s\n", obj_info(obj),
- rgengc_remembersetbits_get(objspace, obj) ? "was already remembered" : "is remembered now");
+ rgengc_remembersetbits_get(objspace, obj) ? "was already remembered" : "is remembered now");
check_rvalue_consistency(obj);
if (RGENGC_CHECK_MODE) {
- if (RVALUE_WB_UNPROTECTED(obj)) rb_bug("rgengc_remember: %s is not wb protected.", obj_info(obj));
+ if (RVALUE_WB_UNPROTECTED(obj)) rb_bug("rgengc_remember: %s is not wb protected.", obj_info(obj));
}
#if RGENGC_PROFILE > 0
if (!rgengc_remembered(objspace, obj)) {
- if (RVALUE_WB_UNPROTECTED(obj) == 0) {
- objspace->profile.total_remembered_normal_object_count++;
+ if (RVALUE_WB_UNPROTECTED(obj) == 0) {
+ objspace->profile.total_remembered_normal_object_count++;
#if RGENGC_PROFILE >= 2
- objspace->profile.remembered_normal_object_count_types[BUILTIN_TYPE(obj)]++;
+ objspace->profile.remembered_normal_object_count_types[BUILTIN_TYPE(obj)]++;
#endif
- }
+ }
}
#endif /* RGENGC_PROFILE > 0 */
@@ -8737,38 +8875,38 @@ rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap)
gc_report(1, objspace, "rgengc_rememberset_mark: start\n");
ccan_list_for_each(&heap->pages, page, page_node) {
- if (page->flags.has_remembered_objects | page->flags.has_uncollectible_shady_objects) {
+ if (page->flags.has_remembered_objects | page->flags.has_uncollectible_shady_objects) {
uintptr_t p = page->start;
- bits_t bitset, bits[HEAP_PAGE_BITMAP_LIMIT];
- bits_t *marking_bits = page->marking_bits;
- bits_t *uncollectible_bits = page->uncollectible_bits;
- bits_t *wb_unprotected_bits = page->wb_unprotected_bits;
+ bits_t bitset, bits[HEAP_PAGE_BITMAP_LIMIT];
+ bits_t *marking_bits = page->marking_bits;
+ bits_t *uncollectible_bits = page->uncollectible_bits;
+ bits_t *wb_unprotected_bits = page->wb_unprotected_bits;
#if PROFILE_REMEMBERSET_MARK
- if (page->flags.has_remembered_objects && page->flags.has_uncollectible_shady_objects) has_both++;
- else if (page->flags.has_remembered_objects) has_old++;
- else if (page->flags.has_uncollectible_shady_objects) has_shady++;
+ if (page->flags.has_remembered_objects && page->flags.has_uncollectible_shady_objects) has_both++;
+ else if (page->flags.has_remembered_objects) has_old++;
+ else if (page->flags.has_uncollectible_shady_objects) has_shady++;
#endif
- for (j=0; j<HEAP_PAGE_BITMAP_LIMIT; j++) {
- bits[j] = marking_bits[j] | (uncollectible_bits[j] & wb_unprotected_bits[j]);
- marking_bits[j] = 0;
- }
- page->flags.has_remembered_objects = FALSE;
+ for (j=0; j<HEAP_PAGE_BITMAP_LIMIT; j++) {
+ bits[j] = marking_bits[j] | (uncollectible_bits[j] & wb_unprotected_bits[j]);
+ marking_bits[j] = 0;
+ }
+ page->flags.has_remembered_objects = FALSE;
bitset = bits[0];
bitset >>= NUM_IN_PAGE(p);
rgengc_rememberset_mark_plane(objspace, p, bitset);
p += (BITS_BITLENGTH - NUM_IN_PAGE(p)) * BASE_SLOT_SIZE;
- for (j=1; j < HEAP_PAGE_BITMAP_LIMIT; j++) {
- bitset = bits[j];
+ for (j=1; j < HEAP_PAGE_BITMAP_LIMIT; j++) {
+ bitset = bits[j];
rgengc_rememberset_mark_plane(objspace, p, bitset);
p += BITS_BITLENGTH * BASE_SLOT_SIZE;
- }
- }
+ }
+ }
#if PROFILE_REMEMBERSET_MARK
- else {
- skip++;
- }
+ else {
+ skip++;
+ }
#endif
}
@@ -8784,12 +8922,12 @@ rgengc_mark_and_rememberset_clear(rb_objspace_t *objspace, rb_heap_t *heap)
struct heap_page *page = 0;
ccan_list_for_each(&heap->pages, page, page_node) {
- memset(&page->mark_bits[0], 0, HEAP_PAGE_BITMAP_SIZE);
- memset(&page->uncollectible_bits[0], 0, HEAP_PAGE_BITMAP_SIZE);
+ memset(&page->mark_bits[0], 0, HEAP_PAGE_BITMAP_SIZE);
+ memset(&page->uncollectible_bits[0], 0, HEAP_PAGE_BITMAP_SIZE);
memset(&page->marking_bits[0], 0, HEAP_PAGE_BITMAP_SIZE);
memset(&page->pinned_bits[0], 0, HEAP_PAGE_BITMAP_SIZE);
- page->flags.has_uncollectible_shady_objects = FALSE;
- page->flags.has_remembered_objects = FALSE;
+ page->flags.has_uncollectible_shady_objects = FALSE;
+ page->flags.has_remembered_objects = FALSE;
}
}
@@ -8801,9 +8939,9 @@ static void
gc_writebarrier_generational(VALUE a, VALUE b, rb_objspace_t *objspace)
{
if (RGENGC_CHECK_MODE) {
- if (!RVALUE_OLD_P(a)) rb_bug("gc_writebarrier_generational: %s is not an old object.", obj_info(a));
- if ( RVALUE_OLD_P(b)) rb_bug("gc_writebarrier_generational: %s is an old object.", obj_info(b));
- if (is_incremental_marking(objspace)) rb_bug("gc_writebarrier_generational: called while incremental marking: %s -> %s", obj_info(a), obj_info(b));
+ if (!RVALUE_OLD_P(a)) rb_bug("gc_writebarrier_generational: %s is not an old object.", obj_info(a));
+ if ( RVALUE_OLD_P(b)) rb_bug("gc_writebarrier_generational: %s is an old object.", obj_info(b));
+ if (is_incremental_marking(objspace)) rb_bug("gc_writebarrier_generational: called while incremental marking: %s -> %s", obj_info(a), obj_info(b));
}
#if 1
@@ -8814,17 +8952,17 @@ gc_writebarrier_generational(VALUE a, VALUE b, rb_objspace_t *objspace)
rgengc_remember(objspace, a);
}
RB_VM_LOCK_LEAVE_NO_BARRIER();
- gc_report(1, objspace, "gc_writebarrier_generational: %s (remembered) -> %s\n", obj_info(a), obj_info(b));
+ gc_report(1, objspace, "gc_writebarrier_generational: %s (remembered) -> %s\n", obj_info(a), obj_info(b));
}
#else
/* mark `b' and remember */
MARK_IN_BITMAP(GET_HEAP_MARK_BITS(b), b);
if (RVALUE_WB_UNPROTECTED(b)) {
- gc_remember_unprotected(objspace, b);
+ gc_remember_unprotected(objspace, b);
}
else {
- RVALUE_AGE_SET_OLD(objspace, b);
- rgengc_remember(objspace, b);
+ RVALUE_AGE_SET_OLD(objspace, b);
+ rgengc_remember(objspace, b);
}
gc_report(1, objspace, "gc_writebarrier_generational: %s -> %s (remembered)\n", obj_info(a), obj_info(b));
@@ -8853,26 +8991,26 @@ gc_writebarrier_incremental(VALUE a, VALUE b, rb_objspace_t *objspace)
gc_report(2, objspace, "gc_writebarrier_incremental: [LG] %p -> %s\n", (void *)a, obj_info(b));
if (RVALUE_BLACK_P(a)) {
- if (RVALUE_WHITE_P(b)) {
- if (!RVALUE_WB_UNPROTECTED(a)) {
- gc_report(2, objspace, "gc_writebarrier_incremental: [IN] %p -> %s\n", (void *)a, obj_info(b));
- gc_mark_from(objspace, b, a);
- }
- }
- else if (RVALUE_OLD_P(a) && !RVALUE_OLD_P(b)) {
- if (!RVALUE_WB_UNPROTECTED(b)) {
- gc_report(1, objspace, "gc_writebarrier_incremental: [GN] %p -> %s\n", (void *)a, obj_info(b));
- RVALUE_AGE_SET_OLD(objspace, b);
-
- if (RVALUE_BLACK_P(b)) {
- gc_grey(objspace, b);
- }
- }
- else {
- gc_report(1, objspace, "gc_writebarrier_incremental: [LL] %p -> %s\n", (void *)a, obj_info(b));
- gc_remember_unprotected(objspace, b);
- }
- }
+ if (RVALUE_WHITE_P(b)) {
+ if (!RVALUE_WB_UNPROTECTED(a)) {
+ gc_report(2, objspace, "gc_writebarrier_incremental: [IN] %p -> %s\n", (void *)a, obj_info(b));
+ gc_mark_from(objspace, b, a);
+ }
+ }
+ else if (RVALUE_OLD_P(a) && !RVALUE_OLD_P(b)) {
+ if (!RVALUE_WB_UNPROTECTED(b)) {
+ gc_report(1, objspace, "gc_writebarrier_incremental: [GN] %p -> %s\n", (void *)a, obj_info(b));
+ RVALUE_AGE_SET_OLD(objspace, b);
+
+ if (RVALUE_BLACK_P(b)) {
+ gc_grey(objspace, b);
+ }
+ }
+ else {
+ gc_report(1, objspace, "gc_writebarrier_incremental: [LL] %p -> %s\n", (void *)a, obj_info(b));
+ gc_remember_unprotected(objspace, b);
+ }
+ }
if (UNLIKELY(objspace->flags.during_compacting)) {
MARK_IN_BITMAP(GET_HEAP_PINNED_BITS(b), b);
@@ -8888,8 +9026,10 @@ rb_gc_writebarrier(VALUE a, VALUE b)
{
rb_objspace_t *objspace = &rb_objspace;
- if (RGENGC_CHECK_MODE && SPECIAL_CONST_P(a)) rb_bug("rb_gc_writebarrier: a is special const");
- if (RGENGC_CHECK_MODE && SPECIAL_CONST_P(b)) rb_bug("rb_gc_writebarrier: b is special const");
+ if (RGENGC_CHECK_MODE) {
+ if (SPECIAL_CONST_P(a)) rb_bug("rb_gc_writebarrier: a is special const: %"PRIxVALUE, a);
+ if (SPECIAL_CONST_P(b)) rb_bug("rb_gc_writebarrier: b is special const: %"PRIxVALUE, b);
+ }
retry:
if (!is_incremental_marking(objspace)) {
@@ -8923,33 +9063,37 @@ void
rb_gc_writebarrier_unprotect(VALUE obj)
{
if (RVALUE_WB_UNPROTECTED(obj)) {
- return;
+ return;
}
else {
- rb_objspace_t *objspace = &rb_objspace;
+ rb_objspace_t *objspace = &rb_objspace;
- gc_report(2, objspace, "rb_gc_writebarrier_unprotect: %s %s\n", obj_info(obj),
- rgengc_remembered(objspace, obj) ? " (already remembered)" : "");
+ gc_report(2, objspace, "rb_gc_writebarrier_unprotect: %s %s\n", obj_info(obj),
+ rgengc_remembered(objspace, obj) ? " (already remembered)" : "");
- if (RVALUE_OLD_P(obj)) {
- gc_report(1, objspace, "rb_gc_writebarrier_unprotect: %s\n", obj_info(obj));
- RVALUE_DEMOTE(objspace, obj);
- gc_mark_set(objspace, obj);
- gc_remember_unprotected(objspace, obj);
+ RB_VM_LOCK_ENTER_NO_BARRIER();
+ {
+ if (RVALUE_OLD_P(obj)) {
+ gc_report(1, objspace, "rb_gc_writebarrier_unprotect: %s\n", obj_info(obj));
+ RVALUE_DEMOTE(objspace, obj);
+ gc_mark_set(objspace, obj);
+ gc_remember_unprotected(objspace, obj);
#if RGENGC_PROFILE
- objspace->profile.total_shade_operation_count++;
+ objspace->profile.total_shade_operation_count++;
#if RGENGC_PROFILE >= 2
- objspace->profile.shade_operation_count_types[BUILTIN_TYPE(obj)]++;
+ objspace->profile.shade_operation_count_types[BUILTIN_TYPE(obj)]++;
#endif /* RGENGC_PROFILE >= 2 */
#endif /* RGENGC_PROFILE */
- }
- else {
- RVALUE_AGE_RESET(obj);
- }
+ }
+ else {
+ RVALUE_AGE_RESET(obj);
+ }
- RB_DEBUG_COUNTER_INC(obj_wb_unprotect);
- MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(obj), obj);
+ RB_DEBUG_COUNTER_INC(obj_wb_unprotect);
+ MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(obj), obj);
+ }
+ RB_VM_LOCK_LEAVE_NO_BARRIER();
}
}
@@ -8964,14 +9108,14 @@ rb_gc_writebarrier_remember(VALUE obj)
gc_report(1, objspace, "rb_gc_writebarrier_remember: %s\n", obj_info(obj));
if (is_incremental_marking(objspace)) {
- if (RVALUE_BLACK_P(obj)) {
- gc_grey(objspace, obj);
- }
+ if (RVALUE_BLACK_P(obj)) {
+ gc_grey(objspace, obj);
+ }
}
else {
- if (RVALUE_OLD_P(obj)) {
- rgengc_remember(objspace, obj);
- }
+ if (RVALUE_OLD_P(obj)) {
+ rgengc_remember(objspace, obj);
+ }
}
}
@@ -8996,25 +9140,25 @@ rb_gc_unprotect_logging(void *objptr, const char *filename, int line)
VALUE obj = (VALUE)objptr;
if (rgengc_unprotect_logging_table == 0) {
- rgengc_unprotect_logging_table = st_init_strtable();
- atexit(rgengc_unprotect_logging_exit_func);
+ rgengc_unprotect_logging_table = st_init_strtable();
+ atexit(rgengc_unprotect_logging_exit_func);
}
if (RVALUE_WB_UNPROTECTED(obj) == 0) {
- char buff[0x100];
- st_data_t cnt = 1;
- char *ptr = buff;
+ char buff[0x100];
+ st_data_t cnt = 1;
+ char *ptr = buff;
- snprintf(ptr, 0x100 - 1, "%s|%s:%d", obj_info(obj), filename, line);
+ snprintf(ptr, 0x100 - 1, "%s|%s:%d", obj_info(obj), filename, line);
- if (st_lookup(rgengc_unprotect_logging_table, (st_data_t)ptr, &cnt)) {
- cnt++;
- }
- else {
- ptr = (strdup)(buff);
- if (!ptr) rb_memerror();
- }
- st_insert(rgengc_unprotect_logging_table, (st_data_t)ptr, cnt);
+ if (st_lookup(rgengc_unprotect_logging_table, (st_data_t)ptr, &cnt)) {
+ cnt++;
+ }
+ else {
+ ptr = (strdup)(buff);
+ if (!ptr) rb_memerror();
+ }
+ st_insert(rgengc_unprotect_logging_table, (st_data_t)ptr, cnt);
}
}
@@ -9024,13 +9168,13 @@ rb_copy_wb_protected_attribute(VALUE dest, VALUE obj)
rb_objspace_t *objspace = &rb_objspace;
if (RVALUE_WB_UNPROTECTED(obj) && !RVALUE_WB_UNPROTECTED(dest)) {
- if (!RVALUE_OLD_P(dest)) {
- MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(dest), dest);
- RVALUE_AGE_RESET_RAW(dest);
- }
- else {
- RVALUE_DEMOTE(objspace, dest);
- }
+ if (!RVALUE_OLD_P(dest)) {
+ MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(dest), dest);
+ RVALUE_AGE_RESET_RAW(dest);
+ }
+ else {
+ RVALUE_DEMOTE(objspace, dest);
+ }
}
check_rvalue_consistency(dest);
@@ -9059,11 +9203,11 @@ rb_obj_gc_flags(VALUE obj, ID* flags, size_t max)
if (!ID_marked) {
#define I(s) ID_##s = rb_intern(#s);
- I(marked);
- I(wb_protected);
- I(old);
- I(marking);
- I(uncollectible);
+ I(marked);
+ I(wb_protected);
+ I(old);
+ I(marking);
+ I(uncollectible);
I(pinned);
#undef I
}
@@ -9122,7 +9266,7 @@ rb_gc_register_mark_object(VALUE obj)
VALUE ary = rb_ary_last(0, 0, ary_ary);
if (NIL_P(ary) || RARRAY_LEN(ary) >= MARK_OBJECT_ARY_BUCKET_SIZE) {
- ary = rb_ary_tmp_new(MARK_OBJECT_ARY_BUCKET_SIZE);
+ ary = rb_ary_hidden_new(MARK_OBJECT_ARY_BUCKET_SIZE);
rb_ary_push(ary_ary, ary);
}
@@ -9137,10 +9281,23 @@ rb_gc_register_address(VALUE *addr)
rb_objspace_t *objspace = &rb_objspace;
struct gc_list *tmp;
+ VALUE obj = *addr;
+
tmp = ALLOC(struct gc_list);
tmp->next = global_list;
tmp->varptr = addr;
global_list = tmp;
+
+ /*
+ * Because some C extensions have assignment-then-register bugs,
+ * we guard `obj` here so that it would not get swept defensively.
+ */
+ RB_GC_GUARD(obj);
+ if (0 && !SPECIAL_CONST_P(obj)) {
+ rb_warn("Object is assigned to registering address already: %"PRIsVALUE,
+ rb_obj_class(obj));
+ rb_print_backtrace();
+ }
}
void
@@ -9150,19 +9307,19 @@ rb_gc_unregister_address(VALUE *addr)
struct gc_list *tmp = global_list;
if (tmp->varptr == addr) {
- global_list = tmp->next;
- xfree(tmp);
- return;
+ global_list = tmp->next;
+ xfree(tmp);
+ return;
}
while (tmp->next) {
- if (tmp->next->varptr == addr) {
- struct gc_list *t = tmp->next;
+ if (tmp->next->varptr == addr) {
+ struct gc_list *t = tmp->next;
- tmp->next = tmp->next->next;
- xfree(t);
- break;
- }
- tmp = tmp->next;
+ tmp->next = tmp->next->next;
+ xfree(t);
+ break;
+ }
+ tmp = tmp->next;
}
}
@@ -9206,7 +9363,7 @@ ready_to_gc(rb_objspace_t *objspace)
return FALSE;
}
else {
- return TRUE;
+ return TRUE;
}
}
@@ -9215,65 +9372,65 @@ gc_reset_malloc_info(rb_objspace_t *objspace, bool full_mark)
{
gc_prof_set_malloc_info(objspace);
{
- size_t inc = ATOMIC_SIZE_EXCHANGE(malloc_increase, 0);
- size_t old_limit = malloc_limit;
-
- if (inc > malloc_limit) {
- malloc_limit = (size_t)(inc * gc_params.malloc_limit_growth_factor);
- if (malloc_limit > gc_params.malloc_limit_max) {
- malloc_limit = gc_params.malloc_limit_max;
- }
- }
- else {
- malloc_limit = (size_t)(malloc_limit * 0.98); /* magic number */
- if (malloc_limit < gc_params.malloc_limit_min) {
- malloc_limit = gc_params.malloc_limit_min;
- }
- }
-
- if (0) {
- if (old_limit != malloc_limit) {
- fprintf(stderr, "[%"PRIuSIZE"] malloc_limit: %"PRIuSIZE" -> %"PRIuSIZE"\n",
- rb_gc_count(), old_limit, malloc_limit);
- }
- else {
- fprintf(stderr, "[%"PRIuSIZE"] malloc_limit: not changed (%"PRIuSIZE")\n",
- rb_gc_count(), malloc_limit);
- }
- }
+ size_t inc = ATOMIC_SIZE_EXCHANGE(malloc_increase, 0);
+ size_t old_limit = malloc_limit;
+
+ if (inc > malloc_limit) {
+ malloc_limit = (size_t)(inc * gc_params.malloc_limit_growth_factor);
+ if (malloc_limit > gc_params.malloc_limit_max) {
+ malloc_limit = gc_params.malloc_limit_max;
+ }
+ }
+ else {
+ malloc_limit = (size_t)(malloc_limit * 0.98); /* magic number */
+ if (malloc_limit < gc_params.malloc_limit_min) {
+ malloc_limit = gc_params.malloc_limit_min;
+ }
+ }
+
+ if (0) {
+ if (old_limit != malloc_limit) {
+ fprintf(stderr, "[%"PRIuSIZE"] malloc_limit: %"PRIuSIZE" -> %"PRIuSIZE"\n",
+ rb_gc_count(), old_limit, malloc_limit);
+ }
+ else {
+ fprintf(stderr, "[%"PRIuSIZE"] malloc_limit: not changed (%"PRIuSIZE")\n",
+ rb_gc_count(), malloc_limit);
+ }
+ }
}
/* reset oldmalloc info */
#if RGENGC_ESTIMATE_OLDMALLOC
if (!full_mark) {
- if (objspace->rgengc.oldmalloc_increase > objspace->rgengc.oldmalloc_increase_limit) {
- objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_OLDMALLOC;
- objspace->rgengc.oldmalloc_increase_limit =
- (size_t)(objspace->rgengc.oldmalloc_increase_limit * gc_params.oldmalloc_limit_growth_factor);
-
- if (objspace->rgengc.oldmalloc_increase_limit > gc_params.oldmalloc_limit_max) {
- objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_max;
- }
- }
-
- if (0) fprintf(stderr, "%"PRIdSIZE"\t%d\t%"PRIuSIZE"\t%"PRIuSIZE"\t%"PRIdSIZE"\n",
- rb_gc_count(),
- objspace->rgengc.need_major_gc,
- objspace->rgengc.oldmalloc_increase,
- objspace->rgengc.oldmalloc_increase_limit,
- gc_params.oldmalloc_limit_max);
+ if (objspace->rgengc.oldmalloc_increase > objspace->rgengc.oldmalloc_increase_limit) {
+ objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_OLDMALLOC;
+ objspace->rgengc.oldmalloc_increase_limit =
+ (size_t)(objspace->rgengc.oldmalloc_increase_limit * gc_params.oldmalloc_limit_growth_factor);
+
+ if (objspace->rgengc.oldmalloc_increase_limit > gc_params.oldmalloc_limit_max) {
+ objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_max;
+ }
+ }
+
+ if (0) fprintf(stderr, "%"PRIdSIZE"\t%d\t%"PRIuSIZE"\t%"PRIuSIZE"\t%"PRIdSIZE"\n",
+ rb_gc_count(),
+ objspace->rgengc.need_major_gc,
+ objspace->rgengc.oldmalloc_increase,
+ objspace->rgengc.oldmalloc_increase_limit,
+ gc_params.oldmalloc_limit_max);
}
else {
- /* major GC */
- objspace->rgengc.oldmalloc_increase = 0;
-
- if ((objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_BY_OLDMALLOC) == 0) {
- objspace->rgengc.oldmalloc_increase_limit =
- (size_t)(objspace->rgengc.oldmalloc_increase_limit / ((gc_params.oldmalloc_limit_growth_factor - 1)/10 + 1));
- if (objspace->rgengc.oldmalloc_increase_limit < gc_params.oldmalloc_limit_min) {
- objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min;
- }
- }
+ /* major GC */
+ objspace->rgengc.oldmalloc_increase = 0;
+
+ if ((objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_BY_OLDMALLOC) == 0) {
+ objspace->rgengc.oldmalloc_increase_limit =
+ (size_t)(objspace->rgengc.oldmalloc_increase_limit / ((gc_params.oldmalloc_limit_growth_factor - 1)/10 + 1));
+ if (objspace->rgengc.oldmalloc_increase_limit < gc_params.oldmalloc_limit_min) {
+ objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min;
+ }
+ }
}
#endif
}
@@ -9336,49 +9493,49 @@ gc_start(rb_objspace_t *objspace, unsigned int reason)
#endif
if (ruby_gc_stressful) {
- int flag = FIXNUM_P(ruby_gc_stress_mode) ? FIX2INT(ruby_gc_stress_mode) : 0;
+ int flag = FIXNUM_P(ruby_gc_stress_mode) ? FIX2INT(ruby_gc_stress_mode) : 0;
- if ((flag & (1<<gc_stress_no_major)) == 0) {
- do_full_mark = TRUE;
- }
+ if ((flag & (1<<gc_stress_no_major)) == 0) {
+ do_full_mark = TRUE;
+ }
- objspace->flags.immediate_sweep = !(flag & (1<<gc_stress_no_immediate_sweep));
+ objspace->flags.immediate_sweep = !(flag & (1<<gc_stress_no_immediate_sweep));
}
else {
- if (objspace->rgengc.need_major_gc) {
- reason |= objspace->rgengc.need_major_gc;
- do_full_mark = TRUE;
- }
- else if (RGENGC_FORCE_MAJOR_GC) {
- reason = GPR_FLAG_MAJOR_BY_FORCE;
- do_full_mark = TRUE;
- }
+ if (objspace->rgengc.need_major_gc) {
+ reason |= objspace->rgengc.need_major_gc;
+ do_full_mark = TRUE;
+ }
+ else if (RGENGC_FORCE_MAJOR_GC) {
+ reason = GPR_FLAG_MAJOR_BY_FORCE;
+ do_full_mark = TRUE;
+ }
- objspace->rgengc.need_major_gc = GPR_FLAG_NONE;
+ objspace->rgengc.need_major_gc = GPR_FLAG_NONE;
}
if (do_full_mark && (reason & GPR_FLAG_MAJOR_MASK) == 0) {
- reason |= GPR_FLAG_MAJOR_BY_FORCE; /* GC by CAPI, METHOD, and so on. */
+ reason |= GPR_FLAG_MAJOR_BY_FORCE; /* GC by CAPI, METHOD, and so on. */
}
#if GC_ENABLE_INCREMENTAL_MARK
if (!GC_ENABLE_INCREMENTAL_MARK || objspace->flags.dont_incremental || immediate_mark) {
- objspace->flags.during_incremental_marking = FALSE;
+ objspace->flags.during_incremental_marking = FALSE;
}
else {
- objspace->flags.during_incremental_marking = do_full_mark;
+ objspace->flags.during_incremental_marking = do_full_mark;
}
#endif
if (!GC_ENABLE_LAZY_SWEEP || objspace->flags.dont_incremental) {
- objspace->flags.immediate_sweep = TRUE;
+ objspace->flags.immediate_sweep = TRUE;
}
if (objspace->flags.immediate_sweep) reason |= GPR_FLAG_IMMEDIATE_SWEEP;
gc_report(1, objspace, "gc_start(reason: %x) => %u, %d, %d\n",
- reason,
- do_full_mark, !is_incremental_marking(objspace), objspace->flags.immediate_sweep);
+ reason,
+ do_full_mark, !is_incremental_marking(objspace), objspace->flags.immediate_sweep);
#if USE_DEBUG_COUNTER
RB_DEBUG_COUNTER_INC(gc_count);
@@ -9414,7 +9571,7 @@ gc_start(rb_objspace_t *objspace, unsigned int reason)
gc_prof_timer_start(objspace);
{
- gc_marks(objspace, do_full_mark);
+ gc_marks(objspace, do_full_mark);
}
gc_prof_timer_stop(objspace);
@@ -9430,17 +9587,17 @@ gc_rest(rb_objspace_t *objspace)
if (marking || sweeping) {
unsigned int lock_lev;
- gc_enter(objspace, gc_enter_event_rest, &lock_lev);
+ gc_enter(objspace, gc_enter_event_rest, &lock_lev);
if (RGENGC_CHECK_MODE >= 2) gc_verify_internal_consistency(objspace);
- if (is_incremental_marking(objspace)) {
+ if (is_incremental_marking(objspace)) {
gc_marks_rest(objspace);
}
- if (is_lazy_sweeping(objspace)) {
- gc_sweep_rest(objspace);
- }
- gc_exit(objspace, gc_enter_event_rest, &lock_lev);
+ if (is_lazy_sweeping(objspace)) {
+ gc_sweep_rest(objspace);
+ }
+ gc_exit(objspace, gc_enter_event_rest, &lock_lev);
}
}
@@ -9454,18 +9611,18 @@ gc_current_status_fill(rb_objspace_t *objspace, char *buff)
{
int i = 0;
if (is_marking(objspace)) {
- buff[i++] = 'M';
- if (is_full_marking(objspace)) buff[i++] = 'F';
+ buff[i++] = 'M';
+ if (is_full_marking(objspace)) buff[i++] = 'F';
#if GC_ENABLE_INCREMENTAL_MARK
- if (is_incremental_marking(objspace)) buff[i++] = 'I';
+ if (is_incremental_marking(objspace)) buff[i++] = 'I';
#endif
}
else if (is_sweeping(objspace)) {
- buff[i++] = 'S';
- if (is_lazy_sweeping(objspace)) buff[i++] = 'L';
+ buff[i++] = 'S';
+ if (is_lazy_sweeping(objspace)) buff[i++] = 'L';
}
else {
- buff[i++] = 'N';
+ buff[i++] = 'N';
}
buff[i] = '\0';
}
@@ -9489,31 +9646,31 @@ static inline void
gc_record(rb_objspace_t *objspace, int direction, const char *event)
{
if (direction == 0) { /* enter */
- enter_count++;
- enter_tick = tick();
- gc_current_status_fill(objspace, last_gc_status);
+ enter_count++;
+ enter_tick = tick();
+ gc_current_status_fill(objspace, last_gc_status);
}
else { /* exit */
- tick_t exit_tick = tick();
- char current_gc_status[0x10];
- gc_current_status_fill(objspace, current_gc_status);
+ tick_t exit_tick = tick();
+ char current_gc_status[0x10];
+ gc_current_status_fill(objspace, current_gc_status);
#if 1
- /* [last mutator time] [gc time] [event] */
- fprintf(stderr, "%"PRItick"\t%"PRItick"\t%s\t[%s->%s|%c]\n",
- enter_tick - last_exit_tick,
- exit_tick - enter_tick,
- event,
- last_gc_status, current_gc_status,
- (objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_MASK) ? '+' : '-');
- last_exit_tick = exit_tick;
+ /* [last mutator time] [gc time] [event] */
+ fprintf(stderr, "%"PRItick"\t%"PRItick"\t%s\t[%s->%s|%c]\n",
+ enter_tick - last_exit_tick,
+ exit_tick - enter_tick,
+ event,
+ last_gc_status, current_gc_status,
+ (objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_MASK) ? '+' : '-');
+ last_exit_tick = exit_tick;
#else
- /* [enter_tick] [gc time] [event] */
- fprintf(stderr, "%"PRItick"\t%"PRItick"\t%s\t[%s->%s|%c]\n",
- enter_tick,
- exit_tick - enter_tick,
- event,
- last_gc_status, current_gc_status,
- (objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_MASK) ? '+' : '-');
+ /* [enter_tick] [gc time] [event] */
+ fprintf(stderr, "%"PRItick"\t%"PRItick"\t%s\t[%s->%s|%c]\n",
+ enter_tick,
+ exit_tick - enter_tick,
+ event,
+ last_gc_status, current_gc_status,
+ (objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_MASK) ? '+' : '-');
#endif
}
}
@@ -9677,20 +9834,20 @@ garbage_collect_with_gvl(rb_objspace_t *objspace, unsigned int reason)
{
if (dont_gc_val()) return TRUE;
if (ruby_thread_has_gvl_p()) {
- return garbage_collect(objspace, reason);
+ return garbage_collect(objspace, reason);
}
else {
- if (ruby_native_thread_p()) {
- struct objspace_and_reason oar;
- oar.objspace = objspace;
- oar.reason = reason;
- return (int)(VALUE)rb_thread_call_with_gvl(gc_with_gvl, (void *)&oar);
- }
- else {
- /* no ruby thread */
- fprintf(stderr, "[FATAL] failed to allocate memory\n");
- exit(EXIT_FAILURE);
- }
+ if (ruby_native_thread_p()) {
+ struct objspace_and_reason oar;
+ oar.objspace = objspace;
+ oar.reason = reason;
+ return (int)(VALUE)rb_thread_call_with_gvl(gc_with_gvl, (void *)&oar);
+ }
+ else {
+ /* no ruby thread */
+ fprintf(stderr, "[FATAL] failed to allocate memory\n");
+ exit(EXIT_FAILURE);
+ }
}
}
@@ -9778,6 +9935,17 @@ gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj)
return FALSE;
}
+/* Used in places that could malloc, which can cause the GC to run. We need to
+ * temporarily disable the GC to allow the malloc to happen. */
+#define COULD_MALLOC_REGION_START() \
+ GC_ASSERT(during_gc); \
+ VALUE _already_disabled = rb_gc_disable_no_rest(); \
+ during_gc = false;
+
+#define COULD_MALLOC_REGION_END() \
+ during_gc = true; \
+ if (_already_disabled == Qfalse) rb_objspace_gc_enable(objspace);
+
static VALUE
gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, size_t slot_size)
{
@@ -9806,11 +9974,12 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS((VALUE)src), (VALUE)src);
if (FL_TEST((VALUE)src, FL_EXIVAR)) {
- /* Same deal as below. Generic ivars are held in st tables.
- * Resizing the table could cause a GC to happen and we can't allow it */
- VALUE already_disabled = rb_gc_disable_no_rest();
- rb_mv_generic_ivar((VALUE)src, (VALUE)dest);
- if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace);
+ /* Resizing the st table could cause a malloc */
+ COULD_MALLOC_REGION_START();
+ {
+ rb_mv_generic_ivar((VALUE)src, (VALUE)dest);
+ }
+ COULD_MALLOC_REGION_END();
}
st_data_t srcid = (st_data_t)src, id;
@@ -9819,17 +9988,25 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
* the object to object id mapping. */
if (st_lookup(objspace->obj_to_id_tbl, srcid, &id)) {
gc_report(4, objspace, "Moving object with seen id: %p -> %p\n", (void *)src, (void *)dest);
- /* inserting in the st table can cause the GC to run. We need to
- * prevent re-entry in to the GC since `gc_move` is running in the GC,
- * so temporarily disable the GC around the st table mutation */
- VALUE already_disabled = rb_gc_disable_no_rest();
- st_delete(objspace->obj_to_id_tbl, &srcid, 0);
- st_insert(objspace->obj_to_id_tbl, (st_data_t)dest, id);
- if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace);
+ /* Resizing the st table could cause a malloc */
+ COULD_MALLOC_REGION_START();
+ {
+ st_delete(objspace->obj_to_id_tbl, &srcid, 0);
+ st_insert(objspace->obj_to_id_tbl, (st_data_t)dest, id);
+ }
+ COULD_MALLOC_REGION_END();
}
/* Move the object */
memcpy(dest, src, MIN(src_slot_size, slot_size));
+
+ if (RVALUE_OVERHEAD > 0) {
+ void *dest_overhead = (void *)(((uintptr_t)dest) + slot_size - RVALUE_OVERHEAD);
+ void *src_overhead = (void *)(((uintptr_t)src) + src_slot_size - RVALUE_OVERHEAD);
+
+ memcpy(dest_overhead, src_overhead, RVALUE_OVERHEAD);
+ }
+
memset(src, 0, src_slot_size);
/* Set bits for object in new location */
@@ -9924,16 +10101,40 @@ gc_sort_heap_by_empty_slots(rb_objspace_t *objspace)
static void
gc_ref_update_array(rb_objspace_t * objspace, VALUE v)
{
- long i, len;
+ if (ARY_SHARED_P(v)) {
+#if USE_RVARGC
+ VALUE old_root = RARRAY(v)->as.heap.aux.shared_root;
+#endif
+
+ UPDATE_IF_MOVED(objspace, RARRAY(v)->as.heap.aux.shared_root);
+
+#if USE_RVARGC
+ VALUE new_root = RARRAY(v)->as.heap.aux.shared_root;
+ // If the root is embedded and its location has changed
+ if (ARY_EMBED_P(new_root) && new_root != old_root) {
+ size_t offset = (size_t)(RARRAY(v)->as.heap.ptr - RARRAY(old_root)->as.ary);
+ GC_ASSERT(RARRAY(v)->as.heap.ptr >= RARRAY(old_root)->as.ary);
+ RARRAY(v)->as.heap.ptr = RARRAY(new_root)->as.ary + offset;
+ }
+#endif
+ }
+ else {
+ long len = RARRAY_LEN(v);
- if (ARY_SHARED_P(v)) return;
+ if (len > 0) {
+ VALUE *ptr = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(v);
+ for (long i = 0; i < len; i++) {
+ UPDATE_IF_MOVED(objspace, ptr[i]);
+ }
+ }
- len = RARRAY_LEN(v);
- if (len > 0) {
- VALUE *ptr = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(v);
- for (i = 0; i < len; i++) {
- UPDATE_IF_MOVED(objspace, ptr[i]);
+#if USE_RVARGC
+ if (rb_gc_obj_slot_size(v) >= rb_ary_size_as_embedded(v)) {
+ if (rb_ary_embeddable_p(v)) {
+ rb_ary_make_embedded(v);
+ }
}
+#endif
}
}
@@ -9941,14 +10142,18 @@ static void
gc_ref_update_object(rb_objspace_t *objspace, VALUE v)
{
VALUE *ptr = ROBJECT_IVPTR(v);
- uint32_t numiv = ROBJECT_NUMIV(v);
+
+ if (rb_shape_obj_too_complex(v)) {
+ rb_gc_update_tbl_refs(ROBJECT_IV_HASH(v));
+ return;
+ }
#if USE_RVARGC
size_t slot_size = rb_gc_obj_slot_size(v);
- size_t embed_size = rb_obj_embedded_size(numiv);
+ size_t embed_size = rb_obj_embedded_size(ROBJECT_IV_CAPACITY(v));
if (slot_size >= embed_size && !RB_FL_TEST_RAW(v, ROBJECT_EMBED)) {
// Object can be re-embedded
- memcpy(ROBJECT(v)->as.ary, ptr, sizeof(VALUE) * numiv);
+ memcpy(ROBJECT(v)->as.ary, ptr, sizeof(VALUE) * ROBJECT_IV_COUNT(v));
RB_FL_SET_RAW(v, ROBJECT_EMBED);
if (ROBJ_TRANSIENT_P(v)) {
ROBJ_TRANSIENT_UNSET(v);
@@ -9957,18 +10162,10 @@ gc_ref_update_object(rb_objspace_t *objspace, VALUE v)
xfree(ptr);
}
ptr = ROBJECT(v)->as.ary;
-
- uint32_t capa = (uint32_t)((slot_size - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
- ROBJECT(v)->numiv = capa;
-
- // Fill end with Qundef
- for (uint32_t i = numiv; i < capa; i++) {
- ptr[i] = Qundef;
- }
}
#endif
- for (uint32_t i = 0; i < numiv; i++) {
+ for (uint32_t i = 0; i < ROBJECT_IV_COUNT(v); i++) {
UPDATE_IF_MOVED(objspace, ptr[i]);
}
}
@@ -10295,9 +10492,14 @@ static enum rb_id_table_iterator_result
update_cvc_tbl_i(VALUE cvc_entry, void *data)
{
struct rb_cvar_class_tbl_entry *entry;
+ rb_objspace_t * objspace = (rb_objspace_t *)data;
entry = (struct rb_cvar_class_tbl_entry *)cvc_entry;
+ if (entry->cref) {
+ TYPED_UPDATE_IF_MOVED(objspace, rb_cref_t *, entry->cref);
+ }
+
entry->class_value = rb_gc_location(entry->class_value);
return ID_TABLE_CONTINUE;
@@ -10313,6 +10515,28 @@ update_cvc_tbl(rb_objspace_t *objspace, VALUE klass)
}
static enum rb_id_table_iterator_result
+mark_cvc_tbl_i(VALUE cvc_entry, void *data)
+{
+ struct rb_cvar_class_tbl_entry *entry;
+
+ entry = (struct rb_cvar_class_tbl_entry *)cvc_entry;
+
+ RUBY_ASSERT(entry->cref == 0 || (BUILTIN_TYPE((VALUE)entry->cref) == T_IMEMO && IMEMO_TYPE_P(entry->cref, imemo_cref)));
+ rb_gc_mark((VALUE) entry->cref);
+
+ return ID_TABLE_CONTINUE;
+}
+
+static void
+mark_cvc_tbl(rb_objspace_t *objspace, VALUE klass)
+{
+ struct rb_id_table *tbl = RCLASS_CVC_TBL(klass);
+ if (tbl) {
+ rb_id_table_foreach_values(tbl, mark_cvc_tbl_i, objspace);
+ }
+}
+
+static enum rb_id_table_iterator_result
update_const_table(VALUE value, void *data)
{
rb_const_entry_t *ce = (rb_const_entry_t *)value;
@@ -10345,15 +10569,6 @@ update_subclass_entries(rb_objspace_t *objspace, rb_subclass_entry_t *entry)
}
}
-static int
-update_iv_index_tbl_i(st_data_t key, st_data_t value, st_data_t arg)
-{
- rb_objspace_t *objspace = (rb_objspace_t *)arg;
- struct rb_iv_index_tbl_entry *ent = (struct rb_iv_index_tbl_entry *)value;
- UPDATE_IF_MOVED(objspace, ent->class_value);
- return ST_CONTINUE;
-}
-
static void
update_class_ext(rb_objspace_t *objspace, rb_classext_t *ext)
{
@@ -10361,11 +10576,6 @@ update_class_ext(rb_objspace_t *objspace, rb_classext_t *ext)
UPDATE_IF_MOVED(objspace, ext->includer);
UPDATE_IF_MOVED(objspace, ext->refined_class);
update_subclass_entries(objspace, ext->subclasses);
-
- // ext->iv_index_tbl
- if (ext->iv_index_tbl) {
- st_foreach(ext->iv_index_tbl, update_iv_index_tbl_i, (st_data_t)objspace);
- }
}
static void
@@ -10397,7 +10607,9 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
update_cvc_tbl(objspace, obj);
update_superclasses(objspace, obj);
- gc_update_tbl_refs(objspace, RCLASS_IV_TBL(obj));
+ for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) {
+ UPDATE_IF_MOVED(objspace, RCLASS_IVPTR(obj)[i]);
+ }
update_class_ext(objspace, RCLASS_EXT(obj));
update_const_tbl(objspace, RCLASS_CONST_TBL(obj));
@@ -10412,9 +10624,6 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
UPDATE_IF_MOVED(objspace, RCLASS(obj)->super);
}
if (!RCLASS_EXT(obj)) break;
- if (RCLASS_IV_TBL(obj)) {
- gc_update_tbl_refs(objspace, RCLASS_IV_TBL(obj));
- }
update_class_ext(objspace, RCLASS_EXT(obj));
update_m_tbl(objspace, RCLASS_CALLABLE_M_TBL(obj));
update_cc_tbl(objspace, obj);
@@ -10433,19 +10642,7 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
return;
case T_ARRAY:
- if (ARY_SHARED_P(obj)) {
- UPDATE_IF_MOVED(objspace, any->as.array.as.heap.aux.shared_root);
- }
- else {
- gc_ref_update_array(objspace, obj);
- }
-#if USE_RVARGC
- if ((size_t)GET_HEAP_PAGE(obj)->slot_size >= rb_ary_size_as_embedded(obj)) {
- if (rb_ary_embeddable_p(obj)) {
- rb_ary_make_embedded(obj);
- }
- }
-#endif
+ gc_ref_update_array(objspace, obj);
break;
case T_HASH:
@@ -10466,16 +10663,18 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
#if USE_RVARGC
VALUE new_root = any->as.string.as.heap.aux.shared;
rb_str_update_shared_ary(obj, old_root, new_root);
+#endif
+ }
- // if, after move the string is not embedded, and can fit in the
- // slot it's been placed in, then re-embed it
- if ((size_t)GET_HEAP_PAGE(obj)->slot_size >= rb_str_size_as_embedded(obj)) {
- if (!STR_EMBED_P(obj) && rb_str_reembeddable_p(obj)) {
- rb_str_make_embedded(obj);
- }
+#if USE_RVARGC
+ /* If, after move the string is not embedded, and can fit in the
+ * slot it's been placed in, then re-embed it. */
+ if (rb_gc_obj_slot_size(obj) >= rb_str_size_as_embedded(obj)) {
+ if (!STR_EMBED_P(obj) && rb_str_reembeddable_p(obj)) {
+ rb_str_make_embedded(obj);
}
-#endif
}
+#endif
break;
}
@@ -10569,8 +10768,8 @@ static int
gc_ref_update(void *vstart, void *vend, size_t stride, rb_objspace_t * objspace, struct heap_page *page)
{
VALUE v = (VALUE)vstart;
- asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
+ asan_unlock_freelist(page);
+ asan_lock_freelist(page);
page->flags.has_uncollectible_shady_objects = FALSE;
page->flags.has_remembered_objects = FALSE;
@@ -10651,9 +10850,9 @@ gc_update_references(rb_objspace_t *objspace)
#if GC_CAN_COMPILE_COMPACTION
/*
* call-seq:
- * GC.latest_compact_info -> {:considered=>{:T_CLASS=>11}, :moved=>{:T_CLASS=>11}}
+ * GC.latest_compact_info -> hash
*
- * Returns information about object moved in the most recent GC compaction.
+ * Returns information about object moved in the most recent \GC compaction.
*
* The returned hash has two keys :considered and :moved. The hash for
* :considered lists the number of objects that were considered for movement
@@ -10757,13 +10956,13 @@ heap_check_moved_i(void *vstart, void *vend, size_t stride, void *data)
* This function compacts objects together in Ruby's heap. It eliminates
* unused space (or fragmentation) in the heap by moving objects in to that
* unused space. This function returns a hash which contains statistics about
- * which objects were moved. See `GC.latest_gc_info` for details about
+ * which objects were moved. See <tt>GC.latest_gc_info</tt> for details about
* compaction statistics.
*
* This method is implementation specific and not expected to be implemented
* in any implementation besides MRI.
*
- * To test whether GC compaction is supported, use the idiom:
+ * To test whether \GC compaction is supported, use the idiom:
*
* GC.respond_to?(:compact)
*/
@@ -10884,7 +11083,7 @@ gc_count(rb_execution_context_t *ec, VALUE self)
static VALUE
gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned int orig_flags)
{
- static VALUE sym_major_by = Qnil, sym_gc_by, sym_immediate_sweep, sym_have_finalizer, sym_state;
+ static VALUE sym_major_by = Qnil, sym_gc_by, sym_immediate_sweep, sym_have_finalizer, sym_state, sym_need_major_by;
static VALUE sym_nofree, sym_oldgen, sym_shady, sym_force, sym_stress;
#if RGENGC_ESTIMATE_OLDMALLOC
static VALUE sym_oldmalloc;
@@ -10892,7 +11091,7 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned
static VALUE sym_newobj, sym_malloc, sym_method, sym_capi;
static VALUE sym_none, sym_marking, sym_sweeping;
VALUE hash = Qnil, key = Qnil;
- VALUE major_by;
+ VALUE major_by, need_major_by;
unsigned int flags = orig_flags ? orig_flags : objspace->profile.latest_gc_info;
if (SYMBOL_P(hash_or_key)) {
@@ -10912,6 +11111,7 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned
S(immediate_sweep);
S(have_finalizer);
S(state);
+ S(need_major_by);
S(stress);
S(nofree);
@@ -10949,6 +11149,20 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned
Qnil;
SET(major_by, major_by);
+ if (orig_flags == 0) { /* set need_major_by only if flags not set explicitly */
+ unsigned int need_major_flags = objspace->rgengc.need_major_gc;
+ need_major_by =
+ (need_major_flags & GPR_FLAG_MAJOR_BY_NOFREE) ? sym_nofree :
+ (need_major_flags & GPR_FLAG_MAJOR_BY_OLDGEN) ? sym_oldgen :
+ (need_major_flags & GPR_FLAG_MAJOR_BY_SHADY) ? sym_shady :
+ (need_major_flags & GPR_FLAG_MAJOR_BY_FORCE) ? sym_force :
+#if RGENGC_ESTIMATE_OLDMALLOC
+ (need_major_flags & GPR_FLAG_MAJOR_BY_OLDMALLOC) ? sym_oldmalloc :
+#endif
+ Qnil;
+ SET(need_major_by, need_major_by);
+ }
+
SET(gc_by,
(flags & GPR_FLAG_NEWOBJ) ? sym_newobj :
(flags & GPR_FLAG_MALLOC) ? sym_malloc :
@@ -11046,44 +11260,44 @@ setup_gc_stat_symbols(void)
{
if (gc_stat_symbols[0] == 0) {
#define S(s) gc_stat_symbols[gc_stat_sym_##s] = ID2SYM(rb_intern_const(#s))
- S(count);
+ S(count);
S(time);
- S(heap_allocated_pages);
- S(heap_sorted_length);
- S(heap_allocatable_pages);
- S(heap_available_slots);
- S(heap_live_slots);
- S(heap_free_slots);
- S(heap_final_slots);
- S(heap_marked_slots);
- S(heap_eden_pages);
- S(heap_tomb_pages);
- S(total_allocated_pages);
- S(total_freed_pages);
- S(total_allocated_objects);
- S(total_freed_objects);
- S(malloc_increase_bytes);
- S(malloc_increase_bytes_limit);
- S(minor_gc_count);
- S(major_gc_count);
- S(compact_count);
- S(read_barrier_faults);
- S(total_moved_objects);
- S(remembered_wb_unprotected_objects);
- S(remembered_wb_unprotected_objects_limit);
- S(old_objects);
- S(old_objects_limit);
+ S(heap_allocated_pages);
+ S(heap_sorted_length);
+ S(heap_allocatable_pages);
+ S(heap_available_slots);
+ S(heap_live_slots);
+ S(heap_free_slots);
+ S(heap_final_slots);
+ S(heap_marked_slots);
+ S(heap_eden_pages);
+ S(heap_tomb_pages);
+ S(total_allocated_pages);
+ S(total_freed_pages);
+ S(total_allocated_objects);
+ S(total_freed_objects);
+ S(malloc_increase_bytes);
+ S(malloc_increase_bytes_limit);
+ S(minor_gc_count);
+ S(major_gc_count);
+ S(compact_count);
+ S(read_barrier_faults);
+ S(total_moved_objects);
+ S(remembered_wb_unprotected_objects);
+ S(remembered_wb_unprotected_objects_limit);
+ S(old_objects);
+ S(old_objects_limit);
#if RGENGC_ESTIMATE_OLDMALLOC
- S(oldmalloc_increase_bytes);
- S(oldmalloc_increase_bytes_limit);
+ S(oldmalloc_increase_bytes);
+ S(oldmalloc_increase_bytes_limit);
#endif
#if RGENGC_PROFILE
- S(total_generated_normal_object_count);
- S(total_generated_shady_object_count);
- S(total_shade_operation_count);
- S(total_promoted_count);
- S(total_remembered_normal_object_count);
- S(total_remembered_shady_object_count);
+ S(total_generated_normal_object_count);
+ S(total_generated_shady_object_count);
+ S(total_shade_operation_count);
+ S(total_promoted_count);
+ S(total_remembered_normal_object_count);
+ S(total_remembered_shady_object_count);
#endif /* RGENGC_PROFILE */
#undef S
}
@@ -11098,20 +11312,20 @@ gc_stat_internal(VALUE hash_or_sym)
setup_gc_stat_symbols();
if (RB_TYPE_P(hash_or_sym, T_HASH)) {
- hash = hash_or_sym;
+ hash = hash_or_sym;
}
else if (SYMBOL_P(hash_or_sym)) {
- key = hash_or_sym;
+ key = hash_or_sym;
}
else {
- rb_raise(rb_eTypeError, "non-hash or symbol argument");
+ rb_raise(rb_eTypeError, "non-hash or symbol argument");
}
#define SET(name, attr) \
if (key == gc_stat_symbols[gc_stat_sym_##name]) \
- return attr; \
+ return attr; \
else if (hash != Qnil) \
- rb_hash_aset(hash, gc_stat_symbols[gc_stat_sym_##name], SIZET2NUM(attr));
+ rb_hash_aset(hash, gc_stat_symbols[gc_stat_sym_##name], SIZET2NUM(attr));
SET(count, objspace->profile.count);
SET(time, (size_t) (objspace->profile.total_time_ns / (1000 * 1000) /* ns -> ms */)); // TODO: UINT64T2NUM
@@ -11158,17 +11372,17 @@ gc_stat_internal(VALUE hash_or_sym)
#undef SET
if (!NIL_P(key)) { /* matched key should return above */
- rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key));
+ rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key));
}
#if defined(RGENGC_PROFILE) && RGENGC_PROFILE >= 2
if (hash != Qnil) {
- gc_count_add_each_types(hash, "generated_normal_object_count_types", objspace->profile.generated_normal_object_count_types);
- gc_count_add_each_types(hash, "generated_shady_object_count_types", objspace->profile.generated_shady_object_count_types);
- gc_count_add_each_types(hash, "shade_operation_count_types", objspace->profile.shade_operation_count_types);
- gc_count_add_each_types(hash, "promoted_types", objspace->profile.promoted_types);
- gc_count_add_each_types(hash, "remembered_normal_object_count_types", objspace->profile.remembered_normal_object_count_types);
- gc_count_add_each_types(hash, "remembered_shady_object_count_types", objspace->profile.remembered_shady_object_count_types);
+ gc_count_add_each_types(hash, "generated_normal_object_count_types", objspace->profile.generated_normal_object_count_types);
+ gc_count_add_each_types(hash, "generated_shady_object_count_types", objspace->profile.generated_shady_object_count_types);
+ gc_count_add_each_types(hash, "shade_operation_count_types", objspace->profile.shade_operation_count_types);
+ gc_count_add_each_types(hash, "promoted_types", objspace->profile.promoted_types);
+ gc_count_add_each_types(hash, "remembered_normal_object_count_types", objspace->profile.remembered_normal_object_count_types);
+ gc_count_add_each_types(hash, "remembered_shady_object_count_types", objspace->profile.remembered_shady_object_count_types);
}
#endif
@@ -11200,12 +11414,12 @@ size_t
rb_gc_stat(VALUE key)
{
if (SYMBOL_P(key)) {
- size_t value = gc_stat_internal(key);
- return value;
+ size_t value = gc_stat_internal(key);
+ return value;
}
else {
- gc_stat_internal(key);
- return 0;
+ gc_stat_internal(key);
+ return 0;
}
}
@@ -11465,53 +11679,53 @@ get_envparam_size(const char *name, size_t *default_value, size_t lower_bound)
ssize_t val;
if (ptr != NULL && *ptr) {
- size_t unit = 0;
- char *end;
+ size_t unit = 0;
+ char *end;
#if SIZEOF_SIZE_T == SIZEOF_LONG_LONG
- val = strtoll(ptr, &end, 0);
+ val = strtoll(ptr, &end, 0);
#else
- val = strtol(ptr, &end, 0);
-#endif
- switch (*end) {
- case 'k': case 'K':
- unit = 1024;
- ++end;
- break;
- case 'm': case 'M':
- unit = 1024*1024;
- ++end;
- break;
- case 'g': case 'G':
- unit = 1024*1024*1024;
- ++end;
- break;
- }
- while (*end && isspace((unsigned char)*end)) end++;
- if (*end) {
- if (RTEST(ruby_verbose)) fprintf(stderr, "invalid string for %s: %s\n", name, ptr);
- return 0;
- }
- if (unit > 0) {
- if (val < -(ssize_t)(SIZE_MAX / 2 / unit) || (ssize_t)(SIZE_MAX / 2 / unit) < val) {
- if (RTEST(ruby_verbose)) fprintf(stderr, "%s=%s is ignored because it overflows\n", name, ptr);
- return 0;
- }
- val *= unit;
- }
- if (val > 0 && (size_t)val > lower_bound) {
- if (RTEST(ruby_verbose)) {
- fprintf(stderr, "%s=%"PRIdSIZE" (default value: %"PRIuSIZE")\n", name, val, *default_value);
- }
- *default_value = (size_t)val;
- return 1;
- }
- else {
- if (RTEST(ruby_verbose)) {
- fprintf(stderr, "%s=%"PRIdSIZE" (default value: %"PRIuSIZE") is ignored because it must be greater than %"PRIuSIZE".\n",
- name, val, *default_value, lower_bound);
- }
- return 0;
- }
+ val = strtol(ptr, &end, 0);
+#endif
+ switch (*end) {
+ case 'k': case 'K':
+ unit = 1024;
+ ++end;
+ break;
+ case 'm': case 'M':
+ unit = 1024*1024;
+ ++end;
+ break;
+ case 'g': case 'G':
+ unit = 1024*1024*1024;
+ ++end;
+ break;
+ }
+ while (*end && isspace((unsigned char)*end)) end++;
+ if (*end) {
+ if (RTEST(ruby_verbose)) fprintf(stderr, "invalid string for %s: %s\n", name, ptr);
+ return 0;
+ }
+ if (unit > 0) {
+ if (val < -(ssize_t)(SIZE_MAX / 2 / unit) || (ssize_t)(SIZE_MAX / 2 / unit) < val) {
+ if (RTEST(ruby_verbose)) fprintf(stderr, "%s=%s is ignored because it overflows\n", name, ptr);
+ return 0;
+ }
+ val *= unit;
+ }
+ if (val > 0 && (size_t)val > lower_bound) {
+ if (RTEST(ruby_verbose)) {
+ fprintf(stderr, "%s=%"PRIdSIZE" (default value: %"PRIuSIZE")\n", name, val, *default_value);
+ }
+ *default_value = (size_t)val;
+ return 1;
+ }
+ else {
+ if (RTEST(ruby_verbose)) {
+ fprintf(stderr, "%s=%"PRIdSIZE" (default value: %"PRIuSIZE") is ignored because it must be greater than %"PRIuSIZE".\n",
+ name, val, *default_value, lower_bound);
+ }
+ return 0;
+ }
}
return 0;
}
@@ -11523,32 +11737,32 @@ get_envparam_double(const char *name, double *default_value, double lower_bound,
double val;
if (ptr != NULL && *ptr) {
- char *end;
- val = strtod(ptr, &end);
- if (!*ptr || *end) {
- if (RTEST(ruby_verbose)) fprintf(stderr, "invalid string for %s: %s\n", name, ptr);
- return 0;
- }
-
- if (accept_zero && val == 0.0) {
- goto accept;
- }
- else if (val <= lower_bound) {
- if (RTEST(ruby_verbose)) {
- fprintf(stderr, "%s=%f (default value: %f) is ignored because it must be greater than %f.\n",
- name, val, *default_value, lower_bound);
- }
- }
- else if (upper_bound != 0.0 && /* ignore upper_bound if it is 0.0 */
- val > upper_bound) {
- if (RTEST(ruby_verbose)) {
- fprintf(stderr, "%s=%f (default value: %f) is ignored because it must be lower than %f.\n",
- name, val, *default_value, upper_bound);
- }
- }
- else {
+ char *end;
+ val = strtod(ptr, &end);
+ if (!*ptr || *end) {
+ if (RTEST(ruby_verbose)) fprintf(stderr, "invalid string for %s: %s\n", name, ptr);
+ return 0;
+ }
+
+ if (accept_zero && val == 0.0) {
+ goto accept;
+ }
+ else if (val <= lower_bound) {
+ if (RTEST(ruby_verbose)) {
+ fprintf(stderr, "%s=%f (default value: %f) is ignored because it must be greater than %f.\n",
+ name, val, *default_value, lower_bound);
+ }
+ }
+ else if (upper_bound != 0.0 && /* ignore upper_bound if it is 0.0 */
+ val > upper_bound) {
+ if (RTEST(ruby_verbose)) {
+ fprintf(stderr, "%s=%f (default value: %f) is ignored because it must be lower than %f.\n",
+ name, val, *default_value, upper_bound);
+ }
+ }
+ else {
goto accept;
- }
+ }
}
return 0;
@@ -11559,24 +11773,25 @@ get_envparam_double(const char *name, double *default_value, double lower_bound,
}
static void
-gc_set_initial_pages(void)
+gc_set_initial_pages(rb_objspace_t *objspace)
{
- size_t min_pages;
- rb_objspace_t *objspace = &rb_objspace;
-
gc_rest(objspace);
- min_pages = gc_params.heap_init_slots / HEAP_PAGE_OBJ_LIMIT;
-
- size_t pages_per_class = (min_pages - heap_eden_total_pages(objspace)) / SIZE_POOL_COUNT;
-
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
rb_size_pool_t *size_pool = &size_pools[i];
- heap_add_pages(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool), pages_per_class);
+ if (gc_params.heap_init_slots > size_pool->eden_heap.total_slots) {
+ size_t slots = gc_params.heap_init_slots - size_pool->eden_heap.total_slots;
+ int multiple = size_pool->slot_size / BASE_SLOT_SIZE;
+ size_pool->allocatable_pages = slots * multiple / HEAP_PAGE_OBJ_LIMIT;
+ }
+ else {
+ /* We already have more slots than heap_init_slots allows, so
+ * prevent creating more pages. */
+ size_pool->allocatable_pages = 0;
+ }
}
-
- heap_add_pages(objspace, &size_pools[0], SIZE_POOL_EDEN_HEAP(&size_pools[0]), min_pages - heap_eden_total_pages(objspace));
+ heap_pages_expand_sorted(objspace);
}
/*
@@ -11627,26 +11842,26 @@ ruby_gc_set_params(void)
rb_objspace_t *objspace = &rb_objspace;
/* RUBY_GC_HEAP_FREE_SLOTS */
if (get_envparam_size("RUBY_GC_HEAP_FREE_SLOTS", &gc_params.heap_free_slots, 0)) {
- /* ok */
+ /* ok */
}
/* RUBY_GC_HEAP_INIT_SLOTS */
if (get_envparam_size("RUBY_GC_HEAP_INIT_SLOTS", &gc_params.heap_init_slots, 0)) {
- gc_set_initial_pages();
+ gc_set_initial_pages(objspace);
}
get_envparam_double("RUBY_GC_HEAP_GROWTH_FACTOR", &gc_params.growth_factor, 1.0, 0.0, FALSE);
get_envparam_size ("RUBY_GC_HEAP_GROWTH_MAX_SLOTS", &gc_params.growth_max_slots, 0);
get_envparam_double("RUBY_GC_HEAP_FREE_SLOTS_MIN_RATIO", &gc_params.heap_free_slots_min_ratio,
- 0.0, 1.0, FALSE);
+ 0.0, 1.0, FALSE);
get_envparam_double("RUBY_GC_HEAP_FREE_SLOTS_MAX_RATIO", &gc_params.heap_free_slots_max_ratio,
- gc_params.heap_free_slots_min_ratio, 1.0, FALSE);
+ gc_params.heap_free_slots_min_ratio, 1.0, FALSE);
get_envparam_double("RUBY_GC_HEAP_FREE_SLOTS_GOAL_RATIO", &gc_params.heap_free_slots_goal_ratio,
- gc_params.heap_free_slots_min_ratio, gc_params.heap_free_slots_max_ratio, TRUE);
+ gc_params.heap_free_slots_min_ratio, gc_params.heap_free_slots_max_ratio, TRUE);
get_envparam_double("RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR", &gc_params.oldobject_limit_factor, 0.0, 0.0, TRUE);
if (get_envparam_size("RUBY_GC_MALLOC_LIMIT", &gc_params.malloc_limit_min, 0)) {
- malloc_limit = gc_params.malloc_limit_min;
+ malloc_limit = gc_params.malloc_limit_min;
}
get_envparam_size ("RUBY_GC_MALLOC_LIMIT_MAX", &gc_params.malloc_limit_max, 0);
if (!gc_params.malloc_limit_max) { /* ignore max-check if 0 */
@@ -11656,7 +11871,7 @@ ruby_gc_set_params(void)
#if RGENGC_ESTIMATE_OLDMALLOC
if (get_envparam_size("RUBY_GC_OLDMALLOC_LIMIT", &gc_params.oldmalloc_limit_min, 0)) {
- objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min;
+ objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min;
}
get_envparam_size ("RUBY_GC_OLDMALLOC_LIMIT_MAX", &gc_params.oldmalloc_limit_max, 0);
get_envparam_double("RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR", &gc_params.oldmalloc_limit_growth_factor, 1.0, 0.0, FALSE);
@@ -11800,16 +12015,16 @@ static void
ruby_memerror(void)
{
if (ruby_thread_has_gvl_p()) {
- rb_memerror();
+ rb_memerror();
}
else {
- if (ruby_native_thread_p()) {
- rb_thread_call_with_gvl(ruby_memerror_body, 0);
- }
- else {
- /* no ruby thread */
- fprintf(stderr, "[FATAL] failed to allocate memory\n");
- }
+ if (ruby_native_thread_p()) {
+ rb_thread_call_with_gvl(ruby_memerror_body, 0);
+ }
+ else {
+ /* no ruby thread */
+ fprintf(stderr, "[FATAL] failed to allocate memory\n");
+ }
}
exit(EXIT_FAILURE);
}
@@ -11834,16 +12049,16 @@ rb_memerror(void)
exc = nomem_error;
if (!exc ||
- rb_ec_raised_p(ec, RAISED_NOMEMORY)) {
- fprintf(stderr, "[FATAL] failed to allocate memory\n");
- exit(EXIT_FAILURE);
+ rb_ec_raised_p(ec, RAISED_NOMEMORY)) {
+ fprintf(stderr, "[FATAL] failed to allocate memory\n");
+ exit(EXIT_FAILURE);
}
if (rb_ec_raised_p(ec, RAISED_NOMEMORY)) {
- rb_ec_raised_clear(ec);
+ rb_ec_raised_clear(ec);
}
else {
- rb_ec_raised_set(ec, RAISED_NOMEMORY);
- exc = ruby_vm_special_exception_copy(exc);
+ rb_ec_raised_set(ec, RAISED_NOMEMORY);
+ exc = ruby_vm_special_exception_copy(exc);
}
ec->errinfo = exc;
EC_JUMP_TAG(ec, TAG_RAISE);
@@ -11919,9 +12134,9 @@ atomic_sub_nounderflow(size_t *var, size_t sub)
if (sub == 0) return;
while (1) {
- size_t val = *var;
- if (val < sub) sub = val;
- if (ATOMIC_SIZE_CAS(*var, val, val-sub) == val) break;
+ size_t val = *var;
+ if (val < sub) sub = val;
+ if (ATOMIC_SIZE_CAS(*var, val, val-sub) == val) break;
}
}
@@ -11943,11 +12158,11 @@ static inline bool
objspace_malloc_increase_report(rb_objspace_t *objspace, void *mem, size_t new_size, size_t old_size, enum memop_type type)
{
if (0) fprintf(stderr, "increase - ptr: %p, type: %s, new_size: %"PRIdSIZE", old_size: %"PRIdSIZE"\n",
- mem,
- type == MEMOP_TYPE_MALLOC ? "malloc" :
- type == MEMOP_TYPE_FREE ? "free " :
- type == MEMOP_TYPE_REALLOC ? "realloc": "error",
- new_size, old_size);
+ mem,
+ type == MEMOP_TYPE_MALLOC ? "malloc" :
+ type == MEMOP_TYPE_FREE ? "free " :
+ type == MEMOP_TYPE_REALLOC ? "realloc": "error",
+ new_size, old_size);
return false;
}
@@ -11955,62 +12170,62 @@ static bool
objspace_malloc_increase_body(rb_objspace_t *objspace, void *mem, size_t new_size, size_t old_size, enum memop_type type)
{
if (new_size > old_size) {
- ATOMIC_SIZE_ADD(malloc_increase, new_size - old_size);
+ ATOMIC_SIZE_ADD(malloc_increase, new_size - old_size);
#if RGENGC_ESTIMATE_OLDMALLOC
- ATOMIC_SIZE_ADD(objspace->rgengc.oldmalloc_increase, new_size - old_size);
+ ATOMIC_SIZE_ADD(objspace->rgengc.oldmalloc_increase, new_size - old_size);
#endif
}
else {
- atomic_sub_nounderflow(&malloc_increase, old_size - new_size);
+ atomic_sub_nounderflow(&malloc_increase, old_size - new_size);
#if RGENGC_ESTIMATE_OLDMALLOC
- atomic_sub_nounderflow(&objspace->rgengc.oldmalloc_increase, old_size - new_size);
+ atomic_sub_nounderflow(&objspace->rgengc.oldmalloc_increase, old_size - new_size);
#endif
}
if (type == MEMOP_TYPE_MALLOC) {
retry:
- if (malloc_increase > malloc_limit && ruby_native_thread_p() && !dont_gc_val()) {
- if (ruby_thread_has_gvl_p() && is_lazy_sweeping(objspace)) {
- gc_rest(objspace); /* gc_rest can reduce malloc_increase */
- goto retry;
- }
- garbage_collect_with_gvl(objspace, GPR_FLAG_MALLOC);
- }
+ if (malloc_increase > malloc_limit && ruby_native_thread_p() && !dont_gc_val()) {
+ if (ruby_thread_has_gvl_p() && is_lazy_sweeping(objspace)) {
+ gc_rest(objspace); /* gc_rest can reduce malloc_increase */
+ goto retry;
+ }
+ garbage_collect_with_gvl(objspace, GPR_FLAG_MALLOC);
+ }
}
#if MALLOC_ALLOCATED_SIZE
if (new_size >= old_size) {
- ATOMIC_SIZE_ADD(objspace->malloc_params.allocated_size, new_size - old_size);
+ ATOMIC_SIZE_ADD(objspace->malloc_params.allocated_size, new_size - old_size);
}
else {
- size_t dec_size = old_size - new_size;
- size_t allocated_size = objspace->malloc_params.allocated_size;
+ size_t dec_size = old_size - new_size;
+ size_t allocated_size = objspace->malloc_params.allocated_size;
#if MALLOC_ALLOCATED_SIZE_CHECK
- if (allocated_size < dec_size) {
- rb_bug("objspace_malloc_increase: underflow malloc_params.allocated_size.");
- }
+ if (allocated_size < dec_size) {
+ rb_bug("objspace_malloc_increase: underflow malloc_params.allocated_size.");
+ }
#endif
- atomic_sub_nounderflow(&objspace->malloc_params.allocated_size, dec_size);
+ atomic_sub_nounderflow(&objspace->malloc_params.allocated_size, dec_size);
}
switch (type) {
case MEMOP_TYPE_MALLOC:
- ATOMIC_SIZE_INC(objspace->malloc_params.allocations);
- break;
+ ATOMIC_SIZE_INC(objspace->malloc_params.allocations);
+ break;
case MEMOP_TYPE_FREE:
- {
- size_t allocations = objspace->malloc_params.allocations;
- if (allocations > 0) {
- atomic_sub_nounderflow(&objspace->malloc_params.allocations, 1);
- }
+ {
+ size_t allocations = objspace->malloc_params.allocations;
+ if (allocations > 0) {
+ atomic_sub_nounderflow(&objspace->malloc_params.allocations, 1);
+ }
#if MALLOC_ALLOCATED_SIZE_CHECK
- else {
- GC_ASSERT(objspace->malloc_params.allocations > 0);
- }
+ else {
+ GC_ASSERT(objspace->malloc_params.allocations > 0);
+ }
#endif
- }
- break;
+ }
+ break;
case MEMOP_TYPE_REALLOC: /* ignore */ break;
}
#endif
@@ -12019,8 +12234,8 @@ objspace_malloc_increase_body(rb_objspace_t *objspace, void *mem, size_t new_siz
#define objspace_malloc_increase(...) \
for (bool malloc_increase_done = objspace_malloc_increase_report(__VA_ARGS__); \
- !malloc_increase_done; \
- malloc_increase_done = objspace_malloc_increase_body(__VA_ARGS__))
+ !malloc_increase_done; \
+ malloc_increase_done = objspace_malloc_increase_body(__VA_ARGS__))
struct malloc_obj_info { /* 4 words */
size_t size;
@@ -12048,6 +12263,16 @@ objspace_malloc_prepare(rb_objspace_t *objspace, size_t size)
return size;
}
+static bool
+malloc_during_gc_p(rb_objspace_t *objspace)
+{
+ /* malloc is not allowed during GC when we're not using multiple ractors
+ * (since ractors can run while another thread is sweeping) and when we
+ * have the GVL (since if we don't have the GVL, we'll try to acquire the
+ * GVL which will block and ensure the other thread finishes GC). */
+ return during_gc && !rb_multi_ractor_p() && ruby_thread_has_gvl_p();
+}
+
static inline void *
objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
{
@@ -12071,10 +12296,16 @@ objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
}
#if defined(__GNUC__) && RUBY_DEBUG
-#define RB_BUG_INSTEAD_OF_RB_MEMERROR
+#define RB_BUG_INSTEAD_OF_RB_MEMERROR 1
+#endif
+
+#ifndef RB_BUG_INSTEAD_OF_RB_MEMERROR
+# define RB_BUG_INSTEAD_OF_RB_MEMERROR 0
#endif
-#ifdef RB_BUG_INSTEAD_OF_RB_MEMERROR
+#define GC_MEMERROR(...) \
+ ((RB_BUG_INSTEAD_OF_RB_MEMERROR+0) ? rb_bug("" __VA_ARGS__) : rb_memerror())
+
#define TRY_WITH_GC(siz, expr) do { \
const gc_profile_record_flag gpr = \
GPR_FLAG_FULL_MARK | \
@@ -12088,29 +12319,27 @@ objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
} \
else if (!garbage_collect_with_gvl(objspace, gpr)) { \
/* @shyouhei thinks this doesn't happen */ \
- rb_bug("TRY_WITH_GC: could not GC"); \
+ GC_MEMERROR("TRY_WITH_GC: could not GC"); \
} \
else if ((expr)) { \
/* Success on 2nd try */ \
} \
else { \
- rb_bug("TRY_WITH_GC: could not allocate:" \
- "%"PRIdSIZE" bytes for %s", \
- siz, # expr); \
+ GC_MEMERROR("TRY_WITH_GC: could not allocate:" \
+ "%"PRIdSIZE" bytes for %s", \
+ siz, # expr); \
} \
} while (0)
-#else
-#define TRY_WITH_GC(siz, alloc) do { \
- objspace_malloc_gc_stress(objspace); \
- if (!(alloc) && \
- (!garbage_collect_with_gvl(objspace, GPR_FLAG_FULL_MARK | \
- GPR_FLAG_IMMEDIATE_MARK | GPR_FLAG_IMMEDIATE_SWEEP | \
- GPR_FLAG_MALLOC) || \
- !(alloc))) { \
- ruby_memerror(); \
- } \
- } while (0)
-#endif
+
+static void
+check_malloc_not_in_gc(rb_objspace_t *objspace, const char *msg)
+{
+ if (UNLIKELY(malloc_during_gc_p(objspace))) {
+ dont_gc_on();
+ during_gc = false;
+ rb_bug("Cannot %s during GC", msg);
+ }
+}
/* these shouldn't be called directly.
* objspace_* functions do not check allocation size.
@@ -12118,6 +12347,8 @@ objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
static void *
objspace_xmalloc0(rb_objspace_t *objspace, size_t size)
{
+ check_malloc_not_in_gc(objspace, "malloc");
+
void *mem;
size = objspace_malloc_prepare(objspace, size);
@@ -12135,6 +12366,8 @@ xmalloc2_size(const size_t count, const size_t elsize)
static void *
objspace_xrealloc(rb_objspace_t *objspace, void *ptr, size_t new_size, size_t old_size)
{
+ check_malloc_not_in_gc(objspace, "realloc");
+
void *mem;
if (!ptr) return objspace_xmalloc0(objspace, new_size);
@@ -12192,7 +12425,7 @@ objspace_xrealloc(rb_objspace_t *objspace, void *ptr, size_t new_size, size_t ol
#endif
old_size = objspace_malloc_size(objspace, ptr, old_size);
- TRY_WITH_GC(new_size, mem = realloc(ptr, new_size));
+ TRY_WITH_GC(new_size, mem = RB_GNUC_EXTENSION_BLOCK(realloc(ptr, new_size)));
new_size = objspace_malloc_size(objspace, mem, new_size);
#if CALC_EXACT_MALLOC_SIZE
@@ -12334,8 +12567,9 @@ objspace_xfree(rb_objspace_t *objspace, void *ptr, size_t old_size)
old_size = objspace_malloc_size(objspace, ptr, old_size);
objspace_malloc_increase(objspace, ptr, 0, old_size, MEMOP_TYPE_FREE) {
- free(ptr);
- RB_DEBUG_COUNTER_INC(heap_xfree);
+ free(ptr);
+ ptr = NULL;
+ RB_DEBUG_COUNTER_INC(heap_xfree);
}
}
@@ -12349,7 +12583,7 @@ void *
ruby_xmalloc_body(size_t size)
{
if ((ssize_t)size < 0) {
- negative_size_allocation_error("too large allocation size");
+ negative_size_allocation_error("too large allocation size");
}
return ruby_xmalloc0(size);
}
@@ -12358,8 +12592,8 @@ void
ruby_malloc_size_overflow(size_t count, size_t elsize)
{
rb_raise(rb_eArgError,
- "malloc: possible integer overflow (%"PRIuSIZE"*%"PRIuSIZE")",
- count, elsize);
+ "malloc: possible integer overflow (%"PRIuSIZE"*%"PRIuSIZE")",
+ count, elsize);
}
void *
@@ -12371,6 +12605,13 @@ ruby_xmalloc2_body(size_t n, size_t size)
static void *
objspace_xcalloc(rb_objspace_t *objspace, size_t size)
{
+ if (UNLIKELY(malloc_during_gc_p(objspace))) {
+ rb_warn("calloc during GC detected, this could cause crashes if it triggers another GC");
+#if RGENGC_CHECK_MODE || RUBY_DEBUG
+ rb_bug("Cannot calloc during GC");
+#endif
+ }
+
void *mem;
size = objspace_malloc_prepare(objspace, size);
@@ -12391,7 +12632,7 @@ void *
ruby_sized_xrealloc(void *ptr, size_t new_size, size_t old_size)
{
if ((ssize_t)new_size < 0) {
- negative_size_allocation_error("too large allocation size");
+ negative_size_allocation_error("too large allocation size");
}
return objspace_xrealloc(&rb_objspace, ptr, new_size, old_size);
@@ -12425,8 +12666,16 @@ ruby_xrealloc2_body(void *ptr, size_t n, size_t size)
void
ruby_sized_xfree(void *x, size_t size)
{
- if (x) {
- objspace_xfree(&rb_objspace, x, size);
+ if (LIKELY(x)) {
+ /* It's possible for a C extension's pthread destructor function set by pthread_key_create
+ * to be called after ruby_vm_destruct and attempt to free memory. Fall back to mimfree in
+ * that case. */
+ if (LIKELY(GET_VM())) {
+ objspace_xfree(&rb_objspace, x, size);
+ }
+ else {
+ ruby_mimfree(x);
+ }
}
}
@@ -12536,7 +12785,7 @@ rb_alloc_tmp_buffer(volatile VALUE *store, long len)
long cnt;
if (len < 0 || (cnt = (long)roomof(len, sizeof(VALUE))) < 0) {
- rb_raise(rb_eArgError, "negative buffer size (or size too big)");
+ rb_raise(rb_eArgError, "negative buffer size (or size too big)");
}
return rb_alloc_tmp_buffer_with_count(store, len, cnt);
@@ -12547,9 +12796,9 @@ rb_free_tmp_buffer(volatile VALUE *store)
{
rb_imemo_tmpbuf_t *s = (rb_imemo_tmpbuf_t*)ATOMIC_VALUE_EXCHANGE(*store, 0);
if (s) {
- void *ptr = ATOMIC_PTR_EXCHANGE(s->ptr, 0);
- s->cnt = 0;
- ruby_xfree(ptr);
+ void *ptr = ATOMIC_PTR_EXCHANGE(s->ptr, 0);
+ s->cnt = 0;
+ ruby_xfree(ptr);
}
}
@@ -12590,10 +12839,10 @@ rb_gc_adjust_memory_usage(ssize_t diff)
{
rb_objspace_t *objspace = &rb_objspace;
if (diff > 0) {
- objspace_malloc_increase(objspace, 0, diff, 0, MEMOP_TYPE_REALLOC);
+ objspace_malloc_increase(objspace, 0, diff, 0, MEMOP_TYPE_REALLOC);
}
else if (diff < 0) {
- objspace_malloc_increase(objspace, 0, 0, -diff, MEMOP_TYPE_REALLOC);
+ objspace_malloc_increase(objspace, 0, 0, -diff, MEMOP_TYPE_REALLOC);
}
}
@@ -12620,12 +12869,47 @@ wmap_mark_map(st_data_t key, st_data_t val, st_data_t arg)
}
#endif
+static int
+wmap_replace_ref(st_data_t *key, st_data_t *value, st_data_t _argp, int existing)
+{
+ *key = rb_gc_location((VALUE)*key);
+
+ VALUE *values = (VALUE *)*value;
+ VALUE size = values[0];
+
+ for (VALUE index = 1; index <= size; index++) {
+ values[index] = rb_gc_location(values[index]);
+ }
+
+ return ST_CONTINUE;
+}
+
+static int
+wmap_foreach_replace(st_data_t key, st_data_t value, st_data_t _argp, int error)
+{
+ if (rb_gc_location((VALUE)key) != (VALUE)key) {
+ return ST_REPLACE;
+ }
+
+ VALUE *values = (VALUE *)value;
+ VALUE size = values[0];
+
+ for (VALUE index = 1; index <= size; index++) {
+ VALUE val = values[index];
+ if (rb_gc_location(val) != val) {
+ return ST_REPLACE;
+ }
+ }
+
+ return ST_CONTINUE;
+}
+
static void
wmap_compact(void *ptr)
{
struct weakmap *w = ptr;
if (w->wmap2obj) rb_gc_update_tbl_refs(w->wmap2obj);
- if (w->obj2wmap) rb_gc_update_tbl_refs(w->obj2wmap);
+ if (w->obj2wmap) st_foreach_with_replace(w->obj2wmap, wmap_foreach_replace, wmap_replace_ref, (st_data_t)NULL);
w->final = rb_gc_location(w->final);
}
@@ -12654,6 +12938,7 @@ wmap_free(void *ptr)
st_foreach(w->obj2wmap, wmap_free_map, 0);
st_free_table(w->obj2wmap);
st_free_table(w->wmap2obj);
+ xfree(w);
}
static int
@@ -12679,9 +12964,9 @@ wmap_memsize(const void *ptr)
static const rb_data_type_t weakmap_type = {
"weakmap",
{
- wmap_mark,
- wmap_free,
- wmap_memsize,
+ wmap_mark,
+ wmap_free,
+ wmap_memsize,
wmap_compact,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
@@ -12722,25 +13007,40 @@ wmap_live_p(rb_objspace_t *objspace, VALUE obj)
}
static int
-wmap_final_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
+wmap_remove_inverse_ref(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
{
- VALUE wmap, *ptr, size, i, j;
if (!existing) return ST_STOP;
- wmap = (VALUE)arg, ptr = (VALUE *)*value;
- for (i = j = 1, size = ptr[0]; i <= size; ++i) {
- if (ptr[i] != wmap) {
- ptr[j++] = ptr[i];
- }
+
+ VALUE old_ref = (VALUE)arg;
+
+ VALUE *values = (VALUE *)*val;
+ VALUE size = values[0];
+
+ if (size == 1) {
+ // fast path, we only had one backref
+ RUBY_ASSERT(values[1] == old_ref);
+ ruby_sized_xfree(values, 2 * sizeof(VALUE));
+ return ST_DELETE;
}
- if (j == 1) {
- ruby_sized_xfree(ptr, i * sizeof(VALUE));
- return ST_DELETE;
+
+ bool found = false;
+ VALUE index = 1;
+ for (; index <= size; index++) {
+ if (values[index] == old_ref) {
+ found = true;
+ break;
+ }
}
- if (j < i) {
- SIZED_REALLOC_N(ptr, VALUE, j + 1, i);
- ptr[0] = j;
- *value = (st_data_t)ptr;
+ if (!found) return ST_STOP;
+
+ if (size > index) {
+ MEMMOVE(&values[index], &values[index + 1], VALUE, size - index);
}
+
+ size -= 1;
+ values[0] = size;
+ SIZED_REALLOC_N(values, VALUE, size + 1, size + 2);
+ *val = (st_data_t)values;
return ST_CONTINUE;
}
@@ -12754,26 +13054,26 @@ wmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self))
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
/* Get reference from object id. */
- if ((obj = id2ref_obj_tbl(&rb_objspace, objid)) == Qundef) {
+ if (UNDEF_P(obj = id2ref_obj_tbl(&rb_objspace, objid))) {
rb_bug("wmap_finalize: objid is not found.");
}
/* obj is original referenced object and/or weak reference. */
orig = (st_data_t)obj;
if (st_delete(w->obj2wmap, &orig, &data)) {
- rids = (VALUE *)data;
- size = *rids++;
- for (i = 0; i < size; ++i) {
- wmap = (st_data_t)rids[i];
- st_delete(w->wmap2obj, &wmap, NULL);
- }
- ruby_sized_xfree((VALUE *)data, (size + 1) * sizeof(VALUE));
+ rids = (VALUE *)data;
+ size = *rids++;
+ for (i = 0; i < size; ++i) {
+ wmap = (st_data_t)rids[i];
+ st_delete(w->wmap2obj, &wmap, NULL);
+ }
+ ruby_sized_xfree((VALUE *)data, (size + 1) * sizeof(VALUE));
}
wmap = (st_data_t)obj;
if (st_delete(w->wmap2obj, &wmap, &orig)) {
- wmap = (st_data_t)obj;
- st_update(w->obj2wmap, orig, wmap_final_func, wmap);
+ wmap = (st_data_t)obj;
+ st_update(w->obj2wmap, orig, wmap_remove_inverse_ref, wmap);
}
return self;
}
@@ -12806,11 +13106,11 @@ wmap_inspect_i(st_data_t key, st_data_t val, st_data_t arg)
VALUE k = (VALUE)key, v = (VALUE)val;
if (RSTRING_PTR(str)[0] == '#') {
- rb_str_cat2(str, ", ");
+ rb_str_cat2(str, ", ");
}
else {
- rb_str_cat2(str, ": ");
- RSTRING_PTR(str)[0] = '#';
+ rb_str_cat2(str, ": ");
+ RSTRING_PTR(str)[0] = '#';
}
wmap_inspect_append(objspace, str, k);
rb_str_cat2(str, " => ");
@@ -12830,9 +13130,9 @@ wmap_inspect(VALUE self)
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
str = rb_sprintf("-<%"PRIsVALUE":%p", c, (void *)self);
if (w->wmap2obj) {
- args.objspace = &rb_objspace;
- args.value = str;
- st_foreach(w->wmap2obj, wmap_inspect_i, (st_data_t)&args);
+ args.objspace = &rb_objspace;
+ args.value = str;
+ st_foreach(w->wmap2obj, wmap_inspect_i, (st_data_t)&args);
}
RSTRING_PTR(str)[0] = '#';
rb_str_cat2(str, ">");
@@ -12988,14 +13288,22 @@ wmap_aset_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
{
VALUE size, *ptr, *optr;
if (existing) {
- size = (ptr = optr = (VALUE *)*val)[0];
- ++size;
+ size = (ptr = optr = (VALUE *)*val)[0];
+
+ for (VALUE index = 1; index <= size; index++) {
+ if (ptr[index] == (VALUE)arg) {
+ // The reference was already registered.
+ return ST_STOP;
+ }
+ }
+
+ ++size;
SIZED_REALLOC_N(ptr, VALUE, size + 1, size);
}
else {
- optr = 0;
- size = 1;
- ptr = ruby_xmalloc0(2 * sizeof(VALUE));
+ optr = 0;
+ size = 1;
+ ptr = ruby_xmalloc0(2 * sizeof(VALUE));
}
ptr[0] = size;
ptr[size] = (VALUE)arg;
@@ -13004,6 +13312,23 @@ wmap_aset_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
return ST_CONTINUE;
}
+struct wmap_aset_replace_args {
+ VALUE new_value;
+ VALUE old_value;
+};
+
+static int
+wmap_aset_replace_value(st_data_t *key, st_data_t *val, st_data_t _args, int existing)
+{
+ struct wmap_aset_replace_args *args = (struct wmap_aset_replace_args *)_args;
+
+ if (existing) {
+ args->old_value = *val;
+ }
+ *val = (st_data_t)args->new_value;
+ return ST_CONTINUE;
+}
+
/* Creates a weak reference from the given key to the given value */
static VALUE
wmap_aset(VALUE self, VALUE key, VALUE value)
@@ -13018,8 +13343,25 @@ wmap_aset(VALUE self, VALUE key, VALUE value)
define_final0(key, w->final);
}
- st_update(w->obj2wmap, (st_data_t)value, wmap_aset_update, key);
- st_insert(w->wmap2obj, (st_data_t)key, (st_data_t)value);
+ struct wmap_aset_replace_args aset_args = {
+ .new_value = value,
+ .old_value = Qundef,
+ };
+ st_update(w->wmap2obj, (st_data_t)key, wmap_aset_replace_value, (st_data_t)&aset_args);
+
+ // If the value is unchanged, we have nothing to do.
+ if (value != aset_args.old_value) {
+ if (!UNDEF_P(aset_args.old_value) && FL_ABLE(aset_args.old_value)) {
+ // That key existed and had an inverse reference, we need to clear the outdated inverse reference.
+ st_update(w->obj2wmap, (st_data_t)aset_args.old_value, wmap_remove_inverse_ref, key);
+ }
+
+ if (FL_ABLE(value)) {
+ // If the value has no finalizer, we don't need to keep the inverse reference
+ st_update(w->obj2wmap, (st_data_t)value, wmap_aset_update, key);
+ }
+ }
+
return nonspecial_obj_id(value);
}
@@ -13045,14 +13387,14 @@ static VALUE
wmap_aref(VALUE self, VALUE key)
{
VALUE obj = wmap_lookup(self, key);
- return obj != Qundef ? obj : Qnil;
+ return !UNDEF_P(obj) ? obj : Qnil;
}
/* Returns +true+ if +key+ is registered */
static VALUE
wmap_has_key(VALUE self, VALUE key)
{
- return RBOOL(wmap_lookup(self, key) != Qundef);
+ return RBOOL(!UNDEF_P(wmap_lookup(self, key)));
}
/* Returns the number of referenced objects */
@@ -13107,17 +13449,17 @@ current_process_time(struct timespec *ts)
#ifdef _WIN32
{
- FILETIME creation_time, exit_time, kernel_time, user_time;
+ FILETIME creation_time, exit_time, kernel_time, user_time;
ULARGE_INTEGER ui;
if (GetProcessTimes(GetCurrentProcess(),
- &creation_time, &exit_time, &kernel_time, &user_time) != 0) {
+ &creation_time, &exit_time, &kernel_time, &user_time) != 0) {
memcpy(&ui, &user_time, sizeof(FILETIME));
#define PER100NSEC (uint64_t)(1000 * 1000 * 10)
ts->tv_nsec = (long)(ui.QuadPart % PER100NSEC);
ts->tv_sec = (time_t)(ui.QuadPart / PER100NSEC);
return true;
- }
+ }
}
#endif
@@ -13141,44 +13483,44 @@ static inline void
gc_prof_setup_new_record(rb_objspace_t *objspace, unsigned int reason)
{
if (objspace->profile.run) {
- size_t index = objspace->profile.next_index;
- gc_profile_record *record;
-
- /* create new record */
- objspace->profile.next_index++;
-
- if (!objspace->profile.records) {
- objspace->profile.size = GC_PROFILE_RECORD_DEFAULT_SIZE;
- objspace->profile.records = malloc(xmalloc2_size(sizeof(gc_profile_record), objspace->profile.size));
- }
- if (index >= objspace->profile.size) {
- void *ptr;
- objspace->profile.size += 1000;
- ptr = realloc(objspace->profile.records, xmalloc2_size(sizeof(gc_profile_record), objspace->profile.size));
- if (!ptr) rb_memerror();
- objspace->profile.records = ptr;
- }
- if (!objspace->profile.records) {
- rb_bug("gc_profile malloc or realloc miss");
- }
- record = objspace->profile.current_record = &objspace->profile.records[objspace->profile.next_index - 1];
- MEMZERO(record, gc_profile_record, 1);
-
- /* setup before-GC parameter */
- record->flags = reason | (ruby_gc_stressful ? GPR_FLAG_STRESS : 0);
+ size_t index = objspace->profile.next_index;
+ gc_profile_record *record;
+
+ /* create new record */
+ objspace->profile.next_index++;
+
+ if (!objspace->profile.records) {
+ objspace->profile.size = GC_PROFILE_RECORD_DEFAULT_SIZE;
+ objspace->profile.records = malloc(xmalloc2_size(sizeof(gc_profile_record), objspace->profile.size));
+ }
+ if (index >= objspace->profile.size) {
+ void *ptr;
+ objspace->profile.size += 1000;
+ ptr = realloc(objspace->profile.records, xmalloc2_size(sizeof(gc_profile_record), objspace->profile.size));
+ if (!ptr) rb_memerror();
+ objspace->profile.records = ptr;
+ }
+ if (!objspace->profile.records) {
+ rb_bug("gc_profile malloc or realloc miss");
+ }
+ record = objspace->profile.current_record = &objspace->profile.records[objspace->profile.next_index - 1];
+ MEMZERO(record, gc_profile_record, 1);
+
+ /* setup before-GC parameter */
+ record->flags = reason | (ruby_gc_stressful ? GPR_FLAG_STRESS : 0);
#if MALLOC_ALLOCATED_SIZE
- record->allocated_size = malloc_allocated_size;
+ record->allocated_size = malloc_allocated_size;
#endif
#if GC_PROFILE_MORE_DETAIL && GC_PROFILE_DETAIL_MEMORY
#ifdef RUSAGE_SELF
- {
- struct rusage usage;
- if (getrusage(RUSAGE_SELF, &usage) == 0) {
- record->maxrss = usage.ru_maxrss;
- record->minflt = usage.ru_minflt;
- record->majflt = usage.ru_majflt;
- }
- }
+ {
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage) == 0) {
+ record->maxrss = usage.ru_maxrss;
+ record->minflt = usage.ru_minflt;
+ record->majflt = usage.ru_majflt;
+ }
+ }
#endif
#endif
}
@@ -13188,12 +13530,12 @@ static inline void
gc_prof_timer_start(rb_objspace_t *objspace)
{
if (gc_prof_enabled(objspace)) {
- gc_profile_record *record = gc_prof_record(objspace);
+ gc_profile_record *record = gc_prof_record(objspace);
#if GC_PROFILE_MORE_DETAIL
- record->prepare_time = objspace->profile.prepare_time;
+ record->prepare_time = objspace->profile.prepare_time;
#endif
- record->gc_time = 0;
- record->gc_invoke_time = getrusage_time();
+ record->gc_time = 0;
+ record->gc_invoke_time = getrusage_time();
}
}
@@ -13202,10 +13544,10 @@ elapsed_time_from(double time)
{
double now = getrusage_time();
if (now > time) {
- return now - time;
+ return now - time;
}
else {
- return 0;
+ return 0;
}
}
@@ -13213,9 +13555,9 @@ static inline void
gc_prof_timer_stop(rb_objspace_t *objspace)
{
if (gc_prof_enabled(objspace)) {
- gc_profile_record *record = gc_prof_record(objspace);
- record->gc_time = elapsed_time_from(record->gc_invoke_time);
- record->gc_invoke_time -= objspace->profile.invoke_time;
+ gc_profile_record *record = gc_prof_record(objspace);
+ record->gc_time = elapsed_time_from(record->gc_invoke_time);
+ record->gc_invoke_time -= objspace->profile.invoke_time;
}
}
@@ -13227,7 +13569,7 @@ gc_prof_mark_timer_start(rb_objspace_t *objspace)
RUBY_DTRACE_GC_HOOK(MARK_BEGIN);
#if GC_PROFILE_MORE_DETAIL
if (gc_prof_enabled(objspace)) {
- gc_prof_record(objspace)->gc_mark_time = getrusage_time();
+ gc_prof_record(objspace)->gc_mark_time = getrusage_time();
}
#endif
}
@@ -13239,7 +13581,7 @@ gc_prof_mark_timer_stop(rb_objspace_t *objspace)
#if GC_PROFILE_MORE_DETAIL
if (gc_prof_enabled(objspace)) {
gc_profile_record *record = gc_prof_record(objspace);
- record->gc_mark_time = elapsed_time_from(record->gc_mark_time);
+ record->gc_mark_time = elapsed_time_from(record->gc_mark_time);
}
#endif
}
@@ -13249,11 +13591,11 @@ gc_prof_sweep_timer_start(rb_objspace_t *objspace)
{
RUBY_DTRACE_GC_HOOK(SWEEP_BEGIN);
if (gc_prof_enabled(objspace)) {
- gc_profile_record *record = gc_prof_record(objspace);
+ gc_profile_record *record = gc_prof_record(objspace);
- if (record->gc_time > 0 || GC_PROFILE_MORE_DETAIL) {
- objspace->profile.gc_sweep_start_time = getrusage_time();
- }
+ if (record->gc_time > 0 || GC_PROFILE_MORE_DETAIL) {
+ objspace->profile.gc_sweep_start_time = getrusage_time();
+ }
}
}
@@ -13263,23 +13605,23 @@ gc_prof_sweep_timer_stop(rb_objspace_t *objspace)
RUBY_DTRACE_GC_HOOK(SWEEP_END);
if (gc_prof_enabled(objspace)) {
- double sweep_time;
- gc_profile_record *record = gc_prof_record(objspace);
-
- if (record->gc_time > 0) {
- sweep_time = elapsed_time_from(objspace->profile.gc_sweep_start_time);
- /* need to accumulate GC time for lazy sweep after gc() */
- record->gc_time += sweep_time;
- }
- else if (GC_PROFILE_MORE_DETAIL) {
- sweep_time = elapsed_time_from(objspace->profile.gc_sweep_start_time);
- }
+ double sweep_time;
+ gc_profile_record *record = gc_prof_record(objspace);
+
+ if (record->gc_time > 0) {
+ sweep_time = elapsed_time_from(objspace->profile.gc_sweep_start_time);
+ /* need to accumulate GC time for lazy sweep after gc() */
+ record->gc_time += sweep_time;
+ }
+ else if (GC_PROFILE_MORE_DETAIL) {
+ sweep_time = elapsed_time_from(objspace->profile.gc_sweep_start_time);
+ }
#if GC_PROFILE_MORE_DETAIL
- record->gc_sweep_time += sweep_time;
- if (heap_pages_deferred_final) record->flags |= GPR_FLAG_HAVE_FINALIZE;
+ record->gc_sweep_time += sweep_time;
+ if (heap_pages_deferred_final) record->flags |= GPR_FLAG_HAVE_FINALIZE;
#endif
- if (heap_pages_deferred_final) objspace->profile.latest_gc_info |= GPR_FLAG_HAVE_FINALIZE;
+ if (heap_pages_deferred_final) objspace->profile.latest_gc_info |= GPR_FLAG_HAVE_FINALIZE;
}
}
@@ -13289,8 +13631,8 @@ gc_prof_set_malloc_info(rb_objspace_t *objspace)
#if GC_PROFILE_MORE_DETAIL
if (gc_prof_enabled(objspace)) {
gc_profile_record *record = gc_prof_record(objspace);
- record->allocate_increase = malloc_increase;
- record->allocate_limit = malloc_limit;
+ record->allocate_increase = malloc_increase;
+ record->allocate_limit = malloc_limit;
}
#endif
}
@@ -13299,19 +13641,19 @@ static inline void
gc_prof_set_heap_info(rb_objspace_t *objspace)
{
if (gc_prof_enabled(objspace)) {
- gc_profile_record *record = gc_prof_record(objspace);
- size_t live = objspace->profile.total_allocated_objects_at_gc_start - objspace->profile.total_freed_objects;
- size_t total = objspace->profile.heap_used_at_gc_start * HEAP_PAGE_OBJ_LIMIT;
+ gc_profile_record *record = gc_prof_record(objspace);
+ size_t live = objspace->profile.total_allocated_objects_at_gc_start - objspace->profile.total_freed_objects;
+ size_t total = objspace->profile.heap_used_at_gc_start * HEAP_PAGE_OBJ_LIMIT;
#if GC_PROFILE_MORE_DETAIL
- record->heap_use_pages = objspace->profile.heap_used_at_gc_start;
- record->heap_live_objects = live;
- record->heap_free_objects = total - live;
+ record->heap_use_pages = objspace->profile.heap_used_at_gc_start;
+ record->heap_live_objects = live;
+ record->heap_free_objects = total - live;
#endif
- record->heap_total_objects = total;
- record->heap_use_size = live * sizeof(RVALUE);
- record->heap_total_size = total * sizeof(RVALUE);
+ record->heap_total_objects = total;
+ record->heap_use_size = live * sizeof(RVALUE);
+ record->heap_total_size = total * sizeof(RVALUE);
}
}
@@ -13319,7 +13661,7 @@ gc_prof_set_heap_info(rb_objspace_t *objspace)
* call-seq:
* GC::Profiler.clear -> nil
*
- * Clears the GC profiler data.
+ * Clears the \GC profiler data.
*
*/
@@ -13397,14 +13739,14 @@ gc_profile_record_get(VALUE _)
rb_objspace_t *objspace = (&rb_objspace);
if (!objspace->profile.run) {
- return Qnil;
+ return Qnil;
}
for (i =0; i < objspace->profile.next_index; i++) {
- gc_profile_record *record = &objspace->profile.records[i];
+ gc_profile_record *record = &objspace->profile.records[i];
- prof = rb_hash_new();
- rb_hash_aset(prof, ID2SYM(rb_intern("GC_FLAGS")), gc_info_decode(0, rb_hash_new(), record->flags));
+ prof = rb_hash_new();
+ rb_hash_aset(prof, ID2SYM(rb_intern("GC_FLAGS")), gc_info_decode(0, rb_hash_new(), record->flags));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_TIME")), DBL2NUM(record->gc_time));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_INVOKE_TIME")), DBL2NUM(record->gc_invoke_time));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SIZE")), SIZET2NUM(record->heap_use_size));
@@ -13421,18 +13763,18 @@ gc_profile_record_get(VALUE _)
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_LIVE_OBJECTS")), SIZET2NUM(record->heap_live_objects));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_FREE_OBJECTS")), SIZET2NUM(record->heap_free_objects));
- rb_hash_aset(prof, ID2SYM(rb_intern("REMOVING_OBJECTS")), SIZET2NUM(record->removing_objects));
- rb_hash_aset(prof, ID2SYM(rb_intern("EMPTY_OBJECTS")), SIZET2NUM(record->empty_objects));
+ rb_hash_aset(prof, ID2SYM(rb_intern("REMOVING_OBJECTS")), SIZET2NUM(record->removing_objects));
+ rb_hash_aset(prof, ID2SYM(rb_intern("EMPTY_OBJECTS")), SIZET2NUM(record->empty_objects));
- rb_hash_aset(prof, ID2SYM(rb_intern("HAVE_FINALIZE")), RBOOL(record->flags & GPR_FLAG_HAVE_FINALIZE));
+ rb_hash_aset(prof, ID2SYM(rb_intern("HAVE_FINALIZE")), RBOOL(record->flags & GPR_FLAG_HAVE_FINALIZE));
#endif
#if RGENGC_PROFILE > 0
- rb_hash_aset(prof, ID2SYM(rb_intern("OLD_OBJECTS")), SIZET2NUM(record->old_objects));
- rb_hash_aset(prof, ID2SYM(rb_intern("REMEMBERED_NORMAL_OBJECTS")), SIZET2NUM(record->remembered_normal_objects));
- rb_hash_aset(prof, ID2SYM(rb_intern("REMEMBERED_SHADY_OBJECTS")), SIZET2NUM(record->remembered_shady_objects));
+ rb_hash_aset(prof, ID2SYM(rb_intern("OLD_OBJECTS")), SIZET2NUM(record->old_objects));
+ rb_hash_aset(prof, ID2SYM(rb_intern("REMEMBERED_NORMAL_OBJECTS")), SIZET2NUM(record->remembered_normal_objects));
+ rb_hash_aset(prof, ID2SYM(rb_intern("REMEMBERED_SHADY_OBJECTS")), SIZET2NUM(record->remembered_shady_objects));
#endif
- rb_ary_push(gc_profile, prof);
+ rb_ary_push(gc_profile, prof);
}
return gc_profile;
@@ -13448,8 +13790,8 @@ gc_profile_dump_major_reason(unsigned int flags, char *buff)
int i = 0;
if (reason == GPR_FLAG_NONE) {
- buff[0] = '-';
- buff[1] = 0;
+ buff[0] = '-';
+ buff[1] = 0;
}
else {
#define C(x, s) \
@@ -13458,11 +13800,11 @@ gc_profile_dump_major_reason(unsigned int flags, char *buff)
if (i >= MAJOR_REASON_MAX) rb_bug("gc_profile_dump_major_reason: overflow"); \
buff[i] = 0; \
}
- C(NOFREE, N);
- C(OLDGEN, O);
- C(SHADY, S);
+ C(NOFREE, N);
+ C(OLDGEN, O);
+ C(SHADY, S);
#if RGENGC_ESTIMATE_OLDMALLOC
- C(OLDMALLOC, M);
+ C(OLDMALLOC, M);
#endif
#undef C
}
@@ -13480,88 +13822,88 @@ gc_profile_dump_on(VALUE out, VALUE (*append)(VALUE, VALUE))
#endif
if (objspace->profile.run && count /* > 1 */) {
- size_t i;
- const gc_profile_record *record;
+ size_t i;
+ const gc_profile_record *record;
- append(out, rb_sprintf("GC %"PRIuSIZE" invokes.\n", objspace->profile.count));
- append(out, rb_str_new_cstr("Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms)\n"));
+ append(out, rb_sprintf("GC %"PRIuSIZE" invokes.\n", objspace->profile.count));
+ append(out, rb_str_new_cstr("Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms)\n"));
- for (i = 0; i < count; i++) {
- record = &objspace->profile.records[i];
- append(out, rb_sprintf("%5"PRIuSIZE" %19.3f %20"PRIuSIZE" %20"PRIuSIZE" %20"PRIuSIZE" %30.20f\n",
- i+1, record->gc_invoke_time, record->heap_use_size,
- record->heap_total_size, record->heap_total_objects, record->gc_time*1000));
- }
+ for (i = 0; i < count; i++) {
+ record = &objspace->profile.records[i];
+ append(out, rb_sprintf("%5"PRIuSIZE" %19.3f %20"PRIuSIZE" %20"PRIuSIZE" %20"PRIuSIZE" %30.20f\n",
+ i+1, record->gc_invoke_time, record->heap_use_size,
+ record->heap_total_size, record->heap_total_objects, record->gc_time*1000));
+ }
#if GC_PROFILE_MORE_DETAIL
const char *str = "\n\n" \
- "More detail.\n" \
- "Prepare Time = Previously GC's rest sweep time\n"
- "Index Flags Allocate Inc. Allocate Limit"
+ "More detail.\n" \
+ "Prepare Time = Previously GC's rest sweep time\n"
+ "Index Flags Allocate Inc. Allocate Limit"
#if CALC_EXACT_MALLOC_SIZE
- " Allocated Size"
+ " Allocated Size"
#endif
- " Use Page Mark Time(ms) Sweep Time(ms) Prepare Time(ms) LivingObj FreeObj RemovedObj EmptyObj"
+ " Use Page Mark Time(ms) Sweep Time(ms) Prepare Time(ms) LivingObj FreeObj RemovedObj EmptyObj"
#if RGENGC_PROFILE
- " OldgenObj RemNormObj RemShadObj"
+ " OldgenObj RemNormObj RemShadObj"
#endif
#if GC_PROFILE_DETAIL_MEMORY
- " MaxRSS(KB) MinorFLT MajorFLT"
+ " MaxRSS(KB) MinorFLT MajorFLT"
#endif
"\n";
append(out, rb_str_new_cstr(str));
- for (i = 0; i < count; i++) {
- record = &objspace->profile.records[i];
- append(out, rb_sprintf("%5"PRIuSIZE" %4s/%c/%6s%c %13"PRIuSIZE" %15"PRIuSIZE
+ for (i = 0; i < count; i++) {
+ record = &objspace->profile.records[i];
+ append(out, rb_sprintf("%5"PRIuSIZE" %4s/%c/%6s%c %13"PRIuSIZE" %15"PRIuSIZE
#if CALC_EXACT_MALLOC_SIZE
- " %15"PRIuSIZE
+ " %15"PRIuSIZE
#endif
- " %9"PRIuSIZE" %17.12f %17.12f %17.12f %10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE
+ " %9"PRIuSIZE" %17.12f %17.12f %17.12f %10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE
#if RGENGC_PROFILE
- "%10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE
+ "%10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE
#endif
#if GC_PROFILE_DETAIL_MEMORY
- "%11ld %8ld %8ld"
-#endif
-
- "\n",
- i+1,
- gc_profile_dump_major_reason(record->flags, reason_str),
- (record->flags & GPR_FLAG_HAVE_FINALIZE) ? 'F' : '.',
- (record->flags & GPR_FLAG_NEWOBJ) ? "NEWOBJ" :
- (record->flags & GPR_FLAG_MALLOC) ? "MALLOC" :
- (record->flags & GPR_FLAG_METHOD) ? "METHOD" :
- (record->flags & GPR_FLAG_CAPI) ? "CAPI__" : "??????",
- (record->flags & GPR_FLAG_STRESS) ? '!' : ' ',
- record->allocate_increase, record->allocate_limit,
+ "%11ld %8ld %8ld"
+#endif
+
+ "\n",
+ i+1,
+ gc_profile_dump_major_reason(record->flags, reason_str),
+ (record->flags & GPR_FLAG_HAVE_FINALIZE) ? 'F' : '.',
+ (record->flags & GPR_FLAG_NEWOBJ) ? "NEWOBJ" :
+ (record->flags & GPR_FLAG_MALLOC) ? "MALLOC" :
+ (record->flags & GPR_FLAG_METHOD) ? "METHOD" :
+ (record->flags & GPR_FLAG_CAPI) ? "CAPI__" : "??????",
+ (record->flags & GPR_FLAG_STRESS) ? '!' : ' ',
+ record->allocate_increase, record->allocate_limit,
#if CALC_EXACT_MALLOC_SIZE
- record->allocated_size,
+ record->allocated_size,
#endif
- record->heap_use_pages,
- record->gc_mark_time*1000,
- record->gc_sweep_time*1000,
- record->prepare_time*1000,
+ record->heap_use_pages,
+ record->gc_mark_time*1000,
+ record->gc_sweep_time*1000,
+ record->prepare_time*1000,
- record->heap_live_objects,
- record->heap_free_objects,
- record->removing_objects,
- record->empty_objects
+ record->heap_live_objects,
+ record->heap_free_objects,
+ record->removing_objects,
+ record->empty_objects
#if RGENGC_PROFILE
- ,
- record->old_objects,
- record->remembered_normal_objects,
- record->remembered_shady_objects
+ ,
+ record->old_objects,
+ record->remembered_normal_objects,
+ record->remembered_shady_objects
#endif
#if GC_PROFILE_DETAIL_MEMORY
- ,
- record->maxrss / 1024,
- record->minflt,
- record->majflt
+ ,
+ record->maxrss / 1024,
+ record->minflt,
+ record->majflt
#endif
- ));
- }
+ ));
+ }
#endif
}
}
@@ -13619,12 +13961,12 @@ gc_profile_total_time(VALUE self)
rb_objspace_t *objspace = &rb_objspace;
if (objspace->profile.run && objspace->profile.next_index > 0) {
- size_t i;
- size_t count = objspace->profile.next_index;
+ size_t i;
+ size_t count = objspace->profile.next_index;
- for (i = 0; i < count; i++) {
- time += objspace->profile.records[i].gc_time;
- }
+ for (i = 0; i < count; i++) {
+ time += objspace->profile.records[i].gc_time;
+ }
}
return DBL2NUM(time);
}
@@ -13633,7 +13975,7 @@ gc_profile_total_time(VALUE self)
* call-seq:
* GC::Profiler.enabled? -> true or false
*
- * The current status of GC profile mode.
+ * The current status of \GC profile mode.
*/
static VALUE
@@ -13647,7 +13989,7 @@ gc_profile_enable_get(VALUE self)
* call-seq:
* GC::Profiler.enable -> nil
*
- * Starts the GC profiler.
+ * Starts the \GC profiler.
*
*/
@@ -13664,7 +14006,7 @@ gc_profile_enable(VALUE _)
* call-seq:
* GC::Profiler.disable -> nil
*
- * Stops the GC profiler.
+ * Stops the \GC profiler.
*
*/
@@ -13687,36 +14029,36 @@ type_name(int type, VALUE obj)
{
switch (type) {
#define TYPE_NAME(t) case (t): return #t;
- TYPE_NAME(T_NONE);
- TYPE_NAME(T_OBJECT);
- TYPE_NAME(T_CLASS);
- TYPE_NAME(T_MODULE);
- TYPE_NAME(T_FLOAT);
- TYPE_NAME(T_STRING);
- TYPE_NAME(T_REGEXP);
- TYPE_NAME(T_ARRAY);
- TYPE_NAME(T_HASH);
- TYPE_NAME(T_STRUCT);
- TYPE_NAME(T_BIGNUM);
- TYPE_NAME(T_FILE);
- TYPE_NAME(T_MATCH);
- TYPE_NAME(T_COMPLEX);
- TYPE_NAME(T_RATIONAL);
- TYPE_NAME(T_NIL);
- TYPE_NAME(T_TRUE);
- TYPE_NAME(T_FALSE);
- TYPE_NAME(T_SYMBOL);
- TYPE_NAME(T_FIXNUM);
- TYPE_NAME(T_UNDEF);
- TYPE_NAME(T_IMEMO);
- TYPE_NAME(T_ICLASS);
+ TYPE_NAME(T_NONE);
+ TYPE_NAME(T_OBJECT);
+ TYPE_NAME(T_CLASS);
+ TYPE_NAME(T_MODULE);
+ TYPE_NAME(T_FLOAT);
+ TYPE_NAME(T_STRING);
+ TYPE_NAME(T_REGEXP);
+ TYPE_NAME(T_ARRAY);
+ TYPE_NAME(T_HASH);
+ TYPE_NAME(T_STRUCT);
+ TYPE_NAME(T_BIGNUM);
+ TYPE_NAME(T_FILE);
+ TYPE_NAME(T_MATCH);
+ TYPE_NAME(T_COMPLEX);
+ TYPE_NAME(T_RATIONAL);
+ TYPE_NAME(T_NIL);
+ TYPE_NAME(T_TRUE);
+ TYPE_NAME(T_FALSE);
+ TYPE_NAME(T_SYMBOL);
+ TYPE_NAME(T_FIXNUM);
+ TYPE_NAME(T_UNDEF);
+ TYPE_NAME(T_IMEMO);
+ TYPE_NAME(T_ICLASS);
TYPE_NAME(T_MOVED);
- TYPE_NAME(T_ZOMBIE);
+ TYPE_NAME(T_ZOMBIE);
case T_DATA:
- if (obj && rb_objspace_data_type_name(obj)) {
- return rb_objspace_data_type_name(obj);
- }
- return "T_DATA";
+ if (obj && rb_objspace_data_type_name(obj)) {
+ return rb_objspace_data_type_name(obj);
+ }
+ return "T_DATA";
#undef TYPE_NAME
}
return "unknown";
@@ -13752,12 +14094,11 @@ static void
rb_raw_iseq_info(char *const buff, const size_t buff_size, const rb_iseq_t *iseq)
{
if (buff_size > 0 && ISEQ_BODY(iseq) && ISEQ_BODY(iseq)->location.label && !RB_TYPE_P(ISEQ_BODY(iseq)->location.pathobj, T_MOVED)) {
- VALUE path = rb_iseq_path(iseq);
- VALUE n = ISEQ_BODY(iseq)->location.first_lineno;
+ VALUE path = rb_iseq_path(iseq);
+ int n = ISEQ_BODY(iseq)->location.first_lineno;
snprintf(buff, buff_size, " %s@%s:%d",
RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
- RSTRING_PTR(path),
- n ? FIX2INT(n) : 0 );
+ RSTRING_PTR(path), n);
}
}
@@ -13799,7 +14140,7 @@ rb_raw_obj_info_common(char *const buff, const size_t buff_size, const VALUE obj
}
}
else {
- const int age = RVALUE_FLAGS_AGE(RBASIC(obj)->flags);
+ const int age = RVALUE_FLAGS_AGE(RBASIC(obj)->flags);
if (is_pointer_to_heap(&rb_objspace, (void *)obj)) {
APPEND_F("%p [%d%s%s%s%s%s%s] %s ",
@@ -13819,18 +14160,18 @@ rb_raw_obj_info_common(char *const buff, const size_t buff_size, const VALUE obj
obj_type_name(obj));
}
- if (internal_object_p(obj)) {
- /* ignore */
- }
- else if (RBASIC(obj)->klass == 0) {
+ if (internal_object_p(obj)) {
+ /* ignore */
+ }
+ else if (RBASIC(obj)->klass == 0) {
APPEND_S("(temporary internal)");
- }
- else if (RTEST(RBASIC(obj)->klass)) {
+ }
+ else if (RTEST(RBASIC(obj)->klass)) {
VALUE class_path = rb_class_path_cached(RBASIC(obj)->klass);
- if (!NIL_P(class_path)) {
+ if (!NIL_P(class_path)) {
APPEND_F("(%s)", RSTRING_PTR(class_path));
- }
- }
+ }
+ }
#if GC_DEBUG
APPEND_F("@%s:%d", RANY(obj)->file, RANY(obj)->line);
@@ -13845,13 +14186,13 @@ static size_t
rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALUE obj, size_t pos)
{
if (LIKELY(pos < buff_size) && !SPECIAL_CONST_P(obj)) {
- const enum ruby_value_type type = BUILTIN_TYPE(obj);
+ const enum ruby_value_type type = BUILTIN_TYPE(obj);
- switch (type) {
- case T_NODE:
- UNEXPECTED_NODE(rb_raw_obj_info);
- break;
- case T_ARRAY:
+ switch (type) {
+ case T_NODE:
+ UNEXPECTED_NODE(rb_raw_obj_info);
+ break;
+ case T_ARRAY:
if (ARY_SHARED_P(obj)) {
APPEND_S("shared -> ");
rb_raw_obj_info(BUFF_ARGS, ARY_SHARED_ROOT(obj));
@@ -13871,8 +14212,8 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
ARY_EMBED_P(obj) ? -1L : RARRAY(obj)->as.heap.aux.capa,
(void *)RARRAY_CONST_PTR_TRANSIENT(obj));
}
- break;
- case T_STRING: {
+ break;
+ case T_STRING: {
if (STR_SHARED_P(obj)) {
APPEND_F(" [shared] len: %ld", RSTRING_LEN(obj));
}
@@ -13882,8 +14223,8 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
APPEND_F(" len: %ld, capa: %" PRIdSIZE, RSTRING_LEN(obj), rb_str_capacity(obj));
}
APPEND_F(" \"%.*s\"", str_len_no_raise(obj), RSTRING_PTR(obj));
- break;
- }
+ break;
+ }
case T_SYMBOL: {
VALUE fstr = RSYMBOL(obj)->fstr;
ID id = RSYMBOL(obj)->id;
@@ -13928,7 +14269,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
}
case T_OBJECT:
{
- uint32_t len = ROBJECT_NUMIV(obj);
+ uint32_t len = ROBJECT_IV_CAPACITY(obj);
if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) {
APPEND_F("(embed) len:%d", len);
@@ -13939,38 +14280,38 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
}
}
break;
- case T_DATA: {
- const struct rb_block *block;
- const rb_iseq_t *iseq;
- if (rb_obj_is_proc(obj) &&
- (block = vm_proc_block(obj)) != NULL &&
- (vm_block_type(block) == block_type_iseq) &&
- (iseq = vm_block_iseq(block)) != NULL) {
+ case T_DATA: {
+ const struct rb_block *block;
+ const rb_iseq_t *iseq;
+ if (rb_obj_is_proc(obj) &&
+ (block = vm_proc_block(obj)) != NULL &&
+ (vm_block_type(block) == block_type_iseq) &&
+ (iseq = vm_block_iseq(block)) != NULL) {
rb_raw_iseq_info(BUFF_ARGS, iseq);
- }
+ }
else if (rb_ractor_p(obj)) {
rb_ractor_t *r = (void *)DATA_PTR(obj);
if (r) {
APPEND_F("r:%d", r->pub.id);
}
}
- else {
- const char * const type_name = rb_objspace_data_type_name(obj);
- if (type_name) {
+ else {
+ const char * const type_name = rb_objspace_data_type_name(obj);
+ if (type_name) {
APPEND_F("%s", type_name);
- }
- }
- break;
- }
- case T_IMEMO: {
+ }
+ }
+ break;
+ }
+ case T_IMEMO: {
APPEND_F("<%s> ", rb_imemo_name(imemo_type(obj)));
- switch (imemo_type(obj)) {
- case imemo_ment:
+ switch (imemo_type(obj)) {
+ case imemo_ment:
{
const rb_method_entry_t *me = &RANY(obj)->as.imemo.ment;
- APPEND_F(":%s (%s%s%s%s) type:%s alias:%d owner:%p defined_class:%p",
+ APPEND_F(":%s (%s%s%s%s) type:%s aliased:%d owner:%p defined_class:%p",
rb_id2name(me->called_id),
METHOD_ENTRY_VISI(me) == METHOD_VISI_PUBLIC ? "pub" :
METHOD_ENTRY_VISI(me) == METHOD_VISI_PRIVATE ? "pri" : "pro",
@@ -13978,7 +14319,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
METHOD_ENTRY_CACHED(me) ? ",cc" : "",
METHOD_ENTRY_INVALIDATED(me) ? ",inv" : "",
me->def ? rb_method_type_name(me->def->type) : "NULL",
- me->def ? me->def->alias_count : -1,
+ me->def ? me->def->aliased : -1,
(void *)me->owner, // obj_info(me->owner),
(void *)me->defined_class); //obj_info(me->defined_class)));
@@ -13996,11 +14337,11 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
break;
}
- case imemo_iseq: {
- const rb_iseq_t *iseq = (const rb_iseq_t *)obj;
+ case imemo_iseq: {
+ const rb_iseq_t *iseq = (const rb_iseq_t *)obj;
rb_raw_iseq_info(BUFF_ARGS, iseq);
- break;
- }
+ break;
+ }
case imemo_callinfo:
{
const struct rb_callinfo *ci = (const struct rb_callinfo *)obj;
@@ -14025,13 +14366,13 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
(void *)vm_cc_call(cc));
break;
}
- default:
- break;
- }
- }
- default:
- break;
- }
+ default:
+ break;
+ }
+ }
+ default:
+ break;
+ }
}
end:
@@ -14180,7 +14521,7 @@ rb_gcdebug_add_stress_to_class(int argc, VALUE *argv, VALUE self)
rb_objspace_t *objspace = &rb_objspace;
if (!stress_to_class) {
- stress_to_class = rb_ary_tmp_new(argc);
+ stress_to_class = rb_ary_hidden_new(argc);
}
rb_ary_cat(stress_to_class, argv, argc);
return self;
@@ -14201,12 +14542,12 @@ rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self)
int i;
if (stress_to_class) {
- for (i = 0; i < argc; ++i) {
- rb_ary_delete_same(stress_to_class, argv[i]);
- }
- if (RARRAY_LEN(stress_to_class) == 0) {
- stress_to_class = 0;
- }
+ for (i = 0; i < argc; ++i) {
+ rb_ary_delete_same(stress_to_class, argv[i]);
+ }
+ if (RARRAY_LEN(stress_to_class) == 0) {
+ stress_to_class = 0;
+ }
}
return Qnil;
}
@@ -14269,6 +14610,22 @@ rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self)
*/
#include "gc.rbinc"
+/*
+ * call-seq:
+ * GC.using_rvargc? -> true or false
+ *
+ * Returns true if using experimental feature Variable Width Allocation, false
+ * otherwise.
+ */
+static VALUE
+gc_using_rvargc_p(VALUE mod)
+{
+#if USE_RVARGC
+ return Qtrue;
+#else
+ return Qfalse;
+#endif
+}
void
Init_GC(void)
@@ -14282,7 +14639,8 @@ Init_GC(void)
gc_constants = rb_hash_new();
rb_hash_aset(gc_constants, ID2SYM(rb_intern("DEBUG")), RBOOL(GC_DEBUG));
- rb_hash_aset(gc_constants, ID2SYM(rb_intern("BASE_SLOT_SIZE")), SIZET2NUM(BASE_SLOT_SIZE));
+ rb_hash_aset(gc_constants, ID2SYM(rb_intern("BASE_SLOT_SIZE")), SIZET2NUM(BASE_SLOT_SIZE - RVALUE_OVERHEAD));
+ rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OVERHEAD")), SIZET2NUM(RVALUE_OVERHEAD));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_SIZE")), SIZET2NUM(sizeof(RVALUE)));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_OBJ_LIMIT")), SIZET2NUM(HEAP_PAGE_OBJ_LIMIT));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_BITMAP_SIZE")), SIZET2NUM(HEAP_PAGE_BITMAP_SIZE));
@@ -14320,23 +14678,23 @@ Init_GC(void)
rb_define_module_function(rb_mObjSpace, "count_objects", count_objects, -1);
{
- VALUE rb_cWeakMap = rb_define_class_under(rb_mObjSpace, "WeakMap", rb_cObject);
- rb_define_alloc_func(rb_cWeakMap, wmap_allocate);
- rb_define_method(rb_cWeakMap, "[]=", wmap_aset, 2);
- rb_define_method(rb_cWeakMap, "[]", wmap_aref, 1);
- rb_define_method(rb_cWeakMap, "include?", wmap_has_key, 1);
- rb_define_method(rb_cWeakMap, "member?", wmap_has_key, 1);
- rb_define_method(rb_cWeakMap, "key?", wmap_has_key, 1);
- rb_define_method(rb_cWeakMap, "inspect", wmap_inspect, 0);
- rb_define_method(rb_cWeakMap, "each", wmap_each, 0);
- rb_define_method(rb_cWeakMap, "each_pair", wmap_each, 0);
- rb_define_method(rb_cWeakMap, "each_key", wmap_each_key, 0);
- rb_define_method(rb_cWeakMap, "each_value", wmap_each_value, 0);
- rb_define_method(rb_cWeakMap, "keys", wmap_keys, 0);
- rb_define_method(rb_cWeakMap, "values", wmap_values, 0);
- rb_define_method(rb_cWeakMap, "size", wmap_size, 0);
- rb_define_method(rb_cWeakMap, "length", wmap_size, 0);
- rb_include_module(rb_cWeakMap, rb_mEnumerable);
+ VALUE rb_cWeakMap = rb_define_class_under(rb_mObjSpace, "WeakMap", rb_cObject);
+ rb_define_alloc_func(rb_cWeakMap, wmap_allocate);
+ rb_define_method(rb_cWeakMap, "[]=", wmap_aset, 2);
+ rb_define_method(rb_cWeakMap, "[]", wmap_aref, 1);
+ rb_define_method(rb_cWeakMap, "include?", wmap_has_key, 1);
+ rb_define_method(rb_cWeakMap, "member?", wmap_has_key, 1);
+ rb_define_method(rb_cWeakMap, "key?", wmap_has_key, 1);
+ rb_define_method(rb_cWeakMap, "inspect", wmap_inspect, 0);
+ rb_define_method(rb_cWeakMap, "each", wmap_each, 0);
+ rb_define_method(rb_cWeakMap, "each_pair", wmap_each, 0);
+ rb_define_method(rb_cWeakMap, "each_key", wmap_each_key, 0);
+ rb_define_method(rb_cWeakMap, "each_value", wmap_each_value, 0);
+ rb_define_method(rb_cWeakMap, "keys", wmap_keys, 0);
+ rb_define_method(rb_cWeakMap, "values", wmap_values, 0);
+ rb_define_method(rb_cWeakMap, "size", wmap_size, 0);
+ rb_define_method(rb_cWeakMap, "length", wmap_size, 0);
+ rb_include_module(rb_cWeakMap, rb_mEnumerable);
}
/* internal methods */
@@ -14347,6 +14705,8 @@ Init_GC(void)
rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0);
#endif
+ rb_define_singleton_method(rb_mGC, "using_rvargc?", gc_using_rvargc_p, 0);
+
if (GC_COMPACTION_SUPPORTED) {
rb_define_singleton_method(rb_mGC, "compact", gc_compact, 0);
rb_define_singleton_method(rb_mGC, "auto_compact", gc_get_auto_compact, 0);
@@ -14368,25 +14728,25 @@ Init_GC(void)
#endif
{
- VALUE opts;
- /* GC build options */
- rb_define_const(rb_mGC, "OPTS", opts = rb_ary_new());
+ VALUE opts;
+ /* \GC build options */
+ rb_define_const(rb_mGC, "OPTS", opts = rb_ary_new());
#define OPT(o) if (o) rb_ary_push(opts, rb_fstring_lit(#o))
- OPT(GC_DEBUG);
- OPT(USE_RGENGC);
- OPT(RGENGC_DEBUG);
- OPT(RGENGC_CHECK_MODE);
- OPT(RGENGC_PROFILE);
- OPT(RGENGC_ESTIMATE_OLDMALLOC);
- OPT(GC_PROFILE_MORE_DETAIL);
- OPT(GC_ENABLE_LAZY_SWEEP);
- OPT(CALC_EXACT_MALLOC_SIZE);
- OPT(MALLOC_ALLOCATED_SIZE);
- OPT(MALLOC_ALLOCATED_SIZE_CHECK);
- OPT(GC_PROFILE_DETAIL_MEMORY);
- OPT(GC_COMPACTION_SUPPORTED);
+ OPT(GC_DEBUG);
+ OPT(USE_RGENGC);
+ OPT(RGENGC_DEBUG);
+ OPT(RGENGC_CHECK_MODE);
+ OPT(RGENGC_PROFILE);
+ OPT(RGENGC_ESTIMATE_OLDMALLOC);
+ OPT(GC_PROFILE_MORE_DETAIL);
+ OPT(GC_ENABLE_LAZY_SWEEP);
+ OPT(CALC_EXACT_MALLOC_SIZE);
+ OPT(MALLOC_ALLOCATED_SIZE);
+ OPT(MALLOC_ALLOCATED_SIZE_CHECK);
+ OPT(GC_PROFILE_DETAIL_MEMORY);
+ OPT(GC_COMPACTION_SUPPORTED);
#undef OPT
- OBJ_FREEZE(opts);
+ OBJ_FREEZE(opts);
}
}
diff --git a/gc.h b/gc.h
index a1f6e6e49d..23218c1a9e 100644
--- a/gc.h
+++ b/gc.h
@@ -6,10 +6,12 @@
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("movq\t%%rsp, %0" : "=r" (*(p)))
#elif defined(__i386) && defined(__GNUC__)
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("movl\t%%esp, %0" : "=r" (*(p)))
-#elif (defined(__powerpc__) || defined(__powerpc64__)) && defined(__GNUC__) && !defined(_AIX)
+#elif (defined(__powerpc__) || defined(__powerpc64__)) && defined(__GNUC__) && !defined(_AIX) && !defined(__APPLE__) // Not Apple is NEEDED to unbreak ppc64 build on Darwin. Don't ask.
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("mr\t%0, %%r1" : "=r" (*(p)))
#elif (defined(__powerpc__) || defined(__powerpc64__)) && defined(__GNUC__) && defined(_AIX)
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("mr %0,1" : "=r" (*(p)))
+#elif defined(__POWERPC__) && defined(__APPLE__) // Darwin ppc and ppc64
+#define SET_MACHINE_STACK_END(p) __asm__ volatile("mr %0, r1" : "=r" (*(p)))
#elif defined(__aarch64__) && defined(__GNUC__)
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("mov\t%0, sp" : "=r" (*(p)))
#else
@@ -20,9 +22,9 @@ NOINLINE(void rb_gc_set_stack_end(VALUE **stack_end_p));
#define RB_GC_SAVE_MACHINE_CONTEXT(th) \
do { \
- FLUSH_REGISTER_WINDOWS; \
- setjmp((th)->ec->machine.regs); \
- SET_MACHINE_STACK_END(&(th)->ec->machine.stack_end); \
+ FLUSH_REGISTER_WINDOWS; \
+ setjmp((th)->ec->machine.regs); \
+ SET_MACHINE_STACK_END(&(th)->ec->machine.stack_end); \
} while (0)
/* for GC debug */
@@ -44,13 +46,13 @@ static inline void
rb_gc_debug_body(const char *mode, const char *msg, int st, void *ptr)
{
if (st == 0) {
- ruby_gc_debug_indent--;
+ ruby_gc_debug_indent--;
}
rb_gc_debug_indent();
ruby_debug_printf("%s: %s %s (%p)\n", mode, st ? "->" : "<-", msg, ptr);
if (st) {
- ruby_gc_debug_indent++;
+ ruby_gc_debug_indent++;
}
fflush(stdout);
@@ -88,9 +90,9 @@ rb_gc_debug_body(const char *mode, const char *msg, int st, void *ptr)
RUBY_EXTERN int ruby_stack_grow_direction;
int ruby_get_stack_grow_direction(volatile VALUE *addr);
# define stack_growup_p(x) ( \
- (ruby_stack_grow_direction ? \
- ruby_stack_grow_direction : \
- ruby_get_stack_grow_direction(x)) > 0)
+ (ruby_stack_grow_direction ? \
+ ruby_stack_grow_direction : \
+ ruby_get_stack_grow_direction(x)) > 0)
# define STACK_UPPER(x, a, b) (stack_growup_p(x) ? (a) : (b))
#endif
@@ -114,10 +116,10 @@ int ruby_get_stack_grow_direction(volatile VALUE *addr);
const char *rb_obj_info(VALUE obj);
const char *rb_raw_obj_info(char *const buff, const size_t buff_size, VALUE obj);
-VALUE rb_gc_disable_no_rest(void);
-
struct rb_thread_struct;
+size_t rb_size_pool_slot_size(unsigned char pool_id);
+
RUBY_SYMBOL_EXPORT_BEGIN
/* exports for objspace module */
@@ -138,6 +140,8 @@ void rb_objspace_each_objects_without_setup(
size_t rb_gc_obj_slot_size(VALUE obj);
+VALUE rb_gc_disable_no_rest(void);
+
RUBY_SYMBOL_EXPORT_END
#endif /* RUBY_GC_H */
diff --git a/gc.rb b/gc.rb
index 9144a96603..57aeeb9131 100644
--- a/gc.rb
+++ b/gc.rb
@@ -6,7 +6,7 @@
# Some of the underlying methods are also available via the ObjectSpace
# module.
#
-# You may obtain information about the operation of the GC through
+# You may obtain information about the operation of the \GC through
# GC::Profiler.
module GC
@@ -24,7 +24,7 @@ module GC
#
# def GC.start(full_mark: true, immediate_sweep: true); end
#
- # Use full_mark: false to perform a minor GC.
+ # Use full_mark: false to perform a minor \GC.
# Use immediate_sweep: false to defer sweeping (use lazy sweep).
#
# Note: These keyword arguments are implementation and version dependent. They
@@ -67,7 +67,7 @@ module GC
# call-seq:
# GC.stress -> integer, true or false
#
- # Returns current status of GC stress mode.
+ # Returns current status of \GC stress mode.
def self.stress
Primitive.gc_stress_get
end
@@ -75,9 +75,9 @@ module GC
# call-seq:
# GC.stress = flag -> flag
#
- # Updates the GC stress mode.
+ # Updates the \GC stress mode.
#
- # When stress mode is enabled, the GC is invoked at every GC opportunity:
+ # When stress mode is enabled, the \GC is invoked at every \GC opportunity:
# all memory and object allocations.
#
# Enabling stress mode will degrade performance, it is only for debugging.
@@ -93,9 +93,9 @@ module GC
# call-seq:
# GC.count -> Integer
#
- # The number of times GC occurred.
+ # The number of times \GC occurred.
#
- # It returns the number of times GC occurred since the process started.
+ # It returns the number of times \GC occurred since the process started.
def self.count
Primitive.gc_count
end
@@ -105,12 +105,12 @@ module GC
# GC.stat(hash) -> Hash
# GC.stat(:key) -> Numeric
#
- # Returns a Hash containing information about the GC.
+ # Returns a Hash containing information about the \GC.
#
# The contents of the hash are implementation specific and may change in
# the future without notice.
#
- # The hash includes information about internal statistics about GC such as:
+ # The hash includes information about internal statistics about \GC such as:
#
# [count]
# The total number of garbage collections ran since application start
@@ -123,7 +123,7 @@ module GC
# The number of pages that can fit into the buffer that holds references to
# all pages
# [heap_allocatable_pages]
- # The total number of pages the application could allocate without additional GC
+ # The total number of pages the application could allocate without additional \GC
# [heap_available_slots]
# The total number of slots in all `:heap_allocated_pages`
# [heap_live_slots]
@@ -133,7 +133,7 @@ module GC
# [heap_final_slots]
# The total number of slots with pending finalizers to be run
# [heap_marked_slots]
- # The total number of objects marked in the last GC
+ # The total number of objects marked in the last \GC
# [heap_eden_pages]
# The total number of pages which contain at least one live slot
# [heap_tomb_pages]
@@ -147,9 +147,9 @@ module GC
# [total_freed_objects]
# The cumulative number of objects freed since application start
# [malloc_increase_bytes]
- # Amount of memory allocated on the heap for objects. Decreased by any GC
+ # Amount of memory allocated on the heap for objects. Decreased by any \GC
# [malloc_increase_bytes_limit]
- # When `:malloc_increase_bytes` crosses this limit, GC is triggered
+ # When `:malloc_increase_bytes` crosses this limit, \GC is triggered
# [minor_gc_count]
# The total number of minor garbage collections run since process start
# [major_gc_count]
@@ -165,15 +165,15 @@ module GC
# The total number of objects without write barriers
# [remembered_wb_unprotected_objects_limit]
# When `:remembered_wb_unprotected_objects` crosses this limit,
- # major GC is triggered
+ # major \GC is triggered
# [old_objects]
# Number of live, old objects which have survived at least 3 garbage collections
# [old_objects_limit]
- # When `:old_objects` crosses this limit, major GC is triggered
+ # When `:old_objects` crosses this limit, major \GC is triggered
# [oldmalloc_increase_bytes]
- # Amount of memory allocated on the heap for objects. Decreased by major GC
+ # Amount of memory allocated on the heap for objects. Decreased by major \GC
# [oldmalloc_increase_bytes_limit]
- # When `:old_malloc_increase_bytes` crosses this limit, major GC is triggered
+ # When `:old_malloc_increase_bytes` crosses this limit, major \GC is triggered
#
# If the optional argument, hash, is given,
# it is overwritten and returned.
@@ -191,7 +191,7 @@ module GC
# GC.stat_heap(heap_name, hash) -> Hash
# GC.stat_heap(heap_name, :key) -> Numeric
#
- # Returns information for memory pools in the GC.
+ # Returns information for memory pools in the \GC.
#
# If the first optional argument, +heap_name+, is passed in and not +nil+, it
# returns a +Hash+ containing information about the particular memory pool.
@@ -218,12 +218,12 @@ module GC
Primitive.gc_stat_heap heap_name, hash_or_key
end
- # call-seq:
- # GC.latest_gc_info -> {:gc_by=>:newobj}
+ # call-seq:
+ # GC.latest_gc_info -> hash
# GC.latest_gc_info(hash) -> hash
# GC.latest_gc_info(:major_by) -> :malloc
#
- # Returns information about the most recent garbage collection.
+ # Returns information about the most recent garbage collection.
#
# If the optional argument, hash, is given,
# it is overwritten and returned.
@@ -244,7 +244,7 @@ module GC
#
# This function expands the heap to ensure room to move all objects,
# compacts the heap to make sure everything moves, updates all references,
- # then performs a full GC. If any object contains a reference to a T_MOVED
+ # then performs a full \GC. If any object contains a reference to a T_MOVED
# object, that object should be pushed on the mark stack, and will
# make a SEGV.
def self.verify_compaction_references(toward: nil, double_heap: false, expand_heap: false)
@@ -253,21 +253,11 @@ module GC
end
# call-seq:
- # GC.using_rvargc? -> true or false
- #
- # Returns true if using experimental feature Variable Width Allocation, false
- # otherwise.
- def self.using_rvargc? # :nodoc:
- GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] > 1
- end
-
-
- # call-seq:
# GC.measure_total_time = true/false
#
- # Enable to measure GC time.
+ # Enable to measure \GC time.
# You can get the result with <tt>GC.stat(:time)</tt>.
- # Note that GC time measurement can cause some performance overhead.
+ # Note that \GC time measurement can cause some performance overhead.
def self.measure_total_time=(flag)
Primitive.cstmt! %{
rb_objspace.flags.measure_gc = RTEST(flag) ? TRUE : FALSE;
@@ -289,7 +279,7 @@ module GC
# call-seq:
# GC.total_time -> int
#
- # Return measured GC total time in nano seconds.
+ # Return measured \GC total time in nano seconds.
def self.total_time
Primitive.cexpr! %{
ULL2NUM(rb_objspace.profile.total_time_ns)
diff --git a/gem_prelude.rb b/gem_prelude.rb
index 94ada316aa..f382021ca3 100644
--- a/gem_prelude.rb
+++ b/gem_prelude.rb
@@ -17,3 +17,10 @@ begin
rescue LoadError
warn "`did_you_mean' was not loaded."
end if defined?(DidYouMean)
+
+begin
+ require 'syntax_suggest/core_ext'
+rescue LoadError
+ warn "`syntax_suggest' was not loaded."
+end if defined?(SyntaxSuggest)
+
diff --git a/gems/bundled_gems b/gems/bundled_gems
index ebe195f78f..d37d869d41 100644
--- a/gems/bundled_gems
+++ b/gems/bundled_gems
@@ -1,16 +1,16 @@
# gem-name version-to-bundle repository-url [optional-commit-hash-to-test-or-defaults-to-v-version]
-minitest 5.16.2 https://github.com/seattlerb/minitest
-power_assert 2.0.1 https://github.com/ruby/power_assert
-rake 13.0.6 https://github.com/ruby/rake
-test-unit 3.5.3 https://github.com/test-unit/test-unit 3.5.3
-rexml 3.2.5 https://github.com/ruby/rexml
-rss 0.2.9 https://github.com/ruby/rss 0.2.9
-net-ftp 0.1.3 https://github.com/ruby/net-ftp
-net-imap 0.2.3 https://github.com/ruby/net-imap
-net-pop 0.1.1 https://github.com/ruby/net-pop
-net-smtp 0.3.1 https://github.com/ruby/net-smtp
-matrix 0.4.2 https://github.com/ruby/matrix
-prime 0.1.2 https://github.com/ruby/prime
-rbs 2.6.0 https://github.com/ruby/rbs 14abbbae8885a09a2ed82de2ef31d67a9c0a108d
-typeprof 0.21.3 https://github.com/ruby/typeprof
-debug 1.6.1 https://github.com/ruby/debug
+minitest 5.25.1 https://github.com/seattlerb/minitest
+power_assert 2.0.3 https://github.com/ruby/power_assert
+rake 13.0.6 https://github.com/ruby/rake
+test-unit 3.5.7 https://github.com/test-unit/test-unit
+rexml 3.3.9 https://github.com/ruby/rexml
+rss 0.3.1 https://github.com/ruby/rss
+net-ftp 0.2.1 https://github.com/ruby/net-ftp
+net-imap 0.3.9 https://github.com/ruby/net-imap
+net-pop 0.1.2 https://github.com/ruby/net-pop
+net-smtp 0.3.4 https://github.com/ruby/net-smtp
+matrix 0.4.2 https://github.com/ruby/matrix
+prime 0.1.2 https://github.com/ruby/prime
+rbs 2.8.2 https://github.com/ruby/rbs
+typeprof 0.21.3 https://github.com/ruby/typeprof
+debug 1.7.1 https://github.com/ruby/debug
diff --git a/gems/lib/core_assertions.rb b/gems/lib/core_assertions.rb
new file mode 100644
index 0000000000..7334063885
--- /dev/null
+++ b/gems/lib/core_assertions.rb
@@ -0,0 +1 @@
+require_relative "../../tool/lib/core_assertions.rb"
diff --git a/gems/lib/envutil.rb b/gems/lib/envutil.rb
new file mode 100644
index 0000000000..d684c22cf2
--- /dev/null
+++ b/gems/lib/envutil.rb
@@ -0,0 +1 @@
+require_relative "../../tool/lib/envutil.rb"
diff --git a/tool/dummy-rake-compiler/rake/extensiontask.rb b/gems/lib/rake/extensiontask.rb
index 62b7ff8018..fdbe8d8874 100644
--- a/tool/dummy-rake-compiler/rake/extensiontask.rb
+++ b/gems/lib/rake/extensiontask.rb
@@ -1,8 +1,11 @@
+require "rake/tasklib" unless defined?(Rake::TaskLib)
+
module Rake
class ExtensionTask < TaskLib
def initialize(...)
- task :compile do
+ task :compile do |args|
puts "Dummy `compile` task defined in #{__FILE__}"
+ puts "#{args.name} => #{args.prereqs.join(' ')}"
end
end
end
diff --git a/goruby.c b/goruby.c
index c90fc97b0a..5d45277207 100644
--- a/goruby.c
+++ b/goruby.c
@@ -37,21 +37,21 @@ goruby_options(int argc, char **argv)
void *ret;
if ((isatty(0) && isatty(1) && isatty(2)) && (pipe(rw) == 0)) {
- ssize_t n;
- infd = dup(0);
- if (infd < 0) {
- close(rw[0]);
- close(rw[1]);
- goto no_irb;
- }
- dup2(rw[0], 0);
- close(rw[0]);
- n = write(rw[1], cmd, sizeof(cmd) - 1);
- close(rw[1]);
- ret = n > 0 ? ruby_options(argc, argv) : NULL;
- dup2(infd, 0);
- close(infd);
- return ret;
+ ssize_t n;
+ infd = dup(0);
+ if (infd < 0) {
+ close(rw[0]);
+ close(rw[1]);
+ goto no_irb;
+ }
+ dup2(rw[0], 0);
+ close(rw[0]);
+ n = write(rw[1], cmd, sizeof(cmd) - 1);
+ close(rw[1]);
+ ret = n > 0 ? ruby_options(argc, argv) : NULL;
+ dup2(infd, 0);
+ close(infd);
+ return ret;
}
no_irb:
return ruby_options(argc, argv);
@@ -62,7 +62,7 @@ goruby_run_node(void *arg)
{
int state;
if (NIL_P(rb_protect(init_golf, Qtrue, &state))) {
- return state == EXIT_SUCCESS ? EXIT_FAILURE : state;
+ return state == EXIT_SUCCESS ? EXIT_FAILURE : state;
}
return ruby_run_node(arg);
}
diff --git a/hash.c b/hash.c
index 1c36a22bc0..d2dce30624 100644
--- a/hash.c
+++ b/hash.c
@@ -28,6 +28,7 @@
#include "internal.h"
#include "internal/array.h"
#include "internal/bignum.h"
+#include "internal/basic_operators.h"
#include "internal/class.h"
#include "internal/cont.h"
#include "internal/error.h"
@@ -93,9 +94,11 @@ rb_hash_freeze(VALUE hash)
VALUE rb_cHash;
static VALUE envtbl;
-static ID id_hash, id_default, id_flatten_bang;
+static ID id_hash, id_flatten_bang;
static ID id_hash_iter_lev;
+#define id_default idDefault
+
VALUE
rb_hash_set_ifnone(VALUE hash, VALUE ifnone)
{
@@ -108,12 +111,12 @@ rb_any_cmp(VALUE a, VALUE b)
{
if (a == b) return 0;
if (RB_TYPE_P(a, T_STRING) && RBASIC(a)->klass == rb_cString &&
- RB_TYPE_P(b, T_STRING) && RBASIC(b)->klass == rb_cString) {
- return rb_str_hash_cmp(a, b);
+ RB_TYPE_P(b, T_STRING) && RBASIC(b)->klass == rb_cString) {
+ return rb_str_hash_cmp(a, b);
}
- if (a == Qundef || b == Qundef) return -1;
+ if (UNDEF_P(a) || UNDEF_P(b)) return -1;
if (SYMBOL_P(a) && SYMBOL_P(b)) {
- return a != b;
+ return a != b;
}
return !rb_eql(a, b);
@@ -156,7 +159,7 @@ any_hash(VALUE a, st_index_t (*other_func)(VALUE))
switch (TYPE(a)) {
case T_SYMBOL:
- if (STATIC_SYM_P(a)) {
+ if (STATIC_SYM_P(a)) {
hnum = a >> (RUBY_SPECIAL_SHIFT + ID_SCOPE_SHIFT);
hnum = rb_hash_start(hnum);
}
@@ -168,25 +171,25 @@ any_hash(VALUE a, st_index_t (*other_func)(VALUE))
case T_TRUE:
case T_FALSE:
case T_NIL:
- hnum = rb_objid_hash((st_index_t)a);
+ hnum = rb_objid_hash((st_index_t)a);
break;
case T_STRING:
- hnum = rb_str_hash(a);
+ hnum = rb_str_hash(a);
break;
case T_BIGNUM:
- hval = rb_big_hash(a);
- hnum = FIX2LONG(hval);
+ hval = rb_big_hash(a);
+ hnum = FIX2LONG(hval);
break;
case T_FLOAT: /* prevent pathological behavior: [Bug #10761] */
- hnum = rb_dbl_long_hash(rb_float_value(a));
+ hnum = rb_dbl_long_hash(rb_float_value(a));
break;
default:
- hnum = other_func(a);
+ hnum = other_func(a);
}
if ((SIGNED_VALUE)hnum > 0)
- hnum &= FIXNUM_MAX;
+ hnum &= FIXNUM_MAX;
else
- hnum |= FIXNUM_MIN;
+ hnum |= FIXNUM_MIN;
return (long)hnum;
}
@@ -195,7 +198,7 @@ obj_any_hash(VALUE obj)
{
VALUE hval = rb_check_funcall_basic_kw(obj, id_hash, rb_mKernel, 0, 0, 0);
- if (hval == Qundef) {
+ if (UNDEF_P(hval)) {
hval = rb_exec_recursive_outer_mid(hash_recursive, obj, 0, id_hash);
}
@@ -218,7 +221,7 @@ obj_any_hash(VALUE obj)
return FIX2LONG(hval);
}
-static st_index_t
+st_index_t
rb_any_hash(VALUE a)
{
return any_hash(a, obj_any_hash);
@@ -358,6 +361,9 @@ const struct st_hash_type rb_hashtype_ident = {
rb_ident_hash,
};
+#define RHASH_IDENTHASH_P(hash) (RHASH_TYPE(hash) == &identhash)
+#define RHASH_STRING_KEY_P(hash, key) (!RHASH_IDENTHASH_P(hash) && (rb_obj_class(key) == rb_cString))
+
typedef st_index_t st_hash_t;
/*
@@ -437,7 +443,7 @@ ar_cleared_entry(VALUE hash, unsigned int index)
* so you need to check key == Qundef
*/
ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, index);
- return pair->key == Qundef;
+ return UNDEF_P(pair->key);
}
else {
return FALSE;
@@ -517,8 +523,8 @@ hash_verify_(VALUE hash, const char *file, int line)
ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
k = pair->key;
v = pair->val;
- HASH_ASSERT(k != Qundef);
- HASH_ASSERT(v != Qundef);
+ HASH_ASSERT(!UNDEF_P(k));
+ HASH_ASSERT(!UNDEF_P(v));
n++;
}
}
@@ -764,31 +770,39 @@ ar_free_and_clear_table(VALUE hash)
HASH_ASSERT(RHASH_TRANSIENT_P(hash) == 0);
}
-static void
-ar_try_convert_table(VALUE hash)
-{
- if (!RHASH_AR_TABLE_P(hash)) return;
-
- const unsigned size = RHASH_AR_TABLE_SIZE(hash);
+void rb_st_add_direct_with_hash(st_table *tab, st_data_t key, st_data_t value, st_hash_t hash); // st.c
- st_table *new_tab;
- st_index_t i;
+enum ar_each_key_type {
+ ar_each_key_copy,
+ ar_each_key_cmp,
+ ar_each_key_insert,
+};
- if (size < RHASH_AR_TABLE_MAX_SIZE) {
- return;
+static inline int
+ar_each_key(ar_table *ar, int max, enum ar_each_key_type type, st_data_t *dst_keys, st_table *new_tab, st_hash_t *hashes)
+{
+ for (int i = 0; i < max; i++) {
+ ar_table_pair *pair = &ar->pairs[i];
+
+ switch (type) {
+ case ar_each_key_copy:
+ dst_keys[i] = pair->key;
+ break;
+ case ar_each_key_cmp:
+ if (dst_keys[i] != pair->key) return 1;
+ break;
+ case ar_each_key_insert:
+ if (UNDEF_P(pair->key)) continue; // deleted entry
+ rb_st_add_direct_with_hash(new_tab, pair->key, pair->val, hashes[i]);
+ break;
+ }
}
- new_tab = st_init_table_with_size(&objhash, size * 2);
-
- for (i = 0; i < RHASH_AR_TABLE_MAX_BOUND; i++) {
- ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
- st_add_direct(new_tab, pair->key, pair->val);
- }
- ar_free_and_clear_table(hash);
- RHASH_ST_TABLE_SET(hash, new_tab);
- return;
+ return 0;
}
+
+
static st_table *
ar_force_convert_table(VALUE hash, const char *file, int line)
{
@@ -799,22 +813,32 @@ ar_force_convert_table(VALUE hash, const char *file, int line)
}
if (RHASH_AR_TABLE(hash)) {
- unsigned i, bound = RHASH_AR_TABLE_BOUND(hash);
-
-#if defined(RHASH_CONVERT_TABLE_DEBUG) && RHASH_CONVERT_TABLE_DEBUG
- rb_obj_info_dump(hash);
- fprintf(stderr, "force_convert: %s:%d\n", file, line);
- RB_DEBUG_COUNTER_INC(obj_hash_force_convert);
-#endif
+ ar_table *ar = RHASH_AR_TABLE(hash);
+ st_hash_t hashes[RHASH_AR_TABLE_MAX_SIZE];
+ unsigned int bound, size;
+
+ // prepare hash values
+ do {
+ st_data_t keys[RHASH_AR_TABLE_MAX_SIZE];
+ bound = RHASH_AR_TABLE_BOUND(hash);
+ size = RHASH_AR_TABLE_SIZE(hash);
+ ar_each_key(ar, bound, ar_each_key_copy, keys, NULL, NULL);
+
+ for (unsigned int i = 0; i < bound; i++) {
+ // do_hash calls #hash method and it can modify hash object
+ hashes[i] = UNDEF_P(keys[i]) ? 0 : ar_do_hash(keys[i]);
+ }
- new_tab = st_init_table_with_size(&objhash, RHASH_AR_TABLE_SIZE(hash));
+ // check if modified
+ if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) return RHASH_ST_TABLE(hash);
+ if (UNLIKELY(RHASH_AR_TABLE_BOUND(hash) != bound)) continue;
+ if (UNLIKELY(ar_each_key(ar, bound, ar_each_key_cmp, keys, NULL, NULL))) continue;
+ } while (0);
- for (i = 0; i < bound; i++) {
- if (ar_cleared_entry(hash, i)) continue;
- ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
- st_add_direct(new_tab, pair->key, pair->val);
- }
+ // make st
+ new_tab = st_init_table_with_size(&objhash, size);
+ ar_each_key(ar, bound, ar_each_key_insert, NULL, new_tab, hashes);
ar_free_and_clear_table(hash);
}
else {
@@ -963,7 +987,7 @@ ar_foreach(VALUE hash, st_foreach_callback_func *func, st_data_t arg)
static int
ar_foreach_check(VALUE hash, st_foreach_check_callback_func *func, st_data_t arg,
- st_data_t never)
+ st_data_t never)
{
if (RHASH_AR_TABLE_SIZE(hash) > 0) {
unsigned i, ret = 0, bound = RHASH_AR_TABLE_BOUND(hash);
@@ -984,13 +1008,13 @@ ar_foreach_check(VALUE hash, st_foreach_check_callback_func *func, st_data_t arg
switch (retval) {
case ST_CHECK: {
- pair = RHASH_AR_TABLE_REF(hash, i);
- if (pair->key == never) break;
- ret = ar_find_entry_hint(hash, hint, key);
- if (ret == RHASH_AR_TABLE_MAX_BOUND) {
- retval = (*func)(0, 0, arg, 1);
- return 2;
- }
+ pair = RHASH_AR_TABLE_REF(hash, i);
+ if (pair->key == never) break;
+ ret = ar_find_entry_hint(hash, hint, key);
+ if (ret == RHASH_AR_TABLE_MAX_BOUND) {
+ retval = (*func)(0, 0, arg, 1);
+ return 2;
+ }
}
case ST_CONTINUE:
break;
@@ -998,11 +1022,11 @@ ar_foreach_check(VALUE hash, st_foreach_check_callback_func *func, st_data_t arg
case ST_REPLACE:
return 0;
case ST_DELETE: {
- if (!ar_cleared_entry(hash, i)) {
- ar_clear_entry(hash, i);
- RHASH_AR_TABLE_SIZE_DEC(hash);
- }
- break;
+ if (!ar_cleared_entry(hash, i)) {
+ ar_clear_entry(hash, i);
+ RHASH_AR_TABLE_SIZE_DEC(hash);
+ }
+ break;
}
}
}
@@ -1319,7 +1343,7 @@ foreach_safe_i(st_data_t key, st_data_t value, st_data_t args, int error)
if (error) return ST_STOP;
status = (*arg->func)(key, value, arg->arg);
if (status == ST_CONTINUE) {
- return ST_CHECK;
+ return ST_CHECK;
}
return status;
}
@@ -1333,7 +1357,7 @@ st_foreach_safe(st_table *table, st_foreach_func *func, st_data_t a)
arg.func = (st_foreach_func *)func;
arg.arg = a;
if (st_foreach_check(table, foreach_safe_i, (st_data_t)&arg, 0)) {
- rb_raise(rb_eRuntimeError, "hash modified during iteration");
+ rb_raise(rb_eRuntimeError, "hash modified during iteration");
}
}
@@ -1346,15 +1370,8 @@ struct hash_foreach_arg {
};
static int
-hash_ar_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error)
+hash_iter_status_check(int status)
{
- struct hash_foreach_arg *arg = (struct hash_foreach_arg *)argp;
- int status;
-
- if (error) return ST_STOP;
- status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);
- /* TODO: rehash check? rb_raise(rb_eRuntimeError, "rehash occurred during iteration"); */
-
switch (status) {
case ST_DELETE:
return ST_DELETE;
@@ -1363,31 +1380,38 @@ hash_ar_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error)
case ST_STOP:
return ST_STOP;
}
+
return ST_CHECK;
}
static int
+hash_ar_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error)
+{
+ struct hash_foreach_arg *arg = (struct hash_foreach_arg *)argp;
+
+ if (error) return ST_STOP;
+
+ int status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);
+ /* TODO: rehash check? rb_raise(rb_eRuntimeError, "rehash occurred during iteration"); */
+
+ return hash_iter_status_check(status);
+}
+
+static int
hash_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error)
{
struct hash_foreach_arg *arg = (struct hash_foreach_arg *)argp;
- int status;
- st_table *tbl;
if (error) return ST_STOP;
- tbl = RHASH_ST_TABLE(arg->hash);
- status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);
+
+ st_table *tbl = RHASH_ST_TABLE(arg->hash);
+ int status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);
+
if (RHASH_ST_TABLE(arg->hash) != tbl) {
- rb_raise(rb_eRuntimeError, "rehash occurred during iteration");
- }
- switch (status) {
- case ST_DELETE:
- return ST_DELETE;
- case ST_CONTINUE:
- break;
- case ST_STOP:
- return ST_STOP;
+ rb_raise(rb_eRuntimeError, "rehash occurred during iteration");
}
- return ST_CHECK;
+
+ return hash_iter_status_check(status);
}
static int
@@ -1406,13 +1430,19 @@ iter_lev_in_ivar_set(VALUE hash, int lev)
rb_ivar_set_internal(hash, id_hash_iter_lev, INT2FIX(lev));
}
-static int
+static inline int
iter_lev_in_flags(VALUE hash)
{
unsigned int u = (unsigned int)((RBASIC(hash)->flags >> RHASH_LEV_SHIFT) & RHASH_LEV_MAX);
return (int)u;
}
+static inline void
+iter_lev_in_flags_set(VALUE hash, int lev)
+{
+ RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((VALUE)lev << RHASH_LEV_SHIFT));
+}
+
static int
RHASH_ITER_LEV(VALUE hash)
{
@@ -1436,7 +1466,7 @@ hash_iter_lev_inc(VALUE hash)
}
else {
lev += 1;
- RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((VALUE)lev << RHASH_LEV_SHIFT));
+ iter_lev_in_flags_set(hash, lev);
if (lev == RHASH_LEV_MAX) {
iter_lev_in_ivar_set(hash, lev);
}
@@ -1454,7 +1484,7 @@ hash_iter_lev_dec(VALUE hash)
}
else {
HASH_ASSERT(lev > 0);
- RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((lev-1) << RHASH_LEV_SHIFT));
+ iter_lev_in_flags_set(hash, lev - 1);
}
}
@@ -1533,6 +1563,16 @@ rb_hash_foreach(VALUE hash, rb_foreach_func *func, VALUE farg)
hash_verify(hash);
}
+void rb_st_compact_table(st_table *tab);
+
+static void
+compact_after_delete(VALUE hash)
+{
+ if (RHASH_ITER_LEV(hash) == 0 && RHASH_ST_TABLE_P(hash)) {
+ rb_st_compact_table(RHASH_ST_TABLE(hash));
+ }
+}
+
static VALUE
hash_alloc_flags(VALUE klass, VALUE flags, VALUE ifnone)
{
@@ -1705,7 +1745,7 @@ rb_hash_stlike_update(VALUE hash, st_data_t key, st_update_callback_func *func,
if (RHASH_AR_TABLE_P(hash)) {
int result = ar_update(hash, key, func, arg);
if (result == -1) {
- ar_try_convert_table(hash);
+ ar_force_convert_table(hash, __FILE__, __LINE__);
}
else {
return result;
@@ -1775,12 +1815,12 @@ static void
set_proc_default(VALUE hash, VALUE proc)
{
if (rb_proc_lambda_p(proc)) {
- int n = rb_proc_arity(proc);
+ int n = rb_proc_arity(proc);
- if (n != 2 && (n >= 0 || n < -3)) {
- if (n < 0) n = -n-1;
- rb_raise(rb_eTypeError, "default_proc takes two arguments (2 for %d)", n);
- }
+ if (n != 2 && (n >= 0 || n < -3)) {
+ if (n < 0) n = -n-1;
+ rb_raise(rb_eTypeError, "default_proc takes two arguments (2 for %d)", n);
+ }
}
FL_SET_RAW(hash, RHASH_PROC_DEFAULT);
@@ -1825,14 +1865,14 @@ rb_hash_initialize(int argc, VALUE *argv, VALUE hash)
rb_hash_modify(hash);
if (rb_block_given_p()) {
- rb_check_arity(argc, 0, 0);
- ifnone = rb_block_proc();
- SET_PROC_DEFAULT(hash, ifnone);
+ rb_check_arity(argc, 0, 0);
+ ifnone = rb_block_proc();
+ SET_PROC_DEFAULT(hash, ifnone);
}
else {
- rb_check_arity(argc, 0, 1);
- ifnone = argc == 0 ? Qnil : argv[0];
- RHASH_SET_IFNONE(hash, ifnone);
+ rb_check_arity(argc, 0, 1);
+ ifnone = argc == 0 ? Qnil : argv[0];
+ RHASH_SET_IFNONE(hash, ifnone);
}
return hash;
@@ -1880,42 +1920,42 @@ rb_hash_s_create(int argc, VALUE *argv, VALUE klass)
if (argc == 1) {
tmp = rb_hash_s_try_convert(Qnil, argv[0]);
- if (!NIL_P(tmp)) {
- hash = hash_alloc(klass);
+ if (!NIL_P(tmp)) {
+ hash = hash_alloc(klass);
hash_copy(hash, tmp);
- return hash;
- }
-
- tmp = rb_check_array_type(argv[0]);
- if (!NIL_P(tmp)) {
- long i;
-
- hash = hash_alloc(klass);
- for (i = 0; i < RARRAY_LEN(tmp); ++i) {
- VALUE e = RARRAY_AREF(tmp, i);
- VALUE v = rb_check_array_type(e);
- VALUE key, val = Qnil;
-
- if (NIL_P(v)) {
- rb_raise(rb_eArgError, "wrong element type %s at %ld (expected array)",
- rb_builtin_class_name(e), i);
- }
- switch (RARRAY_LEN(v)) {
- default:
- rb_raise(rb_eArgError, "invalid number of elements (%ld for 1..2)",
- RARRAY_LEN(v));
- case 2:
- val = RARRAY_AREF(v, 1);
- case 1:
- key = RARRAY_AREF(v, 0);
- rb_hash_aset(hash, key, val);
- }
- }
- return hash;
- }
+ return hash;
+ }
+
+ tmp = rb_check_array_type(argv[0]);
+ if (!NIL_P(tmp)) {
+ long i;
+
+ hash = hash_alloc(klass);
+ for (i = 0; i < RARRAY_LEN(tmp); ++i) {
+ VALUE e = RARRAY_AREF(tmp, i);
+ VALUE v = rb_check_array_type(e);
+ VALUE key, val = Qnil;
+
+ if (NIL_P(v)) {
+ rb_raise(rb_eArgError, "wrong element type %s at %ld (expected array)",
+ rb_builtin_class_name(e), i);
+ }
+ switch (RARRAY_LEN(v)) {
+ default:
+ rb_raise(rb_eArgError, "invalid number of elements (%ld for 1..2)",
+ RARRAY_LEN(v));
+ case 2:
+ val = RARRAY_AREF(v, 1);
+ case 1:
+ key = RARRAY_AREF(v, 0);
+ rb_hash_aset(hash, key, val);
+ }
+ }
+ return hash;
+ }
}
if (argc % 2 != 0) {
- rb_raise(rb_eArgError, "odd number of arguments for Hash");
+ rb_raise(rb_eArgError, "odd number of arguments for Hash");
}
hash = hash_alloc(klass);
@@ -2038,7 +2078,7 @@ rb_hash_rehash(VALUE hash)
st_table *tbl;
if (RHASH_ITER_LEV(hash) > 0) {
- rb_raise(rb_eRuntimeError, "rehash during iteration");
+ rb_raise(rb_eRuntimeError, "rehash during iteration");
}
rb_hash_modify_check(hash);
if (RHASH_AR_TABLE_P(hash)) {
@@ -2070,17 +2110,31 @@ call_default_proc(VALUE proc, VALUE hash, VALUE key)
return rb_proc_call_with_block(proc, 2, args, Qnil);
}
+static bool
+rb_hash_default_unredefined(VALUE hash)
+{
+ VALUE klass = RBASIC_CLASS(hash);
+ if (LIKELY(klass == rb_cHash)) {
+ return !!BASIC_OP_UNREDEFINED_P(BOP_DEFAULT, HASH_REDEFINED_OP_FLAG);
+ }
+ else {
+ return LIKELY(rb_method_basic_definition_p(klass, id_default));
+ }
+}
+
VALUE
rb_hash_default_value(VALUE hash, VALUE key)
{
- if (LIKELY(rb_method_basic_definition_p(CLASS_OF(hash), id_default))) {
- VALUE ifnone = RHASH_IFNONE(hash);
- if (!FL_TEST(hash, RHASH_PROC_DEFAULT)) return ifnone;
- if (key == Qundef) return Qnil;
+ RUBY_ASSERT(RB_TYPE_P(hash, T_HASH));
+
+ if (LIKELY(rb_hash_default_unredefined(hash))) {
+ VALUE ifnone = RHASH_IFNONE(hash);
+ if (LIKELY(!FL_TEST_RAW(hash, RHASH_PROC_DEFAULT))) return ifnone;
+ if (UNDEF_P(key)) return Qnil;
return call_default_proc(ifnone, hash, key);
}
else {
- return rb_funcall(hash, id_default, 1, key);
+ return rb_funcall(hash, id_default, 1, key);
}
}
@@ -2184,7 +2238,7 @@ rb_hash_fetch_m(int argc, VALUE *argv, VALUE hash)
block_given = rb_block_given_p();
if (block_given && argc == 2) {
- rb_warn("block supersedes default value argument");
+ rb_warn("block supersedes default value argument");
}
if (hash_stlike_lookup(hash, key, &val)) {
@@ -2242,8 +2296,8 @@ rb_hash_default(int argc, VALUE *argv, VALUE hash)
rb_check_arity(argc, 0, 1);
ifnone = RHASH_IFNONE(hash);
if (FL_TEST(hash, RHASH_PROC_DEFAULT)) {
- if (argc == 0) return Qnil;
- return call_default_proc(ifnone, hash, argv[0]);
+ if (argc == 0) return Qnil;
+ return call_default_proc(ifnone, hash, argv[0]);
}
return ifnone;
}
@@ -2285,7 +2339,7 @@ static VALUE
rb_hash_default_proc(VALUE hash)
{
if (FL_TEST(hash, RHASH_PROC_DEFAULT)) {
- return RHASH_IFNONE(hash);
+ return RHASH_IFNONE(hash);
}
return Qnil;
}
@@ -2311,14 +2365,14 @@ rb_hash_set_default_proc(VALUE hash, VALUE proc)
rb_hash_modify_check(hash);
if (NIL_P(proc)) {
- SET_DEFAULT(hash, proc);
- return proc;
+ SET_DEFAULT(hash, proc);
+ return proc;
}
b = rb_check_convert_type_with_id(proc, T_DATA, "Proc", idTo_proc);
if (NIL_P(b) || !rb_obj_is_proc(b)) {
- rb_raise(rb_eTypeError,
- "wrong default_proc type %s (expected Proc)",
- rb_obj_classname(proc));
+ rb_raise(rb_eTypeError,
+ "wrong default_proc type %s (expected Proc)",
+ rb_obj_classname(proc));
}
proc = b;
SET_PROC_DEFAULT(hash, proc);
@@ -2331,8 +2385,8 @@ key_i(VALUE key, VALUE value, VALUE arg)
VALUE *args = (VALUE *)arg;
if (rb_equal(value, args[0])) {
- args[1] = key;
- return ST_STOP;
+ args[1] = key;
+ return ST_STOP;
}
return ST_CONTINUE;
}
@@ -2402,11 +2456,11 @@ rb_hash_delete(VALUE hash, VALUE key)
{
VALUE deleted_value = rb_hash_delete_entry(hash, key);
- if (deleted_value != Qundef) { /* likely pass */
- return deleted_value;
+ if (!UNDEF_P(deleted_value)) { /* likely pass */
+ return deleted_value;
}
else {
- return Qnil;
+ return Qnil;
}
}
@@ -2445,16 +2499,17 @@ rb_hash_delete_m(VALUE hash, VALUE key)
rb_hash_modify_check(hash);
val = rb_hash_delete_entry(hash, key);
- if (val != Qundef) {
- return val;
+ if (!UNDEF_P(val)) {
+ compact_after_delete(hash);
+ return val;
}
else {
- if (rb_block_given_p()) {
- return rb_yield(key);
- }
- else {
- return Qnil;
- }
+ if (rb_block_given_p()) {
+ return rb_yield(key);
+ }
+ else {
+ return Qnil;
+ }
}
}
@@ -2494,15 +2549,15 @@ rb_hash_shift(VALUE hash)
rb_hash_modify_check(hash);
if (RHASH_AR_TABLE_P(hash)) {
- var.key = Qundef;
- if (RHASH_ITER_LEV(hash) == 0) {
+ var.key = Qundef;
+ if (RHASH_ITER_LEV(hash) == 0) {
if (ar_shift(hash, &var.key, &var.val)) {
- return rb_assoc_new(var.key, var.val);
- }
- }
- else {
+ return rb_assoc_new(var.key, var.val);
+ }
+ }
+ else {
rb_hash_foreach(hash, shift_i_safe, (VALUE)&var);
- if (var.key != Qundef) {
+ if (!UNDEF_P(var.key)) {
rb_hash_delete_entry(hash, var.key);
return rb_assoc_new(var.key, var.val);
}
@@ -2516,12 +2571,12 @@ rb_hash_shift(VALUE hash)
}
}
else {
- rb_hash_foreach(hash, shift_i_safe, (VALUE)&var);
- if (var.key != Qundef) {
- rb_hash_delete_entry(hash, var.key);
- return rb_assoc_new(var.key, var.val);
- }
- }
+ rb_hash_foreach(hash, shift_i_safe, (VALUE)&var);
+ if (!UNDEF_P(var.key)) {
+ rb_hash_delete_entry(hash, var.key);
+ return rb_assoc_new(var.key, var.val);
+ }
+ }
}
return Qnil;
}
@@ -2530,8 +2585,8 @@ static int
delete_if_i(VALUE key, VALUE value, VALUE hash)
{
if (RTEST(rb_yield_values(2, key, value))) {
- rb_hash_modify(hash);
- return ST_DELETE;
+ rb_hash_modify(hash);
+ return ST_DELETE;
}
return ST_CONTINUE;
}
@@ -2566,6 +2621,7 @@ rb_hash_delete_if(VALUE hash)
rb_hash_modify_check(hash);
if (!RHASH_TABLE_EMPTY_P(hash)) {
rb_hash_foreach(hash, delete_if_i, hash);
+ compact_after_delete(hash);
}
return hash;
}
@@ -2628,7 +2684,8 @@ rb_hash_reject(VALUE hash)
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
result = hash_dup_with_compare_by_id(hash);
if (!RHASH_EMPTY_P(hash)) {
- rb_hash_foreach(result, delete_if_i, result);
+ rb_hash_foreach(result, delete_if_i, result);
+ compact_after_delete(result);
}
return result;
}
@@ -2656,10 +2713,10 @@ rb_hash_slice(int argc, VALUE *argv, VALUE hash)
result = copy_compare_by_id(rb_hash_new_with_size(argc), hash);
for (i = 0; i < argc; i++) {
- key = argv[i];
- value = rb_hash_lookup2(hash, key, Qundef);
- if (value != Qundef)
- rb_hash_aset(result, key, value);
+ key = argv[i];
+ value = rb_hash_lookup2(hash, key, Qundef);
+ if (!UNDEF_P(value))
+ rb_hash_aset(result, key, value);
}
return result;
@@ -2688,6 +2745,7 @@ rb_hash_except(int argc, VALUE *argv, VALUE hash)
key = argv[i];
rb_hash_delete(result, key);
}
+ compact_after_delete(result);
return result;
}
@@ -2712,7 +2770,7 @@ rb_hash_values_at(int argc, VALUE *argv, VALUE hash)
long i;
for (i=0; i<argc; i++) {
- rb_ary_push(result, rb_hash_aref(hash, argv[i]));
+ rb_ary_push(result, rb_hash_aref(hash, argv[i]));
}
return result;
}
@@ -2744,7 +2802,7 @@ rb_hash_fetch_values(int argc, VALUE *argv, VALUE hash)
long i;
for (i=0; i<argc; i++) {
- rb_ary_push(result, rb_hash_fetch(hash, argv[i]));
+ rb_ary_push(result, rb_hash_fetch(hash, argv[i]));
}
return result;
}
@@ -2753,8 +2811,8 @@ static int
keep_if_i(VALUE key, VALUE value, VALUE hash)
{
if (!RTEST(rb_yield_values(2, key, value))) {
- rb_hash_modify(hash);
- return ST_DELETE;
+ rb_hash_modify(hash);
+ return ST_DELETE;
}
return ST_CONTINUE;
}
@@ -2784,7 +2842,8 @@ rb_hash_select(VALUE hash)
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
result = hash_dup_with_compare_by_id(hash);
if (!RHASH_EMPTY_P(hash)) {
- rb_hash_foreach(result, keep_if_i, result);
+ rb_hash_foreach(result, keep_if_i, result);
+ compact_after_delete(result);
}
return result;
}
@@ -2876,6 +2935,7 @@ rb_hash_clear(VALUE hash)
}
else {
st_clear(RHASH_ST_TABLE(hash));
+ compact_after_delete(hash);
}
return hash;
@@ -2895,7 +2955,7 @@ rb_hash_key_str(VALUE key)
return rb_fstring(key);
}
else {
- return rb_str_new_frozen(key);
+ return rb_str_new_frozen(key);
}
}
@@ -2903,7 +2963,7 @@ static int
hash_aset_str(st_data_t *key, st_data_t *val, struct update_arg *arg, int existing)
{
if (!existing && !RB_OBJ_FROZEN(*key)) {
- *key = rb_hash_key_str(*key);
+ *key = rb_hash_key_str(*key);
}
return hash_aset(key, val, arg, existing);
}
@@ -2945,15 +3005,15 @@ rb_hash_aset(VALUE hash, VALUE key, VALUE val)
rb_hash_modify(hash);
if (RHASH_TABLE_NULL_P(hash)) {
- if (iter_lev > 0) no_new_key();
+ if (iter_lev > 0) no_new_key();
ar_alloc_table(hash);
}
- if (RHASH_TYPE(hash) == &identhash || rb_obj_class(key) != rb_cString) {
- RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset, val);
+ if (!RHASH_STRING_KEY_P(hash, key)) {
+ RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset, val);
}
else {
- RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset_str, val);
+ RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset_str, val);
}
return val;
}
@@ -3163,9 +3223,9 @@ rb_hash_each_pair(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
if (rb_block_pair_yield_optimizable())
- rb_hash_foreach(hash, each_pair_i_fast, 0);
+ rb_hash_foreach(hash, each_pair_i_fast, 0);
else
- rb_hash_foreach(hash, each_pair_i, 0);
+ rb_hash_foreach(hash, each_pair_i, 0);
return hash;
}
@@ -3181,7 +3241,7 @@ transform_keys_hash_i(VALUE key, VALUE value, VALUE transarg)
struct transform_keys_args *p = (void *)transarg;
VALUE trans = p->trans, result = p->result;
VALUE new_key = rb_hash_lookup2(trans, key, Qundef);
- if (new_key == Qundef) {
+ if (UNDEF_P(new_key)) {
if (p->block_given)
new_key = rb_yield(key);
else
@@ -3294,7 +3354,7 @@ rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash)
if (!RHASH_TABLE_EMPTY_P(hash)) {
long i;
VALUE new_keys = hash_alloc(0);
- VALUE pairs = rb_ary_tmp_new(RHASH_SIZE(hash) * 2);
+ VALUE pairs = rb_ary_hidden_new(RHASH_SIZE(hash) * 2);
rb_hash_foreach(hash, flatten_i, pairs);
for (i = 0; i < RARRAY_LEN(pairs); i += 2) {
VALUE key = RARRAY_AREF(pairs, i), new_key, val;
@@ -3302,7 +3362,7 @@ rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash)
if (!trans) {
new_key = rb_yield(key);
}
- else if ((new_key = rb_hash_lookup2(trans, key, Qundef)) != Qundef) {
+ else if (!UNDEF_P(new_key = rb_hash_lookup2(trans, key, Qundef))) {
/* use the transformed key */
}
else if (block_given) {
@@ -3321,6 +3381,7 @@ rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash)
rb_ary_clear(pairs);
rb_hash_clear(new_keys);
}
+ compact_after_delete(hash);
return hash;
}
@@ -3371,6 +3432,7 @@ rb_hash_transform_values(VALUE hash)
if (!RHASH_EMPTY_P(hash)) {
rb_hash_stlike_foreach_with_replace(result, transform_values_foreach_func, transform_values_foreach_replace, result);
+ compact_after_delete(result);
}
return result;
@@ -3439,10 +3501,10 @@ inspect_i(VALUE key, VALUE value, VALUE str)
str2 = rb_inspect(key);
if (RSTRING_LEN(str) > 1) {
- rb_str_buf_cat_ascii(str, ", ");
+ rb_str_buf_cat_ascii(str, ", ");
}
else {
- rb_enc_copy(str, str2);
+ rb_enc_copy(str, str2);
}
rb_str_buf_append(str, str2);
rb_str_buf_cat_ascii(str, "=>");
@@ -3480,7 +3542,7 @@ static VALUE
rb_hash_inspect(VALUE hash)
{
if (RHASH_EMPTY_P(hash))
- return rb_usascii_str_new2("{}");
+ return rb_usascii_str_new2("{}");
return rb_exec_recursive(inspect_hash, hash, 0);
}
@@ -3555,7 +3617,7 @@ rb_hash_to_h(VALUE hash)
return rb_hash_to_h_block(hash);
}
if (rb_obj_class(hash) != rb_cHash) {
- const VALUE flags = RBASIC(hash)->flags;
+ const VALUE flags = RBASIC(hash)->flags;
hash = hash_dup(hash, rb_cHash, flags & RHASH_PROC_DEFAULT);
}
return hash;
@@ -3596,10 +3658,10 @@ rb_hash_keys(VALUE hash)
}
});
rb_gc_writebarrier_remember(keys);
- rb_ary_set_len(keys, size);
+ rb_ary_set_len(keys, size);
}
else {
- rb_hash_foreach(hash, keys_i, keys);
+ rb_hash_foreach(hash, keys_i, keys);
}
return keys;
@@ -3644,11 +3706,11 @@ rb_hash_values(VALUE hash)
size = st_values(table, ptr, size);
});
}
- rb_ary_set_len(values, size);
+ rb_ary_set_len(values, size);
}
else {
- rb_hash_foreach(hash, values_i, values);
+ rb_hash_foreach(hash, values_i, values);
}
return values;
@@ -3678,8 +3740,8 @@ rb_hash_search_value(VALUE key, VALUE value, VALUE arg)
VALUE *data = (VALUE *)arg;
if (rb_equal(value, data[1])) {
- data[0] = Qtrue;
- return ST_STOP;
+ data[0] = Qtrue;
+ return ST_STOP;
}
return ST_CONTINUE;
}
@@ -3750,23 +3812,23 @@ hash_equal(VALUE hash1, VALUE hash2, int eql)
if (hash1 == hash2) return Qtrue;
if (!RB_TYPE_P(hash2, T_HASH)) {
- if (!rb_respond_to(hash2, idTo_hash)) {
- return Qfalse;
- }
- if (eql) {
- if (rb_eql(hash2, hash1)) {
- return Qtrue;
- }
- else {
- return Qfalse;
- }
- }
- else {
- return rb_equal(hash2, hash1);
- }
+ if (!rb_respond_to(hash2, idTo_hash)) {
+ return Qfalse;
+ }
+ if (eql) {
+ if (rb_eql(hash2, hash1)) {
+ return Qtrue;
+ }
+ else {
+ return Qfalse;
+ }
+ }
+ else {
+ return rb_equal(hash2, hash1);
+ }
}
if (RHASH_SIZE(hash1) != RHASH_SIZE(hash2))
- return Qfalse;
+ return Qfalse;
if (!RHASH_TABLE_EMPTY_P(hash1) && !RHASH_TABLE_EMPTY_P(hash2)) {
if (RHASH_TYPE(hash1) != RHASH_TYPE(hash2)) {
return Qfalse;
@@ -3781,7 +3843,7 @@ hash_equal(VALUE hash1, VALUE hash2, int eql)
#if 0
if (!(rb_equal(RHASH_IFNONE(hash1), RHASH_IFNONE(hash2)) &&
FL_TEST(hash1, RHASH_PROC_DEFAULT) == FL_TEST(hash2, RHASH_PROC_DEFAULT)))
- return Qfalse;
+ return Qfalse;
#endif
return Qtrue;
}
@@ -3869,7 +3931,7 @@ rb_hash_hash(VALUE hash)
st_index_t hval = rb_hash_start(size);
hval = rb_hash_uint(hval, (st_index_t)rb_hash_hash);
if (size) {
- rb_hash_foreach(hash, hash_i, (VALUE)&hval);
+ rb_hash_foreach(hash, hash_i, (VALUE)&hval);
}
hval = rb_hash_end(hval);
return ST2FIX(hval);
@@ -3907,18 +3969,9 @@ rb_hash_invert(VALUE hash)
}
static int
-rb_hash_update_callback(st_data_t *key, st_data_t *value, struct update_arg *arg, int existing)
-{
- *value = arg->arg;
- return ST_CONTINUE;
-}
-
-NOINSERT_UPDATE_CALLBACK(rb_hash_update_callback)
-
-static int
rb_hash_update_i(VALUE key, VALUE value, VALUE hash)
{
- RHASH_UPDATE(hash, key, rb_hash_update_callback, value);
+ rb_hash_aset(hash, key, value);
return ST_CONTINUE;
}
@@ -3930,6 +3983,9 @@ rb_hash_update_block_callback(st_data_t *key, st_data_t *value, struct update_ar
if (existing) {
newvalue = (st_data_t)rb_yield_values(3, (VALUE)*key, (VALUE)*value, (VALUE)newvalue);
}
+ else if (RHASH_STRING_KEY_P(arg->hash, *key) && !RB_OBJ_FROZEN(*key)) {
+ *key = rb_hash_key_str(*key);
+ }
*value = newvalue;
return ST_CONTINUE;
}
@@ -4025,7 +4081,7 @@ rb_hash_update_func_callback(st_data_t *key, st_data_t *value, struct update_arg
VALUE newvalue = uf_arg->value;
if (existing) {
- newvalue = (*uf_arg->func)((VALUE)*key, (VALUE)*value, newvalue);
+ newvalue = (*uf_arg->func)((VALUE)*key, (VALUE)*value, newvalue);
}
*value = newvalue;
return ST_CONTINUE;
@@ -4050,13 +4106,13 @@ rb_hash_update_by(VALUE hash1, VALUE hash2, rb_hash_update_func *func)
rb_hash_modify(hash1);
hash2 = to_hash(hash2);
if (func) {
- struct update_func_arg arg;
- arg.hash = hash1;
- arg.func = func;
- rb_hash_foreach(hash2, rb_hash_update_func_i, (VALUE)&arg);
+ struct update_func_arg arg;
+ arg.hash = hash1;
+ arg.func = func;
+ rb_hash_foreach(hash2, rb_hash_update_func_i, (VALUE)&arg);
}
else {
- rb_hash_foreach(hash2, rb_hash_update_i, hash1);
+ rb_hash_foreach(hash2, rb_hash_update_i, hash1);
}
return hash1;
}
@@ -4151,8 +4207,8 @@ assoc_i(VALUE key, VALUE val, VALUE arg)
VALUE *args = (VALUE *)arg;
if (RTEST(rb_equal(args[0], key))) {
- args[1] = rb_assoc_new(key, val);
- return ST_STOP;
+ args[1] = rb_assoc_new(key, val);
+ return ST_STOP;
}
return ST_CONTINUE;
}
@@ -4182,20 +4238,20 @@ rb_hash_assoc(VALUE hash, VALUE key)
table = RHASH_ST_TABLE(hash);
orighash = table->type;
- if (orighash != &identhash) {
- VALUE value;
- struct reset_hash_type_arg ensure_arg;
- struct st_hash_type assochash;
+ if (!RHASH_IDENTHASH_P(hash)) {
+ VALUE value;
+ struct reset_hash_type_arg ensure_arg;
+ struct st_hash_type assochash;
- assochash.compare = assoc_cmp;
- assochash.hash = orighash->hash;
+ assochash.compare = assoc_cmp;
+ assochash.hash = orighash->hash;
table->type = &assochash;
- args[0] = hash;
- args[1] = key;
- ensure_arg.hash = hash;
- ensure_arg.orighash = orighash;
- value = rb_ensure(lookup2_call, (VALUE)&args, reset_hash_type, (VALUE)&ensure_arg);
- if (value != Qundef) return rb_assoc_new(key, value);
+ args[0] = hash;
+ args[1] = key;
+ ensure_arg.hash = hash;
+ ensure_arg.orighash = orighash;
+ value = rb_ensure(lookup2_call, (VALUE)&args, reset_hash_type, (VALUE)&ensure_arg);
+ if (!UNDEF_P(value)) return rb_assoc_new(key, value);
}
args[0] = key;
@@ -4210,8 +4266,8 @@ rassoc_i(VALUE key, VALUE val, VALUE arg)
VALUE *args = (VALUE *)arg;
if (RTEST(rb_equal(args[0], val))) {
- args[1] = rb_assoc_new(key, val);
- return ST_STOP;
+ args[1] = rb_assoc_new(key, val);
+ return ST_STOP;
}
return ST_CONTINUE;
}
@@ -4291,26 +4347,26 @@ rb_hash_flatten(int argc, VALUE *argv, VALUE hash)
rb_check_arity(argc, 0, 1);
if (argc) {
- int level = NUM2INT(argv[0]);
+ int level = NUM2INT(argv[0]);
- if (level == 0) return rb_hash_to_a(hash);
+ if (level == 0) return rb_hash_to_a(hash);
- ary = rb_ary_new_capa(RHASH_SIZE(hash) * 2);
- rb_hash_foreach(hash, flatten_i, ary);
- level--;
+ ary = rb_ary_new_capa(RHASH_SIZE(hash) * 2);
+ rb_hash_foreach(hash, flatten_i, ary);
+ level--;
- if (level > 0) {
- VALUE ary_flatten_level = INT2FIX(level);
- rb_funcallv(ary, id_flatten_bang, 1, &ary_flatten_level);
- }
- else if (level < 0) {
- /* flatten recursively */
- rb_funcallv(ary, id_flatten_bang, 0, 0);
- }
+ if (level > 0) {
+ VALUE ary_flatten_level = INT2FIX(level);
+ rb_funcallv(ary, id_flatten_bang, 1, &ary_flatten_level);
+ }
+ else if (level < 0) {
+ /* flatten recursively */
+ rb_funcallv(ary, id_flatten_bang, 0, 0);
+ }
}
else {
- ary = rb_ary_new_capa(RHASH_SIZE(hash) * 2);
- rb_hash_foreach(hash, flatten_i, ary);
+ ary = rb_ary_new_capa(RHASH_SIZE(hash) * 2);
+ rb_hash_foreach(hash, flatten_i, ary);
}
return ary;
@@ -4320,7 +4376,7 @@ static int
delete_if_nil(VALUE key, VALUE value, VALUE hash)
{
if (NIL_P(value)) {
- return ST_DELETE;
+ return ST_DELETE;
}
return ST_CONTINUE;
}
@@ -4329,7 +4385,7 @@ static int
set_if_not_nil(VALUE key, VALUE value, VALUE hash)
{
if (!NIL_P(value)) {
- rb_hash_aset(hash, key, value);
+ rb_hash_aset(hash, key, value);
}
return ST_CONTINUE;
}
@@ -4349,7 +4405,7 @@ rb_hash_compact(VALUE hash)
{
VALUE result = rb_hash_new();
if (!RHASH_EMPTY_P(hash)) {
- rb_hash_foreach(hash, set_if_not_nil, result);
+ rb_hash_foreach(hash, set_if_not_nil, result);
}
return result;
}
@@ -4372,9 +4428,9 @@ rb_hash_compact_bang(VALUE hash)
rb_hash_modify_check(hash);
n = RHASH_SIZE(hash);
if (n) {
- rb_hash_foreach(hash, delete_if_nil, hash);
+ rb_hash_foreach(hash, delete_if_nil, hash);
if (n != RHASH_SIZE(hash))
- return hash;
+ return hash;
}
return Qnil;
}
@@ -4442,7 +4498,7 @@ rb_hash_compare_by_id(VALUE hash)
MJIT_FUNC_EXPORTED VALUE
rb_hash_compare_by_id_p(VALUE hash)
{
- return RBOOL(RHASH_ST_TABLE_P(hash) && RHASH_ST_TABLE(hash)->type == &identhash);
+ return RBOOL(RHASH_IDENTHASH_P(hash));
}
VALUE
@@ -4478,8 +4534,8 @@ any_p_i(VALUE key, VALUE value, VALUE arg)
{
VALUE ret = rb_yield(rb_assoc_new(key, value));
if (RTEST(ret)) {
- *(VALUE *)arg = Qtrue;
- return ST_STOP;
+ *(VALUE *)arg = Qtrue;
+ return ST_STOP;
}
return ST_CONTINUE;
}
@@ -4489,8 +4545,8 @@ any_p_i_fast(VALUE key, VALUE value, VALUE arg)
{
VALUE ret = rb_yield_values(2, key, value);
if (RTEST(ret)) {
- *(VALUE *)arg = Qtrue;
- return ST_STOP;
+ *(VALUE *)arg = Qtrue;
+ return ST_STOP;
}
return ST_CONTINUE;
}
@@ -4500,8 +4556,8 @@ any_p_i_pattern(VALUE key, VALUE value, VALUE arg)
{
VALUE ret = rb_funcall(((VALUE *)arg)[1], idEqq, 1, rb_assoc_new(key, value));
if (RTEST(ret)) {
- *(VALUE *)arg = Qtrue;
- return ST_STOP;
+ *(VALUE *)arg = Qtrue;
+ return ST_STOP;
}
return ST_CONTINUE;
}
@@ -4547,19 +4603,19 @@ rb_hash_any_p(int argc, VALUE *argv, VALUE hash)
if (rb_block_given_p()) {
rb_warn("given block not used");
}
- args[1] = argv[0];
+ args[1] = argv[0];
- rb_hash_foreach(hash, any_p_i_pattern, (VALUE)args);
+ rb_hash_foreach(hash, any_p_i_pattern, (VALUE)args);
}
else {
- if (!rb_block_given_p()) {
- /* yields pairs, never false */
- return Qtrue;
- }
+ if (!rb_block_given_p()) {
+ /* yields pairs, never false */
+ return Qtrue;
+ }
if (rb_block_pair_yield_optimizable())
- rb_hash_foreach(hash, any_p_i_fast, (VALUE)args);
- else
- rb_hash_foreach(hash, any_p_i, (VALUE)args);
+ rb_hash_foreach(hash, any_p_i_fast, (VALUE)args);
+ else
+ rb_hash_foreach(hash, any_p_i, (VALUE)args);
}
return args[0];
}
@@ -4608,7 +4664,7 @@ hash_le_i(VALUE key, VALUE value, VALUE arg)
{
VALUE *args = (VALUE *)arg;
VALUE v = rb_hash_lookup2(args[0], key, Qundef);
- if (v != Qundef && rb_equal(value, v)) return ST_CONTINUE;
+ if (!UNDEF_P(v) && rb_equal(value, v)) return ST_CONTINUE;
args[1] = Qfalse;
return ST_STOP;
}
@@ -4760,7 +4816,7 @@ rb_hash_add_new_element(VALUE hash, VALUE key, VALUE val)
if (ret != -1) {
return ret;
}
- ar_try_convert_table(hash);
+ ar_force_convert_table(hash, __FILE__, __LINE__);
}
tbl = RHASH_TBL_RAW(hash);
return st_update(tbl, (st_data_t)key, add_new_i, (st_data_t)args);
@@ -4912,12 +4968,12 @@ get_env_cstr(
char *var;
rb_encoding *enc = rb_enc_get(str);
if (!rb_enc_asciicompat(enc)) {
- rb_raise(rb_eArgError, "bad environment variable %s: ASCII incompatible encoding: %s",
- name, rb_enc_name(enc));
+ rb_raise(rb_eArgError, "bad environment variable %s: ASCII incompatible encoding: %s",
+ name, rb_enc_name(enc));
}
var = RSTRING_PTR(str);
if (memchr(var, '\0', RSTRING_LEN(str))) {
- rb_raise(rb_eArgError, "bad environment variable %s: contains null byte", name);
+ rb_raise(rb_eArgError, "bad environment variable %s: contains null byte", name);
}
return rb_str_fill_terminator(str, 1); /* ASCII compatible */
}
@@ -4939,7 +4995,7 @@ env_name(volatile VALUE *s)
static VALUE env_aset(VALUE nm, VALUE val);
static void
-reset_by_modified_env(const char *nam)
+reset_by_modified_env(const char *nam, const char *val)
{
/*
* ENV['TZ'] = nil has a special meaning.
@@ -4948,7 +5004,7 @@ reset_by_modified_env(const char *nam)
* This hack might works only on Linux glibc.
*/
if (ENVMATCH(nam, TZ_ENV)) {
- ruby_reset_timezone();
+ ruby_reset_timezone(val);
}
}
@@ -4956,7 +5012,7 @@ static VALUE
env_delete(VALUE name)
{
const char *nam = env_name(name);
- reset_by_modified_env(nam);
+ reset_by_modified_env(nam, NULL);
VALUE val = getenv_with_lock(nam);
if (!NIL_P(val)) {
@@ -5057,17 +5113,17 @@ env_fetch(int argc, VALUE *argv, VALUE _)
key = argv[0];
block_given = rb_block_given_p();
if (block_given && argc == 2) {
- rb_warn("block supersedes default value argument");
+ rb_warn("block supersedes default value argument");
}
nam = env_name(key);
env = getenv_with_lock(nam);
if (NIL_P(env)) {
- if (block_given) return rb_yield(key);
- if (argc == 1) {
- rb_key_err_raise(rb_sprintf("key not found: \"%"PRIsVALUE"\"", key), envtbl, key);
- }
- return argv[1];
+ if (block_given) return rb_yield(key);
+ if (argc == 1) {
+ rb_key_err_raise(rb_sprintf("key not found: \"%"PRIsVALUE"\"", key), envtbl, key);
+ }
+ return argv[1];
}
return env;
}
@@ -5079,7 +5135,7 @@ in_origenv(const char *str)
{
char **env;
for (env = origenviron; *env; ++env) {
- if (*env == str) return 1;
+ if (*env == str) return 1;
}
return 0;
}
@@ -5094,8 +5150,8 @@ envix(const char *nam)
env = GET_ENVIRON(environ);
for (i = 0; env[i]; i++) {
- if (ENVNMATCH(env[i],nam,len) && env[i][len] == '=')
- break; /* memcmp must come first to avoid */
+ if (ENVNMATCH(env[i],nam,len) && env[i][len] == '=')
+ break; /* memcmp must come first to avoid */
} /* potential SEGV's */
FREE_ENVIRON(environ);
return i;
@@ -5125,16 +5181,16 @@ static int
check_envsize(size_t n)
{
if (_WIN32_WINNT < 0x0600 && rb_w32_osver() < 6) {
- /* https://msdn.microsoft.com/en-us/library/windows/desktop/ms682653(v=vs.85).aspx */
- /* Windows Server 2003 and Windows XP: The maximum size of the
- * environment block for the process is 32,767 characters. */
- WCHAR* p = GetEnvironmentStringsW();
- if (!p) return -1; /* never happen */
- n += getenvsize(p);
- FreeEnvironmentStringsW(p);
- if (n >= getenvblocksize()) {
- return -1;
- }
+ /* https://msdn.microsoft.com/en-us/library/windows/desktop/ms682653(v=vs.85).aspx */
+ /* Windows Server 2003 and Windows XP: The maximum size of the
+ * environment block for the process is 32,767 characters. */
+ WCHAR* p = GetEnvironmentStringsW();
+ if (!p) return -1; /* never happen */
+ n += getenvsize(p);
+ FreeEnvironmentStringsW(p);
+ if (n >= getenvblocksize()) {
+ return -1;
+ }
}
return 0;
}
@@ -5155,7 +5211,7 @@ static const char *
check_envname(const char *name)
{
if (strchr(name, '=')) {
- invalid_envname(name);
+ invalid_envname(name);
}
return name;
}
@@ -5176,26 +5232,26 @@ ruby_setenv(const char *name, const char *value)
check_envname(name);
len = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0);
if (value) {
- int len2;
- len2 = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0);
- if (check_envsize((size_t)len + len2)) { /* len and len2 include '\0' */
- goto fail; /* 2 for '=' & '\0' */
- }
- wname = ALLOCV_N(WCHAR, buf, len + len2);
- wvalue = wname + len;
- MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, len);
- MultiByteToWideChar(CP_UTF8, 0, value, -1, wvalue, len2);
+ int len2;
+ len2 = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0);
+ if (check_envsize((size_t)len + len2)) { /* len and len2 include '\0' */
+ goto fail; /* 2 for '=' & '\0' */
+ }
+ wname = ALLOCV_N(WCHAR, buf, len + len2);
+ wvalue = wname + len;
+ MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, len);
+ MultiByteToWideChar(CP_UTF8, 0, value, -1, wvalue, len2);
#ifndef HAVE__WPUTENV_S
- wname[len-1] = L'=';
+ wname[len-1] = L'=';
#endif
}
else {
- wname = ALLOCV_N(WCHAR, buf, len + 1);
- MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, len);
- wvalue = wname + len;
- *wvalue = L'\0';
+ wname = ALLOCV_N(WCHAR, buf, len + 1);
+ MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, len);
+ wvalue = wname + len;
+ *wvalue = L'\0';
#ifndef HAVE__WPUTENV_S
- wname[len-1] = L'=';
+ wname[len-1] = L'=';
#endif
}
@@ -5213,13 +5269,13 @@ ruby_setenv(const char *name, const char *value)
/* even if putenv() failed, clean up and try to delete the
* variable from the system area. */
if (!value || !*value) {
- /* putenv() doesn't handle empty value */
- if (!SetEnvironmentVariable(name, value) &&
- GetLastError() != ERROR_ENVVAR_NOT_FOUND) goto fail;
+ /* putenv() doesn't handle empty value */
+ if (!SetEnvironmentVariable(name, value) &&
+ GetLastError() != ERROR_ENVVAR_NOT_FOUND) goto fail;
}
if (failed) {
fail:
- invalid_envname(name);
+ invalid_envname(name);
}
#elif defined(HAVE_SETENV) && defined(HAVE_UNSETENV)
if (value) {
@@ -5289,7 +5345,7 @@ ruby_setenv(const char *name, const char *value)
ENV_UNLOCK();
if (ret) {
- free(mem_ptr);
+ free(mem_ptr);
rb_sys_fail_str(rb_sprintf("putenv(%s)", name));
}
}
@@ -5406,7 +5462,7 @@ env_aset(VALUE nm, VALUE val)
if (NIL_P(val)) {
env_delete(nm);
- return Qnil;
+ return Qnil;
}
SafeStringValue(nm);
SafeStringValue(val);
@@ -5416,7 +5472,7 @@ env_aset(VALUE nm, VALUE val)
get_env_ptr(value, val);
ruby_setenv(name, value);
- reset_by_modified_env(name);
+ reset_by_modified_env(name, value);
return val;
}
@@ -5512,7 +5568,7 @@ env_each_key(VALUE ehash)
RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size);
keys = env_keys(FALSE);
for (i=0; i<RARRAY_LEN(keys); i++) {
- rb_yield(RARRAY_AREF(keys, i));
+ rb_yield(RARRAY_AREF(keys, i));
}
return ehash;
}
@@ -5584,7 +5640,7 @@ env_each_value(VALUE ehash)
RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size);
values = env_values();
for (i=0; i<RARRAY_LEN(values); i++) {
- rb_yield(RARRAY_AREF(values, i));
+ rb_yield(RARRAY_AREF(values, i));
}
return ehash;
}
@@ -5634,13 +5690,13 @@ env_each_pair(VALUE ehash)
if (rb_block_pair_yield_optimizable()) {
for (i=0; i<RARRAY_LEN(ary); i+=2) {
- rb_yield_values(2, RARRAY_AREF(ary, i), RARRAY_AREF(ary, i+1));
- }
+ rb_yield_values(2, RARRAY_AREF(ary, i), RARRAY_AREF(ary, i+1));
+ }
}
else {
- for (i=0; i<RARRAY_LEN(ary); i+=2) {
- rb_yield(rb_assoc_new(RARRAY_AREF(ary, i), RARRAY_AREF(ary, i+1)));
- }
+ for (i=0; i<RARRAY_LEN(ary); i+=2) {
+ rb_yield(rb_assoc_new(RARRAY_AREF(ary, i), RARRAY_AREF(ary, i+1)));
+ }
}
return ehash;
@@ -5679,13 +5735,13 @@ env_reject_bang(VALUE ehash)
keys = env_keys(FALSE);
RBASIC_CLEAR_CLASS(keys);
for (i=0; i<RARRAY_LEN(keys); i++) {
- VALUE val = rb_f_getenv(Qnil, RARRAY_AREF(keys, i));
- if (!NIL_P(val)) {
- if (RTEST(rb_yield_values(2, RARRAY_AREF(keys, i), val))) {
+ VALUE val = rb_f_getenv(Qnil, RARRAY_AREF(keys, i));
+ if (!NIL_P(val)) {
+ if (RTEST(rb_yield_values(2, RARRAY_AREF(keys, i), val))) {
env_delete(RARRAY_AREF(keys, i));
- del++;
- }
- }
+ del++;
+ }
+ }
}
RB_GC_GUARD(keys);
if (del == 0) return Qnil;
@@ -5745,7 +5801,7 @@ env_values_at(int argc, VALUE *argv, VALUE _)
result = rb_ary_new();
for (i=0; i<argc; i++) {
- rb_ary_push(result, rb_f_getenv(Qnil, argv[i]));
+ rb_ary_push(result, rb_f_getenv(Qnil, argv[i]));
}
return result;
}
@@ -5782,13 +5838,13 @@ env_select(VALUE ehash)
result = rb_hash_new();
keys = env_keys(FALSE);
for (i = 0; i < RARRAY_LEN(keys); ++i) {
- VALUE key = RARRAY_AREF(keys, i);
- VALUE val = rb_f_getenv(Qnil, key);
- if (!NIL_P(val)) {
- if (RTEST(rb_yield_values(2, key, val))) {
- rb_hash_aset(result, key, val);
- }
- }
+ VALUE key = RARRAY_AREF(keys, i);
+ VALUE val = rb_f_getenv(Qnil, key);
+ if (!NIL_P(val)) {
+ if (RTEST(rb_yield_values(2, key, val))) {
+ rb_hash_aset(result, key, val);
+ }
+ }
}
RB_GC_GUARD(keys);
@@ -5843,13 +5899,13 @@ env_select_bang(VALUE ehash)
keys = env_keys(FALSE);
RBASIC_CLEAR_CLASS(keys);
for (i=0; i<RARRAY_LEN(keys); i++) {
- VALUE val = rb_f_getenv(Qnil, RARRAY_AREF(keys, i));
- if (!NIL_P(val)) {
- if (!RTEST(rb_yield_values(2, RARRAY_AREF(keys, i), val))) {
+ VALUE val = rb_f_getenv(Qnil, RARRAY_AREF(keys, i));
+ if (!NIL_P(val)) {
+ if (!RTEST(rb_yield_values(2, RARRAY_AREF(keys, i), val))) {
env_delete(RARRAY_AREF(keys, i));
- del++;
- }
- }
+ del++;
+ }
+ }
}
RB_GC_GUARD(keys);
if (del == 0) return Qnil;
@@ -5971,24 +6027,23 @@ env_to_s(VALUE _)
static VALUE
env_inspect(VALUE _)
{
- VALUE i;
VALUE str = rb_str_buf_new2("{");
+ rb_encoding *enc = env_encoding();
ENV_LOCK();
{
char **env = GET_ENVIRON(environ);
while (*env) {
- char *s = strchr(*env, '=');
+ const char *s = strchr(*env, '=');
if (env != environ) {
rb_str_buf_cat2(str, ", ");
}
if (s) {
- rb_str_buf_cat2(str, "\"");
- rb_str_buf_cat(str, *env, s-*env);
- rb_str_buf_cat2(str, "\"=>");
- i = rb_inspect(rb_str_new2(s+1));
- rb_str_buf_append(str, i);
+ rb_str_buf_append(str, rb_str_inspect(env_enc_str_new(*env, s-*env, enc)));
+ rb_str_buf_cat2(str, "=>");
+ s++;
+ rb_str_buf_append(str, rb_str_inspect(env_enc_str_new(s, strlen(s), enc)));
}
env++;
}
@@ -6100,7 +6155,7 @@ env_empty_p(VALUE _)
if (env[0] != 0) {
empty = false;
}
- FREE_ENVIRON(environ);
+ FREE_ENVIRON(environ);
}
ENV_UNLOCK();
@@ -6317,7 +6372,7 @@ env_to_hash(void)
env_str_new2(s+1));
}
env++;
- }
+ }
FREE_ENVIRON(environ);
}
ENV_UNLOCK();
@@ -6581,7 +6636,7 @@ env_update_block_i(VALUE key, VALUE val, VALUE _)
{
VALUE oldval = rb_f_getenv(Qnil, key);
if (!NIL_P(oldval)) {
- val = rb_yield_values(3, key, oldval, val);
+ val = rb_yield_values(3, key, oldval, val);
}
env_aset(key, val);
return ST_CONTINUE;
@@ -6639,31 +6694,26 @@ env_update(int argc, VALUE *argv, VALUE env)
return env;
}
+NORETURN(static VALUE env_clone(int, VALUE *, VALUE));
/*
* call-seq:
- * ENV.clone(freeze: nil) -> ENV
+ * ENV.clone(freeze: nil) # raises TypeError
*
- * Returns ENV itself, and warns because ENV is a wrapper for the
- * process-wide environment variables and a clone is useless.
- * If +freeze+ keyword is given and not +nil+ or +false+, raises ArgumentError.
- * If +freeze+ keyword is given and +true+, raises TypeError, as ENV storage
- * cannot be frozen.
+ * Raises TypeError, because ENV is a wrapper for the process-wide
+ * environment variables and a clone is useless.
+ * Use #to_h to get a copy of ENV data as a hash.
*/
static VALUE
env_clone(int argc, VALUE *argv, VALUE obj)
{
if (argc) {
- VALUE opt, kwfreeze;
+ VALUE opt;
if (rb_scan_args(argc, argv, "0:", &opt) < argc) {
- kwfreeze = rb_get_freeze_opt(1, &opt);
- if (RTEST(kwfreeze)) {
- rb_raise(rb_eTypeError, "cannot freeze ENV");
- }
+ rb_get_freeze_opt(1, &opt);
}
}
- rb_warn_deprecated("ENV.clone", "ENV.to_h");
- return envtbl;
+ rb_raise(rb_eTypeError, "Cannot clone ENV, use ENV.to_h to get a copy of ENV as a hash");
}
NORETURN(static VALUE env_dup(VALUE));
@@ -7169,7 +7219,6 @@ void
Init_Hash(void)
{
id_hash = rb_intern_const("hash");
- id_default = rb_intern_const("default");
id_flatten_bang = rb_intern_const("flatten!");
id_hash_iter_lev = rb_make_internal_id();
diff --git a/hrtime.h b/hrtime.h
index 4ac3d54723..7ed4e6b04c 100644
--- a/hrtime.h
+++ b/hrtime.h
@@ -36,6 +36,7 @@
#define RB_HRTIME_PER_MSEC (RB_HRTIME_PER_USEC * (rb_hrtime_t)1000)
#define RB_HRTIME_PER_SEC (RB_HRTIME_PER_MSEC * (rb_hrtime_t)1000)
#define RB_HRTIME_MAX UINT64_MAX
+#define RB_HRTIME_MIN ((rb_hrtime_t)0)
/*
* Lets try to support time travelers. Lets assume anybody with a time machine
@@ -91,6 +92,15 @@ rb_hrtime_add(rb_hrtime_t a, rb_hrtime_t b)
return c;
}
+static inline rb_hrtime_t
+rb_hrtime_sub(rb_hrtime_t a, rb_hrtime_t b)
+{
+ if (a < b) {
+ return RB_HRTIME_MIN;
+ }
+ return a - b;
+}
+
/*
* convert a timeval struct to rb_hrtime_t, clamping at RB_HRTIME_MAX
*/
@@ -196,6 +206,7 @@ double2hrtime(rb_hrtime_t *hrt, double d)
const double TIMESPEC_SEC_MAX_PLUS_ONE = 2.0 * (TIMESPEC_SEC_MAX_as_double / 2.0 + 1.0);
if (TIMESPEC_SEC_MAX_PLUS_ONE <= d) {
+ *hrt = RB_HRTIME_MAX;
return NULL;
}
else if (d <= 0) {
diff --git a/id_table.c b/id_table.c
index a1b09ba0ed..a9a041b955 100644
--- a/id_table.c
+++ b/id_table.c
@@ -85,9 +85,9 @@ rb_id_table_init(struct rb_id_table *tbl, int capa)
{
MEMZERO(tbl, struct rb_id_table, 1);
if (capa > 0) {
- capa = round_capa(capa);
- tbl->capa = (int)capa;
- tbl->items = ZALLOC_N(item_t, capa);
+ capa = round_capa(capa);
+ tbl->capa = (int)capa;
+ tbl->items = ZALLOC_N(item_t, capa);
}
return tbl;
}
@@ -130,16 +130,16 @@ static int
hash_table_index(struct rb_id_table* tbl, id_key_t key)
{
if (tbl->capa > 0) {
- int mask = tbl->capa - 1;
- int ix = key & mask;
- int d = 1;
- while (key != ITEM_GET_KEY(tbl, ix)) {
- if (!ITEM_COLLIDED(tbl, ix))
- return -1;
- ix = (ix + d) & mask;
- d++;
- }
- return ix;
+ int mask = tbl->capa - 1;
+ int ix = key & mask;
+ int d = 1;
+ while (key != ITEM_GET_KEY(tbl, ix)) {
+ if (!ITEM_COLLIDED(tbl, ix))
+ return -1;
+ ix = (ix + d) & mask;
+ d++;
+ }
+ return ix;
}
return -1;
}
@@ -152,13 +152,13 @@ hash_table_raw_insert(struct rb_id_table *tbl, id_key_t key, VALUE val)
int d = 1;
assert(key != 0);
while (ITEM_KEY_ISSET(tbl, ix)) {
- ITEM_SET_COLLIDED(tbl, ix);
- ix = (ix + d) & mask;
- d++;
+ ITEM_SET_COLLIDED(tbl, ix);
+ ix = (ix + d) & mask;
+ d++;
}
tbl->num++;
if (!ITEM_COLLIDED(tbl, ix)) {
- tbl->used++;
+ tbl->used++;
}
ITEM_SET_KEY(tbl, ix, key);
tbl->items[ix].val = val;
@@ -168,16 +168,16 @@ static int
hash_delete_index(struct rb_id_table *tbl, int ix)
{
if (ix >= 0) {
- if (!ITEM_COLLIDED(tbl, ix)) {
- tbl->used--;
- }
- tbl->num--;
- ITEM_SET_KEY(tbl, ix, 0);
- tbl->items[ix].val = 0;
- return TRUE;
+ if (!ITEM_COLLIDED(tbl, ix)) {
+ tbl->used--;
+ }
+ tbl->num--;
+ ITEM_SET_KEY(tbl, ix, 0);
+ tbl->items[ix].val = 0;
+ return TRUE;
}
else {
- return FALSE;
+ return FALSE;
}
}
@@ -185,24 +185,24 @@ static void
hash_table_extend(struct rb_id_table* tbl)
{
if (tbl->used + (tbl->used >> 1) >= tbl->capa) {
- int new_cap = round_capa(tbl->num + (tbl->num >> 1));
- int i;
- item_t* old;
- struct rb_id_table tmp_tbl = {0, 0, 0};
- if (new_cap < tbl->capa) {
- new_cap = round_capa(tbl->used + (tbl->used >> 1));
- }
- tmp_tbl.capa = new_cap;
- tmp_tbl.items = ZALLOC_N(item_t, new_cap);
- for (i = 0; i < tbl->capa; i++) {
- id_key_t key = ITEM_GET_KEY(tbl, i);
- if (key != 0) {
- hash_table_raw_insert(&tmp_tbl, key, tbl->items[i].val);
- }
- }
- old = tbl->items;
- *tbl = tmp_tbl;
- xfree(old);
+ int new_cap = round_capa(tbl->num + (tbl->num >> 1));
+ int i;
+ item_t* old;
+ struct rb_id_table tmp_tbl = {0, 0, 0};
+ if (new_cap < tbl->capa) {
+ new_cap = round_capa(tbl->used + (tbl->used >> 1));
+ }
+ tmp_tbl.capa = new_cap;
+ tmp_tbl.items = ZALLOC_N(item_t, new_cap);
+ for (i = 0; i < tbl->capa; i++) {
+ id_key_t key = ITEM_GET_KEY(tbl, i);
+ if (key != 0) {
+ hash_table_raw_insert(&tmp_tbl, key, tbl->items[i].val);
+ }
+ }
+ old = tbl->items;
+ *tbl = tmp_tbl;
+ xfree(old);
}
}
@@ -216,9 +216,9 @@ hash_table_show(struct rb_id_table *tbl)
fprintf(stderr, "tbl: %p (capa: %d, num: %d, used: %d)\n", tbl, tbl->capa, tbl->num, tbl->used);
for (i=0; i<capa; i++) {
- if (ITEM_KEY_ISSET(tbl, i)) {
- fprintf(stderr, " -> [%d] %s %d\n", i, rb_id2name(key2id(keys[i])), (int)keys[i]);
- }
+ if (ITEM_KEY_ISSET(tbl, i)) {
+ fprintf(stderr, " -> [%d] %s %d\n", i, rb_id2name(key2id(keys[i])), (int)keys[i]);
+ }
}
}
#endif
@@ -231,10 +231,10 @@ rb_id_table_lookup(struct rb_id_table *tbl, ID id, VALUE *valp)
if (index >= 0) {
*valp = tbl->items[index].val;
- return TRUE;
+ return TRUE;
}
else {
- return FALSE;
+ return FALSE;
}
}
@@ -244,11 +244,11 @@ rb_id_table_insert_key(struct rb_id_table *tbl, const id_key_t key, const VALUE
const int index = hash_table_index(tbl, key);
if (index >= 0) {
- tbl->items[index].val = val;
+ tbl->items[index].val = val;
}
else {
- hash_table_extend(tbl);
- hash_table_raw_insert(tbl, key, val);
+ hash_table_extend(tbl);
+ hash_table_raw_insert(tbl, key, val);
}
return TRUE;
}
@@ -273,16 +273,16 @@ rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, v
int i, capa = tbl->capa;
for (i=0; i<capa; i++) {
- if (ITEM_KEY_ISSET(tbl, i)) {
- const id_key_t key = ITEM_GET_KEY(tbl, i);
- enum rb_id_table_iterator_result ret = (*func)(key2id(key), tbl->items[i].val, data);
- assert(key != 0);
-
- if (ret == ID_TABLE_DELETE)
- hash_delete_index(tbl, i);
- else if (ret == ID_TABLE_STOP)
- return;
- }
+ if (ITEM_KEY_ISSET(tbl, i)) {
+ const id_key_t key = ITEM_GET_KEY(tbl, i);
+ enum rb_id_table_iterator_result ret = (*func)(key2id(key), tbl->items[i].val, data);
+ assert(key != 0);
+
+ if (ret == ID_TABLE_DELETE)
+ hash_delete_index(tbl, i);
+ else if (ret == ID_TABLE_STOP)
+ return;
+ }
}
}
@@ -292,14 +292,14 @@ rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_f
int i, capa = tbl->capa;
for (i=0; i<capa; i++) {
- if (ITEM_KEY_ISSET(tbl, i)) {
- enum rb_id_table_iterator_result ret = (*func)(tbl->items[i].val, data);
-
- if (ret == ID_TABLE_DELETE)
- hash_delete_index(tbl, i);
- else if (ret == ID_TABLE_STOP)
- return;
- }
+ if (ITEM_KEY_ISSET(tbl, i)) {
+ enum rb_id_table_iterator_result ret = (*func)(tbl->items[i].val, data);
+
+ if (ret == ID_TABLE_DELETE)
+ hash_delete_index(tbl, i);
+ else if (ret == ID_TABLE_STOP)
+ return;
+ }
}
}
diff --git a/id_table.h b/id_table.h
index 9d9eb5648e..f72e2d1d92 100644
--- a/id_table.h
+++ b/id_table.h
@@ -19,7 +19,6 @@ struct rb_id_table *rb_id_table_create(size_t size);
void rb_id_table_free(struct rb_id_table *tbl);
void rb_id_table_clear(struct rb_id_table *tbl);
-size_t rb_id_table_size(const struct rb_id_table *tbl);
size_t rb_id_table_memsize(const struct rb_id_table *tbl);
int rb_id_table_insert(struct rb_id_table *tbl, ID id, VALUE val);
@@ -33,4 +32,8 @@ void rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *fu
void rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data);
void rb_id_table_foreach_values_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, rb_id_table_update_value_callback_func_t *replace, void *data);
+RUBY_SYMBOL_EXPORT_BEGIN
+size_t rb_id_table_size(const struct rb_id_table *tbl);
+RUBY_SYMBOL_EXPORT_END
+
#endif /* RUBY_ID_TABLE_H */
diff --git a/include/ruby/assert.h b/include/ruby/assert.h
index c9f2c3fbef..0c052363bc 100644
--- a/include/ruby/assert.h
+++ b/include/ruby/assert.h
@@ -103,7 +103,7 @@
# /* keep NDEBUG undefined */
#elif (RBIMPL_NDEBUG == 0) && (RBIMPL_RUBY_DEBUG == 0)
-# /* The (*1) situation in avobe diagram. */
+# /* The (*1) situation in above diagram. */
# define RUBY_DEBUG 0
# define RUBY_NDEBUG 1
# define NDEBUG
diff --git a/include/ruby/debug.h b/include/ruby/debug.h
index c88da9c43d..f95acdb17e 100644
--- a/include/ruby/debug.h
+++ b/include/ruby/debug.h
@@ -207,6 +207,17 @@ typedef VALUE (*rb_debug_inspector_func_t)(const rb_debug_inspector_t *dc, void
VALUE rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data);
/**
+ * Queries the backtrace object of the context. This is as if you call
+ * `caller_locations` at the point of debugger.
+ *
+ * @param[in] dc A debug context.
+ * @return An array of `Thread::Backtrace::Location` which represents the
+ * current point of execution at `dc`.
+
+ */
+VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc);
+
+/**
* Queries the current receiver of the passed context's upper frame.
*
* @param[in] dc A debug context.
@@ -250,15 +261,27 @@ VALUE rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long
VALUE rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index);
/**
- * Queries the backtrace object of the context. This is as if you call
- * `caller_locations` at the point of debugger.
+ * Queries the depth of the passed context's upper frame.
*
- * @param[in] dc A debug context.
- * @return An array of `Thread::Backtrace::Location` which represents the
- * current point of execution at `dc`.
+ * Note that the depth is not same as the frame index because debug_inspector
+ * skips some special frames but the depth counts all frames.
+ *
+ * @param[in] dc A debug context.
+ * @param[in] index Index of the frame from top to bottom.
+ * @exception rb_eArgError `index` out of range.
+ * @retval The depth at `index`-th frame in Integer.
+ */
+VALUE rb_debug_inspector_frame_depth(const rb_debug_inspector_t *dc, long index);
+
+// A macro to recognize `rb_debug_inspector_frame_depth()` is available or not
+#define RB_DEBUG_INSPECTOR_FRAME_DEPTH(dc, index) rb_debug_inspector_frame_depth(dc, index)
+/**
+ * Return current frmae depth.
+ *
+ * @retval The depth of the current frame in Integer.
*/
-VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc);
+VALUE rb_debug_inspector_current_depth(void);
/** @} */
diff --git a/include/ruby/fiber/scheduler.h b/include/ruby/fiber/scheduler.h
index ced092adbc..250b39b6df 100644
--- a/include/ruby/fiber/scheduler.h
+++ b/include/ruby/fiber/scheduler.h
@@ -23,6 +23,8 @@
RBIMPL_SYMBOL_EXPORT_BEGIN()
+#define RUBY_FIBER_SCHEDULER_VERSION 2
+
struct timeval;
/**
@@ -43,10 +45,12 @@ struct timeval;
* @return A `VALUE` which contains the result and/or errno.
*/
static inline VALUE
-rb_fiber_scheduler_io_result(ssize_t result, int error) {
+rb_fiber_scheduler_io_result(ssize_t result, int error)
+{
if (result == -1) {
return RB_INT2NUM(-error);
- } else {
+ }
+ else {
return RB_SIZE2NUM(result);
}
}
@@ -63,11 +67,13 @@ rb_fiber_scheduler_io_result(ssize_t result, int error) {
* @return The original result of the system call.
*/
static inline ssize_t
-rb_fiber_scheduler_io_result_apply(VALUE result) {
+rb_fiber_scheduler_io_result_apply(VALUE result)
+{
if (RB_FIXNUM_P(result) && RB_NUM2INT(result) < 0) {
errno = -RB_NUM2INT(result);
return -1;
- } else {
+ }
+ else {
return RB_NUM2SIZE(result);
}
}
@@ -140,7 +146,7 @@ VALUE rb_fiber_scheduler_make_timeout(struct timeval *timeout);
VALUE rb_fiber_scheduler_close(VALUE scheduler);
/**
- * Nonblocking `sleep`. Depending on scheduler implementation, this for
+ * Non-blocking `sleep`. Depending on scheduler implementation, this for
* instance switches to another fiber etc.
*
* @param[in] scheduler Target scheduler.
@@ -168,7 +174,7 @@ int rb_fiber_scheduler_supports_process_wait(VALUE scheduler);
#endif
/**
- * Nonblocking `waitpid`. Depending on scheduler implementation, this for
+ * Non-blocking `waitpid`. Depending on scheduler implementation, this for
* instance switches to another fiber etc.
*
* @param[in] scheduler Target scheduler.
@@ -179,7 +185,7 @@ int rb_fiber_scheduler_supports_process_wait(VALUE scheduler);
VALUE rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags);
/**
- * Nonblocking wait for the passed "blocker", which is for instance
+ * Non-blocking wait for the passed "blocker", which is for instance
* `Thread.join` or `Mutex.lock`. Depending on scheduler implementation, this
* for instance switches to another fiber etc.
*
@@ -201,8 +207,8 @@ VALUE rb_fiber_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout);
VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber);
/**
- * Nonblocking version of rb_io_wait(). Depending on scheduler implementation,
- * this for instance switches to another fiber etc.
+ * Non-blocking version of rb_io_wait(). Depending on scheduler
+ * implementation, this for instance switches to another fiber etc.
*
* The "events" here is a Ruby level integer, which is an OR-ed value of
* `IO::READABLE`, `IO::WRITABLE`, and `IO::PRIORITY`.
@@ -216,7 +222,7 @@ VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber);
VALUE rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeout);
/**
- * Nonblocking wait until the passed IO is ready for reading. This is a
+ * Non-blocking wait until the passed IO is ready for reading. This is a
* special case of rb_fiber_scheduler_io_wait(), where the interest is
* `IO::READABLE` and timeout is never.
*
@@ -227,7 +233,7 @@ VALUE rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE
VALUE rb_fiber_scheduler_io_wait_readable(VALUE scheduler, VALUE io);
/**
- * Nonblocking wait until the passed IO is ready for writing. This is a
+ * Non-blocking wait until the passed IO is ready for writing. This is a
* special case of rb_fiber_scheduler_io_wait(), where the interest is
* `IO::WRITABLE` and timeout is never.
*
@@ -238,57 +244,81 @@ VALUE rb_fiber_scheduler_io_wait_readable(VALUE scheduler, VALUE io);
VALUE rb_fiber_scheduler_io_wait_writable(VALUE scheduler, VALUE io);
/**
- * Nonblocking read from the passed IO.
+ * Non-blocking version of `IO.select`.
+ *
+ * It's possible that this will be emulated using a thread, so you should not
+ * rely on it for high performance.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] readables An array of readable objects.
+ * @param[in] writables An array of writable objects.
+ * @param[in] exceptables An array of objects that might encounter exceptional conditions.
+ * @param[in] timeout Numeric timeout or nil.
+ * @return What `scheduler.io_select` returns, normally a 3-tuple of arrays of ready objects.
+ */
+VALUE rb_fiber_scheduler_io_select(VALUE scheduler, VALUE readables, VALUE writables, VALUE exceptables, VALUE timeout);
+
+/**
+ * Non-blocking version of `IO.select`, `argv` variant.
+ */
+VALUE rb_fiber_scheduler_io_selectv(VALUE scheduler, int argc, VALUE *argv);
+
+/**
+ * Non-blocking read from the passed IO.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to read from.
* @param[out] buffer Return buffer.
* @param[in] length Requested number of bytes to read.
+ * @param[in] offset The offset in the buffer to read to.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
* @return otherwise What `scheduler.io_read` returns `[-errno, size]`.
*/
-VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length);
+VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset);
/**
- * Nonblocking write to the passed IO.
+ * Non-blocking write to the passed IO.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to write to.
* @param[in] buffer What to write.
* @param[in] length Number of bytes to write.
+ * @param[in] offset The offset in the buffer to write from.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
* @return otherwise What `scheduler.io_write` returns `[-errno, size]`.
*/
-VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length);
+VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset);
/**
- * Nonblocking read from the passed IO at the specified offset.
+ * Non-blocking read from the passed IO at the specified offset.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to read from.
- * @param[out] buffer Return buffer.
+ * @param[in] from The offset in the given IO to read the data from.
+ * @param[out] buffer The buffer to read the data to.
* @param[in] length Requested number of bytes to read.
- * @param[in] offset The offset in the given IO to read the data from.
+ * @param[in] offset The offset in the buffer to read to.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
* @return otherwise What `scheduler.io_read` returns.
*/
-VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset);
+VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset);
/**
- * Nonblocking write to the passed IO at the specified offset.
+ * Non-blocking write to the passed IO at the specified offset.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to write to.
- * @param[in] buffer What to write.
+ * @param[in] from The offset in the given IO to write the data to.
+ * @param[in] buffer The buffer to write the data from.
* @param[in] length Number of bytes to write.
- * @param[in] offset The offset in the given IO to write the data to.
+ * @param[in] offset The offset in the buffer to write from.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
* @return otherwise What `scheduler.io_write` returns.
*/
-VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset);
+VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset);
/**
- * Nonblocking read from the passed IO using a native buffer.
+ * Non-blocking read from the passed IO using a native buffer.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to read from.
@@ -301,7 +331,7 @@ VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size
VALUE rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *buffer, size_t size, size_t length);
/**
- * Nonblocking write to the passed IO using a native buffer.
+ * Non-blocking write to the passed IO using a native buffer.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to write to.
@@ -314,7 +344,7 @@ VALUE rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *buffer,
VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *buffer, size_t size, size_t length);
/**
- * Nonblocking close the given IO.
+ * Non-blocking close the given IO.
*
* @param[in] scheduler Target scheduler.
* @param[in] io An io object to close.
@@ -324,7 +354,7 @@ VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *
VALUE rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io);
/**
- * Nonblocking DNS lookup.
+ * Non-blocking DNS lookup.
*
* @param[in] scheduler Target scheduler.
* @param[in] hostname A host name to query.
@@ -333,6 +363,12 @@ VALUE rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io);
*/
VALUE rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname);
+/**
+ * Create and schedule a non-blocking fiber.
+ *
+ */
+VALUE rb_fiber_scheduler_fiber(VALUE scheduler, int argc, VALUE *argv, int kw_splat);
+
RBIMPL_SYMBOL_EXPORT_END()
#endif /* RUBY_FIBER_SCHEDULER_H */
diff --git a/include/ruby/internal/abi.h b/include/ruby/internal/abi.h
index e42a1777ff..44111a0055 100644
--- a/include/ruby/internal/abi.h
+++ b/include/ruby/internal/abi.h
@@ -1,6 +1,8 @@
#ifndef RUBY_ABI_H
#define RUBY_ABI_H
+#ifdef RUBY_ABI_VERSION /* should match the definition in config.h */
+
/* This number represents Ruby's ABI version.
*
* In development Ruby, it should be bumped every time an ABI incompatible
@@ -19,16 +21,17 @@
* - Backwards compatible refactors.
* - Editing comments.
*
- * In released versions of Ruby, this number should not be changed since teeny
+ * In released versions of Ruby, this number is not defined since teeny
* versions of Ruby should guarantee ABI compatibility.
*/
-#define RUBY_ABI_VERSION 2
+#define RUBY_ABI_VERSION 3
/* Windows does not support weak symbols so ruby_abi_version will not exist
* in the shared library. */
#if defined(HAVE_FUNC_WEAK) && !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
# define RUBY_DLN_CHECK_ABI
#endif
+#endif /* RUBY_ABI_VERSION */
#ifdef RUBY_DLN_CHECK_ABI
@@ -39,7 +42,11 @@ extern "C" {
RUBY_FUNC_EXPORTED unsigned long long __attribute__((weak))
ruby_abi_version(void)
{
+# ifdef RUBY_ABI_VERSION
return RUBY_ABI_VERSION;
+# else
+ return 0;
+# endif
}
# ifdef __cplusplus
diff --git a/include/ruby/internal/anyargs.h b/include/ruby/internal/anyargs.h
index 9d8d16fdab..e4c6d155cc 100644
--- a/include/ruby/internal/anyargs.h
+++ b/include/ruby/internal/anyargs.h
@@ -84,12 +84,15 @@
#elif defined(_WIN32) || defined(__CYGWIN__)
# /* Skip due to [Bug #16134] */
+# define RBIMPL_CAST_FN_PTR 1
#elif ! RBIMPL_HAS_ATTRIBUTE(transparent_union)
# /* :TODO: improve here, please find a way to support. */
+# define RBIMPL_CAST_FN_PTR 1
#elif ! defined(HAVE_VA_ARGS_MACRO)
# /* :TODO: improve here, please find a way to support. */
+# define RBIMPL_CAST_FN_PTR 1
#else
# /** @cond INTERNAL_MACRO */
@@ -239,15 +242,16 @@
# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_13(n) RBIMPL_ANYARGS_DISPATCH((n) == 13, rb_define_method_13, RBIMPL_ANYARGS_DISPATCH_rb_define_method_12(n))
# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_14(n) RBIMPL_ANYARGS_DISPATCH((n) == 14, rb_define_method_14, RBIMPL_ANYARGS_DISPATCH_rb_define_method_13(n))
# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n) RBIMPL_ANYARGS_DISPATCH((n) == 15, rb_define_method_15, RBIMPL_ANYARGS_DISPATCH_rb_define_method_14(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_singleton_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_15(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_protected_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_15(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_private_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_15(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_module_function_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_15(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_global_function_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_15(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_id_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_15(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_singleton_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_protected_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_private_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_module_function_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_global_function_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_id_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n))
# define RBIMPL_ANYARGS_ATTRSET(sym) RBIMPL_ATTR_MAYBE_UNUSED() RBIMPL_ATTR_NONNULL(()) RBIMPL_ATTR_WEAKREF(sym)
# define RBIMPL_ANYARGS_DECL(sym, ...) \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _notimpl(__VA_ARGS__, VALUE(*)(int, const VALUE *, VALUE, VALUE), int); \
RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m3(__VA_ARGS__, VALUE(*)(ANYARGS), int); \
RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m2(__VA_ARGS__, VALUE(*)(VALUE, VALUE), int); \
RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m1(__VA_ARGS__, VALUE(*)(int, union { VALUE *x; const VALUE *y; } __attribute__((__transparent_union__)), VALUE), int); \
@@ -347,6 +351,25 @@ RBIMPL_ANYARGS_DECL(rb_define_method, VALUE, const char *)
#endif /* __cplusplus */
+#if defined(RBIMPL_CAST_FN_PTR) && !defined(__cplusplus)
+/* In C23, K&R style prototypes are gone and so `void foo(ANYARGS)` became
+ * equivalent to `void foo(void)` unlike in earlier versions. This is a problem
+ * for rb_define_* functions since that makes all valid functions one can pass
+ * trip -Wincompatible-pointer-types, which we treat as errors. This is mostly
+ * not a problem for the __builtin_choose_expr path, but outside of that we
+ * need to add a cast for compatibility.
+ */
+#define rb_define_method(klass, mid, func, arity) rb_define_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_method_id(klass, mid, func, arity) rb_define_method_id((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_singleton_method(obj, mid, func, arity) rb_define_singleton_method((obj), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_protected_method(klass, mid, func, arity) rb_define_protected_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_private_method(klass, mid, func, arity) rb_define_private_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_module_function(mod, mid, func, arity) rb_define_module_function((mod), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_global_function(mid, func, arity) rb_define_global_function((mid), (VALUE (*)(ANYARGS))(func), (arity))
+
+#undef RBIMPL_CAST_FN_PTR
+#endif /* defined(RBIMPL_CAST_FN_PTR) && !defined(__cplusplus) */
+
/**
* This macro is to properly cast a function parameter of *_define_method
* family. It has been around since 1.x era so you can maximise backwards
diff --git a/include/ruby/internal/arithmetic.h b/include/ruby/internal/arithmetic.h
index 3f7840c384..7ebb4a86f1 100644
--- a/include/ruby/internal/arithmetic.h
+++ b/include/ruby/internal/arithmetic.h
@@ -18,7 +18,8 @@
* Do not expect for instance `__VA_ARGS__` is always available.
* We assume C99 for ruby itself but we don't assume languages of
* extension libraries. They could be written in C++98.
- * @brief Conversion between C's arithmtic types and Ruby's numeric types.
+ * @brief Conversion between C's arithmetic types and Ruby's numeric
+ * types.
*/
#include "ruby/internal/arithmetic/char.h"
#include "ruby/internal/arithmetic/double.h"
diff --git a/include/ruby/internal/attr/nodiscard.h b/include/ruby/internal/attr/nodiscard.h
index 087192a7a8..c3ae118942 100644
--- a/include/ruby/internal/attr/nodiscard.h
+++ b/include/ruby/internal/attr/nodiscard.h
@@ -26,7 +26,7 @@
/**
* Wraps (or simulates) `[[nodiscard]]`. In C++ (at least since C++20) a
- * nodiscard attribute can have a message why the result shall not be ignoed.
+ * nodiscard attribute can have a message why the result shall not be ignored.
* However GCC attribute and SAL annotation cannot take them.
*/
#if RBIMPL_HAS_CPP_ATTRIBUTE(nodiscard)
diff --git a/include/ruby/internal/attr/nonstring.h b/include/ruby/internal/attr/nonstring.h
new file mode 100644
index 0000000000..de26e926d4
--- /dev/null
+++ b/include/ruby/internal/attr/nonstring.h
@@ -0,0 +1,32 @@
+#ifndef RBIMPL_ATTR_NONSTRING_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_NONSTRING_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_NONSTRING.
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((nonstring))` */
+#if RBIMPL_HAS_ATTRIBUTE(nonstring)
+# define RBIMPL_ATTR_NONSTRING() __attribute__((nonstring))
+#else
+# define RBIMPL_ATTR_NONSTRING() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_NONSTRING_H */
diff --git a/include/ruby/internal/config.h b/include/ruby/internal/config.h
index 0c434e5b05..aa63376d7c 100644
--- a/include/ruby/internal/config.h
+++ b/include/ruby/internal/config.h
@@ -113,6 +113,8 @@
# define UNALIGNED_WORD_ACCESS 1
#elif defined(__powerpc64__)
# define UNALIGNED_WORD_ACCESS 1
+#elif defined(__POWERPC__) // __POWERPC__ is defined for ppc and ppc64 on Darwin
+# define UNALIGNED_WORD_ACCESS 1
#elif defined(__aarch64__)
# define UNALIGNED_WORD_ACCESS 1
#elif defined(__mc68020__)
diff --git a/include/ruby/internal/core/robject.h b/include/ruby/internal/core/robject.h
index 7823061d8f..b1c2e1b0a9 100644
--- a/include/ruby/internal/core/robject.h
+++ b/include/ruby/internal/core/robject.h
@@ -37,16 +37,15 @@
/**
* Convenient casting macro.
*
- * @param obj An object, which is in fact an ::RRegexp.
- * @return The passed object casted to ::RRegexp.
+ * @param obj An object, which is in fact an ::RObject.
+ * @return The passed object casted to ::RObject.
*/
#define ROBJECT(obj) RBIMPL_CAST((struct RObject *)(obj))
/** @cond INTERNAL_MACRO */
#define ROBJECT_EMBED_LEN_MAX ROBJECT_EMBED_LEN_MAX
#define ROBJECT_EMBED ROBJECT_EMBED
-#define ROBJECT_NUMIV ROBJECT_NUMIV
+#define ROBJECT_IV_CAPACITY ROBJECT_IV_CAPACITY
#define ROBJECT_IVPTR ROBJECT_IVPTR
-#define ROBJECT_IV_INDEX_TBL ROBJECT_IV_INDEX_TBL
/** @endcond */
/**
@@ -97,14 +96,6 @@ struct RObject {
/** Basic part, including flags and class. */
struct RBasic basic;
-#if USE_RVARGC
- /**
- * Number of instance variables. This is per object; objects might
- * differ in this field even if they have the identical classes.
- */
- uint32_t numiv;
-#endif
-
/** Object's specific fields. */
union {
@@ -113,14 +104,6 @@ struct RObject {
* this pattern.
*/
struct {
-#if !USE_RVARGC
- /**
- * Number of instance variables. This is per object; objects might
- * differ in this field even if they have the identical classes.
- */
- uint32_t numiv;
-#endif
-
/** Pointer to a C array that holds instance variables. */
VALUE *ivptr;
@@ -132,7 +115,7 @@ struct RObject {
*
* This is a shortcut for `RCLASS_IV_INDEX_TBL(rb_obj_class(obj))`.
*/
- struct st_table *iv_index_tbl;
+ struct rb_id_table *iv_index_tbl;
} heap;
#if USE_RVARGC
@@ -157,11 +140,6 @@ struct RObject {
/* Offsets for YJIT */
#ifndef __cplusplus
-# if USE_RVARGC
-static const int32_t ROBJECT_OFFSET_NUMIV = offsetof(struct RObject, numiv);
-# else
-static const int32_t ROBJECT_OFFSET_NUMIV = offsetof(struct RObject, as.heap.numiv);
-# endif
static const int32_t ROBJECT_OFFSET_AS_HEAP_IVPTR = offsetof(struct RObject, as.heap.ivptr);
static const int32_t ROBJECT_OFFSET_AS_HEAP_IV_INDEX_TBL = offsetof(struct RObject, as.heap.iv_index_tbl);
static const int32_t ROBJECT_OFFSET_AS_ARY = offsetof(struct RObject, as.ary);
@@ -170,32 +148,6 @@ static const int32_t ROBJECT_OFFSET_AS_ARY = offsetof(struct RObject, as.ary);
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/**
- * Queries the number of instance variables.
- *
- * @param[in] obj Object in question.
- * @return Its number of instance variables.
- * @pre `obj` must be an instance of ::RObject.
- */
-static inline uint32_t
-ROBJECT_NUMIV(VALUE obj)
-{
- RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
-
-#if USE_RVARGC
- return ROBJECT(obj)->numiv;
-#else
- if (RB_FL_ANY_RAW(obj, ROBJECT_EMBED)) {
- return ROBJECT_EMBED_LEN_MAX;
- }
- else {
- return ROBJECT(obj)->as.heap.numiv;
- }
-#endif
-}
-
-RBIMPL_ATTR_PURE_UNLESS_DEBUG()
-RBIMPL_ATTR_ARTIFICIAL()
-/**
* Queries the instance variables.
*
* @param[in] obj Object in question.
diff --git a/include/ruby/internal/encoding/ctype.h b/include/ruby/internal/encoding/ctype.h
index 64aaf0a990..05c314aeb3 100644
--- a/include/ruby/internal/encoding/ctype.h
+++ b/include/ruby/internal/encoding/ctype.h
@@ -36,8 +36,8 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
* @param[in] p Pointer to a possibly-middle of a character.
* @param[in] end End of the string.
* @param[in] enc Encoding.
- * @retval 0 It isn't.
- * @retval otherwise It is.
+ * @retval false It isn't.
+ * @retval true It is.
*/
static inline bool
rb_enc_is_newline(const char *p, const char *e, rb_encoding *enc)
@@ -53,11 +53,11 @@ rb_enc_is_newline(const char *p, const char *e, rb_encoding *enc)
* encoding. The "character type" here is a set of macros defined in onigmo.h,
* like `ONIGENC_CTYPE_PUNCT`.
*
- * @param[in] c An `OnigCodePoint` value.
- * @param[in] t An `OnigCtype` value.
- * @param[in] enc A `rb_encoding*` value.
- * @retval 1 `c` is of `t` in `enc`.
- * @retval 0 Otherwise.
+ * @param[in] c An `OnigCodePoint` value.
+ * @param[in] t An `OnigCtype` value.
+ * @param[in] enc A `rb_encoding*` value.
+ * @retval true `c` is of `t` in `enc`.
+ * @retval false Otherwise.
*/
static inline bool
rb_enc_isctype(OnigCodePoint c, OnigCtype t, rb_encoding *enc)
@@ -68,10 +68,10 @@ rb_enc_isctype(OnigCodePoint c, OnigCtype t, rb_encoding *enc)
/**
* Identical to rb_isascii(), except it additionally takes an encoding.
*
- * @param[in] c A code point.
- * @param[in] enc An encoding.
- * @retval 0 `c` is out of range of ASCII character set in `enc`.
- * @retval 1 Otherwise.
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval false `c` is out of range of ASCII character set in `enc`.
+ * @retval true Otherwise.
*
* @internal
*
@@ -87,10 +87,10 @@ rb_enc_isascii(OnigCodePoint c, rb_encoding *enc)
/**
* Identical to rb_isalpha(), except it additionally takes an encoding.
*
- * @param[in] c A code point.
- * @param[in] enc An encoding.
- * @retval 1 `enc` classifies `c` as "ALPHA".
- * @retval 0 Otherwise.
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "ALPHA".
+ * @retval false Otherwise.
*/
static inline bool
rb_enc_isalpha(OnigCodePoint c, rb_encoding *enc)
@@ -101,10 +101,10 @@ rb_enc_isalpha(OnigCodePoint c, rb_encoding *enc)
/**
* Identical to rb_islower(), except it additionally takes an encoding.
*
- * @param[in] c A code point.
- * @param[in] enc An encoding.
- * @retval 1 `enc` classifies `c` as "LOWER".
- * @retval 0 Otherwise.
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "LOWER".
+ * @retval false Otherwise.
*/
static inline bool
rb_enc_islower(OnigCodePoint c, rb_encoding *enc)
@@ -115,10 +115,10 @@ rb_enc_islower(OnigCodePoint c, rb_encoding *enc)
/**
* Identical to rb_isupper(), except it additionally takes an encoding.
*
- * @param[in] c A code point.
- * @param[in] enc An encoding.
- * @retval 1 `enc` classifies `c` as "UPPER".
- * @retval 0 Otherwise.
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "UPPER".
+ * @retval false Otherwise.
*/
static inline bool
rb_enc_isupper(OnigCodePoint c, rb_encoding *enc)
@@ -127,12 +127,26 @@ rb_enc_isupper(OnigCodePoint c, rb_encoding *enc)
}
/**
+ * Identical to rb_iscntrl(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "CNTRL".
+ * @retval false Otherwise.
+ */
+static inline bool
+rb_enc_iscntrl(OnigCodePoint c, rb_encoding *enc)
+{
+ return ONIGENC_IS_CODE_CNTRL(enc, c);
+}
+
+/**
* Identical to rb_ispunct(), except it additionally takes an encoding.
*
- * @param[in] c A code point.
- * @param[in] enc An encoding.
- * @retval 1 `enc` classifies `c` as "PUNCT".
- * @retval 0 Otherwise.
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "PUNCT".
+ * @retval false Otherwise.
*/
static inline bool
rb_enc_ispunct(OnigCodePoint c, rb_encoding *enc)
@@ -143,10 +157,10 @@ rb_enc_ispunct(OnigCodePoint c, rb_encoding *enc)
/**
* Identical to rb_isalnum(), except it additionally takes an encoding.
*
- * @param[in] c A code point.
- * @param[in] enc An encoding.
- * @retval 1 `enc` classifies `c` as "ANUM".
- * @retval 0 Otherwise.
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "ANUM".
+ * @retval false Otherwise.
*/
static inline bool
rb_enc_isalnum(OnigCodePoint c, rb_encoding *enc)
@@ -157,10 +171,10 @@ rb_enc_isalnum(OnigCodePoint c, rb_encoding *enc)
/**
* Identical to rb_isprint(), except it additionally takes an encoding.
*
- * @param[in] c A code point.
- * @param[in] enc An encoding.
- * @retval 1 `enc` classifies `c` as "PRINT".
- * @retval 0 Otherwise.
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "PRINT".
+ * @retval false Otherwise.
*/
static inline bool
rb_enc_isprint(OnigCodePoint c, rb_encoding *enc)
@@ -171,10 +185,10 @@ rb_enc_isprint(OnigCodePoint c, rb_encoding *enc)
/**
* Identical to rb_isspace(), except it additionally takes an encoding.
*
- * @param[in] c A code point.
- * @param[in] enc An encoding.
- * @retval 1 `enc` classifies `c` as "PRINT".
- * @retval 0 Otherwise.
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "PRINT".
+ * @retval false Otherwise.
*/
static inline bool
rb_enc_isspace(OnigCodePoint c, rb_encoding *enc)
@@ -185,10 +199,10 @@ rb_enc_isspace(OnigCodePoint c, rb_encoding *enc)
/**
* Identical to rb_isdigit(), except it additionally takes an encoding.
*
- * @param[in] c A code point.
- * @param[in] enc An encoding.
- * @retval 1 `enc` classifies `c` as "DIGIT".
- * @retval 0 Otherwise.
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "DIGIT".
+ * @retval false Otherwise.
*/
static inline bool
rb_enc_isdigit(OnigCodePoint c, rb_encoding *enc)
@@ -235,6 +249,7 @@ RBIMPL_SYMBOL_EXPORT_END()
#define rb_enc_isdigit rb_enc_isdigit
#define rb_enc_islower rb_enc_islower
#define rb_enc_isprint rb_enc_isprint
+#define rb_enc_iscntrl rb_enc_iscntrl
#define rb_enc_ispunct rb_enc_ispunct
#define rb_enc_isspace rb_enc_isspace
#define rb_enc_isupper rb_enc_isupper
diff --git a/include/ruby/internal/encoding/encoding.h b/include/ruby/internal/encoding/encoding.h
index 22deb8f8c9..4748ca806b 100644
--- a/include/ruby/internal/encoding/encoding.h
+++ b/include/ruby/internal/encoding/encoding.h
@@ -643,10 +643,12 @@ rb_enc_code_to_mbclen(int c, rb_encoding *enc)
* Identical to rb_enc_uint_chr(), except it writes back to the passed buffer
* instead of allocating one.
*
- * @param[in] c Code point.
- * @param[out] buf Return buffer.
- * @param[in] enc Target encoding scheme.
- * @post `c` is encoded according to `enc`, then written to `buf`.
+ * @param[in] c Code point.
+ * @param[out] buf Return buffer.
+ * @param[in] enc Target encoding scheme.
+ * @retval <= 0 `c` is invalid in `enc`.
+ * @return otherwise Number of bytes written to `buf`.
+ * @post `c` is encoded according to `enc`, then written to `buf`.
*
* @internal
*
diff --git a/include/ruby/internal/encoding/transcode.h b/include/ruby/internal/encoding/transcode.h
index 60c96a41c9..7f26d2eae9 100644
--- a/include/ruby/internal/encoding/transcode.h
+++ b/include/ruby/internal/encoding/transcode.h
@@ -476,16 +476,16 @@ enum ruby_econv_flag_type {
RUBY_ECONV_UNDEF_HEX_CHARREF = 0x00000030,
/** Decorators are there. */
- RUBY_ECONV_DECORATOR_MASK = 0x0000ff00,
+ RUBY_ECONV_DECORATOR_MASK = 0x0001ff00,
/** Newline converters are there. */
- RUBY_ECONV_NEWLINE_DECORATOR_MASK = 0x00003f00,
+ RUBY_ECONV_NEWLINE_DECORATOR_MASK = 0x00007f00,
/** (Unclear; seems unused). */
RUBY_ECONV_NEWLINE_DECORATOR_READ_MASK = 0x00000f00,
/** (Unclear; seems unused). */
- RUBY_ECONV_NEWLINE_DECORATOR_WRITE_MASK = 0x00003000,
+ RUBY_ECONV_NEWLINE_DECORATOR_WRITE_MASK = 0x00007000,
/** Universal newline mode. */
RUBY_ECONV_UNIVERSAL_NEWLINE_DECORATOR = 0x00000100,
@@ -496,11 +496,14 @@ enum ruby_econv_flag_type {
/** CRLF to CR conversion shall happen. */
RUBY_ECONV_CR_NEWLINE_DECORATOR = 0x00002000,
+ /** CRLF to LF conversion shall happen. */
+ RUBY_ECONV_LF_NEWLINE_DECORATOR = 0x00004000,
+
/** Texts shall be XML-escaped. */
- RUBY_ECONV_XML_TEXT_DECORATOR = 0x00004000,
+ RUBY_ECONV_XML_TEXT_DECORATOR = 0x00008000,
/** Texts shall be AttrValue escaped */
- RUBY_ECONV_XML_ATTR_CONTENT_DECORATOR = 0x00008000,
+ RUBY_ECONV_XML_ATTR_CONTENT_DECORATOR = 0x00010000,
/** (Unclear; seems unused). */
RUBY_ECONV_STATEFUL_DECORATOR_MASK = 0x00f00000,
@@ -529,6 +532,7 @@ enum ruby_econv_flag_type {
#define ECONV_UNIVERSAL_NEWLINE_DECORATOR RUBY_ECONV_UNIVERSAL_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_UNIVERSAL_NEWLINE_DECORATOR} */
#define ECONV_CRLF_NEWLINE_DECORATOR RUBY_ECONV_CRLF_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_CRLF_NEWLINE_DECORATOR} */
#define ECONV_CR_NEWLINE_DECORATOR RUBY_ECONV_CR_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_CR_NEWLINE_DECORATOR} */
+#define ECONV_LF_NEWLINE_DECORATOR RUBY_ECONV_LF_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_LF_NEWLINE_DECORATOR} */
#define ECONV_XML_TEXT_DECORATOR RUBY_ECONV_XML_TEXT_DECORATOR /**< @old{RUBY_ECONV_XML_TEXT_DECORATOR} */
#define ECONV_XML_ATTR_CONTENT_DECORATOR RUBY_ECONV_XML_ATTR_CONTENT_DECORATOR /**< @old{RUBY_ECONV_XML_ATTR_CONTENT_DECORATOR} */
#define ECONV_STATEFUL_DECORATOR_MASK RUBY_ECONV_STATEFUL_DECORATOR_MASK /**< @old{RUBY_ECONV_STATEFUL_DECORATOR_MASK} */
@@ -543,10 +547,10 @@ enum ruby_econv_flag_type {
*/
/** Indicates the input is a part of much larger one. */
- RUBY_ECONV_PARTIAL_INPUT = 0x00010000,
+ RUBY_ECONV_PARTIAL_INPUT = 0x00020000,
/** Instructs the converter to stop after output. */
- RUBY_ECONV_AFTER_OUTPUT = 0x00020000,
+ RUBY_ECONV_AFTER_OUTPUT = 0x00040000,
#define ECONV_PARTIAL_INPUT RUBY_ECONV_PARTIAL_INPUT /**< @old{RUBY_ECONV_PARTIAL_INPUT} */
#define ECONV_AFTER_OUTPUT RUBY_ECONV_AFTER_OUTPUT /**< @old{RUBY_ECONV_AFTER_OUTPUT} */
diff --git a/include/ruby/internal/eval.h b/include/ruby/internal/eval.h
index 34a53849da..5bcbb97746 100644
--- a/include/ruby/internal/eval.h
+++ b/include/ruby/internal/eval.h
@@ -28,10 +28,12 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
RBIMPL_ATTR_NONNULL(())
/**
- * Evaluates the given string in an isolated binding.
+ * Evaluates the given string.
*
- * Here "isolated" means that the binding does not inherit any other
- * bindings. This behaves same as the binding for required libraries.
+ * In case it is called from within a C-backended method, the evaluation is
+ * done under the current binding. However there can be no method. On such
+ * situation this function evaluates in an isolated binding, like `require`
+ * runs in a separate one.
*
* `__FILE__` will be `"(eval)"`, and `__LINE__` starts from 1 in the
* evaluation.
@@ -39,6 +41,31 @@ RBIMPL_ATTR_NONNULL(())
* @param[in] str Ruby code to evaluate.
* @exception rb_eException Raises an exception on error.
* @return The evaluated result.
+ *
+ * @internal
+ *
+ * @shyouhei's old tale about the birth and growth of this function:
+ *
+ * At the beginning, there was no rb_eval_string(). @shyouhei heard that
+ * @shugo, author of Apache httpd's mod_ruby module, requested @matz for this
+ * API. He wanted a way so that mod_ruby can evaluate ruby scripts one by one,
+ * separately, in each different contexts. So this function was made. It was
+ * designed to be a global interpreter entry point like ruby_run_node().
+ *
+ * The way it is implemented however allows extension libraries (not just
+ * programs like Apache httpd) to call this function. Because its name says
+ * nothing about the initial design, people started to think of it as an
+ * orthodox way to call ruby level `eval` method from their extension
+ * libraries. Even our `extension.rdoc` has had a description of this function
+ * basically according to this understanding.
+ *
+ * The old (mod_ruby like) usage still works. But over time, usages of this
+ * function from extension libraries got popular, while mod_ruby faded out; is
+ * no longer maintained now. Devs decided to actively support both. This
+ * function now auto-detects how it is called, and switches how it works
+ * depending on it.
+ *
+ * @see https://bugs.ruby-lang.org/issues/18780
*/
VALUE rb_eval_string(const char *str);
diff --git a/include/ruby/internal/fl_type.h b/include/ruby/internal/fl_type.h
index c51bd2e9d9..7383426b23 100644
--- a/include/ruby/internal/fl_type.h
+++ b/include/ruby/internal/fl_type.h
@@ -941,21 +941,8 @@ RB_OBJ_FREEZE_RAW(VALUE obj)
RB_FL_SET_RAW(obj, RUBY_FL_FREEZE);
}
-/**
- * Prevents further modifications to the given object. ::rb_eFrozenError shall
- * be raised if modification is attempted.
- *
- * @param[out] x Object in question.
- */
-static inline void
-rb_obj_freeze_inline(VALUE x)
-{
- if (RB_FL_ABLE(x)) {
- RB_OBJ_FREEZE_RAW(x);
- if (RBASIC_CLASS(x) && !(RBASIC(x)->flags & RUBY_FL_SINGLETON)) {
- rb_freeze_singleton_class(x);
- }
- }
-}
+RUBY_SYMBOL_EXPORT_BEGIN
+void rb_obj_freeze_inline(VALUE obj);
+RUBY_SYMBOL_EXPORT_END
#endif /* RBIMPL_FL_TYPE_H */
diff --git a/include/ruby/internal/gc.h b/include/ruby/internal/gc.h
index 66fc14e511..054e4b0f9c 100644
--- a/include/ruby/internal/gc.h
+++ b/include/ruby/internal/gc.h
@@ -26,10 +26,15 @@
RBIMPL_SYMBOL_EXPORT_BEGIN()
/**
- * Inform the garbage collector that `valptr` points to a live Ruby object that
- * should not be moved. Note that extensions should use this API on global
- * constants instead of assuming constants defined in Ruby are always alive.
- * Ruby code can remove global constants.
+ * Inform the garbage collector that the global or static variable pointed by
+ * `valptr` stores a live Ruby object that should not be moved. Note that
+ * extensions should use this API on global constants instead of assuming
+ * constants defined in Ruby are always alive. Ruby code can remove global
+ * constants.
+ *
+ * Because this registration itself has a possibility to trigger a GC, this
+ * function must be called before any GC-able objects is assigned to the
+ * address pointed by `valptr`.
*/
void rb_gc_register_address(VALUE *valptr);
diff --git a/include/ruby/internal/intern/array.h b/include/ruby/internal/intern/array.h
index 17964bf810..2262c6f0c6 100644
--- a/include/ruby/internal/intern/array.h
+++ b/include/ruby/internal/intern/array.h
@@ -107,14 +107,14 @@ VALUE rb_ary_new_from_args(long n, ...);
VALUE rb_ary_new_from_values(long n, const VALUE *elts);
/**
- * Allocates a "temporary" array. This is a hidden empty array. Handy on
- * occasions.
+ * Allocates a hidden (no class) empty array.
*
* @param[in] capa Designed capacity of the array.
* @return A hidden, empty array.
* @see rb_obj_hide()
*/
-VALUE rb_ary_tmp_new(long capa);
+VALUE rb_ary_hidden_new(long capa);
+#define rb_ary_tmp_new rb_ary_hidden_new
/**
* Destroys the given array for no reason.
diff --git a/include/ruby/internal/intern/class.h b/include/ruby/internal/intern/class.h
index 2181ab93c7..0fb2d001bc 100644
--- a/include/ruby/internal/intern/class.h
+++ b/include/ruby/internal/intern/class.h
@@ -200,6 +200,18 @@ VALUE rb_class_descendants(VALUE klass);
*/
VALUE rb_class_subclasses(VALUE klass);
+
+/**
+ * Returns the attached object for a singleton class.
+ * If the given class is not a singleton class, raises a TypeError.
+ *
+ * @param[in] klass A class.
+ * @return The object which has the singleton class `klass`.
+ *
+ * @internal
+ */
+VALUE rb_class_attached_object(VALUE klass);
+
/**
* Generates an array of symbols, which are the list of method names defined in
* the passed class.
diff --git a/include/ruby/internal/intern/cont.h b/include/ruby/internal/intern/cont.h
index 37493009f5..32647f48aa 100644
--- a/include/ruby/internal/intern/cont.h
+++ b/include/ruby/internal/intern/cont.h
@@ -39,6 +39,28 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
VALUE rb_fiber_new(rb_block_call_func_t func, VALUE callback_obj);
/**
+ * Creates a Fiber instance from a C-backended block with the specified
+ * storage.
+ *
+ * If the given storage is Qundef or Qtrue, this function is equivalent to
+ * rb_fiber_new() which inherits storage from the current fiber.
+ *
+ * Specifying Qtrue is experimental and may be changed in the future.
+ *
+ * If the given storage is Qnil, this function will lazy initialize the
+ * internal storage which starts of empty (without any inheritance).
+ *
+ * Otherwise, the given storage is used as the internal storage.
+ *
+ * @param[in] func A function, to become the fiber's body.
+ * @param[in] callback_obj Passed as-is to `func`.
+ * @param[in] storage The way to set up the storage for the fiber.
+ * @return An allocated new instance of rb_cFiber, which is ready to be
+ * "resume"d.
+ */
+VALUE rb_fiber_new_storage(rb_block_call_func_t func, VALUE callback_obj, VALUE storage);
+
+/**
* Queries the fiber which is calling this function. Any ruby execution
* context has its fiber, either explicitly or implicitly.
*
diff --git a/include/ruby/internal/intern/file.h b/include/ruby/internal/intern/file.h
index 2dc60c7ba7..79820fdc61 100644
--- a/include/ruby/internal/intern/file.h
+++ b/include/ruby/internal/intern/file.h
@@ -206,7 +206,7 @@ int rb_is_absolute_path(const char *path);
* unpredictable. POSIX's `<sys/stat.h>` states that "the use of
* this field is unspecified" then.
*/
-off_t rb_file_size(VALUE file);
+rb_off_t rb_file_size(VALUE file);
RBIMPL_SYMBOL_EXPORT_END()
diff --git a/include/ruby/internal/intern/gc.h b/include/ruby/internal/intern/gc.h
index e7b8008729..2ee1d257db 100644
--- a/include/ruby/internal/intern/gc.h
+++ b/include/ruby/internal/intern/gc.h
@@ -26,7 +26,7 @@
# include <stddef.h> /* size_t */
#endif
-#if HAVE_SYS_TYPES_H
+#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h> /* ssize_t */
#endif
@@ -71,7 +71,7 @@ RBIMPL_ATTR_NONNULL((1))
* addressable.
* @param[out] start Pointer to an array of objects.
* @param[out] end Pointer that terminates the array of objects.
- * @post Objects from `start` to `end`, both inclusive, are marked.
+ * @post Objects from `start` (included) to `end` (excluded) are marked.
*
* @internal
*
diff --git a/include/ruby/internal/intern/object.h b/include/ruby/internal/intern/object.h
index 19af49b140..b9ffa57c06 100644
--- a/include/ruby/internal/intern/object.h
+++ b/include/ruby/internal/intern/object.h
@@ -92,8 +92,8 @@ VALUE rb_class_new_instance_kw(int argc, const VALUE *argv, VALUE klass, int kw_
*
* @param[in] lhs Comparison left hand side.
* @param[in] rhs Comparison right hand side.
- * @retval RUBY_Qtrue They are equal.
- * @retval RUBY_Qfalse Otherwise.
+ * @retval non-zero They are equal.
+ * @retval 0 Otherwise.
* @note This function actually calls `lhs.eql?(rhs)` so you cannot
* implement your class' `#eql?` method using it.
*/
diff --git a/include/ruby/internal/intern/select/posix.h b/include/ruby/internal/intern/select/posix.h
index 5f828e66e2..0a9b0b2e51 100644
--- a/include/ruby/internal/intern/select/posix.h
+++ b/include/ruby/internal/intern/select/posix.h
@@ -136,7 +136,7 @@ rb_fd_max(const rb_fdset_t *f)
}
/** @cond INTERNAL_MACRO */
-/* :FIXME: What are these? They don't exist for shibling implementations. */
+/* :FIXME: What are these? They don't exist for sibling implementations. */
#define rb_fd_init_copy(d, s) (*(d) = *(s))
#define rb_fd_term(f) ((void)(f))
/** @endcond */
diff --git a/include/ruby/internal/memory.h b/include/ruby/internal/memory.h
index 0f59262a91..6884db195d 100644
--- a/include/ruby/internal/memory.h
+++ b/include/ruby/internal/memory.h
@@ -287,12 +287,12 @@ typedef uint128_t DSIZE_T;
RBIMPL_CAST((type *)alloca(rbimpl_size_mul_or_raise(sizeof(type), (n))))
/**
- * Identical to #RB_ALLOCV_N(), except it implicitly assumes the type of array
- * is ::VALUE.
+ * Identical to #RB_ALLOCV_N(), except that it allocates a number of bytes and
+ * returns a void* .
*
* @param v A variable to hold the just-in-case opaque Ruby object.
* @param n Size of allocation, in bytes.
- * @return An array of `n` bytes of ::VALUE.
+ * @return A void pointer to `n` bytes storage.
* @note `n` may be evaluated twice.
*/
#define RB_ALLOCV(v, n) \
diff --git a/include/ruby/internal/scan_args.h b/include/ruby/internal/scan_args.h
index cf5b18f77d..1ed2bf6368 100644
--- a/include/ruby/internal/scan_args.h
+++ b/include/ruby/internal/scan_args.h
@@ -100,7 +100,7 @@ RBIMPL_ATTR_NONNULL((2, 3))
* param-arg-spec := pre-arg-spec [post-arg-spec] / post-arg-spec /
* pre-opt-post-arg-spec
* pre-arg-spec := num-of-leading-mandatory-args
- [num-of-optional-args]
+ * [num-of-optional-args]
* post-arg-spec := sym-for-variable-length-args
* [num-of-trailing-mandatory-args]
* pre-opt-post-arg-spec := num-of-leading-mandatory-args num-of-optional-args
diff --git a/include/ruby/internal/special_consts.h b/include/ruby/internal/special_consts.h
index 38934e4da3..dc0a6b41d6 100644
--- a/include/ruby/internal/special_consts.h
+++ b/include/ruby/internal/special_consts.h
@@ -76,6 +76,8 @@
#define RB_SPECIAL_CONST_P RB_SPECIAL_CONST_P
#define RB_STATIC_SYM_P RB_STATIC_SYM_P
#define RB_TEST RB_TEST
+#define RB_UNDEF_P RB_UNDEF_P
+#define RB_NIL_OR_UNDEF_P RB_NIL_OR_UNDEF_P
/** @endcond */
/** special constants - i.e. non-zero and non-fixnum constants */
@@ -94,9 +96,9 @@ ruby_special_consts {
RUBY_SYMBOL_FLAG, /**< Flag to denote a static symbol. */
#elif USE_FLONUM
RUBY_Qfalse = 0x00, /* ...0000 0000 */
+ RUBY_Qnil = 0x04, /* ...0000 0100 */
RUBY_Qtrue = 0x14, /* ...0001 0100 */
- RUBY_Qnil = 0x08, /* ...0000 1000 */
- RUBY_Qundef = 0x34, /* ...0011 0100 */
+ RUBY_Qundef = 0x24, /* ...0010 0100 */
RUBY_IMMEDIATE_MASK = 0x07, /* ...0000 0111 */
RUBY_FIXNUM_FLAG = 0x01, /* ...xxxx xxx1 */
RUBY_FLONUM_MASK = 0x03, /* ...0000 0011 */
@@ -104,14 +106,14 @@ ruby_special_consts {
RUBY_SYMBOL_FLAG = 0x0c, /* ...xxxx 1100 */
#else
RUBY_Qfalse = 0x00, /* ...0000 0000 */
- RUBY_Qtrue = 0x02, /* ...0000 0010 */
- RUBY_Qnil = 0x04, /* ...0000 0100 */
- RUBY_Qundef = 0x06, /* ...0000 0110 */
+ RUBY_Qnil = 0x02, /* ...0000 0010 */
+ RUBY_Qtrue = 0x06, /* ...0000 0110 */
+ RUBY_Qundef = 0x0a, /* ...0000 1010 */
RUBY_IMMEDIATE_MASK = 0x03, /* ...0000 0011 */
RUBY_FIXNUM_FLAG = 0x01, /* ...xxxx xxx1 */
RUBY_FLONUM_MASK = 0x00, /* any values ANDed with FLONUM_MASK cannot be FLONUM_FLAG */
RUBY_FLONUM_FLAG = 0x02, /* ...0000 0010 */
- RUBY_SYMBOL_FLAG = 0x0e, /* ...0000 1110 */
+ RUBY_SYMBOL_FLAG = 0x0e, /* ...xxxx 1110 */
#endif
RUBY_SPECIAL_SHIFT = 8 /**< Least significant 8 bits are reserved. */
@@ -136,12 +138,21 @@ static inline bool
RB_TEST(VALUE obj)
{
/*
+ * if USE_FLONUM
* Qfalse: ....0000 0000
- * Qnil: ....0000 1000
- * ~Qnil: ....1111 0111
+ * Qnil: ....0000 0100
+ * ~Qnil: ....1111 1011
* v ....xxxx xxxx
* ----------------------------
- * RTEST(v) ....xxxx 0xxx
+ * RTEST(v) ....xxxx x0xx
+ *
+ * if ! USE_FLONUM
+ * Qfalse: ....0000 0000
+ * Qnil: ....0000 0010
+ * ~Qnil: ....1111 1101
+ * v ....xxxx xxxx
+ * ----------------------------
+ * RTEST(v) ....xxxx xx0x
*
* RTEST(v) can be 0 if and only if (v == Qfalse || v == Qnil).
*/
@@ -168,6 +179,62 @@ RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
+ * Checks if the given object is undef.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval true `obj` is ::RUBY_Qundef.
+ * @retval false Anything else.
+ */
+static inline bool
+RB_UNDEF_P(VALUE obj)
+{
+ return obj == RUBY_Qundef;
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX14)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks if the given object is nil or undef. Can be used to see if
+ * a keyword argument is not given or given `nil`.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval true `obj` is ::RUBY_Qnil or ::RUBY_Qundef.
+ * @retval false Anything else.
+ */
+static inline bool
+RB_NIL_OR_UNDEF_P(VALUE obj)
+{
+ /*
+ * if USE_FLONUM
+ * Qundef: ....0010 0100
+ * Qnil: ....0000 0100
+ * mask: ....1101 1111
+ * common_bits: ....0000 0100
+ * ---------------------------------
+ * Qnil & mask ....0000 0100
+ * Qundef & mask ....0000 0100
+ *
+ * if ! USE_FLONUM
+ * Qundef: ....0000 1010
+ * Qnil: ....0000 0010
+ * mask: ....1111 0111
+ * common_bits: ....0000 0010
+ * ----------------------------
+ * Qnil & mask ....0000 0010
+ * Qundef & mask ....0000 0010
+ *
+ * NIL_OR_UNDEF_P(v) can be true only when v is Qundef or Qnil.
+ */
+ const VALUE mask = ~(RUBY_Qundef ^ RUBY_Qnil);
+ const VALUE common_bits = RUBY_Qundef & RUBY_Qnil;
+ return (obj & mask) == common_bits;
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
* Checks if the given object is a so-called Fixnum.
*
* @param[in] obj An arbitrary ruby object.
@@ -259,7 +326,7 @@ RBIMPL_ATTR_ARTIFICIAL()
static inline bool
RB_SPECIAL_CONST_P(VALUE obj)
{
- return RB_IMMEDIATE_P(obj) || ! RB_TEST(obj);
+ return RB_IMMEDIATE_P(obj) || obj == RUBY_Qfalse;
}
RBIMPL_ATTR_CONST()
diff --git a/include/ruby/io.h b/include/ruby/io.h
index b5e33c1cf9..88029b1bb9 100644
--- a/include/ruby/io.h
+++ b/include/ruby/io.h
@@ -58,12 +58,20 @@
// IO#wait, IO#wait_readable, IO#wait_writable, IO#wait_priority are defined by this implementation.
#define RUBY_IO_WAIT_METHODS
+// Used as the default timeout argument to `rb_io_wait` to use the `IO#timeout` value.
+#define RUBY_IO_TIMEOUT_DEFAULT Qnil
+
RBIMPL_SYMBOL_EXPORT_BEGIN()
struct stat;
struct timeval;
/**
+ * Indicates that a timeout has occurred while performing an IO operation.
+ */
+RUBY_EXTERN VALUE rb_eIOTimeoutError;
+
+/**
* Type of events that an IO can wait.
*
* @internal
@@ -98,6 +106,34 @@ PACKED_STRUCT_UNALIGNED(struct rb_io_buffer_t {
/** @alias{rb_io_buffer_t} */
typedef struct rb_io_buffer_t rb_io_buffer_t;
+/** Decomposed encoding flags (e.g. `"enc:enc2""`). */
+/*
+ * enc enc2 read action write action
+ * NULL NULL force_encoding(default_external) write the byte sequence of str
+ * e1 NULL force_encoding(e1) convert str.encoding to e1
+ * e1 e2 convert from e2 to e1 convert str.encoding to e2
+ */
+struct rb_io_enc_t {
+ /** Internal encoding. */
+ rb_encoding *enc;
+ /** External encoding. */
+ rb_encoding *enc2;
+ /**
+ * Flags.
+ *
+ * @see enum ::ruby_econv_flag_type
+ */
+ int ecflags;
+ /**
+ * Flags as Ruby hash.
+ *
+ * @internal
+ *
+ * This is set. But used from nowhere maybe?
+ */
+ VALUE ecopts;
+};
+
/** Ruby's IO, metadata and buffers. */
typedef struct rb_io_t {
@@ -141,36 +177,7 @@ typedef struct rb_io_t {
*/
VALUE tied_io_for_writing;
- /** Decomposed encoding flags (e.g. `"enc:enc2""`). */
- /*
- * enc enc2 read action write action
- * NULL NULL force_encoding(default_external) write the byte sequence of str
- * e1 NULL force_encoding(e1) convert str.encoding to e1
- * e1 e2 convert from e2 to e1 convert str.encoding to e2
- */
- struct rb_io_enc_t {
- /** Internal encoding. */
- rb_encoding *enc;
-
- /** External encoding. */
- rb_encoding *enc2;
-
- /**
- * Flags.
- *
- * @see enum ::ruby_econv_flag_type
- */
- int ecflags;
-
- /**
- * Flags as Ruby hash.
- *
- * @internal
- *
- * This is set. But used from nowhere maybe?
- */
- VALUE ecopts;
- } encs; /**< Decomposed encoding flags. */
+ struct rb_io_enc_t encs; /**< Decomposed encoding flags. */
/** Encoding converter used when reading from this IO. */
rb_econv_t *readconv;
@@ -215,6 +222,11 @@ typedef struct rb_io_t {
* This of course doesn't help inter-process IO interleaves, though.
*/
VALUE write_lock;
+
+ /**
+ * The timeout associated with this IO when performing blocking operations.
+ */
+ VALUE timeout;
} rb_io_t;
/** @alias{rb_io_enc_t} */
@@ -846,13 +858,37 @@ int rb_io_wait_writable(int fd);
int rb_wait_for_single_fd(int fd, int events, struct timeval *tv);
/**
+ * Get the timeout associated with the specified io object.
+ *
+ * @param[in] io An IO object.
+ * @retval RUBY_Qnil There is no associated timeout.
+ * @retval Otherwise The timeout value.
+ */
+VALUE rb_io_timeout(VALUE io);
+
+/**
+ * Set the timeout associated with the specified io object. This timeout is
+ * used as a best effort timeout to prevent operations from blocking forever.
+ *
+ * @param[in] io An IO object.
+ * @param[in] timeout A timeout value. Must respond to #to_f.
+ * @
+ */
+VALUE rb_io_set_timeout(VALUE io, VALUE timeout);
+
+/**
* Blocks until the passed IO is ready for the passed events. The "events"
* here is a Ruby level integer, which is an OR-ed value of `IO::READABLE`,
* `IO::WRITable`, and `IO::PRIORITY`.
*
+ * If timeout is `Qnil`, it will use the default timeout as given by
+ * `rb_io_timeout(io)`.
+ *
* @param[in] io An IO object to wait.
* @param[in] events See above.
* @param[in] timeout Time, or numeric seconds since UNIX epoch.
+ * If Qnil, use the default timeout. If Qfalse
+ * or Qundef, wait forever.
* @exception rb_eIOError `io` is not open.
* @exception rb_eRangeError `timeout` is out of range.
* @exception rb_eSystemCallError `select(2)` failed for some reason.
@@ -904,13 +940,8 @@ VALUE rb_io_maybe_wait(int error, VALUE io, VALUE events, VALUE timeout);
* @exception rb_eIOError `io` is not open.
* @exception rb_eRangeError `timeout` is out of range.
* @exception rb_eSystemCallError `select(2)` failed for some reason.
- * @exception rb_eTypeError Operation timed out.
- * @return Always returns ::RUBY_IO_READABLE.
- *
- * @internal
- *
- * Because rb_io_maybe_wait() returns ::RUBY_Qfalse on timeout, this function
- * fails to convert that value to `int`, and raises ::rb_eTypeError.
+ * @retval 0 Operation timed out.
+ * @retval Otherwise Always returns ::RUBY_IO_READABLE.
*/
int rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout);
@@ -925,13 +956,8 @@ int rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout);
* @exception rb_eIOError `io` is not open.
* @exception rb_eRangeError `timeout` is out of range.
* @exception rb_eSystemCallError `select(2)` failed for some reason.
- * @exception rb_eTypeError Operation timed out.
- * @return Always returns ::RUBY_IO_WRITABLE.
- *
- * @internal
- *
- * Because rb_io_maybe_wait() returns ::RUBY_Qfalse on timeout, this function
- * fails to convert that value to `int`, and raises ::rb_eTypeError.
+ * @retval 0 Operation timed out.
+ * @retval Otherwise Always returns ::RUBY_IO_WRITABLE.
*/
int rb_io_maybe_wait_writable(int error, VALUE io, VALUE timeout);
diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h
index 907fec20bb..e4b855d8e7 100644
--- a/include/ruby/io/buffer.h
+++ b/include/ruby/io/buffer.h
@@ -1,5 +1,5 @@
-#ifndef RUBY_IO_BUFFER_T
-#define RUBY_IO_BUFFER_T 1
+#ifndef RUBY_IO_BUFFER_H
+#define RUBY_IO_BUFFER_H
/**
* @file
* @author Samuel Williams
@@ -21,6 +21,8 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
// WARNING: This entire interface is experimental and may change in the future!
#define RB_IO_BUFFER_EXPERIMENTAL 1
+#define RUBY_IO_BUFFER_VERSION 2
+
RUBY_EXTERN VALUE rb_cIOBuffer;
RUBY_EXTERN size_t RUBY_IO_BUFFER_PAGE_SIZE;
RUBY_EXTERN size_t RUBY_IO_BUFFER_DEFAULT_SIZE;
@@ -35,6 +37,9 @@ enum rb_io_buffer_flags {
// A non-private mapping is marked as external.
RB_IO_BUFFER_MAPPED = 4,
+ // A mapped buffer that is also shared.
+ RB_IO_BUFFER_SHARED = 8,
+
// The buffer is locked and cannot be resized.
// More specifically, it means we can't change the base address or size.
// A buffer is typically locked before a system call that uses the data.
@@ -51,21 +56,17 @@ enum rb_io_buffer_endian {
RB_IO_BUFFER_LITTLE_ENDIAN = 4,
RB_IO_BUFFER_BIG_ENDIAN = 8,
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN,
-#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#if defined(WORDS_BIGENDIAN)
RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN,
-#elif REG_DWORD == REG_DWORD_LITTLE_ENDIAN
+#else
RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN,
-#elif REG_DWORD == REG_DWORD_BIG_ENDIAN
- RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN,
#endif
RB_IO_BUFFER_NETWORK_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN
};
VALUE rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags);
-VALUE rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags flags);
+VALUE rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags);
VALUE rb_io_buffer_lock(VALUE self);
VALUE rb_io_buffer_unlock(VALUE self);
@@ -81,11 +82,11 @@ void rb_io_buffer_resize(VALUE self, size_t size);
void rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length);
// The length is the minimum required length.
-VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length);
-VALUE rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset);
-VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length);
-VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset);
+VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset);
+VALUE rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset);
+VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset);
+VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset);
RBIMPL_SYMBOL_EXPORT_END()
-#endif /* RUBY_IO_BUFFER_T */
+#endif /* RUBY_IO_BUFFER_H */
diff --git a/include/ruby/memory_view.h b/include/ruby/memory_view.h
index 83931038a0..1ddca2d46f 100644
--- a/include/ruby/memory_view.h
+++ b/include/ruby/memory_view.h
@@ -16,7 +16,7 @@
# include <stddef.h> /* size_t */
#endif
-#if HAVE_SYS_TYPES_H
+#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h> /* ssize_t */
#endif
diff --git a/include/ruby/onigmo.h b/include/ruby/onigmo.h
index a7ef59c7c8..8d7c601703 100644
--- a/include/ruby/onigmo.h
+++ b/include/ruby/onigmo.h
@@ -356,9 +356,9 @@ int onigenc_ascii_only_case_map(OnigCaseFoldType* flagP, const OnigUChar** pp, c
#define ONIGENC_PRECISE_MBC_ENC_LEN(enc,p,e) (enc)->precise_mbc_enc_len(p,e,enc)
ONIG_EXTERN
-int onigenc_mbclen_approximate(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc);
+int onigenc_mbclen(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc);
-#define ONIGENC_MBC_ENC_LEN(enc,p,e) onigenc_mbclen_approximate(p,e,enc)
+#define ONIGENC_MBC_ENC_LEN(enc,p,e) onigenc_mbclen(p,e,enc)
#define ONIGENC_MBC_MAXLEN(enc) ((enc)->max_enc_len)
#define ONIGENC_MBC_MAXLEN_DIST(enc) ONIGENC_MBC_MAXLEN(enc)
#define ONIGENC_MBC_MINLEN(enc) ((enc)->min_enc_len)
@@ -744,6 +744,8 @@ typedef struct {
typedef struct {
int lower;
int upper;
+ long base_num;
+ long inner_num;
} OnigRepeatRange;
typedef void (*OnigWarnFunc)(const char* s);
@@ -852,6 +854,8 @@ OnigPosition onig_search_gpos(OnigRegex, const OnigUChar* str, const OnigUChar*
ONIG_EXTERN
OnigPosition onig_match(OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* at, OnigRegion* region, OnigOptionType option);
ONIG_EXTERN
+int onig_check_linear_time(OnigRegex reg);
+ONIG_EXTERN
OnigRegion* onig_region_new(void);
ONIG_EXTERN
void onig_region_init(OnigRegion* region);
diff --git a/include/ruby/random.h b/include/ruby/random.h
index 657b37f034..39bdb6f3e3 100644
--- a/include/ruby/random.h
+++ b/include/ruby/random.h
@@ -16,6 +16,26 @@
#include "ruby/ruby.h"
+/*
+ * version
+ * 0: before versioning; deprecated
+ * 1: added version, flags and init_32bit function
+ */
+#define RUBY_RANDOM_INTERFACE_VERSION_MAJOR 1
+#define RUBY_RANDOM_INTERFACE_VERSION_MINOR 0
+
+#define RUBY_RANDOM_PASTE_VERSION_SUFFIX(x, y, z) x##_##y##_##z
+#define RUBY_RANDOM_WITH_VERSION_SUFFIX(name, major, minor) \
+ RUBY_RANDOM_PASTE_VERSION_SUFFIX(name, major, minor)
+#define rb_random_data_type \
+ RUBY_RANDOM_WITH_VERSION_SUFFIX(rb_random_data_type, \
+ RUBY_RANDOM_INTERFACE_VERSION_MAJOR, \
+ RUBY_RANDOM_INTERFACE_VERSION_MINOR)
+#define RUBY_RANDOM_INTERFACE_VERSION_INITIALIZER \
+ {RUBY_RANDOM_INTERFACE_VERSION_MAJOR, RUBY_RANDOM_INTERFACE_VERSION_MINOR}
+#define RUBY_RANDOM_INTERFACE_VERSION_MAJOR_MAX 0xff
+#define RUBY_RANDOM_INTERFACE_VERSION_MINOR_MAX 0xff
+
RBIMPL_SYMBOL_EXPORT_BEGIN()
/**
@@ -48,6 +68,17 @@ typedef void rb_random_init_func(rb_random_t *rng, const uint32_t *buf, size_t l
RBIMPL_ATTR_NONNULL(())
/**
+ * This is the type of functions called when your random object is initialised.
+ * Passed data is the seed integer.
+ *
+ * @param[out] rng Your random struct to fill in.
+ * @param[in] data Seed, single word.
+ * @post `rng` is initialised using the passed seeds.
+ */
+typedef void rb_random_init_int32_func(rb_random_t *rng, uint32_t data);
+
+RBIMPL_ATTR_NONNULL(())
+/**
* This is the type of functions called from your object's `#rand` method.
*
* @param[out] rng Your random struct to extract an integer from.
@@ -84,9 +115,24 @@ typedef struct {
/** Number of bits of seed numbers. */
size_t default_seed_bits;
- /** Initialiser function. */
+ /**
+ * Major/minor versions of this interface
+ */
+ struct {
+ uint8_t major, minor;
+ } version;
+
+ /**
+ * Reserved flags
+ */
+ uint16_t flags;
+
+ /** Function to initialize from uint32_t array. */
rb_random_init_func *init;
+ /** Function to initialize from single uint32_t. */
+ rb_random_init_int32_func *init_int32;
+
/** Function to obtain a random integer. */
rb_random_get_int32_func *get_int32;
@@ -130,11 +176,12 @@ typedef struct {
} rb_random_interface_t;
/**
- * This utility macro defines 3 functions named prefix_init, prefix_get_int32,
- * prefix_get_bytes.
+ * This utility macro defines 4 functions named prefix_init, prefix_init_int32,
+ * prefix_get_int32, prefix_get_bytes.
*/
#define RB_RANDOM_INTERFACE_DECLARE(prefix) \
static void prefix##_init(rb_random_t *, const uint32_t *, size_t); \
+ static void prefix##_init_int32(rb_random_t *, uint32_t); \
static unsigned int prefix##_get_int32(rb_random_t *); \
static void prefix##_get_bytes(rb_random_t *, void *, size_t)
@@ -161,7 +208,9 @@ typedef struct {
* ```
*/
#define RB_RANDOM_INTERFACE_DEFINE(prefix) \
+ RUBY_RANDOM_INTERFACE_VERSION_INITIALIZER, 0, \
prefix##_init, \
+ prefix##_init_int32, \
prefix##_get_int32, \
prefix##_get_bytes
@@ -173,6 +222,12 @@ typedef struct {
RB_RANDOM_INTERFACE_DEFINE(prefix), \
prefix##_get_real
+#define RB_RANDOM_DEFINE_INIT_INT32_FUNC(prefix) \
+ static void prefix##_init_int32(rb_random_t *rnd, uint32_t data) \
+ { \
+ prefix##_init(rnd, &data, 1); \
+ }
+
#if defined _WIN32 && !defined __CYGWIN__
typedef rb_data_type_t rb_random_data_type_t;
# define RB_RANDOM_PARENT 0
@@ -189,7 +244,7 @@ typedef const rb_data_type_t rb_random_data_type_t;
* 0, RB_RANDOM_INTERFACE_DEFINE(your),
* };
*
- * static inline constexpr your_prng = {
+ * static inline constexpr rb_random_data_type_t your_prng_type = {
* "your PRNG",
* { rb_random_mark, },
* RB_RANDOM_PARENT, // <<-- HERE
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index 108127a93c..444940ca3a 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -108,7 +108,7 @@ VALUE rb_get_path_no_checksafe(VALUE);
# define rb_varargs_argc_check_runtime(argc, vargc) \
(((argc) <= (vargc)) ? (argc) : \
(rb_fatal("argc(%d) exceeds actual arguments(%d)", \
- argc, vargc), 0))
+ argc, vargc), 0))
# define rb_varargs_argc_valid_p(argc, vargc) \
((argc) == 0 ? (vargc) <= 1 : /* [ruby-core:85266] [Bug #14425] */ \
(argc) == (vargc))
@@ -117,16 +117,16 @@ VALUE rb_get_path_no_checksafe(VALUE);
ERRORFUNC((" argument length doesn't match"), int rb_varargs_bad_length(int,int));
# else
# define rb_varargs_bad_length(argc, vargc) \
- ((argc)/rb_varargs_argc_valid_p(argc, vargc))
+ ((argc)/rb_varargs_argc_valid_p(argc, vargc))
# endif
# define rb_varargs_argc_check(argc, vargc) \
__builtin_choose_expr(__builtin_constant_p(argc), \
- (rb_varargs_argc_valid_p(argc, vargc) ? (argc) : \
- rb_varargs_bad_length(argc, vargc)), \
- rb_varargs_argc_check_runtime(argc, vargc))
+ (rb_varargs_argc_valid_p(argc, vargc) ? (argc) : \
+ rb_varargs_bad_length(argc, vargc)), \
+ rb_varargs_argc_check_runtime(argc, vargc))
# else
# define rb_varargs_argc_check(argc, vargc) \
- rb_varargs_argc_check_runtime(argc, vargc)
+ rb_varargs_argc_check_runtime(argc, vargc)
# endif
#endif
/** @endcond */
@@ -277,24 +277,24 @@ int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap);
#elif defined(__GNUC__) && defined(HAVE_VA_ARGS_MACRO) && defined(__OPTIMIZE__)
# define rb_yield_values(argc, ...) \
__extension__({ \
- const int rb_yield_values_argc = (argc); \
- const VALUE rb_yield_values_args[] = {__VA_ARGS__}; \
- const int rb_yield_values_nargs = \
- (int)(sizeof(rb_yield_values_args) / sizeof(VALUE)); \
- rb_yield_values2( \
- rb_varargs_argc_check(rb_yield_values_argc, rb_yield_values_nargs), \
- rb_yield_values_nargs ? rb_yield_values_args : NULL); \
+ const int rb_yield_values_argc = (argc); \
+ const VALUE rb_yield_values_args[] = {__VA_ARGS__}; \
+ const int rb_yield_values_nargs = \
+ (int)(sizeof(rb_yield_values_args) / sizeof(VALUE)); \
+ rb_yield_values2( \
+ rb_varargs_argc_check(rb_yield_values_argc, rb_yield_values_nargs), \
+ rb_yield_values_nargs ? rb_yield_values_args : NULL); \
})
# define rb_funcall(recv, mid, argc, ...) \
__extension__({ \
- const int rb_funcall_argc = (argc); \
- const VALUE rb_funcall_args[] = {__VA_ARGS__}; \
- const int rb_funcall_nargs = \
- (int)(sizeof(rb_funcall_args) / sizeof(VALUE)); \
+ const int rb_funcall_argc = (argc); \
+ const VALUE rb_funcall_args[] = {__VA_ARGS__}; \
+ const int rb_funcall_nargs = \
+ (int)(sizeof(rb_funcall_args) / sizeof(VALUE)); \
rb_funcallv(recv, mid, \
- rb_varargs_argc_check(rb_funcall_argc, rb_funcall_nargs), \
- rb_funcall_nargs ? rb_funcall_args : NULL); \
+ rb_varargs_argc_check(rb_funcall_argc, rb_funcall_nargs), \
+ rb_funcall_nargs ? rb_funcall_args : NULL); \
})
#endif
/** @endcond */
diff --git a/include/ruby/st.h b/include/ruby/st.h
index 1e4bb80686..f35ab43603 100644
--- a/include/ruby/st.h
+++ b/include/ruby/st.h
@@ -98,6 +98,8 @@ struct st_table {
enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK, ST_REPLACE};
+size_t rb_st_table_size(const struct st_table *tbl);
+#define st_table_size rb_st_table_size
st_table *rb_st_init_table(const struct st_hash_type *);
#define st_init_table rb_st_init_table
st_table *rb_st_init_table_with_size(const struct st_hash_type *, st_index_t);
diff --git a/include/ruby/thread.h b/include/ruby/thread.h
index 299aa2d62e..0b5b1ca0f3 100644
--- a/include/ruby/thread.h
+++ b/include/ruby/thread.h
@@ -128,7 +128,7 @@ RBIMPL_ATTR_NONNULL((1))
* your code to see if it is actually worth releasing the GVL.
*/
void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
- rb_unblock_function_t *ubf, void *data2);
+ rb_unblock_function_t *ubf, void *data2);
RBIMPL_ATTR_NONNULL((1))
/**
@@ -152,7 +152,7 @@ RBIMPL_ATTR_NONNULL((1))
* @return What `func` returned, or 0 in case `func` did not return.
*/
void *rb_thread_call_without_gvl2(void *(*func)(void *), void *data1,
- rb_unblock_function_t *ubf, void *data2);
+ rb_unblock_function_t *ubf, void *data2);
/*
* XXX: unstable/unapproved - out-of-tree code should NOT not depend
diff --git a/include/ruby/util.h b/include/ruby/util.h
index b2bc1a09f6..e8727a3200 100644
--- a/include/ruby/util.h
+++ b/include/ruby/util.h
@@ -19,7 +19,7 @@
# include <stddef.h> /* size_t */
#endif
-#if HAVE_SYS_TYPES_H
+#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h> /* ssize_t */
#endif
@@ -124,7 +124,7 @@ unsigned long ruby_scan_hex(const char *str, size_t len, size_t *ret);
# define ruby_qsort qsort_r
#else
void ruby_qsort(void *, const size_t, const size_t,
- int (*)(const void *, const void *, void *), void *);
+ int (*)(const void *, const void *, void *), void *);
#endif
RBIMPL_ATTR_NONNULL((1))
diff --git a/include/ruby/win32.h b/include/ruby/win32.h
index a3f466d627..18de3a17d8 100644
--- a/include/ruby/win32.h
+++ b/include/ruby/win32.h
@@ -19,11 +19,6 @@ RUBY_SYMBOL_EXPORT_BEGIN
*/
/*
- * Definitions for NT port of Perl
- */
-
-
-/*
* Ok now we can include the normal include files.
*/
@@ -130,8 +125,15 @@ typedef unsigned int uintptr_t;
#define O_SHARE_DELETE 0x20000000 /* for rb_w32_open(), rb_w32_wopen() */
typedef int clockid_t;
+#if defined(__MINGW32__)
+#undef CLOCK_PROCESS_CPUTIME_ID
+#undef CLOCK_THREAD_CPUTIME_ID
+#undef CLOCK_REALTIME_COARSE
+#endif
+#if defined(HAVE_CLOCK_GETTIME) && !defined(CLOCK_REALTIME)
#define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1
+#endif
#undef utime
#undef lseek
@@ -195,7 +197,6 @@ struct stati128 {
long st_ctimensec;
};
-#define off_t __int64
#define stat stati128
#undef SIZEOF_STRUCT_STAT_ST_INO
#define SIZEOF_STRUCT_STAT_ST_INO sizeof(unsigned __int64)
@@ -303,7 +304,6 @@ extern DWORD rb_w32_osver(void);
extern int rb_w32_uchown(const char *, int, int);
extern int rb_w32_ulink(const char *, const char *);
extern ssize_t rb_w32_ureadlink(const char *, char *, size_t);
-extern ssize_t rb_w32_wreadlink(const WCHAR *, WCHAR *, size_t);
extern int rb_w32_usymlink(const char *src, const char *link);
extern int gettimeofday(struct timeval *, struct timezone *);
extern int clock_gettime(clockid_t, struct timespec *);
@@ -394,6 +394,7 @@ scalb(double a, long b)
#endif
#define S_IFLNK 0xa000
+#define S_IFSOCK 0xc000
/*
* define this so we can do inplace editing
@@ -401,9 +402,9 @@ scalb(double a, long b)
#define SUFFIX
-extern int rb_w32_ftruncate(int fd, off_t length);
-extern int rb_w32_truncate(const char *path, off_t length);
-extern int rb_w32_utruncate(const char *path, off_t length);
+extern int rb_w32_ftruncate(int fd, rb_off_t length);
+extern int rb_w32_truncate(const char *path, rb_off_t length);
+extern int rb_w32_utruncate(const char *path, rb_off_t length);
#undef HAVE_FTRUNCATE
#define HAVE_FTRUNCATE 1
@@ -702,10 +703,10 @@ extern char *rb_w32_strerror(int);
#endif
struct tms {
- long tms_utime;
- long tms_stime;
- long tms_cutime;
- long tms_cstime;
+ long tms_utime;
+ long tms_stime;
+ long tms_cutime;
+ long tms_cstime;
};
int rb_w32_times(struct tms *);
@@ -722,7 +723,7 @@ int rb_w32_fclose(FILE*);
int rb_w32_pipe(int[2]);
ssize_t rb_w32_read(int, void *, size_t);
ssize_t rb_w32_write(int, const void *, size_t);
-off_t rb_w32_lseek(int, off_t, int);
+rb_off_t rb_w32_lseek(int, rb_off_t, int);
int rb_w32_uutime(const char *, const struct utimbuf *);
int rb_w32_uutimes(const char *, const struct timeval *);
int rb_w32_uutimensat(int /* must be AT_FDCWD */, const char *, const struct timespec *, int /* must be 0 */);
@@ -815,7 +816,7 @@ double rb_w32_pow(double x, double y);
#define MAP_ANON 0x1000
#define MAP_ANONYMOUS MAP_ANON
-extern void *rb_w32_mmap(void *, size_t, int, int, int, off_t);
+extern void *rb_w32_mmap(void *, size_t, int, int, int, rb_off_t);
extern int rb_w32_munmap(void *, size_t);
extern int rb_w32_mprotect(void *, size_t, int);
diff --git a/inits.c b/inits.c
index f41e88d838..e809b56cc9 100644
--- a/inits.c
+++ b/inits.c
@@ -20,6 +20,7 @@ static void Init_builtin_prelude(void);
void
rb_call_inits(void)
{
+ CALL(default_shapes);
CALL(Thread_Mutex);
#if USE_TRANSIENT_HEAP
CALL(TransientHeap);
@@ -77,6 +78,7 @@ rb_call_inits(void)
CALL(vm_stack_canary);
CALL(ast);
CALL(gc_stress);
+ CALL(shape);
// enable builtin loading
CALL(builtin);
@@ -97,12 +99,15 @@ rb_call_builtin_inits(void)
BUILTIN(warning);
BUILTIN(array);
BUILTIN(kernel);
+ BUILTIN(symbol);
BUILTIN(timev);
+ BUILTIN(thread_sync);
BUILTIN(yjit);
BUILTIN(nilclass);
BUILTIN(marshal);
#if USE_MJIT
BUILTIN(mjit);
+ BUILTIN(mjit_c);
#endif
Init_builtin_prelude();
}
diff --git a/insns.def b/insns.def
index b5dea9c10c..9f5ee7095a 100644
--- a/insns.def
+++ b/insns.def
@@ -109,14 +109,14 @@ getblockparam
VM_ASSERT(VM_ENV_LOCAL_P(ep));
if (!VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)) {
- val = rb_vm_bh_to_procval(ec, VM_ENV_BLOCK_HANDLER(ep));
- vm_env_write(ep, -(int)idx, val);
- VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM);
+ val = rb_vm_bh_to_procval(ec, VM_ENV_BLOCK_HANDLER(ep));
+ vm_env_write(ep, -(int)idx, val);
+ VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM);
}
else {
- val = *(ep - idx);
- RB_DEBUG_COUNTER_INC(lvar_get);
- (void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0);
+ val = *(ep - idx);
+ RB_DEBUG_COUNTER_INC(lvar_get);
+ (void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0);
}
}
@@ -150,35 +150,35 @@ getblockparamproxy
VM_ASSERT(VM_ENV_LOCAL_P(ep));
if (!VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)) {
- VALUE block_handler = VM_ENV_BLOCK_HANDLER(ep);
-
- if (block_handler) {
- switch (vm_block_handler_type(block_handler)) {
- case block_handler_type_iseq:
- case block_handler_type_ifunc:
- val = rb_block_param_proxy;
- break;
- case block_handler_type_symbol:
- val = rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler));
- goto INSN_LABEL(set);
- case block_handler_type_proc:
- val = VM_BH_TO_PROC(block_handler);
- goto INSN_LABEL(set);
- default:
- VM_UNREACHABLE(getblockparamproxy);
- }
- }
- else {
- val = Qnil;
- INSN_LABEL(set):
- vm_env_write(ep, -(int)idx, val);
- VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM);
- }
+ VALUE block_handler = VM_ENV_BLOCK_HANDLER(ep);
+
+ if (block_handler) {
+ switch (vm_block_handler_type(block_handler)) {
+ case block_handler_type_iseq:
+ case block_handler_type_ifunc:
+ val = rb_block_param_proxy;
+ break;
+ case block_handler_type_symbol:
+ val = rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler));
+ goto INSN_LABEL(set);
+ case block_handler_type_proc:
+ val = VM_BH_TO_PROC(block_handler);
+ goto INSN_LABEL(set);
+ default:
+ VM_UNREACHABLE(getblockparamproxy);
+ }
+ }
+ else {
+ val = Qnil;
+ INSN_LABEL(set):
+ vm_env_write(ep, -(int)idx, val);
+ VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM);
+ }
}
else {
- val = *(ep - idx);
- RB_DEBUG_COUNTER_INC(lvar_get);
- (void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0);
+ val = *(ep - idx);
+ RB_DEBUG_COUNTER_INC(lvar_get);
+ (void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0);
}
}
@@ -253,6 +253,29 @@ setclassvariable
vm_setclassvariable(GET_ISEQ(), GET_CFP(), id, val, ic);
}
+DEFINE_INSN
+opt_getconstant_path
+(IC ic)
+()
+(VALUE val)
+// attr bool leaf = false; /* may autoload or raise */
+{
+ const ID *segments = ic->segments;
+ struct iseq_inline_constant_cache_entry *ice = ic->entry;
+ if (ice && vm_ic_hit_p(ice, GET_EP())) {
+ val = ice->value;
+
+ VM_ASSERT(val == vm_get_ev_const_chain(ec, segments));
+ } else {
+ ruby_vm_constant_cache_misses++;
+ val = vm_get_ev_const_chain(ec, segments);
+ vm_ic_track_const_chain(GET_CFP(), ic, segments);
+ // Because leaf=false, we need to undo the PC increment to get the address to this instruction
+ // INSN_ATTR(width) == 2
+ vm_ic_update(GET_ISEQ(), ic, val, GET_EP(), GET_PC() - 2);
+ }
+}
+
/* Get constant variable id. If klass is Qnil and allow_nil is Qtrue, constants
are searched in the current scope. Otherwise, get constant under klass
class or module.
@@ -597,6 +620,25 @@ swap
/* none */
}
+/* reverse stack top N order. */
+DEFINE_INSN
+opt_reverse
+(rb_num_t n)
+(...)
+(...)
+// attr rb_snum_t sp_inc = 0;
+{
+ rb_num_t i;
+ VALUE *sp = STACK_ADDR_FROM_TOP(n);
+
+ for (i=0; i<n/2; i++) {
+ VALUE v0 = sp[i];
+ VALUE v1 = TOPN(i);
+ sp[i] = v1;
+ TOPN(i) = v0;
+ }
+}
+
/* for stack caching. */
DEFINE_INSN_IF(STACK_CACHING)
reput
@@ -655,7 +697,7 @@ defined
{
val = Qnil;
if (vm_defined(ec, GET_CFP(), op_type, obj, v)) {
- val = pushval;
+ val = pushval;
}
}
@@ -715,8 +757,8 @@ defineclass
/* enter scope */
vm_push_frame(ec, class_iseq, VM_FRAME_MAGIC_CLASS | VM_ENV_FLAG_LOCAL, klass,
- GET_BLOCK_HANDLER(),
- (VALUE)vm_cref_push(ec, klass, NULL, FALSE, FALSE),
+ GET_BLOCK_HANDLER(),
+ (VALUE)vm_cref_push(ec, klass, NULL, FALSE, FALSE),
ISEQ_BODY(class_iseq)->iseq_encoded, GET_SP(),
ISEQ_BODY(class_iseq)->local_table_size,
ISEQ_BODY(class_iseq)->stack_max);
@@ -920,19 +962,19 @@ leave
const VALUE *const bp = vm_base_ptr(GET_CFP());
if (GET_SP() != bp) {
vm_stack_consistency_error(ec, GET_CFP(), bp);
- }
+ }
}
if (vm_pop_frame(ec, GET_CFP(), GET_EP())) {
#if OPT_CALL_THREADED_CODE
- rb_ec_thread_ptr(ec)->retval = val;
- return 0;
+ rb_ec_thread_ptr(ec)->retval = val;
+ return 0;
#else
- return val;
+ return val;
#endif
}
else {
- RESTORE_REGS();
+ RESTORE_REGS();
}
}
@@ -981,8 +1023,8 @@ branchif
// attr bool leaf = leafness_of_check_ints; /* has rb_threadptr_execute_interrupts() */
{
if (RTEST(val)) {
- RUBY_VM_CHECK_INTS(ec);
- JUMP(dst);
+ RUBY_VM_CHECK_INTS(ec);
+ JUMP(dst);
}
}
@@ -996,8 +1038,8 @@ branchunless
// attr bool leaf = leafness_of_check_ints; /* has rb_threadptr_execute_interrupts() */
{
if (!RTEST(val)) {
- RUBY_VM_CHECK_INTS(ec);
- JUMP(dst);
+ RUBY_VM_CHECK_INTS(ec);
+ JUMP(dst);
}
}
@@ -1011,8 +1053,8 @@ branchnil
// attr bool leaf = leafness_of_check_ints; /* has rb_threadptr_execute_interrupts() */
{
if (NIL_P(val)) {
- RUBY_VM_CHECK_INTS(ec);
- JUMP(dst);
+ RUBY_VM_CHECK_INTS(ec);
+ JUMP(dst);
}
}
@@ -1020,46 +1062,6 @@ branchnil
/* for optimize */
/**********************************************************/
-/* push inline-cached value and go to dst if it is valid */
-DEFINE_INSN
-opt_getinlinecache
-(OFFSET dst, IC ic)
-()
-(VALUE val)
-{
- struct iseq_inline_constant_cache_entry *ice = ic->entry;
-
- // If there isn't an entry, then we're going to walk through the ISEQ
- // starting at this instruction until we get to the associated
- // opt_setinlinecache and associate this inline cache with every getconstant
- // listed in between. We're doing this here instead of when the instructions
- // are first compiled because it's possible to turn off inline caches and we
- // want this to work in either case.
- if (!ice) {
- vm_ic_compile(GET_CFP(), ic);
- }
-
- if (ice && vm_ic_hit_p(ice, GET_EP())) {
- val = ice->value;
- JUMP(dst);
- }
- else {
- ruby_vm_constant_cache_misses++;
- val = Qnil;
- }
-}
-
-/* set inline cache */
-DEFINE_INSN
-opt_setinlinecache
-(IC ic)
-(VALUE val)
-(VALUE val)
-// attr bool leaf = false;
-{
- vm_ic_update(GET_ISEQ(), ic, val, GET_EP());
-}
-
/* run iseq only once */
DEFINE_INSN
once
@@ -1081,7 +1083,7 @@ opt_case_dispatch
OFFSET dst = vm_case_dispatch(hash, else_offset, key);
if (dst) {
- JUMP(dst);
+ JUMP(dst);
}
}
@@ -1340,12 +1342,12 @@ opt_aset_with
VALUE tmp = vm_opt_aset_with(recv, key, val);
if (tmp != Qundef) {
- val = tmp;
+ val = tmp;
}
else {
#ifndef MJIT_HEADER
- TOPN(0) = rb_str_resurrect(key);
- PUSH(val);
+ TOPN(0) = rb_str_resurrect(key);
+ PUSH(val);
#endif
CALL_SIMPLE_METHOD();
}
@@ -1364,7 +1366,7 @@ opt_aref_with
if (val == Qundef) {
#ifndef MJIT_HEADER
- PUSH(rb_str_resurrect(key));
+ PUSH(rb_str_resurrect(key));
#endif
CALL_SIMPLE_METHOD();
}
@@ -1467,9 +1469,9 @@ opt_call_c_function
reg_cfp = (funcptr)(ec, reg_cfp);
if (reg_cfp == 0) {
- VALUE err = ec->errinfo;
- ec->errinfo = Qnil;
- THROW_EXCEPTION(err);
+ VALUE err = ec->errinfo;
+ ec->errinfo = Qnil;
+ THROW_EXCEPTION(err);
}
RESTORE_REGS();
diff --git a/internal.h b/internal.h
index 0740ae99e5..b63af50616 100644
--- a/internal.h
+++ b/internal.h
@@ -25,6 +25,9 @@
/* Prevent compiler from reordering access */
#define ACCESS_ONCE(type,x) (*((volatile type *)&(x)))
+#define UNDEF_P RB_UNDEF_P
+#define NIL_OR_UNDEF_P RB_NIL_OR_UNDEF_P
+
#include "ruby/ruby.h"
/* Following macros were formerly defined in this header but moved to somewhere
@@ -48,9 +51,6 @@
#undef RHASH_TBL
#undef RHASH_EMPTY_P
-/* internal/object.h */
-#undef ROBJECT_IV_INDEX_TBL
-
/* internal/struct.h */
#undef RSTRUCT_LEN
#undef RSTRUCT_PTR
diff --git a/internal/array.h b/internal/array.h
index d8a824423d..a0d16dec3f 100644
--- a/internal/array.h
+++ b/internal/array.h
@@ -21,13 +21,12 @@
#define RARRAY_SHARED_FLAG ELTS_SHARED
#define RARRAY_SHARED_ROOT_FLAG FL_USER12
#define RARRAY_PTR_IN_USE_FLAG FL_USER14
-#define RARRAY_LITERAL_FLAG FL_USER15
/* array.c */
VALUE rb_ary_last(int, const VALUE *, VALUE);
void rb_ary_set_len(VALUE, long);
void rb_ary_delete_same(VALUE, VALUE);
-VALUE rb_ary_tmp_new_fill(long capa);
+VALUE rb_ary_hidden_new_fill(long capa);
VALUE rb_ary_at(VALUE, VALUE);
size_t rb_ary_memsize(VALUE);
VALUE rb_to_array_type(VALUE obj);
@@ -36,7 +35,7 @@ void rb_ary_cancel_sharing(VALUE ary);
size_t rb_ary_size_as_embedded(VALUE ary);
void rb_ary_make_embedded(VALUE ary);
bool rb_ary_embeddable_p(VALUE ary);
-VALUE rb_ary_literal_new(long capa);
+VALUE rb_ary_diff(VALUE ary1, VALUE ary2);
static inline VALUE rb_ary_entry_internal(VALUE ary, long offset);
static inline bool ARY_PTR_USING_P(VALUE ary);
@@ -120,13 +119,6 @@ ARY_SHARED_ROOT_REFCNT(VALUE ary)
return RARRAY(ary)->as.heap.aux.capa;
}
-static inline bool
-ARY_LITERAL_P(VALUE ary)
-{
- assert(RB_TYPE_P(ary, T_ARRAY));
- return FL_TEST_RAW(ary, RARRAY_LITERAL_FLAG);
-}
-
static inline void
RARY_TRANSIENT_SET(VALUE ary)
{
diff --git a/internal/basic_operators.h b/internal/basic_operators.h
new file mode 100644
index 0000000000..2cd9f50073
--- /dev/null
+++ b/internal/basic_operators.h
@@ -0,0 +1,64 @@
+#ifndef INTERNAL_BOP_H /*-*-C-*-vi:se ft=c:*/
+#define INTERNAL_BOP_H
+
+#include "internal.h"
+#include "ruby/internal/dllexport.h"
+
+enum ruby_basic_operators {
+ BOP_PLUS,
+ BOP_MINUS,
+ BOP_MULT,
+ BOP_DIV,
+ BOP_MOD,
+ BOP_EQ,
+ BOP_EQQ,
+ BOP_LT,
+ BOP_LE,
+ BOP_LTLT,
+ BOP_AREF,
+ BOP_ASET,
+ BOP_LENGTH,
+ BOP_SIZE,
+ BOP_EMPTY_P,
+ BOP_NIL_P,
+ BOP_SUCC,
+ BOP_GT,
+ BOP_GE,
+ BOP_NOT,
+ BOP_NEQ,
+ BOP_MATCH,
+ BOP_FREEZE,
+ BOP_UMINUS,
+ BOP_MAX,
+ BOP_MIN,
+ BOP_CALL,
+ BOP_AND,
+ BOP_OR,
+ BOP_CMP,
+ BOP_DEFAULT,
+
+ BOP_LAST_
+};
+
+MJIT_SYMBOL_EXPORT_BEGIN
+RUBY_EXTERN short ruby_vm_redefined_flag[BOP_LAST_];
+MJIT_SYMBOL_EXPORT_END
+
+/* optimize insn */
+#define INTEGER_REDEFINED_OP_FLAG (1 << 0)
+#define FLOAT_REDEFINED_OP_FLAG (1 << 1)
+#define STRING_REDEFINED_OP_FLAG (1 << 2)
+#define ARRAY_REDEFINED_OP_FLAG (1 << 3)
+#define HASH_REDEFINED_OP_FLAG (1 << 4)
+/* #define BIGNUM_REDEFINED_OP_FLAG (1 << 5) */
+#define SYMBOL_REDEFINED_OP_FLAG (1 << 6)
+#define TIME_REDEFINED_OP_FLAG (1 << 7)
+#define REGEXP_REDEFINED_OP_FLAG (1 << 8)
+#define NIL_REDEFINED_OP_FLAG (1 << 9)
+#define TRUE_REDEFINED_OP_FLAG (1 << 10)
+#define FALSE_REDEFINED_OP_FLAG (1 << 11)
+#define PROC_REDEFINED_OP_FLAG (1 << 12)
+
+#define BASIC_OP_UNREDEFINED_P(op, klass) (LIKELY((ruby_vm_redefined_flag[(op)]&(klass)) == 0))
+
+#endif
diff --git a/internal/class.h b/internal/class.h
index ae680564a6..63917e867f 100644
--- a/internal/class.h
+++ b/internal/class.h
@@ -14,6 +14,10 @@
#include "ruby/internal/stdbool.h" /* for bool */
#include "ruby/intern.h" /* for rb_alloc_func_t */
#include "ruby/ruby.h" /* for struct RBasic */
+#include "shape.h"
+#include "ruby_assert.h"
+#include "vm_core.h"
+#include "method.h" /* for rb_cref_t */
#ifdef RCLASS_SUPER
# undef RCLASS_SUPER
@@ -25,24 +29,15 @@ struct rb_subclass_entry {
struct rb_subclass_entry *prev;
};
-struct rb_iv_index_tbl_entry {
- uint32_t index;
- rb_serial_t class_serial;
- VALUE class_value;
-};
-
struct rb_cvar_class_tbl_entry {
uint32_t index;
rb_serial_t global_cvar_state;
+ const rb_cref_t * cref;
VALUE class_value;
};
struct rb_classext_struct {
- struct st_table *iv_index_tbl; // ID -> struct rb_iv_index_tbl_entry
- struct st_table *iv_tbl;
-#if SIZEOF_SERIAL_T == SIZEOF_VALUE /* otherwise m_tbl is in struct RClass */
- struct rb_id_table *m_tbl;
-#endif
+ VALUE *iv_ptr;
struct rb_id_table *const_tbl;
struct rb_id_table *callable_m_tbl;
struct rb_id_table *cc_tbl; /* ID -> [[ci, cc1], cc2, ...] */
@@ -57,63 +52,42 @@ struct rb_classext_struct {
* included. Hopefully that makes sense.
*/
struct rb_subclass_entry *module_subclass_entry;
-#if SIZEOF_SERIAL_T != SIZEOF_VALUE && !USE_RVARGC /* otherwise class_serial is in struct RClass */
- rb_serial_t class_serial;
-#endif
const VALUE origin_;
const VALUE refined_class;
rb_alloc_func_t allocator;
const VALUE includer;
+ uint32_t max_iv_count;
+ uint32_t variation_count;
+#if !SHAPE_IN_BASIC_FLAGS
+ shape_id_t shape_id;
+#endif
};
struct RClass {
struct RBasic basic;
VALUE super;
-#if !USE_RVARGC
- struct rb_classext_struct *ptr;
-#endif
-#if SIZEOF_SERIAL_T == SIZEOF_VALUE
- /* Class serial is as wide as VALUE. Place it here. */
- rb_serial_t class_serial;
-#else
- /* Class serial does not fit into struct RClass. Place m_tbl instead. */
struct rb_id_table *m_tbl;
-# if USE_RVARGC
- rb_serial_t *class_serial_ptr;
-# endif
+#if SIZE_POOL_COUNT == 1
+ struct rb_classext_struct *ptr;
#endif
};
typedef struct rb_subclass_entry rb_subclass_entry_t;
typedef struct rb_classext_struct rb_classext_t;
-#if USE_RVARGC
+#if RCLASS_EXT_EMBEDDED
# define RCLASS_EXT(c) ((rb_classext_t *)((char *)(c) + sizeof(struct RClass)))
#else
# define RCLASS_EXT(c) (RCLASS(c)->ptr)
#endif
-#define RCLASS_IV_TBL(c) (RCLASS_EXT(c)->iv_tbl)
#define RCLASS_CONST_TBL(c) (RCLASS_EXT(c)->const_tbl)
-#if SIZEOF_SERIAL_T == SIZEOF_VALUE
-# define RCLASS_M_TBL(c) (RCLASS_EXT(c)->m_tbl)
-#else
-# define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
-#endif
+#define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
+#define RCLASS_IVPTR(c) (RCLASS_EXT(c)->iv_ptr)
#define RCLASS_CALLABLE_M_TBL(c) (RCLASS_EXT(c)->callable_m_tbl)
#define RCLASS_CC_TBL(c) (RCLASS_EXT(c)->cc_tbl)
#define RCLASS_CVC_TBL(c) (RCLASS_EXT(c)->cvc_tbl)
-#define RCLASS_IV_INDEX_TBL(c) (RCLASS_EXT(c)->iv_index_tbl)
#define RCLASS_ORIGIN(c) (RCLASS_EXT(c)->origin_)
#define RCLASS_REFINED_CLASS(c) (RCLASS_EXT(c)->refined_class)
-#if SIZEOF_SERIAL_T == SIZEOF_VALUE
-# define RCLASS_SERIAL(c) (RCLASS(c)->class_serial)
-#else
-# if USE_RVARGC
-# define RCLASS_SERIAL(c) (*RCLASS(c)->class_serial_ptr)
-# else
-# define RCLASS_SERIAL(c) (RCLASS_EXT(c)->class_serial)
-# endif
-#endif
#define RCLASS_INCLUDER(c) (RCLASS_EXT(c)->includer)
#define RCLASS_SUBCLASS_ENTRY(c) (RCLASS_EXT(c)->subclass_entry)
#define RCLASS_MODULE_SUBCLASS_ENTRY(c) (RCLASS_EXT(c)->module_subclass_entry)
@@ -145,6 +119,7 @@ void rb_class_foreach_subclass(VALUE klass, void (*f)(VALUE, VALUE), VALUE);
void rb_class_detach_subclasses(VALUE);
void rb_class_detach_module_subclasses(VALUE);
void rb_class_remove_from_module_subclasses(VALUE);
+VALUE rb_define_class_id_under_no_pin(VALUE outer, ID id, VALUE super);
VALUE rb_obj_methods(int argc, const VALUE *argv, VALUE obj);
VALUE rb_obj_protected_methods(int argc, const VALUE *argv, VALUE obj);
VALUE rb_obj_private_methods(int argc, const VALUE *argv, VALUE obj);
diff --git a/internal/cmdlineopt.h b/internal/cmdlineopt.h
index 71568a8745..bf52f1214b 100644
--- a/internal/cmdlineopt.h
+++ b/internal/cmdlineopt.h
@@ -36,6 +36,9 @@ typedef struct ruby_cmdline_options {
unsigned int do_split: 1;
unsigned int do_search: 1;
unsigned int setids: 2;
+#if USE_YJIT
+ unsigned int yjit: 1;
+#endif
} ruby_cmdline_options_t;
struct ruby_opt_message {
diff --git a/internal/compar.h b/internal/compar.h
index 5e336adafa..9115e4bd63 100644
--- a/internal/compar.h
+++ b/internal/compar.h
@@ -8,38 +8,18 @@
* file COPYING are met. Consult the file for details.
* @brief Internal header for Comparable.
*/
-#include "internal/vm.h" /* for rb_method_basic_definition_p */
+#include "internal/basic_operators.h"
#define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString)
-enum {
- cmp_opt_Integer,
- cmp_opt_String,
- cmp_opt_Float,
- cmp_optimizable_count
-};
+#define CMP_OPTIMIZABLE(type) BASIC_OP_UNREDEFINED_P(BOP_CMP, type##_REDEFINED_OP_FLAG)
-struct cmp_opt_data {
- unsigned int opt_methods;
- unsigned int opt_inited;
-};
-
-#define NEW_CMP_OPT_MEMO(type, value) \
- NEW_PARTIAL_MEMO_FOR(type, value, cmp_opt)
-#define CMP_OPTIMIZABLE_BIT(type) (1U << TOKEN_PASTE(cmp_opt_,type))
-#define CMP_OPTIMIZABLE(data, type) \
- (((data).opt_inited & CMP_OPTIMIZABLE_BIT(type)) ? \
- ((data).opt_methods & CMP_OPTIMIZABLE_BIT(type)) : \
- (((data).opt_inited |= CMP_OPTIMIZABLE_BIT(type)), \
- rb_method_basic_definition_p(TOKEN_PASTE(rb_c,type), id_cmp) && \
- ((data).opt_methods |= CMP_OPTIMIZABLE_BIT(type))))
-
-#define OPTIMIZED_CMP(a, b, data) \
- ((FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(data, Integer)) ? \
+#define OPTIMIZED_CMP(a, b) \
+ ((FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(INTEGER)) ? \
(((long)a > (long)b) ? 1 : ((long)a < (long)b) ? -1 : 0) : \
- (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(data, String)) ? \
+ (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(STRING)) ? \
rb_str_cmp(a, b) : \
- (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b) && CMP_OPTIMIZABLE(data, Float)) ? \
+ (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b) && CMP_OPTIMIZABLE(FLOAT)) ? \
rb_float_cmp(a, b) : \
rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b))
diff --git a/internal/cont.h b/internal/cont.h
index abffc97104..c3b091668a 100644
--- a/internal/cont.h
+++ b/internal/cont.h
@@ -9,6 +9,7 @@
* @brief Internal header for Fiber.
*/
#include "ruby/ruby.h" /* for VALUE */
+#include "iseq.h"
struct rb_thread_struct; /* in vm_core.h */
struct rb_fiber_struct; /* in cont.c */
@@ -17,7 +18,12 @@ struct rb_execution_context_struct; /* in vm_core.c */
/* cont.c */
void rb_fiber_reset_root_local_storage(struct rb_thread_struct *);
void ruby_register_rollback_func_for_ensure(VALUE (*ensure_func)(VALUE), VALUE (*rollback_func)(VALUE));
-void rb_fiber_init_mjit_cont(struct rb_fiber_struct *fiber);
+void rb_jit_cont_init(void);
+void rb_jit_cont_each_iseq(rb_iseq_callback callback, void *data);
+void rb_jit_cont_finish(void);
+
+// Copy locals from the current execution to the specified fiber.
+VALUE rb_fiber_inherit_storage(struct rb_execution_context_struct *ec, struct rb_fiber_struct *fiber);
VALUE rb_fiberptr_self(struct rb_fiber_struct *fiber);
unsigned int rb_fiberptr_blocking(struct rb_fiber_struct *fiber);
diff --git a/internal/encoding.h b/internal/encoding.h
index c48cb24b04..a3b81bd388 100644
--- a/internal/encoding.h
+++ b/internal/encoding.h
@@ -12,6 +12,9 @@
#include "ruby/encoding.h" /* for rb_encoding */
#define rb_enc_autoload_p(enc) (!rb_enc_mbmaxlen(enc))
+#define rb_is_usascii_enc(enc) ((enc) == rb_usascii_encoding())
+#define rb_is_ascii8bit_enc(enc) ((enc) == rb_ascii8bit_encoding())
+#define rb_is_locale_enc(enc) ((enc) == rb_locale_encoding())
/* encoding.c */
ID rb_id_encoding(void);
@@ -24,7 +27,6 @@ int rb_encdb_dummy(const char *name);
void rb_encdb_declare(const char *name);
void rb_enc_set_base(const char *name, const char *orig);
int rb_enc_set_dummy(int index);
-void rb_encdb_set_unicode(int index);
PUREFUNC(int rb_data_is_encoding(VALUE obj));
#endif /* INTERNAL_ENCODING_H */
diff --git a/internal/eval.h b/internal/eval.h
index 73bb656d96..e594d8516d 100644
--- a/internal/eval.h
+++ b/internal/eval.h
@@ -21,6 +21,7 @@ extern ID ruby_static_id_status;
VALUE rb_refinement_module_get_refined_class(VALUE module);
void rb_class_modify_check(VALUE);
NORETURN(VALUE rb_f_raise(int argc, VALUE *argv));
+VALUE rb_top_main_class(const char *method);
/* eval_error.c */
VALUE rb_get_backtrace(VALUE info);
diff --git a/internal/gc.h b/internal/gc.h
index 84b7f9fa3e..e54a5dce9d 100644
--- a/internal/gc.h
+++ b/internal/gc.h
@@ -67,12 +67,16 @@ struct rb_objspace; /* in vm_core.h */
rb_obj_write((VALUE)(a), UNALIGNED_MEMBER_ACCESS((VALUE *)(slot)), \
(VALUE)(b), __FILE__, __LINE__)
-#if USE_RVARGC
+// We use SIZE_POOL_COUNT number of shape IDs for transitions out of different size pools
+// The next available shapd ID will be the SPECIAL_CONST_SHAPE_ID
+#if USE_RVARGC && (SIZEOF_UINT64_T == SIZEOF_VALUE)
# define SIZE_POOL_COUNT 5
#else
# define SIZE_POOL_COUNT 1
#endif
+#define RCLASS_EXT_EMBEDDED (SIZE_POOL_COUNT > 1)
+
typedef struct ractor_newobj_size_pool_cache {
struct RVALUE *freelist;
struct heap_page *using_page;
diff --git a/internal/hash.h b/internal/hash.h
index 657e5eff3c..64832c9610 100644
--- a/internal/hash.h
+++ b/internal/hash.h
@@ -73,6 +73,7 @@ VALUE rb_hash_default_value(VALUE hash, VALUE key);
VALUE rb_hash_set_default_proc(VALUE hash, VALUE proc);
long rb_dbl_long_hash(double d);
st_table *rb_init_identtable(void);
+st_index_t rb_any_hash(VALUE a);
VALUE rb_to_hash_type(VALUE obj);
VALUE rb_hash_key_str(VALUE);
VALUE rb_hash_values(VALUE hash);
diff --git a/internal/imemo.h b/internal/imemo.h
index 53d82eb20f..91b524e0a6 100644
--- a/internal/imemo.h
+++ b/internal/imemo.h
@@ -10,7 +10,7 @@
*/
#include "ruby/internal/config.h"
#include <stddef.h> /* for size_t */
-#include "internal/array.h" /* for rb_ary_tmp_new_fill */
+#include "internal/array.h" /* for rb_ary_hidden_new_fill */
#include "internal/gc.h" /* for RB_OBJ_WRITE */
#include "ruby/internal/stdbool.h" /* for bool */
#include "ruby/ruby.h" /* for rb_block_call_func_t */
@@ -121,9 +121,9 @@ struct MEMO {
#define MEMO_NEW(a, b, c) ((struct MEMO *)rb_imemo_new(imemo_memo, (VALUE)(a), (VALUE)(b), (VALUE)(c), 0))
#define MEMO_FOR(type, value) ((type *)RARRAY_PTR(value))
#define NEW_MEMO_FOR(type, value) \
- ((value) = rb_ary_tmp_new_fill(type_roomof(type, VALUE)), MEMO_FOR(type, value))
+ ((value) = rb_ary_hidden_new_fill(type_roomof(type, VALUE)), MEMO_FOR(type, value))
#define NEW_PARTIAL_MEMO_FOR(type, value, member) \
- ((value) = rb_ary_tmp_new_fill(type_roomof(type, VALUE)), \
+ ((value) = rb_ary_hidden_new_fill(type_roomof(type, VALUE)), \
rb_ary_set_len((value), offsetof(type, member) / sizeof(VALUE)), \
MEMO_FOR(type, value))
diff --git a/internal/numeric.h b/internal/numeric.h
index 19069cb3bc..89bc54b307 100644
--- a/internal/numeric.h
+++ b/internal/numeric.h
@@ -35,12 +35,16 @@ enum ruby_num_rounding_mode {
RUBY_NUM_ROUND_DEFAULT = ROUND_DEFAULT,
};
+/* same as internal.h */
+#define numberof(array) ((int)(sizeof(array) / sizeof((array)[0])))
+#define roomof(x, y) (((x) + (y) - 1) / (y))
+#define type_roomof(x, y) roomof(sizeof(x), sizeof(y))
+
#if SIZEOF_DOUBLE <= SIZEOF_VALUE
typedef double rb_float_value_type;
#else
typedef struct {
- VALUE values[(SIZEOF_DOUBLE + SIZEOF_VALUE - 1) / SIZEOF_VALUE];
- /* roomof() needs internal.h, and the order of some macros may matter */
+ VALUE values[roomof(SIZEOF_DOUBLE, SIZEOF_VALUE)];
} rb_float_value_type;
#endif
diff --git a/internal/object.h b/internal/object.h
index 88f3a44bc6..7b54e13dd2 100644
--- a/internal/object.h
+++ b/internal/object.h
@@ -9,11 +9,6 @@
* @brief Internal header for Object.
*/
#include "ruby/ruby.h" /* for VALUE */
-#include "internal/class.h" /* for RCLASS_IV_INDEX_TBL */
-
-#ifdef ROBJECT_IV_INDEX_TBL
-# undef ROBJECT_IV_INDEX_TBL
-#endif
/* object.c */
VALUE rb_class_search_ancestor(VALUE klass, VALUE super);
@@ -26,7 +21,6 @@ int rb_bool_expected(VALUE, const char *, int raise);
static inline void RBASIC_CLEAR_CLASS(VALUE obj);
static inline void RBASIC_SET_CLASS_RAW(VALUE obj, VALUE klass);
static inline void RBASIC_SET_CLASS(VALUE obj, VALUE klass);
-static inline struct st_table *ROBJECT_IV_INDEX_TBL_inline(VALUE obj);
RUBY_SYMBOL_EXPORT_BEGIN
/* object.c (export) */
@@ -64,20 +58,4 @@ RBASIC_SET_CLASS(VALUE obj, VALUE klass)
RBASIC_SET_CLASS_RAW(obj, klass);
RB_OBJ_WRITTEN(obj, oldv, klass);
}
-
-RBIMPL_ATTR_PURE()
-static inline struct st_table *
-ROBJECT_IV_INDEX_TBL_inline(VALUE obj)
-{
- if (RB_FL_ANY_RAW(obj, ROBJECT_EMBED)) {
- VALUE klass = rb_obj_class(obj);
- return RCLASS_IV_INDEX_TBL(klass);
- }
- else {
- const struct RObject *const ptr = ROBJECT(obj);
- return ptr->as.heap.iv_index_tbl;
- }
-}
-#define ROBJECT_IV_INDEX_TBL ROBJECT_IV_INDEX_TBL_inline
-
#endif /* INTERNAL_OBJECT_H */
diff --git a/internal/parse.h b/internal/parse.h
index d9f5b56bc5..f242c384ad 100644
--- a/internal/parse.h
+++ b/internal/parse.h
@@ -15,6 +15,8 @@ struct rb_iseq_struct; /* in vm_core.h */
VALUE rb_parser_set_yydebug(VALUE, VALUE);
void *rb_parser_load_file(VALUE parser, VALUE name);
void rb_parser_keep_script_lines(VALUE vparser);
+void rb_parser_error_tolerant(VALUE vparser);
+void rb_parser_keep_tokens(VALUE vparser);
RUBY_SYMBOL_EXPORT_BEGIN
VALUE rb_parser_set_context(VALUE, const struct rb_iseq_struct *, int);
diff --git a/internal/string.h b/internal/string.h
index 8fb9553d03..12edbff2b1 100644
--- a/internal/string.h
+++ b/internal/string.h
@@ -43,6 +43,8 @@ char *rb_str_to_cstr(VALUE str);
const char *ruby_escaped_char(int c);
void rb_str_make_independent(VALUE str);
int rb_enc_str_coderange_scan(VALUE str, rb_encoding *enc);
+int rb_ascii8bit_appendable_encoding_index(rb_encoding *enc, unsigned int code);
+VALUE rb_str_include(VALUE str, VALUE arg);
static inline bool STR_EMBED_P(VALUE str);
static inline bool STR_SHARED_P(VALUE str);
@@ -104,7 +106,7 @@ STR_EMBED_P(VALUE str)
static inline bool
STR_SHARED_P(VALUE str)
{
- return FL_ALL_RAW(str, STR_NOEMBED | ELTS_SHARED);
+ return FL_ALL_RAW(str, STR_NOEMBED | STR_SHARED);
}
static inline bool
diff --git a/internal/thread.h b/internal/thread.h
index 63f03bb94a..c3e54de683 100644
--- a/internal/thread.h
+++ b/internal/thread.h
@@ -20,6 +20,7 @@ struct rb_thread_struct; /* in vm_core.h */
#define COVERAGE_TARGET_BRANCHES 2
#define COVERAGE_TARGET_METHODS 4
#define COVERAGE_TARGET_ONESHOT_LINES 8
+#define COVERAGE_TARGET_EVAL 16
VALUE rb_obj_is_mutex(VALUE obj);
VALUE rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg);
@@ -28,6 +29,7 @@ VALUE rb_get_coverages(void);
int rb_get_coverage_mode(void);
VALUE rb_default_coverage(int);
VALUE rb_thread_shield_new(void);
+bool rb_thread_shield_owned(VALUE self);
VALUE rb_thread_shield_wait(VALUE self);
VALUE rb_thread_shield_release(VALUE self);
VALUE rb_thread_shield_destroy(VALUE self);
diff --git a/internal/time.h b/internal/time.h
index a3bf0587ec..e21b3574f6 100644
--- a/internal/time.h
+++ b/internal/time.h
@@ -28,7 +28,10 @@ struct timeval rb_time_timeval(VALUE);
RUBY_SYMBOL_EXPORT_BEGIN
/* time.c (export) */
void ruby_reset_leap_second_info(void);
-void ruby_reset_timezone(void);
+#ifdef RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY
+RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY()
+#endif
+void ruby_reset_timezone(const char *);
RUBY_SYMBOL_EXPORT_END
#endif /* INTERNAL_TIME_H */
diff --git a/internal/variable.h b/internal/variable.h
index 4b67bef907..6dec6a6759 100644
--- a/internal/variable.h
+++ b/internal/variable.h
@@ -13,10 +13,11 @@
#include "constant.h" /* for rb_const_entry_t */
#include "ruby/internal/stdbool.h" /* for bool */
#include "ruby/ruby.h" /* for VALUE */
+#include "shape.h" /* for rb_shape_t */
/* global variable */
-#define ROBJECT_TRANSIENT_FLAG FL_USER13
+#define ROBJECT_TRANSIENT_FLAG FL_USER2
/* variable.c */
void rb_gc_mark_global_tbl(void);
@@ -24,7 +25,6 @@ void rb_gc_update_global_tbl(void);
size_t rb_generic_ivar_memsize(VALUE);
VALUE rb_search_class_path(VALUE);
VALUE rb_attr_delete(VALUE, ID);
-VALUE rb_ivar_lookup(VALUE obj, ID id, VALUE undef);
void rb_autoload_str(VALUE mod, ID id, VALUE file);
VALUE rb_autoload_at_p(VALUE, ID, int);
NORETURN(VALUE rb_mod_const_missing(VALUE,VALUE));
@@ -35,7 +35,10 @@ void rb_gvar_ractor_local(const char *name);
static inline bool ROBJ_TRANSIENT_P(VALUE obj);
static inline void ROBJ_TRANSIENT_SET(VALUE obj);
static inline void ROBJ_TRANSIENT_UNSET(VALUE obj);
-uint32_t rb_obj_ensure_iv_index_mapping(VALUE obj, ID id);
+
+struct gen_ivtbl;
+int rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl);
+int rb_obj_evacuate_ivs_to_hash_table(ID key, VALUE val, st_data_t arg);
RUBY_SYMBOL_EXPORT_BEGIN
/* variable.c (export) */
@@ -47,11 +50,15 @@ void rb_iv_tbl_copy(VALUE dst, VALUE src);
RUBY_SYMBOL_EXPORT_END
MJIT_SYMBOL_EXPORT_BEGIN
+VALUE rb_ivar_lookup(VALUE obj, ID id, VALUE undef);
VALUE rb_gvar_get(ID);
VALUE rb_gvar_set(ID, VALUE);
VALUE rb_gvar_defined(ID);
void rb_const_warn_if_deprecated(const rb_const_entry_t *, VALUE, ID);
-void rb_init_iv_list(VALUE obj);
+rb_shape_t * rb_grow_iv_list(VALUE obj);
+void rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize);
+struct gen_ivtbl *rb_ensure_generic_iv_list_size(VALUE obj, rb_shape_t *shape, uint32_t newsize);
+attr_index_t rb_obj_ivar_set(VALUE obj, ID id, VALUE val);
MJIT_SYMBOL_EXPORT_END
static inline bool
diff --git a/internal/vm.h b/internal/vm.h
index c6c6b2ccc2..cf245c6579 100644
--- a/internal/vm.h
+++ b/internal/vm.h
@@ -40,7 +40,7 @@ enum method_missing_reason {
};
/* vm_insnhelper.h */
-rb_serial_t rb_next_class_serial(void);
+VALUE rb_vm_push_frame_fname(struct rb_execution_context_struct *ec, VALUE fname);
/* vm.c */
VALUE rb_obj_is_thread(VALUE obj);
diff --git a/io.c b/io.c
index 650ea5e377..99ec59da29 100644
--- a/io.c
+++ b/io.c
@@ -75,10 +75,6 @@
#include <sys/fcntl.h>
#endif
-#if !HAVE_OFF_T && !defined(off_t)
-# define off_t long
-#endif
-
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
@@ -149,10 +145,6 @@
#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
#endif
-#if SIZEOF_OFF_T > SIZEOF_LONG && !defined(HAVE_LONG_LONG)
-# error off_t is bigger than long, but you have no long long...
-#endif
-
#ifndef PIPE_BUF
# ifdef _POSIX_PIPE_BUF
# define PIPE_BUF _POSIX_PIPE_BUF
@@ -175,6 +167,8 @@ off_t __syscall(quad_t number, ...);
#define IO_RBUF_CAPA_FOR(fptr) (NEED_READCONV(fptr) ? IO_CBUF_CAPA_MIN : IO_RBUF_CAPA_MIN)
#define IO_WBUF_CAPA_MIN 8192
+#define IO_MAX_BUFFER_GROWTH 8 * 1024 * 1024 // 8MB
+
/* define system APIs */
#ifdef _WIN32
#undef open
@@ -186,6 +180,7 @@ off_t __syscall(quad_t number, ...);
VALUE rb_cIO;
VALUE rb_eEOFError;
VALUE rb_eIOError;
+VALUE rb_eIOTimeoutError;
VALUE rb_mWaitReadable;
VALUE rb_mWaitWritable;
@@ -254,7 +249,7 @@ rb_update_max_fd(int fd)
}
while (max_fd < afd) {
- max_fd = ATOMIC_CAS(max_file_descriptor, max_fd, afd);
+ max_fd = ATOMIC_CAS(max_file_descriptor, max_fd, afd);
}
}
@@ -299,7 +294,7 @@ rb_fix_detect_o_cloexec(int fd)
rb_bug("rb_fix_detect_o_cloexec: fcntl(%d, F_GETFD) failed: %s", fd, strerror(errno));
if (flags & FD_CLOEXEC)
- return 1;
+ return 1;
#endif /* fall through if O_CLOEXEC does not work: */
rb_maygvl_fd_fix_cloexec(fd);
return 0;
@@ -339,13 +334,13 @@ rb_cloexec_open(const char *pathname, int flags, mode_t mode)
if (ret < 0) return ret;
if (ret <= 2 || o_cloexec_state == 0) {
- rb_maygvl_fd_fix_cloexec(ret);
+ rb_maygvl_fd_fix_cloexec(ret);
}
else if (o_cloexec_state > 0) {
- return ret;
+ return ret;
}
else {
- o_cloexec_state = rb_fix_detect_o_cloexec(ret);
+ o_cloexec_state = rb_fix_detect_o_cloexec(ret);
}
return ret;
}
@@ -501,7 +496,7 @@ rb_cloexec_fcntl_dupfd(int fd, int minfd)
#if defined(_WIN32)
#define WAIT_FD_IN_WIN32(fptr) \
- (rb_w32_io_cancelable_p((fptr)->fd) ? Qnil : rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil))
+ (rb_w32_io_cancelable_p((fptr)->fd) ? Qnil : rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), RUBY_IO_TIMEOUT_DEFAULT))
#else
#define WAIT_FD_IN_WIN32(fptr)
#endif
@@ -598,19 +593,19 @@ raise_on_write(rb_io_t *fptr, int e, VALUE errinfo)
#define NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr) do {\
if (NEED_NEWLINE_DECORATOR_ON_READ(fptr)) {\
- if (((fptr)->mode & FMODE_READABLE) &&\
- !((fptr)->encs.ecflags & ECONV_NEWLINE_DECORATOR_MASK)) {\
- setmode((fptr)->fd, O_BINARY);\
- }\
- else {\
- setmode((fptr)->fd, O_TEXT);\
- }\
+ if (((fptr)->mode & FMODE_READABLE) &&\
+ !((fptr)->encs.ecflags & ECONV_NEWLINE_DECORATOR_MASK)) {\
+ setmode((fptr)->fd, O_BINARY);\
+ }\
+ else {\
+ setmode((fptr)->fd, O_TEXT);\
+ }\
}\
} while(0)
#define SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags) do {\
if ((enc2) && ((ecflags) & ECONV_DEFAULT_NEWLINE_DECORATOR)) {\
- (ecflags) |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;\
+ (ecflags) |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;\
}\
} while(0)
@@ -620,7 +615,7 @@ raise_on_write(rb_io_t *fptr, int e, VALUE errinfo)
static void
io_unread(rb_io_t *fptr)
{
- off_t r, pos;
+ rb_off_t r, pos;
ssize_t read_size;
long i;
long newlines = 0;
@@ -630,28 +625,28 @@ io_unread(rb_io_t *fptr)
rb_io_check_closed(fptr);
if (fptr->rbuf.len == 0 || fptr->mode & FMODE_DUPLEX) {
- return;
+ return;
}
errno = 0;
if (!rb_w32_fd_is_text(fptr->fd)) {
- r = lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
- if (r < 0 && errno) {
- if (errno == ESPIPE)
- fptr->mode |= FMODE_DUPLEX;
- return;
- }
+ r = lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
+ if (r < 0 && errno) {
+ if (errno == ESPIPE)
+ fptr->mode |= FMODE_DUPLEX;
+ return;
+ }
- fptr->rbuf.off = 0;
- fptr->rbuf.len = 0;
- return;
+ fptr->rbuf.off = 0;
+ fptr->rbuf.len = 0;
+ return;
}
pos = lseek(fptr->fd, 0, SEEK_CUR);
if (pos < 0 && errno) {
- if (errno == ESPIPE)
- fptr->mode |= FMODE_DUPLEX;
- return;
+ if (errno == ESPIPE)
+ fptr->mode |= FMODE_DUPLEX;
+ return;
}
/* add extra offset for removed '\r' in rbuf */
@@ -660,36 +655,36 @@ io_unread(rb_io_t *fptr)
/* if the end of rbuf is '\r', rbuf doesn't have '\r' within rbuf.len */
if (*(fptr->rbuf.ptr + fptr->rbuf.capa - 1) == '\r') {
- newlines++;
+ newlines++;
}
for (i = 0; i < fptr->rbuf.len; i++) {
- if (*p == '\n') newlines++;
- if (extra_max == newlines) break;
- p++;
+ if (*p == '\n') newlines++;
+ if (extra_max == newlines) break;
+ p++;
}
buf = ALLOC_N(char, fptr->rbuf.len + newlines);
while (newlines >= 0) {
- r = lseek(fptr->fd, pos - fptr->rbuf.len - newlines, SEEK_SET);
- if (newlines == 0) break;
- if (r < 0) {
- newlines--;
- continue;
- }
- read_size = _read(fptr->fd, buf, fptr->rbuf.len + newlines);
- if (read_size < 0) {
- int e = errno;
- free(buf);
- rb_syserr_fail_path(e, fptr->pathv);
- }
- if (read_size == fptr->rbuf.len) {
- lseek(fptr->fd, r, SEEK_SET);
- break;
- }
- else {
- newlines--;
- }
+ r = lseek(fptr->fd, pos - fptr->rbuf.len - newlines, SEEK_SET);
+ if (newlines == 0) break;
+ if (r < 0) {
+ newlines--;
+ continue;
+ }
+ read_size = _read(fptr->fd, buf, fptr->rbuf.len + newlines);
+ if (read_size < 0) {
+ int e = errno;
+ free(buf);
+ rb_syserr_fail_path(e, fptr->pathv);
+ }
+ if (read_size == fptr->rbuf.len) {
+ lseek(fptr->fd, r, SEEK_SET);
+ break;
+ }
+ else {
+ newlines--;
+ }
}
free(buf);
fptr->rbuf.off = 0;
@@ -710,7 +705,7 @@ set_binary_mode_with_seek_cur(rb_io_t *fptr)
if (!rb_w32_fd_is_text(fptr->fd)) return O_BINARY;
if (fptr->rbuf.len == 0 || fptr->mode & FMODE_DUPLEX) {
- return setmode(fptr->fd, O_BINARY);
+ return setmode(fptr->fd, O_BINARY);
}
flush_before_seek(fptr);
return setmode(fptr->fd, O_BINARY);
@@ -779,7 +774,7 @@ void
rb_io_check_initialized(rb_io_t *fptr)
{
if (!fptr) {
- rb_raise(rb_eIOError, "uninitialized stream");
+ rb_raise(rb_eIOError, "uninitialized stream");
}
}
@@ -827,10 +822,10 @@ rb_io_set_write_io(VALUE io, VALUE w)
VALUE write_io;
rb_io_t *fptr = rb_io_get_fptr(io);
if (!RTEST(w)) {
- w = 0;
+ w = 0;
}
else {
- GetWriteIO(w);
+ GetWriteIO(w);
}
write_io = fptr->tied_io_for_writing;
fptr->tied_io_for_writing = w;
@@ -839,6 +834,54 @@ rb_io_set_write_io(VALUE io, VALUE w)
/*
* call-seq:
+ * timeout -> duration or nil
+ *
+ * Get the internal timeout duration or nil if it was not set.
+ *
+ */
+VALUE
+rb_io_timeout(VALUE self)
+{
+ rb_io_t *fptr = rb_io_get_fptr(self);
+
+ return fptr->timeout;
+}
+
+/*
+ * call-seq:
+ * timeout = duration -> duration
+ * timeout = nil -> nil
+ *
+ * Set the internal timeout to the specified duration or nil. The timeout
+ * applies to all blocking operations where possible.
+ *
+ * This affects the following methods (but is not limited to): #gets, #puts,
+ * #read, #write, #wait_readable and #wait_writable. This also affects
+ * blocking socket operations like Socket#accept and Socket#connect.
+ *
+ * Some operations like File#open and IO#close are not affected by the
+ * timeout. A timeout during a write operation may leave the IO in an
+ * inconsistent state, e.g. data was partially written. Generally speaking, a
+ * timeout is a last ditch effort to prevent an application from hanging on
+ * slow I/O operations, such as those that occur during a slowloris attack.
+ */
+VALUE
+rb_io_set_timeout(VALUE self, VALUE timeout)
+{
+ // Validate it:
+ if (RTEST(timeout)) {
+ rb_time_interval(timeout);
+ }
+
+ rb_io_t *fptr = rb_io_get_fptr(self);
+
+ fptr->timeout = timeout;
+
+ return self;
+}
+
+/*
+ * call-seq:
* IO.try_convert(object) -> new_io or nil
*
* Attempts to convert +object+ into an \IO object via method +to_io+;
@@ -859,7 +902,7 @@ rb_io_s_try_convert(VALUE dummy, VALUE io)
static void
io_unread(rb_io_t *fptr)
{
- off_t r;
+ rb_off_t r;
rb_io_check_closed(fptr);
if (fptr->rbuf.len == 0 || fptr->mode & FMODE_DUPLEX)
return;
@@ -889,17 +932,17 @@ io_ungetbyte(VALUE str, rb_io_t *fptr)
fptr->rbuf.off = 0;
fptr->rbuf.len = 0;
#if SIZEOF_LONG > SIZEOF_INT
- if (len > INT_MAX)
- rb_raise(rb_eIOError, "ungetbyte failed");
+ if (len > INT_MAX)
+ rb_raise(rb_eIOError, "ungetbyte failed");
#endif
- if (len > min_capa)
- fptr->rbuf.capa = (int)len;
- else
- fptr->rbuf.capa = min_capa;
+ if (len > min_capa)
+ fptr->rbuf.capa = (int)len;
+ else
+ fptr->rbuf.capa = min_capa;
fptr->rbuf.ptr = ALLOC_N(char, fptr->rbuf.capa);
}
if (fptr->rbuf.capa < len + fptr->rbuf.len) {
- rb_raise(rb_eIOError, "ungetbyte failed");
+ rb_raise(rb_eIOError, "ungetbyte failed");
}
if (fptr->rbuf.off < len) {
MEMMOVE(fptr->rbuf.ptr+fptr->rbuf.capa-fptr->rbuf.len,
@@ -936,15 +979,15 @@ rb_io_check_char_readable(rb_io_t *fptr)
{
rb_io_check_closed(fptr);
if (!(fptr->mode & FMODE_READABLE)) {
- rb_raise(rb_eIOError, "not opened for reading");
+ rb_raise(rb_eIOError, "not opened for reading");
}
if (fptr->wbuf.len) {
if (io_fflush(fptr) < 0)
rb_sys_fail_on_write(fptr);
}
if (fptr->tied_io_for_writing) {
- rb_io_t *wfptr;
- GetOpenFile(fptr->tied_io_for_writing, wfptr);
+ rb_io_t *wfptr;
+ GetOpenFile(fptr->tied_io_for_writing, wfptr);
if (io_fflush(wfptr) < 0)
rb_sys_fail_on_write(wfptr);
}
@@ -955,7 +998,7 @@ rb_io_check_byte_readable(rb_io_t *fptr)
{
rb_io_check_char_readable(fptr);
if (READ_CHAR_PENDING(fptr)) {
- rb_raise(rb_eIOError, "byte oriented read for character buffered IO");
+ rb_raise(rb_eIOError, "byte oriented read for character buffered IO");
}
}
@@ -969,7 +1012,7 @@ static rb_encoding*
io_read_encoding(rb_io_t *fptr)
{
if (fptr->encs.enc) {
- return fptr->encs.enc;
+ return fptr->encs.enc;
}
return rb_default_external_encoding();
}
@@ -978,7 +1021,7 @@ static rb_encoding*
io_input_encoding(rb_io_t *fptr)
{
if (fptr->encs.enc2) {
- return fptr->encs.enc2;
+ return fptr->encs.enc2;
}
return io_read_encoding(fptr);
}
@@ -988,7 +1031,7 @@ rb_io_check_writable(rb_io_t *fptr)
{
rb_io_check_closed(fptr);
if (!(fptr->mode & FMODE_WRITABLE)) {
- rb_raise(rb_eIOError, "not opened for writing");
+ rb_raise(rb_eIOError, "not opened for writing");
}
if (fptr->rbuf.len) {
io_unread(fptr);
@@ -1008,7 +1051,7 @@ void
rb_io_read_check(rb_io_t *fptr)
{
if (!READ_DATA_PENDING(fptr)) {
- rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil);
+ rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), RUBY_IO_TIMEOUT_DEFAULT);
}
return;
}
@@ -1017,8 +1060,8 @@ int
rb_gc_for_fd(int err)
{
if (err == EMFILE || err == ENFILE || err == ENOMEM) {
- rb_gc();
- return 1;
+ rb_gc();
+ return 1;
}
return 0;
}
@@ -1030,13 +1073,13 @@ ruby_dup(int orig)
fd = rb_cloexec_dup(orig);
if (fd < 0) {
- int e = errno;
- if (rb_gc_for_fd(e)) {
- fd = rb_cloexec_dup(orig);
- }
- if (fd < 0) {
- rb_syserr_fail(e, 0);
- }
+ int e = errno;
+ if (rb_gc_for_fd(e)) {
+ fd = rb_cloexec_dup(orig);
+ }
+ if (fd < 0) {
+ rb_syserr_fail(e, 0);
+ }
}
rb_update_max_fd(fd);
return fd;
@@ -1060,56 +1103,124 @@ struct io_internal_read_struct {
VALUE th;
rb_io_t *fptr;
int nonblock;
+ int fd;
+
void *buf;
size_t capa;
+ struct timeval *timeout;
};
struct io_internal_write_struct {
+ VALUE th;
+ rb_io_t *fptr;
+ int nonblock;
int fd;
+
const void *buf;
size_t capa;
+ struct timeval *timeout;
};
#ifdef HAVE_WRITEV
struct io_internal_writev_struct {
+ VALUE th;
+ rb_io_t *fptr;
+ int nonblock;
int fd;
+
int iovcnt;
const struct iovec *iov;
+ struct timeval *timeout;
};
#endif
-static int nogvl_wait_for(VALUE th, rb_io_t *fptr, short events);
+static int nogvl_wait_for(VALUE th, rb_io_t *fptr, short events, struct timeval *timeout);
+
+/**
+ * Wait for the given events on the given file descriptor.
+ * Returns -1 if an error or timeout occurred. +errno+ will be set.
+ * Returns the event mask if an event occurred.
+ */
+static inline int
+io_internal_wait(VALUE thread, rb_io_t *fptr, int error, int events, struct timeval *timeout)
+{
+ int ready = nogvl_wait_for(thread, fptr, events, timeout);
+
+ if (ready > 0) {
+ return ready;
+ }
+ else if (ready == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ errno = error;
+ return -1;
+}
+
static VALUE
internal_read_func(void *ptr)
{
struct io_internal_read_struct *iis = ptr;
- ssize_t r;
-retry:
- r = read(iis->fptr->fd, iis->buf, iis->capa);
- if (r < 0 && !iis->nonblock) {
- int e = errno;
- if (io_again_p(e)) {
- if (nogvl_wait_for(iis->th, iis->fptr, RB_WAITFD_IN) != -1) {
+ ssize_t result;
+
+ if (iis->timeout && !iis->nonblock) {
+ if (io_internal_wait(iis->th, iis->fptr, 0, RB_WAITFD_IN, iis->timeout) == -1) {
+ return -1;
+ }
+ }
+
+ retry:
+ result = read(iis->fd, iis->buf, iis->capa);
+
+ if (result < 0 && !iis->nonblock) {
+ if (io_again_p(errno)) {
+ if (io_internal_wait(iis->th, iis->fptr, errno, RB_WAITFD_IN, iis->timeout) == -1) {
+ return -1;
+ }
+ else {
goto retry;
}
- errno = e;
}
}
- return r;
+
+ return result;
}
#if defined __APPLE__
-# define do_write_retry(code) do {ret = code;} while (ret == -1 && errno == EPROTOTYPE)
+# define do_write_retry(code) do {result = code;} while (result == -1 && errno == EPROTOTYPE)
#else
-# define do_write_retry(code) ret = code
+# define do_write_retry(code) result = code
#endif
+
static VALUE
internal_write_func(void *ptr)
{
struct io_internal_write_struct *iis = ptr;
- ssize_t ret;
+ ssize_t result;
+
+ if (iis->timeout && !iis->nonblock) {
+ if (io_internal_wait(iis->th, iis->fptr, 0, RB_WAITFD_OUT, iis->timeout) == -1) {
+ return -1;
+ }
+ }
+
+ retry:
do_write_retry(write(iis->fd, iis->buf, iis->capa));
- return (VALUE)ret;
+
+ if (result < 0 && !iis->nonblock) {
+ int e = errno;
+ if (io_again_p(e)) {
+ if (io_internal_wait(iis->th, iis->fptr, errno, RB_WAITFD_OUT, iis->timeout) == -1) {
+ return -1;
+ }
+ else {
+ goto retry;
+ }
+ }
+ }
+
+ return result;
}
#ifdef HAVE_WRITEV
@@ -1117,20 +1228,40 @@ static VALUE
internal_writev_func(void *ptr)
{
struct io_internal_writev_struct *iis = ptr;
- ssize_t ret;
+ ssize_t result;
+
+ if (iis->timeout && !iis->nonblock) {
+ if (io_internal_wait(iis->th, iis->fptr, 0, RB_WAITFD_OUT, iis->timeout) == -1) {
+ return -1;
+ }
+ }
+
+ retry:
do_write_retry(writev(iis->fd, iis->iov, iis->iovcnt));
- return (VALUE)ret;
+
+ if (result < 0 && !iis->nonblock) {
+ if (io_again_p(errno)) {
+ if (io_internal_wait(iis->th, iis->fptr, errno, RB_WAITFD_OUT, iis->timeout) == -1) {
+ return -1;
+ }
+ else {
+ goto retry;
+ }
+ }
+ }
+
+ return result;
}
#endif
static ssize_t
-rb_read_internal(rb_io_t *fptr, void *buf, size_t count)
+rb_io_read_memory(rb_io_t *fptr, void *buf, size_t count)
{
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
VALUE result = rb_fiber_scheduler_io_read_memory(scheduler, fptr->self, buf, count, 0);
- if (result != Qundef) {
+ if (!UNDEF_P(result)) {
return rb_fiber_scheduler_io_result_apply(result);
}
}
@@ -1139,31 +1270,53 @@ rb_read_internal(rb_io_t *fptr, void *buf, size_t count)
.th = rb_thread_current(),
.fptr = fptr,
.nonblock = 0,
+ .fd = fptr->fd,
+
.buf = buf,
- .capa = count
+ .capa = count,
+ .timeout = NULL,
};
+ struct timeval timeout_storage;
+
+ if (fptr->timeout != Qnil) {
+ timeout_storage = rb_time_interval(fptr->timeout);
+ iis.timeout = &timeout_storage;
+ }
+
return (ssize_t)rb_thread_io_blocking_region(internal_read_func, &iis, fptr->fd);
}
static ssize_t
-rb_write_internal(rb_io_t *fptr, const void *buf, size_t count)
+rb_io_write_memory(rb_io_t *fptr, const void *buf, size_t count)
{
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
VALUE result = rb_fiber_scheduler_io_write_memory(scheduler, fptr->self, buf, count, 0);
- if (result != Qundef) {
+ if (!UNDEF_P(result)) {
return rb_fiber_scheduler_io_result_apply(result);
}
}
struct io_internal_write_struct iis = {
+ .th = rb_thread_current(),
+ .fptr = fptr,
+ .nonblock = 0,
.fd = fptr->fd,
+
.buf = buf,
- .capa = count
+ .capa = count,
+ .timeout = NULL
};
+ struct timeval timeout_storage;
+
+ if (fptr->timeout != Qnil) {
+ timeout_storage = rb_time_interval(fptr->timeout);
+ iis.timeout = &timeout_storage;
+ }
+
return (ssize_t)rb_thread_io_blocking_region(internal_write_func, &iis, fptr->fd);
}
@@ -1171,23 +1324,36 @@ rb_write_internal(rb_io_t *fptr, const void *buf, size_t count)
static ssize_t
rb_writev_internal(rb_io_t *fptr, const struct iovec *iov, int iovcnt)
{
+ if (!iovcnt) return 0;
+
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
- for (int i = 0; i < iovcnt; i += 1) {
- VALUE result = rb_fiber_scheduler_io_write_memory(scheduler, fptr->self, iov[i].iov_base, iov[i].iov_len, 0);
+ // This path assumes at least one `iov`:
+ VALUE result = rb_fiber_scheduler_io_write_memory(scheduler, fptr->self, iov[0].iov_base, iov[0].iov_len, 0);
- if (result != Qundef) {
- return rb_fiber_scheduler_io_result_apply(result);
- }
+ if (!UNDEF_P(result)) {
+ return rb_fiber_scheduler_io_result_apply(result);
}
}
struct io_internal_writev_struct iis = {
+ .th = rb_thread_current(),
+ .fptr = fptr,
+ .nonblock = 0,
.fd = fptr->fd,
+
.iov = iov,
.iovcnt = iovcnt,
+ .timeout = NULL
};
+ struct timeval timeout_storage;
+
+ if (fptr->timeout != Qnil) {
+ timeout_storage = rb_time_interval(fptr->timeout);
+ iis.timeout = &timeout_storage;
+ }
+
return (ssize_t)rb_thread_io_blocking_region(internal_writev_func, &iis, fptr->fd);
}
#endif
@@ -1200,15 +1366,17 @@ io_flush_buffer_sync(void *arg)
ssize_t r = write(fptr->fd, fptr->wbuf.ptr+fptr->wbuf.off, (size_t)l);
if (fptr->wbuf.len <= r) {
- fptr->wbuf.off = 0;
- fptr->wbuf.len = 0;
- return 0;
+ fptr->wbuf.off = 0;
+ fptr->wbuf.len = 0;
+ return 0;
}
+
if (0 <= r) {
- fptr->wbuf.off += (int)r;
- fptr->wbuf.len -= (int)r;
- errno = EAGAIN;
+ fptr->wbuf.off += (int)r;
+ fptr->wbuf.len -= (int)r;
+ errno = EAGAIN;
}
+
return (VALUE)-1;
}
@@ -1224,7 +1392,8 @@ io_flush_buffer(rb_io_t *fptr)
{
if (!NIL_P(fptr->write_lock) && rb_mutex_owned_p(fptr->write_lock)) {
return (int)io_flush_buffer_async((VALUE)fptr);
- } else {
+ }
+ else {
return (int)rb_mutex_synchronize(fptr->write_lock, io_flush_buffer_async, (VALUE)fptr);
}
}
@@ -1238,7 +1407,7 @@ io_fflush(rb_io_t *fptr)
return 0;
while (fptr->wbuf.len > 0 && io_flush_buffer(fptr) != 0) {
- if (!rb_io_maybe_wait_writable(errno, fptr->self, Qnil))
+ if (!rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT))
return -1;
rb_io_check_closed(fptr);
@@ -1262,6 +1431,10 @@ rb_io_wait(VALUE io, VALUE events, VALUE timeout)
struct timeval tv_storage;
struct timeval *tv = NULL;
+ if (NIL_OR_UNDEF_P(timeout)) {
+ timeout = fptr->timeout;
+ }
+
if (timeout != Qnil) {
tv_storage = rb_time_interval(timeout);
tv = &tv_storage;
@@ -1480,7 +1653,7 @@ make_writeconv(rb_io_t *fptr)
ecflags = fptr->encs.ecflags & ~ECONV_NEWLINE_DECORATOR_READ_MASK;
ecopts = fptr->encs.ecopts;
- if (!fptr->encs.enc || (fptr->encs.enc == rb_ascii8bit_encoding() && !fptr->encs.enc2)) {
+ if (!fptr->encs.enc || (rb_is_ascii8bit_enc(fptr->encs.enc) && !fptr->encs.enc2)) {
/* no encoding conversion */
fptr->writeconv_pre_ecflags = 0;
fptr->writeconv_pre_ecopts = Qnil;
@@ -1569,7 +1742,7 @@ io_binwrite_string_internal(rb_io_t *fptr, const char *ptr, long length)
return result;
}
else {
- return rb_write_internal(fptr, ptr, length);
+ return rb_io_write_memory(fptr, ptr, length);
}
}
#else
@@ -1604,7 +1777,7 @@ io_binwrite_string_internal(rb_io_t *fptr, const char *ptr, long length)
}
// Otherwise, we should write the data directly:
- return rb_write_internal(fptr, ptr, length);
+ return rb_io_write_memory(fptr, ptr, length);
}
#endif
@@ -1620,19 +1793,17 @@ io_binwrite_string(VALUE arg)
// Write as much as possible:
ssize_t result = io_binwrite_string_internal(p->fptr, ptr, remaining);
- // If only the internal buffer is written, result will be zero [bytes of given data written]. This means we
- // should try again.
if (result == 0) {
- errno = EWOULDBLOCK;
+ // If only the internal buffer is written, result will be zero [bytes of given data written]. This means we
+ // should try again immediately.
}
-
- if (result > 0) {
+ else if (result > 0) {
if ((size_t)result == remaining) break;
ptr += result;
remaining -= result;
}
// Wait for it to become writable:
- else if (rb_io_maybe_wait_writable(errno, p->fptr->self, Qnil)) {
+ else if (rb_io_maybe_wait_writable(errno, p->fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
rb_io_check_closed(p->fptr);
}
else {
@@ -1699,7 +1870,8 @@ io_binwrite(VALUE str, const char *ptr, long len, rb_io_t *fptr, int nosync)
else {
return io_binwrite_string((VALUE)&arg);
}
- } else {
+ }
+ else {
if (fptr->wbuf.off) {
if (fptr->wbuf.len)
MEMMOVE(fptr->wbuf.ptr, fptr->wbuf.ptr+fptr->wbuf.off, char, fptr->wbuf.len);
@@ -1725,7 +1897,7 @@ do_writeconv(VALUE str, rb_io_t *fptr, int *converted)
{
if (NEED_WRITECONV(fptr)) {
VALUE common_encoding = Qnil;
- SET_BINARY_MODE(fptr);
+ SET_BINARY_MODE(fptr);
make_writeconv(fptr);
@@ -1749,27 +1921,27 @@ do_writeconv(VALUE str, rb_io_t *fptr, int *converted)
if (!NIL_P(common_encoding)) {
str = rb_str_encode(str, common_encoding,
fptr->writeconv_pre_ecflags, fptr->writeconv_pre_ecopts);
- *converted = 1;
+ *converted = 1;
}
if (fptr->writeconv) {
str = rb_econv_str_convert(fptr->writeconv, str, ECONV_PARTIAL_INPUT);
- *converted = 1;
+ *converted = 1;
}
}
#if RUBY_CRLF_ENVIRONMENT
#define fmode (fptr->mode)
else if (MODE_BTMODE(DEFAULT_TEXTMODE,0,1)) {
- if ((fptr->mode & FMODE_READABLE) &&
- !(fptr->encs.ecflags & ECONV_NEWLINE_DECORATOR_MASK)) {
- setmode(fptr->fd, O_BINARY);
- }
- else {
- setmode(fptr->fd, O_TEXT);
- }
- if (!rb_enc_asciicompat(rb_enc_get(str))) {
- rb_raise(rb_eArgError, "ASCII incompatible string written for text mode IO without encoding conversion: %s",
- rb_enc_name(rb_enc_get(str)));
+ if ((fptr->mode & FMODE_READABLE) &&
+ !(fptr->encs.ecflags & ECONV_NEWLINE_DECORATOR_MASK)) {
+ setmode(fptr->fd, O_BINARY);
+ }
+ else {
+ setmode(fptr->fd, O_TEXT);
+ }
+ if (!rb_enc_asciicompat(rb_enc_get(str))) {
+ rb_raise(rb_eArgError, "ASCII incompatible string written for text mode IO without encoding conversion: %s",
+ rb_enc_name(rb_enc_get(str)));
}
}
#undef fmode
@@ -1865,7 +2037,7 @@ io_binwritev_internal(VALUE arg)
while (remaining) {
long result = rb_writev_internal(fptr, iov, iovcnt);
- if (result > 0) {
+ if (result >= 0) {
offset += result;
if (fptr->wbuf.ptr && fptr->wbuf.len) {
if (offset < (size_t)fptr->wbuf.len) {
@@ -1898,7 +2070,7 @@ io_binwritev_internal(VALUE arg)
iov->iov_base = (char *)iov->iov_base + result;
iov->iov_len -= result;
}
- else if (rb_io_maybe_wait_writable(errno, fptr->self, Qnil)) {
+ else if (rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
rb_io_check_closed(fptr);
}
else {
@@ -2063,7 +2235,8 @@ io_writev(int argc, const VALUE *argv, VALUE io)
* write(*objects) -> integer
*
* Writes each of the given +objects+ to +self+,
- * which must be opened for writing (see IO@Modes);
+ * which must be opened for writing
+ * (see {Access Modes}[rdoc-ref:File@Access+Modes]);
* returns the total number bytes written;
* each of +objects+ that is not a string is converted via method +to_s+:
*
@@ -2075,6 +2248,7 @@ io_writev(int argc, const VALUE *argv, VALUE io)
* Hello, World!
* foobar2
*
+ * Related: IO#read.
*/
static VALUE
@@ -2122,7 +2296,7 @@ rb_io_writev(VALUE io, int argc, const VALUE *argv)
* self << object -> self
*
* Writes the given +object+ to +self+,
- * which must be opened for writing (see IO@Modes);
+ * which must be opened for writing (see {Access Modes}[rdoc-ref:File@Access+Modes]);
* returns +self+;
* if +object+ is not a string, it is converted via method +to_s+:
*
@@ -2152,7 +2326,7 @@ nogvl_fsync(void *ptr)
#ifdef _WIN32
if (GetFileType((HANDLE)rb_w32_get_osfhandle(fptr->fd)) != FILE_TYPE_DISK)
- return 0;
+ return 0;
#endif
return (VALUE)fsync(fptr->fd);
}
@@ -2222,7 +2396,7 @@ static VALUE
rb_io_tell(VALUE io)
{
rb_io_t *fptr;
- off_t pos;
+ rb_off_t pos;
GetOpenFile(io, fptr);
pos = io_tell(fptr);
@@ -2235,7 +2409,7 @@ static VALUE
rb_io_seek(VALUE io, VALUE offset, int whence)
{
rb_io_t *fptr;
- off_t pos;
+ rb_off_t pos;
pos = NUM2OFFT(offset);
GetOpenFile(io, fptr);
@@ -2319,7 +2493,7 @@ rb_io_seek_m(int argc, VALUE *argv, VALUE io)
int whence = SEEK_SET;
if (rb_scan_args(argc, argv, "11", &offset, &ptrname) == 2) {
- whence = interpret_seek_whence(ptrname);
+ whence = interpret_seek_whence(ptrname);
}
return rb_io_seek(io, offset, whence);
@@ -2346,7 +2520,7 @@ static VALUE
rb_io_set_pos(VALUE io, VALUE offset)
{
rb_io_t *fptr;
- off_t pos;
+ rb_off_t pos;
pos = NUM2OFFT(offset);
GetOpenFile(io, fptr);
@@ -2390,11 +2564,11 @@ rb_io_rewind(VALUE io)
GetOpenFile(io, fptr);
if (io_seek(fptr, 0L, 0) < 0 && errno) rb_sys_fail_path(fptr->pathv);
if (io == ARGF.current_file) {
- ARGF.lineno -= fptr->lineno;
+ ARGF.lineno -= fptr->lineno;
}
fptr->lineno = 0;
if (fptr->readconv) {
- clear_readconv(fptr);
+ clear_readconv(fptr);
}
return INT2FIX(0);
@@ -2403,12 +2577,12 @@ rb_io_rewind(VALUE io)
static int
fptr_wait_readable(rb_io_t *fptr)
{
- int ret = rb_io_maybe_wait_readable(errno, fptr->self, Qnil);
+ int result = rb_io_maybe_wait_readable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT);
- if (ret)
+ if (result)
rb_io_check_closed(fptr);
- return ret;
+ return result;
}
static int
@@ -2427,7 +2601,7 @@ io_fillbuf(rb_io_t *fptr)
}
if (fptr->rbuf.len == 0) {
retry:
- r = rb_read_internal(fptr, fptr->rbuf.ptr, fptr->rbuf.capa);
+ r = rb_io_read_memory(fptr, fptr->rbuf.ptr, fptr->rbuf.capa);
if (r < 0) {
if (fptr_wait_readable(fptr))
@@ -2464,7 +2638,7 @@ io_fillbuf(rb_io_t *fptr)
* f.close
*
* Raises an exception unless the stream is opened for reading;
- * see {Mode}[rdoc-ref:IO@Mode].
+ * see {Mode}[rdoc-ref:File@Access+Modes].
*
* If +self+ is a stream such as pipe or socket, this method
* blocks until the other end sends some data or closes it:
@@ -2484,7 +2658,7 @@ io_fillbuf(rb_io_t *fptr)
* IO#sysread may not behave as you intend with IO#eof?, unless you
* call IO#rewind first (which is not available for some streams).
*
- * I#eof? is an alias for IO#eof.
+ * IO#eof? is an alias for IO#eof.
*
*/
@@ -2501,7 +2675,7 @@ rb_io_eof(VALUE io)
READ_CHECK(fptr);
#if RUBY_CRLF_ENVIRONMENT
if (!NEED_READCONV(fptr) && NEED_NEWLINE_DECORATOR_ON_READ(fptr)) {
- return RBOOL(eof(fptr->fd));;
+ return RBOOL(eof(fptr->fd));;
}
#endif
return RBOOL(io_fillbuf(fptr) < 0);
@@ -2568,10 +2742,10 @@ rb_io_set_sync(VALUE io, VALUE sync)
io = GetWriteIO(io);
GetOpenFile(io, fptr);
if (RTEST(sync)) {
- fptr->mode |= FMODE_SYNC;
+ fptr->mode |= FMODE_SYNC;
}
else {
- fptr->mode &= ~FMODE_SYNC;
+ fptr->mode &= ~FMODE_SYNC;
}
return sync;
}
@@ -2605,7 +2779,7 @@ rb_io_fsync(VALUE io)
if (io_fflush(fptr) < 0)
rb_sys_fail_on_write(fptr);
if ((int)rb_thread_io_blocking_region(nogvl_fsync, fptr, fptr->fd) < 0)
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
return INT2FIX(0);
}
#else
@@ -2627,7 +2801,7 @@ nogvl_fdatasync(void *ptr)
#ifdef _WIN32
if (GetFileType((HANDLE)rb_w32_get_osfhandle(fptr->fd)) != FILE_TYPE_DISK)
- return 0;
+ return 0;
#endif
return (VALUE)fdatasync(fptr->fd);
}
@@ -2655,7 +2829,7 @@ rb_io_fdatasync(VALUE io)
rb_sys_fail_on_write(fptr);
if ((int)rb_thread_io_blocking_region(nogvl_fdatasync, fptr, fptr->fd) == 0)
- return INT2FIX(0);
+ return INT2FIX(0);
/* fall back */
return rb_io_fsync(io);
@@ -2733,10 +2907,33 @@ rb_io_pid(VALUE io)
GetOpenFile(io, fptr);
if (!fptr->pid)
- return Qnil;
+ return Qnil;
return PIDT2NUM(fptr->pid);
}
+/*
+ * call-seq:
+ * path -> string or nil
+ *
+ * Returns the path associated with the IO, or +nil+ if there is no path
+ * associated with the IO. It is not guaranteed that the path exists on
+ * the filesystem.
+ *
+ * $stdin.path # => "<STDIN>"
+ *
+ * File.open("testfile") {|f| f.path} # => "testfile"
+ */
+
+static VALUE
+rb_io_path(VALUE io)
+{
+ rb_io_t *fptr = RFILE(io)->fptr;
+
+ if (!fptr)
+ return Qnil;
+
+ return rb_obj_dup(fptr->pathv);
+}
/*
* call-seq:
@@ -2764,16 +2961,16 @@ rb_io_inspect(VALUE obj)
rb_str_cat2(result, ":");
if (NIL_P(fptr->pathv)) {
if (fptr->fd < 0) {
- rb_str_cat(result, closed+1, strlen(closed)-1);
+ rb_str_cat(result, closed+1, strlen(closed)-1);
}
else {
- rb_str_catf(result, "fd %d", fptr->fd);
+ rb_str_catf(result, "fd %d", fptr->fd);
}
}
else {
- rb_str_append(result, fptr->pathv);
+ rb_str_append(result, fptr->pathv);
if (fptr->fd < 0) {
- rb_str_cat(result, closed, strlen(closed));
+ rb_str_cat(result, closed, strlen(closed));
}
}
return rb_str_cat2(result, ">");
@@ -2819,7 +3016,7 @@ io_bufread(char *ptr, long len, rb_io_t *fptr)
while (n > 0) {
again:
rb_io_check_closed(fptr);
- c = rb_read_internal(fptr, ptr+offset, n);
+ c = rb_io_read_memory(fptr, ptr+offset, n);
if (c == 0) break;
if (c < 0) {
if (fptr_wait_readable(fptr))
@@ -2882,27 +3079,27 @@ static long
remain_size(rb_io_t *fptr)
{
struct stat st;
- off_t siz = READ_DATA_PENDING_COUNT(fptr);
- off_t pos;
+ rb_off_t siz = READ_DATA_PENDING_COUNT(fptr);
+ rb_off_t pos;
if (fstat(fptr->fd, &st) == 0 && S_ISREG(st.st_mode)
#if defined(__HAIKU__)
- && (st.st_dev > 3)
+ && (st.st_dev > 3)
#endif
- )
+ )
{
if (io_fflush(fptr) < 0)
rb_sys_fail_on_write(fptr);
- pos = lseek(fptr->fd, 0, SEEK_CUR);
- if (st.st_size >= pos && pos >= 0) {
- siz += st.st_size - pos;
- if (siz > LONG_MAX) {
- rb_raise(rb_eIOError, "file too big for single read");
- }
- }
+ pos = lseek(fptr->fd, 0, SEEK_CUR);
+ if (st.st_size >= pos && pos >= 0) {
+ siz += st.st_size - pos;
+ if (siz > LONG_MAX) {
+ rb_raise(rb_eIOError, "file too big for single read");
+ }
+ }
}
else {
- siz += BUFSIZ;
+ siz += BUFSIZ;
}
return (long)siz;
}
@@ -2937,7 +3134,7 @@ make_readconv(rb_io_t *fptr, int size)
rb_exc_raise(rb_econv_open_exc(sname, dname, ecflags));
fptr->cbuf.off = 0;
fptr->cbuf.len = 0;
- if (size < IO_CBUF_CAPA_MIN) size = IO_CBUF_CAPA_MIN;
+ if (size < IO_CBUF_CAPA_MIN) size = IO_CBUF_CAPA_MIN;
fptr->cbuf.capa = size;
fptr->cbuf.ptr = ALLOC_N(char, fptr->cbuf.capa);
}
@@ -2994,27 +3191,27 @@ fill_cbuf(rb_io_t *fptr, int ec_flags)
if (res == econv_finished) {
return MORE_CHAR_FINISHED;
- }
+ }
if (res == econv_source_buffer_empty) {
if (fptr->rbuf.len == 0) {
- READ_CHECK(fptr);
+ READ_CHECK(fptr);
if (io_fillbuf(fptr) < 0) {
- if (!fptr->readconv) {
- return MORE_CHAR_FINISHED;
- }
+ if (!fptr->readconv) {
+ return MORE_CHAR_FINISHED;
+ }
ds = dp = (unsigned char *)fptr->cbuf.ptr + fptr->cbuf.off + fptr->cbuf.len;
de = (unsigned char *)fptr->cbuf.ptr + fptr->cbuf.capa;
res = rb_econv_convert(fptr->readconv, NULL, NULL, &dp, de, 0);
fptr->cbuf.len += (int)(dp - ds);
rb_econv_check_error(fptr->readconv);
- break;
+ break;
}
}
}
}
if (cbuf_len0 != fptr->cbuf.len)
- return MORE_CHAR_SUSPENDED;
+ return MORE_CHAR_SUSPENDED;
return MORE_CHAR_FINISHED;
}
@@ -3034,14 +3231,14 @@ io_shift_cbuf(rb_io_t *fptr, int len, VALUE *strp)
{
VALUE str = Qnil;
if (strp) {
- str = *strp;
- if (NIL_P(str)) {
- *strp = str = rb_str_new(fptr->cbuf.ptr+fptr->cbuf.off, len);
- }
- else {
- rb_str_cat(str, fptr->cbuf.ptr+fptr->cbuf.off, len);
- }
- rb_enc_associate(str, fptr->encs.enc);
+ str = *strp;
+ if (NIL_P(str)) {
+ *strp = str = rb_str_new(fptr->cbuf.ptr+fptr->cbuf.off, len);
+ }
+ else {
+ rb_str_cat(str, fptr->cbuf.ptr+fptr->cbuf.off, len);
+ }
+ rb_enc_associate(str, fptr->encs.enc);
}
fptr->cbuf.off += len;
fptr->cbuf.len -= len;
@@ -3059,22 +3256,25 @@ static int
io_setstrbuf(VALUE *str, long len)
{
#ifdef _WIN32
- len = (len + 1) & ~1L; /* round up for wide char */
+ if (len > 0)
+ len = (len + 1) & ~1L; /* round up for wide char */
#endif
if (NIL_P(*str)) {
- *str = rb_str_new(0, len);
- return TRUE;
+ *str = rb_str_new(0, len);
+ return TRUE;
}
else {
- VALUE s = StringValue(*str);
- long clen = RSTRING_LEN(s);
- if (clen >= len) {
- rb_str_modify(s);
- return FALSE;
- }
- len -= clen;
- }
- rb_str_modify_expand(*str, len);
+ VALUE s = StringValue(*str);
+ long clen = RSTRING_LEN(s);
+ if (clen >= len) {
+ rb_str_modify(s);
+ return FALSE;
+ }
+ len -= clen;
+ }
+ if ((rb_str_capacity(*str) - (size_t)RSTRING_LEN(*str)) < (size_t)len) {
+ rb_str_modify_expand(*str, len);
+ }
return FALSE;
}
@@ -3083,7 +3283,7 @@ static void
io_shrink_read_string(VALUE str, long n)
{
if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
- rb_str_resize(str, n);
+ rb_str_resize(str, n);
}
}
@@ -3091,9 +3291,9 @@ static void
io_set_read_length(VALUE str, long n, int shrinkable)
{
if (RSTRING_LEN(str) != n) {
- rb_str_modify(str);
- rb_str_set_len(str, n);
- if (shrinkable) io_shrink_read_string(str, n);
+ rb_str_modify(str);
+ rb_str_set_len(str, n);
+ if (shrinkable) io_shrink_read_string(str, n);
}
}
@@ -3108,28 +3308,28 @@ read_all(rb_io_t *fptr, long siz, VALUE str)
int shrinkable;
if (NEED_READCONV(fptr)) {
- int first = !NIL_P(str);
- SET_BINARY_MODE(fptr);
- shrinkable = io_setstrbuf(&str,0);
+ int first = !NIL_P(str);
+ SET_BINARY_MODE(fptr);
+ shrinkable = io_setstrbuf(&str,0);
make_readconv(fptr, 0);
while (1) {
VALUE v;
if (fptr->cbuf.len) {
- if (first) rb_str_set_len(str, first = 0);
+ if (first) rb_str_set_len(str, first = 0);
io_shift_cbuf(fptr, fptr->cbuf.len, &str);
}
v = fill_cbuf(fptr, 0);
if (v != MORE_CHAR_SUSPENDED && v != MORE_CHAR_FINISHED) {
if (fptr->cbuf.len) {
- if (first) rb_str_set_len(str, first = 0);
+ if (first) rb_str_set_len(str, first = 0);
io_shift_cbuf(fptr, fptr->cbuf.len, &str);
}
rb_exc_raise(v);
}
if (v == MORE_CHAR_FINISHED) {
clear_readconv(fptr);
- if (first) rb_str_set_len(str, first = 0);
- if (shrinkable) io_shrink_read_string(str, RSTRING_LEN(str));
+ if (first) rb_str_set_len(str, first = 0);
+ if (shrinkable) io_shrink_read_string(str, RSTRING_LEN(str));
return io_enc_str(str, fptr);
}
}
@@ -3145,19 +3345,29 @@ read_all(rb_io_t *fptr, long siz, VALUE str)
if (siz == 0) siz = BUFSIZ;
shrinkable = io_setstrbuf(&str, siz);
for (;;) {
- READ_CHECK(fptr);
- n = io_fread(str, bytes, siz - bytes, fptr);
- if (n == 0 && bytes == 0) {
- rb_str_set_len(str, 0);
- break;
- }
- bytes += n;
- rb_str_set_len(str, bytes);
- if (cr != ENC_CODERANGE_BROKEN)
- pos += rb_str_coderange_scan_restartable(RSTRING_PTR(str) + pos, RSTRING_PTR(str) + bytes, enc, &cr);
- if (bytes < siz) break;
- siz += BUFSIZ;
- rb_str_modify_expand(str, BUFSIZ);
+ READ_CHECK(fptr);
+ n = io_fread(str, bytes, siz - bytes, fptr);
+ if (n == 0 && bytes == 0) {
+ rb_str_set_len(str, 0);
+ break;
+ }
+ bytes += n;
+ rb_str_set_len(str, bytes);
+ if (cr != ENC_CODERANGE_BROKEN)
+ pos += rb_str_coderange_scan_restartable(RSTRING_PTR(str) + pos, RSTRING_PTR(str) + bytes, enc, &cr);
+ if (bytes < siz) break;
+ siz += BUFSIZ;
+
+ size_t capa = rb_str_capacity(str);
+ if (capa < (size_t)RSTRING_LEN(str) + BUFSIZ) {
+ if (capa < BUFSIZ) {
+ capa = BUFSIZ;
+ }
+ else if (capa > IO_MAX_BUFFER_GROWTH) {
+ capa = IO_MAX_BUFFER_GROWTH;
+ }
+ rb_str_modify_expand(str, capa);
+ }
}
if (shrinkable) io_shrink_read_string(str, RSTRING_LEN(str));
str = io_enc_str(str, fptr);
@@ -3169,12 +3379,12 @@ void
rb_io_set_nonblock(rb_io_t *fptr)
{
if (rb_fd_set_nonblock(fptr->fd) != 0) {
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
}
}
static VALUE
-read_internal_call(VALUE arg)
+io_read_memory_call(VALUE arg)
{
struct io_internal_read_struct *iis = (struct io_internal_read_struct *)arg;
@@ -3182,7 +3392,7 @@ read_internal_call(VALUE arg)
if (scheduler != Qnil) {
VALUE result = rb_fiber_scheduler_io_read_memory(scheduler, iis->fptr->self, iis->buf, iis->capa, 0);
- if (result != Qundef) {
+ if (!UNDEF_P(result)) {
// This is actually returned as a pseudo-VALUE and later cast to a long:
return (VALUE)rb_fiber_scheduler_io_result_apply(result);
}
@@ -3192,9 +3402,9 @@ read_internal_call(VALUE arg)
}
static long
-read_internal_locktmp(VALUE str, struct io_internal_read_struct *iis)
+io_read_memory_locktmp(VALUE str, struct io_internal_read_struct *iis)
{
- return (long)rb_str_locktmp_ensure(str, read_internal_call, (VALUE)iis);
+ return (long)rb_str_locktmp_ensure(str, io_read_memory_call, (VALUE)iis);
}
#define no_exception_p(opts) !rb_opts_exception_p((opts), TRUE)
@@ -3211,7 +3421,7 @@ io_getpartial(int argc, VALUE *argv, VALUE io, int no_exception, int nonblock)
rb_scan_args(argc, argv, "11", &length, &str);
if ((len = NUM2LONG(length)) < 0) {
- rb_raise(rb_eArgError, "negative length %ld given", len);
+ rb_raise(rb_eArgError, "negative length %ld given", len);
}
shrinkable = io_setstrbuf(&str, len);
@@ -3220,8 +3430,8 @@ io_getpartial(int argc, VALUE *argv, VALUE io, int no_exception, int nonblock)
rb_io_check_byte_readable(fptr);
if (len == 0) {
- io_set_read_length(str, 0, shrinkable);
- return str;
+ io_set_read_length(str, 0, shrinkable);
+ return str;
}
if (!nonblock)
@@ -3232,23 +3442,25 @@ io_getpartial(int argc, VALUE *argv, VALUE io, int no_exception, int nonblock)
if (nonblock) {
rb_io_set_nonblock(fptr);
}
- io_setstrbuf(&str, len);
+ io_setstrbuf(&str, len);
iis.th = rb_thread_current();
iis.fptr = fptr;
iis.nonblock = nonblock;
+ iis.fd = fptr->fd;
iis.buf = RSTRING_PTR(str);
iis.capa = len;
- n = read_internal_locktmp(str, &iis);
+ iis.timeout = NULL;
+ n = io_read_memory_locktmp(str, &iis);
if (n < 0) {
- int e = errno;
+ int e = errno;
if (!nonblock && fptr_wait_readable(fptr))
goto again;
- if (nonblock && (io_again_p(e))) {
+ if (nonblock && (io_again_p(e))) {
if (no_exception)
return sym_wait_readable;
else
- rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE,
- e, "read would block");
+ rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE,
+ e, "read would block");
}
rb_syserr_fail_path(e, fptr->pathv);
}
@@ -3383,7 +3595,7 @@ io_read_nonblock(rb_execution_context_t *ec, VALUE io, VALUE length, VALUE str,
int shrinkable;
if ((len = NUM2LONG(length)) < 0) {
- rb_raise(rb_eArgError, "negative length %ld given", len);
+ rb_raise(rb_eArgError, "negative length %ld given", len);
}
shrinkable = io_setstrbuf(&str, len);
@@ -3393,25 +3605,27 @@ io_read_nonblock(rb_execution_context_t *ec, VALUE io, VALUE length, VALUE str,
rb_io_check_byte_readable(fptr);
if (len == 0) {
- io_set_read_length(str, 0, shrinkable);
- return str;
+ io_set_read_length(str, 0, shrinkable);
+ return str;
}
n = read_buffered_data(RSTRING_PTR(str), len, fptr);
if (n <= 0) {
- rb_io_set_nonblock(fptr);
- shrinkable |= io_setstrbuf(&str, len);
+ rb_fd_set_nonblock(fptr->fd);
+ shrinkable |= io_setstrbuf(&str, len);
iis.fptr = fptr;
iis.nonblock = 1;
+ iis.fd = fptr->fd;
iis.buf = RSTRING_PTR(str);
iis.capa = len;
- n = read_internal_locktmp(str, &iis);
+ iis.timeout = NULL;
+ n = io_read_memory_locktmp(str, &iis);
if (n < 0) {
- int e = errno;
- if (io_again_p(e)) {
+ int e = errno;
+ if (io_again_p(e)) {
if (!ex) return sym_wait_readable;
- rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE,
- e, "read would block");
+ rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE,
+ e, "read would block");
}
rb_syserr_fail_path(e, fptr->pathv);
}
@@ -3420,7 +3634,7 @@ io_read_nonblock(rb_execution_context_t *ec, VALUE io, VALUE length, VALUE str,
if (n == 0) {
if (!ex) return Qnil;
- rb_eof_error();
+ rb_eof_error();
}
return str;
@@ -3434,7 +3648,7 @@ io_write_nonblock(rb_execution_context_t *ec, VALUE io, VALUE str, VALUE ex)
long n;
if (!RB_TYPE_P(str, T_STRING))
- str = rb_obj_as_string(str);
+ str = rb_obj_as_string(str);
rb_bool_expected(ex, "exception", TRUE);
io = GetWriteIO(io);
@@ -3444,21 +3658,21 @@ io_write_nonblock(rb_execution_context_t *ec, VALUE io, VALUE str, VALUE ex)
if (io_fflush(fptr) < 0)
rb_sys_fail_on_write(fptr);
- rb_io_set_nonblock(fptr);
+ rb_fd_set_nonblock(fptr->fd);
n = write(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str));
RB_GC_GUARD(str);
if (n < 0) {
- int e = errno;
- if (io_again_p(e)) {
+ int e = errno;
+ if (io_again_p(e)) {
if (!ex) {
- return sym_wait_writable;
- }
- else {
- rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e, "write would block");
- }
- }
- rb_syserr_fail_path(e, fptr->pathv);
+ return sym_wait_writable;
+ }
+ else {
+ rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e, "write would block");
+ }
+ }
+ rb_syserr_fail_path(e, fptr->pathv);
}
return LONG2FIX(n);
@@ -3466,14 +3680,13 @@ io_write_nonblock(rb_execution_context_t *ec, VALUE io, VALUE str, VALUE ex)
/*
* call-seq:
- * read(maxlen = nil) -> string or nil
- * read(maxlen = nil, out_string) -> out_string or nil
+ * read(maxlen = nil, out_string = nil) -> new_string, out_string, or nil
*
- * Reads bytes from the stream (in binary mode):
+ * Reads bytes from the stream; the stream must be opened for reading
+ * (see {Access Modes}[rdoc-ref:File@Access+Modes]):
*
- * - If +maxlen+ is +nil+, reads all bytes.
- * - Otherwise reads +maxlen+ bytes, if available.
- * - Otherwise reads all bytes.
+ * - If +maxlen+ is +nil+, reads all bytes using the stream's data mode.
+ * - Otherwise reads up to +maxlen+ bytes in binary mode.
*
* Returns a string (either a new string or the given +out_string+)
* containing the bytes read.
@@ -3533,6 +3746,7 @@ io_write_nonblock(rb_execution_context_t *ec, VALUE io, VALUE str, VALUE ex)
* If you need the behavior like a single read(2) system call,
* consider #readpartial, #read_nonblock, and #sysread.
*
+ * Related: IO#write.
*/
static VALUE
@@ -3549,13 +3763,13 @@ io_read(int argc, VALUE *argv, VALUE io)
rb_scan_args(argc, argv, "02", &length, &str);
if (NIL_P(length)) {
- GetOpenFile(io, fptr);
- rb_io_check_char_readable(fptr);
- return read_all(fptr, remain_size(fptr), str);
+ GetOpenFile(io, fptr);
+ rb_io_check_char_readable(fptr);
+ return read_all(fptr, remain_size(fptr), str);
}
len = NUM2LONG(length);
if (len < 0) {
- rb_raise(rb_eArgError, "negative length %ld given", len);
+ rb_raise(rb_eArgError, "negative length %ld given", len);
}
shrinkable = io_setstrbuf(&str,len);
@@ -3563,8 +3777,8 @@ io_read(int argc, VALUE *argv, VALUE io)
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
if (len == 0) {
- io_set_read_length(str, 0, shrinkable);
- return str;
+ io_set_read_length(str, 0, shrinkable);
+ return str;
}
READ_CHECK(fptr);
@@ -3575,7 +3789,7 @@ io_read(int argc, VALUE *argv, VALUE io)
io_set_read_length(str, n, shrinkable);
#if RUBY_CRLF_ENVIRONMENT
if (previous_mode == O_TEXT) {
- setmode(fptr->fd, O_TEXT);
+ setmode(fptr->fd, O_TEXT);
}
#endif
if (n == 0) return Qnil;
@@ -3588,17 +3802,42 @@ rscheck(const char *rsptr, long rslen, VALUE rs)
{
if (!rs) return;
if (RSTRING_PTR(rs) != rsptr && RSTRING_LEN(rs) != rslen)
- rb_raise(rb_eRuntimeError, "rs modified");
+ rb_raise(rb_eRuntimeError, "rs modified");
+}
+
+static const char *
+search_delim(const char *p, long len, int delim, rb_encoding *enc)
+{
+ if (rb_enc_mbminlen(enc) == 1) {
+ p = memchr(p, delim, len);
+ if (p) return p + 1;
+ }
+ else {
+ const char *end = p + len;
+ while (p < end) {
+ int r = rb_enc_precise_mbclen(p, end, enc);
+ if (!MBCLEN_CHARFOUND_P(r)) {
+ p += rb_enc_mbminlen(enc);
+ continue;
+ }
+ int n = MBCLEN_CHARFOUND_LEN(r);
+ if (rb_enc_mbc_to_codepoint(p, end, enc) == (unsigned int)delim) {
+ return p + n;
+ }
+ p += n;
+ }
+ }
+ return NULL;
}
static int
-appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp)
+appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp, rb_encoding *enc)
{
VALUE str = *strp;
long limit = *lp;
if (NEED_READCONV(fptr)) {
- SET_BINARY_MODE(fptr);
+ SET_BINARY_MODE(fptr);
make_readconv(fptr, 0);
do {
const char *p, *e;
@@ -3607,9 +3846,9 @@ appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp)
p = READ_CHAR_PENDING_PTR(fptr);
if (0 < limit && limit < searchlen)
searchlen = (int)limit;
- e = memchr(p, delim, searchlen);
+ e = search_delim(p, searchlen, delim, enc);
if (e) {
- int len = (int)(e-p+1);
+ int len = (int)(e-p);
if (NIL_P(str))
*strp = str = rb_str_new(p, len);
else
@@ -3642,32 +3881,32 @@ appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp)
NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
do {
- long pending = READ_DATA_PENDING_COUNT(fptr);
- if (pending > 0) {
- const char *p = READ_DATA_PENDING_PTR(fptr);
- const char *e;
- long last;
-
- if (limit > 0 && pending > limit) pending = limit;
- e = memchr(p, delim, pending);
- if (e) pending = e - p + 1;
- if (!NIL_P(str)) {
- last = RSTRING_LEN(str);
- rb_str_resize(str, last + pending);
- }
- else {
+ long pending = READ_DATA_PENDING_COUNT(fptr);
+ if (pending > 0) {
+ const char *p = READ_DATA_PENDING_PTR(fptr);
+ const char *e;
+ long last;
+
+ if (limit > 0 && pending > limit) pending = limit;
+ e = search_delim(p, pending, delim, enc);
+ if (e) pending = e - p;
+ if (!NIL_P(str)) {
+ last = RSTRING_LEN(str);
+ rb_str_resize(str, last + pending);
+ }
+ else {
last = 0;
- *strp = str = rb_str_buf_new(pending);
- rb_str_set_len(str, pending);
- }
- read_buffered_data(RSTRING_PTR(str) + last, pending, fptr); /* must not fail */
- limit -= pending;
- *lp = limit;
- if (e) return delim;
- if (limit == 0)
- return (unsigned char)RSTRING_PTR(str)[RSTRING_LEN(str)-1];
- }
- READ_CHECK(fptr);
+ *strp = str = rb_str_buf_new(pending);
+ rb_str_set_len(str, pending);
+ }
+ read_buffered_data(RSTRING_PTR(str) + last, pending, fptr); /* must not fail */
+ limit -= pending;
+ *lp = limit;
+ if (e) return delim;
+ if (limit == 0)
+ return (unsigned char)RSTRING_PTR(str)[RSTRING_LEN(str)-1];
+ }
+ READ_CHECK(fptr);
} while (io_fillbuf(fptr) >= 0);
*lp = limit;
return EOF;
@@ -3677,47 +3916,47 @@ static inline int
swallow(rb_io_t *fptr, int term)
{
if (NEED_READCONV(fptr)) {
- rb_encoding *enc = io_read_encoding(fptr);
- int needconv = rb_enc_mbminlen(enc) != 1;
- SET_BINARY_MODE(fptr);
- make_readconv(fptr, 0);
- do {
- size_t cnt;
- while ((cnt = READ_CHAR_PENDING_COUNT(fptr)) > 0) {
- const char *p = READ_CHAR_PENDING_PTR(fptr);
- int i;
- if (!needconv) {
- if (*p != term) return TRUE;
- i = (int)cnt;
- while (--i && *++p == term);
- }
- else {
- const char *e = p + cnt;
- if (rb_enc_ascget(p, e, &i, enc) != term) return TRUE;
- while ((p += i) < e && rb_enc_ascget(p, e, &i, enc) == term);
- i = (int)(e - p);
- }
- io_shift_cbuf(fptr, (int)cnt - i, NULL);
- }
- } while (more_char(fptr) != MORE_CHAR_FINISHED);
- return FALSE;
+ rb_encoding *enc = io_read_encoding(fptr);
+ int needconv = rb_enc_mbminlen(enc) != 1;
+ SET_BINARY_MODE(fptr);
+ make_readconv(fptr, 0);
+ do {
+ size_t cnt;
+ while ((cnt = READ_CHAR_PENDING_COUNT(fptr)) > 0) {
+ const char *p = READ_CHAR_PENDING_PTR(fptr);
+ int i;
+ if (!needconv) {
+ if (*p != term) return TRUE;
+ i = (int)cnt;
+ while (--i && *++p == term);
+ }
+ else {
+ const char *e = p + cnt;
+ if (rb_enc_ascget(p, e, &i, enc) != term) return TRUE;
+ while ((p += i) < e && rb_enc_ascget(p, e, &i, enc) == term);
+ i = (int)(e - p);
+ }
+ io_shift_cbuf(fptr, (int)cnt - i, NULL);
+ }
+ } while (more_char(fptr) != MORE_CHAR_FINISHED);
+ return FALSE;
}
NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
do {
- size_t cnt;
- while ((cnt = READ_DATA_PENDING_COUNT(fptr)) > 0) {
- char buf[1024];
- const char *p = READ_DATA_PENDING_PTR(fptr);
- int i;
- if (cnt > sizeof buf) cnt = sizeof buf;
- if (*p != term) return TRUE;
- i = (int)cnt;
- while (--i && *++p == term);
- if (!read_buffered_data(buf, cnt - i, fptr)) /* must not fail */
- rb_sys_fail_path(fptr->pathv);
- }
- READ_CHECK(fptr);
+ size_t cnt;
+ while ((cnt = READ_DATA_PENDING_COUNT(fptr)) > 0) {
+ char buf[1024];
+ const char *p = READ_DATA_PENDING_PTR(fptr);
+ int i;
+ if (cnt > sizeof buf) cnt = sizeof buf;
+ if (*p != term) return TRUE;
+ i = (int)cnt;
+ while (--i && *++p == term);
+ if (!read_buffered_data(buf, cnt - i, fptr)) /* must not fail */
+ rb_sys_fail_path(fptr->pathv);
+ }
+ READ_CHECK(fptr);
} while (io_fillbuf(fptr) == 0);
return FALSE;
}
@@ -3731,43 +3970,43 @@ rb_io_getline_fast(rb_io_t *fptr, rb_encoding *enc, int chomp)
int cr = 0;
do {
- int pending = READ_DATA_PENDING_COUNT(fptr);
+ int pending = READ_DATA_PENDING_COUNT(fptr);
- if (pending > 0) {
- const char *p = READ_DATA_PENDING_PTR(fptr);
- const char *e;
- int chomplen = 0;
+ if (pending > 0) {
+ const char *p = READ_DATA_PENDING_PTR(fptr);
+ const char *e;
+ int chomplen = 0;
- e = memchr(p, '\n', pending);
- if (e) {
+ e = memchr(p, '\n', pending);
+ if (e) {
pending = (int)(e - p + 1);
- if (chomp) {
- chomplen = (pending > 1 && *(e-1) == '\r') + 1;
- }
- }
- if (NIL_P(str)) {
- str = rb_str_new(p, pending - chomplen);
- fptr->rbuf.off += pending;
- fptr->rbuf.len -= pending;
- }
- else {
- rb_str_resize(str, len + pending - chomplen);
- read_buffered_data(RSTRING_PTR(str)+len, pending - chomplen, fptr);
- fptr->rbuf.off += chomplen;
- fptr->rbuf.len -= chomplen;
+ if (chomp) {
+ chomplen = (pending > 1 && *(e-1) == '\r') + 1;
+ }
+ }
+ if (NIL_P(str)) {
+ str = rb_str_new(p, pending - chomplen);
+ fptr->rbuf.off += pending;
+ fptr->rbuf.len -= pending;
+ }
+ else {
+ rb_str_resize(str, len + pending - chomplen);
+ read_buffered_data(RSTRING_PTR(str)+len, pending - chomplen, fptr);
+ fptr->rbuf.off += chomplen;
+ fptr->rbuf.len -= chomplen;
if (pending == 1 && chomplen == 1 && len > 0) {
if (RSTRING_PTR(str)[len-1] == '\r') {
rb_str_resize(str, --len);
break;
}
}
- }
- len += pending - chomplen;
- if (cr != ENC_CODERANGE_BROKEN)
- pos += rb_str_coderange_scan_restartable(RSTRING_PTR(str) + pos, RSTRING_PTR(str) + len, enc, &cr);
- if (e) break;
- }
- READ_CHECK(fptr);
+ }
+ len += pending - chomplen;
+ if (cr != ENC_CODERANGE_BROKEN)
+ pos += rb_str_coderange_scan_restartable(RSTRING_PTR(str) + pos, RSTRING_PTR(str) + len, enc, &cr);
+ if (e) break;
+ }
+ READ_CHECK(fptr);
} while (io_fillbuf(fptr) >= 0);
if (NIL_P(str)) return Qnil;
@@ -3790,13 +4029,13 @@ extract_getline_opts(VALUE opts, struct getline_arg *args)
{
int chomp = FALSE;
if (!NIL_P(opts)) {
- static ID kwds[1];
- VALUE vchomp;
- if (!kwds[0]) {
- kwds[0] = rb_intern_const("chomp");
- }
- rb_get_kwargs(opts, kwds, 0, -2, &vchomp);
- chomp = (vchomp != Qundef) && RTEST(vchomp);
+ static ID kwds[1];
+ VALUE vchomp;
+ if (!kwds[0]) {
+ kwds[0] = rb_intern_const("chomp");
+ }
+ rb_get_kwargs(opts, kwds, 0, -2, &vchomp);
+ chomp = (!UNDEF_P(vchomp)) && RTEST(vchomp);
}
args->chomp = chomp;
}
@@ -3817,7 +4056,7 @@ extract_getline_args(int argc, VALUE *argv, struct getline_arg *args)
}
}
else if (2 <= argc) {
- rs = argv[0], lim = argv[1];
+ rs = argv[0], lim = argv[1];
if (!NIL_P(rs))
StringValue(rs);
}
@@ -3832,25 +4071,25 @@ check_getline_args(VALUE *rsp, long *limit, VALUE io)
VALUE rs = *rsp;
if (!NIL_P(rs)) {
- rb_encoding *enc_rs, *enc_io;
-
- GetOpenFile(io, fptr);
- enc_rs = rb_enc_get(rs);
- enc_io = io_read_encoding(fptr);
- if (enc_io != enc_rs &&
- (!is_ascii_string(rs) ||
- (RSTRING_LEN(rs) > 0 && !rb_enc_asciicompat(enc_io)))) {
+ rb_encoding *enc_rs, *enc_io;
+
+ GetOpenFile(io, fptr);
+ enc_rs = rb_enc_get(rs);
+ enc_io = io_read_encoding(fptr);
+ if (enc_io != enc_rs &&
+ (!is_ascii_string(rs) ||
+ (RSTRING_LEN(rs) > 0 && !rb_enc_asciicompat(enc_io)))) {
if (rs == rb_default_rs) {
rs = rb_enc_str_new(0, 0, enc_io);
rb_str_buf_cat_ascii(rs, "\n");
- *rsp = rs;
+ *rsp = rs;
}
else {
rb_raise(rb_eArgError, "encoding mismatch: %s IO with %s RS",
rb_enc_name(enc_io),
rb_enc_name(enc_rs));
}
- }
+ }
}
}
@@ -3873,77 +4112,86 @@ rb_io_getline_0(VALUE rs, long limit, int chomp, rb_io_t *fptr)
rb_io_check_char_readable(fptr);
if (NIL_P(rs) && limit < 0) {
- str = read_all(fptr, 0, Qnil);
- if (RSTRING_LEN(str) == 0) return Qnil;
- if (chomp) rb_str_chomp_string(str, rb_default_rs);
+ str = read_all(fptr, 0, Qnil);
+ if (RSTRING_LEN(str) == 0) return Qnil;
}
else if (limit == 0) {
- return rb_enc_str_new(0, 0, io_read_encoding(fptr));
+ return rb_enc_str_new(0, 0, io_read_encoding(fptr));
}
else if (rs == rb_default_rs && limit < 0 && !NEED_READCONV(fptr) &&
rb_enc_asciicompat(enc = io_read_encoding(fptr))) {
- NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
- return rb_io_getline_fast(fptr, enc, chomp);
+ NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
+ return rb_io_getline_fast(fptr, enc, chomp);
}
else {
- int c, newline = -1;
- const char *rsptr = 0;
- long rslen = 0;
- int rspara = 0;
+ int c, newline = -1;
+ const char *rsptr = 0;
+ long rslen = 0;
+ int rspara = 0;
int extra_limit = 16;
- int chomp_cr = chomp;
+ int chomp_cr = chomp;
- SET_BINARY_MODE(fptr);
+ SET_BINARY_MODE(fptr);
enc = io_read_encoding(fptr);
- if (!NIL_P(rs)) {
- rslen = RSTRING_LEN(rs);
- if (rslen == 0) {
- rsptr = "\n\n";
- rslen = 2;
- rspara = 1;
- swallow(fptr, '\n');
- rs = 0;
- if (!rb_enc_asciicompat(enc)) {
- rs = rb_usascii_str_new(rsptr, rslen);
- rs = rb_str_encode(rs, rb_enc_from_encoding(enc), 0, Qnil);
- OBJ_FREEZE(rs);
- rsptr = RSTRING_PTR(rs);
- rslen = RSTRING_LEN(rs);
- }
- }
- else {
- rsptr = RSTRING_PTR(rs);
- }
- newline = (unsigned char)rsptr[rslen - 1];
- chomp_cr = chomp && rslen == 1 && newline == '\n';
- }
-
- /* MS - Optimization */
- while ((c = appendline(fptr, newline, &str, &limit)) != EOF) {
+ if (!NIL_P(rs)) {
+ rslen = RSTRING_LEN(rs);
+ if (rslen == 0) {
+ rsptr = "\n\n";
+ rslen = 2;
+ rspara = 1;
+ swallow(fptr, '\n');
+ rs = 0;
+ if (!rb_enc_asciicompat(enc)) {
+ rs = rb_usascii_str_new(rsptr, rslen);
+ rs = rb_str_encode(rs, rb_enc_from_encoding(enc), 0, Qnil);
+ OBJ_FREEZE(rs);
+ rsptr = RSTRING_PTR(rs);
+ rslen = RSTRING_LEN(rs);
+ }
+ newline = '\n';
+ }
+ else if (rb_enc_mbminlen(enc) == 1) {
+ rsptr = RSTRING_PTR(rs);
+ newline = (unsigned char)rsptr[rslen - 1];
+ }
+ else {
+ rs = rb_str_encode(rs, rb_enc_from_encoding(enc), 0, Qnil);
+ rsptr = RSTRING_PTR(rs);
+ const char *e = rsptr + rslen;
+ const char *last = rb_enc_prev_char(rsptr, e, e, enc);
+ int n;
+ newline = rb_enc_codepoint_len(last, e, &n, enc);
+ if (last + n != e) rb_raise(rb_eArgError, "broken separator");
+ }
+ chomp_cr = chomp && newline == '\n' && rslen == rb_enc_mbminlen(enc);
+ }
+
+ /* MS - Optimization */
+ while ((c = appendline(fptr, newline, &str, &limit, enc)) != EOF) {
const char *s, *p, *pp, *e;
- if (c == newline) {
- if (RSTRING_LEN(str) < rslen) continue;
- s = RSTRING_PTR(str);
+ if (c == newline) {
+ if (RSTRING_LEN(str) < rslen) continue;
+ s = RSTRING_PTR(str);
e = RSTRING_END(str);
- p = e - rslen;
- pp = rb_enc_left_char_head(s, p, e, enc);
- if (pp != p) continue;
- if (!rspara) rscheck(rsptr, rslen, rs);
- if (memcmp(p, rsptr, rslen) == 0) {
- if (chomp) {
- if (chomp_cr && p > s && *(p-1) == '\r') --p;
- rb_str_set_len(str, p - s);
- }
- break;
- }
- }
- if (limit == 0) {
- s = RSTRING_PTR(str);
- p = RSTRING_END(str);
- pp = rb_enc_left_char_head(s, p-1, p, enc);
- if (extra_limit &&
+ p = e - rslen;
+ pp = rb_enc_left_char_head(s, p, e, enc);
+ if (pp != p) continue;
+ if (!rspara) rscheck(rsptr, rslen, rs);
+ if (memcmp(p, rsptr, rslen) == 0) {
+ if (chomp) {
+ if (chomp_cr && p > s && *(p-1) == '\r') --p;
+ rb_str_set_len(str, p - s);
+ }
+ break;
+ }
+ }
+ if (limit == 0) {
+ s = RSTRING_PTR(str);
+ p = RSTRING_END(str);
+ pp = rb_enc_prev_char(s, p, p, enc);
+ if (extra_limit && pp &&
MBCLEN_NEEDMORE_P(rb_enc_precise_mbclen(pp, p, enc))) {
/* relax the limit while incomplete character.
* extra_limit limits the relax length */
@@ -3954,17 +4202,17 @@ rb_io_getline_0(VALUE rs, long limit, int chomp, rb_io_t *fptr)
nolimit = 1;
break;
}
- }
- }
+ }
+ }
- if (rspara && c != EOF)
- swallow(fptr, '\n');
- if (!NIL_P(str))
+ if (rspara && c != EOF)
+ swallow(fptr, '\n');
+ if (!NIL_P(str))
str = io_enc_str(str, fptr);
}
if (!NIL_P(str) && !nolimit) {
- fptr->lineno++;
+ fptr->lineno++;
}
return str;
@@ -3981,13 +4229,13 @@ rb_io_getline_1(VALUE rs, long limit, int chomp, VALUE io)
old_lineno = fptr->lineno;
str = rb_io_getline_0(rs, limit, chomp, fptr);
if (!NIL_P(str) && (new_lineno = fptr->lineno) != old_lineno) {
- if (io == ARGF.current_file) {
- ARGF.lineno += new_lineno - old_lineno;
- ARGF.last_lineno = ARGF.lineno;
- }
- else {
- ARGF.last_lineno = new_lineno;
- }
+ if (io == ARGF.current_file) {
+ ARGF.lineno += new_lineno - old_lineno;
+ ARGF.last_lineno = ARGF.lineno;
+ }
+ else {
+ ARGF.last_lineno = new_lineno;
+ }
}
return str;
@@ -4018,13 +4266,13 @@ rb_io_gets_internal(VALUE io)
/*
* call-seq:
- * gets(sep = $/, **line_opts) -> string or nil
- * gets(limit, **line_opts) -> string or nil
- * gets(sep, limit, **line_opts) -> string or nil
+ * gets(sep = $/, chomp: false) -> string or nil
+ * gets(limit, chomp: false) -> string or nil
+ * gets(sep, limit, chomp: false) -> string or nil
*
- * Reads and returns a line from the stream
- * (see {Lines}[rdoc-ref:IO@Lines]);
+ * Reads and returns a line from the stream;
* assigns the return value to <tt>$_</tt>.
+ * See {Line IO}[rdoc-ref:IO@Line+IO].
*
* With no arguments given, returns the next line
* as determined by line separator <tt>$/</tt>, or +nil+ if none:
@@ -4062,7 +4310,7 @@ rb_io_gets_internal(VALUE io)
*
* With only integer argument +limit+ given,
* limits the number of bytes in the line;
- * see {Line Limit}}[rdoc-ref:IO@Line+Limit]:
+ * see {Line Limit}[rdoc-ref:IO@Line+Limit]:
*
* # No more than one line.
* File.open('t.txt') {|f| f.gets(10) } # => "First line"
@@ -4076,8 +4324,8 @@ rb_io_gets_internal(VALUE io)
* or +nil+ if none.
* - But returns no more bytes than are allowed by the limit.
*
- * For all forms above, optional keyword arguments +line_opts+ specify
- * {Line Options}[rdoc-ref:IO@Line+Options]:
+ * Optional keyword argument +chomp+ specifies whether line separators
+ * are to be omitted:
*
* f = File.open('t.txt')
* # Chomp the lines.
@@ -4106,8 +4354,8 @@ rb_io_gets_m(int argc, VALUE *argv, VALUE io)
* call-seq:
* lineno -> integer
*
- * Returns the current line number for the stream.
- * See {Line Number}[rdoc-ref:IO@Line+Number].
+ * Returns the current line number for the stream;
+ * see {Line Number}[rdoc-ref:IO@Line+Number].
*
*/
@@ -4125,8 +4373,8 @@ rb_io_lineno(VALUE io)
* call-seq:
* lineno = integer -> integer
*
- * Sets and returns the line number for the stream.
- * See {Line Number}[rdoc-ref:IO@Line+Number].
+ * Sets and returns the line number for the stream;
+ * see {Line Number}[rdoc-ref:IO@Line+Number].
*
*/
@@ -4143,12 +4391,14 @@ rb_io_set_lineno(VALUE io, VALUE lineno)
/*
* call-seq:
- * readline(sep = $/, **line_opts) -> string
- * readline(limit, **line_opts) -> string
- * readline(sep, limit, **line_opts) -> string
+ * readline(sep = $/, chomp: false) -> string
+ * readline(limit, chomp: false) -> string
+ * readline(sep, limit, chomp: false) -> string
*
- * Reads a line as with IO#gets, but raises EOFError if already at end-of-file.
+ * Reads a line as with IO#gets, but raises EOFError if already at end-of-stream.
*
+ * Optional keyword argument +chomp+ specifies whether line separators
+ * are to be omitted.
*/
static VALUE
@@ -4157,7 +4407,7 @@ rb_io_readline(int argc, VALUE *argv, VALUE io)
VALUE line = rb_io_gets_m(argc, argv, io);
if (NIL_P(line)) {
- rb_eof_error();
+ rb_eof_error();
}
return line;
}
@@ -4166,13 +4416,13 @@ static VALUE io_readlines(const struct getline_arg *arg, VALUE io);
/*
* call-seq:
- * readlines(sep = $/, **line_opts) -> array
- * readlines(limit, **line_opts) -> array
- * readlines(sep, limit, **line_opts) -> array
+ * readlines(sep = $/, chomp: false) -> array
+ * readlines(limit, chomp: false) -> array
+ * readlines(sep, limit, chomp: false) -> array
*
- * Reads and returns all remaining line from the stream
- * (see {Lines}[rdoc-ref:IO@Lines]);
+ * Reads and returns all remaining line from the stream;
* does not modify <tt>$_</tt>.
+ * See {Line IO}[rdoc-ref:IO@Line+IO].
*
* With no arguments given, returns lines
* as determined by line separator <tt>$/</tt>, or +nil+ if none:
@@ -4220,8 +4470,8 @@ static VALUE io_readlines(const struct getline_arg *arg, VALUE io);
* - Returns lines as determined by line separator +sep+.
* - But returns no more bytes in a line than are allowed by the limit.
*
- * For all forms above, optional keyword arguments +line_opts+ specify
- * {Line Options}[rdoc-ref:IO@Line+Options]:
+ * Optional keyword argument +chomp+ specifies whether line separators
+ * are to be omitted:
*
* f = File.new('t.txt')
* f.readlines(chomp: true)
@@ -4245,25 +4495,25 @@ io_readlines(const struct getline_arg *arg, VALUE io)
VALUE line, ary;
if (arg->limit == 0)
- rb_raise(rb_eArgError, "invalid limit: 0 for readlines");
+ rb_raise(rb_eArgError, "invalid limit: 0 for readlines");
ary = rb_ary_new();
while (!NIL_P(line = rb_io_getline_1(arg->rs, arg->limit, arg->chomp, io))) {
- rb_ary_push(ary, line);
+ rb_ary_push(ary, line);
}
return ary;
}
/*
* call-seq:
- * each_line(sep = $/, **line_opts) {|line| ... } -> self
- * each_line(limit, **line_opts) {|line| ... } -> self
- * each_line(sep, limit, **line_opts) {|line| ... } -> self
+ * each_line(sep = $/, chomp: false) {|line| ... } -> self
+ * each_line(limit, chomp: false) {|line| ... } -> self
+ * each_line(sep, limit, chomp: false) {|line| ... } -> self
* each_line -> enumerator
*
- * Calls the block with each remaining line read from the stream
- * (see {Lines}[rdoc-ref:IO@Lines]);
- * does nothing if already at end-of-file;
+ * Calls the block with each remaining line read from the stream;
* returns +self+.
+ * Does nothing if already at end-of-stream;
+ * See {Line IO}[rdoc-ref:IO@Line+IO].
*
* With no arguments given, reads lines
* as determined by line separator <tt>$/</tt>:
@@ -4319,7 +4569,7 @@ io_readlines(const struct getline_arg *arg, VALUE io)
*
* With only integer argument +limit+ given,
* limits the number of bytes in each line;
- * see {Line Limit}}[rdoc-ref:IO@Line+Limit]:
+ * see {Line Limit}[rdoc-ref:IO@Line+Limit]:
*
* f = File.new('t.txt')
* f.each_line(8) {|line| p line }
@@ -4343,8 +4593,8 @@ io_readlines(const struct getline_arg *arg, VALUE io)
* - Calls with the next line as determined by line separator +sep+.
* - But returns no more bytes than are allowed by the limit.
*
- * For all forms above, optional keyword arguments +line_opts+ specify
- * {Line Options}[rdoc-ref:IO@Line+Options]:
+ * Optional keyword argument +chomp+ specifies whether line separators
+ * are to be omitted:
*
* f = File.new('t.txt')
* f.each_line(chomp: true) {|line| p line }
@@ -4373,9 +4623,9 @@ rb_io_each_line(int argc, VALUE *argv, VALUE io)
RETURN_ENUMERATOR(io, argc, argv);
prepare_getline_args(argc, argv, &args, io);
if (args.limit == 0)
- rb_raise(rb_eArgError, "invalid limit: 0 for each_line");
+ rb_raise(rb_eArgError, "invalid limit: 0 for each_line");
while (!NIL_P(str = rb_io_getline_1(args.rs, args.limit, args.chomp, io))) {
- rb_yield(str);
+ rb_yield(str);
}
return io;
}
@@ -4385,7 +4635,8 @@ rb_io_each_line(int argc, VALUE *argv, VALUE io)
* each_byte {|byte| ... } -> self
* each_byte -> enumerator
*
- * Calls the given block with each byte (0..255) in the stream; returns +self+:
+ * Calls the given block with each byte (0..255) in the stream; returns +self+.
+ * See {Byte IO}[rdoc-ref:IO@Byte+IO].
*
* f = File.new('t.rus')
* a = []
@@ -4408,14 +4659,14 @@ rb_io_each_byte(VALUE io)
GetOpenFile(io, fptr);
do {
- while (fptr->rbuf.len > 0) {
- char *p = fptr->rbuf.ptr + fptr->rbuf.off++;
- fptr->rbuf.len--;
- rb_yield(INT2FIX(*p & 0xff));
- rb_io_check_byte_readable(fptr);
- errno = 0;
- }
- READ_CHECK(fptr);
+ while (fptr->rbuf.len > 0) {
+ char *p = fptr->rbuf.ptr + fptr->rbuf.off++;
+ fptr->rbuf.len--;
+ rb_yield(INT2FIX(*p & 0xff));
+ rb_io_check_byte_readable(fptr);
+ errno = 0;
+ }
+ READ_CHECK(fptr);
} while (io_fillbuf(fptr) >= 0);
return io;
}
@@ -4427,17 +4678,17 @@ io_getc(rb_io_t *fptr, rb_encoding *enc)
VALUE str;
if (NEED_READCONV(fptr)) {
- rb_encoding *read_enc = io_read_encoding(fptr);
+ rb_encoding *read_enc = io_read_encoding(fptr);
- str = Qnil;
- SET_BINARY_MODE(fptr);
+ str = Qnil;
+ SET_BINARY_MODE(fptr);
make_readconv(fptr, 0);
while (1) {
if (fptr->cbuf.len) {
- r = rb_enc_precise_mbclen(fptr->cbuf.ptr+fptr->cbuf.off,
- fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
- read_enc);
+ r = rb_enc_precise_mbclen(fptr->cbuf.ptr+fptr->cbuf.off,
+ fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
+ read_enc);
if (!MBCLEN_NEEDMORE_P(r))
break;
if (fptr->cbuf.len == fptr->cbuf.capa) {
@@ -4447,16 +4698,16 @@ io_getc(rb_io_t *fptr, rb_encoding *enc)
if (more_char(fptr) == MORE_CHAR_FINISHED) {
if (fptr->cbuf.len == 0) {
- clear_readconv(fptr);
- return Qnil;
- }
+ clear_readconv(fptr);
+ return Qnil;
+ }
/* return an unit of an incomplete character just before EOF */
- str = rb_enc_str_new(fptr->cbuf.ptr+fptr->cbuf.off, 1, read_enc);
- fptr->cbuf.off += 1;
- fptr->cbuf.len -= 1;
+ str = rb_enc_str_new(fptr->cbuf.ptr+fptr->cbuf.off, 1, read_enc);
+ fptr->cbuf.off += 1;
+ fptr->cbuf.len -= 1;
if (fptr->cbuf.len == 0) clear_readconv(fptr);
- ENC_CODERANGE_SET(str, ENC_CODERANGE_BROKEN);
- return str;
+ ENC_CODERANGE_SET(str, ENC_CODERANGE_BROKEN);
+ return str;
}
}
if (MBCLEN_INVALID_P(r)) {
@@ -4464,62 +4715,62 @@ io_getc(rb_io_t *fptr, rb_encoding *enc)
fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
read_enc);
io_shift_cbuf(fptr, r, &str);
- cr = ENC_CODERANGE_BROKEN;
- }
- else {
- io_shift_cbuf(fptr, MBCLEN_CHARFOUND_LEN(r), &str);
- cr = ENC_CODERANGE_VALID;
- if (MBCLEN_CHARFOUND_LEN(r) == 1 && rb_enc_asciicompat(read_enc) &&
- ISASCII(RSTRING_PTR(str)[0])) {
- cr = ENC_CODERANGE_7BIT;
- }
- }
- str = io_enc_str(str, fptr);
- ENC_CODERANGE_SET(str, cr);
- return str;
+ cr = ENC_CODERANGE_BROKEN;
+ }
+ else {
+ io_shift_cbuf(fptr, MBCLEN_CHARFOUND_LEN(r), &str);
+ cr = ENC_CODERANGE_VALID;
+ if (MBCLEN_CHARFOUND_LEN(r) == 1 && rb_enc_asciicompat(read_enc) &&
+ ISASCII(RSTRING_PTR(str)[0])) {
+ cr = ENC_CODERANGE_7BIT;
+ }
+ }
+ str = io_enc_str(str, fptr);
+ ENC_CODERANGE_SET(str, cr);
+ return str;
}
NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
if (io_fillbuf(fptr) < 0) {
- return Qnil;
+ return Qnil;
}
if (rb_enc_asciicompat(enc) && ISASCII(fptr->rbuf.ptr[fptr->rbuf.off])) {
- str = rb_str_new(fptr->rbuf.ptr+fptr->rbuf.off, 1);
- fptr->rbuf.off += 1;
- fptr->rbuf.len -= 1;
- cr = ENC_CODERANGE_7BIT;
+ str = rb_str_new(fptr->rbuf.ptr+fptr->rbuf.off, 1);
+ fptr->rbuf.off += 1;
+ fptr->rbuf.len -= 1;
+ cr = ENC_CODERANGE_7BIT;
}
else {
- r = rb_enc_precise_mbclen(fptr->rbuf.ptr+fptr->rbuf.off, fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc);
- if (MBCLEN_CHARFOUND_P(r) &&
- (n = MBCLEN_CHARFOUND_LEN(r)) <= fptr->rbuf.len) {
- str = rb_str_new(fptr->rbuf.ptr+fptr->rbuf.off, n);
- fptr->rbuf.off += n;
- fptr->rbuf.len -= n;
- cr = ENC_CODERANGE_VALID;
- }
- else if (MBCLEN_NEEDMORE_P(r)) {
- str = rb_str_new(fptr->rbuf.ptr+fptr->rbuf.off, fptr->rbuf.len);
- fptr->rbuf.len = 0;
- getc_needmore:
- if (io_fillbuf(fptr) != -1) {
- rb_str_cat(str, fptr->rbuf.ptr+fptr->rbuf.off, 1);
- fptr->rbuf.off++;
- fptr->rbuf.len--;
- r = rb_enc_precise_mbclen(RSTRING_PTR(str), RSTRING_PTR(str)+RSTRING_LEN(str), enc);
- if (MBCLEN_NEEDMORE_P(r)) {
- goto getc_needmore;
- }
- else if (MBCLEN_CHARFOUND_P(r)) {
- cr = ENC_CODERANGE_VALID;
- }
- }
- }
- else {
- str = rb_str_new(fptr->rbuf.ptr+fptr->rbuf.off, 1);
- fptr->rbuf.off++;
- fptr->rbuf.len--;
- }
+ r = rb_enc_precise_mbclen(fptr->rbuf.ptr+fptr->rbuf.off, fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc);
+ if (MBCLEN_CHARFOUND_P(r) &&
+ (n = MBCLEN_CHARFOUND_LEN(r)) <= fptr->rbuf.len) {
+ str = rb_str_new(fptr->rbuf.ptr+fptr->rbuf.off, n);
+ fptr->rbuf.off += n;
+ fptr->rbuf.len -= n;
+ cr = ENC_CODERANGE_VALID;
+ }
+ else if (MBCLEN_NEEDMORE_P(r)) {
+ str = rb_str_new(fptr->rbuf.ptr+fptr->rbuf.off, fptr->rbuf.len);
+ fptr->rbuf.len = 0;
+ getc_needmore:
+ if (io_fillbuf(fptr) != -1) {
+ rb_str_cat(str, fptr->rbuf.ptr+fptr->rbuf.off, 1);
+ fptr->rbuf.off++;
+ fptr->rbuf.len--;
+ r = rb_enc_precise_mbclen(RSTRING_PTR(str), RSTRING_PTR(str)+RSTRING_LEN(str), enc);
+ if (MBCLEN_NEEDMORE_P(r)) {
+ goto getc_needmore;
+ }
+ else if (MBCLEN_CHARFOUND_P(r)) {
+ cr = ENC_CODERANGE_VALID;
+ }
+ }
+ }
+ else {
+ str = rb_str_new(fptr->rbuf.ptr+fptr->rbuf.off, 1);
+ fptr->rbuf.off++;
+ fptr->rbuf.len--;
+ }
}
if (!cr) cr = ENC_CODERANGE_BROKEN;
str = io_enc_str(str, fptr);
@@ -4532,7 +4783,8 @@ io_getc(rb_io_t *fptr, rb_encoding *enc)
* each_char {|c| ... } -> self
* each_char -> enumerator
*
- * Calls the given block with each character in the stream; returns +self+:
+ * Calls the given block with each character in the stream; returns +self+.
+ * See {Character IO}[rdoc-ref:IO@Character+IO].
*
* f = File.new('t.rus')
* a = []
@@ -4598,87 +4850,87 @@ rb_io_each_codepoint(VALUE io)
READ_CHECK(fptr);
if (NEED_READCONV(fptr)) {
- SET_BINARY_MODE(fptr);
- r = 1; /* no invalid char yet */
- for (;;) {
- make_readconv(fptr, 0);
- for (;;) {
- if (fptr->cbuf.len) {
- if (fptr->encs.enc)
- r = rb_enc_precise_mbclen(fptr->cbuf.ptr+fptr->cbuf.off,
- fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
- fptr->encs.enc);
- else
- r = ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(1);
- if (!MBCLEN_NEEDMORE_P(r))
- break;
- if (fptr->cbuf.len == fptr->cbuf.capa) {
- rb_raise(rb_eIOError, "too long character");
- }
- }
- if (more_char(fptr) == MORE_CHAR_FINISHED) {
+ SET_BINARY_MODE(fptr);
+ r = 1; /* no invalid char yet */
+ for (;;) {
+ make_readconv(fptr, 0);
+ for (;;) {
+ if (fptr->cbuf.len) {
+ if (fptr->encs.enc)
+ r = rb_enc_precise_mbclen(fptr->cbuf.ptr+fptr->cbuf.off,
+ fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
+ fptr->encs.enc);
+ else
+ r = ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(1);
+ if (!MBCLEN_NEEDMORE_P(r))
+ break;
+ if (fptr->cbuf.len == fptr->cbuf.capa) {
+ rb_raise(rb_eIOError, "too long character");
+ }
+ }
+ if (more_char(fptr) == MORE_CHAR_FINISHED) {
clear_readconv(fptr);
- if (!MBCLEN_CHARFOUND_P(r)) {
- enc = fptr->encs.enc;
- goto invalid;
- }
- return io;
- }
- }
- if (MBCLEN_INVALID_P(r)) {
- enc = fptr->encs.enc;
- goto invalid;
- }
- n = MBCLEN_CHARFOUND_LEN(r);
- if (fptr->encs.enc) {
- c = rb_enc_codepoint(fptr->cbuf.ptr+fptr->cbuf.off,
- fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
- fptr->encs.enc);
- }
- else {
- c = (unsigned char)fptr->cbuf.ptr[fptr->cbuf.off];
- }
- fptr->cbuf.off += n;
- fptr->cbuf.len -= n;
- rb_yield(UINT2NUM(c));
+ if (!MBCLEN_CHARFOUND_P(r)) {
+ enc = fptr->encs.enc;
+ goto invalid;
+ }
+ return io;
+ }
+ }
+ if (MBCLEN_INVALID_P(r)) {
+ enc = fptr->encs.enc;
+ goto invalid;
+ }
+ n = MBCLEN_CHARFOUND_LEN(r);
+ if (fptr->encs.enc) {
+ c = rb_enc_codepoint(fptr->cbuf.ptr+fptr->cbuf.off,
+ fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
+ fptr->encs.enc);
+ }
+ else {
+ c = (unsigned char)fptr->cbuf.ptr[fptr->cbuf.off];
+ }
+ fptr->cbuf.off += n;
+ fptr->cbuf.len -= n;
+ rb_yield(UINT2NUM(c));
rb_io_check_byte_readable(fptr);
- }
+ }
}
NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
enc = io_input_encoding(fptr);
while (io_fillbuf(fptr) >= 0) {
- r = rb_enc_precise_mbclen(fptr->rbuf.ptr+fptr->rbuf.off,
- fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc);
- if (MBCLEN_CHARFOUND_P(r) &&
- (n = MBCLEN_CHARFOUND_LEN(r)) <= fptr->rbuf.len) {
- c = rb_enc_codepoint(fptr->rbuf.ptr+fptr->rbuf.off,
- fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc);
- fptr->rbuf.off += n;
- fptr->rbuf.len -= n;
- rb_yield(UINT2NUM(c));
- }
- else if (MBCLEN_INVALID_P(r)) {
+ r = rb_enc_precise_mbclen(fptr->rbuf.ptr+fptr->rbuf.off,
+ fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc);
+ if (MBCLEN_CHARFOUND_P(r) &&
+ (n = MBCLEN_CHARFOUND_LEN(r)) <= fptr->rbuf.len) {
+ c = rb_enc_codepoint(fptr->rbuf.ptr+fptr->rbuf.off,
+ fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc);
+ fptr->rbuf.off += n;
+ fptr->rbuf.len -= n;
+ rb_yield(UINT2NUM(c));
+ }
+ else if (MBCLEN_INVALID_P(r)) {
goto invalid;
- }
- else if (MBCLEN_NEEDMORE_P(r)) {
- char cbuf[8], *p = cbuf;
- int more = MBCLEN_NEEDMORE_LEN(r);
- if (more > numberof(cbuf)) goto invalid;
- more += n = fptr->rbuf.len;
- if (more > numberof(cbuf)) goto invalid;
- while ((n = (int)read_buffered_data(p, more, fptr)) > 0 &&
- (p += n, (more -= n) > 0)) {
- if (io_fillbuf(fptr) < 0) goto invalid;
- if ((n = fptr->rbuf.len) > more) n = more;
- }
- r = rb_enc_precise_mbclen(cbuf, p, enc);
- if (!MBCLEN_CHARFOUND_P(r)) goto invalid;
- c = rb_enc_codepoint(cbuf, p, enc);
- rb_yield(UINT2NUM(c));
- }
- else {
- continue;
- }
+ }
+ else if (MBCLEN_NEEDMORE_P(r)) {
+ char cbuf[8], *p = cbuf;
+ int more = MBCLEN_NEEDMORE_LEN(r);
+ if (more > numberof(cbuf)) goto invalid;
+ more += n = fptr->rbuf.len;
+ if (more > numberof(cbuf)) goto invalid;
+ while ((n = (int)read_buffered_data(p, more, fptr)) > 0 &&
+ (p += n, (more -= n) > 0)) {
+ if (io_fillbuf(fptr) < 0) goto invalid;
+ if ((n = fptr->rbuf.len) > more) n = more;
+ }
+ r = rb_enc_precise_mbclen(cbuf, p, enc);
+ if (!MBCLEN_CHARFOUND_P(r)) goto invalid;
+ c = rb_enc_codepoint(cbuf, p, enc);
+ rb_yield(UINT2NUM(c));
+ }
+ else {
+ continue;
+ }
rb_io_check_byte_readable(fptr);
}
return io;
@@ -4693,7 +4945,8 @@ rb_io_each_codepoint(VALUE io)
* getc -> character or nil
*
* Reads and returns the next 1-character string from the stream;
- * returns +nil+ if already at end-of-file:
+ * returns +nil+ if already at end-of-stream.
+ * See {Character IO}[rdoc-ref:IO@Character+IO].
*
* f = File.open('t.txt')
* f.getc # => "F"
@@ -4725,7 +4978,8 @@ rb_io_getc(VALUE io)
* readchar -> string
*
* Reads and returns the next 1-character string from the stream;
- * raises EOFError if already at end-of-file:
+ * raises EOFError if already at end-of-stream.
+ * See {Character IO}[rdoc-ref:IO@Character+IO].
*
* f = File.open('t.txt')
* f.readchar # => "F"
@@ -4744,7 +4998,7 @@ rb_io_readchar(VALUE io)
VALUE c = rb_io_getc(io);
if (NIL_P(c)) {
- rb_eof_error();
+ rb_eof_error();
}
return c;
}
@@ -4754,7 +5008,8 @@ rb_io_readchar(VALUE io)
* getbyte -> integer or nil
*
* Reads and returns the next byte (in range 0..255) from the stream;
- * returns +nil+ if already at end-of-file:
+ * returns +nil+ if already at end-of-stream.
+ * See {Byte IO}[rdoc-ref:IO@Byte+IO].
*
* f = File.open('t.txt')
* f.getbyte # => 70
@@ -4764,7 +5019,6 @@ rb_io_readchar(VALUE io)
* f.close
*
* Related: IO#readbyte (may raise EOFError).
- *
*/
VALUE
@@ -4785,7 +5039,7 @@ rb_io_getbyte(VALUE io)
}
}
if (io_fillbuf(fptr) < 0) {
- return Qnil;
+ return Qnil;
}
fptr->rbuf.off++;
fptr->rbuf.len--;
@@ -4798,7 +5052,8 @@ rb_io_getbyte(VALUE io)
* readbyte -> integer
*
* Reads and returns the next byte (in range 0..255) from the stream;
- * raises EOFError if already at end-of-file:
+ * raises EOFError if already at end-of-stream.
+ * See {Byte IO}[rdoc-ref:IO@Byte+IO].
*
* f = File.open('t.txt')
* f.readbyte # => 70
@@ -4817,7 +5072,7 @@ rb_io_readbyte(VALUE io)
VALUE c = rb_io_getbyte(io);
if (NIL_P(c)) {
- rb_eof_error();
+ rb_eof_error();
}
return c;
}
@@ -4829,10 +5084,11 @@ rb_io_readbyte(VALUE io)
*
* Pushes back ("unshifts") the given data onto the stream's buffer,
* placing the data so that it is next to be read; returns +nil+.
+ * See {Byte IO}[rdoc-ref:IO@Byte+IO].
*
* Note that:
*
- * - Calling the method hs no effect with unbuffered reads (such as IO#sysread).
+ * - Calling the method has no effect with unbuffered reads (such as IO#sysread).
* - Calling #rewind on the stream discards the pushed-back data.
*
* When argument +integer+ is given, uses only its low-order byte:
@@ -4889,10 +5145,11 @@ rb_io_ungetbyte(VALUE io, VALUE b)
*
* Pushes back ("unshifts") the given data onto the stream's buffer,
* placing the data so that it is next to be read; returns +nil+.
+ * See {Character IO}[rdoc-ref:IO@Character+IO].
*
* Note that:
*
- * - Calling the method hs no effect with unbuffered reads (such as IO#sysread).
+ * - Calling the method has no effect with unbuffered reads (such as IO#sysread).
* - Calling #rewind on the stream discards the pushed-back data.
*
* When argument +integer+ is given, interprets the integer as a character:
@@ -4931,20 +5188,20 @@ rb_io_ungetc(VALUE io, VALUE c)
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
if (FIXNUM_P(c)) {
- c = rb_enc_uint_chr(FIX2UINT(c), io_read_encoding(fptr));
+ c = rb_enc_uint_chr(FIX2UINT(c), io_read_encoding(fptr));
}
else if (RB_BIGNUM_TYPE_P(c)) {
- c = rb_enc_uint_chr(NUM2UINT(c), io_read_encoding(fptr));
+ c = rb_enc_uint_chr(NUM2UINT(c), io_read_encoding(fptr));
}
else {
- SafeStringValue(c);
+ SafeStringValue(c);
}
if (NEED_READCONV(fptr)) {
- SET_BINARY_MODE(fptr);
+ SET_BINARY_MODE(fptr);
len = RSTRING_LEN(c);
#if SIZEOF_LONG > SIZEOF_INT
- if (len > INT_MAX)
- rb_raise(rb_eIOError, "ungetc failed");
+ if (len > INT_MAX)
+ rb_raise(rb_eIOError, "ungetc failed");
#endif
make_readconv(fptr, (int)len);
if (fptr->cbuf.capa - fptr->cbuf.len < len)
@@ -4960,7 +5217,7 @@ rb_io_ungetc(VALUE io, VALUE c)
MEMMOVE(fptr->cbuf.ptr+fptr->cbuf.off, RSTRING_PTR(c), char, len);
}
else {
- NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
+ NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
io_ungetbyte(c, fptr);
}
return Qnil;
@@ -4973,8 +5230,10 @@ rb_io_ungetc(VALUE io, VALUE c)
* Returns +true+ if the stream is associated with a terminal device (tty),
* +false+ otherwise:
*
- * File.new('t.txt').isatty #=> false
- * File.new('/dev/tty').isatty #=> true
+ * f = File.new('t.txt').isatty #=> false
+ * f.close
+ * f = File.new('/dev/tty').isatty #=> true
+ * f.close
*
* IO#tty? is an alias for IO#isatty.
*
@@ -5108,13 +5367,13 @@ finish_writeconv(rb_io_t *fptr, int noalloc)
res = rb_econv_convert(fptr->writeconv, NULL, NULL, &dp, de, 0);
while (dp-ds) {
size_t remaining = dp-ds;
- long result = rb_write_internal(fptr, ds, remaining);
+ long result = rb_io_write_memory(fptr, ds, remaining);
if (result > 0) {
ds += result;
if ((size_t)result == remaining) break;
}
- else if (rb_io_maybe_wait_writable(errno, fptr->self, Qnil)) {
+ else if (rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
if (fptr->fd < 0)
return noalloc ? Qtrue : rb_exc_new3(rb_eIOError, rb_str_new_cstr(closed_stream));
}
@@ -5259,7 +5518,7 @@ fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl,
// VALUE scheduler = rb_fiber_scheduler_current();
// if (scheduler != Qnil) {
// VALUE result = rb_fiber_scheduler_io_close(scheduler, fptr->self);
- // if (result != Qundef) done = 1;
+ // if (!UNDEF_P(result)) done = 1;
// }
// }
@@ -5310,10 +5569,10 @@ static void
rb_io_fptr_cleanup(rb_io_t *fptr, int noraise)
{
if (fptr->finalize) {
- (*fptr->finalize)(fptr, noraise);
+ (*fptr->finalize)(fptr, noraise);
}
else {
- fptr_finalize(fptr, noraise);
+ fptr_finalize(fptr, noraise);
}
}
@@ -5455,12 +5714,32 @@ rb_io_close(VALUE io)
* call-seq:
* close -> nil
*
- * Closes the stream, if it is open, after flushing any buffered writes
- * to the operating system; does nothing if the stream is already closed.
- * A stream is automatically closed when claimed by the garbage collector.
+ * Closes the stream for both reading and writing
+ * if open for either or both; returns +nil+.
+ * See {Open and Closed Streams}[rdoc-ref:IO@Open+and+Closed+Streams].
+ *
+ * If the stream is open for writing, flushes any buffered writes
+ * to the operating system before closing.
+ *
+ * If the stream was opened by IO.popen, sets global variable <tt>$?</tt>
+ * (child exit status).
+ *
+ * Example:
+ *
+ * IO.popen('ruby', 'r+') do |pipe|
+ * puts pipe.closed?
+ * pipe.close
+ * puts $?
+ * puts pipe.closed?
+ * end
*
- * If the stream was opened by IO.popen, #close sets global variable <tt>$?</tt>.
+ * Output:
*
+ * false
+ * pid 13760 exit 0
+ * true
+ *
+ * Related: IO#close_read, IO#close_write, IO#closed?.
*/
static VALUE
@@ -5487,9 +5766,9 @@ ignore_closed_stream(VALUE io, VALUE exc)
enum {mesg_len = sizeof(closed_stream)-1};
VALUE mesg = rb_attr_get(exc, idMesg);
if (!RB_TYPE_P(mesg, T_STRING) ||
- RSTRING_LEN(mesg) != mesg_len ||
- memcmp(RSTRING_PTR(mesg), closed_stream, mesg_len)) {
- rb_exc_raise(exc);
+ RSTRING_LEN(mesg) != mesg_len ||
+ memcmp(RSTRING_PTR(mesg), closed_stream, mesg_len)) {
+ rb_exc_raise(exc);
}
return io;
}
@@ -5498,9 +5777,9 @@ static VALUE
io_close(VALUE io)
{
VALUE closed = rb_check_funcall(io, rb_intern("closed?"), 0, 0);
- if (closed != Qundef && RTEST(closed)) return io;
+ if (!UNDEF_P(closed) && RTEST(closed)) return io;
rb_rescue2(io_call_close, io, ignore_closed_stream, io,
- rb_eIOError, (VALUE)0);
+ rb_eIOError, (VALUE)0);
return io;
}
@@ -5509,17 +5788,24 @@ io_close(VALUE io)
* closed? -> true or false
*
* Returns +true+ if the stream is closed for both reading and writing,
- * +false+ otherwise:
+ * +false+ otherwise.
+ * See {Open and Closed Streams}[rdoc-ref:IO@Open+and+Closed+Streams].
*
- * f = File.new('t.txt')
- * f.close # => nil
- * f.closed? # => true
- * f = IO.popen('/bin/sh','r+')
- * f.close_write # => nil
- * f.closed? # => false
- * f.close_read # => nil
- * f.closed? # => true
+ * IO.popen('ruby', 'r+') do |pipe|
+ * puts pipe.closed?
+ * pipe.close_read
+ * puts pipe.closed?
+ * pipe.close_write
+ * puts pipe.closed?
+ * end
+ *
+ * Output:
*
+ * false
+ * false
+ * true
+ *
+ * Related: IO#close_read, IO#close_write, IO#close.
*/
@@ -5546,15 +5832,32 @@ rb_io_closed(VALUE io)
* call-seq:
* close_read -> nil
*
- * Closes the read end of a duplexed stream (i.e., one that is both readable
- * and writable, such as a pipe); does nothing if already closed:
+ * Closes the stream for reading if open for reading;
+ * returns +nil+.
+ * See {Open and Closed Streams}[rdoc-ref:IO@Open+and+Closed+Streams].
*
- * f = IO.popen('/bin/sh','r+')
- * f.close_read
- * f.readlines # Raises IOError
+ * If the stream was opened by IO.popen and is also closed for writing,
+ * sets global variable <tt>$?</tt> (child exit status).
*
- * Raises an exception if the stream is not duplexed.
+ * Example:
*
+ * IO.popen('ruby', 'r+') do |pipe|
+ * puts pipe.closed?
+ * pipe.close_write
+ * puts pipe.closed?
+ * pipe.close_read
+ * puts $?
+ * puts pipe.closed?
+ * end
+ *
+ * Output:
+ *
+ * false
+ * false
+ * pid 14748 exit 0
+ * true
+ *
+ * Related: IO#close, IO#close_write, IO#closed?.
*/
static VALUE
@@ -5579,21 +5882,21 @@ rb_io_close_read(VALUE io)
write_io = GetWriteIO(io);
if (io != write_io) {
- rb_io_t *wfptr;
- wfptr = rb_io_get_fptr(rb_io_taint_check(write_io));
- wfptr->pid = fptr->pid;
- fptr->pid = 0;
+ rb_io_t *wfptr;
+ wfptr = rb_io_get_fptr(rb_io_taint_check(write_io));
+ wfptr->pid = fptr->pid;
+ fptr->pid = 0;
RFILE(io)->fptr = wfptr;
- /* bind to write_io temporarily to get rid of memory/fd leak */
- fptr->tied_io_for_writing = 0;
- RFILE(write_io)->fptr = fptr;
- rb_io_fptr_cleanup(fptr, FALSE);
- /* should not finalize fptr because another thread may be reading it */
+ /* bind to write_io temporarily to get rid of memory/fd leak */
+ fptr->tied_io_for_writing = 0;
+ RFILE(write_io)->fptr = fptr;
+ rb_io_fptr_cleanup(fptr, FALSE);
+ /* should not finalize fptr because another thread may be reading it */
return Qnil;
}
if ((fptr->mode & (FMODE_DUPLEX|FMODE_WRITABLE)) == FMODE_WRITABLE) {
- rb_raise(rb_eIOError, "closing non-duplex IO for reading");
+ rb_raise(rb_eIOError, "closing non-duplex IO for reading");
}
return rb_io_close(io);
}
@@ -5602,13 +5905,32 @@ rb_io_close_read(VALUE io)
* call-seq:
* close_write -> nil
*
- * Closes the write end of a duplexed stream (i.e., one that is both readable
- * and writable, such as a pipe); does nothing if already closed:
+ * Closes the stream for writing if open for writing;
+ * returns +nil+.
+ * See {Open and Closed Streams}[rdoc-ref:IO@Open+and+Closed+Streams].
*
- * f = IO.popen('/bin/sh', 'r+')
- * f.close_write
- * f.print 'nowhere' # Raises IOError.
+ * Flushes any buffered writes to the operating system before closing.
*
+ * If the stream was opened by IO.popen and is also closed for reading,
+ * sets global variable <tt>$?</tt> (child exit status).
+ *
+ * IO.popen('ruby', 'r+') do |pipe|
+ * puts pipe.closed?
+ * pipe.close_read
+ * puts pipe.closed?
+ * pipe.close_write
+ * puts $?
+ * puts pipe.closed?
+ * end
+ *
+ * Output:
+ *
+ * false
+ * false
+ * pid 15044 exit 0
+ * true
+ *
+ * Related: IO#close, IO#close_read, IO#closed?.
*/
static VALUE
@@ -5628,17 +5950,17 @@ rb_io_close_write(VALUE io)
rb_sys_fail_path(fptr->pathv);
fptr->mode &= ~FMODE_WRITABLE;
if (!(fptr->mode & FMODE_READABLE))
- return rb_io_close(write_io);
+ return rb_io_close(write_io);
return Qnil;
}
if ((fptr->mode & (FMODE_DUPLEX|FMODE_READABLE)) == FMODE_READABLE) {
- rb_raise(rb_eIOError, "closing non-duplex IO for writing");
+ rb_raise(rb_eIOError, "closing non-duplex IO for writing");
}
if (io != write_io) {
- fptr = rb_io_get_fptr(rb_io_taint_check(io));
- fptr->tied_io_for_writing = 0;
+ fptr = rb_io_get_fptr(rb_io_taint_check(io));
+ fptr->tied_io_for_writing = 0;
}
rb_io_close(write_io);
return Qnil;
@@ -5661,19 +5983,19 @@ rb_io_sysseek(int argc, VALUE *argv, VALUE io)
VALUE offset, ptrname;
int whence = SEEK_SET;
rb_io_t *fptr;
- off_t pos;
+ rb_off_t pos;
if (rb_scan_args(argc, argv, "11", &offset, &ptrname) == 2) {
- whence = interpret_seek_whence(ptrname);
+ whence = interpret_seek_whence(ptrname);
}
pos = NUM2OFFT(offset);
GetOpenFile(io, fptr);
if ((fptr->mode & FMODE_READABLE) &&
(READ_DATA_BUFFERED(fptr) || READ_CHAR_PENDING(fptr))) {
- rb_raise(rb_eIOError, "sysseek for buffered IO");
+ rb_raise(rb_eIOError, "sysseek for buffered IO");
}
if ((fptr->mode & FMODE_WRITABLE) && fptr->wbuf.len) {
- rb_warn("sysseek for buffered IO");
+ rb_warn("sysseek for buffered IO");
}
errno = 0;
pos = lseek(fptr->fd, pos, whence);
@@ -5709,19 +6031,19 @@ rb_io_syswrite(VALUE io, VALUE str)
const char *ptr;
if (!RB_TYPE_P(str, T_STRING))
- str = rb_obj_as_string(str);
+ str = rb_obj_as_string(str);
io = GetWriteIO(io);
GetOpenFile(io, fptr);
rb_io_check_writable(fptr);
if (fptr->wbuf.len) {
- rb_warn("syswrite for buffered IO");
+ rb_warn("syswrite for buffered IO");
}
tmp = rb_str_tmp_frozen_acquire(str);
RSTRING_GETMEM(tmp, ptr, len);
- n = rb_write_internal(fptr, ptr, len);
+ n = rb_io_write_memory(fptr, ptr, len);
if (n < 0) rb_sys_fail_path(fptr->pathv);
rb_str_tmp_frozen_release(str, tmp);
@@ -5767,9 +6089,11 @@ rb_io_sysread(int argc, VALUE *argv, VALUE io)
iis.th = rb_thread_current();
iis.fptr = fptr;
iis.nonblock = 0;
+ iis.fd = fptr->fd;
iis.buf = RSTRING_PTR(str);
iis.capa = ilen;
- n = read_internal_locktmp(str, &iis);
+ iis.timeout = NULL;
+ n = io_read_memory_locktmp(str, &iis);
if (n < 0) {
rb_sys_fail_path(fptr->pathv);
@@ -5789,7 +6113,7 @@ struct prdwr_internal_arg {
int fd;
void *buf;
size_t count;
- off_t offset;
+ rb_off_t offset;
};
#endif /* HAVE_PREAD || HAVE_PWRITE */
@@ -5863,11 +6187,11 @@ rb_io_pread(int argc, VALUE *argv, VALUE io)
n = (ssize_t)rb_ensure(pread_internal_call, (VALUE)&arg, rb_str_unlocktmp, str);
if (n < 0) {
- rb_sys_fail_path(fptr->pathv);
+ rb_sys_fail_path(fptr->pathv);
}
io_set_read_length(str, n, shrinkable);
if (n == 0 && arg.count > 0) {
- rb_eof_error();
+ rb_eof_error();
}
return str;
@@ -5919,7 +6243,7 @@ rb_io_pwrite(VALUE io, VALUE str, VALUE offset)
VALUE tmp;
if (!RB_TYPE_P(str, T_STRING))
- str = rb_obj_as_string(str);
+ str = rb_obj_as_string(str);
arg.offset = NUM2OFFT(offset);
@@ -5957,10 +6281,10 @@ rb_io_binmode(VALUE io)
fptr->writeconv_pre_ecflags &= ~ECONV_NEWLINE_DECORATOR_MASK;
#ifdef O_BINARY
if (!fptr->readconv) {
- SET_BINARY_MODE_WITH_SEEK_CUR(fptr);
+ SET_BINARY_MODE_WITH_SEEK_CUR(fptr);
}
else {
- setmode(fptr->fd, O_BINARY);
+ setmode(fptr->fd, O_BINARY);
}
#endif
return io;
@@ -6004,7 +6328,7 @@ rb_io_ascii8bit_binmode(VALUE io)
* binmode -> self
*
* Sets the stream's data mode as binary
- * (see {Data Mode}[rdoc-ref:IO@Data+Mode]).
+ * (see {Data Mode}[rdoc-ref:File@Data+Mode]).
*
* A stream's data mode may not be changed from binary to text.
*
@@ -6028,7 +6352,7 @@ rb_io_binmode_m(VALUE io)
* binmode? -> true or false
*
* Returns +true+ if the stream is on binary mode, +false+ otherwise.
- * See {Data Mode}[rdoc-ref:IO@Data+Mode].
+ * See {Data Mode}[rdoc-ref:File@Data+Mode].
*
*/
static VALUE
@@ -6043,23 +6367,23 @@ static const char*
rb_io_fmode_modestr(int fmode)
{
if (fmode & FMODE_APPEND) {
- if ((fmode & FMODE_READWRITE) == FMODE_READWRITE) {
- return MODE_BTMODE("a+", "ab+", "at+");
- }
- return MODE_BTMODE("a", "ab", "at");
+ if ((fmode & FMODE_READWRITE) == FMODE_READWRITE) {
+ return MODE_BTMODE("a+", "ab+", "at+");
+ }
+ return MODE_BTMODE("a", "ab", "at");
}
switch (fmode & FMODE_READWRITE) {
default:
- rb_raise(rb_eArgError, "invalid access fmode 0x%x", fmode);
+ rb_raise(rb_eArgError, "invalid access fmode 0x%x", fmode);
case FMODE_READABLE:
- return MODE_BTMODE("r", "rb", "rt");
+ return MODE_BTMODE("r", "rb", "rt");
case FMODE_WRITABLE:
- return MODE_BTXMODE("w", "wb", "wt", "wx", "wbx", "wtx");
+ return MODE_BTXMODE("w", "wb", "wt", "wx", "wbx", "wtx");
case FMODE_READWRITE:
- if (fmode & FMODE_CREATE) {
+ if (fmode & FMODE_CREATE) {
return MODE_BTXMODE("w+", "wb+", "wt+", "w+x", "wb+x", "wt+x");
- }
- return MODE_BTMODE("r+", "rb+", "rt+");
+ }
+ return MODE_BTMODE("r+", "rb+", "rt+");
}
}
@@ -6082,27 +6406,27 @@ rb_io_modestr_fmode(const char *modestr)
switch (*m++) {
case 'r':
- fmode |= FMODE_READABLE;
- break;
+ fmode |= FMODE_READABLE;
+ break;
case 'w':
- fmode |= FMODE_WRITABLE | FMODE_TRUNC | FMODE_CREATE;
- break;
+ fmode |= FMODE_WRITABLE | FMODE_TRUNC | FMODE_CREATE;
+ break;
case 'a':
- fmode |= FMODE_WRITABLE | FMODE_APPEND | FMODE_CREATE;
- break;
+ fmode |= FMODE_WRITABLE | FMODE_APPEND | FMODE_CREATE;
+ break;
default:
goto error;
}
while (*m) {
switch (*m++) {
- case 'b':
+ case 'b':
fmode |= FMODE_BINMODE;
break;
- case 't':
+ case 't':
fmode |= FMODE_TEXTMODE;
break;
- case '+':
+ case '+':
fmode |= FMODE_READWRITE;
break;
case 'x':
@@ -6110,12 +6434,12 @@ rb_io_modestr_fmode(const char *modestr)
goto error;
fmode |= FMODE_EXCL;
break;
- default:
+ default:
goto error;
- case ':':
- p = strchr(m, ':');
- if (io_encname_bom_p(m, p ? (long)(p - m) : (long)strlen(m)))
- fmode |= FMODE_SETENC_BY_BOM;
+ case ':':
+ p = strchr(m, ':');
+ if (io_encname_bom_p(m, p ? (long)(p - m) : (long)strlen(m)))
+ fmode |= FMODE_SETENC_BY_BOM;
goto finished;
}
}
@@ -6138,31 +6462,31 @@ rb_io_oflags_fmode(int oflags)
switch (oflags & O_ACCMODE) {
case O_RDONLY:
- fmode = FMODE_READABLE;
- break;
+ fmode = FMODE_READABLE;
+ break;
case O_WRONLY:
- fmode = FMODE_WRITABLE;
- break;
+ fmode = FMODE_WRITABLE;
+ break;
case O_RDWR:
- fmode = FMODE_READWRITE;
- break;
+ fmode = FMODE_READWRITE;
+ break;
}
if (oflags & O_APPEND) {
- fmode |= FMODE_APPEND;
+ fmode |= FMODE_APPEND;
}
if (oflags & O_TRUNC) {
- fmode |= FMODE_TRUNC;
+ fmode |= FMODE_TRUNC;
}
if (oflags & O_CREAT) {
- fmode |= FMODE_CREATE;
+ fmode |= FMODE_CREATE;
}
if (oflags & O_EXCL) {
fmode |= FMODE_EXCL;
}
#ifdef O_BINARY
if (oflags & O_BINARY) {
- fmode |= FMODE_BINMODE;
+ fmode |= FMODE_BINMODE;
}
#endif
@@ -6227,25 +6551,25 @@ rb_io_oflags_modestr(int oflags)
}
accmode = oflags & (O_RDONLY|O_WRONLY|O_RDWR);
if (oflags & O_APPEND) {
- if (accmode == O_WRONLY) {
- return MODE_BINARY("a", "ab");
- }
- if (accmode == O_RDWR) {
- return MODE_BINARY("a+", "ab+");
- }
+ if (accmode == O_WRONLY) {
+ return MODE_BINARY("a", "ab");
+ }
+ if (accmode == O_RDWR) {
+ return MODE_BINARY("a+", "ab+");
+ }
}
switch (accmode) {
default:
- rb_raise(rb_eArgError, "invalid access oflags 0x%x", oflags);
+ rb_raise(rb_eArgError, "invalid access oflags 0x%x", oflags);
case O_RDONLY:
- return MODE_BINARY("r", "rb");
+ return MODE_BINARY("r", "rb");
case O_WRONLY:
- return MODE_BINARY("w", "wb");
+ return MODE_BINARY("w", "wb");
case O_RDWR:
- if (oflags & O_TRUNC) {
- return MODE_BINARY("w+", "wb+");
- }
- return MODE_BINARY("r+", "rb+");
+ if (oflags & O_TRUNC) {
+ return MODE_BINARY("w+", "wb+");
+ }
+ return MODE_BINARY("r+", "rb+");
}
}
@@ -6260,25 +6584,25 @@ rb_io_ext_int_to_encs(rb_encoding *ext, rb_encoding *intern, rb_encoding **enc,
int default_ext = 0;
if (ext == NULL) {
- ext = rb_default_external_encoding();
- default_ext = 1;
+ ext = rb_default_external_encoding();
+ default_ext = 1;
}
- if (ext == rb_ascii8bit_encoding()) {
- /* If external is ASCII-8BIT, no transcoding */
- intern = NULL;
+ if (rb_is_ascii8bit_enc(ext)) {
+ /* If external is ASCII-8BIT, no transcoding */
+ intern = NULL;
}
else if (intern == NULL) {
- intern = rb_default_internal_encoding();
+ intern = rb_default_internal_encoding();
}
if (intern == NULL || intern == (rb_encoding *)Qnil ||
- (!(fmode & FMODE_SETENC_BY_BOM) && (intern == ext))) {
- /* No internal encoding => use external + no transcoding */
- *enc = (default_ext && intern != ext) ? NULL : ext;
- *enc2 = NULL;
+ (!(fmode & FMODE_SETENC_BY_BOM) && (intern == ext))) {
+ /* No internal encoding => use external + no transcoding */
+ *enc = (default_ext && intern != ext) ? NULL : ext;
+ *enc2 = NULL;
}
else {
- *enc = intern;
- *enc2 = ext;
+ *enc = intern;
+ *enc2 = ext;
}
}
@@ -6290,7 +6614,7 @@ unsupported_encoding(const char *name, rb_encoding *enc)
static void
parse_mode_enc(const char *estr, rb_encoding *estr_enc,
- rb_encoding **enc_p, rb_encoding **enc2_p, int *fmode_p)
+ rb_encoding **enc_p, rb_encoding **enc2_p, int *fmode_p)
{
const char *p;
char encname[ENCODING_MAXNAMELEN+1];
@@ -6304,53 +6628,53 @@ parse_mode_enc(const char *estr, rb_encoding *estr_enc,
p = strrchr(estr, ':');
len = p ? (p++ - estr) : (long)strlen(estr);
if ((fmode & FMODE_SETENC_BY_BOM) || io_encname_bom_p(estr, len)) {
- estr += bom_prefix_len;
- len -= bom_prefix_len;
- if (!STRNCASECMP(estr, utf_prefix, utf_prefix_len)) {
- fmode |= FMODE_SETENC_BY_BOM;
- }
- else {
- rb_enc_warn(estr_enc, "BOM with non-UTF encoding %s is nonsense", estr);
- fmode &= ~FMODE_SETENC_BY_BOM;
- }
+ estr += bom_prefix_len;
+ len -= bom_prefix_len;
+ if (!STRNCASECMP(estr, utf_prefix, utf_prefix_len)) {
+ fmode |= FMODE_SETENC_BY_BOM;
+ }
+ else {
+ rb_enc_warn(estr_enc, "BOM with non-UTF encoding %s is nonsense", estr);
+ fmode &= ~FMODE_SETENC_BY_BOM;
+ }
}
if (len == 0 || len > ENCODING_MAXNAMELEN) {
- idx = -1;
+ idx = -1;
}
else {
- if (p) {
- memcpy(encname, estr, len);
- encname[len] = '\0';
- estr = encname;
- }
- idx = rb_enc_find_index(estr);
+ if (p) {
+ memcpy(encname, estr, len);
+ encname[len] = '\0';
+ estr = encname;
+ }
+ idx = rb_enc_find_index(estr);
}
if (fmode_p) *fmode_p = fmode;
if (idx >= 0)
- ext_enc = rb_enc_from_index(idx);
+ ext_enc = rb_enc_from_index(idx);
else {
- if (idx != -2)
- unsupported_encoding(estr, estr_enc);
- ext_enc = NULL;
+ if (idx != -2)
+ unsupported_encoding(estr, estr_enc);
+ ext_enc = NULL;
}
int_enc = NULL;
if (p) {
- if (*p == '-' && *(p+1) == '\0') {
- /* Special case - "-" => no transcoding */
- int_enc = (rb_encoding *)Qnil;
- }
- else {
- idx2 = rb_enc_find_index(p);
- if (idx2 < 0)
- unsupported_encoding(p, estr_enc);
- else if (!(fmode & FMODE_SETENC_BY_BOM) && (idx2 == idx)) {
- int_enc = (rb_encoding *)Qnil;
- }
- else
- int_enc = rb_enc_from_index(idx2);
- }
+ if (*p == '-' && *(p+1) == '\0') {
+ /* Special case - "-" => no transcoding */
+ int_enc = (rb_encoding *)Qnil;
+ }
+ else {
+ idx2 = rb_enc_find_index(p);
+ if (idx2 < 0)
+ unsupported_encoding(p, estr_enc);
+ else if (!(fmode & FMODE_SETENC_BY_BOM) && (idx2 == idx)) {
+ int_enc = (rb_encoding *)Qnil;
+ }
+ else
+ int_enc = rb_enc_from_index(idx2);
+ }
}
rb_io_ext_int_to_encs(ext_enc, int_enc, enc_p, enc2_p, fmode);
@@ -6365,62 +6689,62 @@ rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2
rb_encoding *intencoding = NULL;
if (!NIL_P(opt)) {
- VALUE v;
- v = rb_hash_lookup2(opt, sym_encoding, Qnil);
- if (v != Qnil) encoding = v;
- v = rb_hash_lookup2(opt, sym_extenc, Qundef);
- if (v != Qnil) extenc = v;
- v = rb_hash_lookup2(opt, sym_intenc, Qundef);
- if (v != Qundef) intenc = v;
- }
- if ((extenc != Qundef || intenc != Qundef) && !NIL_P(encoding)) {
- if (!NIL_P(ruby_verbose)) {
- int idx = rb_to_encoding_index(encoding);
- if (idx >= 0) encoding = rb_enc_from_encoding(rb_enc_from_index(idx));
- rb_warn("Ignoring encoding parameter '%"PRIsVALUE"': %s_encoding is used",
- encoding, extenc == Qundef ? "internal" : "external");
- }
- encoding = Qnil;
- }
- if (extenc != Qundef && !NIL_P(extenc)) {
- extencoding = rb_to_encoding(extenc);
- }
- if (intenc != Qundef) {
- if (NIL_P(intenc)) {
- /* internal_encoding: nil => no transcoding */
- intencoding = (rb_encoding *)Qnil;
- }
- else if (!NIL_P(tmp = rb_check_string_type(intenc))) {
- char *p = StringValueCStr(tmp);
-
- if (*p == '-' && *(p+1) == '\0') {
- /* Special case - "-" => no transcoding */
- intencoding = (rb_encoding *)Qnil;
- }
- else {
- intencoding = rb_to_encoding(intenc);
- }
- }
- else {
- intencoding = rb_to_encoding(intenc);
- }
- if (extencoding == intencoding) {
- intencoding = (rb_encoding *)Qnil;
- }
+ VALUE v;
+ v = rb_hash_lookup2(opt, sym_encoding, Qnil);
+ if (v != Qnil) encoding = v;
+ v = rb_hash_lookup2(opt, sym_extenc, Qundef);
+ if (v != Qnil) extenc = v;
+ v = rb_hash_lookup2(opt, sym_intenc, Qundef);
+ if (!UNDEF_P(v)) intenc = v;
+ }
+ if ((!UNDEF_P(extenc) || !UNDEF_P(intenc)) && !NIL_P(encoding)) {
+ if (!NIL_P(ruby_verbose)) {
+ int idx = rb_to_encoding_index(encoding);
+ if (idx >= 0) encoding = rb_enc_from_encoding(rb_enc_from_index(idx));
+ rb_warn("Ignoring encoding parameter '%"PRIsVALUE"': %s_encoding is used",
+ encoding, UNDEF_P(extenc) ? "internal" : "external");
+ }
+ encoding = Qnil;
+ }
+ if (!UNDEF_P(extenc) && !NIL_P(extenc)) {
+ extencoding = rb_to_encoding(extenc);
+ }
+ if (!UNDEF_P(intenc)) {
+ if (NIL_P(intenc)) {
+ /* internal_encoding: nil => no transcoding */
+ intencoding = (rb_encoding *)Qnil;
+ }
+ else if (!NIL_P(tmp = rb_check_string_type(intenc))) {
+ char *p = StringValueCStr(tmp);
+
+ if (*p == '-' && *(p+1) == '\0') {
+ /* Special case - "-" => no transcoding */
+ intencoding = (rb_encoding *)Qnil;
+ }
+ else {
+ intencoding = rb_to_encoding(intenc);
+ }
+ }
+ else {
+ intencoding = rb_to_encoding(intenc);
+ }
+ if (extencoding == intencoding) {
+ intencoding = (rb_encoding *)Qnil;
+ }
}
if (!NIL_P(encoding)) {
- extracted = 1;
- if (!NIL_P(tmp = rb_check_string_type(encoding))) {
- parse_mode_enc(StringValueCStr(tmp), rb_enc_get(tmp),
- enc_p, enc2_p, fmode_p);
- }
- else {
- rb_io_ext_int_to_encs(rb_to_encoding(encoding), NULL, enc_p, enc2_p, 0);
- }
- }
- else if (extenc != Qundef || intenc != Qundef) {
extracted = 1;
- rb_io_ext_int_to_encs(extencoding, intencoding, enc_p, enc2_p, 0);
+ if (!NIL_P(tmp = rb_check_string_type(encoding))) {
+ parse_mode_enc(StringValueCStr(tmp), rb_enc_get(tmp),
+ enc_p, enc2_p, fmode_p);
+ }
+ else {
+ rb_io_ext_int_to_encs(rb_to_encoding(encoding), NULL, enc_p, enc2_p, 0);
+ }
+ }
+ else if (!UNDEF_P(extenc) || !UNDEF_P(intenc)) {
+ extracted = 1;
+ rb_io_ext_int_to_encs(extencoding, intencoding, enc_p, enc2_p, 0);
}
return extracted;
}
@@ -6439,17 +6763,17 @@ validate_enc_binmode(int *fmode_p, int ecflags, rb_encoding *enc, rb_encoding *e
rb_raise(rb_eArgError, "ASCII incompatible encoding needs binmode");
if ((fmode & FMODE_BINMODE) && (ecflags & ECONV_NEWLINE_DECORATOR_MASK)) {
- rb_raise(rb_eArgError, "newline decorator with binary mode");
+ rb_raise(rb_eArgError, "newline decorator with binary mode");
}
if (!(fmode & FMODE_BINMODE) &&
- (DEFAULT_TEXTMODE || (ecflags & ECONV_NEWLINE_DECORATOR_MASK))) {
- fmode |= FMODE_TEXTMODE;
- *fmode_p = fmode;
+ (DEFAULT_TEXTMODE || (ecflags & ECONV_NEWLINE_DECORATOR_MASK))) {
+ fmode |= FMODE_TEXTMODE;
+ *fmode_p = fmode;
}
#if !DEFAULT_TEXTMODE
else if (!(ecflags & ECONV_NEWLINE_DECORATOR_MASK)) {
- fmode &= ~FMODE_TEXTMODE;
- *fmode_p = fmode;
+ fmode &= ~FMODE_TEXTMODE;
+ *fmode_p = fmode;
}
#endif
}
@@ -6458,28 +6782,28 @@ static void
extract_binmode(VALUE opthash, int *fmode)
{
if (!NIL_P(opthash)) {
- VALUE v;
- v = rb_hash_aref(opthash, sym_textmode);
- if (!NIL_P(v)) {
- if (*fmode & FMODE_TEXTMODE)
- rb_raise(rb_eArgError, "textmode specified twice");
- if (*fmode & FMODE_BINMODE)
- rb_raise(rb_eArgError, "both textmode and binmode specified");
- if (RTEST(v))
- *fmode |= FMODE_TEXTMODE;
- }
- v = rb_hash_aref(opthash, sym_binmode);
- if (!NIL_P(v)) {
- if (*fmode & FMODE_BINMODE)
- rb_raise(rb_eArgError, "binmode specified twice");
- if (*fmode & FMODE_TEXTMODE)
- rb_raise(rb_eArgError, "both textmode and binmode specified");
- if (RTEST(v))
- *fmode |= FMODE_BINMODE;
- }
-
- if ((*fmode & FMODE_BINMODE) && (*fmode & FMODE_TEXTMODE))
- rb_raise(rb_eArgError, "both textmode and binmode specified");
+ VALUE v;
+ v = rb_hash_aref(opthash, sym_textmode);
+ if (!NIL_P(v)) {
+ if (*fmode & FMODE_TEXTMODE)
+ rb_raise(rb_eArgError, "textmode specified twice");
+ if (*fmode & FMODE_BINMODE)
+ rb_raise(rb_eArgError, "both textmode and binmode specified");
+ if (RTEST(v))
+ *fmode |= FMODE_TEXTMODE;
+ }
+ v = rb_hash_aref(opthash, sym_binmode);
+ if (!NIL_P(v)) {
+ if (*fmode & FMODE_BINMODE)
+ rb_raise(rb_eArgError, "binmode specified twice");
+ if (*fmode & FMODE_TEXTMODE)
+ rb_raise(rb_eArgError, "both textmode and binmode specified");
+ if (RTEST(v))
+ *fmode |= FMODE_BINMODE;
+ }
+
+ if ((*fmode & FMODE_BINMODE) && (*fmode & FMODE_TEXTMODE))
+ rb_raise(rb_eArgError, "both textmode and binmode specified");
}
}
@@ -6522,24 +6846,24 @@ rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash,
has_enc = 1;
parse_mode_enc(p+1, rb_enc_get(vmode), &enc, &enc2, &fmode);
}
- else {
- rb_encoding *e;
+ else {
+ rb_encoding *e;
- e = (fmode & FMODE_BINMODE) ? rb_ascii8bit_encoding() : NULL;
- rb_io_ext_int_to_encs(e, NULL, &enc, &enc2, fmode);
- }
+ e = (fmode & FMODE_BINMODE) ? rb_ascii8bit_encoding() : NULL;
+ rb_io_ext_int_to_encs(e, NULL, &enc, &enc2, fmode);
+ }
}
if (NIL_P(opthash)) {
- ecflags = (fmode & FMODE_READABLE) ?
- MODE_BTMODE(ECONV_DEFAULT_NEWLINE_DECORATOR,
- 0, ECONV_UNIVERSAL_NEWLINE_DECORATOR) : 0;
+ ecflags = (fmode & FMODE_READABLE) ?
+ MODE_BTMODE(ECONV_DEFAULT_NEWLINE_DECORATOR,
+ 0, ECONV_UNIVERSAL_NEWLINE_DECORATOR) : 0;
#ifdef TEXTMODE_NEWLINE_DECORATOR_ON_WRITE
- ecflags |= (fmode & FMODE_WRITABLE) ?
- MODE_BTMODE(TEXTMODE_NEWLINE_DECORATOR_ON_WRITE,
- 0, TEXTMODE_NEWLINE_DECORATOR_ON_WRITE) : 0;
+ ecflags |= (fmode & FMODE_WRITABLE) ?
+ MODE_BTMODE(TEXTMODE_NEWLINE_DECORATOR_ON_WRITE,
+ 0, TEXTMODE_NEWLINE_DECORATOR_ON_WRITE) : 0;
#endif
- SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags);
+ SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags);
ecopts = Qnil;
if (fmode & FMODE_BINMODE) {
#ifdef O_BINARY
@@ -6555,57 +6879,57 @@ rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash,
#endif
}
else {
- VALUE v;
- if (!has_vmode) {
- v = rb_hash_aref(opthash, sym_mode);
- if (!NIL_P(v)) {
- if (!NIL_P(vmode)) {
- rb_raise(rb_eArgError, "mode specified twice");
- }
- has_vmode = 1;
- vmode = v;
- goto vmode_handle;
- }
- }
- v = rb_hash_aref(opthash, sym_flags);
- if (!NIL_P(v)) {
- v = rb_to_int(v);
- oflags |= NUM2INT(v);
- vmode = INT2NUM(oflags);
- fmode = rb_io_oflags_fmode(oflags);
- }
- extract_binmode(opthash, &fmode);
- if (fmode & FMODE_BINMODE) {
+ VALUE v;
+ if (!has_vmode) {
+ v = rb_hash_aref(opthash, sym_mode);
+ if (!NIL_P(v)) {
+ if (!NIL_P(vmode)) {
+ rb_raise(rb_eArgError, "mode specified twice");
+ }
+ has_vmode = 1;
+ vmode = v;
+ goto vmode_handle;
+ }
+ }
+ v = rb_hash_aref(opthash, sym_flags);
+ if (!NIL_P(v)) {
+ v = rb_to_int(v);
+ oflags |= NUM2INT(v);
+ vmode = INT2NUM(oflags);
+ fmode = rb_io_oflags_fmode(oflags);
+ }
+ extract_binmode(opthash, &fmode);
+ if (fmode & FMODE_BINMODE) {
#ifdef O_BINARY
oflags |= O_BINARY;
#endif
- if (!has_enc)
- rb_io_ext_int_to_encs(rb_ascii8bit_encoding(), NULL, &enc, &enc2, fmode);
- }
+ if (!has_enc)
+ rb_io_ext_int_to_encs(rb_ascii8bit_encoding(), NULL, &enc, &enc2, fmode);
+ }
#if DEFAULT_TEXTMODE
- else if (NIL_P(vmode)) {
- fmode |= DEFAULT_TEXTMODE;
- }
-#endif
- v = rb_hash_aref(opthash, sym_perm);
- if (!NIL_P(v)) {
- if (vperm_p) {
- if (!NIL_P(*vperm_p)) {
- rb_raise(rb_eArgError, "perm specified twice");
- }
- *vperm_p = v;
- }
- else {
- /* perm no use, just ignore */
- }
- }
- ecflags = (fmode & FMODE_READABLE) ?
- MODE_BTMODE(ECONV_DEFAULT_NEWLINE_DECORATOR,
- 0, ECONV_UNIVERSAL_NEWLINE_DECORATOR) : 0;
+ else if (NIL_P(vmode)) {
+ fmode |= DEFAULT_TEXTMODE;
+ }
+#endif
+ v = rb_hash_aref(opthash, sym_perm);
+ if (!NIL_P(v)) {
+ if (vperm_p) {
+ if (!NIL_P(*vperm_p)) {
+ rb_raise(rb_eArgError, "perm specified twice");
+ }
+ *vperm_p = v;
+ }
+ else {
+ /* perm no use, just ignore */
+ }
+ }
+ ecflags = (fmode & FMODE_READABLE) ?
+ MODE_BTMODE(ECONV_DEFAULT_NEWLINE_DECORATOR,
+ 0, ECONV_UNIVERSAL_NEWLINE_DECORATOR) : 0;
#ifdef TEXTMODE_NEWLINE_DECORATOR_ON_WRITE
- ecflags |= (fmode & FMODE_WRITABLE) ?
- MODE_BTMODE(TEXTMODE_NEWLINE_DECORATOR_ON_WRITE,
- 0, TEXTMODE_NEWLINE_DECORATOR_ON_WRITE) : 0;
+ ecflags |= (fmode & FMODE_WRITABLE) ?
+ MODE_BTMODE(TEXTMODE_NEWLINE_DECORATOR_ON_WRITE,
+ 0, TEXTMODE_NEWLINE_DECORATOR_ON_WRITE) : 0;
#endif
if (rb_io_extract_encoding_option(opthash, &enc, &enc2, &fmode)) {
@@ -6613,8 +6937,8 @@ rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash,
rb_raise(rb_eArgError, "encoding specified twice");
}
}
- SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags);
- ecflags = rb_econv_prepare_options(opthash, &ecopts, ecflags);
+ SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags);
+ ecflags = rb_econv_prepare_options(opthash, &ecopts, ecflags);
}
validate_enc_binmode(&fmode, ecflags, enc, enc2);
@@ -6666,13 +6990,13 @@ rb_sysopen(VALUE fname, int oflags, mode_t perm)
fd = rb_sysopen_internal(&data);
if (fd < 0) {
- int e = errno;
- if (rb_gc_for_fd(e)) {
- fd = rb_sysopen_internal(&data);
- }
- if (fd < 0) {
- rb_syserr_fail_path(e, fname);
- }
+ int e = errno;
+ if (rb_gc_for_fd(e)) {
+ fd = rb_sysopen_internal(&data);
+ }
+ if (fd < 0) {
+ rb_syserr_fail_path(e, fname);
+ }
}
return fd;
}
@@ -6687,32 +7011,32 @@ rb_fdopen(int fd, const char *modestr)
#endif
file = fdopen(fd, modestr);
if (!file) {
- int e = errno;
+ int e = errno;
#if defined(__sun)
- if (e == 0) {
- rb_gc();
- errno = 0;
- file = fdopen(fd, modestr);
- }
- else
-#endif
- if (rb_gc_for_fd(e)) {
- file = fdopen(fd, modestr);
- }
- if (!file) {
+ if (e == 0) {
+ rb_gc();
+ errno = 0;
+ file = fdopen(fd, modestr);
+ }
+ else
+#endif
+ if (rb_gc_for_fd(e)) {
+ file = fdopen(fd, modestr);
+ }
+ if (!file) {
#ifdef _WIN32
- if (e == 0) e = EINVAL;
+ if (e == 0) e = EINVAL;
#elif defined(__sun)
- if (e == 0) e = EMFILE;
+ if (e == 0) e = EMFILE;
#endif
- rb_syserr_fail(e, 0);
- }
+ rb_syserr_fail(e, 0);
+ }
}
/* xxx: should be _IONBF? A buffer in FILE may have trouble. */
#ifdef USE_SETVBUF
if (setvbuf(file, NULL, _IOFBF, 0) != 0)
- rb_warn("setvbuf() can't be honoured (fd=%d)", fd);
+ rb_warn("setvbuf() can't be honoured (fd=%d)", fd);
#endif
return file;
}
@@ -6740,53 +7064,53 @@ io_strip_bom(VALUE io)
if (NIL_P(b1 = rb_io_getbyte(io))) return 0;
switch (b1) {
case INT2FIX(0xEF):
- if (NIL_P(b2 = rb_io_getbyte(io))) break;
- if (b2 == INT2FIX(0xBB) && !NIL_P(b3 = rb_io_getbyte(io))) {
- if (b3 == INT2FIX(0xBF)) {
- return rb_utf8_encindex();
- }
- rb_io_ungetbyte(io, b3);
- }
- rb_io_ungetbyte(io, b2);
- break;
+ if (NIL_P(b2 = rb_io_getbyte(io))) break;
+ if (b2 == INT2FIX(0xBB) && !NIL_P(b3 = rb_io_getbyte(io))) {
+ if (b3 == INT2FIX(0xBF)) {
+ return rb_utf8_encindex();
+ }
+ rb_io_ungetbyte(io, b3);
+ }
+ rb_io_ungetbyte(io, b2);
+ break;
case INT2FIX(0xFE):
- if (NIL_P(b2 = rb_io_getbyte(io))) break;
- if (b2 == INT2FIX(0xFF)) {
- return ENCINDEX_UTF_16BE;
- }
- rb_io_ungetbyte(io, b2);
- break;
+ if (NIL_P(b2 = rb_io_getbyte(io))) break;
+ if (b2 == INT2FIX(0xFF)) {
+ return ENCINDEX_UTF_16BE;
+ }
+ rb_io_ungetbyte(io, b2);
+ break;
case INT2FIX(0xFF):
- if (NIL_P(b2 = rb_io_getbyte(io))) break;
- if (b2 == INT2FIX(0xFE)) {
- b3 = rb_io_getbyte(io);
- if (b3 == INT2FIX(0) && !NIL_P(b4 = rb_io_getbyte(io))) {
- if (b4 == INT2FIX(0)) {
- return ENCINDEX_UTF_32LE;
- }
- rb_io_ungetbyte(io, b4);
- }
+ if (NIL_P(b2 = rb_io_getbyte(io))) break;
+ if (b2 == INT2FIX(0xFE)) {
+ b3 = rb_io_getbyte(io);
+ if (b3 == INT2FIX(0) && !NIL_P(b4 = rb_io_getbyte(io))) {
+ if (b4 == INT2FIX(0)) {
+ return ENCINDEX_UTF_32LE;
+ }
+ rb_io_ungetbyte(io, b4);
+ }
rb_io_ungetbyte(io, b3);
return ENCINDEX_UTF_16LE;
- }
- rb_io_ungetbyte(io, b2);
- break;
+ }
+ rb_io_ungetbyte(io, b2);
+ break;
case INT2FIX(0):
- if (NIL_P(b2 = rb_io_getbyte(io))) break;
- if (b2 == INT2FIX(0) && !NIL_P(b3 = rb_io_getbyte(io))) {
- if (b3 == INT2FIX(0xFE) && !NIL_P(b4 = rb_io_getbyte(io))) {
- if (b4 == INT2FIX(0xFF)) {
- return ENCINDEX_UTF_32BE;
- }
- rb_io_ungetbyte(io, b4);
- }
- rb_io_ungetbyte(io, b3);
- }
- rb_io_ungetbyte(io, b2);
- break;
+ if (NIL_P(b2 = rb_io_getbyte(io))) break;
+ if (b2 == INT2FIX(0) && !NIL_P(b3 = rb_io_getbyte(io))) {
+ if (b3 == INT2FIX(0xFE) && !NIL_P(b4 = rb_io_getbyte(io))) {
+ if (b4 == INT2FIX(0xFF)) {
+ return ENCINDEX_UTF_32BE;
+ }
+ rb_io_ungetbyte(io, b4);
+ }
+ rb_io_ungetbyte(io, b3);
+ }
+ rb_io_ungetbyte(io, b2);
+ break;
}
rb_io_ungetbyte(io, b1);
return 0;
@@ -6806,27 +7130,27 @@ io_set_encoding_by_bom(VALUE io)
rb_io_internal_encoding(io), Qnil);
}
else {
- fptr->encs.enc2 = NULL;
+ fptr->encs.enc2 = NULL;
}
return extenc;
}
static VALUE
rb_file_open_generic(VALUE io, VALUE filename, int oflags, int fmode,
- const convconfig_t *convconfig, mode_t perm)
+ const convconfig_t *convconfig, mode_t perm)
{
VALUE pathv;
rb_io_t *fptr;
convconfig_t cc;
if (!convconfig) {
- /* Set to default encodings */
- rb_io_ext_int_to_encs(NULL, NULL, &cc.enc, &cc.enc2, fmode);
+ /* Set to default encodings */
+ rb_io_ext_int_to_encs(NULL, NULL, &cc.enc, &cc.enc2, fmode);
cc.ecflags = 0;
cc.ecopts = Qnil;
convconfig = &cc;
}
validate_enc_binmode(&fmode, convconfig->ecflags,
- convconfig->enc, convconfig->enc2);
+ convconfig->enc, convconfig->enc2);
MakeOpenFile(io, fptr);
fptr->mode = fmode;
@@ -6855,16 +7179,16 @@ rb_file_open_internal(VALUE io, VALUE filename, const char *modestr)
if (p) {
parse_mode_enc(p+1, rb_usascii_encoding(),
- &convconfig.enc, &convconfig.enc2, &fmode);
+ &convconfig.enc, &convconfig.enc2, &fmode);
convconfig.ecflags = 0;
convconfig.ecopts = Qnil;
}
else {
- rb_encoding *e;
- /* Set to default encodings */
+ rb_encoding *e;
+ /* Set to default encodings */
- e = (fmode & FMODE_BINMODE) ? rb_ascii8bit_encoding() : NULL;
- rb_io_ext_int_to_encs(e, NULL, &convconfig.enc, &convconfig.enc2, fmode);
+ e = (fmode & FMODE_BINMODE) ? rb_ascii8bit_encoding() : NULL;
+ rb_io_ext_int_to_encs(e, NULL, &convconfig.enc, &convconfig.enc2, fmode);
convconfig.ecflags = 0;
convconfig.ecopts = Qnil;
}
@@ -6913,12 +7237,12 @@ pipe_del_fptr(rb_io_t *fptr)
struct pipe_list *tmp;
while ((tmp = *prev) != 0) {
- if (tmp->fptr == fptr) {
- *prev = tmp->next;
- free(tmp);
- return;
- }
- prev = &tmp->next;
+ if (tmp->fptr == fptr) {
+ *prev = tmp->next;
+ free(tmp);
+ return;
+ }
+ prev = &tmp->next;
}
}
@@ -6930,9 +7254,9 @@ pipe_atexit(void)
struct pipe_list *tmp;
while (list) {
- tmp = list->next;
- rb_io_fptr_finalize(list->fptr);
- list = tmp;
+ tmp = list->next;
+ rb_io_fptr_finalize(list->fptr);
+ list = tmp;
}
}
#endif
@@ -6968,14 +7292,14 @@ fptr_copy_finalizer(rb_io_t *fptr, const rb_io_t *orig)
#if defined(__CYGWIN__) || !defined(HAVE_WORKING_FORK)
if (old_finalize != pipe_finalize) {
- struct pipe_list *list;
- for (list = pipe_list; list; list = list->next) {
- if (list->fptr == fptr) break;
- }
- if (!list) pipe_add_fptr(fptr);
+ struct pipe_list *list;
+ for (list = pipe_list; list; list = list->next) {
+ if (list->fptr == fptr) break;
+ }
+ if (!list) pipe_add_fptr(fptr);
}
else {
- pipe_del_fptr(fptr);
+ pipe_del_fptr(fptr);
}
#endif
}
@@ -7126,15 +7450,15 @@ rb_close_before_exec(int lowfd, int maxhint, VALUE noclose_fds)
if (!NIL_P(noclose_fds) &&
RTEST(rb_hash_lookup(noclose_fds, INT2FIX(fd)))) /* async-signal-safe */
continue;
- ret = fcntl(fd, F_GETFD); /* async-signal-safe */
- if (ret != -1 && !(ret & FD_CLOEXEC)) {
+ ret = fcntl(fd, F_GETFD); /* async-signal-safe */
+ if (ret != -1 && !(ret & FD_CLOEXEC)) {
fcntl(fd, F_SETFD, ret|FD_CLOEXEC); /* async-signal-safe */
}
# define CONTIGUOUS_CLOSED_FDS 20
if (ret != -1) {
- if (max < fd + CONTIGUOUS_CLOSED_FDS)
- max = fd + CONTIGUOUS_CLOSED_FDS;
- }
+ if (max < fd + CONTIGUOUS_CLOSED_FDS)
+ max = fd + CONTIGUOUS_CLOSED_FDS;
+ }
}
#endif
}
@@ -7164,7 +7488,7 @@ char *rb_execarg_commandline(const struct rb_execarg *eargp, VALUE *prog);
#ifndef __EMSCRIPTEN__
static VALUE
pipe_open(VALUE execarg_obj, const char *modestr, int fmode,
- const convconfig_t *convconfig)
+ const convconfig_t *convconfig)
{
struct rb_execarg *eargp = NIL_P(execarg_obj) ? NULL : rb_execarg_get(execarg_obj);
VALUE prog = eargp ? (eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name) : Qfalse ;
@@ -7185,12 +7509,12 @@ pipe_open(VALUE execarg_obj, const char *modestr, int fmode,
#if defined(HAVE_SPAWNV)
# if defined(HAVE_SPAWNVE)
# define DO_SPAWN(cmd, args, envp) ((args) ? \
- spawnve(P_NOWAIT, (cmd), (args), (envp)) : \
- spawne(P_NOWAIT, (cmd), (envp)))
+ spawnve(P_NOWAIT, (cmd), (args), (envp)) : \
+ spawne(P_NOWAIT, (cmd), (envp)))
# else
# define DO_SPAWN(cmd, args, envp) ((args) ? \
- spawnv(P_NOWAIT, (cmd), (args)) : \
- spawn(P_NOWAIT, (cmd)))
+ spawnv(P_NOWAIT, (cmd), (args)) : \
+ spawn(P_NOWAIT, (cmd)))
# endif
# if !defined(HAVE_WORKING_FORK)
char **args = NULL;
@@ -7237,19 +7561,19 @@ pipe_open(VALUE execarg_obj, const char *modestr, int fmode,
rb_execarg_addopt(execarg_obj, INT2FIX(0), INT2FIX(arg.write_pair[0]));
rb_execarg_addopt(execarg_obj, INT2FIX(1), INT2FIX(arg.pair[1]));
}
- break;
+ break;
case FMODE_READABLE:
if (rb_pipe(arg.pair) < 0)
rb_sys_fail_str(prog);
if (eargp)
rb_execarg_addopt(execarg_obj, INT2FIX(1), INT2FIX(arg.pair[1]));
- break;
+ break;
case FMODE_WRITABLE:
if (rb_pipe(arg.pair) < 0)
rb_sys_fail_str(prog);
if (eargp)
rb_execarg_addopt(execarg_obj, INT2FIX(0), INT2FIX(arg.pair[0]));
- break;
+ break;
default:
rb_sys_fail_str(prog);
}
@@ -7265,59 +7589,59 @@ pipe_open(VALUE execarg_obj, const char *modestr, int fmode,
}
# if defined(HAVE_WORKING_FORK)
- pid = rb_fork_async_signal_safe(&status, popen_exec, &arg, arg.eargp->redirect_fds, errmsg, sizeof(errmsg));
+ pid = rb_fork_async_signal_safe(&status, popen_exec, &arg, arg.eargp->redirect_fds, errmsg, sizeof(errmsg));
# else
- rb_execarg_run_options(eargp, sargp, NULL, 0);
+ rb_execarg_run_options(eargp, sargp, NULL, 0);
# if defined(HAVE_SPAWNVE)
- if (eargp->envp_str) envp = (char **)RSTRING_PTR(eargp->envp_str);
+ if (eargp->envp_str) envp = (char **)RSTRING_PTR(eargp->envp_str);
# endif
while ((pid = DO_SPAWN(cmd, args, envp)) < 0) {
- /* exec failed */
- switch (e = errno) {
- case EAGAIN:
+ /* exec failed */
+ switch (e = errno) {
+ case EAGAIN:
# if EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK:
+ case EWOULDBLOCK:
# endif
- rb_thread_sleep(1);
- continue;
- }
- break;
- }
- if (eargp)
- rb_execarg_run_options(sargp, NULL, NULL, 0);
+ rb_thread_sleep(1);
+ continue;
+ }
+ break;
+ }
+ if (eargp)
+ rb_execarg_run_options(sargp, NULL, NULL, 0);
# endif
rb_execarg_parent_end(execarg_obj);
}
else {
# if defined(HAVE_WORKING_FORK)
- pid = rb_call_proc__fork();
- if (pid == 0) { /* child */
- popen_redirect(&arg);
- rb_io_synchronized(RFILE(orig_stdout)->fptr);
- rb_io_synchronized(RFILE(orig_stderr)->fptr);
- return Qnil;
- }
+ pid = rb_call_proc__fork();
+ if (pid == 0) { /* child */
+ popen_redirect(&arg);
+ rb_io_synchronized(RFILE(orig_stdout)->fptr);
+ rb_io_synchronized(RFILE(orig_stderr)->fptr);
+ return Qnil;
+ }
# else
- rb_notimplement();
+ rb_notimplement();
# endif
}
/* parent */
if (pid < 0) {
# if defined(HAVE_WORKING_FORK)
- e = errno;
+ e = errno;
# endif
- close(arg.pair[0]);
- close(arg.pair[1]);
+ close(arg.pair[0]);
+ close(arg.pair[1]);
if ((fmode & (FMODE_READABLE|FMODE_WRITABLE)) == (FMODE_READABLE|FMODE_WRITABLE)) {
close(arg.write_pair[0]);
close(arg.write_pair[1]);
}
# if defined(HAVE_WORKING_FORK)
if (errmsg[0])
- rb_syserr_fail(e, errmsg);
+ rb_syserr_fail(e, errmsg);
# endif
- rb_syserr_fail_str(e, prog);
+ rb_syserr_fail_str(e, prog);
}
if ((fmode & FMODE_READABLE) && (fmode & FMODE_WRITABLE)) {
close(arg.pair[1]);
@@ -7336,14 +7660,14 @@ pipe_open(VALUE execarg_obj, const char *modestr, int fmode,
#else
cmd = rb_execarg_commandline(eargp, &prog);
if (!NIL_P(execarg_obj)) {
- rb_execarg_parent_start(execarg_obj);
- rb_execarg_run_options(eargp, sargp, NULL, 0);
+ rb_execarg_parent_start(execarg_obj);
+ rb_execarg_run_options(eargp, sargp, NULL, 0);
}
fp = popen(cmd, modestr);
e = errno;
if (eargp) {
rb_execarg_parent_end(execarg_obj);
- rb_execarg_run_options(sargp, NULL, NULL, 0);
+ rb_execarg_run_options(sargp, NULL, NULL, 0);
}
if (!fp) rb_syserr_fail_path(e, prog);
fd = fileno(fp);
@@ -7357,19 +7681,19 @@ pipe_open(VALUE execarg_obj, const char *modestr, int fmode,
if (convconfig) {
fptr->encs = *convconfig;
#if RUBY_CRLF_ENVIRONMENT
- if (fptr->encs.ecflags & ECONV_DEFAULT_NEWLINE_DECORATOR) {
- fptr->encs.ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
- }
+ if (fptr->encs.ecflags & ECONV_DEFAULT_NEWLINE_DECORATOR) {
+ fptr->encs.ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
+ }
#endif
}
else {
- if (NEED_NEWLINE_DECORATOR_ON_READ(fptr)) {
- fptr->encs.ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
- }
+ if (NEED_NEWLINE_DECORATOR_ON_READ(fptr)) {
+ fptr->encs.ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
+ }
#ifdef TEXTMODE_NEWLINE_DECORATOR_ON_WRITE
- if (NEED_NEWLINE_DECORATOR_ON_WRITE(fptr)) {
- fptr->encs.ecflags |= TEXTMODE_NEWLINE_DECORATOR_ON_WRITE;
- }
+ if (NEED_NEWLINE_DECORATOR_ON_WRITE(fptr)) {
+ fptr->encs.ecflags |= TEXTMODE_NEWLINE_DECORATOR_ON_WRITE;
+ }
#endif
}
fptr->pid = pid;
@@ -7393,7 +7717,7 @@ pipe_open(VALUE execarg_obj, const char *modestr, int fmode,
#else
static VALUE
pipe_open(VALUE execarg_obj, const char *modestr, int fmode,
- const convconfig_t *convconfig)
+ const convconfig_t *convconfig)
{
rb_raise(rb_eNotImpError, "popen() is not available");
}
@@ -7404,10 +7728,10 @@ is_popen_fork(VALUE prog)
{
if (RSTRING_LEN(prog) == 1 && RSTRING_PTR(prog)[0] == '-') {
#if !defined(HAVE_WORKING_FORK)
- rb_raise(rb_eNotImpError,
- "fork() function is unimplemented on this machine");
+ rb_raise(rb_eNotImpError,
+ "fork() function is unimplemented on this machine");
#else
- return TRUE;
+ return TRUE;
#endif
}
return FALSE;
@@ -7415,7 +7739,7 @@ is_popen_fork(VALUE prog)
static VALUE
pipe_open_s(VALUE prog, const char *modestr, int fmode,
- const convconfig_t *convconfig)
+ const convconfig_t *convconfig)
{
int argc = 1;
VALUE *argv = &prog;
@@ -7431,7 +7755,7 @@ pipe_close(VALUE io)
{
rb_io_t *fptr = io_close_fptr(io);
if (fptr) {
- fptr_waitpid(fptr, rb_thread_to_be_killed(rb_thread_current()));
+ fptr_waitpid(fptr, rb_thread_to_be_killed(rb_thread_current()));
}
return Qnil;
}
@@ -7459,7 +7783,7 @@ static VALUE popen_finish(VALUE port, VALUE klass);
* and the block's value is assigned to global variable <tt>$?</tt> and returned.
*
* Optional argument +mode+ may be any valid \IO mode.
- * See IO@Modes.
+ * See {Access Modes}[rdoc-ref:File@Access+Modes].
*
* Required argument +cmd+ determines which of the following occurs:
*
@@ -7605,15 +7929,15 @@ rb_io_s_popen(int argc, VALUE *argv, VALUE klass)
if (argc > 1 && !NIL_P(env = rb_check_hash_type(argv[0]))) --argc, ++argv;
switch (argc) {
case 2:
- pmode = argv[1];
+ pmode = argv[1];
case 1:
- pname = argv[0];
- break;
+ pname = argv[0];
+ break;
default:
- {
- int ex = !NIL_P(opt);
- rb_error_arity(argc + ex, 1 + ex, 2 + ex);
- }
+ {
+ int ex = !NIL_P(opt);
+ rb_error_arity(argc + ex, 1 + ex, 2 + ex);
+ }
}
return popen_finish(rb_io_popen(pname, pmode, env, opt), klass);
}
@@ -7628,26 +7952,26 @@ rb_io_popen(VALUE pname, VALUE pmode, VALUE env, VALUE opt)
tmp = rb_check_array_type(pname);
if (!NIL_P(tmp)) {
- long len = RARRAY_LEN(tmp);
+ long len = RARRAY_LEN(tmp);
#if SIZEOF_LONG > SIZEOF_INT
- if (len > INT_MAX) {
- rb_raise(rb_eArgError, "too many arguments");
- }
+ if (len > INT_MAX) {
+ rb_raise(rb_eArgError, "too many arguments");
+ }
#endif
execarg_obj = rb_execarg_new((int)len, RARRAY_CONST_PTR(tmp), FALSE, FALSE);
- RB_GC_GUARD(tmp);
+ RB_GC_GUARD(tmp);
}
else {
- SafeStringValue(pname);
- execarg_obj = Qnil;
- if (!is_popen_fork(pname))
+ SafeStringValue(pname);
+ execarg_obj = Qnil;
+ if (!is_popen_fork(pname))
execarg_obj = rb_execarg_new(1, &pname, TRUE, FALSE);
}
if (!NIL_P(execarg_obj)) {
- if (!NIL_P(opt))
- opt = rb_execarg_extract_options(execarg_obj, opt);
- if (!NIL_P(env))
- rb_execarg_setenv(execarg_obj, env);
+ if (!NIL_P(opt))
+ opt = rb_execarg_extract_options(execarg_obj, opt);
+ if (!NIL_P(env))
+ rb_execarg_setenv(execarg_obj, env);
}
rb_io_extract_modeenc(&pmode, 0, opt, &oflags, &fmode, &convconfig);
modestr = rb_io_oflags_modestr(oflags);
@@ -7659,18 +7983,18 @@ static VALUE
popen_finish(VALUE port, VALUE klass)
{
if (NIL_P(port)) {
- /* child */
- if (rb_block_given_p()) {
- rb_yield(Qnil);
+ /* child */
+ if (rb_block_given_p()) {
+ rb_protect(rb_yield, Qnil, NULL);
rb_io_flush(rb_ractor_stdout());
rb_io_flush(rb_ractor_stderr());
- _exit(0);
- }
- return Qnil;
+ _exit(0);
+ }
+ return Qnil;
}
RBASIC_SET_CLASS(port, klass);
if (rb_block_given_p()) {
- return rb_ensure(rb_yield, port, pipe_close, port);
+ return rb_ensure(rb_yield, port, pipe_close, port);
}
return port;
}
@@ -7749,7 +8073,7 @@ rb_io_s_open(int argc, VALUE *argv, VALUE klass)
VALUE io = rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS);
if (rb_block_given_p()) {
- return rb_ensure(rb_yield, io, io_close, io);
+ return rb_ensure(rb_yield, io, io_close, io);
}
return io;
@@ -7789,8 +8113,8 @@ rb_io_s_sysopen(int argc, VALUE *argv, VALUE _)
else if (!NIL_P(intmode = rb_check_to_integer(vmode, "to_int")))
oflags = NUM2INT(intmode);
else {
- SafeStringValue(vmode);
- oflags = rb_io_modestr_oflags(StringValueCStr(vmode));
+ SafeStringValue(vmode);
+ oflags = rb_io_modestr_oflags(StringValueCStr(vmode));
}
if (NIL_P(vperm)) perm = 0666;
else perm = NUM2MODET(vperm);
@@ -7920,32 +8244,32 @@ rb_f_open(int argc, VALUE *argv, VALUE _)
int redirect = FALSE;
if (argc >= 1) {
- CONST_ID(to_open, "to_open");
- if (rb_respond_to(argv[0], to_open)) {
- redirect = TRUE;
- }
- else {
- VALUE tmp = argv[0];
- FilePathValue(tmp);
- if (NIL_P(tmp)) {
- redirect = TRUE;
- }
- else {
+ CONST_ID(to_open, "to_open");
+ if (rb_respond_to(argv[0], to_open)) {
+ redirect = TRUE;
+ }
+ else {
+ VALUE tmp = argv[0];
+ FilePathValue(tmp);
+ if (NIL_P(tmp)) {
+ redirect = TRUE;
+ }
+ else {
VALUE cmd = check_pipe_command(tmp);
if (!NIL_P(cmd)) {
- argv[0] = cmd;
- return rb_io_s_popen(argc, argv, rb_cIO);
- }
- }
- }
+ argv[0] = cmd;
+ return rb_io_s_popen(argc, argv, rb_cIO);
+ }
+ }
+ }
}
if (redirect) {
VALUE io = rb_funcallv_kw(argv[0], to_open, argc-1, argv+1, RB_PASS_CALLED_KEYWORDS);
- if (rb_block_given_p()) {
- return rb_ensure(rb_yield, io, io_close, io);
- }
- return io;
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, io, io_close, io);
+ }
+ return io;
}
return rb_io_s_open(argc, argv, rb_cFile);
}
@@ -7966,15 +8290,15 @@ rb_io_open(VALUE io, VALUE filename, VALUE vmode, VALUE vperm, VALUE opt)
static VALUE
rb_io_open_generic(VALUE klass, VALUE filename, int oflags, int fmode,
- const convconfig_t *convconfig, mode_t perm)
+ const convconfig_t *convconfig, mode_t perm)
{
VALUE cmd;
if (klass == rb_cIO && !NIL_P(cmd = check_pipe_command(filename))) {
- return pipe_open_s(cmd, rb_io_oflags_modestr(oflags), fmode, convconfig);
+ return pipe_open_s(cmd, rb_io_oflags_modestr(oflags), fmode, convconfig);
}
else {
- return rb_file_open_generic(io_alloc(klass), filename,
- oflags, fmode, convconfig, perm);
+ return rb_file_open_generic(io_alloc(klass), filename,
+ oflags, fmode, convconfig, perm);
}
}
@@ -7983,7 +8307,7 @@ io_reopen(VALUE io, VALUE nfile)
{
rb_io_t *fptr, *orig;
int fd, fd2;
- off_t pos = 0;
+ rb_off_t pos = 0;
nfile = rb_io_get_io(nfile);
GetOpenFile(io, fptr);
@@ -7994,11 +8318,11 @@ io_reopen(VALUE io, VALUE nfile)
if ((fptr->stdio_file == stdin && !(orig->mode & FMODE_READABLE)) ||
(fptr->stdio_file == stdout && !(orig->mode & FMODE_WRITABLE)) ||
(fptr->stdio_file == stderr && !(orig->mode & FMODE_WRITABLE))) {
- rb_raise(rb_eArgError,
- "%s can't change access mode from \"%s\" to \"%s\"",
- PREP_STDIO_NAME(fptr), rb_io_fmode_modestr(fptr->mode),
- rb_io_fmode_modestr(orig->mode));
- }
+ rb_raise(rb_eArgError,
+ "%s can't change access mode from \"%s\" to \"%s\"",
+ PREP_STDIO_NAME(fptr), rb_io_fmode_modestr(fptr->mode),
+ rb_io_fmode_modestr(orig->mode));
+ }
}
if (fptr->mode & FMODE_WRITABLE) {
if (io_fflush(fptr) < 0)
@@ -8008,7 +8332,7 @@ io_reopen(VALUE io, VALUE nfile)
flush_before_seek(fptr);
}
if (orig->mode & FMODE_READABLE) {
- pos = io_tell(orig);
+ pos = io_tell(orig);
}
if (orig->mode & FMODE_WRITABLE) {
if (io_fflush(orig) < 0)
@@ -8026,13 +8350,13 @@ io_reopen(VALUE io, VALUE nfile)
fd = fptr->fd;
fd2 = orig->fd;
if (fd != fd2) {
- if (IS_PREP_STDIO(fptr) || fd <= 2 || !fptr->stdio_file) {
- /* need to keep FILE objects of stdin, stdout and stderr */
- if (rb_cloexec_dup2(fd2, fd) < 0)
- rb_sys_fail_path(orig->pathv);
+ if (IS_PREP_STDIO(fptr) || fd <= 2 || !fptr->stdio_file) {
+ /* need to keep FILE objects of stdin, stdout and stderr */
+ if (rb_cloexec_dup2(fd2, fd) < 0)
+ rb_sys_fail_path(orig->pathv);
rb_update_max_fd(fd);
- }
- else {
+ }
+ else {
fclose(fptr->stdio_file);
fptr->stdio_file = 0;
fptr->fd = -1;
@@ -8040,20 +8364,20 @@ io_reopen(VALUE io, VALUE nfile)
rb_sys_fail_path(orig->pathv);
rb_update_max_fd(fd);
fptr->fd = fd;
- }
- rb_thread_fd_close(fd);
- if ((orig->mode & FMODE_READABLE) && pos >= 0) {
- if (io_seek(fptr, pos, SEEK_SET) < 0 && errno) {
- rb_sys_fail_path(fptr->pathv);
- }
- if (io_seek(orig, pos, SEEK_SET) < 0 && errno) {
- rb_sys_fail_path(orig->pathv);
- }
- }
+ }
+ rb_thread_fd_close(fd);
+ if ((orig->mode & FMODE_READABLE) && pos >= 0) {
+ if (io_seek(fptr, pos, SEEK_SET) < 0 && errno) {
+ rb_sys_fail_path(fptr->pathv);
+ }
+ if (io_seek(orig, pos, SEEK_SET) < 0 && errno) {
+ rb_sys_fail_path(orig->pathv);
+ }
+ }
}
if (fptr->mode & FMODE_BINMODE) {
- rb_io_binmode(io);
+ rb_io_binmode(io);
}
RBASIC_SET_CLASS(io, rb_obj_class(nfile));
@@ -8067,8 +8391,8 @@ static int
rb_freopen(VALUE fname, const char *mode, FILE *fp)
{
if (!freopen(RSTRING_PTR(fname), mode, fp)) {
- RB_GC_GUARD(fname);
- return errno;
+ RB_GC_GUARD(fname);
+ return errno;
}
return 0;
}
@@ -8116,44 +8440,44 @@ rb_io_reopen(int argc, VALUE *argv, VALUE file)
rb_io_t *fptr;
if (rb_scan_args(argc, argv, "11:", &fname, &nmode, &opt) == 1) {
- VALUE tmp = rb_io_check_io(fname);
- if (!NIL_P(tmp)) {
- return io_reopen(file, tmp);
- }
+ VALUE tmp = rb_io_check_io(fname);
+ if (!NIL_P(tmp)) {
+ return io_reopen(file, tmp);
+ }
}
FilePathValue(fname);
rb_io_taint_check(file);
fptr = RFILE(file)->fptr;
if (!fptr) {
- fptr = RFILE(file)->fptr = ZALLOC(rb_io_t);
+ fptr = RFILE(file)->fptr = ZALLOC(rb_io_t);
}
if (!NIL_P(nmode) || !NIL_P(opt)) {
- int fmode;
- convconfig_t convconfig;
+ int fmode;
+ convconfig_t convconfig;
- rb_io_extract_modeenc(&nmode, 0, opt, &oflags, &fmode, &convconfig);
- if (IS_PREP_STDIO(fptr) &&
+ rb_io_extract_modeenc(&nmode, 0, opt, &oflags, &fmode, &convconfig);
+ if (IS_PREP_STDIO(fptr) &&
((fptr->mode & FMODE_READWRITE) & (fmode & FMODE_READWRITE)) !=
(fptr->mode & FMODE_READWRITE)) {
- rb_raise(rb_eArgError,
- "%s can't change access mode from \"%s\" to \"%s\"",
- PREP_STDIO_NAME(fptr), rb_io_fmode_modestr(fptr->mode),
- rb_io_fmode_modestr(fmode));
- }
- fptr->mode = fmode;
- fptr->encs = convconfig;
+ rb_raise(rb_eArgError,
+ "%s can't change access mode from \"%s\" to \"%s\"",
+ PREP_STDIO_NAME(fptr), rb_io_fmode_modestr(fptr->mode),
+ rb_io_fmode_modestr(fmode));
+ }
+ fptr->mode = fmode;
+ fptr->encs = convconfig;
}
else {
- oflags = rb_io_fmode_oflags(fptr->mode);
+ oflags = rb_io_fmode_oflags(fptr->mode);
}
fptr->pathv = fname;
if (fptr->fd < 0) {
fptr->fd = rb_sysopen(fptr->pathv, oflags, 0666);
- fptr->stdio_file = 0;
- return file;
+ fptr->stdio_file = 0;
+ return file;
}
if (fptr->mode & FMODE_WRITABLE) {
@@ -8163,9 +8487,9 @@ rb_io_reopen(int argc, VALUE *argv, VALUE file)
fptr->rbuf.off = fptr->rbuf.len = 0;
if (fptr->stdio_file) {
- int e = rb_freopen(rb_str_encode_ospath(fptr->pathv),
- rb_io_oflags_modestr(oflags),
- fptr->stdio_file);
+ int e = rb_freopen(rb_str_encode_ospath(fptr->pathv),
+ rb_io_oflags_modestr(oflags),
+ fptr->stdio_file);
if (e) rb_syserr_fail_path(e, fptr->pathv);
fptr->fd = fileno(fptr->stdio_file);
rb_fd_fix_cloexec(fptr->fd);
@@ -8183,14 +8507,14 @@ rb_io_reopen(int argc, VALUE *argv, VALUE file)
}
}
else {
- int tmpfd = rb_sysopen(fptr->pathv, oflags, 0666);
- int err = 0;
- if (rb_cloexec_dup2(tmpfd, fptr->fd) < 0)
- err = errno;
- (void)close(tmpfd);
- if (err) {
- rb_syserr_fail_path(err, fptr->pathv);
- }
+ int tmpfd = rb_sysopen(fptr->pathv, oflags, 0666);
+ int err = 0;
+ if (rb_cloexec_dup2(tmpfd, fptr->fd) < 0)
+ err = errno;
+ (void)close(tmpfd);
+ if (err) {
+ rb_syserr_fail_path(err, fptr->pathv);
+ }
}
return file;
@@ -8203,7 +8527,7 @@ rb_io_init_copy(VALUE dest, VALUE io)
rb_io_t *fptr, *orig;
int fd;
VALUE write_io;
- off_t pos;
+ rb_off_t pos;
io = rb_io_get_io(io);
if (!OBJ_INIT_COPY(dest, io)) return dest;
@@ -8217,6 +8541,7 @@ rb_io_init_copy(VALUE dest, VALUE io)
fptr->encs = orig->encs;
fptr->pid = orig->pid;
fptr->lineno = orig->lineno;
+ fptr->timeout = orig->timeout;
if (!NIL_P(orig->pathv)) fptr->pathv = orig->pathv;
fptr_copy_finalizer(fptr, orig);
@@ -8226,7 +8551,7 @@ rb_io_init_copy(VALUE dest, VALUE io)
if (0 <= pos)
io_seek(fptr, pos, SEEK_SET);
if (fptr->mode & FMODE_BINMODE) {
- rb_io_binmode(dest);
+ rb_io_binmode(dest);
}
write_io = GetWriteIO(io);
@@ -8298,12 +8623,12 @@ rb_f_printf(int argc, VALUE *argv, VALUE _)
if (argc == 0) return Qnil;
if (RB_TYPE_P(argv[0], T_STRING)) {
- out = rb_ractor_stdout();
+ out = rb_ractor_stdout();
}
else {
- out = argv[0];
- argv++;
- argc--;
+ out = argv[0];
+ argv++;
+ argc--;
}
rb_io_write(out, rb_f_sprintf(argc, argv));
@@ -8327,6 +8652,7 @@ deprecated_str_setter(VALUE val, ID id, VALUE *var)
* Writes the given objects to the stream; returns +nil+.
* Appends the output record separator <tt>$OUTPUT_RECORD_SEPARATOR</tt>
* (<tt>$\\</tt>), if it is not +nil+.
+ * See {Line IO}[rdoc-ref:IO@Line+IO].
*
* With argument +objects+ given, for each object:
*
@@ -8383,21 +8709,21 @@ rb_io_print(int argc, const VALUE *argv, VALUE out)
/* if no argument given, print `$_' */
if (argc == 0) {
- argc = 1;
- line = rb_lastline_get();
- argv = &line;
+ argc = 1;
+ line = rb_lastline_get();
+ argv = &line;
}
if (argc > 1 && !NIL_P(rb_output_fs)) {
rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "$, is set to non-nil value");
}
for (i=0; i<argc; i++) {
- if (!NIL_P(rb_output_fs) && i>0) {
- rb_io_write(out, rb_output_fs);
- }
- rb_io_write(out, argv[i]);
+ if (!NIL_P(rb_output_fs) && i>0) {
+ rb_io_write(out, rb_output_fs);
+ }
+ rb_io_write(out, argv[i]);
}
if (argc > 0 && !NIL_P(rb_output_rs)) {
- rb_io_write(out, rb_output_rs);
+ rb_io_write(out, rb_output_rs);
}
return Qnil;
@@ -8464,6 +8790,7 @@ rb_f_print(int argc, const VALUE *argv, VALUE _)
* putc(object) -> object
*
* Writes a character to the stream.
+ * See {Character IO}[rdoc-ref:IO@Character+IO].
*
* If +object+ is numeric, converts to integer if necessary,
* then writes the character whose code is the
@@ -8484,11 +8811,11 @@ rb_io_putc(VALUE io, VALUE ch)
{
VALUE str;
if (RB_TYPE_P(ch, T_STRING)) {
- str = rb_str_substr(ch, 0, 1);
+ str = rb_str_substr(ch, 0, 1);
}
else {
- char c = NUM2CHR(ch);
- str = rb_str_new(&c, 1);
+ char c = NUM2CHR(ch);
+ str = rb_str_new(&c, 1);
}
rb_io_write(io, str);
return ch;
@@ -8518,7 +8845,7 @@ rb_f_putc(VALUE recv, VALUE ch)
{
VALUE r_stdout = rb_ractor_stdout();
if (recv == r_stdout) {
- return rb_io_putc(recv, ch);
+ return rb_io_putc(recv, ch);
}
return forward(r_stdout, rb_intern("putc"), 1, &ch);
}
@@ -8534,7 +8861,7 @@ rb_str_end_with_asciichar(VALUE str, int c)
if (len == 0) return 0;
if ((n = rb_enc_mbminlen(enc)) == 1) {
- return ptr[len - 1] == c;
+ return ptr[len - 1] == c;
}
return rb_enc_ascget(ptr + ((len - 1) / n) * n, ptr + len, &n, enc) == c;
}
@@ -8546,15 +8873,15 @@ io_puts_ary(VALUE ary, VALUE out, int recur)
long i;
if (recur) {
- tmp = rb_str_new2("[...]");
- rb_io_puts(1, &tmp, out);
- return Qtrue;
+ tmp = rb_str_new2("[...]");
+ rb_io_puts(1, &tmp, out);
+ return Qtrue;
}
ary = rb_check_array_type(ary);
if (NIL_P(ary)) return Qfalse;
for (i=0; i<RARRAY_LEN(ary); i++) {
- tmp = RARRAY_AREF(ary, i);
- rb_io_puts(1, &tmp, out);
+ tmp = RARRAY_AREF(ary, i);
+ rb_io_puts(1, &tmp, out);
}
return Qtrue;
}
@@ -8567,6 +8894,7 @@ io_puts_ary(VALUE ary, VALUE out, int recur)
* returns +nil+.\
* Writes a newline after each that does not already end with a newline sequence.
* If called without arguments, writes a newline.
+ * See {Line IO}[rdoc-ref:IO@Line+IO].
*
* Note that each added newline is the character <tt>"\n"<//tt>,
* not the output record separator (<tt>$\\</tt>).
@@ -8608,31 +8936,38 @@ io_puts_ary(VALUE ary, VALUE out, int recur)
VALUE
rb_io_puts(int argc, const VALUE *argv, VALUE out)
{
- int i, n;
VALUE line, args[2];
/* if no argument given, print newline. */
if (argc == 0) {
- rb_io_write(out, rb_default_rs);
- return Qnil;
+ rb_io_write(out, rb_default_rs);
+ return Qnil;
}
- for (i=0; i<argc; i++) {
- if (RB_TYPE_P(argv[i], T_STRING)) {
- line = argv[i];
- goto string;
- }
- if (rb_exec_recursive(io_puts_ary, argv[i], out)) {
- continue;
- }
- line = rb_obj_as_string(argv[i]);
- string:
- n = 0;
- args[n++] = line;
- if (RSTRING_LEN(line) == 0 ||
- !rb_str_end_with_asciichar(line, '\n')) {
- args[n++] = rb_default_rs;
- }
- rb_io_writev(out, n, args);
+ for (int i = 0; i < argc; i++) {
+ // Convert the argument to a string:
+ if (RB_TYPE_P(argv[i], T_STRING)) {
+ line = argv[i];
+ }
+ else if (rb_exec_recursive(io_puts_ary, argv[i], out)) {
+ continue;
+ }
+ else {
+ line = rb_obj_as_string(argv[i]);
+ }
+
+ // Write the line:
+ int n = 0;
+ if (RSTRING_LEN(line) == 0) {
+ args[n++] = rb_default_rs;
+ }
+ else {
+ args[n++] = line;
+ if (!rb_str_end_with_asciichar(line, '\n')) {
+ args[n++] = rb_default_rs;
+ }
+ }
+
+ rb_io_writev(out, n, args);
}
return Qnil;
@@ -8652,7 +8987,7 @@ rb_f_puts(int argc, VALUE *argv, VALUE recv)
{
VALUE r_stdout = rb_ractor_stdout();
if (recv == r_stdout) {
- return rb_io_puts(argc, argv, recv);
+ return rb_io_puts(argc, argv, recv);
}
return forward(r_stdout, rb_intern("puts"), argc, argv);
}
@@ -8666,10 +9001,10 @@ rb_p_write(VALUE str)
VALUE r_stdout = rb_ractor_stdout();
if (RB_TYPE_P(r_stdout, T_FILE) &&
rb_method_basic_definition_p(CLASS_OF(r_stdout), id_write)) {
- io_writev(2, args, r_stdout);
+ io_writev(2, args, r_stdout);
}
else {
- rb_io_writev(r_stdout, 2, args);
+ rb_io_writev(r_stdout, 2, args);
}
return Qnil;
}
@@ -8777,17 +9112,17 @@ rb_write_error2(const char *mesg, long len)
VALUE out = rb_ractor_stderr();
if (rb_stderr_to_original_p(out)) {
#ifdef _WIN32
- if (isatty(fileno(stderr))) {
- if (rb_w32_write_console(rb_str_new(mesg, len), fileno(stderr)) > 0) return;
- }
+ if (isatty(fileno(stderr))) {
+ if (rb_w32_write_console(rb_str_new(mesg, len), fileno(stderr)) > 0) return;
+ }
#endif
- if (fwrite(mesg, sizeof(char), (size_t)len, stderr) < (size_t)len) {
- /* failed to write to stderr, what can we do? */
- return;
- }
+ if (fwrite(mesg, sizeof(char), (size_t)len, stderr) < (size_t)len) {
+ /* failed to write to stderr, what can we do? */
+ return;
+ }
}
else {
- rb_io_write(out, rb_str_new(mesg, len));
+ rb_io_write(out, rb_str_new(mesg, len));
}
}
@@ -8803,20 +9138,20 @@ rb_write_error_str(VALUE mesg)
VALUE out = rb_ractor_stderr();
/* a stopgap measure for the time being */
if (rb_stderr_to_original_p(out)) {
- size_t len = (size_t)RSTRING_LEN(mesg);
+ size_t len = (size_t)RSTRING_LEN(mesg);
#ifdef _WIN32
- if (isatty(fileno(stderr))) {
- if (rb_w32_write_console(mesg, fileno(stderr)) > 0) return;
- }
+ if (isatty(fileno(stderr))) {
+ if (rb_w32_write_console(mesg, fileno(stderr)) > 0) return;
+ }
#endif
- if (fwrite(RSTRING_PTR(mesg), sizeof(char), len, stderr) < len) {
- RB_GC_GUARD(mesg);
- return;
- }
+ if (fwrite(RSTRING_PTR(mesg), sizeof(char), len, stderr) < len) {
+ RB_GC_GUARD(mesg);
+ return;
+ }
}
else {
- /* may unlock GVL, and */
- rb_io_write(out, mesg);
+ /* may unlock GVL, and */
+ rb_io_write(out, mesg);
}
}
@@ -8824,7 +9159,7 @@ int
rb_stderr_tty_p(void)
{
if (rb_stderr_to_original_p(rb_ractor_stderr()))
- return isatty(fileno(stderr));
+ return isatty(fileno(stderr));
return 0;
}
@@ -8832,9 +9167,9 @@ static void
must_respond_to(ID mid, VALUE val, ID id)
{
if (!rb_respond_to(val, mid)) {
- rb_raise(rb_eTypeError, "%"PRIsVALUE" must have %"PRIsVALUE" method, %"PRIsVALUE" given",
- rb_id2str(id), rb_id2str(mid),
- rb_obj_class(val));
+ rb_raise(rb_eTypeError, "%"PRIsVALUE" must have %"PRIsVALUE" method, %"PRIsVALUE" given",
+ rb_id2str(id), rb_id2str(mid),
+ rb_obj_class(val));
}
}
@@ -8886,10 +9221,11 @@ prep_io(int fd, int fmode, VALUE klass, const char *path)
fp->self = io;
fp->fd = fd;
fp->mode = fmode;
+ fp->timeout = Qnil;
if (!io_check_tty(fp)) {
#ifdef __CYGWIN__
- fp->mode |= FMODE_BINMODE;
- setmode(fd, O_BINARY);
+ fp->mode |= FMODE_BINMODE;
+ setmode(fd, O_BINARY);
#endif
}
if (path) fp->pathv = rb_obj_freeze(rb_str_new_cstr(path));
@@ -8918,7 +9254,7 @@ prep_stdio(FILE *f, int fmode, VALUE klass, const char *path)
#ifdef TEXTMODE_NEWLINE_DECORATOR_ON_WRITE
fptr->encs.ecflags |= TEXTMODE_NEWLINE_DECORATOR_ON_WRITE;
if (fmode & FMODE_READABLE) {
- fptr->encs.ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
+ fptr->encs.ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
}
#endif
fptr->stdio_file = f;
@@ -8990,6 +9326,7 @@ rb_io_fptr_new(void)
fp->encs.ecflags = 0;
fp->encs.ecopts = Qnil;
fp->write_lock = Qnil;
+ fp->timeout = Qnil;
return fp;
}
@@ -9033,8 +9370,8 @@ rb_io_make_open_file(VALUE obj)
* io = IO.new(fd)
* io.external_encoding # => #<Encoding:UTF-8> # Not ASCII-8BIT.
*
- * Optional argument +mode+ (defaults to 'r') must specify a valid mode
- * see IO@Modes:
+ * Optional argument +mode+ (defaults to 'r') must specify a valid mode;
+ * see {Access Modes}[rdoc-ref:File@Access+Modes]:
*
* IO.new(fd, 'w') # => #<IO:fd 3>
* IO.new(fd, File::WRONLY) # => #<IO:fd 3>
@@ -9071,7 +9408,7 @@ rb_io_initialize(int argc, VALUE *argv, VALUE io)
fd = NUM2INT(fnum);
if (rb_reserved_fd_p(fd)) {
- rb_raise(rb_eArgError, "The given fd is not accessible because RubyVM reserves it");
+ rb_raise(rb_eArgError, "The given fd is not accessible because RubyVM reserves it");
}
#if defined(HAVE_FCNTL) && defined(F_GETFL)
oflags = fcntl(fd, F_GETFL);
@@ -9083,29 +9420,42 @@ rb_io_initialize(int argc, VALUE *argv, VALUE io)
#if defined(HAVE_FCNTL) && defined(F_GETFL)
ofmode = rb_io_oflags_fmode(oflags);
if (NIL_P(vmode)) {
- fmode = ofmode;
+ fmode = ofmode;
}
else if ((~ofmode & fmode) & FMODE_READWRITE) {
- VALUE error = INT2FIX(EINVAL);
- rb_exc_raise(rb_class_new_instance(1, &error, rb_eSystemCallError));
+ VALUE error = INT2FIX(EINVAL);
+ rb_exc_raise(rb_class_new_instance(1, &error, rb_eSystemCallError));
}
#endif
- if (!NIL_P(opt) && rb_hash_aref(opt, sym_autoclose) == Qfalse) {
- fmode |= FMODE_PREP;
+ VALUE path = Qnil;
+
+ if (!NIL_P(opt)) {
+ if (rb_hash_aref(opt, sym_autoclose) == Qfalse) {
+ fmode |= FMODE_PREP;
+ }
+
+ path = rb_hash_aref(opt, RB_ID2SYM(idPath));
+ if (!NIL_P(path)) {
+ StringValue(path);
+ path = rb_str_new_frozen(path);
+ }
}
+
MakeOpenFile(io, fp);
fp->self = io;
fp->fd = fd;
fp->mode = fmode;
fp->encs = convconfig;
+ fp->pathv = path;
+ fp->timeout = Qnil;
clear_codeconv(fp);
io_check_tty(fp);
if (fileno(stdin) == fd)
- fp->stdio_file = stdin;
+ fp->stdio_file = stdin;
else if (fileno(stdout) == fd)
- fp->stdio_file = stdout;
+ fp->stdio_file = stdout;
else if (fileno(stderr) == fd)
- fp->stdio_file = stderr;
+ fp->stdio_file = stderr;
if (fmode & FMODE_SETENC_BY_BOM) io_set_encoding_by_bom(io);
return io;
@@ -9168,46 +9518,47 @@ rb_io_set_encoding_by_bom(VALUE io)
*
* Argument +path+ must be a valid file path:
*
- * File.new('/etc/fstab')
- * File.new('t.txt')
+ * f = File.new('/etc/fstab')
+ * f.close
+ * f = File.new('t.txt')
+ * f.close
*
- * Optional argument +mode+ (defaults to 'r') must specify a valid mode
- * see IO@Modes:
+ * Optional argument +mode+ (defaults to 'r') must specify a valid mode;
+ * see {Access Modes}[rdoc-ref:File@Access+Modes]:
*
- * File.new('t.tmp', 'w')
- * File.new('t.tmp', File::RDONLY)
+ * f = File.new('t.tmp', 'w')
+ * f.close
+ * f = File.new('t.tmp', File::RDONLY)
+ * f.close
*
* Optional argument +perm+ (defaults to 0666) must specify valid permissions
- * see {File Permissions}[rdoc-ref:IO@File+Permissions]:
+ * see {File Permissions}[rdoc-ref:File@File+Permissions]:
*
- * File.new('t.tmp', File::CREAT, 0644)
- * File.new('t.tmp', File::CREAT, 0444)
+ * f = File.new('t.tmp', File::CREAT, 0644)
+ * f.close
+ * f = File.new('t.tmp', File::CREAT, 0444)
+ * f.close
*
* Optional keyword arguments +opts+ specify:
*
* - {Open Options}[rdoc-ref:IO@Open+Options].
* - {Encoding options}[rdoc-ref:encodings.rdoc@Encoding+Options].
*
- * Examples:
- *
- * File.new('t.tmp', autoclose: true)
- * File.new('t.tmp', internal_encoding: nil)
- *
*/
static VALUE
rb_file_initialize(int argc, VALUE *argv, VALUE io)
{
if (RFILE(io)->fptr) {
- rb_raise(rb_eRuntimeError, "reinitializing File");
+ rb_raise(rb_eRuntimeError, "reinitializing File");
}
if (0 < argc && argc < 3) {
- VALUE fd = rb_check_to_int(argv[0]);
+ VALUE fd = rb_check_to_int(argv[0]);
- if (!NIL_P(fd)) {
- argv[0] = fd;
- return rb_io_initialize(argc, argv, io);
- }
+ if (!NIL_P(fd)) {
+ argv[0] = fd;
+ return rb_io_initialize(argc, argv, io);
+ }
}
rb_open_file(argc, argv, io);
@@ -9219,10 +9570,10 @@ static VALUE
rb_io_s_new(int argc, VALUE *argv, VALUE klass)
{
if (rb_block_given_p()) {
- VALUE cname = rb_obj_as_string(klass);
+ VALUE cname = rb_obj_as_string(klass);
- rb_warn("%"PRIsVALUE"::new() does not take block; use %"PRIsVALUE"::open() instead",
- cname, cname);
+ rb_warn("%"PRIsVALUE"::new() does not take block; use %"PRIsVALUE"::open() instead",
+ cname, cname);
}
return rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS);
}
@@ -9283,9 +9634,9 @@ rb_io_set_autoclose(VALUE io, VALUE autoclose)
rb_io_t *fptr;
GetOpenFile(io, fptr);
if (!RTEST(autoclose))
- fptr->mode |= FMODE_PREP;
+ fptr->mode |= FMODE_PREP;
else
- fptr->mode &= ~FMODE_PREP;
+ fptr->mode &= ~FMODE_PREP;
return autoclose;
}
@@ -9465,7 +9816,7 @@ io_wait(int argc, VALUE *argv, VALUE io)
if (RB_SYMBOL_P(argv[i])) {
events |= wait_mode_sym(argv[i]);
}
- else if (timeout == Qundef) {
+ else if (UNDEF_P(timeout)) {
rb_time_interval(timeout = argv[i]);
}
else {
@@ -9473,7 +9824,7 @@ io_wait(int argc, VALUE *argv, VALUE io)
}
}
- if (timeout == Qundef) timeout = Qnil;
+ if (UNDEF_P(timeout)) timeout = Qnil;
if (events == 0) {
events = RUBY_IO_READABLE;
@@ -9570,11 +9921,11 @@ argf_initialize_copy(VALUE argf, VALUE orig)
* call-seq:
* ARGF.lineno = integer -> integer
*
- * Sets the line number of +ARGF+ as a whole to the given +Integer+.
+ * Sets the line number of ARGF as a whole to the given Integer.
*
- * +ARGF+ sets the line number automatically as you read data, so normally
+ * ARGF sets the line number automatically as you read data, so normally
* you will not need to set it explicitly. To access the current line number
- * use +ARGF.lineno+.
+ * use ARGF.lineno.
*
* For example:
*
@@ -9597,7 +9948,7 @@ argf_set_lineno(VALUE argf, VALUE val)
* ARGF.lineno -> integer
*
* Returns the current line number of ARGF as a whole. This value
- * can be set manually with +ARGF.lineno=+.
+ * can be set manually with ARGF.lineno=.
*
* For example:
*
@@ -9622,7 +9973,7 @@ argf_forward(int argc, VALUE *argv, VALUE argf)
(ARGF.current_file == rb_stdin && !RB_TYPE_P(ARGF.current_file, T_FILE))
#define ARGF_FORWARD(argc, argv) do {\
if (ARGF_GENERIC_INPUT_P())\
- return argf_forward((argc), (argv), argf);\
+ return argf_forward((argc), (argv), argf);\
} while (0)
#define NEXT_ARGF_FORWARD(argc, argv) do {\
if (!next_argv()) return Qnil;\
@@ -9635,7 +9986,7 @@ argf_close(VALUE argf)
VALUE file = ARGF.current_file;
if (file == rb_stdin) return;
if (RB_TYPE_P(file, T_FILE)) {
- rb_io_set_write_io(file, Qnil);
+ rb_io_set_write_io(file, Qnil);
}
io_close(file);
ARGF.init_p = -1;
@@ -9658,163 +10009,163 @@ argf_next_argv(VALUE argf)
}
if (ARGF.init_p == 0) {
- if (!NIL_P(ARGF.argv) && RARRAY_LEN(ARGF.argv) > 0) {
- ARGF.next_p = 1;
- }
- else {
- ARGF.next_p = -1;
- }
- ARGF.init_p = 1;
+ if (!NIL_P(ARGF.argv) && RARRAY_LEN(ARGF.argv) > 0) {
+ ARGF.next_p = 1;
+ }
+ else {
+ ARGF.next_p = -1;
+ }
+ ARGF.init_p = 1;
}
else {
- if (NIL_P(ARGF.argv)) {
- ARGF.next_p = -1;
- }
- else if (ARGF.next_p == -1 && RARRAY_LEN(ARGF.argv) > 0) {
- ARGF.next_p = 1;
- }
+ if (NIL_P(ARGF.argv)) {
+ ARGF.next_p = -1;
+ }
+ else if (ARGF.next_p == -1 && RARRAY_LEN(ARGF.argv) > 0) {
+ ARGF.next_p = 1;
+ }
}
if (ARGF.next_p == 1) {
- if (ARGF.init_p == 1) argf_close(argf);
+ if (ARGF.init_p == 1) argf_close(argf);
retry:
- if (RARRAY_LEN(ARGF.argv) > 0) {
- VALUE filename = rb_ary_shift(ARGF.argv);
- FilePathValue(filename);
- ARGF.filename = filename;
- filename = rb_str_encode_ospath(filename);
- fn = StringValueCStr(filename);
- if (RSTRING_LEN(filename) == 1 && fn[0] == '-') {
- ARGF.current_file = rb_stdin;
- if (ARGF.inplace) {
- rb_warn("Can't do inplace edit for stdio; skipping");
- goto retry;
- }
- }
- else {
- VALUE write_io = Qnil;
- int fr = rb_sysopen(filename, O_RDONLY, 0);
-
- if (ARGF.inplace) {
- struct stat st;
+ if (RARRAY_LEN(ARGF.argv) > 0) {
+ VALUE filename = rb_ary_shift(ARGF.argv);
+ FilePathValue(filename);
+ ARGF.filename = filename;
+ filename = rb_str_encode_ospath(filename);
+ fn = StringValueCStr(filename);
+ if (RSTRING_LEN(filename) == 1 && fn[0] == '-') {
+ ARGF.current_file = rb_stdin;
+ if (ARGF.inplace) {
+ rb_warn("Can't do inplace edit for stdio; skipping");
+ goto retry;
+ }
+ }
+ else {
+ VALUE write_io = Qnil;
+ int fr = rb_sysopen(filename, O_RDONLY, 0);
+
+ if (ARGF.inplace) {
+ struct stat st;
#ifndef NO_SAFE_RENAME
- struct stat st2;
-#endif
- VALUE str;
- int fw;
-
- if (RB_TYPE_P(r_stdout, T_FILE) && r_stdout != orig_stdout) {
- rb_io_close(r_stdout);
- }
- fstat(fr, &st);
- str = filename;
- if (!NIL_P(ARGF.inplace)) {
- VALUE suffix = ARGF.inplace;
- str = rb_str_dup(str);
- if (NIL_P(rb_str_cat_conv_enc_opts(str, RSTRING_LEN(str),
- RSTRING_PTR(suffix), RSTRING_LEN(suffix),
- rb_enc_get(suffix), 0, Qnil))) {
- rb_str_append(str, suffix);
- }
+ struct stat st2;
+#endif
+ VALUE str;
+ int fw;
+
+ if (RB_TYPE_P(r_stdout, T_FILE) && r_stdout != orig_stdout) {
+ rb_io_close(r_stdout);
+ }
+ fstat(fr, &st);
+ str = filename;
+ if (!NIL_P(ARGF.inplace)) {
+ VALUE suffix = ARGF.inplace;
+ str = rb_str_dup(str);
+ if (NIL_P(rb_str_cat_conv_enc_opts(str, RSTRING_LEN(str),
+ RSTRING_PTR(suffix), RSTRING_LEN(suffix),
+ rb_enc_get(suffix), 0, Qnil))) {
+ rb_str_append(str, suffix);
+ }
#ifdef NO_SAFE_RENAME
- (void)close(fr);
- (void)unlink(RSTRING_PTR(str));
- if (rename(fn, RSTRING_PTR(str)) < 0) {
- rb_warn("Can't rename %"PRIsVALUE" to %"PRIsVALUE": %s, skipping file",
- filename, str, strerror(errno));
- goto retry;
- }
- fr = rb_sysopen(str, O_RDONLY, 0);
+ (void)close(fr);
+ (void)unlink(RSTRING_PTR(str));
+ if (rename(fn, RSTRING_PTR(str)) < 0) {
+ rb_warn("Can't rename %"PRIsVALUE" to %"PRIsVALUE": %s, skipping file",
+ filename, str, strerror(errno));
+ goto retry;
+ }
+ fr = rb_sysopen(str, O_RDONLY, 0);
#else
- if (rename(fn, RSTRING_PTR(str)) < 0) {
- rb_warn("Can't rename %"PRIsVALUE" to %"PRIsVALUE": %s, skipping file",
- filename, str, strerror(errno));
- close(fr);
- goto retry;
- }
-#endif
- }
- else {
+ if (rename(fn, RSTRING_PTR(str)) < 0) {
+ rb_warn("Can't rename %"PRIsVALUE" to %"PRIsVALUE": %s, skipping file",
+ filename, str, strerror(errno));
+ close(fr);
+ goto retry;
+ }
+#endif
+ }
+ else {
#ifdef NO_SAFE_RENAME
- rb_fatal("Can't do inplace edit without backup");
+ rb_fatal("Can't do inplace edit without backup");
#else
- if (unlink(fn) < 0) {
- rb_warn("Can't remove %"PRIsVALUE": %s, skipping file",
- filename, strerror(errno));
- close(fr);
- goto retry;
- }
-#endif
- }
- fw = rb_sysopen(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (unlink(fn) < 0) {
+ rb_warn("Can't remove %"PRIsVALUE": %s, skipping file",
+ filename, strerror(errno));
+ close(fr);
+ goto retry;
+ }
+#endif
+ }
+ fw = rb_sysopen(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
#ifndef NO_SAFE_RENAME
- fstat(fw, &st2);
+ fstat(fw, &st2);
#ifdef HAVE_FCHMOD
- fchmod(fw, st.st_mode);
+ fchmod(fw, st.st_mode);
#else
- chmod(fn, st.st_mode);
+ chmod(fn, st.st_mode);
#endif
- if (st.st_uid!=st2.st_uid || st.st_gid!=st2.st_gid) {
- int err;
+ if (st.st_uid!=st2.st_uid || st.st_gid!=st2.st_gid) {
+ int err;
#ifdef HAVE_FCHOWN
- err = fchown(fw, st.st_uid, st.st_gid);
+ err = fchown(fw, st.st_uid, st.st_gid);
#else
- err = chown(fn, st.st_uid, st.st_gid);
-#endif
- if (err && getuid() == 0 && st2.st_uid == 0) {
- const char *wkfn = RSTRING_PTR(filename);
- rb_warn("Can't set owner/group of %"PRIsVALUE" to same as %"PRIsVALUE": %s, skipping file",
- filename, str, strerror(errno));
- (void)close(fr);
- (void)close(fw);
- (void)unlink(wkfn);
- goto retry;
- }
- }
-#endif
- write_io = prep_io(fw, FMODE_WRITABLE, rb_cFile, fn);
- rb_ractor_stdout_set(write_io);
- if (stdout_binmode) rb_io_binmode(rb_stdout);
- }
- fmode = FMODE_READABLE;
- if (!ARGF.binmode) {
- fmode |= DEFAULT_TEXTMODE;
- }
- ARGF.current_file = prep_io(fr, fmode, rb_cFile, fn);
- if (!NIL_P(write_io)) {
- rb_io_set_write_io(ARGF.current_file, write_io);
- }
- RB_GC_GUARD(filename);
- }
- if (ARGF.binmode) rb_io_ascii8bit_binmode(ARGF.current_file);
- GetOpenFile(ARGF.current_file, fptr);
- if (ARGF.encs.enc) {
- fptr->encs = ARGF.encs;
+ err = chown(fn, st.st_uid, st.st_gid);
+#endif
+ if (err && getuid() == 0 && st2.st_uid == 0) {
+ const char *wkfn = RSTRING_PTR(filename);
+ rb_warn("Can't set owner/group of %"PRIsVALUE" to same as %"PRIsVALUE": %s, skipping file",
+ filename, str, strerror(errno));
+ (void)close(fr);
+ (void)close(fw);
+ (void)unlink(wkfn);
+ goto retry;
+ }
+ }
+#endif
+ write_io = prep_io(fw, FMODE_WRITABLE, rb_cFile, fn);
+ rb_ractor_stdout_set(write_io);
+ if (stdout_binmode) rb_io_binmode(rb_stdout);
+ }
+ fmode = FMODE_READABLE;
+ if (!ARGF.binmode) {
+ fmode |= DEFAULT_TEXTMODE;
+ }
+ ARGF.current_file = prep_io(fr, fmode, rb_cFile, fn);
+ if (!NIL_P(write_io)) {
+ rb_io_set_write_io(ARGF.current_file, write_io);
+ }
+ RB_GC_GUARD(filename);
+ }
+ if (ARGF.binmode) rb_io_ascii8bit_binmode(ARGF.current_file);
+ GetOpenFile(ARGF.current_file, fptr);
+ if (ARGF.encs.enc) {
+ fptr->encs = ARGF.encs;
clear_codeconv(fptr);
- }
- else {
- fptr->encs.ecflags &= ~ECONV_NEWLINE_DECORATOR_MASK;
- if (!ARGF.binmode) {
- fptr->encs.ecflags |= ECONV_DEFAULT_NEWLINE_DECORATOR;
+ }
+ else {
+ fptr->encs.ecflags &= ~ECONV_NEWLINE_DECORATOR_MASK;
+ if (!ARGF.binmode) {
+ fptr->encs.ecflags |= ECONV_DEFAULT_NEWLINE_DECORATOR;
#ifdef TEXTMODE_NEWLINE_DECORATOR_ON_WRITE
- fptr->encs.ecflags |= TEXTMODE_NEWLINE_DECORATOR_ON_WRITE;
+ fptr->encs.ecflags |= TEXTMODE_NEWLINE_DECORATOR_ON_WRITE;
#endif
- }
- }
- ARGF.next_p = 0;
- }
- else {
- ARGF.next_p = 1;
- return FALSE;
- }
+ }
+ }
+ ARGF.next_p = 0;
+ }
+ else {
+ ARGF.next_p = 1;
+ return FALSE;
+ }
}
else if (ARGF.next_p == -1) {
- ARGF.current_file = rb_stdin;
- ARGF.filename = rb_str_new2("-");
- if (ARGF.inplace) {
- rb_warn("Can't do inplace edit for stdio");
- rb_ractor_stdout_set(orig_stdout);
- }
+ ARGF.current_file = rb_stdin;
+ ARGF.filename = rb_str_new2("-");
+ if (ARGF.inplace) {
+ rb_warn("Can't do inplace edit for stdio");
+ rb_ractor_stdout_set(orig_stdout);
+ }
}
if (ARGF.init_p == -1) ARGF.init_p = 1;
return TRUE;
@@ -9829,24 +10180,24 @@ argf_getline(int argc, VALUE *argv, VALUE argf)
retry:
if (!next_argv()) return Qnil;
if (ARGF_GENERIC_INPUT_P()) {
- line = forward_current(idGets, argc, argv);
+ line = forward_current(idGets, argc, argv);
}
else {
- if (argc == 0 && rb_rs == rb_default_rs) {
- line = rb_io_gets(ARGF.current_file);
- }
- else {
- line = rb_io_getline(argc, argv, ARGF.current_file);
- }
- if (NIL_P(line) && ARGF.next_p != -1) {
- argf_close(argf);
- ARGF.next_p = 1;
- goto retry;
- }
+ if (argc == 0 && rb_rs == rb_default_rs) {
+ line = rb_io_gets(ARGF.current_file);
+ }
+ else {
+ line = rb_io_getline(argc, argv, ARGF.current_file);
+ }
+ if (NIL_P(line) && ARGF.next_p != -1) {
+ argf_close(argf);
+ ARGF.next_p = 1;
+ goto retry;
+ }
}
if (!NIL_P(line)) {
- ARGF.lineno = ++lineno;
- ARGF.last_lineno = ARGF.lineno;
+ ARGF.lineno = ++lineno;
+ ARGF.last_lineno = ARGF.lineno;
}
return line;
}
@@ -9911,7 +10262,7 @@ static VALUE
rb_f_gets(int argc, VALUE *argv, VALUE recv)
{
if (recv == argf) {
- return argf_gets(argc, argv, argf);
+ return argf_gets(argc, argv, argf);
}
return forward(argf, idGets, argc, argv);
}
@@ -9922,10 +10273,10 @@ rb_f_gets(int argc, VALUE *argv, VALUE recv)
* ARGF.gets(limit [, getline_args]) -> string or nil
* ARGF.gets(sep, limit [, getline_args]) -> string or nil
*
- * Returns the next line from the current file in +ARGF+.
+ * Returns the next line from the current file in ARGF.
*
* By default lines are assumed to be separated by <code>$/</code>;
- * to use a different character as a separator, supply it as a +String+
+ * to use a different character as a separator, supply it as a String
* for the _sep_ argument.
*
* The optional _limit_ argument specifies how many characters of each line
@@ -9951,21 +10302,21 @@ rb_gets(void)
VALUE line;
if (rb_rs != rb_default_rs) {
- return rb_f_gets(0, 0, argf);
+ return rb_f_gets(0, 0, argf);
}
retry:
if (!next_argv()) return Qnil;
line = rb_io_gets(ARGF.current_file);
if (NIL_P(line) && ARGF.next_p != -1) {
- rb_io_close(ARGF.current_file);
- ARGF.next_p = 1;
- goto retry;
+ rb_io_close(ARGF.current_file);
+ ARGF.next_p = 1;
+ goto retry;
}
rb_lastline_set(line);
if (!NIL_P(line)) {
- ARGF.lineno++;
- ARGF.last_lineno = ARGF.lineno;
+ ARGF.lineno++;
+ ARGF.last_lineno = ARGF.lineno;
}
return line;
@@ -9975,9 +10326,9 @@ static VALUE argf_readline(int, VALUE *, VALUE);
/*
* call-seq:
- * readline(sep = $/, **line_opts) -> string
- * readline(limit, **line_opts) -> string
- * readline(sep, limit, **line_opts) -> string
+ * readline(sep = $/, chomp: false) -> string
+ * readline(limit, chomp: false) -> string
+ * readline(sep, limit, chomp: false) -> string
*
* Equivalent to method Kernel#gets, except that it raises an exception
* if called at end-of-stream:
@@ -9986,13 +10337,15 @@ static VALUE argf_readline(int, VALUE *, VALUE);
* ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"]
* in `readline': end of file reached (EOFError)
*
+ * Optional keyword argument +chomp+ specifies whether line separators
+ * are to be omitted.
*/
static VALUE
rb_f_readline(int argc, VALUE *argv, VALUE recv)
{
if (recv == argf) {
- return argf_readline(argc, argv, argf);
+ return argf_readline(argc, argv, argf);
}
return forward(argf, rb_intern("readline"), argc, argv);
}
@@ -10004,16 +10357,16 @@ rb_f_readline(int argc, VALUE *argv, VALUE recv)
* ARGF.readline(limit) -> string
* ARGF.readline(sep, limit) -> string
*
- * Returns the next line from the current file in +ARGF+.
+ * Returns the next line from the current file in ARGF.
*
* By default lines are assumed to be separated by <code>$/</code>;
- * to use a different character as a separator, supply it as a +String+
+ * to use a different character as a separator, supply it as a String
* for the _sep_ argument.
*
* The optional _limit_ argument specifies how many characters of each line
* to return. By default all characters are returned.
*
- * An +EOFError+ is raised at the end of the file.
+ * An EOFError is raised at the end of the file.
*/
static VALUE
argf_readline(int argc, VALUE *argv, VALUE argf)
@@ -10024,7 +10377,7 @@ argf_readline(int argc, VALUE *argv, VALUE argf)
ARGF_FORWARD(argc, argv);
line = argf_gets(argc, argv, argf);
if (NIL_P(line)) {
- rb_eof_error();
+ rb_eof_error();
}
return line;
@@ -10034,13 +10387,13 @@ static VALUE argf_readlines(int, VALUE *, VALUE);
/*
* call-seq:
- * readlines(sep = $/, **line_opts) -> array
- * readlines(limit, **line_opts) -> array
- * readlines(sep, limit, **line_opts) -> array
+ * readlines(sep = $/, chomp: false, **enc_opts) -> array
+ * readlines(limit, chomp: false, **enc_opts) -> array
+ * readlines(sep, limit, chomp: false, **enc_opts) -> array
*
* Returns an array containing the lines returned by calling
- * Kernel#gets until the end-of-file is reached;
- * (see {Lines}[rdoc-ref:IO@Lines]).
+ * Kernel#gets until the end-of-stream is reached;
+ * (see {Line IO}[rdoc-ref:IO@Line+IO]).
*
* With only string argument +sep+ given,
* returns the remaining lines as determined by line separator +sep+,
@@ -10079,23 +10432,22 @@ static VALUE argf_readlines(int, VALUE *, VALUE);
* With arguments +sep+ and +limit+ given, combines the two behaviors;
* see {Line Separator and Line Limit}[rdoc-ref:IO@Line+Separator+and+Line+Limit].
*
- * For all forms above, optional keyword arguments specify:
- *
- * - {Line Options}[rdoc-ref:IO@Line+Options].
- * - {Encoding options}[rdoc-ref:encodings.rdoc@Encoding+Options].
- *
- * Examples:
+ * Optional keyword argument +chomp+ specifies whether line separators
+ * are to be omitted:
*
* $ cat t.txt | ruby -e "p readlines(chomp: true)"
* ["First line", "Second line", "", "Fourth line", "Fifth line"]
*
+ * Optional keyword arguments +enc_opts+ specify encoding options;
+ * see {Encoding options}[rdoc-ref:encodings.rdoc@Encoding+Options].
+ *
*/
static VALUE
rb_f_readlines(int argc, VALUE *argv, VALUE recv)
{
if (recv == argf) {
- return argf_readlines(argc, argv, argf);
+ return argf_readlines(argc, argv, argf);
}
return forward(argf, rb_intern("readlines"), argc, argv);
}
@@ -10110,7 +10462,7 @@ rb_f_readlines(int argc, VALUE *argv, VALUE recv)
* ARGF.to_a(limit) -> array
* ARGF.to_a(sep, limit) -> array
*
- * Reads each file in +ARGF+ in its entirety, returning an +Array+ containing
+ * Reads each file in ARGF in its entirety, returning an Array containing
* lines from the files. Lines are assumed to be separated by _sep_.
*
* lines = ARGF.readlines
@@ -10124,17 +10476,17 @@ argf_readlines(int argc, VALUE *argv, VALUE argf)
ary = rb_ary_new();
while (next_argv()) {
- if (ARGF_GENERIC_INPUT_P()) {
- lines = forward_current(rb_intern("readlines"), argc, argv);
- }
- else {
- lines = rb_io_readlines(argc, argv, ARGF.current_file);
- argf_close(argf);
- }
- ARGF.next_p = 1;
- rb_ary_concat(ary, lines);
- ARGF.lineno = lineno + RARRAY_LEN(ary);
- ARGF.last_lineno = ARGF.lineno;
+ if (ARGF_GENERIC_INPUT_P()) {
+ lines = forward_current(rb_intern("readlines"), argc, argv);
+ }
+ else {
+ lines = rb_io_readlines(argc, argv, ARGF.current_file);
+ argf_close(argf);
+ }
+ ARGF.next_p = 1;
+ rb_ary_concat(ary, lines);
+ ARGF.lineno = lineno + RARRAY_LEN(ary);
+ ARGF.last_lineno = ARGF.lineno;
}
ARGF.init_p = 0;
return ary;
@@ -10199,63 +10551,63 @@ select_internal(VALUE read, VALUE write, VALUE except, struct timeval *tp, rb_fd
struct timeval timerec;
if (!NIL_P(read)) {
- Check_Type(read, T_ARRAY);
- for (i=0; i<RARRAY_LEN(read); i++) {
- GetOpenFile(rb_io_get_io(RARRAY_AREF(read, i)), fptr);
- rb_fd_set(fptr->fd, &fds[0]);
- if (READ_DATA_PENDING(fptr) || READ_CHAR_PENDING(fptr)) { /* check for buffered data */
- pending++;
- rb_fd_set(fptr->fd, &fds[3]);
- }
- if (max < fptr->fd) max = fptr->fd;
- }
- if (pending) { /* no blocking if there's buffered data */
- timerec.tv_sec = timerec.tv_usec = 0;
- tp = &timerec;
- }
- rp = &fds[0];
+ Check_Type(read, T_ARRAY);
+ for (i=0; i<RARRAY_LEN(read); i++) {
+ GetOpenFile(rb_io_get_io(RARRAY_AREF(read, i)), fptr);
+ rb_fd_set(fptr->fd, &fds[0]);
+ if (READ_DATA_PENDING(fptr) || READ_CHAR_PENDING(fptr)) { /* check for buffered data */
+ pending++;
+ rb_fd_set(fptr->fd, &fds[3]);
+ }
+ if (max < fptr->fd) max = fptr->fd;
+ }
+ if (pending) { /* no blocking if there's buffered data */
+ timerec.tv_sec = timerec.tv_usec = 0;
+ tp = &timerec;
+ }
+ rp = &fds[0];
}
else
- rp = 0;
+ rp = 0;
if (!NIL_P(write)) {
- Check_Type(write, T_ARRAY);
- for (i=0; i<RARRAY_LEN(write); i++) {
+ Check_Type(write, T_ARRAY);
+ for (i=0; i<RARRAY_LEN(write); i++) {
VALUE write_io = GetWriteIO(rb_io_get_io(RARRAY_AREF(write, i)));
- GetOpenFile(write_io, fptr);
- rb_fd_set(fptr->fd, &fds[1]);
- if (max < fptr->fd) max = fptr->fd;
- }
- wp = &fds[1];
+ GetOpenFile(write_io, fptr);
+ rb_fd_set(fptr->fd, &fds[1]);
+ if (max < fptr->fd) max = fptr->fd;
+ }
+ wp = &fds[1];
}
else
- wp = 0;
+ wp = 0;
if (!NIL_P(except)) {
- Check_Type(except, T_ARRAY);
- for (i=0; i<RARRAY_LEN(except); i++) {
+ Check_Type(except, T_ARRAY);
+ for (i=0; i<RARRAY_LEN(except); i++) {
VALUE io = rb_io_get_io(RARRAY_AREF(except, i));
VALUE write_io = GetWriteIO(io);
- GetOpenFile(io, fptr);
- rb_fd_set(fptr->fd, &fds[2]);
- if (max < fptr->fd) max = fptr->fd;
+ GetOpenFile(io, fptr);
+ rb_fd_set(fptr->fd, &fds[2]);
+ if (max < fptr->fd) max = fptr->fd;
if (io != write_io) {
GetOpenFile(write_io, fptr);
rb_fd_set(fptr->fd, &fds[2]);
if (max < fptr->fd) max = fptr->fd;
}
- }
- ep = &fds[2];
+ }
+ ep = &fds[2];
}
else {
- ep = 0;
+ ep = 0;
}
max++;
n = rb_thread_fd_select(max, rp, wp, ep, tp);
if (n < 0) {
- rb_sys_fail(0);
+ rb_sys_fail(0);
}
if (!pending && n == 0) return Qnil; /* returns nil on timeout */
@@ -10265,48 +10617,48 @@ select_internal(VALUE read, VALUE write, VALUE except, struct timeval *tp, rb_fd
rb_ary_push(res, ep?rb_ary_new():rb_ary_new2(0));
if (rp) {
- list = RARRAY_AREF(res, 0);
- for (i=0; i< RARRAY_LEN(read); i++) {
- VALUE obj = rb_ary_entry(read, i);
- VALUE io = rb_io_get_io(obj);
- GetOpenFile(io, fptr);
- if (rb_fd_isset(fptr->fd, &fds[0]) ||
- rb_fd_isset(fptr->fd, &fds[3])) {
- rb_ary_push(list, obj);
- }
- }
+ list = RARRAY_AREF(res, 0);
+ for (i=0; i< RARRAY_LEN(read); i++) {
+ VALUE obj = rb_ary_entry(read, i);
+ VALUE io = rb_io_get_io(obj);
+ GetOpenFile(io, fptr);
+ if (rb_fd_isset(fptr->fd, &fds[0]) ||
+ rb_fd_isset(fptr->fd, &fds[3])) {
+ rb_ary_push(list, obj);
+ }
+ }
}
if (wp) {
- list = RARRAY_AREF(res, 1);
- for (i=0; i< RARRAY_LEN(write); i++) {
- VALUE obj = rb_ary_entry(write, i);
- VALUE io = rb_io_get_io(obj);
- VALUE write_io = GetWriteIO(io);
- GetOpenFile(write_io, fptr);
- if (rb_fd_isset(fptr->fd, &fds[1])) {
- rb_ary_push(list, obj);
- }
- }
+ list = RARRAY_AREF(res, 1);
+ for (i=0; i< RARRAY_LEN(write); i++) {
+ VALUE obj = rb_ary_entry(write, i);
+ VALUE io = rb_io_get_io(obj);
+ VALUE write_io = GetWriteIO(io);
+ GetOpenFile(write_io, fptr);
+ if (rb_fd_isset(fptr->fd, &fds[1])) {
+ rb_ary_push(list, obj);
+ }
+ }
}
if (ep) {
- list = RARRAY_AREF(res, 2);
- for (i=0; i< RARRAY_LEN(except); i++) {
- VALUE obj = rb_ary_entry(except, i);
- VALUE io = rb_io_get_io(obj);
- VALUE write_io = GetWriteIO(io);
- GetOpenFile(io, fptr);
- if (rb_fd_isset(fptr->fd, &fds[2])) {
- rb_ary_push(list, obj);
- }
- else if (io != write_io) {
- GetOpenFile(write_io, fptr);
- if (rb_fd_isset(fptr->fd, &fds[2])) {
- rb_ary_push(list, obj);
- }
- }
- }
+ list = RARRAY_AREF(res, 2);
+ for (i=0; i< RARRAY_LEN(except); i++) {
+ VALUE obj = rb_ary_entry(except, i);
+ VALUE io = rb_io_get_io(obj);
+ VALUE write_io = GetWriteIO(io);
+ GetOpenFile(io, fptr);
+ if (rb_fd_isset(fptr->fd, &fds[2])) {
+ rb_ary_push(list, obj);
+ }
+ else if (io != write_io) {
+ GetOpenFile(write_io, fptr);
+ if (rb_fd_isset(fptr->fd, &fds[2])) {
+ rb_ary_push(list, obj);
+ }
+ }
+ }
}
return res; /* returns an empty array on interrupt */
@@ -10333,7 +10685,7 @@ select_end(VALUE arg)
int i;
for (i = 0; i < numberof(p->fdsets); ++i)
- rb_fd_term(&p->fdsets[i]);
+ rb_fd_term(&p->fdsets[i]);
return Qnil;
}
@@ -10344,8 +10696,8 @@ static VALUE sym_normal, sym_sequential, sym_random,
struct io_advise_struct {
int fd;
int advice;
- off_t offset;
- off_t len;
+ rb_off_t offset;
+ rb_off_t len;
};
static VALUE
@@ -10360,39 +10712,39 @@ io_advise_sym_to_const(VALUE sym)
{
#ifdef POSIX_FADV_NORMAL
if (sym == sym_normal)
- return INT2NUM(POSIX_FADV_NORMAL);
+ return INT2NUM(POSIX_FADV_NORMAL);
#endif
#ifdef POSIX_FADV_RANDOM
if (sym == sym_random)
- return INT2NUM(POSIX_FADV_RANDOM);
+ return INT2NUM(POSIX_FADV_RANDOM);
#endif
#ifdef POSIX_FADV_SEQUENTIAL
if (sym == sym_sequential)
- return INT2NUM(POSIX_FADV_SEQUENTIAL);
+ return INT2NUM(POSIX_FADV_SEQUENTIAL);
#endif
#ifdef POSIX_FADV_WILLNEED
if (sym == sym_willneed)
- return INT2NUM(POSIX_FADV_WILLNEED);
+ return INT2NUM(POSIX_FADV_WILLNEED);
#endif
#ifdef POSIX_FADV_DONTNEED
if (sym == sym_dontneed)
- return INT2NUM(POSIX_FADV_DONTNEED);
+ return INT2NUM(POSIX_FADV_DONTNEED);
#endif
#ifdef POSIX_FADV_NOREUSE
if (sym == sym_noreuse)
- return INT2NUM(POSIX_FADV_NOREUSE);
+ return INT2NUM(POSIX_FADV_NOREUSE);
#endif
return Qnil;
}
static VALUE
-do_io_advise(rb_io_t *fptr, VALUE advice, off_t offset, off_t len)
+do_io_advise(rb_io_t *fptr, VALUE advice, rb_off_t offset, rb_off_t len)
{
int rv;
struct io_advise_struct ias;
@@ -10405,7 +10757,7 @@ do_io_advise(rb_io_t *fptr, VALUE advice, off_t offset, off_t len)
* silently ignore it. Because IO::advise is only hint.
*/
if (NIL_P(num_adv))
- return Qnil;
+ return Qnil;
ias.fd = fptr->fd;
ias.advice = NUM2INT(num_adv);
@@ -10414,14 +10766,14 @@ do_io_advise(rb_io_t *fptr, VALUE advice, off_t offset, off_t len)
rv = (int)rb_thread_io_blocking_region(io_advise_internal, &ias, fptr->fd);
if (rv && rv != ENOSYS) {
- /* posix_fadvise(2) doesn't set errno. On success it returns 0; otherwise
- it returns the error code. */
- VALUE message = rb_sprintf("%"PRIsVALUE" "
- "(%"PRI_OFFT_PREFIX"d, "
- "%"PRI_OFFT_PREFIX"d, "
- "%"PRIsVALUE")",
- fptr->pathv, offset, len, advice);
- rb_syserr_fail_str(rv, message);
+ /* posix_fadvise(2) doesn't set errno. On success it returns 0; otherwise
+ it returns the error code. */
+ VALUE message = rb_sprintf("%"PRIsVALUE" "
+ "(%"PRI_OFFT_PREFIX"d, "
+ "%"PRI_OFFT_PREFIX"d, "
+ "%"PRIsVALUE")",
+ fptr->pathv, offset, len, advice);
+ rb_syserr_fail_str(rv, message);
}
return Qnil;
@@ -10433,15 +10785,15 @@ static void
advice_arg_check(VALUE advice)
{
if (!SYMBOL_P(advice))
- rb_raise(rb_eTypeError, "advice must be a Symbol");
+ rb_raise(rb_eTypeError, "advice must be a Symbol");
if (advice != sym_normal &&
- advice != sym_sequential &&
- advice != sym_random &&
- advice != sym_willneed &&
- advice != sym_dontneed &&
- advice != sym_noreuse) {
- rb_raise(rb_eNotImpError, "Unsupported advice: %+"PRIsVALUE, advice);
+ advice != sym_sequential &&
+ advice != sym_random &&
+ advice != sym_willneed &&
+ advice != sym_dontneed &&
+ advice != sym_noreuse) {
+ rb_raise(rb_eNotImpError, "Unsupported advice: %+"PRIsVALUE, advice);
}
}
@@ -10482,7 +10834,7 @@ static VALUE
rb_io_advise(int argc, VALUE *argv, VALUE io)
{
VALUE advice, offset, len;
- off_t off, l;
+ rb_off_t off, l;
rb_io_t *fptr;
rb_scan_args(argc, argv, "12", &advice, &offset, &len);
@@ -10654,6 +11006,13 @@ rb_io_advise(int argc, VALUE *argv, VALUE io)
static VALUE
rb_f_select(int argc, VALUE *argv, VALUE obj)
{
+ VALUE scheduler = rb_fiber_scheduler_current();
+ if (scheduler != Qnil) {
+ // It's optionally supported.
+ VALUE result = rb_fiber_scheduler_io_selectv(scheduler, argc, argv);
+ if (!UNDEF_P(result)) return result;
+ }
+
VALUE timeout;
struct select_args args;
struct timeval timerec;
@@ -10661,15 +11020,15 @@ rb_f_select(int argc, VALUE *argv, VALUE obj)
rb_scan_args(argc, argv, "13", &args.read, &args.write, &args.except, &timeout);
if (NIL_P(timeout)) {
- args.timeout = 0;
+ args.timeout = 0;
}
else {
- timerec = rb_time_interval(timeout);
- args.timeout = &timerec;
+ timerec = rb_time_interval(timeout);
+ args.timeout = &timerec;
}
for (i = 0; i < numberof(args.fdsets); ++i)
- rb_fd_init(&args.fdsets[i]);
+ rb_fd_init(&args.fdsets[i]);
return rb_ensure(select_call, (VALUE)&args, select_end, (VALUE)&args);
}
@@ -10721,15 +11080,15 @@ linux_iocparm_len(ioctl_req_t cmd)
long len;
if ((cmd & 0xFFFF0000) == 0) {
- /* legacy and unstructured ioctl number. */
- return DEFAULT_IOCTL_NARG_LEN;
+ /* legacy and unstructured ioctl number. */
+ return DEFAULT_IOCTL_NARG_LEN;
}
len = _IOC_SIZE(cmd);
/* paranoia check for silly drivers which don't keep ioctl convention */
if (len < DEFAULT_IOCTL_NARG_LEN)
- len = DEFAULT_IOCTL_NARG_LEN;
+ len = DEFAULT_IOCTL_NARG_LEN;
return len;
}
@@ -10775,113 +11134,113 @@ fcntl_narg_len(ioctl_req_t cmd)
switch (cmd) {
#ifdef F_DUPFD
case F_DUPFD:
- len = sizeof(fcntl_arg_t);
- break;
+ len = sizeof(fcntl_arg_t);
+ break;
#endif
#ifdef F_DUP2FD /* bsd specific */
case F_DUP2FD:
- len = sizeof(int);
- break;
+ len = sizeof(int);
+ break;
#endif
#ifdef F_DUPFD_CLOEXEC /* linux specific */
case F_DUPFD_CLOEXEC:
- len = sizeof(fcntl_arg_t);
- break;
+ len = sizeof(fcntl_arg_t);
+ break;
#endif
#ifdef F_GETFD
case F_GETFD:
- len = 1;
- break;
+ len = 1;
+ break;
#endif
#ifdef F_SETFD
case F_SETFD:
- len = sizeof(fcntl_arg_t);
- break;
+ len = sizeof(fcntl_arg_t);
+ break;
#endif
#ifdef F_GETFL
case F_GETFL:
- len = 1;
- break;
+ len = 1;
+ break;
#endif
#ifdef F_SETFL
case F_SETFL:
- len = sizeof(fcntl_arg_t);
- break;
+ len = sizeof(fcntl_arg_t);
+ break;
#endif
#ifdef F_GETOWN
case F_GETOWN:
- len = 1;
- break;
+ len = 1;
+ break;
#endif
#ifdef F_SETOWN
case F_SETOWN:
- len = sizeof(fcntl_arg_t);
- break;
+ len = sizeof(fcntl_arg_t);
+ break;
#endif
#ifdef F_GETOWN_EX /* linux specific */
case F_GETOWN_EX:
- len = sizeof(struct f_owner_ex);
- break;
+ len = sizeof(struct f_owner_ex);
+ break;
#endif
#ifdef F_SETOWN_EX /* linux specific */
case F_SETOWN_EX:
- len = sizeof(struct f_owner_ex);
- break;
+ len = sizeof(struct f_owner_ex);
+ break;
#endif
#ifdef F_GETLK
case F_GETLK:
- len = sizeof(struct flock);
- break;
+ len = sizeof(struct flock);
+ break;
#endif
#ifdef F_SETLK
case F_SETLK:
- len = sizeof(struct flock);
- break;
+ len = sizeof(struct flock);
+ break;
#endif
#ifdef F_SETLKW
case F_SETLKW:
- len = sizeof(struct flock);
- break;
+ len = sizeof(struct flock);
+ break;
#endif
#ifdef F_READAHEAD /* bsd specific */
case F_READAHEAD:
- len = sizeof(int);
- break;
+ len = sizeof(int);
+ break;
#endif
#ifdef F_RDAHEAD /* Darwin specific */
case F_RDAHEAD:
- len = sizeof(int);
- break;
+ len = sizeof(int);
+ break;
#endif
#ifdef F_GETSIG /* linux specific */
case F_GETSIG:
- len = 1;
- break;
+ len = 1;
+ break;
#endif
#ifdef F_SETSIG /* linux specific */
case F_SETSIG:
- len = sizeof(fcntl_arg_t);
- break;
+ len = sizeof(fcntl_arg_t);
+ break;
#endif
#ifdef F_GETLEASE /* linux specific */
case F_GETLEASE:
- len = 1;
- break;
+ len = 1;
+ break;
#endif
#ifdef F_SETLEASE /* linux specific */
case F_SETLEASE:
- len = sizeof(fcntl_arg_t);
- break;
+ len = sizeof(fcntl_arg_t);
+ break;
#endif
#ifdef F_NOTIFY /* linux specific */
case F_NOTIFY:
- len = sizeof(fcntl_arg_t);
- break;
+ len = sizeof(fcntl_arg_t);
+ break;
#endif
default:
- len = 256;
- break;
+ len = 256;
+ break;
}
return len;
@@ -10903,40 +11262,40 @@ setup_narg(ioctl_req_t cmd, VALUE *argp, long (*narg_len)(ioctl_req_t))
VALUE arg = *argp;
if (!RTEST(arg)) {
- narg = 0;
+ narg = 0;
}
else if (FIXNUM_P(arg)) {
- narg = FIX2LONG(arg);
+ narg = FIX2LONG(arg);
}
else if (arg == Qtrue) {
- narg = 1;
+ narg = 1;
}
else {
- VALUE tmp = rb_check_string_type(arg);
-
- if (NIL_P(tmp)) {
- narg = NUM2LONG(arg);
- }
- else {
- char *ptr;
- long len, slen;
-
- *argp = arg = tmp;
- len = narg_len(cmd);
- rb_str_modify(arg);
-
- slen = RSTRING_LEN(arg);
- /* expand for data + sentinel. */
- if (slen < len+1) {
- rb_str_resize(arg, len+1);
- MEMZERO(RSTRING_PTR(arg)+slen, char, len-slen);
- slen = len+1;
- }
- /* a little sanity check here */
- ptr = RSTRING_PTR(arg);
- ptr[slen - 1] = NARG_SENTINEL;
- narg = (long)(SIGNED_VALUE)ptr;
- }
+ VALUE tmp = rb_check_string_type(arg);
+
+ if (NIL_P(tmp)) {
+ narg = NUM2LONG(arg);
+ }
+ else {
+ char *ptr;
+ long len, slen;
+
+ *argp = arg = tmp;
+ len = narg_len(cmd);
+ rb_str_modify(arg);
+
+ slen = RSTRING_LEN(arg);
+ /* expand for data + sentinel. */
+ if (slen < len+1) {
+ rb_str_resize(arg, len+1);
+ MEMZERO(RSTRING_PTR(arg)+slen, char, len-slen);
+ slen = len+1;
+ }
+ /* a little sanity check here */
+ ptr = RSTRING_PTR(arg);
+ ptr[slen - 1] = NARG_SENTINEL;
+ narg = (long)(SIGNED_VALUE)ptr;
+ }
}
return narg;
@@ -10947,12 +11306,12 @@ finish_narg(int retval, VALUE arg, const rb_io_t *fptr)
{
if (retval < 0) rb_sys_fail_path(fptr->pathv);
if (RB_TYPE_P(arg, T_STRING)) {
- char *ptr;
- long slen;
- RSTRING_GETMEM(arg, ptr, slen);
- if (ptr[slen-1] != NARG_SENTINEL)
- rb_raise(rb_eArgError, "return value overflowed string");
- ptr[slen-1] = '\0';
+ char *ptr;
+ long slen;
+ RSTRING_GETMEM(arg, ptr, slen);
+ if (ptr[slen-1] != NARG_SENTINEL)
+ rb_raise(rb_eArgError, "return value overflowed string");
+ ptr[slen-1] = '\0';
}
return INT2NUM(retval);
@@ -11017,7 +11376,7 @@ nogvl_fcntl(void *ptr)
#if defined(F_DUPFD)
if (arg->cmd == F_DUPFD)
- return (VALUE)rb_cloexec_fcntl_dupfd(arg->fd, (int)arg->narg);
+ return (VALUE)rb_cloexec_fcntl_dupfd(arg->fd, (int)arg->narg);
#endif
return (VALUE)fcntl(arg->fd, arg->cmd, arg->narg);
}
@@ -11034,15 +11393,15 @@ do_fcntl(int fd, int cmd, long narg)
retval = (int)rb_thread_io_blocking_region(nogvl_fcntl, &arg, fd);
if (retval != -1) {
- switch (cmd) {
+ switch (cmd) {
#if defined(F_DUPFD)
- case F_DUPFD:
+ case F_DUPFD:
#endif
#if defined(F_DUPFD_CLOEXEC)
- case F_DUPFD_CLOEXEC:
+ case F_DUPFD_CLOEXEC:
#endif
- rb_update_max_fd(retval);
- }
+ rb_update_max_fd(retval);
+ }
}
return retval;
@@ -11160,52 +11519,52 @@ rb_f_syscall(int argc, VALUE *argv, VALUE _)
}
if (argc == 0)
- rb_raise(rb_eArgError, "too few arguments for syscall");
+ rb_raise(rb_eArgError, "too few arguments for syscall");
if (argc > numberof(arg))
- rb_raise(rb_eArgError, "too many arguments for syscall");
+ rb_raise(rb_eArgError, "too many arguments for syscall");
num = NUM2SYSCALLID(argv[0]); ++argv;
for (i = argc - 1; i--; ) {
- VALUE v = rb_check_string_type(argv[i]);
+ VALUE v = rb_check_string_type(argv[i]);
- if (!NIL_P(v)) {
- SafeStringValue(v);
- rb_str_modify(v);
- arg[i] = (VALUE)StringValueCStr(v);
- }
- else {
- arg[i] = (VALUE)NUM2LONG(argv[i]);
- }
+ if (!NIL_P(v)) {
+ SafeStringValue(v);
+ rb_str_modify(v);
+ arg[i] = (VALUE)StringValueCStr(v);
+ }
+ else {
+ arg[i] = (VALUE)NUM2LONG(argv[i]);
+ }
}
switch (argc) {
case 1:
- retval = SYSCALL(num);
- break;
+ retval = SYSCALL(num);
+ break;
case 2:
- retval = SYSCALL(num, arg[0]);
- break;
+ retval = SYSCALL(num, arg[0]);
+ break;
case 3:
- retval = SYSCALL(num, arg[0],arg[1]);
- break;
+ retval = SYSCALL(num, arg[0],arg[1]);
+ break;
case 4:
- retval = SYSCALL(num, arg[0],arg[1],arg[2]);
- break;
+ retval = SYSCALL(num, arg[0],arg[1],arg[2]);
+ break;
case 5:
- retval = SYSCALL(num, arg[0],arg[1],arg[2],arg[3]);
- break;
+ retval = SYSCALL(num, arg[0],arg[1],arg[2],arg[3]);
+ break;
case 6:
- retval = SYSCALL(num, arg[0],arg[1],arg[2],arg[3],arg[4]);
- break;
+ retval = SYSCALL(num, arg[0],arg[1],arg[2],arg[3],arg[4]);
+ break;
case 7:
- retval = SYSCALL(num, arg[0],arg[1],arg[2],arg[3],arg[4],arg[5]);
- break;
+ retval = SYSCALL(num, arg[0],arg[1],arg[2],arg[3],arg[4],arg[5]);
+ break;
case 8:
- retval = SYSCALL(num, arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6]);
- break;
+ retval = SYSCALL(num, arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6]);
+ break;
}
if (retval == -1)
- rb_sys_fail(0);
+ rb_sys_fail(0);
return RETVAL2NUM(retval);
#undef SYSCALL
#undef NUM2SYSCALLID
@@ -11237,51 +11596,51 @@ io_encoding_set(rb_io_t *fptr, VALUE v1, VALUE v2, VALUE opt)
VALUE ecopts, tmp;
if (!NIL_P(v2)) {
- enc2 = find_encoding(v1);
- tmp = rb_check_string_type(v2);
- if (!NIL_P(tmp)) {
- if (RSTRING_LEN(tmp) == 1 && RSTRING_PTR(tmp)[0] == '-') {
- /* Special case - "-" => no transcoding */
- enc = enc2;
- enc2 = NULL;
- }
- else
- enc = find_encoding(v2);
- if (enc == enc2) {
- /* Special case - "-" => no transcoding */
- enc2 = NULL;
- }
- }
- else {
- enc = find_encoding(v2);
- if (enc == enc2) {
- /* Special case - "-" => no transcoding */
- enc2 = NULL;
- }
- }
- SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags);
- ecflags = rb_econv_prepare_options(opt, &ecopts, ecflags);
+ enc2 = find_encoding(v1);
+ tmp = rb_check_string_type(v2);
+ if (!NIL_P(tmp)) {
+ if (RSTRING_LEN(tmp) == 1 && RSTRING_PTR(tmp)[0] == '-') {
+ /* Special case - "-" => no transcoding */
+ enc = enc2;
+ enc2 = NULL;
+ }
+ else
+ enc = find_encoding(v2);
+ if (enc == enc2) {
+ /* Special case - "-" => no transcoding */
+ enc2 = NULL;
+ }
+ }
+ else {
+ enc = find_encoding(v2);
+ if (enc == enc2) {
+ /* Special case - "-" => no transcoding */
+ enc2 = NULL;
+ }
+ }
+ SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags);
+ ecflags = rb_econv_prepare_options(opt, &ecopts, ecflags);
}
else {
- if (NIL_P(v1)) {
- /* Set to default encodings */
- rb_io_ext_int_to_encs(NULL, NULL, &enc, &enc2, 0);
- SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags);
+ if (NIL_P(v1)) {
+ /* Set to default encodings */
+ rb_io_ext_int_to_encs(NULL, NULL, &enc, &enc2, 0);
+ SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags);
ecopts = Qnil;
- }
- else {
- tmp = rb_check_string_type(v1);
- if (!NIL_P(tmp) && rb_enc_asciicompat(enc = rb_enc_get(tmp))) {
+ }
+ else {
+ tmp = rb_check_string_type(v1);
+ if (!NIL_P(tmp) && rb_enc_asciicompat(enc = rb_enc_get(tmp))) {
parse_mode_enc(RSTRING_PTR(tmp), enc, &enc, &enc2, NULL);
- SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags);
+ SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags);
ecflags = rb_econv_prepare_options(opt, &ecopts, ecflags);
- }
- else {
- rb_io_ext_int_to_encs(find_encoding(v1), NULL, &enc, &enc2, 0);
- SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags);
+ }
+ else {
+ rb_io_ext_int_to_encs(find_encoding(v1), NULL, &enc, &enc2, 0);
+ SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(enc2, ecflags);
ecopts = Qnil;
- }
- }
+ }
+ }
}
validate_enc_binmode(&fptr->mode, ecflags, enc, enc2);
fptr->encs.enc = enc;
@@ -11413,9 +11772,9 @@ rb_io_s_pipe(int argc, VALUE *argv, VALUE klass)
args[2] = INT2FIX(O_RDONLY);
r = rb_protect(io_new_instance, (VALUE)args, &state);
if (state) {
- close(pipes[0]);
- close(pipes[1]);
- rb_jump_tag(state);
+ close(pipes[0]);
+ close(pipes[1]);
+ rb_jump_tag(state);
}
GetOpenFile(r, fptr);
@@ -11425,18 +11784,18 @@ rb_io_s_pipe(int argc, VALUE *argv, VALUE klass)
ies_args.opt = opt;
rb_protect(io_encoding_set_v, (VALUE)&ies_args, &state);
if (state) {
- close(pipes[1]);
+ close(pipes[1]);
io_close(r);
- rb_jump_tag(state);
+ rb_jump_tag(state);
}
args[1] = INT2NUM(pipes[1]);
args[2] = INT2FIX(O_WRONLY);
w = rb_protect(io_new_instance, (VALUE)args, &state);
if (state) {
- close(pipes[1]);
- if (!NIL_P(r)) rb_io_close(r);
- rb_jump_tag(state);
+ close(pipes[1]);
+ if (!NIL_P(r)) rb_io_close(r);
+ rb_jump_tag(state);
}
GetOpenFile(w, fptr2);
rb_io_synchronized(fptr2);
@@ -11450,30 +11809,30 @@ rb_io_s_pipe(int argc, VALUE *argv, VALUE klass)
#if DEFAULT_TEXTMODE
if ((fptr->mode & FMODE_TEXTMODE) && (fmode & FMODE_BINMODE)) {
- fptr->mode &= ~FMODE_TEXTMODE;
- setmode(fptr->fd, O_BINARY);
+ fptr->mode &= ~FMODE_TEXTMODE;
+ setmode(fptr->fd, O_BINARY);
}
#if RUBY_CRLF_ENVIRONMENT
if (fptr->encs.ecflags & ECONV_DEFAULT_NEWLINE_DECORATOR) {
- fptr->encs.ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
+ fptr->encs.ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
}
#endif
#endif
fptr->mode |= fmode;
#if DEFAULT_TEXTMODE
if ((fptr2->mode & FMODE_TEXTMODE) && (fmode & FMODE_BINMODE)) {
- fptr2->mode &= ~FMODE_TEXTMODE;
- setmode(fptr2->fd, O_BINARY);
+ fptr2->mode &= ~FMODE_TEXTMODE;
+ setmode(fptr2->fd, O_BINARY);
}
#endif
fptr2->mode |= fmode;
ret = rb_assoc_new(r, w);
if (rb_block_given_p()) {
- VALUE rw[2];
- rw[0] = r;
- rw[1] = w;
- return rb_ensure(rb_yield, ret, pipe_pair_close, (VALUE)rw);
+ VALUE rw[2];
+ rw[0] = r;
+ rw[1] = w;
+ return rb_ensure(rb_yield, ret, pipe_pair_close, (VALUE)rw);
}
return ret;
}
@@ -11497,15 +11856,15 @@ open_key_args(VALUE klass, int argc, VALUE *argv, VALUE opt, struct foreach_arg
arg->argc = argc;
arg->argv = argv;
if (NIL_P(opt)) {
- vmode = INT2NUM(O_RDONLY);
- vperm = INT2FIX(0666);
+ vmode = INT2NUM(O_RDONLY);
+ vperm = INT2FIX(0666);
}
else if (!NIL_P(v = rb_hash_aref(opt, sym_open_args))) {
- int n;
+ int n;
- v = rb_to_array_type(v);
- n = RARRAY_LENINT(v);
- rb_check_arity(n, 0, 3); /* rb_io_open */
+ v = rb_to_array_type(v);
+ n = RARRAY_LENINT(v);
+ rb_check_arity(n, 0, 3); /* rb_io_open */
rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS, n, RARRAY_CONST_PTR(v), "02:", &vmode, &vperm, &opt);
}
arg->io = rb_io_open(klass, path, vmode, vperm, opt);
@@ -11517,9 +11876,11 @@ io_s_foreach(VALUE v)
struct getline_arg *arg = (void *)v;
VALUE str;
+ if (arg->limit == 0)
+ rb_raise(rb_eArgError, "invalid limit: 0 for foreach");
while (!NIL_P(str = rb_io_getline_1(arg->rs, arg->limit, arg->chomp, arg->io))) {
- rb_lastline_set(str);
- rb_yield(str);
+ rb_lastline_set(str);
+ rb_yield(str);
}
rb_lastline_set(Qnil);
return Qnil;
@@ -11775,12 +12136,16 @@ seek_before_access(VALUE argp)
* IO.read('| cat t.txt')
* # => "First line\nSecond line\n\nThird line\nFourth line\n"
*
- * With only argument +path+ given, reads and returns the entire content
+ * With only argument +path+ given, reads in text mode and returns the entire content
* of the file at the given path:
*
* IO.read('t.txt')
* # => "First line\nSecond line\n\nThird line\nFourth line\n"
*
+ * On Windows, text mode can terminate reading and leave bytes in the file
+ * unread when encountering certain special bytes. Consider using
+ * IO.binread if all bytes in the file should be read.
+ *
* For both forms, command and path, the remaining arguments are the same.
*
* With argument +length+, returns +length+ bytes if available:
@@ -11812,17 +12177,17 @@ rb_io_s_read(int argc, VALUE *argv, VALUE io)
open_key_args(io, argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
if (!NIL_P(offset)) {
- struct seek_arg sarg;
- int state = 0;
- sarg.io = arg.io;
- sarg.offset = offset;
- sarg.mode = SEEK_SET;
- rb_protect(seek_before_access, (VALUE)&sarg, &state);
- if (state) {
- rb_io_close(arg.io);
- rb_jump_tag(state);
- }
- if (arg.argc == 2) arg.argc = 1;
+ struct seek_arg sarg;
+ int state = 0;
+ sarg.io = arg.io;
+ sarg.offset = offset;
+ sarg.mode = SEEK_SET;
+ rb_protect(seek_before_access, (VALUE)&sarg, &state);
+ if (state) {
+ rb_io_close(arg.io);
+ rb_jump_tag(state);
+ }
+ if (arg.argc == 2) arg.argc = 1;
}
return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
}
@@ -11847,10 +12212,10 @@ rb_io_s_binread(int argc, VALUE *argv, VALUE io)
VALUE offset;
struct foreach_arg arg;
enum {
- fmode = FMODE_READABLE|FMODE_BINMODE,
- oflags = O_RDONLY
+ fmode = FMODE_READABLE|FMODE_BINMODE,
+ oflags = O_RDONLY
#ifdef O_BINARY
- |O_BINARY
+ |O_BINARY
#endif
};
convconfig_t convconfig = {NULL, NULL, 0, Qnil};
@@ -11863,16 +12228,16 @@ rb_io_s_binread(int argc, VALUE *argv, VALUE io)
arg.argv = argv+1;
arg.argc = (argc > 1) ? 1 : 0;
if (!NIL_P(offset)) {
- struct seek_arg sarg;
- int state = 0;
- sarg.io = arg.io;
- sarg.offset = offset;
- sarg.mode = SEEK_SET;
- rb_protect(seek_before_access, (VALUE)&sarg, &state);
- if (state) {
- rb_io_close(arg.io);
- rb_jump_tag(state);
- }
+ struct seek_arg sarg;
+ int state = 0;
+ sarg.io = arg.io;
+ sarg.offset = offset;
+ sarg.mode = SEEK_SET;
+ rb_protect(seek_before_access, (VALUE)&sarg, &state);
+ if (state) {
+ rb_io_close(arg.io);
+ rb_jump_tag(state);
+ }
}
return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
}
@@ -12022,15 +12387,15 @@ rb_io_s_binwrite(int argc, VALUE *argv, VALUE io)
struct copy_stream_struct {
VALUE src;
VALUE dst;
- off_t copy_length; /* (off_t)-1 if not specified */
- off_t src_offset; /* (off_t)-1 if not specified */
+ rb_off_t copy_length; /* (rb_off_t)-1 if not specified */
+ rb_off_t src_offset; /* (rb_off_t)-1 if not specified */
rb_io_t *src_fptr;
rb_io_t *dst_fptr;
unsigned close_src : 1;
unsigned close_dst : 1;
int error_no;
- off_t total;
+ rb_off_t total;
const char *syserr;
const char *notimp;
VALUE th;
@@ -12062,18 +12427,18 @@ maygvl_copy_stream_continue_p(int has_gvl, struct copy_stream_struct *stp)
#if defined(ERESTART)
case ERESTART:
#endif
- if (rb_thread_interrupted(stp->th)) {
+ if (rb_thread_interrupted(stp->th)) {
if (has_gvl)
rb_thread_execute_interrupts(stp->th);
else
rb_thread_call_with_gvl(exec_interrupts, (void *)stp->th);
}
- return TRUE;
+ return TRUE;
}
return FALSE;
}
-struct wait_for_single_fd {
+struct fiber_scheduler_wait_for_arguments {
VALUE scheduler;
rb_io_t *fptr;
@@ -12083,11 +12448,11 @@ struct wait_for_single_fd {
};
static void *
-rb_thread_fiber_scheduler_wait_for(void * _args)
+fiber_scheduler_wait_for(void * _arguments)
{
- struct wait_for_single_fd *args = (struct wait_for_single_fd *)_args;
+ struct fiber_scheduler_wait_for_arguments *arguments = (struct fiber_scheduler_wait_for_arguments *)_arguments;
- args->result = rb_fiber_scheduler_io_wait(args->scheduler, args->fptr->self, INT2NUM(args->events), Qnil);
+ arguments->result = rb_fiber_scheduler_io_wait(arguments->scheduler, arguments->fptr->self, INT2NUM(arguments->events), RUBY_IO_TIMEOUT_DEFAULT);
return NULL;
}
@@ -12097,12 +12462,12 @@ rb_thread_fiber_scheduler_wait_for(void * _args)
STATIC_ASSERT(pollin_expected, POLLIN == RB_WAITFD_IN);
STATIC_ASSERT(pollout_expected, POLLOUT == RB_WAITFD_OUT);
static int
-nogvl_wait_for(VALUE th, rb_io_t *fptr, short events)
+nogvl_wait_for(VALUE th, rb_io_t *fptr, short events, struct timeval *timeout)
{
VALUE scheduler = rb_fiber_scheduler_current_for_thread(th);
if (scheduler != Qnil) {
- struct wait_for_single_fd args = {.scheduler = scheduler, .fptr = fptr, .events = events};
- rb_thread_call_with_gvl(rb_thread_fiber_scheduler_wait_for, &args);
+ struct fiber_scheduler_wait_for_arguments args = {.scheduler = scheduler, .fptr = fptr, .events = events};
+ rb_thread_call_with_gvl(fiber_scheduler_wait_for, &args);
return RTEST(args.result);
}
@@ -12114,22 +12479,32 @@ nogvl_wait_for(VALUE th, rb_io_t *fptr, short events)
fds.fd = fd;
fds.events = events;
- return poll(&fds, 1, -1);
+ int timeout_milliseconds = -1;
+
+ if (timeout) {
+ timeout_milliseconds = (int)(timeout->tv_sec * 1000) + (int)(timeout->tv_usec / 1000);
+ }
+
+ return poll(&fds, 1, timeout_milliseconds);
}
#else /* !USE_POLL */
# define IOWAIT_SYSCALL "select"
static int
-nogvl_wait_for(VALUE th, rb_io_t *fptr, short events)
+nogvl_wait_for(VALUE th, rb_io_t *fptr, short events, struct timeval *timeout)
{
VALUE scheduler = rb_fiber_scheduler_current_for_thread(th);
if (scheduler != Qnil) {
- struct wait_for_single_fd args = {.scheduler = scheduler, .fptr = fptr, .events = events};
- rb_thread_call_with_gvl(rb_thread_fiber_scheduler_wait_for, &args);
+ struct fiber_scheduler_wait_for_arguments args = {.scheduler = scheduler, .fptr = fptr, .events = events};
+ rb_thread_call_with_gvl(fiber_scheduler_wait_for, &args);
return RTEST(args.result);
}
int fd = fptr->fd;
- if (fd == -1) return 0;
+
+ if (fd == -1) {
+ errno = EBADF;
+ return -1;
+ }
rb_fdset_t fds;
int ret;
@@ -12139,16 +12514,18 @@ nogvl_wait_for(VALUE th, rb_io_t *fptr, short events)
switch (events) {
case RB_WAITFD_IN:
- ret = rb_fd_select(fd + 1, &fds, 0, 0, 0);
+ ret = rb_fd_select(fd + 1, &fds, 0, 0, timeout);
break;
case RB_WAITFD_OUT:
- ret = rb_fd_select(fd + 1, 0, &fds, 0, 0);
+ ret = rb_fd_select(fd + 1, 0, &fds, 0, timeout);
break;
default:
VM_UNREACHABLE(nogvl_wait_for);
}
rb_fd_term(&fds);
+
+ // On timeout, this returns 0.
return ret;
}
#endif /* !USE_POLL */
@@ -12163,7 +12540,7 @@ maygvl_copy_stream_wait_read(int has_gvl, struct copy_stream_struct *stp)
ret = RB_NUM2INT(rb_io_wait(stp->src, RB_INT2NUM(RUBY_IO_READABLE), Qnil));
}
else {
- ret = nogvl_wait_for(stp->th, stp->src_fptr, RB_WAITFD_IN);
+ ret = nogvl_wait_for(stp->th, stp->src_fptr, RB_WAITFD_IN, NULL);
}
} while (ret < 0 && maygvl_copy_stream_continue_p(has_gvl, stp));
@@ -12181,7 +12558,7 @@ nogvl_copy_stream_wait_write(struct copy_stream_struct *stp)
int ret;
do {
- ret = nogvl_wait_for(stp->th, stp->dst_fptr, RB_WAITFD_OUT);
+ ret = nogvl_wait_for(stp->th, stp->dst_fptr, RB_WAITFD_OUT, NULL);
} while (ret < 0 && maygvl_copy_stream_continue_p(0, stp));
if (ret < 0) {
@@ -12195,7 +12572,7 @@ nogvl_copy_stream_wait_write(struct copy_stream_struct *stp)
#ifdef USE_COPY_FILE_RANGE
static ssize_t
-simple_copy_file_range(int in_fd, off_t *in_offset, int out_fd, off_t *out_offset, size_t count, unsigned int flags)
+simple_copy_file_range(int in_fd, rb_off_t *in_offset, int out_fd, rb_off_t *out_offset, size_t count, unsigned int flags)
{
#ifdef HAVE_COPY_FILE_RANGE
return copy_file_range(in_fd, in_offset, out_fd, out_offset, count, flags);
@@ -12208,43 +12585,43 @@ static int
nogvl_copy_file_range(struct copy_stream_struct *stp)
{
ssize_t ss;
- off_t src_size;
- off_t copy_length, src_offset, *src_offset_ptr;
+ rb_off_t src_size;
+ rb_off_t copy_length, src_offset, *src_offset_ptr;
if (!S_ISREG(stp->src_stat.st_mode))
return 0;
src_size = stp->src_stat.st_size;
src_offset = stp->src_offset;
- if (src_offset >= (off_t)0) {
- src_offset_ptr = &src_offset;
+ if (src_offset >= (rb_off_t)0) {
+ src_offset_ptr = &src_offset;
}
else {
- src_offset_ptr = NULL; /* if src_offset_ptr is NULL, then bytes are read from in_fd starting from the file offset */
+ src_offset_ptr = NULL; /* if src_offset_ptr is NULL, then bytes are read from in_fd starting from the file offset */
}
copy_length = stp->copy_length;
- if (copy_length < (off_t)0) {
- if (src_offset < (off_t)0) {
- off_t current_offset;
+ if (copy_length < (rb_off_t)0) {
+ if (src_offset < (rb_off_t)0) {
+ rb_off_t current_offset;
errno = 0;
current_offset = lseek(stp->src_fptr->fd, 0, SEEK_CUR);
- if (current_offset < (off_t)0 && errno) {
+ if (current_offset < (rb_off_t)0 && errno) {
stp->syserr = "lseek";
stp->error_no = errno;
return (int)current_offset;
}
copy_length = src_size - current_offset;
- }
- else {
+ }
+ else {
copy_length = src_size - src_offset;
- }
+ }
}
retry_copy_file_range:
# if SIZEOF_OFF_T > SIZEOF_SIZE_T
/* we are limited by the 32-bit ssize_t return value on 32-bit */
- ss = (copy_length > (off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length;
+ ss = (copy_length > (rb_off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length;
# else
ss = (ssize_t)copy_length;
# endif
@@ -12257,39 +12634,39 @@ nogvl_copy_file_range(struct copy_stream_struct *stp)
}
}
if (ss < 0) {
- if (maygvl_copy_stream_continue_p(0, stp)) {
+ if (maygvl_copy_stream_continue_p(0, stp)) {
goto retry_copy_file_range;
- }
+ }
switch (errno) {
- case EINVAL:
- case EPERM: /* copy_file_range(2) doesn't exist (may happen in
- docker container) */
+ case EINVAL:
+ case EPERM: /* copy_file_range(2) doesn't exist (may happen in
+ docker container) */
#ifdef ENOSYS
- case ENOSYS:
+ case ENOSYS:
#endif
#ifdef EXDEV
- case EXDEV: /* in_fd and out_fd are not on the same filesystem */
+ case EXDEV: /* in_fd and out_fd are not on the same filesystem */
#endif
return 0;
- case EAGAIN:
+ case EAGAIN:
#if EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK:
+ case EWOULDBLOCK:
#endif
{
int ret = nogvl_copy_stream_wait_write(stp);
if (ret < 0) return ret;
}
goto retry_copy_file_range;
- case EBADF:
- {
- int e = errno;
- int flags = fcntl(stp->dst_fptr->fd, F_GETFL);
+ case EBADF:
+ {
+ int e = errno;
+ int flags = fcntl(stp->dst_fptr->fd, F_GETFL);
- if (flags != -1 && flags & O_APPEND) {
- return 0;
- }
- errno = e;
- }
+ if (flags != -1 && flags & O_APPEND) {
+ return 0;
+ }
+ errno = e;
+ }
}
stp->syserr = "copy_file_range";
stp->error_no = errno;
@@ -12303,11 +12680,11 @@ nogvl_copy_file_range(struct copy_stream_struct *stp)
static int
nogvl_fcopyfile(struct copy_stream_struct *stp)
{
- off_t cur, ss = 0;
- const off_t src_offset = stp->src_offset;
+ rb_off_t cur, ss = 0;
+ const rb_off_t src_offset = stp->src_offset;
int ret;
- if (stp->copy_length >= (off_t)0) {
+ if (stp->copy_length >= (rb_off_t)0) {
/* copy_length can't be specified in fcopyfile(3) */
return 0;
}
@@ -12317,30 +12694,30 @@ nogvl_fcopyfile(struct copy_stream_struct *stp)
if (!S_ISREG(stp->dst_stat.st_mode))
return 0;
- if (lseek(stp->dst_fptr->fd, 0, SEEK_CUR) > (off_t)0) /* if dst IO was already written */
+ if (lseek(stp->dst_fptr->fd, 0, SEEK_CUR) > (rb_off_t)0) /* if dst IO was already written */
return 0;
if (fcntl(stp->dst_fptr->fd, F_GETFL) & O_APPEND) {
/* fcopyfile(3) appends src IO to dst IO and then truncates
* dst IO to src IO's original size. */
- off_t end = lseek(stp->dst_fptr->fd, 0, SEEK_END);
+ rb_off_t end = lseek(stp->dst_fptr->fd, 0, SEEK_END);
lseek(stp->dst_fptr->fd, 0, SEEK_SET);
- if (end > (off_t)0) return 0;
+ if (end > (rb_off_t)0) return 0;
}
- if (src_offset > (off_t)0) {
- off_t r;
+ if (src_offset > (rb_off_t)0) {
+ rb_off_t r;
/* get current offset */
errno = 0;
cur = lseek(stp->src_fptr->fd, 0, SEEK_CUR);
- if (cur < (off_t)0 && errno) {
+ if (cur < (rb_off_t)0 && errno) {
stp->error_no = errno;
return 1;
}
errno = 0;
r = lseek(stp->src_fptr->fd, src_offset, SEEK_SET);
- if (r < (off_t)0 && errno) {
+ if (r < (rb_off_t)0 && errno) {
stp->error_no = errno;
return 1;
}
@@ -12352,12 +12729,12 @@ nogvl_fcopyfile(struct copy_stream_struct *stp)
if (ret == 0) { /* success */
stp->total = ss;
- if (src_offset > (off_t)0) {
- off_t r;
+ if (src_offset > (rb_off_t)0) {
+ rb_off_t r;
errno = 0;
/* reset offset */
r = lseek(stp->src_fptr->fd, cur, SEEK_SET);
- if (r < (off_t)0 && errno) {
+ if (r < (rb_off_t)0 && errno) {
stp->error_no = errno;
return 1;
}
@@ -12388,7 +12765,7 @@ nogvl_fcopyfile(struct copy_stream_struct *stp)
# endif
static ssize_t
-simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count)
+simple_sendfile(int out_fd, int in_fd, rb_off_t *offset, rb_off_t count)
{
return sendfile(out_fd, in_fd, offset, (size_t)count);
}
@@ -12400,11 +12777,11 @@ simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count)
# define USE_SENDFILE
static ssize_t
-simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count)
+simple_sendfile(int out_fd, int in_fd, rb_off_t *offset, rb_off_t count)
{
int r;
- off_t pos = offset ? *offset : lseek(in_fd, 0, SEEK_CUR);
- off_t sbytes;
+ rb_off_t pos = offset ? *offset : lseek(in_fd, 0, SEEK_CUR);
+ rb_off_t sbytes;
# ifdef __APPLE__
r = sendfile(in_fd, out_fd, pos, &count, NULL, 0);
sbytes = count;
@@ -12413,10 +12790,10 @@ simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count)
# endif
if (r != 0 && sbytes == 0) return r;
if (offset) {
- *offset += sbytes;
+ *offset += sbytes;
}
else {
- lseek(in_fd, sbytes, SEEK_CUR);
+ lseek(in_fd, sbytes, SEEK_CUR);
}
return (ssize_t)sbytes;
}
@@ -12430,9 +12807,9 @@ static int
nogvl_copy_stream_sendfile(struct copy_stream_struct *stp)
{
ssize_t ss;
- off_t src_size;
- off_t copy_length;
- off_t src_offset;
+ rb_off_t src_size;
+ rb_off_t copy_length;
+ rb_off_t src_offset;
int use_pread;
if (!S_ISREG(stp->src_stat.st_mode))
@@ -12441,21 +12818,21 @@ nogvl_copy_stream_sendfile(struct copy_stream_struct *stp)
src_size = stp->src_stat.st_size;
#ifndef __linux__
if ((stp->dst_stat.st_mode & S_IFMT) != S_IFSOCK)
- return 0;
+ return 0;
#endif
src_offset = stp->src_offset;
- use_pread = src_offset >= (off_t)0;
+ use_pread = src_offset >= (rb_off_t)0;
copy_length = stp->copy_length;
- if (copy_length < (off_t)0) {
+ if (copy_length < (rb_off_t)0) {
if (use_pread)
copy_length = src_size - src_offset;
else {
- off_t cur;
+ rb_off_t cur;
errno = 0;
cur = lseek(stp->src_fptr->fd, 0, SEEK_CUR);
- if (cur < (off_t)0 && errno) {
+ if (cur < (rb_off_t)0 && errno) {
stp->syserr = "lseek";
stp->error_no = errno;
return (int)cur;
@@ -12467,7 +12844,7 @@ nogvl_copy_stream_sendfile(struct copy_stream_struct *stp)
retry_sendfile:
# if SIZEOF_OFF_T > SIZEOF_SIZE_T
/* we are limited by the 32-bit ssize_t return value on 32-bit */
- ss = (copy_length > (off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length;
+ ss = (copy_length > (rb_off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length;
# else
ss = (ssize_t)copy_length;
# endif
@@ -12485,22 +12862,22 @@ nogvl_copy_stream_sendfile(struct copy_stream_struct *stp)
}
}
if (ss < 0) {
- if (maygvl_copy_stream_continue_p(0, stp))
- goto retry_sendfile;
+ if (maygvl_copy_stream_continue_p(0, stp))
+ goto retry_sendfile;
switch (errno) {
- case EINVAL:
+ case EINVAL:
#ifdef ENOSYS
- case ENOSYS:
+ case ENOSYS:
#endif
#ifdef EOPNOTSUP
- /* some RedHat kernels may return EOPNOTSUP on an NFS mount.
- see also: [Feature #16965] */
- case EOPNOTSUP:
+ /* some RedHat kernels may return EOPNOTSUP on an NFS mount.
+ see also: [Feature #16965] */
+ case EOPNOTSUP:
#endif
return 0;
- case EAGAIN:
+ case EAGAIN:
#if EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK:
+ case EWOULDBLOCK:
#endif
{
int ret;
@@ -12532,17 +12909,17 @@ static ssize_t
maygvl_read(int has_gvl, rb_io_t *fptr, void *buf, size_t count)
{
if (has_gvl)
- return rb_read_internal(fptr, buf, count);
+ return rb_io_read_memory(fptr, buf, count);
else
return read(fptr->fd, buf, count);
}
static ssize_t
-maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf, size_t len, off_t offset)
+maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf, size_t len, rb_off_t offset)
{
ssize_t ss;
retry_read:
- if (offset < (off_t)0) {
+ if (offset < (rb_off_t)0) {
ss = maygvl_read(has_gvl, stp->src_fptr, buf, len);
}
else {
@@ -12557,12 +12934,12 @@ maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf,
return 0;
}
if (ss < 0) {
- if (maygvl_copy_stream_continue_p(has_gvl, stp))
- goto retry_read;
+ if (maygvl_copy_stream_continue_p(has_gvl, stp))
+ goto retry_read;
switch (errno) {
- case EAGAIN:
+ case EAGAIN:
#if EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK:
+ case EWOULDBLOCK:
#endif
{
int ret = maygvl_copy_stream_wait_read(has_gvl, stp);
@@ -12570,12 +12947,12 @@ maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf,
}
goto retry_read;
#ifdef ENOSYS
- case ENOSYS:
+ case ENOSYS:
stp->notimp = "pread";
return ss;
#endif
}
- stp->syserr = offset < (off_t)0 ? "read" : "pread";
+ stp->syserr = offset < (rb_off_t)0 ? "read" : "pread";
stp->error_no = errno;
}
return ss;
@@ -12614,31 +12991,31 @@ nogvl_copy_stream_read_write(struct copy_stream_struct *stp)
size_t len;
ssize_t ss;
int ret;
- off_t copy_length;
+ rb_off_t copy_length;
+ rb_off_t src_offset;
int use_eof;
- off_t src_offset;
int use_pread;
copy_length = stp->copy_length;
- use_eof = copy_length < (off_t)0;
+ use_eof = copy_length < (rb_off_t)0;
src_offset = stp->src_offset;
- use_pread = src_offset >= (off_t)0;
+ use_pread = src_offset >= (rb_off_t)0;
if (use_pread && stp->close_src) {
- off_t r;
- errno = 0;
+ rb_off_t r;
+ errno = 0;
r = lseek(stp->src_fptr->fd, src_offset, SEEK_SET);
- if (r < (off_t)0 && errno) {
+ if (r < (rb_off_t)0 && errno) {
stp->syserr = "lseek";
stp->error_no = errno;
return;
}
- src_offset = (off_t)-1;
+ src_offset = (rb_off_t)-1;
use_pread = 0;
}
while (use_eof || 0 < copy_length) {
- if (!use_eof && copy_length < (off_t)sizeof(buf)) {
+ if (!use_eof && copy_length < (rb_off_t)sizeof(buf)) {
len = (size_t)copy_length;
}
else {
@@ -12650,7 +13027,7 @@ nogvl_copy_stream_read_write(struct copy_stream_struct *stp)
src_offset += ss;
}
else {
- ss = maygvl_copy_stream_read(0, stp, buf, len, (off_t)-1);
+ ss = maygvl_copy_stream_read(0, stp, buf, len, (rb_off_t)-1);
}
if (ss <= 0) /* EOF or error */
return;
@@ -12675,7 +13052,7 @@ nogvl_copy_stream_func(void *arg)
#ifdef USE_COPY_FILE_RANGE
ret = nogvl_copy_file_range(stp);
if (ret != 0)
- goto finish; /* error or success */
+ goto finish; /* error or success */
#endif
#ifdef HAVE_FCOPYFILE
@@ -12705,27 +13082,28 @@ copy_stream_fallback_body(VALUE arg)
const int buflen = 16*1024;
VALUE n;
VALUE buf = rb_str_buf_new(buflen);
- off_t rest = stp->copy_length;
- off_t off = stp->src_offset;
+ rb_off_t rest = stp->copy_length;
+ rb_off_t off = stp->src_offset;
ID read_method = id_readpartial;
if (!stp->src_fptr) {
- if (!rb_respond_to(stp->src, read_method)) {
- read_method = id_read;
- }
+ if (!rb_respond_to(stp->src, read_method)) {
+ read_method = id_read;
+ }
}
while (1) {
long numwrote;
long l;
- if (stp->copy_length < (off_t)0) {
+ rb_str_make_independent(buf);
+ if (stp->copy_length < (rb_off_t)0) {
l = buflen;
}
else {
- if (rest == 0) {
- rb_str_resize(buf, 0);
- break;
- }
+ if (rest == 0) {
+ rb_str_resize(buf, 0);
+ break;
+ }
l = buflen < rest ? buflen : (long)rest;
}
if (!stp->src_fptr) {
@@ -12743,16 +13121,16 @@ copy_stream_fallback_body(VALUE arg)
return Qnil;
if (ss == 0)
rb_eof_error();
- if (off >= (off_t)0)
+ if (off >= (rb_off_t)0)
off += ss;
}
n = rb_io_write(stp->dst, buf);
numwrote = NUM2LONG(n);
stp->total += numwrote;
rest -= numwrote;
- if (read_method == id_read && RSTRING_LEN(buf) == 0) {
- break;
- }
+ if (read_method == id_read && RSTRING_LEN(buf) == 0) {
+ break;
+ }
}
return Qnil;
@@ -12761,8 +13139,8 @@ copy_stream_fallback_body(VALUE arg)
static VALUE
copy_stream_fallback(struct copy_stream_struct *stp)
{
- if (!stp->src_fptr && stp->src_offset >= (off_t)0) {
- rb_raise(rb_eArgError, "cannot specify src_offset for non-IO");
+ if (!stp->src_fptr && stp->src_offset >= (rb_off_t)0) {
+ rb_raise(rb_eArgError, "cannot specify src_offset for non-IO");
}
rb_rescue2(copy_stream_fallback_body, (VALUE)stp,
(VALUE (*) (VALUE, VALUE))0, (VALUE)0,
@@ -12777,37 +13155,37 @@ copy_stream_body(VALUE arg)
VALUE src_io = stp->src, dst_io = stp->dst;
const int common_oflags = 0
#ifdef O_NOCTTY
- | O_NOCTTY
+ | O_NOCTTY
#endif
- ;
+ ;
stp->th = rb_thread_current();
stp->total = 0;
if (src_io == argf ||
- !(RB_TYPE_P(src_io, T_FILE) ||
- RB_TYPE_P(src_io, T_STRING) ||
- rb_respond_to(src_io, rb_intern("to_path")))) {
+ !(RB_TYPE_P(src_io, T_FILE) ||
+ RB_TYPE_P(src_io, T_STRING) ||
+ rb_respond_to(src_io, rb_intern("to_path")))) {
stp->src_fptr = NULL;
}
else {
int stat_ret;
- VALUE tmp_io = rb_io_check_io(src_io);
- if (!NIL_P(tmp_io)) {
- src_io = tmp_io;
- }
- else if (!RB_TYPE_P(src_io, T_FILE)) {
- VALUE args[2];
- FilePathValue(src_io);
- args[0] = src_io;
- args[1] = INT2NUM(O_RDONLY|common_oflags);
- src_io = rb_class_new_instance(2, args, rb_cFile);
- stp->src = src_io;
- stp->close_src = 1;
- }
- RB_IO_POINTER(src_io, stp->src_fptr);
- rb_io_check_byte_readable(stp->src_fptr);
+ VALUE tmp_io = rb_io_check_io(src_io);
+ if (!NIL_P(tmp_io)) {
+ src_io = tmp_io;
+ }
+ else if (!RB_TYPE_P(src_io, T_FILE)) {
+ VALUE args[2];
+ FilePathValue(src_io);
+ args[0] = src_io;
+ args[1] = INT2NUM(O_RDONLY|common_oflags);
+ src_io = rb_class_new_instance(2, args, rb_cFile);
+ stp->src = src_io;
+ stp->close_src = 1;
+ }
+ RB_IO_POINTER(src_io, stp->src_fptr);
+ rb_io_check_byte_readable(stp->src_fptr);
stat_ret = fstat(stp->src_fptr->fd, &stp->src_stat);
if (stat_ret < 0) {
@@ -12818,33 +13196,33 @@ copy_stream_body(VALUE arg)
}
if (dst_io == argf ||
- !(RB_TYPE_P(dst_io, T_FILE) ||
- RB_TYPE_P(dst_io, T_STRING) ||
- rb_respond_to(dst_io, rb_intern("to_path")))) {
- stp->dst_fptr = NULL;
+ !(RB_TYPE_P(dst_io, T_FILE) ||
+ RB_TYPE_P(dst_io, T_STRING) ||
+ rb_respond_to(dst_io, rb_intern("to_path")))) {
+ stp->dst_fptr = NULL;
}
else {
int stat_ret;
VALUE tmp_io = rb_io_check_io(dst_io);
- if (!NIL_P(tmp_io)) {
- dst_io = GetWriteIO(tmp_io);
- }
- else if (!RB_TYPE_P(dst_io, T_FILE)) {
- VALUE args[3];
- FilePathValue(dst_io);
- args[0] = dst_io;
- args[1] = INT2NUM(O_WRONLY|O_CREAT|O_TRUNC|common_oflags);
- args[2] = INT2FIX(0666);
- dst_io = rb_class_new_instance(3, args, rb_cFile);
- stp->dst = dst_io;
- stp->close_dst = 1;
- }
- else {
- dst_io = GetWriteIO(dst_io);
- stp->dst = dst_io;
- }
- RB_IO_POINTER(dst_io, stp->dst_fptr);
- rb_io_check_writable(stp->dst_fptr);
+ if (!NIL_P(tmp_io)) {
+ dst_io = GetWriteIO(tmp_io);
+ }
+ else if (!RB_TYPE_P(dst_io, T_FILE)) {
+ VALUE args[3];
+ FilePathValue(dst_io);
+ args[0] = dst_io;
+ args[1] = INT2NUM(O_WRONLY|O_CREAT|O_TRUNC|common_oflags);
+ args[2] = INT2FIX(0666);
+ dst_io = rb_class_new_instance(3, args, rb_cFile);
+ stp->dst = dst_io;
+ stp->close_dst = 1;
+ }
+ else {
+ dst_io = GetWriteIO(dst_io);
+ stp->dst = dst_io;
+ }
+ RB_IO_POINTER(dst_io, stp->dst_fptr);
+ rb_io_check_writable(stp->dst_fptr);
stat_ret = fstat(stp->dst_fptr->fd, &stp->dst_stat);
if (stat_ret < 0) {
@@ -12856,15 +13234,15 @@ copy_stream_body(VALUE arg)
#ifdef O_BINARY
if (stp->src_fptr)
- SET_BINARY_MODE_WITH_SEEK_CUR(stp->src_fptr);
+ SET_BINARY_MODE_WITH_SEEK_CUR(stp->src_fptr);
#endif
if (stp->dst_fptr)
- io_ascii8bit_binmode(stp->dst_fptr);
+ io_ascii8bit_binmode(stp->dst_fptr);
- if (stp->src_offset < (off_t)0 && stp->src_fptr && stp->src_fptr->rbuf.len) {
+ if (stp->src_offset < (rb_off_t)0 && stp->src_fptr && stp->src_fptr->rbuf.len) {
size_t len = stp->src_fptr->rbuf.len;
VALUE str;
- if (stp->copy_length >= (off_t)0 && stp->copy_length < (off_t)len) {
+ if (stp->copy_length >= (rb_off_t)0 && stp->copy_length < (rb_off_t)len) {
len = (size_t)stp->copy_length;
}
str = rb_str_buf_new(len);
@@ -12875,15 +13253,15 @@ copy_stream_body(VALUE arg)
rb_sys_fail_on_write(stp->dst_fptr);
}
else /* others such as StringIO */
- rb_io_write(dst_io, str);
+ rb_io_write(dst_io, str);
rb_str_resize(str, 0);
stp->total += len;
- if (stp->copy_length >= (off_t)0)
+ if (stp->copy_length >= (rb_off_t)0)
stp->copy_length -= len;
}
if (stp->dst_fptr && io_fflush(stp->dst_fptr) < 0) {
- rb_raise(rb_eIOError, "flush failed");
+ rb_raise(rb_eIOError, "flush failed");
}
if (stp->copy_length == 0)
@@ -12918,7 +13296,7 @@ copy_stream_finalize(VALUE arg)
rb_syserr_fail(stp->error_no, stp->syserr);
}
if (stp->notimp) {
- rb_raise(rb_eNotImpError, "%s() not implemented", stp->notimp);
+ rb_raise(rb_eNotImpError, "%s() not implemented", stp->notimp);
}
return Qnil;
}
@@ -12991,12 +13369,12 @@ rb_io_s_copy_stream(int argc, VALUE *argv, VALUE io)
st.dst_fptr = NULL;
if (NIL_P(length))
- st.copy_length = (off_t)-1;
+ st.copy_length = (rb_off_t)-1;
else
st.copy_length = NUM2OFFT(length);
if (NIL_P(src_offset))
- st.src_offset = (off_t)-1;
+ st.src_offset = (rb_off_t)-1;
else
st.src_offset = NUM2OFFT(src_offset);
@@ -13012,7 +13390,7 @@ rb_io_s_copy_stream(int argc, VALUE *argv, VALUE io)
* Returns the Encoding object that represents the encoding of the stream,
* or +nil+ if the stream is in write mode and no encoding is specified.
*
- * See {Encodings}[rdoc-ref:IO@Encodings].
+ * See {Encodings}[rdoc-ref:File@Encodings].
*
*/
@@ -13022,12 +13400,12 @@ rb_io_external_encoding(VALUE io)
rb_io_t *fptr = RFILE(rb_io_taint_check(io))->fptr;
if (fptr->encs.enc2) {
- return rb_enc_from_encoding(fptr->encs.enc2);
+ return rb_enc_from_encoding(fptr->encs.enc2);
}
if (fptr->mode & FMODE_WRITABLE) {
- if (fptr->encs.enc)
- return rb_enc_from_encoding(fptr->encs.enc);
- return Qnil;
+ if (fptr->encs.enc)
+ return rb_enc_from_encoding(fptr->encs.enc);
+ return Qnil;
}
return rb_enc_from_encoding(io_read_encoding(fptr));
}
@@ -13040,7 +13418,7 @@ rb_io_external_encoding(VALUE io)
* if conversion is specified,
* or +nil+ otherwise.
*
- * See {Encodings}[rdoc-ref:IO@Encodings].
+ * See {Encodings}[rdoc-ref:File@Encodings].
*
*/
@@ -13059,7 +13437,7 @@ rb_io_internal_encoding(VALUE io)
* set_encoding(ext_enc, int_enc, **enc_opts) -> self
* set_encoding('ext_enc:int_enc', **enc_opts) -> self
*
- * See {Encodings}[rdoc-ref:IO@Encodings].
+ * See {Encodings}[rdoc-ref:File@Encodings].
*
* Argument +ext_enc+, if given, must be an Encoding object;
* it is assigned as the encoding for the stream.
@@ -13121,16 +13499,27 @@ global_argf_p(VALUE arg)
return arg == argf;
}
+typedef VALUE (*argf_encoding_func)(VALUE io);
+
+static VALUE
+argf_encoding(VALUE argf, argf_encoding_func func)
+{
+ if (!RTEST(ARGF.current_file)) {
+ return rb_enc_default_external();
+ }
+ return func(rb_io_check_io(ARGF.current_file));
+}
+
/*
* call-seq:
* ARGF.external_encoding -> encoding
*
- * Returns the external encoding for files read from +ARGF+ as an +Encoding+
+ * Returns the external encoding for files read from ARGF as an Encoding
* object. The external encoding is the encoding of the text as stored in a
- * file. Contrast with +ARGF.internal_encoding+, which is the encoding used
- * to represent this text within Ruby.
+ * file. Contrast with ARGF.internal_encoding, which is the encoding used to
+ * represent this text within Ruby.
*
- * To set the external encoding use +ARGF.set_encoding+.
+ * To set the external encoding use ARGF.set_encoding.
*
* For example:
*
@@ -13140,20 +13529,17 @@ global_argf_p(VALUE arg)
static VALUE
argf_external_encoding(VALUE argf)
{
- if (!RTEST(ARGF.current_file)) {
- return rb_enc_from_encoding(rb_default_external_encoding());
- }
- return rb_io_external_encoding(rb_io_check_io(ARGF.current_file));
+ return argf_encoding(argf, rb_io_external_encoding);
}
/*
* call-seq:
* ARGF.internal_encoding -> encoding
*
- * Returns the internal encoding for strings read from +ARGF+ as an
- * +Encoding+ object.
+ * Returns the internal encoding for strings read from ARGF as an
+ * Encoding object.
*
- * If +ARGF.set_encoding+ has been called with two encoding names, the second
+ * If ARGF.set_encoding has been called with two encoding names, the second
* is returned. Otherwise, if +Encoding.default_external+ has been set, that
* value is returned. Failing that, if a default external encoding was
* specified on the command-line, that value is used. If the encoding is
@@ -13162,10 +13548,7 @@ argf_external_encoding(VALUE argf)
static VALUE
argf_internal_encoding(VALUE argf)
{
- if (!RTEST(ARGF.current_file)) {
- return rb_enc_from_encoding(rb_default_external_encoding());
- }
- return rb_io_internal_encoding(rb_io_check_io(ARGF.current_file));
+ return argf_encoding(argf, rb_io_internal_encoding);
}
/*
@@ -13189,7 +13572,7 @@ argf_internal_encoding(VALUE argf)
* specifies the internal encoding.
*
* If the external encoding and the internal encoding are specified, the
- * optional +Hash+ argument can be used to adjust the conversion process. The
+ * optional Hash argument can be used to adjust the conversion process. The
* structure of this hash is explained in the String#encode documentation.
*
* For example:
@@ -13205,7 +13588,7 @@ argf_set_encoding(int argc, VALUE *argv, VALUE argf)
rb_io_t *fptr;
if (!next_argv()) {
- rb_raise(rb_eArgError, "no stream to set encoding");
+ rb_raise(rb_eArgError, "no stream to set encoding");
}
rb_io_set_encoding(argc, argv, ARGF.current_file);
GetOpenFile(ARGF.current_file, fptr);
@@ -13218,7 +13601,7 @@ argf_set_encoding(int argc, VALUE *argv, VALUE argf)
* ARGF.tell -> Integer
* ARGF.pos -> Integer
*
- * Returns the current offset (in bytes) of the current file in +ARGF+.
+ * Returns the current offset (in bytes) of the current file in ARGF.
*
* ARGF.pos #=> 0
* ARGF.gets #=> "This is line one\n"
@@ -13229,7 +13612,7 @@ static VALUE
argf_tell(VALUE argf)
{
if (!next_argv()) {
- rb_raise(rb_eArgError, "no stream to tell");
+ rb_raise(rb_eArgError, "no stream to tell");
}
ARGF_FORWARD(0, 0);
return rb_io_tell(ARGF.current_file);
@@ -13239,14 +13622,14 @@ argf_tell(VALUE argf)
* call-seq:
* ARGF.seek(amount, whence=IO::SEEK_SET) -> 0
*
- * Seeks to offset _amount_ (an +Integer+) in the +ARGF+ stream according to
+ * Seeks to offset _amount_ (an Integer) in the ARGF stream according to
* the value of _whence_. See IO#seek for further details.
*/
static VALUE
argf_seek_m(int argc, VALUE *argv, VALUE argf)
{
if (!next_argv()) {
- rb_raise(rb_eArgError, "no stream to seek");
+ rb_raise(rb_eArgError, "no stream to seek");
}
ARGF_FORWARD(argc, argv);
return rb_io_seek_m(argc, argv, ARGF.current_file);
@@ -13256,7 +13639,7 @@ argf_seek_m(int argc, VALUE *argv, VALUE argf)
* call-seq:
* ARGF.pos = position -> Integer
*
- * Seeks to the position given by _position_ (in bytes) in +ARGF+.
+ * Seeks to the position given by _position_ (in bytes) in ARGF.
*
* For example:
*
@@ -13267,7 +13650,7 @@ static VALUE
argf_set_pos(VALUE argf, VALUE offset)
{
if (!next_argv()) {
- rb_raise(rb_eArgError, "no stream to set position");
+ rb_raise(rb_eArgError, "no stream to set position");
}
ARGF_FORWARD(1, &offset);
return rb_io_set_pos(ARGF.current_file, offset);
@@ -13278,7 +13661,7 @@ argf_set_pos(VALUE argf, VALUE offset)
* ARGF.rewind -> 0
*
* Positions the current file to the beginning of input, resetting
- * +ARGF.lineno+ to zero.
+ * ARGF.lineno to zero.
*
* ARGF.readline #=> "This is line one\n"
* ARGF.rewind #=> 0
@@ -13292,13 +13675,13 @@ argf_rewind(VALUE argf)
int old_lineno;
if (!next_argv()) {
- rb_raise(rb_eArgError, "no stream to rewind");
+ rb_raise(rb_eArgError, "no stream to rewind");
}
ARGF_FORWARD(0, 0);
old_lineno = RFILE(ARGF.current_file)->fptr->lineno;
ret = rb_io_rewind(ARGF.current_file);
if (!global_argf_p(argf)) {
- ARGF.last_lineno = ARGF.lineno -= old_lineno;
+ ARGF.last_lineno = ARGF.lineno -= old_lineno;
}
return ret;
}
@@ -13309,7 +13692,7 @@ argf_rewind(VALUE argf)
* ARGF.to_i -> integer
*
* Returns an integer representing the numeric file descriptor for
- * the current file. Raises an +ArgumentError+ if there isn't a current file.
+ * the current file. Raises an ArgumentError if there isn't a current file.
*
* ARGF.fileno #=> 3
*/
@@ -13317,7 +13700,7 @@ static VALUE
argf_fileno(VALUE argf)
{
if (!next_argv()) {
- rb_raise(rb_eArgError, "no stream");
+ rb_raise(rb_eArgError, "no stream");
}
ARGF_FORWARD(0, 0);
return rb_io_fileno(ARGF.current_file);
@@ -13327,8 +13710,8 @@ argf_fileno(VALUE argf)
* call-seq:
* ARGF.to_io -> IO
*
- * Returns an +IO+ object representing the current file. This will be a
- * +File+ object unless the current file is a stream such as STDIN.
+ * Returns an IO object representing the current file. This will be a
+ * File object unless the current file is a stream such as STDIN.
*
* For example:
*
@@ -13348,8 +13731,8 @@ argf_to_io(VALUE argf)
* ARGF.eof? -> true or false
* ARGF.eof -> true or false
*
- * Returns true if the current file in +ARGF+ is at end of file, i.e. it has
- * no data to read. The stream must be opened for reading or an +IOError+
+ * Returns true if the current file in ARGF is at end of file, i.e. it has
+ * no data to read. The stream must be opened for reading or an IOError
* will be raised.
*
* $ echo "eof" | ruby argf.rb
@@ -13366,12 +13749,12 @@ argf_eof(VALUE argf)
{
next_argv();
if (RTEST(ARGF.current_file)) {
- if (ARGF.init_p == 0) return Qtrue;
- next_argv();
- ARGF_FORWARD(0, 0);
- if (rb_io_eof(ARGF.current_file)) {
- return Qtrue;
- }
+ if (ARGF.init_p == 0) return Qtrue;
+ next_argv();
+ ARGF_FORWARD(0, 0);
+ if (rb_io_eof(ARGF.current_file)) {
+ return Qtrue;
+ }
}
return Qfalse;
}
@@ -13432,39 +13815,39 @@ argf_read(int argc, VALUE *argv, VALUE argf)
rb_scan_args(argc, argv, "02", &length, &str);
if (!NIL_P(length)) {
- len = NUM2LONG(argv[0]);
+ len = NUM2LONG(argv[0]);
}
if (!NIL_P(str)) {
- StringValue(str);
- rb_str_resize(str,0);
- argv[1] = Qnil;
+ StringValue(str);
+ rb_str_resize(str,0);
+ argv[1] = Qnil;
}
retry:
if (!next_argv()) {
- return str;
+ return str;
}
if (ARGF_GENERIC_INPUT_P()) {
- tmp = argf_forward(argc, argv, argf);
+ tmp = argf_forward(argc, argv, argf);
}
else {
- tmp = io_read(argc, argv, ARGF.current_file);
+ tmp = io_read(argc, argv, ARGF.current_file);
}
if (NIL_P(str)) str = tmp;
else if (!NIL_P(tmp)) rb_str_append(str, tmp);
if (NIL_P(tmp) || NIL_P(length)) {
- if (ARGF.next_p != -1) {
- argf_close(argf);
- ARGF.next_p = 1;
- goto retry;
- }
+ if (ARGF.next_p != -1) {
+ argf_close(argf);
+ ARGF.next_p = 1;
+ goto retry;
+ }
}
else if (argc >= 1) {
- long slen = RSTRING_LEN(str);
- if (slen < len) {
+ long slen = RSTRING_LEN(str);
+ if (slen < len) {
argv[0] = LONG2NUM(len - slen);
- goto retry;
- }
+ goto retry;
+ }
}
return str;
}
@@ -13547,18 +13930,18 @@ argf_getpartial(int argc, VALUE *argv, VALUE argf, VALUE opts, int nonblock)
no_exception = no_exception_p(opts);
if (!next_argv()) {
- if (!NIL_P(str)) {
- rb_str_resize(str, 0);
- }
+ if (!NIL_P(str)) {
+ rb_str_resize(str, 0);
+ }
rb_eof_error();
}
if (ARGF_GENERIC_INPUT_P()) {
VALUE (*const rescue_does_nothing)(VALUE, VALUE) = 0;
- struct argf_call_arg arg;
- arg.argc = argc;
- arg.argv = argv;
- arg.argf = argf;
- tmp = rb_rescue2(argf_forward_call, (VALUE)&arg,
+ struct argf_call_arg arg;
+ arg.argc = argc;
+ arg.argv = argv;
+ arg.argf = argf;
+ tmp = rb_rescue2(argf_forward_call, (VALUE)&arg,
rescue_does_nothing, Qnil, rb_eEOFError, (VALUE)0);
}
else {
@@ -13572,7 +13955,7 @@ argf_getpartial(int argc, VALUE *argv, VALUE argf, VALUE opts, int nonblock)
ARGF.next_p = 1;
if (RARRAY_LEN(ARGF.argv) == 0) {
return io_nonblock_eof(no_exception);
- }
+ }
if (NIL_P(str))
str = rb_str_new(NULL, 0);
return str;
@@ -13584,10 +13967,10 @@ argf_getpartial(int argc, VALUE *argv, VALUE argf, VALUE opts, int nonblock)
* call-seq:
* ARGF.getc -> String or nil
*
- * Reads the next character from +ARGF+ and returns it as a +String+. Returns
+ * Reads the next character from ARGF and returns it as a String. Returns
* +nil+ at the end of the stream.
*
- * +ARGF+ treats the files named on the command line as a single file created
+ * ARGF treats the files named on the command line as a single file created
* by concatenating their contents. After returning the last character of the
* first file, it returns the first character of the second file, and so on.
*
@@ -13611,15 +13994,15 @@ argf_getc(VALUE argf)
retry:
if (!next_argv()) return Qnil;
if (ARGF_GENERIC_INPUT_P()) {
- ch = forward_current(rb_intern("getc"), 0, 0);
+ ch = forward_current(rb_intern("getc"), 0, 0);
}
else {
- ch = rb_io_getc(ARGF.current_file);
+ ch = rb_io_getc(ARGF.current_file);
}
if (NIL_P(ch) && ARGF.next_p != -1) {
- argf_close(argf);
- ARGF.next_p = 1;
- goto retry;
+ argf_close(argf);
+ ARGF.next_p = 1;
+ goto retry;
}
return ch;
@@ -13629,7 +14012,7 @@ argf_getc(VALUE argf)
* call-seq:
* ARGF.getbyte -> Integer or nil
*
- * Gets the next 8-bit byte (0..255) from +ARGF+. Returns +nil+ if called at
+ * Gets the next 8-bit byte (0..255) from ARGF. Returns +nil+ if called at
* the end of the stream.
*
* For example:
@@ -13651,15 +14034,15 @@ argf_getbyte(VALUE argf)
retry:
if (!next_argv()) return Qnil;
if (!RB_TYPE_P(ARGF.current_file, T_FILE)) {
- ch = forward_current(rb_intern("getbyte"), 0, 0);
+ ch = forward_current(rb_intern("getbyte"), 0, 0);
}
else {
- ch = rb_io_getbyte(ARGF.current_file);
+ ch = rb_io_getbyte(ARGF.current_file);
}
if (NIL_P(ch) && ARGF.next_p != -1) {
- argf_close(argf);
- ARGF.next_p = 1;
- goto retry;
+ argf_close(argf);
+ ARGF.next_p = 1;
+ goto retry;
}
return ch;
@@ -13669,8 +14052,8 @@ argf_getbyte(VALUE argf)
* call-seq:
* ARGF.readchar -> String or nil
*
- * Reads the next character from +ARGF+ and returns it as a +String+. Raises
- * an +EOFError+ after the last character of the last file has been read.
+ * Reads the next character from ARGF and returns it as a String. Raises
+ * an EOFError after the last character of the last file has been read.
*
* For example:
*
@@ -13691,15 +14074,15 @@ argf_readchar(VALUE argf)
retry:
if (!next_argv()) rb_eof_error();
if (!RB_TYPE_P(ARGF.current_file, T_FILE)) {
- ch = forward_current(rb_intern("getc"), 0, 0);
+ ch = forward_current(rb_intern("getc"), 0, 0);
}
else {
- ch = rb_io_getc(ARGF.current_file);
+ ch = rb_io_getc(ARGF.current_file);
}
if (NIL_P(ch) && ARGF.next_p != -1) {
- argf_close(argf);
- ARGF.next_p = 1;
- goto retry;
+ argf_close(argf);
+ ARGF.next_p = 1;
+ goto retry;
}
return ch;
@@ -13709,8 +14092,8 @@ argf_readchar(VALUE argf)
* call-seq:
* ARGF.readbyte -> Integer
*
- * Reads the next 8-bit byte from ARGF and returns it as an +Integer+. Raises
- * an +EOFError+ after the last byte of the last file has been read.
+ * Reads the next 8-bit byte from ARGF and returns it as an Integer. Raises
+ * an EOFError after the last byte of the last file has been read.
*
* For example:
*
@@ -13731,7 +14114,7 @@ argf_readbyte(VALUE argf)
NEXT_ARGF_FORWARD(0, 0);
c = argf_getbyte(argf);
if (NIL_P(c)) {
- rb_eof_error();
+ rb_eof_error();
}
return c;
}
@@ -13744,7 +14127,7 @@ argf_block_call_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, argf))
const VALUE current = ARGF.current_file;
rb_yield_values2(argc, argv);
if (ARGF.init_p == -1 || current != ARGF.current_file) {
- rb_iter_break_value(Qundef);
+ rb_iter_break_value(Qundef);
}
return Qnil;
}
@@ -13757,14 +14140,14 @@ static void
argf_block_call(ID mid, int argc, VALUE *argv, VALUE argf)
{
VALUE ret = ARGF_block_call(mid, argc, argv, argf_block_call_i, argf);
- if (ret != Qundef) ARGF.next_p = 1;
+ if (!UNDEF_P(ret)) ARGF.next_p = 1;
}
static VALUE
argf_block_call_line_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, argf))
{
if (!global_argf_p(argf)) {
- ARGF.last_lineno = ++ARGF.lineno;
+ ARGF.last_lineno = ++ARGF.lineno;
}
return argf_block_call_i(i, argf, argc, argv, blockarg);
}
@@ -13773,7 +14156,7 @@ static void
argf_block_call_line(ID mid, int argc, VALUE *argv, VALUE argf)
{
VALUE ret = ARGF_block_call(mid, argc, argv, argf_block_call_line_i, argf);
- if (ret != Qundef) ARGF.next_p = 1;
+ if (!UNDEF_P(ret)) ARGF.next_p = 1;
}
/*
@@ -13790,15 +14173,15 @@ argf_block_call_line(ID mid, int argc, VALUE *argv, VALUE argf)
* which defaults to your platform's newline character) of each file in
* +ARGV+. If a block is supplied, each line in turn will be yielded to the
* block, otherwise an enumerator is returned.
- * The optional _limit_ argument is an +Integer+ specifying the maximum
+ * The optional _limit_ argument is an Integer specifying the maximum
* length of each line; longer lines will be split according to this limit.
*
* This method allows you to treat the files supplied on the command line as
* a single file consisting of the concatenation of each named file. After
* the last line of the first file has been returned, the first line of the
- * second file is returned. The +ARGF.filename+ and +ARGF.lineno+ methods can
- * be used to determine the filename of the current line and line number of
- * the whole input, respectively.
+ * second file is returned. The ARGF.filename and ARGF.lineno methods can be
+ * used to determine the filename of the current line and line number of the
+ * whole input, respectively.
*
* For example, the following code prints out each line of each named file
* prefixed with its line number, displaying the filename once per file:
@@ -13821,7 +14204,7 @@ argf_each_line(int argc, VALUE *argv, VALUE argf)
{
RETURN_ENUMERATOR(argf, argc, argv);
FOREACH_ARGF() {
- argf_block_call_line(rb_intern("each_line"), argc, argv, argf);
+ argf_block_call_line(rb_intern("each_line"), argc, argv, argf);
}
return argf;
}
@@ -13832,12 +14215,12 @@ argf_each_line(int argc, VALUE *argv, VALUE argf)
* ARGF.each_byte -> an_enumerator
*
* Iterates over each byte of each file in +ARGV+.
- * A byte is returned as an +Integer+ in the range 0..255.
+ * A byte is returned as an Integer in the range 0..255.
*
* This method allows you to treat the files supplied on the command line as
* a single file consisting of the concatenation of each named file. After
* the last byte of the first file has been returned, the first byte of the
- * second file is returned. The +ARGF.filename+ method can be used to
+ * second file is returned. The ARGF.filename method can be used to
* determine the filename of the current byte.
*
* If no block is given, an enumerator is returned instead.
@@ -13852,7 +14235,7 @@ argf_each_byte(VALUE argf)
{
RETURN_ENUMERATOR(argf, 0, 0);
FOREACH_ARGF() {
- argf_block_call(rb_intern("each_byte"), 0, 0, argf);
+ argf_block_call(rb_intern("each_byte"), 0, 0, argf);
}
return argf;
}
@@ -13862,12 +14245,12 @@ argf_each_byte(VALUE argf)
* ARGF.each_char {|char| block } -> ARGF
* ARGF.each_char -> an_enumerator
*
- * Iterates over each character of each file in +ARGF+.
+ * Iterates over each character of each file in ARGF.
*
* This method allows you to treat the files supplied on the command line as
* a single file consisting of the concatenation of each named file. After
* the last character of the first file has been returned, the first
- * character of the second file is returned. The +ARGF.filename+ method can
+ * character of the second file is returned. The ARGF.filename method can
* be used to determine the name of the file in which the current character
* appears.
*
@@ -13878,7 +14261,7 @@ argf_each_char(VALUE argf)
{
RETURN_ENUMERATOR(argf, 0, 0);
FOREACH_ARGF() {
- argf_block_call(rb_intern("each_char"), 0, 0, argf);
+ argf_block_call(rb_intern("each_char"), 0, 0, argf);
}
return argf;
}
@@ -13888,12 +14271,12 @@ argf_each_char(VALUE argf)
* ARGF.each_codepoint {|codepoint| block } -> ARGF
* ARGF.each_codepoint -> an_enumerator
*
- * Iterates over each codepoint of each file in +ARGF+.
+ * Iterates over each codepoint of each file in ARGF.
*
* This method allows you to treat the files supplied on the command line as
* a single file consisting of the concatenation of each named file. After
* the last codepoint of the first file has been returned, the first
- * codepoint of the second file is returned. The +ARGF.filename+ method can
+ * codepoint of the second file is returned. The ARGF.filename method can
* be used to determine the name of the file in which the current codepoint
* appears.
*
@@ -13904,7 +14287,7 @@ argf_each_codepoint(VALUE argf)
{
RETURN_ENUMERATOR(argf, 0, 0);
FOREACH_ARGF() {
- argf_block_call(rb_intern("each_codepoint"), 0, 0, argf);
+ argf_block_call(rb_intern("each_codepoint"), 0, 0, argf);
}
return argf;
}
@@ -13948,7 +14331,7 @@ argf_filename_getter(ID id, VALUE *var)
* call-seq:
* ARGF.file -> IO or File object
*
- * Returns the current file as an +IO+ or +File+ object.
+ * Returns the current file as an IO or File object.
* <code>$stdin</code> is returned when the current file is STDIN.
*
* For example:
@@ -13973,7 +14356,7 @@ argf_file(VALUE argf)
* call-seq:
* ARGF.binmode -> ARGF
*
- * Puts +ARGF+ into binary mode. Once a stream is in binary mode, it cannot
+ * Puts ARGF into binary mode. Once a stream is in binary mode, it cannot
* be reset to non-binary mode. This option has the following effects:
*
* * Newline conversion is disabled.
@@ -13994,8 +14377,8 @@ argf_binmode_m(VALUE argf)
* call-seq:
* ARGF.binmode? -> true or false
*
- * Returns true if +ARGF+ is being read in binary mode; false otherwise.
- * To enable binary mode use +ARGF.binmode+.
+ * Returns true if ARGF is being read in binary mode; false otherwise.
+ * To enable binary mode use ARGF.binmode.
*
* For example:
*
@@ -14027,8 +14410,8 @@ static VALUE
argf_skip(VALUE argf)
{
if (ARGF.init_p && ARGF.next_p == 0) {
- argf_close(argf);
- ARGF.next_p = 1;
+ argf_close(argf);
+ ARGF.next_p = 1;
}
return argf;
}
@@ -14038,7 +14421,7 @@ argf_skip(VALUE argf)
* ARGF.close -> ARGF
*
* Closes the current file and skips to the next file in ARGV. If there are
- * no more files to open, just closes the current file. +STDIN+ will not be
+ * no more files to open, just closes the current file. STDIN will not be
* closed.
*
* For example:
@@ -14056,7 +14439,7 @@ argf_close_m(VALUE argf)
next_argv();
argf_close(argf);
if (ARGF.next_p != -1) {
- ARGF.next_p = 1;
+ ARGF.next_p = 1;
}
ARGF.lineno = 0;
return argf;
@@ -14067,7 +14450,7 @@ argf_close_m(VALUE argf)
* ARGF.closed? -> true or false
*
* Returns _true_ if the current file has been closed; _false_ otherwise. Use
- * +ARGF.close+ to actually close the current file.
+ * ARGF.close to actually close the current file.
*/
static VALUE
argf_closed(VALUE argf)
@@ -14093,9 +14476,9 @@ argf_to_s(VALUE argf)
* call-seq:
* ARGF.inplace_mode -> String
*
- * Returns the file extension appended to the names of modified files under
- * in-place edit mode. This value can be set using +ARGF.inplace_mode=+ or
- * passing the +-i+ switch to the Ruby binary.
+ * Returns the file extension appended to the names of backup copies of
+ * modified files under in-place edit mode. This value can be set using
+ * ARGF.inplace_mode= or passing the +-i+ switch to the Ruby binary.
*/
static VALUE
argf_inplace_mode_get(VALUE argf)
@@ -14116,8 +14499,8 @@ opt_i_get(ID id, VALUE *var)
* ARGF.inplace_mode = ext -> ARGF
*
* Sets the filename extension for in-place editing mode to the given String.
- * Each file being edited has this value appended to its filename. The
- * modified file is saved under this new name.
+ * The backup copy of each file being edited has this value appended to its
+ * filename.
*
* For example:
*
@@ -14128,20 +14511,21 @@ opt_i_get(ID id, VALUE *var)
* print line.sub("foo","bar")
* end
*
- * Each line of _file.txt_ has the first occurrence of "foo" replaced with
- * "bar", then the new line is written out to _file.txt.bak_.
+ * First, _file.txt.bak_ is created as a backup copy of _file.txt_.
+ * Then, each line of _file.txt_ has the first occurrence of "foo" replaced with
+ * "bar".
*/
static VALUE
argf_inplace_mode_set(VALUE argf, VALUE val)
{
if (!RTEST(val)) {
- ARGF.inplace = Qfalse;
+ ARGF.inplace = Qfalse;
}
else if (StringValueCStr(val), !RSTRING_LEN(val)) {
- ARGF.inplace = Qnil;
+ ARGF.inplace = Qnil;
}
else {
- ARGF.inplace = rb_str_new_frozen(val);
+ ARGF.inplace = rb_str_new_frozen(val);
}
return argf;
}
@@ -14201,7 +14585,7 @@ static VALUE
argf_write_io(VALUE argf)
{
if (!RTEST(ARGF.current_file)) {
- rb_raise(rb_eIOError, "not opened for writing");
+ rb_raise(rb_eIOError, "not opened for writing");
}
return GetWriteIO(ARGF.current_file);
}
@@ -14231,41 +14615,41 @@ rb_readwrite_syserr_fail(enum rb_io_wait_readwrite waiting, int n, const char *m
arg = mesg ? rb_str_new2(mesg) : Qnil;
switch (waiting) {
case RB_IO_WAIT_WRITABLE:
- switch (n) {
- case EAGAIN:
+ switch (n) {
+ case EAGAIN:
c = rb_eEAGAINWaitWritable;
- break;
+ break;
#if EAGAIN != EWOULDBLOCK
- case EWOULDBLOCK:
+ case EWOULDBLOCK:
c = rb_eEWOULDBLOCKWaitWritable;
- break;
+ break;
#endif
- case EINPROGRESS:
+ case EINPROGRESS:
c = rb_eEINPROGRESSWaitWritable;
- break;
- default:
+ break;
+ default:
rb_mod_syserr_fail_str(rb_mWaitWritable, n, arg);
- }
+ }
break;
case RB_IO_WAIT_READABLE:
- switch (n) {
- case EAGAIN:
+ switch (n) {
+ case EAGAIN:
c = rb_eEAGAINWaitReadable;
- break;
+ break;
#if EAGAIN != EWOULDBLOCK
- case EWOULDBLOCK:
+ case EWOULDBLOCK:
c = rb_eEWOULDBLOCKWaitReadable;
- break;
+ break;
#endif
- case EINPROGRESS:
+ case EINPROGRESS:
c = rb_eEINPROGRESSWaitReadable;
- break;
- default:
+ break;
+ default:
rb_mod_syserr_fail_str(rb_mWaitReadable, n, arg);
- }
+ }
break;
default:
- rb_bug("invalid read/write type passed to rb_readwrite_sys_fail: %d", waiting);
+ rb_bug("invalid read/write type passed to rb_readwrite_sys_fail: %d", waiting);
}
rb_exc_raise(rb_class_new_instance(1, &arg, c));
}
@@ -14307,9 +14691,9 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* methods exist in two forms,
*
* one that returns +nil+ when the end of file is reached, the other
- * raises +EOFError+.
+ * raises EOFError.
*
- * +EOFError+ is a subclass of +IOError+.
+ * EOFError is a subclass of IOError.
*
* file = File.open("/etc/hosts")
* file.read
@@ -14321,11 +14705,11 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
/*
* Document-class: ARGF
*
- * +ARGF+ is a stream designed for use in scripts that process files given as
+ * ARGF is a stream designed for use in scripts that process files given as
* command-line arguments or passed in via STDIN.
*
* The arguments passed to your script are stored in the +ARGV+ Array, one
- * argument per element. +ARGF+ assumes that any arguments that aren't
+ * argument per element. ARGF assumes that any arguments that aren't
* filenames have been removed from +ARGV+. For example:
*
* $ ruby argf.rb --verbose file1 file2
@@ -14334,15 +14718,15 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* option = ARGV.shift #=> "--verbose"
* ARGV #=> ["file1", "file2"]
*
- * You can now use +ARGF+ to work with a concatenation of each of these named
- * files. For instance, +ARGF.read+ will return the contents of _file1_
+ * You can now use ARGF to work with a concatenation of each of these named
+ * files. For instance, ARGF.read will return the contents of _file1_
* followed by the contents of _file2_.
*
- * After a file in +ARGV+ has been read +ARGF+ removes it from the Array.
+ * After a file in +ARGV+ has been read ARGF removes it from the Array.
* Thus, after all files have been read +ARGV+ will be empty.
*
- * You can manipulate +ARGV+ yourself to control what +ARGF+ operates on. If
- * you remove a file from +ARGV+, it is ignored by +ARGF+; if you add files to
+ * You can manipulate +ARGV+ yourself to control what ARGF operates on. If
+ * you remove a file from +ARGV+, it is ignored by ARGF; if you add files to
* +ARGV+, they are treated as if they were named on the command line. For
* example:
*
@@ -14352,7 +14736,7 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* ARGV.replace ["file2", "file3"]
* ARGF.read # Returns the contents of file2 and file3
*
- * If +ARGV+ is empty, +ARGF+ acts as if it contained STDIN, i.e. the data
+ * If +ARGV+ is empty, ARGF acts as if it contained STDIN, i.e. the data
* piped to your script. For example:
*
* $ echo "glark" | ruby -e 'p ARGF.read'
@@ -14371,7 +14755,10 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* The global constant ARGF (also accessible as <tt>$<</tt>)
* provides an IO-like stream that allows access to all file paths
* found in ARGV (or found in STDIN if ARGV is empty).
- * Note that ARGF is not itself a subclass of \IO.
+ * ARGF is not itself a subclass of \IO.
+ *
+ * \Class StringIO provides an IO-like stream that handles a String.
+ * \StringIO is not itself a subclass of \IO.
*
* Important objects based on \IO include:
*
@@ -14389,20 +14776,23 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* - Kernel#open: Returns a new \IO object connected to a given source:
* stream, file, or subprocess.
*
- * An \IO stream has:
+ * Like a \File stream, an \IO stream has:
*
* - A read/write mode, which may be read-only, write-only, or read/write;
- * see {Read/Write Mode}[rdoc-ref:IO@Read-2FWrite+Mode].
+ * see {Read/Write Mode}[rdoc-ref:File@Read-2FWrite+Mode].
* - A data mode, which may be text-only or binary;
- * see {Data Mode}[rdoc-ref:IO@Data+Mode].
+ * see {Data Mode}[rdoc-ref:File@Data+Mode].
+ * - Internal and external encodings;
+ * see {Encodings}[rdoc-ref:File@Encodings].
+ *
+ * And like other \IO streams, it has:
+ *
* - A position, which determines where in the stream the next
* read or write is to occur;
* see {Position}[rdoc-ref:IO@Position].
* - A line number, which is a special, line-oriented, "position"
* (different from the position mentioned above);
* see {Line Number}[rdoc-ref:IO@Line+Number].
- * - Internal and external encodings;
- * see {Encodings}[rdoc-ref:IO@Encodings].
*
* == Extension <tt>io/console</tt>
*
@@ -14412,219 +14802,146 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
*
* == Example Files
*
- * Many examples here use these filenames and their corresponding files:
- *
- * - <tt>t.txt</tt>: A text-only file that is assumed to exist via:
- *
- * text = <<~EOT
- * First line
- * Second line
- *
- * Fourth line
- * Fifth line
- * EOT
- * File.write('t.txt', text)
- *
- * - <tt>t.dat</tt>: A data file that is assumed to exist via:
- *
- * data = "\u9990\u9991\u9992\u9993\u9994"
- * f = File.open('t.dat', 'wb:UTF-16')
- * f.write(data)
- * f.close
+ * Many examples here use these variables:
*
- * - <tt>t.rus</tt>: A Russian-language text file that is assumed to exist via:
+ * :include: doc/examples/files.rdoc
*
- * File.write('t.rus', "\u{442 435 441 442}")
- *
- * - <tt>t.tmp</tt>: A file that is assumed _not_ to exist.
- *
- * == Modes
- *
- * A number of \IO method calls must or may specify a _mode_ for the stream;
- * the mode determines how stream is to be accessible, including:
- *
- * - Whether the stream is to be read-only, write-only, or read-write.
- * - Whether the stream is positioned at its beginning or its end.
- * - Whether the stream treats data as text-only or binary.
- * - The external and internal encodings.
- *
- * === Read/Write Mode
- *
- * ==== Read/Write Mode Specified as an \Integer
+ * == Open Options
*
- * When +mode+ is an integer it must be one or more (combined by bitwise OR (<tt>|</tt>)
- * of the following modes:
+ * A number of \IO methods accept optional keyword arguments
+ * that determine how a new stream is to be opened:
*
- * - +File::RDONLY+: Open for reading only.
- * - +File::WRONLY+: Open for writing only.
- * - +File::RDWR+: Open for reading and writing.
- * - +File::APPEND+: Open for appending only.
- * - +File::CREAT+: Create file if it does not exist.
- * - +File::EXCL+: Raise an exception if +File::CREAT+ is given and the file exists.
+ * - +:mode+: Stream mode.
+ * - +:flags+: \Integer file open flags;
+ * If +mode+ is also given, the two are bitwise-ORed.
+ * - +:external_encoding+: External encoding for the stream.
+ * - +:internal_encoding+: Internal encoding for the stream.
+ * <tt>'-'</tt> is a synonym for the default internal encoding.
+ * If the value is +nil+ no conversion occurs.
+ * - +:encoding+: Specifies external and internal encodings as <tt>'extern:intern'</tt>.
+ * - +:textmode+: If a truthy value, specifies the mode as text-only, binary otherwise.
+ * - +:binmode+: If a truthy value, specifies the mode as binary, text-only otherwise.
+ * - +:autoclose+: If a truthy value, specifies that the +fd+ will close
+ * when the stream closes; otherwise it remains open.
+ * - +:path:+ If a string value is provided, it is used in #inspect and is available as
+ * #path method.
*
- * Examples:
+ * Also available are the options offered in String#encode,
+ * which may control conversion between external internal encoding.
*
- * File.new('t.txt', File::RDONLY)
- * File.new('t.tmp', File::RDWR | File::CREAT | File::EXCL)
+ * == Basic \IO
*
- * Note: Method IO#set_encoding does not allow the mode to be specified as an integer.
+ * You can perform basic stream \IO with these methods,
+ * which typically operate on multi-byte strings:
*
- * ==== Read/Write Mode Specified As a \String
+ * - IO#read: Reads and returns some or all of the remaining bytes from the stream.
+ * - IO#write: Writes zero or more strings to the stream;
+ * each given object that is not already a string is converted via +to_s+.
*
- * When +mode+ is a string it must begin with one of the following:
+ * === Position
*
- * - <tt>'r'</tt>: Read-only stream, positioned at the beginning;
- * the stream cannot be changed to writable.
- * - <tt>'w'</tt>: Write-only stream, positioned at the beginning;
- * the stream cannot be changed to readable.
- * - <tt>'a'</tt>: Write-only stream, positioned at the end;
- * every write appends to the end;
- * the stream cannot be changed to readable.
- * - <tt>'r+'</tt>: Read-write stream, positioned at the beginning.
- * - <tt>'w+'</tt>: Read-write stream, positioned at the end.
- * - <tt>'a+'</tt>: Read-write stream, positioned at the end.
+ * An \IO stream has a nonnegative integer _position_,
+ * which is the byte offset at which the next read or write is to occur.
+ * A new stream has position zero (and line number zero);
+ * method +rewind+ resets the position (and line number) to zero.
*
- * For a writable file stream (that is, any except read-only),
- * the file is truncated to zero if it exists,
- * and is created if it does not exist.
+ * The relevant methods:
*
- * Examples:
+ * - IO#tell (aliased as +#pos+): Returns the current position (in bytes) in the stream.
+ * - IO#pos=: Sets the position of the stream to a given integer +new_position+ (in bytes).
+ * - IO#seek: Sets the position of the stream to a given integer +offset+ (in bytes),
+ * relative to a given position +whence+
+ * (indicating the beginning, end, or current position).
+ * - IO#rewind: Positions the stream at the beginning (also resetting the line number).
*
- * File.open('t.txt', 'r')
- * File.open('t.tmp', 'w')
+ * === Open and Closed Streams
*
- * === Data Mode
+ * A new \IO stream may be open for reading, open for writing, or both.
*
- * Either of the following may be suffixed to any of the string read/write modes above:
+ * A stream is automatically closed when claimed by the garbage collector.
*
- * - <tt>'t'</tt>: Text data; sets the default external encoding to +Encoding::UTF_8+;
- * on Windows, enables conversion between EOL and CRLF.
- * - <tt>'b'</tt>: Binary data; sets the default external encoding to +Encoding::ASCII_8BIT+;
- * on Windows, suppresses conversion between EOL and CRLF.
+ * Attempted reading or writing on a closed stream raises an exception.
*
- * If neither is given, the stream defaults to text data.
+ * The relevant methods:
*
- * Examples:
+ * - IO#close: Closes the stream for both reading and writing.
+ * - IO#close_read: Closes the stream for reading.
+ * - IO#close_write: Closes the stream for writing.
+ * - IO#closed?: Returns whether the stream is closed.
*
- * File.open('t.txt', 'rt')
- * File.open('t.dat', 'rb')
+ * === End-of-Stream
*
- * The following may be suffixed to any writable string mode above:
+ * You can query whether a stream is positioned at its end:
*
- * - <tt>'x'</tt>: Creates the file if it does not exist;
- * raises an exception if the file exists.
+ * - IO#eof? (also aliased as +#eof+): Returns whether the stream is at end-of-stream.
*
- * Example:
+ * You can reposition to end-of-stream by using method IO#seek:
*
- * File.open('t.tmp', 'wx')
- *
- * Note that when using integer flags to set the read/write mode, it's not
- * possible to also set the binary data mode by adding the File::BINARY flag
- * to the bitwise OR combination of integer flags. This is because, as
- * documented in File::Constants, the File::BINARY flag only disables line code
- * conversion, but does not change the external encoding at all.
- *
- * == Encodings
- *
- * Any of the string modes above may specify encodings --
- * either external encoding only or both external and internal encodings --
- * by appending one or both encoding names, separated by colons:
- *
- * f = File.new('t.dat', 'rb')
- * f.external_encoding # => #<Encoding:ASCII-8BIT>
- * f.internal_encoding # => nil
- * f = File.new('t.dat', 'rb:UTF-16')
- * f.external_encoding # => #<Encoding:UTF-16 (dummy)>
- * f.internal_encoding # => nil
- * f = File.new('t.dat', 'rb:UTF-16:UTF-16')
- * f.external_encoding # => #<Encoding:UTF-16 (dummy)>
- * f.internal_encoding # => #<Encoding:UTF-16>
+ * f = File.new('t.txt')
+ * f.eof? # => false
+ * f.seek(0, :END)
+ * f.eof? # => true
* f.close
*
- * The numerous encoding names are available in array Encoding.name_list:
- *
- * Encoding.name_list.size # => 175
- * Encoding.name_list.take(3) # => ["ASCII-8BIT", "UTF-8", "US-ASCII"]
+ * Or by reading all stream content (which is slower than using IO#seek):
*
- * When the external encoding is set,
- * strings read are tagged by that encoding
- * when reading, and strings written are converted to that
- * encoding when writing.
+ * f.rewind
+ * f.eof? # => false
+ * f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n"
+ * f.eof? # => true
*
- * When both external and internal encodings are set,
- * strings read are converted from external to internal encoding,
- * and strings written are converted from internal to external encoding.
- * For further details about transcoding input and output, see Encoding.
+ * == Line \IO
*
- * If the external encoding is <tt>'BOM|UTF-8'</tt>, <tt>'BOM|UTF-16LE'</tt>
- * or <tt>'BOM|UTF16-BE'</tt>, Ruby checks for
- * a Unicode BOM in the input document to help determine the encoding. For
- * UTF-16 encodings the file open mode must be binary.
- * If the BOM is found, it is stripped and the external encoding from the BOM is used.
+ * You can read an \IO stream line-by-line using these methods:
*
- * Note that the BOM-style encoding option is case insensitive,
- * so 'bom|utf-8' is also valid.)
+ * - IO#each_line: Reads each remaining line, passing it to the given block.
+ * - IO#gets: Returns the next line.
+ * - IO#readline: Like #gets, but raises an exception at end-of-stream.
+ * - IO#readlines: Returns all remaining lines in an array.
*
- * == Open Options
+ * Each of these reader methods accepts:
*
- * A number of \IO methods accept optional keyword arguments
- * that determine how a new stream is to be opened:
- *
- * - +:mode+: Stream mode.
- * - +:flags+: \Integer file open flags;
- * If +mode+ is also given, the two are bitwise-ORed.
- * - +:external_encoding+: External encoding for the stream.
- * - +:internal_encoding+: Internal encoding for the stream.
- * <tt>'-'</tt> is a synonym for the default internal encoding.
- * If the value is +nil+ no conversion occurs.
- * - +:encoding+: Specifies external and internal encodings as <tt>'extern:intern'</tt>.
- * - +:textmode+: If a truthy value, specifies the mode as text-only, binary otherwise.
- * - +:binmode+: If a truthy value, specifies the mode as binary, text-only otherwise.
- * - +:autoclose+: If a truthy value, specifies that the +fd+ will close
- * when the stream closes; otherwise it remains open.
+ * - An optional line separator, +sep+;
+ * see {Line Separator}[rdoc-ref:IO@Line+Separator].
+ * - An optional line-size limit, +limit+;
+ * see {Line Limit}[rdoc-ref:IO@Line+Limit].
*
- * Also available are the options offered in String#encode,
- * which may control conversion between external internal encoding.
+ * For each of these reader methods, reading may begin mid-line,
+ * depending on the stream's position;
+ * see {Position}[rdoc-ref:IO@Position]:
*
- * == Lines
+ * f = File.new('t.txt')
+ * f.pos = 27
+ * f.each_line {|line| p line }
+ * f.close
*
- * Some reader methods in \IO are line-oriented;
- * such a method reads one or more lines,
- * which are separated by an implicit or explicit line separator.
+ * Output:
*
- * These methods include:
+ * "rth line\n"
+ * "Fifth line\n"
*
- * - Kernel#gets
- * - Kernel#readline
- * - Kernel#readlines
- * - IO.foreach
- * - IO.readlines
- * - IO#each_line
- * - IO#gets
- * - IO#readline
- * - IO#readlines
- * - ARGF.each
- * - ARGF.gets
- * - ARGF.readline
- * - ARGF.readlines
+ * You can write to an \IO stream line-by-line using this method:
*
- * Each of these methods returns +nil+ if called when already at end-of-stream,
- * except for IO#readline, which raises an exception.
+ * - IO#puts: Writes objects to the stream.
*
- * Each of these methods may be called with:
+ * === Line Separator
*
- * - An optional line separator, +sep+.
- * - An optional line-size limit, +limit+.
- * - Both +sep+ and +limit+.
+ * Each of these methods uses a <i>line separator</i>,
+ * which is the string that delimits lines:
*
- * === Line Separator
+ * - IO.foreach.
+ * - IO.readlines.
+ * - IO#each_line.
+ * - IO#gets.
+ * - IO#readline.
+ * - IO#readlines.
*
* The default line separator is the given by the global variable <tt>$/</tt>,
- * whose value is often <tt>"\n"</tt>.
+ * whose value is by default <tt>"\n"</tt>.
* The line to be read next is all data from the current position
* to the next line separator:
*
- * f = File.open('t.txt')
+ * f = File.new('t.txt')
* f.gets # => "First line\n"
* f.gets # => "Second line\n"
* f.gets # => "\n"
@@ -14659,9 +14976,18 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
*
* === Line Limit
*
- * The line to be read may be further defined by an optional argument +limit+,
- * which specifies that the line may not be (much) longer than the given limit;
- * a multi-byte character will not be split, and so a line may be slightly longer
+ * Each of these methods uses a <i>line limit</i>,
+ * which specifies that the number of bytes returned may not be (much) longer
+ * than the given +limit+;
+ *
+ * - IO.foreach.
+ * - IO.readlines.
+ * - IO#each_line.
+ * - IO#gets.
+ * - IO#readline.
+ * - IO#readlines.
+ *
+ * A multi-byte character will not be split, and so a line may be slightly longer
* than the given limit.
*
* If +limit+ is not given, the line is determined only by +sep+.
@@ -14677,10 +15003,10 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* File.open('t.txt') {|f| f.gets(12) } # => "First line\n"
*
* # Text with 2-byte characters, which will not be split.
- * File.open('r.rus') {|f| f.gets(1).size } # => 1
- * File.open('r.rus') {|f| f.gets(2).size } # => 1
- * File.open('r.rus') {|f| f.gets(3).size } # => 2
- * File.open('r.rus') {|f| f.gets(4).size } # => 2
+ * File.open('t.rus') {|f| f.gets(1).size } # => 1
+ * File.open('t.rus') {|f| f.gets(2).size } # => 1
+ * File.open('t.rus') {|f| f.gets(3).size } # => 2
+ * File.open('t.rus') {|f| f.gets(4).size } # => 2
*
* === Line Separator and Line Limit
*
@@ -14697,17 +15023,38 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
*
* === Line Number
*
- * A readable \IO stream has a _line_ _number_,
- * which is the non-negative integer line number
- * in the stream where the next read will occur.
+ * A readable \IO stream has a non-negative integer <i>line number</i>.
+ *
+ * The relevant methods:
+ *
+ * - IO#lineno: Returns the line number.
+ * - IO#lineno=: Resets and returns the line number.
+ *
+ * Unless modified by a call to method IO#lineno=,
+ * the line number is the number of lines read
+ * by certain line-oriented methods,
+ * according to the given line separator +sep+:
*
- * A new stream is initially has line number +0+.
+ * - IO.foreach: Increments the line number on each call to the block.
+ * - IO#each_line: Increments the line number on each call to the block.
+ * - IO#gets: Increments the line number.
+ * - IO#readline: Increments the line number.
+ * - IO#readlines: Increments the line number for each line read.
*
- * \Method IO#lineno returns the line number.
+ * A new stream is initially has line number zero (and position zero);
+ * method +rewind+ resets the line number (and position) to zero:
+ *
+ * f = File.new('t.txt')
+ * f.lineno # => 0
+ * f.gets # => "First line\n"
+ * f.lineno # => 1
+ * f.rewind
+ * f.lineno # => 0
+ * f.close
*
* Reading lines from a stream usually changes its line number:
*
- * f = File.open('t.txt', 'r')
+ * f = File.new('t.txt', 'r')
* f.lineno # => 0
* f.readline # => "This is line one.\n"
* f.lineno # => 1
@@ -14720,24 +15067,87 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
*
* Iterating over lines in a stream usually changes its line number:
*
- * f = File.open('t.txt')
- * f.each_line do |line|
- * p "position=#{f.pos} eof?=#{f.eof?} line=#{line}"
- * end
- * f.close
+ * File.open('t.txt') do |f|
+ * f.each_line do |line|
+ * p "position=#{f.pos} eof?=#{f.eof?} lineno=#{f.lineno}"
+ * end
+ * end
*
* Output:
*
- * "position=19 eof?=false line=This is line one.\n"
- * "position=45 eof?=false line=This is the second line.\n"
- * "position=70 eof?=true line=This is the third line.\n"
+ * "position=11 eof?=false lineno=1"
+ * "position=23 eof?=false lineno=2"
+ * "position=24 eof?=false lineno=3"
+ * "position=36 eof?=false lineno=4"
+ * "position=47 eof?=true lineno=5"
*
- * === Line Options
+ * Unlike the stream's {position}[rdoc-ref:IO@Position],
+ * the line number does not affect where the next read or write will occur:
*
- * A number of \IO methods accept optional keyword arguments
- * that determine how lines in a stream are to be treated:
+ * f = File.new('t.txt')
+ * f.lineno = 1000
+ * f.lineno # => 1000
+ * f.gets # => "First line\n"
+ * f.lineno # => 1001
+ * f.close
+ *
+ * Associated with the line number is the global variable <tt>$.</tt>:
+ *
+ * - When a stream is opened, <tt>$.</tt> is not set;
+ * its value is left over from previous activity in the process:
+ *
+ * $. = 41
+ * f = File.new('t.txt')
+ * $. = 41
+ * # => 41
+ * f.close
+ *
+ * - When a stream is read, <tt>#.</tt> is set to the line number for that stream:
+ *
+ * f0 = File.new('t.txt')
+ * f1 = File.new('t.dat')
+ * f0.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"]
+ * $. # => 5
+ * f1.readlines # => ["\xFE\xFF\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94"]
+ * $. # => 1
+ * f0.close
+ * f1.close
+ *
+ * - Methods IO#rewind and IO#seek do not affect <tt>$.</tt>:
+ *
+ * f = File.new('t.txt')
+ * f.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"]
+ * $. # => 5
+ * f.rewind
+ * f.seek(0, :SET)
+ * $. # => 5
+ * f.close
+ *
+ * == Character \IO
+ *
+ * You can process an \IO stream character-by-character using these methods:
+ *
+ * - IO#getc: Reads and returns the next character from the stream.
+ * - IO#readchar: Like #getc, but raises an exception at end-of-stream.
+ * - IO#ungetc: Pushes back ("unshifts") a character or integer onto the stream.
+ * - IO#putc: Writes a character to the stream.
+ * - IO#each_char: Reads each remaining character in the stream,
+ * passing the character to the given block.
+ * == Byte \IO
+ *
+ * You can process an \IO stream byte-by-byte using these methods:
+ *
+ * - IO#getbyte: Returns the next 8-bit byte as an integer in range 0..255.
+ * - IO#readbyte: Like #getbyte, but raises an exception if at end-of-stream.
+ * - IO#ungetbyte: Pushes back ("unshifts") a byte back onto the stream.
+ * - IO#each_byte: Reads each remaining byte in the stream,
+ * passing the byte to the given block.
*
- * - +:chomp+: If +true+, line separators are omitted; default is +false+.
+ * == Codepoint \IO
+ *
+ * You can process an \IO stream codepoint-by-codepoint:
+ *
+ * - IO#each_codepoint: Reads each remaining codepoint, passing it to the given block.
*
* == What's Here
*
@@ -14786,11 +15196,11 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* - #read_nonblock: the next _n_ bytes read from +self+ for a given _n_,
* in non-block mode.
* - #readbyte: Returns the next byte read from +self+;
- * same as #getbyte, but raises an exception on end-of-file.
+ * same as #getbyte, but raises an exception on end-of-stream.
* - #readchar: Returns the next character read from +self+;
- * same as #getc, but raises an exception on end-of-file.
+ * same as #getc, but raises an exception on end-of-stream.
* - #readline: Returns the next line read from +self+;
- * same as #getline, but raises an exception of end-of-file.
+ * same as #getline, but raises an exception of end-of-stream.
* - #readlines: Returns an array of all lines read read from +self+.
* - #readpartial: Returns up to the given number of bytes from +self+.
*
@@ -14850,7 +15260,7 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* - #binmode?: Returns whether +self+ is in binary mode.
* - #close_on_exec?: Returns the close-on-exec flag for +self+.
* - #closed?: Returns whether +self+ is closed.
- * - #eof? (aliased as #eof): Returns whether +self+ is at end-of-file.
+ * - #eof? (aliased as #eof): Returns whether +self+ is at end-of-stream.
* - #external_encoding: Returns the external encoding object for +self+.
* - #fileno (aliased as #to_i): Returns the integer file descriptor for +self+
* - #internal_encoding: Returns the internal encoding object for +self+.
@@ -14900,11 +15310,11 @@ Init_IO(void)
#include <sys/cygwin.h>
static struct __cygwin_perfile pf[] =
{
- {"", O_RDONLY | O_BINARY},
- {"", O_WRONLY | O_BINARY},
- {"", O_RDWR | O_BINARY},
- {"", O_APPEND | O_BINARY},
- {NULL, 0}
+ {"", O_RDONLY | O_BINARY},
+ {"", O_WRONLY | O_BINARY},
+ {"", O_RDWR | O_BINARY},
+ {"", O_APPEND | O_BINARY},
+ {NULL, 0}
};
cygwin_internal(CW_PERFILE, pf);
#endif
@@ -14941,6 +15351,8 @@ Init_IO(void)
rb_cIO = rb_define_class("IO", rb_cObject);
rb_include_module(rb_cIO, rb_mEnumerable);
+ rb_eIOTimeoutError = rb_define_class_under(rb_cIO, "TimeoutError", rb_eIOError);
+
rb_define_const(rb_cIO, "READABLE", INT2NUM(RUBY_IO_READABLE));
rb_define_const(rb_cIO, "WRITABLE", INT2NUM(RUBY_IO_WRITABLE));
rb_define_const(rb_cIO, "PRIORITY", INT2NUM(RUBY_IO_PRIORITY));
@@ -15039,23 +15451,26 @@ Init_IO(void)
rb_define_alias(rb_cIO, "to_i", "fileno");
rb_define_method(rb_cIO, "to_io", rb_io_to_io, 0);
- rb_define_method(rb_cIO, "fsync", rb_io_fsync, 0);
- rb_define_method(rb_cIO, "fdatasync", rb_io_fdatasync, 0);
- rb_define_method(rb_cIO, "sync", rb_io_sync, 0);
- rb_define_method(rb_cIO, "sync=", rb_io_set_sync, 1);
+ rb_define_method(rb_cIO, "timeout", rb_io_timeout, 0);
+ rb_define_method(rb_cIO, "timeout=", rb_io_set_timeout, 1);
+
+ rb_define_method(rb_cIO, "fsync", rb_io_fsync, 0);
+ rb_define_method(rb_cIO, "fdatasync", rb_io_fdatasync, 0);
+ rb_define_method(rb_cIO, "sync", rb_io_sync, 0);
+ rb_define_method(rb_cIO, "sync=", rb_io_set_sync, 1);
- rb_define_method(rb_cIO, "lineno", rb_io_lineno, 0);
- rb_define_method(rb_cIO, "lineno=", rb_io_set_lineno, 1);
+ rb_define_method(rb_cIO, "lineno", rb_io_lineno, 0);
+ rb_define_method(rb_cIO, "lineno=", rb_io_set_lineno, 1);
- rb_define_method(rb_cIO, "readlines", rb_io_readlines, -1);
+ rb_define_method(rb_cIO, "readlines", rb_io_readlines, -1);
- rb_define_method(rb_cIO, "readpartial", io_readpartial, -1);
- rb_define_method(rb_cIO, "read", io_read, -1);
+ rb_define_method(rb_cIO, "readpartial", io_readpartial, -1);
+ rb_define_method(rb_cIO, "read", io_read, -1);
rb_define_method(rb_cIO, "write", io_write_m, -1);
- rb_define_method(rb_cIO, "gets", rb_io_gets_m, -1);
- rb_define_method(rb_cIO, "readline", rb_io_readline, -1);
- rb_define_method(rb_cIO, "getc", rb_io_getc, 0);
- rb_define_method(rb_cIO, "getbyte", rb_io_getbyte, 0);
+ rb_define_method(rb_cIO, "gets", rb_io_gets_m, -1);
+ rb_define_method(rb_cIO, "readline", rb_io_readline, -1);
+ rb_define_method(rb_cIO, "getc", rb_io_getc, 0);
+ rb_define_method(rb_cIO, "getbyte", rb_io_getbyte, 0);
rb_define_method(rb_cIO, "readchar", rb_io_readchar, 0);
rb_define_method(rb_cIO, "readbyte", rb_io_readbyte, 0);
rb_define_method(rb_cIO, "ungetbyte",rb_io_ungetbyte, 1);
@@ -15102,6 +15517,10 @@ Init_IO(void)
rb_define_method(rb_cIO, "ioctl", rb_io_ioctl, -1);
rb_define_method(rb_cIO, "fcntl", rb_io_fcntl, -1);
rb_define_method(rb_cIO, "pid", rb_io_pid, 0);
+
+ rb_define_method(rb_cIO, "path", rb_io_path, 0);
+ rb_define_method(rb_cIO, "to_path", rb_io_path, 0);
+
rb_define_method(rb_cIO, "inspect", rb_io_inspect, 0);
rb_define_method(rb_cIO, "external_encoding", rb_io_external_encoding, 0);
@@ -15128,13 +15547,12 @@ Init_IO(void)
rb_gvar_ractor_local("$>");
rb_gvar_ractor_local("$stderr");
- rb_stdin = rb_io_prep_stdin();
- rb_stdout = rb_io_prep_stdout();
- rb_stderr = rb_io_prep_stderr();
-
rb_global_variable(&rb_stdin);
+ rb_stdin = rb_io_prep_stdin();
rb_global_variable(&rb_stdout);
+ rb_stdout = rb_io_prep_stdout();
rb_global_variable(&rb_stderr);
+ rb_stderr = rb_io_prep_stderr();
orig_stdout = rb_stdout;
orig_stderr = rb_stderr;
diff --git a/io_buffer.c b/io_buffer.c
index cf6784e932..87b51c0b8c 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -11,9 +11,12 @@
#include "ruby/fiber/scheduler.h"
#include "internal.h"
-#include "internal/string.h"
+#include "internal/array.h"
#include "internal/bits.h"
#include "internal/error.h"
+#include "internal/numeric.h"
+#include "internal/string.h"
+#include "internal/thread.h"
VALUE rb_cIOBuffer;
VALUE rb_eIOBufferLockedError;
@@ -44,7 +47,7 @@ struct rb_io_buffer {
};
static inline void *
-io_buffer_map_memory(size_t size)
+io_buffer_map_memory(size_t size, int flags)
{
#if defined(_WIN32)
void * base = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
@@ -53,7 +56,15 @@ io_buffer_map_memory(size_t size)
rb_sys_fail("io_buffer_map_memory:VirtualAlloc");
}
#else
- void * base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+ int mmap_flags = MAP_ANONYMOUS;
+ if (flags & RB_IO_BUFFER_SHARED) {
+ mmap_flags |= MAP_SHARED;
+ }
+ else {
+ mmap_flags |= MAP_PRIVATE;
+ }
+
+ void * base = mmap(NULL, size, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
if (base == MAP_FAILED) {
rb_sys_fail("io_buffer_map_memory:mmap");
@@ -64,7 +75,7 @@ io_buffer_map_memory(size_t size)
}
static void
-io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t offset, enum rb_io_buffer_flags flags)
+io_buffer_map_file(struct rb_io_buffer *buffer, int descriptor, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags)
{
#if defined(_WIN32)
HANDLE file = (HANDLE)_get_osfhandle(descriptor);
@@ -73,7 +84,7 @@ io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t
DWORD protect = PAGE_READONLY, access = FILE_MAP_READ;
if (flags & RB_IO_BUFFER_READONLY) {
- data->flags |= RB_IO_BUFFER_READONLY;
+ buffer->flags |= RB_IO_BUFFER_READONLY;
}
else {
protect = PAGE_READWRITE;
@@ -85,10 +96,12 @@ io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t
if (flags & RB_IO_BUFFER_PRIVATE) {
access |= FILE_MAP_COPY;
- data->flags |= RB_IO_BUFFER_PRIVATE;
- } else {
- // This buffer refers to external data.
- data->flags |= RB_IO_BUFFER_EXTERNAL;
+ buffer->flags |= RB_IO_BUFFER_PRIVATE;
+ }
+ else {
+ // This buffer refers to external buffer.
+ buffer->flags |= RB_IO_BUFFER_EXTERNAL;
+ buffer->flags |= RB_IO_BUFFER_SHARED;
}
void *base = MapViewOfFile(mapping, access, (DWORD)(offset >> 32), (DWORD)(offset & 0xFFFFFFFF), size);
@@ -98,23 +111,24 @@ io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t
rb_sys_fail("io_buffer_map_file:MapViewOfFile");
}
- data->mapping = mapping;
+ buffer->mapping = mapping;
#else
int protect = PROT_READ, access = 0;
if (flags & RB_IO_BUFFER_READONLY) {
- data->flags |= RB_IO_BUFFER_READONLY;
+ buffer->flags |= RB_IO_BUFFER_READONLY;
}
else {
protect |= PROT_WRITE;
}
if (flags & RB_IO_BUFFER_PRIVATE) {
- data->flags |= RB_IO_BUFFER_PRIVATE;
+ buffer->flags |= RB_IO_BUFFER_PRIVATE;
}
else {
- // This buffer refers to external data.
- data->flags |= RB_IO_BUFFER_EXTERNAL;
+ // This buffer refers to external buffer.
+ buffer->flags |= RB_IO_BUFFER_EXTERNAL;
+ buffer->flags |= RB_IO_BUFFER_SHARED;
access |= MAP_SHARED;
}
@@ -125,10 +139,10 @@ io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t
}
#endif
- data->base = base;
- data->size = size;
+ buffer->base = base;
+ buffer->size = size;
- data->flags |= RB_IO_BUFFER_MAPPED;
+ buffer->flags |= RB_IO_BUFFER_MAPPED;
}
static inline void
@@ -158,18 +172,18 @@ io_buffer_experimental(void)
}
static void
-io_buffer_zero(struct rb_io_buffer *data)
+io_buffer_zero(struct rb_io_buffer *buffer)
{
- data->base = NULL;
- data->size = 0;
+ buffer->base = NULL;
+ buffer->size = 0;
#if defined(_WIN32)
- data->mapping = NULL;
+ buffer->mapping = NULL;
#endif
- data->source = Qnil;
+ buffer->source = Qnil;
}
static void
-io_buffer_initialize(struct rb_io_buffer *data, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
+io_buffer_initialize(struct rb_io_buffer *buffer, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
{
if (base) {
// If we are provided a pointer, we use it.
@@ -180,52 +194,53 @@ io_buffer_initialize(struct rb_io_buffer *data, void *base, size_t size, enum rb
base = calloc(size, 1);
}
else if (flags & RB_IO_BUFFER_MAPPED) {
- base = io_buffer_map_memory(size);
+ base = io_buffer_map_memory(size, flags);
}
if (!base) {
rb_raise(rb_eIOBufferAllocationError, "Could not allocate buffer!");
}
- } else {
+ }
+ else {
// Otherwise we don't do anything.
return;
}
- data->base = base;
- data->size = size;
- data->flags = flags;
- data->source = source;
+ buffer->base = base;
+ buffer->size = size;
+ buffer->flags = flags;
+ buffer->source = source;
}
static int
-io_buffer_free(struct rb_io_buffer *data)
+io_buffer_free(struct rb_io_buffer *buffer)
{
- if (data->base) {
- if (data->flags & RB_IO_BUFFER_INTERNAL) {
- free(data->base);
+ if (buffer->base) {
+ if (buffer->flags & RB_IO_BUFFER_INTERNAL) {
+ free(buffer->base);
}
- if (data->flags & RB_IO_BUFFER_MAPPED) {
- io_buffer_unmap(data->base, data->size);
+ if (buffer->flags & RB_IO_BUFFER_MAPPED) {
+ io_buffer_unmap(buffer->base, buffer->size);
}
// Previously we had this, but we found out due to the way GC works, we
// can't refer to any other Ruby objects here.
- // if (RB_TYPE_P(data->source, T_STRING)) {
- // rb_str_unlocktmp(data->source);
+ // if (RB_TYPE_P(buffer->source, T_STRING)) {
+ // rb_str_unlocktmp(buffer->source);
// }
- data->base = NULL;
+ buffer->base = NULL;
#if defined(_WIN32)
- if (data->mapping) {
- CloseHandle(data->mapping);
- data->mapping = NULL;
+ if (buffer->mapping) {
+ CloseHandle(buffer->mapping);
+ buffer->mapping = NULL;
}
#endif
- data->size = 0;
- data->flags = 0;
- data->source = Qnil;
+ buffer->size = 0;
+ buffer->flags = 0;
+ buffer->source = Qnil;
return 1;
}
@@ -234,30 +249,30 @@ io_buffer_free(struct rb_io_buffer *data)
}
void
-rb_io_buffer_type_mark(void *_data)
+rb_io_buffer_type_mark(void *_buffer)
{
- struct rb_io_buffer *data = _data;
- rb_gc_mark(data->source);
+ struct rb_io_buffer *buffer = _buffer;
+ rb_gc_mark(buffer->source);
}
void
-rb_io_buffer_type_free(void *_data)
+rb_io_buffer_type_free(void *_buffer)
{
- struct rb_io_buffer *data = _data;
+ struct rb_io_buffer *buffer = _buffer;
- io_buffer_free(data);
+ io_buffer_free(buffer);
- free(data);
+ free(buffer);
}
size_t
-rb_io_buffer_type_size(const void *_data)
+rb_io_buffer_type_size(const void *_buffer)
{
- const struct rb_io_buffer *data = _data;
+ const struct rb_io_buffer *buffer = _buffer;
size_t total = sizeof(struct rb_io_buffer);
- if (data->flags) {
- total += data->size;
+ if (buffer->flags) {
+ total += buffer->size;
}
return total;
@@ -274,48 +289,157 @@ static const rb_data_type_t rb_io_buffer_type = {
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
};
+// Extract an offset argument, which must be a positive integer.
+static inline size_t
+io_buffer_extract_offset(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Offset can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+// Extract a length argument, which must be a positive integer.
+// Length is generally considered a mutable property of an object and
+// semantically should be considered a subset of "size" as a concept.
+static inline size_t
+io_buffer_extract_length(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Length can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+// Extract a size argument, which must be a positive integer.
+// Size is generally considered an immutable property of an object.
+static inline size_t
+io_buffer_extract_size(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Size can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+// Compute the default length for a buffer, given an offset into that buffer.
+// The default length is the size of the buffer minus the offset. The offset
+// must be less than the size of the buffer otherwise the length will be
+// invalid; in that case, an ArgumentError exception will be raised.
+static inline size_t
+io_buffer_default_length(const struct rb_io_buffer *buffer, size_t offset)
+{
+ if (offset > buffer->size) {
+ rb_raise(rb_eArgError, "The given offset is bigger than the buffer size!");
+ }
+
+ // Note that the "length" is computed by the size the offset.
+ return buffer->size - offset;
+}
+
+// Extract the optional length and offset arguments, returning the buffer.
+// The length and offset are optional, but if they are provided, they must be
+// positive integers. If the length is not provided, the default length is
+// computed from the buffer size and offset. If the offset is not provided, it
+// defaults to zero.
+static inline struct rb_io_buffer *
+io_buffer_extract_length_offset(VALUE self, int argc, VALUE argv[], size_t *length, size_t *offset)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ if (argc >= 2) {
+ *offset = io_buffer_extract_offset(argv[1]);
+ }
+ else {
+ *offset = 0;
+ }
+
+ if (argc >= 1 && !NIL_P(argv[0])) {
+ *length = io_buffer_extract_length(argv[0]);
+ }
+ else {
+ *length = io_buffer_default_length(buffer, *offset);
+ }
+
+ return buffer;
+}
+
+// Extract the optional offset and length arguments, returning the buffer.
+// Similar to `io_buffer_extract_length_offset` but with the order of
+// arguments reversed.
+static inline struct rb_io_buffer *
+io_buffer_extract_offset_length(VALUE self, int argc, VALUE argv[], size_t *offset, size_t *length)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ if (argc >= 1) {
+ *offset = io_buffer_extract_offset(argv[0]);
+ }
+ else {
+ *offset = 0;
+ }
+
+ if (argc >= 2) {
+ *length = io_buffer_extract_length(argv[1]);
+ }
+ else {
+ *length = io_buffer_default_length(buffer, *offset);
+ }
+
+ return buffer;
+}
+
VALUE
rb_io_buffer_type_allocate(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- VALUE instance = TypedData_Make_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ VALUE instance = TypedData_Make_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_zero(data);
+ io_buffer_zero(buffer);
return instance;
}
-static VALUE
-io_buffer_for_make_instance(VALUE klass, VALUE string)
+static VALUE io_buffer_for_make_instance(VALUE klass, VALUE string, enum rb_io_buffer_flags flags)
{
VALUE instance = rb_io_buffer_type_allocate(klass);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- enum rb_io_buffer_flags flags = RB_IO_BUFFER_EXTERNAL;
+ flags |= RB_IO_BUFFER_EXTERNAL;
if (RB_OBJ_FROZEN(string))
flags |= RB_IO_BUFFER_READONLY;
- io_buffer_initialize(data, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
+ if (!(flags & RB_IO_BUFFER_READONLY))
+ rb_str_modify(string);
+
+ io_buffer_initialize(buffer, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
return instance;
}
struct io_buffer_for_yield_instance_arguments {
- VALUE klass;
- VALUE string;
- VALUE instance;
+ VALUE klass;
+ VALUE string;
+ VALUE instance;
+ enum rb_io_buffer_flags flags;
};
static VALUE
-io_buffer_for_yield_instance(VALUE _arguments) {
+io_buffer_for_yield_instance(VALUE _arguments)
+{
struct io_buffer_for_yield_instance_arguments *arguments = (struct io_buffer_for_yield_instance_arguments *)_arguments;
- rb_str_locktmp(arguments->string);
+ arguments->instance = io_buffer_for_make_instance(arguments->klass, arguments->string, arguments->flags);
- arguments->instance = io_buffer_for_make_instance(arguments->klass, arguments->string);
+ rb_str_locktmp(arguments->string);
return rb_yield(arguments->instance);
}
@@ -342,14 +466,15 @@ io_buffer_for_yield_instance_ensure(VALUE _arguments)
* Creates a IO::Buffer from the given string's memory. Without a block a
* frozen internal copy of the string is created efficiently and used as the
* buffer source. When a block is provided, the buffer is associated directly
- * with the string's internal data and updating the buffer will update the
+ * with the string's internal buffer and updating the buffer will update the
* string.
*
* Until #free is invoked on the buffer, either explicitly or via the garbage
* collector, the source string will be locked and cannot be modified.
*
* If the string is frozen, it will create a read-only buffer which cannot be
- * modified.
+ * modified. If the string is shared, it may trigger a copy-on-write when
+ * using the block form.
*
* string = 'test'
* buffer = IO::Buffer.for(string)
@@ -377,17 +502,19 @@ rb_io_buffer_type_for(VALUE klass, VALUE string)
// If the string is frozen, both code paths are okay.
// If the string is not frozen, if a block is not given, it must be frozen.
if (rb_block_given_p()) {
- struct io_buffer_for_yield_instance_arguments arguments = {
- .klass = klass,
- .string = string,
- .instance = Qnil,
- };
+ struct io_buffer_for_yield_instance_arguments arguments = {
+ .klass = klass,
+ .string = string,
+ .instance = Qnil,
+ .flags = 0,
+ };
- return rb_ensure(io_buffer_for_yield_instance, (VALUE)&arguments, io_buffer_for_yield_instance_ensure, (VALUE)&arguments);
- } else {
- // This internally returns the source string if it's already frozen.
- string = rb_str_tmp_frozen_acquire(string);
- return io_buffer_for_make_instance(klass, string);
+ return rb_ensure(io_buffer_for_yield_instance, (VALUE)&arguments, io_buffer_for_yield_instance_ensure, (VALUE)&arguments);
+ }
+ else {
+ // This internally returns the source string if it's already frozen.
+ string = rb_str_tmp_frozen_acquire(string);
+ return io_buffer_for_make_instance(klass, string, RB_IO_BUFFER_READONLY);
}
}
@@ -396,27 +523,27 @@ rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags)
{
VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_initialize(data, base, size, flags, Qnil);
+ io_buffer_initialize(buffer, base, size, flags, Qnil);
return instance;
}
VALUE
-rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags flags)
+rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags)
{
io_buffer_experimental();
VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer);
int descriptor = rb_io_descriptor(io);
- io_buffer_map_file(data, descriptor, size, offset, flags);
+ io_buffer_map_file(buffer, descriptor, size, offset, flags);
return instance;
}
@@ -433,48 +560,47 @@ rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags fl
* mapping, you need to open a file in read-write mode, and explicitly pass
* +flags+ argument without IO::Buffer::IMMUTABLE.
*
- * File.write('test.txt', 'test')
+ * Example:
*
- * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
- * # => #<IO::Buffer 0x00000001014a0000+4 MAPPED READONLY>
+ * File.write('test.txt', 'test')
*
- * buffer.readonly? # => true
+ * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
+ * # => #<IO::Buffer 0x00000001014a0000+4 MAPPED READONLY>
*
- * buffer.get_string
- * # => "test"
+ * buffer.readonly? # => true
*
- * buffer.set_string('b', 0)
- * # `set_string': Buffer is not writable! (IO::Buffer::AccessError)
+ * buffer.get_string
+ * # => "test"
*
- * # create read/write mapping: length 4 bytes, offset 0, flags 0
- * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
- * buffer.set_string('b', 0)
- * # => 1
+ * buffer.set_string('b', 0)
+ * # `set_string': Buffer is not writable! (IO::Buffer::AccessError)
*
- * # Check it
- * File.read('test.txt')
- * # => "best"
+ * # create read/write mapping: length 4 bytes, offset 0, flags 0
+ * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
+ * buffer.set_string('b', 0)
+ * # => 1
+ *
+ * # Check it
+ * File.read('test.txt')
+ * # => "best"
*
* Note that some operating systems may not have cache coherency between mapped
* buffers and file reads.
- *
*/
static VALUE
io_buffer_map(int argc, VALUE *argv, VALUE klass)
{
- if (argc < 1 || argc > 4) {
- rb_error_arity(argc, 2, 4);
- }
+ rb_check_arity(argc, 1, 4);
// We might like to handle a string path?
VALUE io = argv[0];
size_t size;
if (argc >= 2 && !RB_NIL_P(argv[1])) {
- size = RB_NUM2SIZE(argv[1]);
+ size = io_buffer_extract_size(argv[1]);
}
else {
- off_t file_size = rb_file_size(io);
+ rb_off_t file_size = rb_file_size(io);
// Compiler can confirm that we handled file_size < 0 case:
if (file_size < 0) {
@@ -490,7 +616,8 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass)
}
}
- off_t offset = 0;
+ // This is the file offset, not the buffer offset:
+ rb_off_t offset = 0;
if (argc >= 3) {
offset = NUM2OFFT(argv[2]);
}
@@ -520,7 +647,7 @@ io_flags_for_size(size_t size)
* Create a new zero-filled IO::Buffer of +size+ bytes.
* By default, the buffer will be _internal_: directly allocated chunk
* of the memory. But if the requested +size+ is more than OS-specific
- * IO::Bufer::PAGE_SIZE, the buffer would be allocated using the
+ * IO::Buffer::PAGE_SIZE, the buffer would be allocated using the
* virtual memory mechanism (anonymous +mmap+ on Unix, +VirtualAlloc+
* on Windows). The behavior can be forced by passing IO::Buffer::MAPPED
* as a second parameter.
@@ -529,35 +656,32 @@ io_flags_for_size(size_t size)
*
* buffer = IO::Buffer.new(4)
* # =>
- * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
- * # 0x00000000 00 00 00 00 ....
+ * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
+ * # 0x00000000 00 00 00 00 ....
*
* buffer.get_string(0, 1) # => "\x00"
*
* buffer.set_string("test")
* buffer
- * # =>
+ * # =>
* # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
* # 0x00000000 74 65 73 74 test
- *
*/
VALUE
rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
{
io_buffer_experimental();
- if (argc < 0 || argc > 2) {
- rb_error_arity(argc, 0, 2);
- }
+ rb_check_arity(argc, 0, 2);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
size_t size;
-
if (argc > 0) {
- size = RB_NUM2SIZE(argv[0]);
- } else {
+ size = io_buffer_extract_size(argv[0]);
+ }
+ else {
size = RUBY_IO_BUFFER_DEFAULT_SIZE;
}
@@ -569,7 +693,7 @@ rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
flags |= io_flags_for_size(size);
}
- io_buffer_initialize(data, NULL, size, flags, Qnil);
+ io_buffer_initialize(buffer, NULL, size, flags, Qnil);
return self;
}
@@ -604,11 +728,11 @@ io_buffer_validate_slice(VALUE source, void *base, size_t size)
}
static int
-io_buffer_validate(struct rb_io_buffer *data)
+io_buffer_validate(struct rb_io_buffer *buffer)
{
- if (data->source != Qnil) {
+ if (buffer->source != Qnil) {
// Only slices incur this overhead, unfortunately... better safe than sorry!
- return io_buffer_validate_slice(data->source, data->base, data->size);
+ return io_buffer_validate_slice(buffer->source, buffer->base, buffer->size);
}
else {
return 1;
@@ -623,48 +747,51 @@ io_buffer_validate(struct rb_io_buffer *data)
*
* puts IO::Buffer.new(4) # uses to_s internally
* # #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
- *
*/
VALUE
rb_io_buffer_to_s(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
VALUE result = rb_str_new_cstr("#<");
rb_str_append(result, rb_class_name(CLASS_OF(self)));
- rb_str_catf(result, " %p+%"PRIdSIZE, data->base, data->size);
+ rb_str_catf(result, " %p+%"PRIdSIZE, buffer->base, buffer->size);
- if (data->base == NULL) {
+ if (buffer->base == NULL) {
rb_str_cat2(result, " NULL");
}
- if (data->flags & RB_IO_BUFFER_EXTERNAL) {
+ if (buffer->flags & RB_IO_BUFFER_EXTERNAL) {
rb_str_cat2(result, " EXTERNAL");
}
- if (data->flags & RB_IO_BUFFER_INTERNAL) {
+ if (buffer->flags & RB_IO_BUFFER_INTERNAL) {
rb_str_cat2(result, " INTERNAL");
}
- if (data->flags & RB_IO_BUFFER_MAPPED) {
+ if (buffer->flags & RB_IO_BUFFER_MAPPED) {
rb_str_cat2(result, " MAPPED");
}
- if (data->flags & RB_IO_BUFFER_LOCKED) {
+ if (buffer->flags & RB_IO_BUFFER_SHARED) {
+ rb_str_cat2(result, " SHARED");
+ }
+
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_str_cat2(result, " LOCKED");
}
- if (data->flags & RB_IO_BUFFER_READONLY) {
+ if (buffer->flags & RB_IO_BUFFER_READONLY) {
rb_str_cat2(result, " READONLY");
}
- if (data->source != Qnil) {
+ if (buffer->source != Qnil) {
rb_str_cat2(result, " SLICE");
}
- if (!io_buffer_validate(data)) {
+ if (!io_buffer_validate(buffer)) {
rb_str_cat2(result, " INVALID");
}
@@ -682,7 +809,8 @@ io_buffer_hexdump(VALUE string, size_t width, char *base, size_t size, int first
if (first) {
rb_str_catf(string, "0x%08" PRIxSIZE " ", offset);
first = 0;
- } else {
+ }
+ else {
rb_str_catf(string, "\n0x%08" PRIxSIZE " ", offset);
}
@@ -713,15 +841,15 @@ io_buffer_hexdump(VALUE string, size_t width, char *base, size_t size, int first
static VALUE
rb_io_buffer_hexdump(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
VALUE result = Qnil;
- if (io_buffer_validate(data) && data->base) {
- result = rb_str_buf_new(data->size*3 + (data->size/16)*12 + 1);
+ if (io_buffer_validate(buffer) && buffer->base) {
+ result = rb_str_buf_new(buffer->size*3 + (buffer->size/16)*12 + 1);
- io_buffer_hexdump(result, 16, data->base, data->size, 1);
+ io_buffer_hexdump(result, 16, buffer->base, buffer->size, 1);
}
return result;
@@ -730,15 +858,15 @@ rb_io_buffer_hexdump(VALUE self)
VALUE
rb_io_buffer_inspect(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
VALUE result = rb_io_buffer_to_s(self);
- if (io_buffer_validate(data)) {
- // Limit the maximum size genearted by inspect.
- if (data->size <= 256) {
- io_buffer_hexdump(result, 16, data->base, data->size, 0);
+ if (io_buffer_validate(buffer)) {
+ // Limit the maximum size generated by inspect.
+ if (buffer->size <= 256) {
+ io_buffer_hexdump(result, 16, buffer->base, buffer->size, 0);
}
}
@@ -750,33 +878,31 @@ rb_io_buffer_inspect(VALUE self)
*
* Returns the size of the buffer that was explicitly set (on creation with ::new
* or on #resize), or deduced on buffer's creation from string or file.
- *
*/
VALUE
rb_io_buffer_size(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return SIZET2NUM(data->size);
+ return SIZET2NUM(buffer->size);
}
/*
* call-seq: valid? -> true or false
*
- * Returns whether the buffer data is accessible.
+ * Returns whether the buffer buffer is accessible.
*
* A buffer becomes invalid if it is a slice of another buffer which has been
* freed.
- *
*/
static VALUE
rb_io_buffer_valid_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(io_buffer_validate(data));
+ return RBOOL(io_buffer_validate(buffer));
}
/*
@@ -784,15 +910,14 @@ rb_io_buffer_valid_p(VALUE self)
*
* If the buffer was freed with #free or was never allocated in the first
* place.
- *
*/
static VALUE
rb_io_buffer_null_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(data->base == NULL);
+ return RBOOL(buffer->base == NULL);
}
/*
@@ -801,15 +926,14 @@ rb_io_buffer_null_p(VALUE self)
* If the buffer has 0 size: it is created by ::new with size 0, or with ::for
* from an empty string. (Note that empty files can't be mapped, so the buffer
* created with ::map will never be empty.)
- *
*/
static VALUE
rb_io_buffer_empty_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(data->size == 0);
+ return RBOOL(buffer->size == 0);
}
/*
@@ -822,15 +946,14 @@ rb_io_buffer_empty_p(VALUE self)
* memory.
*
* External buffer can't be resized.
- *
*/
static VALUE
rb_io_buffer_external_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(data->flags & RB_IO_BUFFER_EXTERNAL);
+ return RBOOL(buffer->flags & RB_IO_BUFFER_EXTERNAL);
}
/*
@@ -848,15 +971,14 @@ rb_io_buffer_external_p(VALUE self)
*
* Internal buffers can be resized, and such an operation will typically
* invalidate all slices, but not always.
- *
*/
static VALUE
rb_io_buffer_internal_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(data->flags & RB_IO_BUFFER_INTERNAL);
+ return RBOOL(buffer->flags & RB_IO_BUFFER_INTERNAL);
}
/*
@@ -871,15 +993,30 @@ rb_io_buffer_internal_p(VALUE self)
*
* Mapped buffers can usually be resized, and such an operation will typically
* invalidate all slices, but not always.
- *
*/
static VALUE
rb_io_buffer_mapped_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(data->flags & RB_IO_BUFFER_MAPPED);
+ return RBOOL(buffer->flags & RB_IO_BUFFER_MAPPED);
+}
+
+/*
+ * call-seq: shared? -> true or false
+ *
+ * If the buffer is _shared_, meaning it references memory that can be shared
+ * with other processes (and thus might change without being modified
+ * locally).
+ */
+static VALUE
+rb_io_buffer_shared_p(VALUE self)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ return RBOOL(buffer->flags & RB_IO_BUFFER_SHARED);
}
/*
@@ -892,27 +1029,28 @@ rb_io_buffer_mapped_p(VALUE self)
* Locking is not thread safe, but is a semantic used to ensure buffers don't
* move while being used by a system call.
*
+ * Example:
+ *
* buffer.locked do
* buffer.write(io) # theoretical system call interface
* end
- *
*/
static VALUE
rb_io_buffer_locked_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(data->flags & RB_IO_BUFFER_LOCKED);
+ return RBOOL(buffer->flags & RB_IO_BUFFER_LOCKED);
}
int
rb_io_buffer_readonly_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return data->flags & RB_IO_BUFFER_READONLY;
+ return buffer->flags & RB_IO_BUFFER_READONLY;
}
/*
@@ -922,7 +1060,6 @@ rb_io_buffer_readonly_p(VALUE self)
* #set_value, #set_string or #copy and similar.
*
* Frozen strings and read-only files create read-only buffers.
- *
*/
static VALUE
io_buffer_readonly_p(VALUE self)
@@ -930,32 +1067,44 @@ io_buffer_readonly_p(VALUE self)
return RBOOL(rb_io_buffer_readonly_p(self));
}
-VALUE
-rb_io_buffer_lock(VALUE self)
+static void
+io_buffer_lock(struct rb_io_buffer *buffer)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
-
- if (data->flags & RB_IO_BUFFER_LOCKED) {
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
}
- data->flags |= RB_IO_BUFFER_LOCKED;
-
- return self;
+ buffer->flags |= RB_IO_BUFFER_LOCKED;
}
VALUE
-rb_io_buffer_unlock(VALUE self)
+rb_io_buffer_lock(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ io_buffer_lock(buffer);
+
+ return self;
+}
- if (!(data->flags & RB_IO_BUFFER_LOCKED)) {
+static void
+io_buffer_unlock(struct rb_io_buffer *buffer)
+{
+ if (!(buffer->flags & RB_IO_BUFFER_LOCKED)) {
rb_raise(rb_eIOBufferLockedError, "Buffer not locked!");
}
- data->flags &= ~RB_IO_BUFFER_LOCKED;
+ buffer->flags &= ~RB_IO_BUFFER_LOCKED;
+}
+
+VALUE
+rb_io_buffer_unlock(VALUE self)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ io_buffer_unlock(buffer);
return self;
}
@@ -963,11 +1112,11 @@ rb_io_buffer_unlock(VALUE self)
int
rb_io_buffer_try_unlock(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- if (data->flags & RB_IO_BUFFER_LOCKED) {
- data->flags &= ~RB_IO_BUFFER_LOCKED;
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
+ buffer->flags &= ~RB_IO_BUFFER_LOCKED;
return 1;
}
@@ -982,6 +1131,14 @@ rb_io_buffer_try_unlock(VALUE self)
* can enter the lock. Also, locked buffer can't be changed with #resize or
* #free.
*
+ * The following operations acquire a lock: #resize, #free.
+ *
+ * Locking is not thread safe. It is designed as a safety net around
+ * non-blocking system calls. You can only share a buffer between threads with
+ * appropriate synchronisation techniques.
+ *
+ * Example:
+ *
* buffer = IO::Buffer.new(4)
* buffer.locked? #=> false
*
@@ -997,28 +1154,22 @@ rb_io_buffer_try_unlock(VALUE self)
* buffer.set_string("test", 0)
* end
* end
- *
- * The following operations acquire a lock: #resize, #free.
- *
- * Locking is not thread safe. It is designed as a safety net around
- * non-blocking system calls. You can only share a buffer between threads with
- * appropriate synchronisation techniques.
*/
VALUE
rb_io_buffer_locked(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- if (data->flags & RB_IO_BUFFER_LOCKED) {
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
}
- data->flags |= RB_IO_BUFFER_LOCKED;
+ buffer->flags |= RB_IO_BUFFER_LOCKED;
VALUE result = rb_yield(self);
- data->flags &= ~RB_IO_BUFFER_LOCKED;
+ buffer->flags &= ~RB_IO_BUFFER_LOCKED;
return result;
}
@@ -1033,47 +1184,73 @@ rb_io_buffer_locked(VALUE self)
*
* After the buffer is freed, no further operations can't be performed on it.
*
- * buffer = IO::Buffer.for('test')
- * buffer.free
- * # => #<IO::Buffer 0x0000000000000000+0 NULL>
+ * You can resize a freed buffer to re-allocate it.
*
- * buffer.get_value(:U8, 0)
- * # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError)
+ * Example:
*
- * buffer.get_string
- * # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError)
+ * buffer = IO::Buffer.for('test')
+ * buffer.free
+ * # => #<IO::Buffer 0x0000000000000000+0 NULL>
*
- * buffer.null?
- * # => true
+ * buffer.get_value(:U8, 0)
+ * # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError)
*
- * You can resize a freed buffer to re-allocate it.
+ * buffer.get_string
+ * # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError)
*
+ * buffer.null?
+ * # => true
*/
VALUE
rb_io_buffer_free(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- if (data->flags & RB_IO_BUFFER_LOCKED) {
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_raise(rb_eIOBufferLockedError, "Buffer is locked!");
}
- io_buffer_free(data);
+ io_buffer_free(buffer);
return self;
}
+// Validate that access to the buffer is within bounds, assuming you want to
+// access length bytes from the specified offset.
static inline void
-io_buffer_validate_range(struct rb_io_buffer *data, size_t offset, size_t length)
+io_buffer_validate_range(struct rb_io_buffer *buffer, size_t offset, size_t length)
{
- if (offset + length > data->size) {
- rb_raise(rb_eArgError, "Specified offset+length exceeds data size!");
+ // We assume here that offset + length won't overflow:
+ if (offset + length > buffer->size) {
+ rb_raise(rb_eArgError, "Specified offset+length is bigger than the buffer size!");
}
}
+static VALUE
+rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_t length)
+{
+ io_buffer_validate_range(buffer, offset, length);
+
+ VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
+ struct rb_io_buffer *slice = NULL;
+ TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, slice);
+
+ slice->flags |= (buffer->flags & RB_IO_BUFFER_READONLY);
+ slice->base = (char*)buffer->base + offset;
+ slice->size = length;
+
+ // The source should be the root buffer:
+ if (buffer->source != Qnil)
+ slice->source = buffer->source;
+ else
+ slice->source = self;
+
+ return instance;
+}
+
/*
- * call-seq: slice(offset, length) -> io_buffer
+ * call-seq: slice([offset, [length]]) -> io_buffer
*
* Produce another IO::Buffer which is a slice (or view into) the current one
* starting at +offset+ bytes and going for +length+ bytes.
@@ -1081,75 +1258,76 @@ io_buffer_validate_range(struct rb_io_buffer *data, size_t offset, size_t length
* The slicing happens without copying of memory, and the slice keeps being
* associated with the original buffer's source (string, or file), if any.
*
- * Raises RuntimeError if the <tt>offset+length<tt> is out of the current
+ * If the offset is not given, it will be zero. If the offset is negative, it
+ * will raise an ArgumentError.
+ *
+ * If the length is not given, the slice will be as long as the original
+ * buffer minus the specified offset. If the length is negative, it will raise
+ * an ArgumentError.
+ *
+ * Raises RuntimeError if the <tt>offset+length</tt> is out of the current
* buffer's bounds.
*
- * string = 'test'
- * buffer = IO::Buffer.for(string)
+ * Example:
*
- * slice = buffer.slice(1, 2)
- * # =>
- * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
- * # 0x00000000 65 73 es
+ * string = 'test'
+ * buffer = IO::Buffer.for(string)
*
- * # Put "o" into 0s position of the slice
- * slice.set_string('o', 0)
- * slice
- * # =>
- * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
- * # 0x00000000 6f 73 os
+ * slice = buffer.slice
+ * # =>
+ * # #<IO::Buffer 0x0000000108338e68+4 SLICE>
+ * # 0x00000000 74 65 73 74 test
*
+ * buffer.slice(2)
+ * # =>
+ * # #<IO::Buffer 0x0000000108338e6a+2 SLICE>
+ * # 0x00000000 73 74 st
*
- * # it is also visible at position 1 of the original buffer
- * buffer
- * # =>
- * # #<IO::Buffer 0x00007fc3d31e2d80+4 SLICE>
- * # 0x00000000 74 6f 73 74 tost
+ * slice = buffer.slice(1, 2)
+ * # =>
+ * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
+ * # 0x00000000 65 73 es
+ *
+ * # Put "o" into 0s position of the slice
+ * slice.set_string('o', 0)
+ * slice
+ * # =>
+ * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
+ * # 0x00000000 6f 73 os
*
- * # ...and original string
- * string
- * # => tost
+ * # it is also visible at position 1 of the original buffer
+ * buffer
+ * # =>
+ * # #<IO::Buffer 0x00007fc3d31e2d80+4 SLICE>
+ * # 0x00000000 74 6f 73 74 tost
*
+ * # ...and original string
+ * string
+ * # => tost
*/
-VALUE
-rb_io_buffer_slice(VALUE self, VALUE _offset, VALUE _length)
+static VALUE
+io_buffer_slice(int argc, VALUE *argv, VALUE self)
{
- // TODO fail on negative offets/lengths.
- size_t offset = NUM2SIZET(_offset);
- size_t length = NUM2SIZET(_length);
-
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ rb_check_arity(argc, 0, 2);
- io_buffer_validate_range(data, offset, length);
-
- VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
- struct rb_io_buffer *slice = NULL;
- TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, slice);
-
- slice->base = (char*)data->base + offset;
- slice->size = length;
-
- // The source should be the root buffer:
- if (data->source != Qnil)
- slice->source = data->source;
- else
- slice->source = self;
+ size_t offset, length;
+ struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
- return instance;
+ return rb_io_buffer_slice(buffer, self, offset, length);
}
-int rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
+int
+rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- if (io_buffer_validate(data)) {
- if (data->base) {
- *base = data->base;
- *size = data->size;
+ if (io_buffer_validate(buffer)) {
+ if (buffer->base) {
+ *base = buffer->base;
+ *size = buffer->size;
- return data->flags;
+ return buffer->flags;
}
}
@@ -1159,20 +1337,21 @@ int rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
return 0;
}
-static void
-io_buffer_get_bytes_for_writing(struct rb_io_buffer *data, void **base, size_t *size)
+static inline void
+io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t *size)
{
- if (data->flags & RB_IO_BUFFER_READONLY) {
+ if (buffer->flags & RB_IO_BUFFER_READONLY ||
+ (!NIL_P(buffer->source) && OBJ_FROZEN(buffer->source))) {
rb_raise(rb_eIOBufferAccessError, "Buffer is not writable!");
}
- if (!io_buffer_validate(data)) {
+ if (!io_buffer_validate(buffer)) {
rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
}
- if (data->base) {
- *base = data->base;
- *size = data->size;
+ if (buffer->base) {
+ *base = buffer->base;
+ *size = buffer->size;
return;
}
@@ -1183,22 +1362,22 @@ io_buffer_get_bytes_for_writing(struct rb_io_buffer *data, void **base, size_t *
void
rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_get_bytes_for_writing(data, base, size);
+ io_buffer_get_bytes_for_writing(buffer, base, size);
}
static void
-io_buffer_get_bytes_for_reading(struct rb_io_buffer *data, const void **base, size_t *size)
+io_buffer_get_bytes_for_reading(struct rb_io_buffer *buffer, const void **base, size_t *size)
{
- if (!io_buffer_validate(data)) {
+ if (!io_buffer_validate(buffer)) {
rb_raise(rb_eIOBufferInvalidatedError, "Buffer has been invalidated!");
}
- if (data->base) {
- *base = data->base;
- *size = data->size;
+ if (buffer->base) {
+ *base = buffer->base;
+ *size = buffer->size;
return;
}
@@ -1209,10 +1388,10 @@ io_buffer_get_bytes_for_reading(struct rb_io_buffer *data, const void **base, si
void
rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_get_bytes_for_reading(data, base, size);
+ io_buffer_get_bytes_for_reading(buffer, base, size);
}
/*
@@ -1220,26 +1399,27 @@ rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size)
*
* Transfers ownership to a new buffer, deallocating the current one.
*
- * buffer = IO::Buffer.new('test')
- * other = buffer.transfer
- * other
- * # =>
- * # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
- * # 0x00000000 74 65 73 74 test
- * buffer
- * # =>
- * # #<IO::Buffer 0x0000000000000000+0 NULL>
- * buffer.null?
- * # => true
+ * Example:
*
+ * buffer = IO::Buffer.new('test')
+ * other = buffer.transfer
+ * other
+ * # =>
+ * # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
+ * # 0x00000000 74 65 73 74 test
+ * buffer
+ * # =>
+ * # #<IO::Buffer 0x0000000000000000+0 NULL>
+ * buffer.null?
+ * # => true
*/
VALUE
rb_io_buffer_transfer(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- if (data->flags & RB_IO_BUFFER_LOCKED) {
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_raise(rb_eIOBufferLockedError, "Cannot transfer ownership of locked buffer!");
}
@@ -1247,91 +1427,96 @@ rb_io_buffer_transfer(VALUE self)
struct rb_io_buffer *transferred;
TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, transferred);
- *transferred = *data;
- io_buffer_zero(data);
+ *transferred = *buffer;
+ io_buffer_zero(buffer);
return instance;
}
static void
-io_buffer_resize_clear(struct rb_io_buffer *data, void* base, size_t size)
+io_buffer_resize_clear(struct rb_io_buffer *buffer, void* base, size_t size)
{
- if (size > data->size) {
- memset((unsigned char*)base+data->size, 0, size - data->size);
+ if (size > buffer->size) {
+ memset((unsigned char*)base+buffer->size, 0, size - buffer->size);
}
}
static void
-io_buffer_resize_copy(struct rb_io_buffer *data, size_t size)
+io_buffer_resize_copy(struct rb_io_buffer *buffer, size_t size)
{
// Slow path:
struct rb_io_buffer resized;
io_buffer_initialize(&resized, NULL, size, io_flags_for_size(size), Qnil);
- if (data->base) {
- size_t preserve = data->size;
+ if (buffer->base) {
+ size_t preserve = buffer->size;
if (preserve > size) preserve = size;
- memcpy(resized.base, data->base, preserve);
+ memcpy(resized.base, buffer->base, preserve);
- io_buffer_resize_clear(data, resized.base, size);
+ io_buffer_resize_clear(buffer, resized.base, size);
}
- io_buffer_free(data);
- *data = resized;
+ io_buffer_free(buffer);
+ *buffer = resized;
}
void
rb_io_buffer_resize(VALUE self, size_t size)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- if (data->flags & RB_IO_BUFFER_LOCKED) {
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_raise(rb_eIOBufferLockedError, "Cannot resize locked buffer!");
}
- if (data->base == NULL) {
- io_buffer_initialize(data, NULL, size, io_flags_for_size(size), Qnil);
+ if (buffer->base == NULL) {
+ io_buffer_initialize(buffer, NULL, size, io_flags_for_size(size), Qnil);
return;
}
- if (data->flags & RB_IO_BUFFER_EXTERNAL) {
+ if (buffer->flags & RB_IO_BUFFER_EXTERNAL) {
rb_raise(rb_eIOBufferAccessError, "Cannot resize external buffer!");
}
#if defined(HAVE_MREMAP) && defined(MREMAP_MAYMOVE)
- if (data->flags & RB_IO_BUFFER_MAPPED) {
- void *base = mremap(data->base, data->size, size, MREMAP_MAYMOVE);
+ if (buffer->flags & RB_IO_BUFFER_MAPPED) {
+ void *base = mremap(buffer->base, buffer->size, size, MREMAP_MAYMOVE);
if (base == MAP_FAILED) {
rb_sys_fail("rb_io_buffer_resize:mremap");
}
- io_buffer_resize_clear(data, base, size);
+ io_buffer_resize_clear(buffer, base, size);
- data->base = base;
- data->size = size;
+ buffer->base = base;
+ buffer->size = size;
return;
}
#endif
- if (data->flags & RB_IO_BUFFER_INTERNAL) {
- void *base = realloc(data->base, size);
+ if (buffer->flags & RB_IO_BUFFER_INTERNAL) {
+ if (size == 0) {
+ io_buffer_free(buffer);
+ return;
+ }
+
+ void *base = realloc(buffer->base, size);
if (!base) {
rb_sys_fail("rb_io_buffer_resize:realloc");
}
- io_buffer_resize_clear(data, base, size);
+ io_buffer_resize_clear(buffer, base, size);
- data->base = base;
- data->size = size;
+ buffer->base = base;
+ buffer->size = size;
return;
}
- io_buffer_resize_copy(data, size);
+ io_buffer_resize_copy(buffer, size);
}
/*
@@ -1345,18 +1530,17 @@ rb_io_buffer_resize(VALUE self, size_t size)
* buffer = IO::Buffer.new(4)
* buffer.set_string("test", 0)
* buffer.resize(8) # resize to 8 bytes
- * # =>
+ * # =>
* # #<IO::Buffer 0x0000555f5d1a1630+8 INTERNAL>
* # 0x00000000 74 65 73 74 00 00 00 00 test....
*
* External buffer (created with ::for), and locked buffer
* can not be resized.
- *
*/
static VALUE
io_buffer_resize(VALUE self, VALUE size)
{
- rb_io_buffer_resize(self, NUM2SIZET(size));
+ rb_io_buffer_resize(self, io_buffer_extract_size(size));
return self;
}
@@ -1366,7 +1550,6 @@ io_buffer_resize(VALUE self, VALUE size)
*
* Buffers are compared by size and exact contents of the memory they are
* referencing using +memcmp+.
- *
*/
static VALUE
rb_io_buffer_compare(VALUE self, VALUE other)
@@ -1392,7 +1575,7 @@ static void
io_buffer_validate_type(size_t size, size_t offset)
{
if (offset > size) {
- rb_raise(rb_eArgError, "Type extends beyond end of buffer!");
+ rb_raise(rb_eArgError, "Type extends beyond end of buffer! (offset=%"PRIdSIZE" > size=%"PRIdSIZE")", offset, size);
}
}
@@ -1442,8 +1625,8 @@ ruby_swapf64(double value)
return swap.value;
}
-#define DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
-static ID RB_IO_BUFFER_TYPE_##name; \
+#define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
+static ID RB_IO_BUFFER_DATA_TYPE_##name; \
\
static VALUE \
io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
@@ -1464,67 +1647,125 @@ io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _val
if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
memcpy((char*)base + *offset, &value, sizeof(type)); \
*offset += sizeof(type); \
-}
-
-DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8)
-DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8)
-
-DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
-DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
-DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
-DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
+} \
+\
+enum { \
+ RB_IO_BUFFER_DATA_TYPE_##name##_SIZE = sizeof(type) \
+};
-DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
-DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
-DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
-DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
+IO_BUFFER_DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8)
+IO_BUFFER_DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8)
+
+IO_BUFFER_DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
+IO_BUFFER_DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
+IO_BUFFER_DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
+IO_BUFFER_DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
+
+IO_BUFFER_DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
+IO_BUFFER_DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
+IO_BUFFER_DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
+IO_BUFFER_DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
+
+IO_BUFFER_DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
+IO_BUFFER_DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
+IO_BUFFER_DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
+IO_BUFFER_DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
+
+IO_BUFFER_DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
+IO_BUFFER_DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
+IO_BUFFER_DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
+IO_BUFFER_DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
+#undef IO_BUFFER_DECLARE_TYPE
+
+static inline size_t
+io_buffer_buffer_type_size(ID buffer_type)
+{
+#define IO_BUFFER_DATA_TYPE_SIZE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) return RB_IO_BUFFER_DATA_TYPE_##name##_SIZE;
+ IO_BUFFER_DATA_TYPE_SIZE(U8)
+ IO_BUFFER_DATA_TYPE_SIZE(S8)
+ IO_BUFFER_DATA_TYPE_SIZE(u16)
+ IO_BUFFER_DATA_TYPE_SIZE(U16)
+ IO_BUFFER_DATA_TYPE_SIZE(s16)
+ IO_BUFFER_DATA_TYPE_SIZE(S16)
+ IO_BUFFER_DATA_TYPE_SIZE(u32)
+ IO_BUFFER_DATA_TYPE_SIZE(U32)
+ IO_BUFFER_DATA_TYPE_SIZE(s32)
+ IO_BUFFER_DATA_TYPE_SIZE(S32)
+ IO_BUFFER_DATA_TYPE_SIZE(u64)
+ IO_BUFFER_DATA_TYPE_SIZE(U64)
+ IO_BUFFER_DATA_TYPE_SIZE(s64)
+ IO_BUFFER_DATA_TYPE_SIZE(S64)
+ IO_BUFFER_DATA_TYPE_SIZE(f32)
+ IO_BUFFER_DATA_TYPE_SIZE(F32)
+ IO_BUFFER_DATA_TYPE_SIZE(f64)
+ IO_BUFFER_DATA_TYPE_SIZE(F64)
+#undef IO_BUFFER_DATA_TYPE_SIZE
-DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
-DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
-DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
-DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
+ rb_raise(rb_eArgError, "Invalid type name!");
+}
-DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
-DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
-DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
-DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
-#undef DECLARE_TYPE
+/*
+ * call-seq:
+ * size_of(buffer_type) -> byte size
+ * size_of(array of buffer_type) -> byte size
+ *
+ * Returns the size of the given buffer type(s) in bytes.
+ *
+ * Example:
+ *
+ * IO::Buffer.size_of(:u32) # => 4
+ * IO::Buffer.size_of([:u32, :u32]) # => 8
+ */
+static VALUE
+io_buffer_size_of(VALUE klass, VALUE buffer_type)
+{
+ if (RB_TYPE_P(buffer_type, T_ARRAY)) {
+ size_t total = 0;
+ for (long i = 0; i < RARRAY_LEN(buffer_type); i++) {
+ total += io_buffer_buffer_type_size(RB_SYM2ID(RARRAY_AREF(buffer_type, i)));
+ }
+ return SIZET2NUM(total);
+ }
+ else {
+ return SIZET2NUM(io_buffer_buffer_type_size(RB_SYM2ID(buffer_type)));
+ }
+}
-VALUE
-rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset)
+static inline VALUE
+rb_io_buffer_get_value(const void* base, size_t size, ID buffer_type, size_t *offset)
{
-#define READ_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) return io_buffer_read_##name(base, size, &offset);
- READ_TYPE(U8)
- READ_TYPE(S8)
+#define IO_BUFFER_GET_VALUE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) return io_buffer_read_##name(base, size, offset);
+ IO_BUFFER_GET_VALUE(U8)
+ IO_BUFFER_GET_VALUE(S8)
- READ_TYPE(u16)
- READ_TYPE(U16)
- READ_TYPE(s16)
- READ_TYPE(S16)
+ IO_BUFFER_GET_VALUE(u16)
+ IO_BUFFER_GET_VALUE(U16)
+ IO_BUFFER_GET_VALUE(s16)
+ IO_BUFFER_GET_VALUE(S16)
- READ_TYPE(u32)
- READ_TYPE(U32)
- READ_TYPE(s32)
- READ_TYPE(S32)
+ IO_BUFFER_GET_VALUE(u32)
+ IO_BUFFER_GET_VALUE(U32)
+ IO_BUFFER_GET_VALUE(s32)
+ IO_BUFFER_GET_VALUE(S32)
- READ_TYPE(u64)
- READ_TYPE(U64)
- READ_TYPE(s64)
- READ_TYPE(S64)
+ IO_BUFFER_GET_VALUE(u64)
+ IO_BUFFER_GET_VALUE(U64)
+ IO_BUFFER_GET_VALUE(s64)
+ IO_BUFFER_GET_VALUE(S64)
- READ_TYPE(f32)
- READ_TYPE(F32)
- READ_TYPE(f64)
- READ_TYPE(F64)
-#undef READ_TYPE
+ IO_BUFFER_GET_VALUE(f32)
+ IO_BUFFER_GET_VALUE(F32)
+ IO_BUFFER_GET_VALUE(f64)
+ IO_BUFFER_GET_VALUE(F64)
+#undef IO_BUFFER_GET_VALUE
rb_raise(rb_eArgError, "Invalid type name!");
}
/*
- * call-seq: get_value(type, offset) -> numeric
+ * call-seq: get_value(buffer_type, offset) -> numeric
*
- * Read from buffer a value of +type+ at +offset+. +type+ should be one
+ * Read from buffer a value of +type+ at +offset+. +buffer_type+ should be one
* of symbols:
*
* * +:U8+: unsigned integer, 1 byte
@@ -1546,53 +1787,253 @@ rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset)
* * +:f64+: double, 8 bytes, little-endian
* * +:F64+: double, 8 bytes, big-endian
*
+ * A buffer type refers specifically to the type of binary buffer that is stored
+ * in the buffer. For example, a +:u32+ buffer type is a 32-bit unsigned
+ * integer in little-endian format.
+ *
* Example:
*
* string = [1.5].pack('f')
* # => "\x00\x00\xC0?"
* IO::Buffer.for(string).get_value(:f32, 0)
* # => 1.5
- *
*/
static VALUE
io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
{
const void *base;
size_t size;
- size_t offset = NUM2SIZET(_offset);
+ size_t offset = io_buffer_extract_offset(_offset);
rb_io_buffer_get_bytes_for_reading(self, &base, &size);
- return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), offset);
+ return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset);
}
-void
-rb_io_buffer_set_value(const void* base, size_t size, ID type, size_t offset, VALUE value)
+/*
+ * call-seq: get_values(buffer_types, offset) -> array
+ *
+ * Similar to #get_value, except that it can handle multiple buffer types and
+ * returns an array of values.
+ *
+ * Example:
+ *
+ * string = [1.5, 2.5].pack('ff')
+ * IO::Buffer.for(string).get_values([:f32, :f32], 0)
+ * # => [1.5, 2.5]
+ */
+static VALUE
+io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset)
+{
+ size_t offset = io_buffer_extract_offset(_offset);
+
+ const void *base;
+ size_t size;
+ rb_io_buffer_get_bytes_for_reading(self, &base, &size);
+
+ if (!RB_TYPE_P(buffer_types, T_ARRAY)) {
+ rb_raise(rb_eArgError, "Argument buffer_types should be an array!");
+ }
+
+ VALUE array = rb_ary_new_capa(RARRAY_LEN(buffer_types));
+
+ for (long i = 0; i < RARRAY_LEN(buffer_types); i++) {
+ VALUE type = rb_ary_entry(buffer_types, i);
+ VALUE value = rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset);
+ rb_ary_push(array, value);
+ }
+
+ return array;
+}
+
+// Extract a count argument, which must be a positive integer.
+// Count is generally considered relative to the number of things.
+static inline size_t
+io_buffer_extract_count(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Count can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+static inline void
+io_buffer_extract_offset_count(ID buffer_type, size_t size, int argc, VALUE *argv, size_t *offset, size_t *count)
+{
+ if (argc >= 1) {
+ *offset = io_buffer_extract_offset(argv[0]);
+ }
+ else {
+ *offset = 0;
+ }
+
+ if (argc >= 2) {
+ *count = io_buffer_extract_count(argv[1]);
+ }
+ else {
+ if (*offset > size) {
+ rb_raise(rb_eArgError, "The given offset is bigger than the buffer size!");
+ }
+
+ *count = (size - *offset) / io_buffer_buffer_type_size(buffer_type);
+ }
+}
+
+/*
+ * call-seq:
+ * each(buffer_type, [offset, [count]]) {|offset, value| ...} -> self
+ * each(buffer_type, [offset, [count]]) -> enumerator
+ *
+ * Iterates over the buffer, yielding each +value+ of +buffer_type+ starting
+ * from +offset+.
+ *
+ * If +count+ is given, only +count+ values will be yielded.
+ *
+ * Example:
+ *
+ * IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value|
+ * puts "#{offset}: #{value}"
+ * end
+ * # 2: 108
+ * # 3: 108
+ */
+static VALUE
+io_buffer_each(int argc, VALUE *argv, VALUE self)
+{
+ RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS);
+
+ const void *base;
+ size_t size;
+
+ rb_io_buffer_get_bytes_for_reading(self, &base, &size);
+
+ ID buffer_type;
+ if (argc >= 1) {
+ buffer_type = RB_SYM2ID(argv[0]);
+ }
+ else {
+ buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
+ }
+
+ size_t offset, count;
+ io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
+
+ for (size_t i = 0; i < count; i++) {
+ size_t current_offset = offset;
+ VALUE value = rb_io_buffer_get_value(base, size, buffer_type, &offset);
+ rb_yield_values(2, SIZET2NUM(current_offset), value);
+ }
+
+ return self;
+}
+
+/*
+ * call-seq: values(buffer_type, [offset, [count]]) -> array
+ *
+ * Returns an array of values of +buffer_type+ starting from +offset+.
+ *
+ * If +count+ is given, only +count+ values will be returned.
+ *
+ * Example:
+ *
+ * IO::Buffer.for("Hello World").values(:U8, 2, 2)
+ * # => [108, 108]
+ */
+static VALUE
+io_buffer_values(int argc, VALUE *argv, VALUE self)
+{
+ const void *base;
+ size_t size;
+
+ rb_io_buffer_get_bytes_for_reading(self, &base, &size);
+
+ ID buffer_type;
+ if (argc >= 1) {
+ buffer_type = RB_SYM2ID(argv[0]);
+ }
+ else {
+ buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
+ }
+
+ size_t offset, count;
+ io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
+
+ VALUE array = rb_ary_new_capa(count);
+
+ for (size_t i = 0; i < count; i++) {
+ VALUE value = rb_io_buffer_get_value(base, size, buffer_type, &offset);
+ rb_ary_push(array, value);
+ }
+
+ return array;
+}
+
+/*
+ * call-seq:
+ * each_byte([offset, [count]]) {|offset, byte| ...} -> self
+ * each_byte([offset, [count]]) -> enumerator
+ *
+ * Iterates over the buffer, yielding each byte starting from +offset+.
+ *
+ * If +count+ is given, only +count+ bytes will be yielded.
+ *
+ * Example:
+ *
+ * IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte|
+ * puts "#{offset}: #{byte}"
+ * end
+ * # 2: 108
+ * # 3: 108
+ */
+static VALUE
+io_buffer_each_byte(int argc, VALUE *argv, VALUE self)
+{
+ RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS);
+
+ const void *base;
+ size_t size;
+
+ rb_io_buffer_get_bytes_for_reading(self, &base, &size);
+
+ size_t offset, count;
+ io_buffer_extract_offset_count(RB_IO_BUFFER_DATA_TYPE_U8, size, argc-1, argv+1, &offset, &count);
+
+ for (size_t i = 0; i < count; i++) {
+ unsigned char *value = (unsigned char *)base + i + offset;
+ rb_yield(RB_INT2FIX(*value));
+ }
+
+ return self;
+}
+
+static inline void
+rb_io_buffer_set_value(const void* base, size_t size, ID buffer_type, size_t *offset, VALUE value)
{
-#define WRITE_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) {io_buffer_write_##name(base, size, &offset, value); return;}
- WRITE_TYPE(U8)
- WRITE_TYPE(S8)
+#define IO_BUFFER_SET_VALUE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(base, size, offset, value); return;}
+ IO_BUFFER_SET_VALUE(U8);
+ IO_BUFFER_SET_VALUE(S8);
- WRITE_TYPE(u16)
- WRITE_TYPE(U16)
- WRITE_TYPE(s16)
- WRITE_TYPE(S16)
+ IO_BUFFER_SET_VALUE(u16);
+ IO_BUFFER_SET_VALUE(U16);
+ IO_BUFFER_SET_VALUE(s16);
+ IO_BUFFER_SET_VALUE(S16);
- WRITE_TYPE(u32)
- WRITE_TYPE(U32)
- WRITE_TYPE(s32)
- WRITE_TYPE(S32)
+ IO_BUFFER_SET_VALUE(u32);
+ IO_BUFFER_SET_VALUE(U32);
+ IO_BUFFER_SET_VALUE(s32);
+ IO_BUFFER_SET_VALUE(S32);
- WRITE_TYPE(u64)
- WRITE_TYPE(U64)
- WRITE_TYPE(s64)
- WRITE_TYPE(S64)
+ IO_BUFFER_SET_VALUE(u64);
+ IO_BUFFER_SET_VALUE(U64);
+ IO_BUFFER_SET_VALUE(s64);
+ IO_BUFFER_SET_VALUE(S64);
- WRITE_TYPE(f32)
- WRITE_TYPE(F32)
- WRITE_TYPE(f64)
- WRITE_TYPE(F64)
-#undef WRITE_TYPE
+ IO_BUFFER_SET_VALUE(f32);
+ IO_BUFFER_SET_VALUE(F32);
+ IO_BUFFER_SET_VALUE(f64);
+ IO_BUFFER_SET_VALUE(F64);
+#undef IO_BUFFER_SET_VALUE
rb_raise(rb_eArgError, "Invalid type name!");
}
@@ -1604,13 +2045,15 @@ rb_io_buffer_set_value(const void* base, size_t size, ID type, size_t offset, VA
* symbols described in #get_value.
*
* buffer = IO::Buffer.new(8)
- * # =>
+ * # =>
* # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
* # 0x00000000 00 00 00 00 00 00 00 00
+ *
* buffer.set_value(:U8, 1, 111)
* # => 1
+ *
* buffer
- * # =>
+ * # =>
* # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
* # 0x00000000 00 6f 00 00 00 00 00 00 .o......
*
@@ -1618,37 +2061,84 @@ rb_io_buffer_set_value(const void* base, size_t size, ID type, size_t offset, VA
*
* buffer = IO::Buffer.new(8)
* buffer.set_value(:U32, 0, 2.5)
+ *
* buffer
- * # =>
- * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
- * # 0x00000000 00 00 00 02 00 00 00 00
- * # ^^ the same as if we'd pass just integer 2
+ * # =>
+ * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
+ * # 0x00000000 00 00 00 02 00 00 00 00
+ * # ^^ the same as if we'd pass just integer 2
*/
static VALUE
io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
{
void *base;
size_t size;
- size_t offset = NUM2SIZET(_offset);
+ size_t offset = io_buffer_extract_offset(_offset);
+
+ rb_io_buffer_get_bytes_for_writing(self, &base, &size);
+
+ rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value);
+
+ return SIZET2NUM(offset);
+}
+
+/*
+ * call-seq: set_values(buffer_types, offset, values) -> offset
+ *
+ * Write +values+ of +buffer_types+ at +offset+ to the buffer. +buffer_types+
+ * should be an array of symbols as described in #get_value. +values+ should
+ * be an array of values to write.
+ *
+ * Example:
+ *
+ * buffer = IO::Buffer.new(8)
+ * buffer.set_values([:U8, :U16], 0, [1, 2])
+ * buffer
+ * # =>
+ * # #<IO::Buffer 0x696f717561746978+8 INTERNAL>
+ * # 0x00000000 01 00 02 00 00 00 00 00 ........
+ */
+static VALUE
+io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values)
+{
+ if (!RB_TYPE_P(buffer_types, T_ARRAY)) {
+ rb_raise(rb_eArgError, "Argument buffer_types should be an array!");
+ }
+
+ if (!RB_TYPE_P(values, T_ARRAY)) {
+ rb_raise(rb_eArgError, "Argument values should be an array!");
+ }
+
+ if (RARRAY_LEN(buffer_types) != RARRAY_LEN(values)) {
+ rb_raise(rb_eArgError, "Argument buffer_types and values should have the same length!");
+ }
+ size_t offset = io_buffer_extract_offset(_offset);
+
+ void *base;
+ size_t size;
rb_io_buffer_get_bytes_for_writing(self, &base, &size);
- rb_io_buffer_set_value(base, size, RB_SYM2ID(type), offset, value);
+ for (long i = 0; i < RARRAY_LEN(buffer_types); i++) {
+ VALUE type = rb_ary_entry(buffer_types, i);
+ VALUE value = rb_ary_entry(values, i);
+ rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value);
+ }
return SIZET2NUM(offset);
}
static void
-io_buffer_memcpy(struct rb_io_buffer *data, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length)
+io_buffer_memcpy(struct rb_io_buffer *buffer, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length)
{
void *base;
size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- io_buffer_validate_range(data, offset, length);
+ io_buffer_validate_range(buffer, offset, length);
if (source_offset + length > source_size) {
- rb_raise(rb_eArgError, "The computed source range exceeds the size of the source!");
+ rb_raise(rb_eArgError, "The computed source range exceeds the size of the source buffer!");
}
memcpy((unsigned char*)base+offset, (unsigned char*)source_base+source_offset, length);
@@ -1656,39 +2146,39 @@ io_buffer_memcpy(struct rb_io_buffer *data, size_t offset, const void *source_ba
// (offset, length, source_offset) -> length
static VALUE
-io_buffer_copy_from(struct rb_io_buffer *data, const void *source_base, size_t source_size, int argc, VALUE *argv)
+io_buffer_copy_from(struct rb_io_buffer *buffer, const void *source_base, size_t source_size, int argc, VALUE *argv)
{
- size_t offset;
+ size_t offset = 0;
size_t length;
size_t source_offset;
// The offset we copy into the buffer:
if (argc >= 1) {
- offset = NUM2SIZET(argv[0]);
- } else {
- offset = 0;
+ offset = io_buffer_extract_offset(argv[0]);
}
// The offset we start from within the string:
if (argc >= 3) {
- source_offset = NUM2SIZET(argv[2]);
+ source_offset = io_buffer_extract_offset(argv[2]);
if (source_offset > source_size) {
rb_raise(rb_eArgError, "The given source offset is bigger than the source itself!");
}
- } else {
+ }
+ else {
source_offset = 0;
}
// The length we are going to copy:
if (argc >= 2 && !RB_NIL_P(argv[1])) {
- length = NUM2SIZET(argv[1]);
- } else {
+ length = io_buffer_extract_length(argv[1]);
+ }
+ else {
// Default to the source offset -> source size:
length = source_size - source_offset;
}
- io_buffer_memcpy(data, offset, source_base, source_offset, source_size, length);
+ io_buffer_memcpy(buffer, offset, source_base, source_offset, source_size, length);
return SIZET2NUM(length);
}
@@ -1709,54 +2199,53 @@ io_buffer_copy_from(struct rb_io_buffer *data, const void *source_base, size_t s
* # =>
* # #<IO::Buffer 0x0000558cbec03320+11 INTERNAL>
* # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
- *
*/
static VALUE
rb_io_buffer_initialize_copy(VALUE self, VALUE source)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
const void *source_base;
size_t source_size;
rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
- io_buffer_initialize(data, NULL, source_size, io_flags_for_size(source_size), Qnil);
+ io_buffer_initialize(buffer, NULL, source_size, io_flags_for_size(source_size), Qnil);
- return io_buffer_copy_from(data, source_base, source_size, 0, NULL);
+ return io_buffer_copy_from(buffer, source_base, source_size, 0, NULL);
}
/*
* call-seq:
* copy(source, [offset, [length, [source_offset]]]) -> size
*
- * Efficiently copy data from a source IO::Buffer into the buffer,
+ * Efficiently copy buffer from a source IO::Buffer into the buffer,
* at +offset+ using +memcpy+. For copying String instances, see #set_string.
*
* buffer = IO::Buffer.new(32)
- * # =>
+ * # =>
* # #<IO::Buffer 0x0000555f5ca22520+32 INTERNAL>
* # 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
*
* buffer.copy(IO::Buffer.for("test"), 8)
- * # => 4 -- size of data copied
+ * # => 4 -- size of buffer copied
* buffer
- * # =>
+ * # =>
* # #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
* # 0x00000000 00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
* # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
*
- * #copy can be used to put data into strings associated with buffer:
+ * #copy can be used to put buffer into strings associated with buffer:
*
- * string= "data: "
- * # => "data: "
+ * string= "buffer: "
+ * # => "buffer: "
* buffer = IO::Buffer.for(string)
* buffer.copy(IO::Buffer.for("test"), 5)
* # => 4
* string
- * # => "data:test"
+ * # => "buffer:test"
*
* Attempt to copy into a read-only buffer will fail:
*
@@ -1774,21 +2263,20 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source)
* File.read('test.txt')
* # => "boom"
*
- * Attempt to copy the data which will need place outside of buffer's
+ * Attempt to copy the buffer which will need place outside of buffer's
* bounds will fail:
*
* buffer = IO::Buffer.new(2)
* buffer.copy(IO::Buffer.for('test'), 0)
- * # in `copy': Specified offset+length exceeds source size! (ArgumentError)
- *
+ * # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
*/
static VALUE
io_buffer_copy(int argc, VALUE *argv, VALUE self)
{
- if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4);
+ rb_check_arity(argc, 1, 4);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
VALUE source = argv[0];
const void *source_base;
@@ -1796,7 +2284,7 @@ io_buffer_copy(int argc, VALUE *argv, VALUE self)
rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
- return io_buffer_copy_from(data, source_base, source_size, argc-1, argv+1);
+ return io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
}
/*
@@ -1805,46 +2293,35 @@ io_buffer_copy(int argc, VALUE *argv, VALUE self)
* Read a chunk or all of the buffer into a string, in the specified
* +encoding+. If no encoding is provided +Encoding::BINARY+ is used.
*
- * buffer = IO::Buffer.for('test')
- * buffer.get_string
- * # => "test"
- * buffer.get_string(2)
- * # => "st"
- * buffer.get_string(2, 1)
- * # => "s"
- *
+ * buffer = IO::Buffer.for('test')
+ * buffer.get_string
+ * # => "test"
+ * buffer.get_string(2)
+ * # => "st"
+ * buffer.get_string(2, 1)
+ * # => "s"
*/
static VALUE
io_buffer_get_string(int argc, VALUE *argv, VALUE self)
{
- if (argc > 3) rb_error_arity(argc, 0, 3);
+ rb_check_arity(argc, 0, 3);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ size_t offset, length;
+ struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
const void *base;
size_t size;
- io_buffer_get_bytes_for_reading(data, &base, &size);
-
- size_t offset = 0;
- size_t length = size;
- rb_encoding *encoding = rb_ascii8bit_encoding();
-
- if (argc >= 1) {
- offset = NUM2SIZET(argv[0]);
- }
-
- if (argc >= 2 && !RB_NIL_P(argv[1])) {
- length = NUM2SIZET(argv[1]);
- } else {
- length = size - offset;
- }
+ io_buffer_get_bytes_for_reading(buffer, &base, &size);
+ rb_encoding *encoding;
if (argc >= 3) {
encoding = rb_find_encoding(argv[2]);
}
+ else {
+ encoding = rb_ascii8bit_encoding();
+ }
- io_buffer_validate_range(data, offset, length);
+ io_buffer_validate_range(buffer, offset, length);
return rb_enc_str_new((const char*)base + offset, length, encoding);
}
@@ -1852,7 +2329,7 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self)
/*
* call-seq: set_string(string, [offset, [length, [source_offset]]]) -> size
*
- * Efficiently copy data from a source String into the buffer,
+ * Efficiently copy buffer from a source String into the buffer,
* at +offset+ using +memcpy+.
*
* buf = IO::Buffer.new(8)
@@ -1860,7 +2337,7 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self)
* # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
* # 0x00000000 00 00 00 00 00 00 00 00 ........
*
- * # set data starting from offset 1, take 2 bytes starting from string's
+ * # set buffer starting from offset 1, take 2 bytes starting from string's
* # second
* buf.set_string('test', 1, 2, 1)
* # => 2
@@ -1875,30 +2352,30 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self)
static VALUE
io_buffer_set_string(int argc, VALUE *argv, VALUE self)
{
- if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4);
+ rb_check_arity(argc, 1, 4);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
VALUE string = rb_str_to_str(argv[0]);
const void *source_base = RSTRING_PTR(string);
size_t source_size = RSTRING_LEN(string);
- return io_buffer_copy_from(data, source_base, source_size, argc-1, argv+1);
+ return io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
}
void
rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
void *base;
size_t size;
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- rb_io_buffer_get_bytes_for_writing(self, &base, &size);
-
- if (offset + length > size) {
- rb_raise(rb_eArgError, "The given offset + length out of bounds!");
- }
+ io_buffer_validate_range(buffer, offset, length);
memset((char*)base + offset, value, length);
}
@@ -1933,40 +2410,28 @@ rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
* # =>
* # <IO::Buffer 0x00007fca40087c38+4 SLICE>
* # 0x00000000 01 02 02 02 ....
- *
*/
static VALUE
io_buffer_clear(int argc, VALUE *argv, VALUE self)
{
- if (argc > 3) rb_error_arity(argc, 0, 3);
-
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ rb_check_arity(argc, 0, 3);
uint8_t value = 0;
if (argc >= 1) {
value = NUM2UINT(argv[0]);
}
- size_t offset = 0;
- if (argc >= 2) {
- offset = NUM2SIZET(argv[1]);
- }
-
- size_t length;
- if (argc >= 3) {
- length = NUM2SIZET(argv[2]);
- } else {
- length = data->size - offset;
- }
+ size_t offset, length;
+ io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);
rb_io_buffer_clear(self, value, offset, length);
return self;
}
-static
-size_t io_buffer_default_size(size_t page_size) {
+static size_t
+io_buffer_default_size(size_t page_size)
+{
// Platform agnostic default size, based on empirical performance observation:
const size_t platform_agnostic_default_size = 64*1024;
@@ -1989,170 +2454,486 @@ size_t io_buffer_default_size(size_t page_size) {
return platform_agnostic_default_size;
}
-VALUE
-rb_io_buffer_read(VALUE self, VALUE io, size_t length)
+struct io_buffer_blocking_region_argument {
+ struct rb_io_buffer *buffer;
+ rb_blocking_function_t *function;
+ void *data;
+ int descriptor;
+};
+
+static VALUE
+io_buffer_blocking_region_begin(VALUE _argument)
{
- VALUE scheduler = rb_fiber_scheduler_current();
- if (scheduler != Qnil) {
- VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, length);
+ struct io_buffer_blocking_region_argument *argument = (void*)_argument;
- if (result != Qundef) {
- return result;
- }
- }
+ return rb_thread_io_blocking_region(argument->function, argument->data, argument->descriptor);
+}
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+static VALUE
+io_buffer_blocking_region_ensure(VALUE _argument)
+{
+ struct io_buffer_blocking_region_argument *argument = (void*)_argument;
- io_buffer_validate_range(data, 0, length);
+ io_buffer_unlock(argument->buffer);
- int descriptor = rb_io_descriptor(io);
+ return Qnil;
+}
- void * base;
- size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+static VALUE
+io_buffer_blocking_region(struct rb_io_buffer *buffer, rb_blocking_function_t *function, void *data, int descriptor)
+{
+ struct io_buffer_blocking_region_argument argument = {
+ .buffer = buffer,
+ .function = function,
+ .data = data,
+ .descriptor = descriptor,
+ };
- ssize_t result = read(descriptor, base, size);
+ // If the buffer is already locked, we can skip the ensure (unlock):
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
+ return io_buffer_blocking_region_begin((VALUE)&argument);
+ }
+ else {
+ // The buffer should be locked for the duration of the blocking region:
+ io_buffer_lock(buffer);
- return rb_fiber_scheduler_io_result(result, errno);
+ return rb_ensure(io_buffer_blocking_region_begin, (VALUE)&argument, io_buffer_blocking_region_ensure, (VALUE)&argument);
+ }
}
+struct io_buffer_read_internal_argument {
+ int descriptor;
+
+ // The base pointer to read from:
+ char *base;
+ // The size of the buffer:
+ size_t size;
+
+ // The minimum number of bytes to read:
+ size_t length;
+};
+
static VALUE
-io_buffer_read(VALUE self, VALUE io, VALUE length)
+io_buffer_read_internal(void *_argument)
{
- return rb_io_buffer_read(self, io, RB_NUM2SIZE(length));
+ size_t total = 0;
+ struct io_buffer_read_internal_argument *argument = _argument;
+
+ while (true) {
+ ssize_t result = read(argument->descriptor, argument->base, argument->size);
+
+ if (result < 0) {
+ return rb_fiber_scheduler_io_result(result, errno);
+ }
+ else if (result == 0) {
+ return rb_fiber_scheduler_io_result(total, 0);
+ }
+ else {
+ total += result;
+
+ if (total >= argument->length) {
+ return rb_fiber_scheduler_io_result(total, 0);
+ }
+
+ argument->base = argument->base + result;
+ argument->size = argument->size - result;
+ }
+ }
}
VALUE
-rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset)
+rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset)
{
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
- VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, self, length, offset);
+ VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, length, offset);
- if (result != Qundef) {
+ if (!UNDEF_P(result)) {
return result;
}
}
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_validate_range(data, 0, length);
+ io_buffer_validate_range(buffer, offset, length);
int descriptor = rb_io_descriptor(io);
void * base;
size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
+
+ base = (unsigned char*)base + offset;
+ size = size - offset;
+
+ struct io_buffer_read_internal_argument argument = {
+ .descriptor = descriptor,
+ .base = base,
+ .size = size,
+ .length = length,
+ };
+
+ return io_buffer_blocking_region(buffer, io_buffer_read_internal, &argument, descriptor);
+}
+
+/*
+ * call-seq: read(io, [length, [offset]]) -> read length or -errno
+ *
+ * Read at least +length+ bytes from the +io+, into the buffer starting at
+ * +offset+. If an error occurs, return <tt>-errno</tt>.
+ *
+ * If +length+ is not given or +nil+, it defaults to the size of the buffer
+ * minus the offset, i.e. the entire buffer.
+ *
+ * If +length+ is zero, exactly one <tt>read</tt> operation will occur.
+ *
+ * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
+ * buffer.
+ *
+ * IO::Buffer.for('test') do |buffer|
+ * p buffer
+ * # =>
+ * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
+ * # 0x00000000 74 65 73 74 test
+ * buffer.read(File.open('/dev/urandom', 'rb'), 2)
+ * p buffer
+ * # =>
+ * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
+ * # 0x00000000 05 35 73 74 .5st
+ * end
+ */
+static VALUE
+io_buffer_read(int argc, VALUE *argv, VALUE self)
+{
+ rb_check_arity(argc, 1, 3);
+
+ VALUE io = argv[0];
+
+ size_t length, offset;
+ io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);
+
+ return rb_io_buffer_read(self, io, length, offset);
+}
+
+struct io_buffer_pread_internal_argument {
+ int descriptor;
+ void *base;
+ size_t size;
+ off_t offset;
+};
+
+static VALUE
+io_buffer_pread_internal(void *_argument)
+{
+ struct io_buffer_pread_internal_argument *argument = _argument;
#if defined(HAVE_PREAD)
- ssize_t result = pread(descriptor, base, size, offset);
+ ssize_t result = pread(argument->descriptor, argument->base, argument->size, argument->offset);
#else
- // This emulation is not thread safe, but the GVL means it's unlikely to be a problem.
- off_t current_offset = lseek(descriptor, 0, SEEK_CUR);
- if (current_offset == (off_t)-1)
+ // This emulation is not thread safe.
+ rb_off_t offset = lseek(argument->descriptor, 0, SEEK_CUR);
+ if (offset == (rb_off_t)-1)
return rb_fiber_scheduler_io_result(-1, errno);
- if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1)
+ if (lseek(argument->descriptor, argument->offset, SEEK_SET) == (rb_off_t)-1)
return rb_fiber_scheduler_io_result(-1, errno);
- ssize_t result = read(descriptor, base, size);
+ ssize_t result = read(argument->descriptor, argument->base, argument->size);
- if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1)
+ if (lseek(argument->descriptor, offset, SEEK_SET) == (rb_off_t)-1)
return rb_fiber_scheduler_io_result(-1, errno);
#endif
return rb_fiber_scheduler_io_result(result, errno);
}
-static VALUE
-io_buffer_pread(VALUE self, VALUE io, VALUE length, VALUE offset)
-{
- return rb_io_buffer_pread(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
-}
-
VALUE
-rb_io_buffer_write(VALUE self, VALUE io, size_t length)
+rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset)
{
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
- VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, length);
+ VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, from, self, length, offset);
- if (result != Qundef) {
+ if (!UNDEF_P(result)) {
return result;
}
}
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_validate_range(data, 0, length);
+ io_buffer_validate_range(buffer, offset, length);
int descriptor = rb_io_descriptor(io);
- const void * base;
+ void * base;
size_t size;
- io_buffer_get_bytes_for_reading(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- ssize_t result = write(descriptor, base, length);
+ struct io_buffer_pread_internal_argument argument = {
+ .descriptor = descriptor,
- return rb_fiber_scheduler_io_result(result, errno);
+ // Move the base pointer to the offset:
+ .base = (unsigned char*)base + offset,
+
+ // And the size to the length of buffer we want to read:
+ .size = length,
+
+ // From the offset in the file we want to read from:
+ .offset = from,
+ };
+
+ return io_buffer_blocking_region(buffer, io_buffer_pread_internal, &argument, descriptor);
}
+/*
+ * call-seq: pread(io, from, length, [offset]) -> read length or -errno
+ *
+ * Read at most +length+ bytes from +io+ into the buffer, starting at
+ * +from+, and put it in buffer starting from specified +offset+.
+ * If an error occurs, return <tt>-errno</tt>.
+ *
+ * If +offset+ is not given, put it at the beginning of the buffer.
+ *
+ * Example:
+ *
+ * IO::Buffer.for('test') do |buffer|
+ * p buffer
+ * # =>
+ * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
+ * # 0x00000000 74 65 73 74 test
+ *
+ * # take 2 bytes from the beginning of urandom,
+ * # put them in buffer starting from position 2
+ * buffer.pread(File.open('/dev/urandom', 'rb'), 0, 2, 2)
+ * p buffer
+ * # =>
+ * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
+ * # 0x00000000 05 35 73 74 te.5
+ * end
+ */
static VALUE
-io_buffer_write(VALUE self, VALUE io, VALUE length)
+io_buffer_pread(int argc, VALUE *argv, VALUE self)
{
- return rb_io_buffer_write(self, io, RB_NUM2SIZE(length));
+ rb_check_arity(argc, 2, 4);
+
+ VALUE io = argv[0];
+ rb_off_t from = NUM2OFFT(argv[1]);
+
+ size_t length, offset;
+ io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);
+
+ return rb_io_buffer_pread(self, io, from, length, offset);
+}
+
+struct io_buffer_write_internal_argument {
+ int descriptor;
+
+ // The base pointer to write from:
+ const char *base;
+ // The size of the buffer:
+ size_t size;
+
+ // The minimum length to write:
+ size_t length;
+};
+
+static VALUE
+io_buffer_write_internal(void *_argument)
+{
+ size_t total = 0;
+ struct io_buffer_write_internal_argument *argument = _argument;
+
+ while (true) {
+ ssize_t result = write(argument->descriptor, argument->base, argument->size);
+
+ if (result < 0) {
+ return rb_fiber_scheduler_io_result(result, errno);
+ }
+ else if (result == 0) {
+ return rb_fiber_scheduler_io_result(total, 0);
+ }
+ else {
+ total += result;
+
+ if (total >= argument->length) {
+ return rb_fiber_scheduler_io_result(total, 0);
+ }
+
+ argument->base = argument->base + result;
+ argument->size = argument->size - result;
+ }
+ }
}
VALUE
-rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset)
+rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset)
{
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
- VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, self, length, OFFT2NUM(offset));
+ VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, length, offset);
- if (result != Qundef) {
+ if (!UNDEF_P(result)) {
return result;
}
}
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_validate_range(data, 0, length);
+ io_buffer_validate_range(buffer, offset, length);
int descriptor = rb_io_descriptor(io);
const void * base;
size_t size;
- io_buffer_get_bytes_for_reading(data, &base, &size);
+ io_buffer_get_bytes_for_reading(buffer, &base, &size);
+
+ base = (unsigned char*)base + offset;
+ size = size - offset;
+
+ struct io_buffer_write_internal_argument argument = {
+ .descriptor = descriptor,
+ .base = base,
+ .size = size,
+ .length = length,
+ };
+
+ return io_buffer_blocking_region(buffer, io_buffer_write_internal, &argument, descriptor);
+}
+
+/*
+ * call-seq: write(io, [length, [offset]]) -> written length or -errno
+ *
+ * Write at least +length+ bytes from the buffer starting at +offset+, into the +io+.
+ * If an error occurs, return <tt>-errno</tt>.
+ *
+ * If +length+ is not given or +nil+, it defaults to the size of the buffer
+ * minus the offset, i.e. the entire buffer.
+ *
+ * If +length+ is zero, exactly one <tt>write</tt> operation will occur.
+ *
+ * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
+ * buffer.
+ *
+ * out = File.open('output.txt', 'wb')
+ * IO::Buffer.for('1234567').write(out, 3)
+ *
+ * This leads to +123+ being written into <tt>output.txt</tt>
+ */
+static VALUE
+io_buffer_write(int argc, VALUE *argv, VALUE self)
+{
+ rb_check_arity(argc, 1, 3);
+
+ VALUE io = argv[0];
+
+ size_t length, offset;
+ io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);
+
+ return rb_io_buffer_write(self, io, length, offset);
+}
+
+struct io_buffer_pwrite_internal_argument {
+ int descriptor;
+ const void *base;
+ size_t size;
+ off_t offset;
+};
+
+static VALUE
+io_buffer_pwrite_internal(void *_argument)
+{
+ struct io_buffer_pwrite_internal_argument *argument = _argument;
#if defined(HAVE_PWRITE)
- ssize_t result = pwrite(descriptor, base, length, offset);
+ ssize_t result = pwrite(argument->descriptor, argument->base, argument->size, argument->offset);
#else
- // This emulation is not thread safe, but the GVL means it's unlikely to be a problem.
- off_t current_offset = lseek(descriptor, 0, SEEK_CUR);
- if (current_offset == (off_t)-1)
+ // This emulation is not thread safe.
+ rb_off_t offset = lseek(argument->descriptor, 0, SEEK_CUR);
+ if (offset == (rb_off_t)-1)
return rb_fiber_scheduler_io_result(-1, errno);
- if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1)
+ if (lseek(argument->descriptor, argument->offset, SEEK_SET) == (rb_off_t)-1)
return rb_fiber_scheduler_io_result(-1, errno);
- ssize_t result = write(descriptor, base, length);
+ ssize_t result = write(argument->descriptor, argument->base, argument->size);
- if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1)
+ if (lseek(argument->descriptor, offset, SEEK_SET) == (rb_off_t)-1)
return rb_fiber_scheduler_io_result(-1, errno);
#endif
return rb_fiber_scheduler_io_result(result, errno);
}
+VALUE
+rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset)
+{
+ VALUE scheduler = rb_fiber_scheduler_current();
+ if (scheduler != Qnil) {
+ VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, from, self, length, offset);
+
+ if (!UNDEF_P(result)) {
+ return result;
+ }
+ }
+
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ io_buffer_validate_range(buffer, offset, length);
+
+ int descriptor = rb_io_descriptor(io);
+
+ const void * base;
+ size_t size;
+ io_buffer_get_bytes_for_reading(buffer, &base, &size);
+
+ struct io_buffer_pwrite_internal_argument argument = {
+ .descriptor = descriptor,
+
+ // Move the base pointer to the offset:
+ .base = (unsigned char *)base + offset,
+
+ // And the size to the length of buffer we want to read:
+ .size = length,
+
+ // And the offset in the file we want to write from:
+ .offset = from,
+ };
+
+ return io_buffer_blocking_region(buffer, io_buffer_pwrite_internal, &argument, descriptor);
+}
+
+/*
+ * call-seq: pwrite(io, from, length, [offset]) -> written length or -errno
+ *
+ * Writes +length+ bytes from buffer into +io+, starting at
+ * +offset+ in the buffer. If an error occurs, return <tt>-errno</tt>.
+ *
+ * If +offset+ is not given, the bytes are taken from the beginning of the
+ * buffer. If the +offset+ is given and is beyond the end of the file, the
+ * gap will be filled with null (0 value) bytes.
+ *
+ * out = File.open('output.txt', File::RDWR) # open for read/write, no truncation
+ * IO::Buffer.for('1234567').pwrite(out, 2, 3, 1)
+ *
+ * This leads to +234+ (3 bytes, starting from position 1) being written into
+ * <tt>output.txt</tt>, starting from file position 2.
+ */
static VALUE
-io_buffer_pwrite(VALUE self, VALUE io, VALUE length, VALUE offset)
+io_buffer_pwrite(int argc, VALUE *argv, VALUE self)
{
- return rb_io_buffer_pwrite(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
+ rb_check_arity(argc, 2, 4);
+
+ VALUE io = argv[0];
+ rb_off_t from = NUM2OFFT(argv[1]);
+
+ size_t length, offset;
+ io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);
+
+ return rb_io_buffer_pwrite(self, io, from, length, offset);
}
static inline void
@@ -2166,7 +2947,7 @@ static void
memory_and(unsigned char * restrict output, unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
{
for (size_t offset = 0; offset < size; offset += 1) {
- output[offset] = base[offset] & mask[offset % mask_size];
+ output[offset] = base[offset] & mask[offset % mask_size];
}
}
@@ -2185,19 +2966,19 @@ memory_and(unsigned char * restrict output, unsigned char * restrict base, size_
static VALUE
io_buffer_and(VALUE self, VALUE mask)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- struct rb_io_buffer *mask_data = NULL;
- TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
+ struct rb_io_buffer *mask_buffer = NULL;
+ TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_data);
+ io_buffer_check_mask(mask_buffer);
- VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
- struct rb_io_buffer *output_data = NULL;
- TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);
+ VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
+ struct rb_io_buffer *output_buffer = NULL;
+ TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
- memory_and(output_data->base, data->base, data->size, mask_data->base, mask_data->size);
+ memory_and(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);
return output;
}
@@ -2206,7 +2987,7 @@ static void
memory_or(unsigned char * restrict output, unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
{
for (size_t offset = 0; offset < size; offset += 1) {
- output[offset] = base[offset] | mask[offset % mask_size];
+ output[offset] = base[offset] | mask[offset % mask_size];
}
}
@@ -2225,19 +3006,19 @@ memory_or(unsigned char * restrict output, unsigned char * restrict base, size_t
static VALUE
io_buffer_or(VALUE self, VALUE mask)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- struct rb_io_buffer *mask_data = NULL;
- TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
+ struct rb_io_buffer *mask_buffer = NULL;
+ TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_data);
+ io_buffer_check_mask(mask_buffer);
- VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
- struct rb_io_buffer *output_data = NULL;
- TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);
+ VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
+ struct rb_io_buffer *output_buffer = NULL;
+ TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
- memory_or(output_data->base, data->base, data->size, mask_data->base, mask_data->size);
+ memory_or(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);
return output;
}
@@ -2246,7 +3027,7 @@ static void
memory_xor(unsigned char * restrict output, unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
{
for (size_t offset = 0; offset < size; offset += 1) {
- output[offset] = base[offset] ^ mask[offset % mask_size];
+ output[offset] = base[offset] ^ mask[offset % mask_size];
}
}
@@ -2265,19 +3046,19 @@ memory_xor(unsigned char * restrict output, unsigned char * restrict base, size_
static VALUE
io_buffer_xor(VALUE self, VALUE mask)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- struct rb_io_buffer *mask_data = NULL;
- TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
+ struct rb_io_buffer *mask_buffer = NULL;
+ TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_data);
+ io_buffer_check_mask(mask_buffer);
- VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
- struct rb_io_buffer *output_data = NULL;
- TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);
+ VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
+ struct rb_io_buffer *output_buffer = NULL;
+ TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
- memory_xor(output_data->base, data->base, data->size, mask_data->base, mask_data->size);
+ memory_xor(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);
return output;
}
@@ -2286,7 +3067,7 @@ static void
memory_not(unsigned char * restrict output, unsigned char * restrict base, size_t size)
{
for (size_t offset = 0; offset < size; offset += 1) {
- output[offset] = ~base[offset];
+ output[offset] = ~base[offset];
}
}
@@ -2305,14 +3086,14 @@ memory_not(unsigned char * restrict output, unsigned char * restrict base, size_
static VALUE
io_buffer_not(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
- struct rb_io_buffer *output_data = NULL;
- TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);
+ VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
+ struct rb_io_buffer *output_buffer = NULL;
+ TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
- memory_not(output_data->base, data->base, data->size);
+ memory_not(output_buffer->base, buffer->base, buffer->size);
return output;
}
@@ -2321,7 +3102,7 @@ static inline int
io_buffer_overlaps(const struct rb_io_buffer *a, const struct rb_io_buffer *b)
{
if (a->base > b->base) {
- return io_buffer_overlaps(b, a);
+ return io_buffer_overlaps(b, a);
}
return (b->base >= a->base) && (b->base <= (void*)((unsigned char *)a->base + a->size));
@@ -2331,14 +3112,14 @@ static inline void
io_buffer_check_overlaps(struct rb_io_buffer *a, struct rb_io_buffer *b)
{
if (io_buffer_overlaps(a, b))
- rb_raise(rb_eIOBufferMaskError, "Mask overlaps source data!");
+ rb_raise(rb_eIOBufferMaskError, "Mask overlaps source buffer!");
}
static void
memory_and_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
{
for (size_t offset = 0; offset < size; offset += 1) {
- base[offset] &= mask[offset % mask_size];
+ base[offset] &= mask[offset % mask_size];
}
}
@@ -2362,20 +3143,20 @@ memory_and_inplace(unsigned char * restrict base, size_t size, unsigned char * r
static VALUE
io_buffer_and_inplace(VALUE self, VALUE mask)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- struct rb_io_buffer *mask_data = NULL;
- TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
+ struct rb_io_buffer *mask_buffer = NULL;
+ TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_data);
- io_buffer_check_overlaps(data, mask_data);
+ io_buffer_check_mask(mask_buffer);
+ io_buffer_check_overlaps(buffer, mask_buffer);
void *base;
size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- memory_and_inplace(base, size, mask_data->base, mask_data->size);
+ memory_and_inplace(base, size, mask_buffer->base, mask_buffer->size);
return self;
}
@@ -2384,7 +3165,7 @@ static void
memory_or_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
{
for (size_t offset = 0; offset < size; offset += 1) {
- base[offset] |= mask[offset % mask_size];
+ base[offset] |= mask[offset % mask_size];
}
}
@@ -2408,20 +3189,20 @@ memory_or_inplace(unsigned char * restrict base, size_t size, unsigned char * re
static VALUE
io_buffer_or_inplace(VALUE self, VALUE mask)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- struct rb_io_buffer *mask_data = NULL;
- TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
+ struct rb_io_buffer *mask_buffer = NULL;
+ TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_data);
- io_buffer_check_overlaps(data, mask_data);
+ io_buffer_check_mask(mask_buffer);
+ io_buffer_check_overlaps(buffer, mask_buffer);
void *base;
size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- memory_or_inplace(base, size, mask_data->base, mask_data->size);
+ memory_or_inplace(base, size, mask_buffer->base, mask_buffer->size);
return self;
}
@@ -2430,7 +3211,7 @@ static void
memory_xor_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
{
for (size_t offset = 0; offset < size; offset += 1) {
- base[offset] ^= mask[offset % mask_size];
+ base[offset] ^= mask[offset % mask_size];
}
}
@@ -2454,20 +3235,20 @@ memory_xor_inplace(unsigned char * restrict base, size_t size, unsigned char * r
static VALUE
io_buffer_xor_inplace(VALUE self, VALUE mask)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- struct rb_io_buffer *mask_data = NULL;
- TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
+ struct rb_io_buffer *mask_buffer = NULL;
+ TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_data);
- io_buffer_check_overlaps(data, mask_data);
+ io_buffer_check_mask(mask_buffer);
+ io_buffer_check_overlaps(buffer, mask_buffer);
void *base;
size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- memory_xor_inplace(base, size, mask_data->base, mask_data->size);
+ memory_xor_inplace(base, size, mask_buffer->base, mask_buffer->size);
return self;
}
@@ -2476,7 +3257,7 @@ static void
memory_not_inplace(unsigned char * restrict base, size_t size)
{
for (size_t offset = 0; offset < size; offset += 1) {
- base[offset] = ~base[offset];
+ base[offset] = ~base[offset];
}
}
@@ -2500,12 +3281,12 @@ memory_not_inplace(unsigned char * restrict base, size_t size)
static VALUE
io_buffer_not_inplace(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
void *base;
size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
memory_not_inplace(base, size);
@@ -2518,8 +3299,8 @@ io_buffer_not_inplace(VALUE self)
* IO::Buffer is a low-level efficient buffer for input/output. There are three
* ways of using buffer:
*
- * * Create an empty buffer with ::new, fill it with data using #copy or
- * #set_value, #set_string, get data with #get_string;
+ * * Create an empty buffer with ::new, fill it with buffer using #copy or
+ * #set_value, #set_string, get buffer with #get_string;
* * Create a buffer mapped to some string with ::for, then it could be used
* both for reading with #get_string or #get_value, and writing (writing will
* change the source string, too);
@@ -2537,11 +3318,11 @@ io_buffer_not_inplace(VALUE self)
* Empty buffer:
*
* buffer = IO::Buffer.new(8) # create empty 8-byte buffer
- * # =>
+ * # =>
* # #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
* # ...
* buffer
- * # =>
+ * # =>
* # <IO::Buffer 0x0000555f5d156ab0+8 INTERNAL>
* # 0x00000000 00 00 00 00 00 00 00 00
* buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2
@@ -2551,22 +3332,22 @@ io_buffer_not_inplace(VALUE self)
*
* \Buffer from string:
*
- * string = 'data'
+ * string = 'buffer'
* buffer = IO::Buffer.for(string)
- * # =>
+ * # =>
* # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
* # ...
* buffer
- * # =>
+ * # =>
* # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
- * # 0x00000000 64 61 74 61 data
+ * # 0x00000000 64 61 74 61 buffer
*
* buffer.get_string(2) # read content starting from offset 2
* # => "ta"
* buffer.set_string('---', 1) # write content, starting from offset 1
* # => 3
* buffer
- * # =>
+ * # =>
* # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
* # 0x00000000 64 2d 2d 2d d---
* string # original string changed, too
@@ -2574,10 +3355,10 @@ io_buffer_not_inplace(VALUE self)
*
* \Buffer from file:
*
- * File.write('test.txt', 'test data')
+ * File.write('test.txt', 'test buffer')
* # => 9
* buffer = IO::Buffer.map(File.open('test.txt'))
- * # =>
+ * # =>
* # #<IO::Buffer 0x00007f3f0768c000+9 MAPPED IMMUTABLE>
* # ...
* buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
@@ -2591,7 +3372,7 @@ io_buffer_not_inplace(VALUE self)
* buffer.set_string('---', 1)
* # => 3 -- bytes written
* File.read('test.txt')
- * # => "t--- data"
+ * # => "t--- buffer"
*
* <b>The class is experimental and the interface is subject to change.</b>
*/
@@ -2640,6 +3421,7 @@ Init_IO_Buffer(void)
rb_define_const(rb_cIOBuffer, "EXTERNAL", RB_INT2NUM(RB_IO_BUFFER_EXTERNAL));
rb_define_const(rb_cIOBuffer, "INTERNAL", RB_INT2NUM(RB_IO_BUFFER_INTERNAL));
rb_define_const(rb_cIOBuffer, "MAPPED", RB_INT2NUM(RB_IO_BUFFER_MAPPED));
+ rb_define_const(rb_cIOBuffer, "SHARED", RB_INT2NUM(RB_IO_BUFFER_SHARED));
rb_define_const(rb_cIOBuffer, "LOCKED", RB_INT2NUM(RB_IO_BUFFER_LOCKED));
rb_define_const(rb_cIOBuffer, "PRIVATE", RB_INT2NUM(RB_IO_BUFFER_PRIVATE));
rb_define_const(rb_cIOBuffer, "READONLY", RB_INT2NUM(RB_IO_BUFFER_READONLY));
@@ -2655,6 +3437,7 @@ Init_IO_Buffer(void)
rb_define_method(rb_cIOBuffer, "external?", rb_io_buffer_external_p, 0);
rb_define_method(rb_cIOBuffer, "internal?", rb_io_buffer_internal_p, 0);
rb_define_method(rb_cIOBuffer, "mapped?", rb_io_buffer_mapped_p, 0);
+ rb_define_method(rb_cIOBuffer, "shared?", rb_io_buffer_shared_p, 0);
rb_define_method(rb_cIOBuffer, "locked?", rb_io_buffer_locked_p, 0);
rb_define_method(rb_cIOBuffer, "readonly?", io_buffer_readonly_p, 0);
@@ -2664,7 +3447,7 @@ Init_IO_Buffer(void)
rb_define_method(rb_cIOBuffer, "locked", rb_io_buffer_locked, 0);
// Manipulation:
- rb_define_method(rb_cIOBuffer, "slice", rb_io_buffer_slice, 2);
+ rb_define_method(rb_cIOBuffer, "slice", io_buffer_slice, -1);
rb_define_method(rb_cIOBuffer, "<=>", rb_io_buffer_compare, 1);
rb_define_method(rb_cIOBuffer, "resize", io_buffer_resize, 1);
rb_define_method(rb_cIOBuffer, "clear", io_buffer_clear, -1);
@@ -2672,24 +3455,48 @@ Init_IO_Buffer(void)
rb_include_module(rb_cIOBuffer, rb_mComparable);
-#define DEFINE_TYPE(name) RB_IO_BUFFER_TYPE_##name = rb_intern_const(#name)
- DEFINE_TYPE(U8); DEFINE_TYPE(S8);
- DEFINE_TYPE(u16); DEFINE_TYPE(U16); DEFINE_TYPE(s16); DEFINE_TYPE(S16);
- DEFINE_TYPE(u32); DEFINE_TYPE(U32); DEFINE_TYPE(s32); DEFINE_TYPE(S32);
- DEFINE_TYPE(u64); DEFINE_TYPE(U64); DEFINE_TYPE(s64); DEFINE_TYPE(S64);
- DEFINE_TYPE(f32); DEFINE_TYPE(F32); DEFINE_TYPE(f64); DEFINE_TYPE(F64);
-#undef DEFINE_TYPE
+#define IO_BUFFER_DEFINE_DATA_TYPE(name) RB_IO_BUFFER_DATA_TYPE_##name = rb_intern_const(#name)
+ IO_BUFFER_DEFINE_DATA_TYPE(U8);
+ IO_BUFFER_DEFINE_DATA_TYPE(S8);
+
+ IO_BUFFER_DEFINE_DATA_TYPE(u16);
+ IO_BUFFER_DEFINE_DATA_TYPE(U16);
+ IO_BUFFER_DEFINE_DATA_TYPE(s16);
+ IO_BUFFER_DEFINE_DATA_TYPE(S16);
+
+ IO_BUFFER_DEFINE_DATA_TYPE(u32);
+ IO_BUFFER_DEFINE_DATA_TYPE(U32);
+ IO_BUFFER_DEFINE_DATA_TYPE(s32);
+ IO_BUFFER_DEFINE_DATA_TYPE(S32);
+
+ IO_BUFFER_DEFINE_DATA_TYPE(u64);
+ IO_BUFFER_DEFINE_DATA_TYPE(U64);
+ IO_BUFFER_DEFINE_DATA_TYPE(s64);
+ IO_BUFFER_DEFINE_DATA_TYPE(S64);
+
+ IO_BUFFER_DEFINE_DATA_TYPE(f32);
+ IO_BUFFER_DEFINE_DATA_TYPE(F32);
+ IO_BUFFER_DEFINE_DATA_TYPE(f64);
+ IO_BUFFER_DEFINE_DATA_TYPE(F64);
+#undef IO_BUFFER_DEFINE_DATA_TYPE
+
+ rb_define_singleton_method(rb_cIOBuffer, "size_of", io_buffer_size_of, 1);
// Data access:
rb_define_method(rb_cIOBuffer, "get_value", io_buffer_get_value, 2);
+ rb_define_method(rb_cIOBuffer, "get_values", io_buffer_get_values, 2);
+ rb_define_method(rb_cIOBuffer, "each", io_buffer_each, -1);
+ rb_define_method(rb_cIOBuffer, "values", io_buffer_values, -1);
+ rb_define_method(rb_cIOBuffer, "each_byte", io_buffer_each_byte, -1);
rb_define_method(rb_cIOBuffer, "set_value", io_buffer_set_value, 3);
+ rb_define_method(rb_cIOBuffer, "set_values", io_buffer_set_values, 3);
rb_define_method(rb_cIOBuffer, "copy", io_buffer_copy, -1);
rb_define_method(rb_cIOBuffer, "get_string", io_buffer_get_string, -1);
rb_define_method(rb_cIOBuffer, "set_string", io_buffer_set_string, -1);
- // Binary data manipulations:
+ // Binary buffer manipulations:
rb_define_method(rb_cIOBuffer, "&", io_buffer_and, 1);
rb_define_method(rb_cIOBuffer, "|", io_buffer_or, 1);
rb_define_method(rb_cIOBuffer, "^", io_buffer_xor, 1);
@@ -2701,8 +3508,8 @@ Init_IO_Buffer(void)
rb_define_method(rb_cIOBuffer, "not!", io_buffer_not_inplace, 0);
// IO operations:
- rb_define_method(rb_cIOBuffer, "read", io_buffer_read, 2);
- rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, 3);
- rb_define_method(rb_cIOBuffer, "write", io_buffer_write, 2);
- rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, 3);
+ rb_define_method(rb_cIOBuffer, "read", io_buffer_read, -1);
+ rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, -1);
+ rb_define_method(rb_cIOBuffer, "write", io_buffer_write, -1);
+ rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, -1);
}
diff --git a/iseq.c b/iseq.c
index 0369830bdd..d17ce486c5 100644
--- a/iseq.c
+++ b/iseq.c
@@ -60,19 +60,19 @@ static inline VALUE
obj_resurrect(VALUE obj)
{
if (hidden_obj_p(obj)) {
- switch (BUILTIN_TYPE(obj)) {
- case T_STRING:
- obj = rb_str_resurrect(obj);
- break;
- case T_ARRAY:
- obj = rb_ary_resurrect(obj);
- break;
+ switch (BUILTIN_TYPE(obj)) {
+ case T_STRING:
+ obj = rb_str_resurrect(obj);
+ break;
+ case T_ARRAY:
+ obj = rb_ary_resurrect(obj);
+ break;
case T_HASH:
obj = rb_hash_resurrect(obj);
break;
default:
- break;
- }
+ break;
+ }
}
return obj;
}
@@ -93,77 +93,69 @@ static void
compile_data_free(struct iseq_compile_data *compile_data)
{
if (compile_data) {
- free_arena(compile_data->node.storage_head);
- free_arena(compile_data->insn.storage_head);
- if (compile_data->ivar_cache_table) {
- rb_id_table_free(compile_data->ivar_cache_table);
- }
- ruby_xfree(compile_data);
+ free_arena(compile_data->node.storage_head);
+ free_arena(compile_data->insn.storage_head);
+ if (compile_data->ivar_cache_table) {
+ rb_id_table_free(compile_data->ivar_cache_table);
+ }
+ ruby_xfree(compile_data);
}
}
-struct iseq_clear_ic_references_data {
- IC ic;
-};
-
-// This iterator is used to walk through the instructions and clean any
-// references to ICs that are contained within this ISEQ out of the VM's
-// constant cache table. It passes around a struct that holds the current IC
-// we're looking for, which can be NULL (if we haven't hit an opt_getinlinecache
-// instruction yet) or set to an IC (if we've hit an opt_getinlinecache and
-// haven't yet hit the associated opt_setinlinecache).
-static bool
-iseq_clear_ic_references_i(VALUE *code, VALUE insn, size_t index, void *data)
+static void
+remove_from_constant_cache(ID id, IC ic)
{
- struct iseq_clear_ic_references_data *ic_data = (struct iseq_clear_ic_references_data *) data;
+ rb_vm_t *vm = GET_VM();
+ VALUE lookup_result;
+ st_data_t ic_data = (st_data_t)ic;
- switch (insn) {
- case BIN(opt_getinlinecache): {
- RUBY_ASSERT_ALWAYS(ic_data->ic == NULL);
+ if (rb_id_table_lookup(vm->constant_cache, id, &lookup_result)) {
+ st_table *ics = (st_table *)lookup_result;
+ st_delete(ics, &ic_data, NULL);
- ic_data->ic = (IC) code[index + 2];
- return true;
- }
- case BIN(getconstant): {
- if (ic_data->ic != NULL) {
- ID id = (ID) code[index + 1];
- rb_vm_t *vm = GET_VM();
- VALUE lookup_result;
-
- if (rb_id_table_lookup(vm->constant_cache, id, &lookup_result)) {
- st_table *ics = (st_table *)lookup_result;
- st_data_t ic = (st_data_t)ic_data->ic;
- st_delete(ics, &ic, NULL);
-
- if (ics->num_entries == 0) {
- rb_id_table_delete(vm->constant_cache, id);
- st_free_table(ics);
- }
- }
+ if (ics->num_entries == 0 &&
+ // See comment in vm_track_constant_cache on why we need this check
+ id != vm->inserting_constant_cache_id) {
+ rb_id_table_delete(vm->constant_cache, id);
+ st_free_table(ics);
}
-
- return true;
- }
- case BIN(opt_setinlinecache): {
- RUBY_ASSERT_ALWAYS(ic_data->ic != NULL);
-
- ic_data->ic = NULL;
- return true;
- }
- default:
- return true;
}
}
// When an ISEQ is being freed, all of its associated ICs are going to go away
-// as well. Because of this, we need to walk through the ISEQ, find any
-// opt_getinlinecache calls, and clear out the VM's constant cache of associated
-// ICs.
+// as well. Because of this, we need to iterate over the ICs, and clear them
+// from the VM's constant cache.
static void
iseq_clear_ic_references(const rb_iseq_t *iseq)
{
- struct iseq_clear_ic_references_data data = { .ic = NULL };
- rb_iseq_each(iseq, 0, iseq_clear_ic_references_i, (void *) &data);
+ // In some cases (when there is a compilation error), we end up with
+ // ic_size greater than 0, but no allocated is_entries buffer.
+ // If there's no is_entries buffer to loop through, return early.
+ // [Bug #19173]
+ if (!ISEQ_BODY(iseq)->is_entries) {
+ return;
+ }
+
+ for (unsigned int ic_idx = 0; ic_idx < ISEQ_BODY(iseq)->ic_size; ic_idx++) {
+ IC ic = &ISEQ_IS_IC_ENTRY(ISEQ_BODY(iseq), ic_idx);
+
+ // Iterate over the IC's constant path's segments and clean any references to
+ // the ICs out of the VM's constant cache table.
+ const ID *segments = ic->segments;
+
+ // It's possible that segments is NULL if we overallocated an IC but
+ // optimizations removed the instruction using it
+ if (segments == NULL)
+ continue;
+
+ for (int i = 0; segments[i]; i++) {
+ ID id = segments[i];
+ if (id == idNULL) continue;
+ remove_from_constant_cache(id, ic);
+ }
+
+ ruby_xfree((void *)segments);
+ }
}
void
@@ -174,36 +166,40 @@ rb_iseq_free(const rb_iseq_t *iseq)
if (iseq && ISEQ_BODY(iseq)) {
iseq_clear_ic_references(iseq);
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
- mjit_free_iseq(iseq); /* Notify MJIT */
-#if YJIT_BUILD
+ mjit_free_iseq(iseq); /* Notify MJIT */
+#if USE_YJIT
rb_yjit_iseq_free(body->yjit_payload);
#endif
- ruby_xfree((void *)body->iseq_encoded);
- ruby_xfree((void *)body->insns_info.body);
- if (body->insns_info.positions) ruby_xfree((void *)body->insns_info.positions);
+ ruby_xfree((void *)body->iseq_encoded);
+ ruby_xfree((void *)body->insns_info.body);
+ if (body->insns_info.positions) ruby_xfree((void *)body->insns_info.positions);
#if VM_INSN_INFO_TABLE_IMPL == 2
- if (body->insns_info.succ_index_table) ruby_xfree(body->insns_info.succ_index_table);
+ if (body->insns_info.succ_index_table) ruby_xfree(body->insns_info.succ_index_table);
#endif
if (LIKELY(body->local_table != rb_iseq_shared_exc_local_tbl))
ruby_xfree((void *)body->local_table);
- ruby_xfree((void *)body->is_entries);
+ ruby_xfree((void *)body->is_entries);
if (body->call_data) {
ruby_xfree(body->call_data);
- }
- ruby_xfree((void *)body->catch_table);
- ruby_xfree((void *)body->param.opt_table);
+ }
+ ruby_xfree((void *)body->catch_table);
+ ruby_xfree((void *)body->param.opt_table);
if (ISEQ_MBITS_BUFLEN(body->iseq_size) > 1 && body->mark_bits.list) {
ruby_xfree((void *)body->mark_bits.list);
}
- if (body->param.keyword != NULL) {
- ruby_xfree((void *)body->param.keyword->default_values);
- ruby_xfree((void *)body->param.keyword);
- }
- compile_data_free(ISEQ_COMPILE_DATA(iseq));
+ ruby_xfree(body->variable.original_iseq);
+
+ if (body->param.keyword != NULL) {
+ if (body->param.keyword->table != &body->local_table[body->param.keyword->bits_start - body->param.keyword->num])
+ ruby_xfree((void *)body->param.keyword->table);
+ ruby_xfree((void *)body->param.keyword->default_values);
+ ruby_xfree((void *)body->param.keyword);
+ }
+ compile_data_free(ISEQ_COMPILE_DATA(iseq));
if (body->outer_variables) rb_id_table_free(body->outer_variables);
- ruby_xfree(body);
+ ruby_xfree(body);
}
if (iseq && ISEQ_EXECUTABLE_P(iseq) && iseq->aux.exec.local_hooks) {
@@ -213,32 +209,7 @@ rb_iseq_free(const rb_iseq_t *iseq)
RUBY_FREE_LEAVE("iseq");
}
-#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
-static VALUE
-rb_vm_insn_addr2insn2(const void *addr)
-{
- return (VALUE)rb_vm_insn_addr2insn(addr);
-}
-#endif
-
-// The translator for OPT_DIRECT_THREADED_CODE and OPT_CALL_THREADED_CODE does
-// some normalization to always return the non-trace version of instructions. To
-// mirror that behavior in token-threaded environments, we normalize in this
-// translator by also returning non-trace opcodes.
-static VALUE
-rb_vm_insn_normalizing_translator(const void *addr)
-{
- VALUE opcode = (VALUE)addr;
- VALUE trace_opcode_threshold = (VM_INSTRUCTION_SIZE / 2);
-
- if (opcode >= trace_opcode_threshold) {
- return opcode - trace_opcode_threshold;
- }
- return opcode;
-}
-
typedef VALUE iseq_value_itr_t(void *ctx, VALUE obj);
-typedef VALUE rb_vm_insns_translator_t(const void *addr);
static inline void
iseq_scan_bits(unsigned int page, iseq_bits_t bits, VALUE *code, iseq_value_itr_t *func, void *data)
@@ -274,18 +245,8 @@ rb_iseq_each_value(const rb_iseq_t *iseq, iseq_value_itr_t * func, void *data)
union iseq_inline_storage_entry *is_entries = body->is_entries;
if (body->is_entries) {
- // IVC entries
- for (unsigned int i = 0; i < body->ivc_size; i++, is_entries++) {
- IVC ivc = (IVC)is_entries;
- if (ivc->entry) {
- RUBY_ASSERT(!RB_TYPE_P(ivc->entry->class_value, T_NONE));
-
- VALUE nv = func(data, ivc->entry->class_value);
- if (ivc->entry->class_value != nv) {
- ivc->entry->class_value = nv;
- }
- }
- }
+ // Skip iterating over ivc caches
+ is_entries += body->ivc_size;
// ICVARC entries
for (unsigned int i = 0; i < body->icvarc_size; i++, is_entries++) {
@@ -339,39 +300,6 @@ rb_iseq_each_value(const rb_iseq_t *iseq, iseq_value_itr_t * func, void *data)
}
}
-// Similar to rb_iseq_each_value, except that this walks through each
-// instruction instead of the associated VALUEs. The provided iterator should
-// return a boolean that indicates whether or not to continue iterating.
-void
-rb_iseq_each(const rb_iseq_t *iseq, size_t start_index, rb_iseq_each_i iterator, void *data)
-{
- unsigned int size;
- VALUE *code;
- size_t index;
-
- rb_vm_insns_translator_t *const translator =
-#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
- (FL_TEST((VALUE)iseq, ISEQ_TRANSLATED)) ? rb_vm_insn_addr2insn2 :
-#endif
- rb_vm_insn_normalizing_translator; // Always pass non-trace opcodes.
-
- const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
-
- size = body->iseq_size;
- code = body->iseq_encoded;
-
- for (index = start_index; index < size;) {
- void *addr = (void *) code[index];
- VALUE insn = translator(addr);
-
- if (!iterator(code, insn, index, data)) {
- break;
- }
-
- index += insn_len(insn);
- }
-}
-
static VALUE
update_each_insn_value(void *ctx, VALUE obj)
{
@@ -418,7 +346,7 @@ rb_iseq_update_references(rb_iseq_t *iseq)
for (j = 0; i < body->param.keyword->num; i++, j++) {
VALUE obj = body->param.keyword->default_values[j];
- if (obj != Qundef) {
+ if (!UNDEF_P(obj)) {
body->param.keyword->default_values[j] = rb_gc_location(obj);
}
}
@@ -438,7 +366,7 @@ rb_iseq_update_references(rb_iseq_t *iseq)
#if USE_MJIT
mjit_update_references(iseq);
#endif
-#if YJIT_BUILD
+#if USE_YJIT
rb_yjit_iseq_update_references(body->yjit_payload);
#endif
}
@@ -497,45 +425,45 @@ rb_iseq_mark(const rb_iseq_t *iseq)
}
}
- if (body->param.flags.has_kw && ISEQ_COMPILE_DATA(iseq) == NULL) {
- const struct rb_iseq_param_keyword *const keyword = body->param.keyword;
- int i, j;
+ if (body->param.flags.has_kw && ISEQ_COMPILE_DATA(iseq) == NULL) {
+ const struct rb_iseq_param_keyword *const keyword = body->param.keyword;
+ int i, j;
- i = keyword->required_num;
+ i = keyword->required_num;
- for (j = 0; i < keyword->num; i++, j++) {
- VALUE obj = keyword->default_values[j];
- if (!SPECIAL_CONST_P(obj)) {
+ for (j = 0; i < keyword->num; i++, j++) {
+ VALUE obj = keyword->default_values[j];
+ if (!SPECIAL_CONST_P(obj)) {
rb_gc_mark_movable(obj);
- }
- }
- }
-
- if (body->catch_table) {
- const struct iseq_catch_table *table = body->catch_table;
- unsigned int i;
- for (i = 0; i < table->size; i++) {
- const struct iseq_catch_table_entry *entry;
- entry = UNALIGNED_MEMBER_PTR(table, entries[i]);
- if (entry->iseq) {
+ }
+ }
+ }
+
+ if (body->catch_table) {
+ const struct iseq_catch_table *table = body->catch_table;
+ unsigned int i;
+ for (i = 0; i < table->size; i++) {
+ const struct iseq_catch_table_entry *entry;
+ entry = UNALIGNED_MEMBER_PTR(table, entries[i]);
+ if (entry->iseq) {
rb_gc_mark_movable((VALUE)entry->iseq);
- }
- }
- }
+ }
+ }
+ }
#if USE_MJIT
mjit_mark_cc_entries(body);
#endif
-#if YJIT_BUILD
+#if USE_YJIT
rb_yjit_iseq_mark(body->yjit_payload);
#endif
}
if (FL_TEST_RAW((VALUE)iseq, ISEQ_NOT_LOADED_YET)) {
- rb_gc_mark(iseq->aux.loader.obj);
+ rb_gc_mark(iseq->aux.loader.obj);
}
else if (FL_TEST_RAW((VALUE)iseq, ISEQ_USE_COMPILE_DATA)) {
- const struct iseq_compile_data *const compile_data = ISEQ_COMPILE_DATA(iseq);
+ const struct iseq_compile_data *const compile_data = ISEQ_COMPILE_DATA(iseq);
rb_iseq_mark_insn_storage(compile_data->insn.storage_head);
@@ -593,6 +521,19 @@ rb_iseq_memsize(const rb_iseq_t *iseq)
/* body->is_entries */
size += ISEQ_IS_SIZE(body) * sizeof(union iseq_inline_storage_entry);
+ if (ISEQ_BODY(iseq)->is_entries) {
+ /* IC entries constant segments */
+ for (unsigned int ic_idx = 0; ic_idx < body->ic_size; ic_idx++) {
+ IC ic = &ISEQ_IS_IC_ENTRY(body, ic_idx);
+ const ID *ids = ic->segments;
+ if (!ids) continue;
+ while (*ids++) {
+ size += sizeof(ID);
+ }
+ size += sizeof(ID); // null terminator
+ }
+ }
+
/* body->call_data */
size += body->ci_size * sizeof(struct rb_call_data);
// TODO: should we count imemo_callinfo?
@@ -600,15 +541,15 @@ rb_iseq_memsize(const rb_iseq_t *iseq)
compile_data = ISEQ_COMPILE_DATA(iseq);
if (compile_data) {
- struct iseq_compile_data_storage *cur;
+ struct iseq_compile_data_storage *cur;
- size += sizeof(struct iseq_compile_data);
+ size += sizeof(struct iseq_compile_data);
- cur = compile_data->node.storage_head;
- while (cur) {
- size += cur->size + offsetof(struct iseq_compile_data_storage, buff);
- cur = cur->next;
- }
+ cur = compile_data->node.storage_head;
+ while (cur) {
+ size += cur->size + offsetof(struct iseq_compile_data_storage, buff);
+ cur = cur->next;
+ }
}
return size;
@@ -638,13 +579,13 @@ rb_iseq_pathobj_new(VALUE path, VALUE realpath)
VM_ASSERT(NIL_P(realpath) || RB_TYPE_P(realpath, T_STRING));
if (path == realpath ||
- (!NIL_P(realpath) && rb_str_cmp(path, realpath) == 0)) {
- pathobj = rb_fstring(path);
+ (!NIL_P(realpath) && rb_str_cmp(path, realpath) == 0)) {
+ pathobj = rb_fstring(path);
}
else {
- if (!NIL_P(realpath)) realpath = rb_fstring(realpath);
- pathobj = rb_ary_new_from_args(2, rb_fstring(path), realpath);
- rb_obj_freeze(pathobj);
+ if (!NIL_P(realpath)) realpath = rb_fstring(realpath);
+ pathobj = rb_ary_new_from_args(2, rb_fstring(path), realpath);
+ rb_obj_freeze(pathobj);
}
return pathobj;
}
@@ -653,11 +594,24 @@ void
rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath)
{
RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->location.pathobj,
- rb_iseq_pathobj_new(path, realpath));
+ rb_iseq_pathobj_new(path, realpath));
+}
+
+// Make a dummy iseq for a dummy frame that exposes a path for profilers to inspect
+rb_iseq_t *
+rb_iseq_alloc_with_dummy_path(VALUE fname)
+{
+ rb_iseq_t *dummy_iseq = iseq_alloc();
+
+ ISEQ_BODY(dummy_iseq)->type = ISEQ_TYPE_TOP;
+ RB_OBJ_WRITE(dummy_iseq, &ISEQ_BODY(dummy_iseq)->location.pathobj, fname);
+ RB_OBJ_WRITE(dummy_iseq, &ISEQ_BODY(dummy_iseq)->location.label, fname);
+
+ return dummy_iseq;
}
static rb_iseq_location_t *
-iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location, const int node_id)
+iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id)
{
rb_iseq_location_t *loc = &ISEQ_BODY(iseq)->location;
@@ -667,13 +621,13 @@ iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, VAL
loc->first_lineno = first_lineno;
if (code_location) {
loc->node_id = node_id;
- loc->code_location = *code_location;
+ loc->code_location = *code_location;
}
else {
- loc->code_location.beg_pos.lineno = 0;
- loc->code_location.beg_pos.column = 0;
- loc->code_location.end_pos.lineno = -1;
- loc->code_location.end_pos.column = -1;
+ loc->code_location.beg_pos.lineno = 0;
+ loc->code_location.beg_pos.column = 0;
+ loc->code_location.end_pos.lineno = -1;
+ loc->code_location.end_pos.column = -1;
}
return loc;
@@ -687,21 +641,21 @@ set_relation(rb_iseq_t *iseq, const rb_iseq_t *piseq)
/* set class nest stack */
if (type == ISEQ_TYPE_TOP) {
- body->local_iseq = iseq;
+ body->local_iseq = iseq;
}
else if (type == ISEQ_TYPE_METHOD || type == ISEQ_TYPE_CLASS) {
- body->local_iseq = iseq;
+ body->local_iseq = iseq;
}
else if (piseq) {
body->local_iseq = ISEQ_BODY(piseq)->local_iseq;
}
if (piseq) {
- body->parent_iseq = piseq;
+ body->parent_iseq = piseq;
}
if (type == ISEQ_TYPE_MAIN) {
- body->local_iseq = iseq;
+ body->local_iseq = iseq;
}
}
@@ -722,8 +676,8 @@ new_arena(void)
static VALUE
prepare_iseq_build(rb_iseq_t *iseq,
- VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location, const int node_id,
- const rb_iseq_t *parent, int isolated_depth, enum iseq_type type,
+ VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id,
+ const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type type,
VALUE script_lines, const rb_compile_option_t *option)
{
VALUE coverage = Qfalse;
@@ -731,7 +685,7 @@ prepare_iseq_build(rb_iseq_t *iseq,
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
if (parent && (type == ISEQ_TYPE_MAIN || type == ISEQ_TYPE_TOP))
- err_info = Qfalse;
+ err_info = Qfalse;
body->type = type;
set_relation(iseq, parent);
@@ -763,17 +717,16 @@ prepare_iseq_build(rb_iseq_t *iseq,
ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = NULL;
ISEQ_COMPILE_DATA(iseq)->builtin_function_table = GET_VM()->builtin_function_table;
-
if (option->coverage_enabled) {
- VALUE coverages = rb_get_coverages();
- if (RTEST(coverages)) {
- coverage = rb_hash_lookup(coverages, rb_iseq_path(iseq));
- if (NIL_P(coverage)) coverage = Qfalse;
- }
+ VALUE coverages = rb_get_coverages();
+ if (RTEST(coverages)) {
+ coverage = rb_hash_lookup(coverages, rb_iseq_path(iseq));
+ if (NIL_P(coverage)) coverage = Qfalse;
+ }
}
ISEQ_COVERAGE_SET(iseq, coverage);
if (coverage && ISEQ_BRANCH_COVERAGE(iseq))
- ISEQ_PC2BRANCHINDEX_SET(iseq, rb_ary_tmp_new(0));
+ ISEQ_PC2BRANCHINDEX_SET(iseq, rb_ary_hidden_new(0));
return Qtrue;
}
@@ -834,10 +787,10 @@ finish_iseq_build(rb_iseq_t *iseq)
#endif
if (RTEST(err)) {
- VALUE path = pathobj_path(body->location.pathobj);
- if (err == Qtrue) err = rb_exc_new_cstr(rb_eSyntaxError, "compile error");
- rb_funcallv(err, rb_intern("set_backtrace"), 1, &path);
- rb_exc_raise(err);
+ VALUE path = pathobj_path(body->location.pathobj);
+ if (err == Qtrue) err = rb_exc_new_cstr(rb_eSyntaxError, "compile error");
+ rb_funcallv(err, rb_intern("set_backtrace"), 1, &path);
+ rb_exc_raise(err);
}
RB_DEBUG_COUNTER_INC(iseq_num);
@@ -900,22 +853,22 @@ static void
make_compile_option(rb_compile_option_t *option, VALUE opt)
{
if (NIL_P(opt)) {
- *option = COMPILE_OPTION_DEFAULT;
+ *option = COMPILE_OPTION_DEFAULT;
}
else if (opt == Qfalse) {
- *option = COMPILE_OPTION_FALSE;
+ *option = COMPILE_OPTION_FALSE;
}
else if (opt == Qtrue) {
- int i;
- for (i = 0; i < (int)(sizeof(rb_compile_option_t) / sizeof(int)); ++i)
- ((int *)option)[i] = 1;
+ int i;
+ for (i = 0; i < (int)(sizeof(rb_compile_option_t) / sizeof(int)); ++i)
+ ((int *)option)[i] = 1;
}
else if (RB_TYPE_P(opt, T_HASH)) {
- *option = COMPILE_OPTION_DEFAULT;
- set_compile_option_from_hash(option, opt);
+ *option = COMPILE_OPTION_DEFAULT;
+ set_compile_option_from_hash(option, opt);
}
else {
- rb_raise(rb_eTypeError, "Compile option must be Hash/true/false/nil");
+ rb_raise(rb_eTypeError, "Compile option must be Hash/true/false/nil");
}
}
@@ -928,17 +881,17 @@ make_compile_option_value(rb_compile_option_t *option)
#define SET_COMPILE_OPTION_NUM(o, h, mem) \
rb_hash_aset((h), ID2SYM(rb_intern(#mem)), INT2NUM((o)->mem))
{
- SET_COMPILE_OPTION(option, opt, inline_const_cache);
- SET_COMPILE_OPTION(option, opt, peephole_optimization);
- SET_COMPILE_OPTION(option, opt, tailcall_optimization);
- SET_COMPILE_OPTION(option, opt, specialized_instruction);
- SET_COMPILE_OPTION(option, opt, operands_unification);
- SET_COMPILE_OPTION(option, opt, instructions_unification);
- SET_COMPILE_OPTION(option, opt, stack_caching);
- SET_COMPILE_OPTION(option, opt, frozen_string_literal);
- SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
- SET_COMPILE_OPTION(option, opt, coverage_enabled);
- SET_COMPILE_OPTION_NUM(option, opt, debug_level);
+ SET_COMPILE_OPTION(option, opt, inline_const_cache);
+ SET_COMPILE_OPTION(option, opt, peephole_optimization);
+ SET_COMPILE_OPTION(option, opt, tailcall_optimization);
+ SET_COMPILE_OPTION(option, opt, specialized_instruction);
+ SET_COMPILE_OPTION(option, opt, operands_unification);
+ SET_COMPILE_OPTION(option, opt, instructions_unification);
+ SET_COMPILE_OPTION(option, opt, stack_caching);
+ SET_COMPILE_OPTION(option, opt, frozen_string_literal);
+ SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
+ SET_COMPILE_OPTION(option, opt, coverage_enabled);
+ SET_COMPILE_OPTION_NUM(option, opt, debug_level);
}
#undef SET_COMPILE_OPTION
#undef SET_COMPILE_OPTION_NUM
@@ -947,9 +900,9 @@ make_compile_option_value(rb_compile_option_t *option)
rb_iseq_t *
rb_iseq_new(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath,
- const rb_iseq_t *parent, enum iseq_type type)
+ const rb_iseq_t *parent, enum rb_iseq_type type)
{
- return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent,
+ return rb_iseq_new_with_opt(ast, name, path, realpath, 0, parent,
0, type, &COMPILE_OPTION_DEFAULT);
}
@@ -966,34 +919,62 @@ ast_line_count(const rb_ast_body_t *ast)
return FIX2INT(ast->script_lines);
}
-rb_iseq_t *
-rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent)
+static VALUE
+iseq_setup_coverage(VALUE coverages, VALUE path, const rb_ast_body_t *ast, int line_offset)
+{
+ int line_count = line_offset + ast_line_count(ast);
+
+ if (line_count >= 0) {
+ int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count;
+
+ VALUE coverage = rb_default_coverage(len);
+ rb_hash_aset(coverages, path, coverage);
+
+ return coverage;
+ }
+
+ return Qnil;
+}
+
+static inline void
+iseq_new_setup_coverage(VALUE path, const rb_ast_body_t *ast, int line_offset)
{
VALUE coverages = rb_get_coverages();
+
if (RTEST(coverages)) {
- int line_count = ast_line_count(ast);
- if (line_count >= 0) {
- int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count;
- VALUE coverage = rb_default_coverage(len);
- rb_hash_aset(coverages, path, coverage);
- }
+ iseq_setup_coverage(coverages, path, ast, 0);
}
+}
- return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, 0,
+rb_iseq_t *
+rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent)
+{
+ iseq_new_setup_coverage(path, ast, 0);
+
+ return rb_iseq_new_with_opt(ast, name, path, realpath, 0, parent, 0,
ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT);
}
rb_iseq_t *
rb_iseq_new_main(const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt)
{
+ iseq_new_setup_coverage(path, ast, 0);
+
return rb_iseq_new_with_opt(ast, rb_fstring_lit("<main>"),
- path, realpath, INT2FIX(0),
- parent, 0, ISEQ_TYPE_MAIN, opt ? &COMPILE_OPTION_DEFAULT : &COMPILE_OPTION_FALSE);
+ path, realpath, 0,
+ parent, 0, ISEQ_TYPE_MAIN, opt ? &COMPILE_OPTION_DEFAULT : &COMPILE_OPTION_FALSE);
}
rb_iseq_t *
-rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth)
+rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth)
{
+ if (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) {
+ VALUE coverages = rb_get_coverages();
+ if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) {
+ iseq_setup_coverage(coverages, path, ast, first_lineno - 1);
+ }
+ }
+
return rb_iseq_new_with_opt(ast, name, path, realpath, first_lineno,
parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT);
}
@@ -1002,11 +983,11 @@ static inline rb_iseq_t *
iseq_translate(rb_iseq_t *iseq)
{
if (rb_respond_to(rb_cISeq, rb_intern("translate"))) {
- VALUE v1 = iseqw_new(iseq);
- VALUE v2 = rb_funcall(rb_cISeq, rb_intern("translate"), 1, v1);
- if (v1 != v2 && CLASS_OF(v2) == rb_cISeq) {
- iseq = (rb_iseq_t *)iseqw_check(v2);
- }
+ VALUE v1 = iseqw_new(iseq);
+ VALUE v2 = rb_funcall(rb_cISeq, rb_intern("translate"), 1, v1);
+ if (v1 != v2 && CLASS_OF(v2) == rb_cISeq) {
+ iseq = (rb_iseq_t *)iseqw_check(v2);
+ }
}
return iseq;
@@ -1014,8 +995,8 @@ iseq_translate(rb_iseq_t *iseq)
rb_iseq_t *
rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath,
- VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth,
- enum iseq_type type, const rb_compile_option_t *option)
+ int first_lineno, const rb_iseq_t *parent, int isolated_depth,
+ enum rb_iseq_type type, const rb_compile_option_t *option)
{
const NODE *node = ast ? ast->root : 0;
/* TODO: argument check */
@@ -1052,8 +1033,8 @@ rb_iseq_t *
rb_iseq_new_with_callback(
const struct rb_iseq_new_with_callback_callback_func * ifunc,
VALUE name, VALUE path, VALUE realpath,
- VALUE first_lineno, const rb_iseq_t *parent,
- enum iseq_type type, const rb_compile_option_t *option)
+ int first_lineno, const rb_iseq_t *parent,
+ enum rb_iseq_type type, const rb_compile_option_t *option)
{
/* TODO: argument check */
rb_iseq_t *iseq = iseq_alloc();
@@ -1073,7 +1054,7 @@ rb_iseq_load_iseq(VALUE fname)
VALUE iseqv = rb_check_funcall(rb_cISeq, rb_intern("load_iseq"), 1, &fname);
if (!SPECIAL_CONST_P(iseqv) && RBASIC_CLASS(iseqv) == rb_cISeq) {
- return iseqw_check(iseqv);
+ return iseqw_check(iseqv);
}
return NULL;
@@ -1085,7 +1066,7 @@ rb_iseq_load_iseq(VALUE fname)
#define CHECK_SYMBOL(v) rb_to_symbol_type(v)
static inline VALUE CHECK_INTEGER(VALUE v) {(void)NUM2LONG(v); return v;}
-static enum iseq_type
+static enum rb_iseq_type
iseq_type_from_sym(VALUE type)
{
const ID id_top = rb_intern("top");
@@ -1109,7 +1090,7 @@ iseq_type_from_sym(VALUE type)
if (typeid == id_eval) return ISEQ_TYPE_EVAL;
if (typeid == id_main) return ISEQ_TYPE_MAIN;
if (typeid == id_plain) return ISEQ_TYPE_PLAIN;
- return (enum iseq_type)-1;
+ return (enum rb_iseq_type)-1;
}
static VALUE
@@ -1118,7 +1099,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
rb_iseq_t *iseq = iseq_alloc();
VALUE magic, version1, version2, format_type, misc;
- VALUE name, path, realpath, first_lineno, code_location, node_id;
+ VALUE name, path, realpath, code_location, node_id;
VALUE type, body, locals, params, exception;
st_data_t iseq_type;
@@ -1144,7 +1125,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
path = CHECK_STRING(rb_ary_entry(data, i++));
realpath = rb_ary_entry(data, i++);
realpath = NIL_P(realpath) ? Qnil : CHECK_STRING(realpath);
- first_lineno = CHECK_INTEGER(rb_ary_entry(data, i++));
+ int first_lineno = RB_NUM2INT(rb_ary_entry(data, i++));
type = CHECK_SYMBOL(rb_ary_entry(data, i++));
locals = CHECK_ARRAY(rb_ary_entry(data, i++));
@@ -1155,24 +1136,24 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
ISEQ_BODY(iseq)->local_iseq = iseq;
iseq_type = iseq_type_from_sym(type);
- if (iseq_type == (enum iseq_type)-1) {
- rb_raise(rb_eTypeError, "unsupported type: :%"PRIsVALUE, rb_sym2str(type));
+ if (iseq_type == (enum rb_iseq_type)-1) {
+ rb_raise(rb_eTypeError, "unsupported type: :%"PRIsVALUE, rb_sym2str(type));
}
node_id = rb_hash_aref(misc, ID2SYM(rb_intern("node_id")));
code_location = rb_hash_aref(misc, ID2SYM(rb_intern("code_location")));
if (RB_TYPE_P(code_location, T_ARRAY) && RARRAY_LEN(code_location) == 4) {
- tmp_loc.beg_pos.lineno = NUM2INT(rb_ary_entry(code_location, 0));
- tmp_loc.beg_pos.column = NUM2INT(rb_ary_entry(code_location, 1));
- tmp_loc.end_pos.lineno = NUM2INT(rb_ary_entry(code_location, 2));
- tmp_loc.end_pos.column = NUM2INT(rb_ary_entry(code_location, 3));
+ tmp_loc.beg_pos.lineno = NUM2INT(rb_ary_entry(code_location, 0));
+ tmp_loc.beg_pos.column = NUM2INT(rb_ary_entry(code_location, 1));
+ tmp_loc.end_pos.lineno = NUM2INT(rb_ary_entry(code_location, 2));
+ tmp_loc.end_pos.column = NUM2INT(rb_ary_entry(code_location, 3));
}
make_compile_option(&option, opt);
option.peephole_optimization = FALSE; /* because peephole optimization can modify original iseq */
prepare_iseq_build(iseq, name, path, realpath, first_lineno, &tmp_loc, NUM2INT(node_id),
- parent, 0, (enum iseq_type)iseq_type, Qnil, &option);
+ parent, 0, (enum rb_iseq_type)iseq_type, Qnil, &option);
rb_iseq_build_from_ary(iseq, misc, locals, params, exception, body);
@@ -1218,29 +1199,29 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V
ln = NUM2INT(line);
StringValueCStr(file);
if (RB_TYPE_P(src, T_FILE)) {
- parse = rb_parser_compile_file_path;
+ parse = rb_parser_compile_file_path;
}
else {
- parse = rb_parser_compile_string_path;
- StringValue(src);
+ parse = rb_parser_compile_string_path;
+ StringValue(src);
}
{
- const VALUE parser = rb_parser_new();
+ const VALUE parser = rb_parser_new();
const rb_iseq_t *outer_scope = rb_iseq_new(NULL, name, name, Qnil, 0, ISEQ_TYPE_TOP);
VALUE outer_scope_v = (VALUE)outer_scope;
rb_parser_set_context(parser, outer_scope, FALSE);
RB_GC_GUARD(outer_scope_v);
- ast = (*parse)(parser, file, src, ln);
+ ast = (*parse)(parser, file, src, ln);
}
if (!ast->body.root) {
- rb_ast_dispose(ast);
- rb_exc_raise(GET_EC()->errinfo);
+ rb_ast_dispose(ast);
+ rb_exc_raise(GET_EC()->errinfo);
}
else {
- iseq = rb_iseq_new_with_opt(&ast->body, name, file, realpath, line,
- NULL, 0, ISEQ_TYPE_TOP, &option);
- rb_ast_dispose(ast);
+ iseq = rb_iseq_new_with_opt(&ast->body, name, file, realpath, ln,
+ NULL, 0, ISEQ_TYPE_TOP, &option);
+ rb_ast_dispose(ast);
}
return iseq;
@@ -1285,7 +1266,7 @@ rb_iseq_base_label(const rb_iseq_t *iseq)
VALUE
rb_iseq_first_lineno(const rb_iseq_t *iseq)
{
- return ISEQ_BODY(iseq)->location.first_lineno;
+ return RB_INT2NUM(ISEQ_BODY(iseq)->location.first_lineno);
}
VALUE
@@ -1294,10 +1275,10 @@ rb_iseq_method_name(const rb_iseq_t *iseq)
struct rb_iseq_constant_body *const body = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq);
if (body->type == ISEQ_TYPE_METHOD) {
- return body->location.base_label;
+ return body->location.base_label;
}
else {
- return Qnil;
+ return Qnil;
}
}
@@ -1311,7 +1292,7 @@ rb_iseq_code_location(const rb_iseq_t *iseq, int *beg_pos_lineno, int *beg_pos_c
if (end_pos_column) *end_pos_column = loc->end_pos.column;
}
-static ID iseq_type_id(enum iseq_type type);
+static ID iseq_type_id(enum rb_iseq_type type);
VALUE
rb_iseq_type(const rb_iseq_t *iseq)
@@ -1333,10 +1314,10 @@ remove_coverage_i(void *vstart, void *vend, size_t stride, void *data)
void *ptr = asan_poisoned_object_p(v);
asan_unpoison_object(v, false);
- if (rb_obj_is_iseq(v)) {
+ if (rb_obj_is_iseq(v)) {
rb_iseq_t *iseq = (rb_iseq_t *)v;
ISEQ_COVERAGE_SET(iseq, Qnil);
- }
+ }
asan_poison_object_if(ptr, v);
}
@@ -1477,7 +1458,7 @@ iseqw_s_compile(int argc, VALUE *argv, VALUE self)
static VALUE
iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
{
- VALUE file, line = INT2FIX(1), opt = Qnil;
+ VALUE file, opt = Qnil;
VALUE parser, f, exc = Qnil, ret;
rb_ast_t *ast;
rb_compile_option_t option;
@@ -1493,6 +1474,9 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
f = rb_file_open_str(file, "r");
+ rb_execution_context_t *ec = GET_EC();
+ VALUE v = rb_vm_push_frame_fname(ec, file);
+
parser = rb_parser_new();
rb_parser_set_context(parser, NULL, FALSE);
ast = (rb_ast_t *)rb_parser_load_file(parser, file);
@@ -1500,17 +1484,20 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
rb_io_close(f);
if (!ast->body.root) {
- rb_ast_dispose(ast);
- rb_exc_raise(exc);
+ rb_ast_dispose(ast);
+ rb_exc_raise(exc);
}
make_compile_option(&option, opt);
ret = iseqw_new(rb_iseq_new_with_opt(&ast->body, rb_fstring_lit("<main>"),
- file,
- rb_realpath_internal(Qnil, file, 1),
- line, NULL, 0, ISEQ_TYPE_TOP, &option));
+ file,
+ rb_realpath_internal(Qnil, file, 1),
+ 1, NULL, 0, ISEQ_TYPE_TOP, &option));
rb_ast_dispose(ast);
+
+ rb_vm_pop_frame(ec);
+ RB_GC_GUARD(v);
return ret;
}
@@ -1574,11 +1561,11 @@ iseqw_check(VALUE iseqw)
rb_iseq_t *iseq = DATA_PTR(iseqw);
if (!ISEQ_BODY(iseq)) {
- rb_ibf_load_iseq_complete(iseq);
+ rb_ibf_load_iseq_complete(iseq);
}
if (!ISEQ_BODY(iseq)->location.label) {
- rb_raise(rb_eTypeError, "uninitialized InstructionSequence");
+ rb_raise(rb_eTypeError, "uninitialized InstructionSequence");
}
return iseq;
}
@@ -1600,7 +1587,11 @@ rb_iseqw_to_iseq(VALUE iseqw)
static VALUE
iseqw_eval(VALUE self)
{
- return rb_iseq_eval(iseqw_check(self));
+ const rb_iseq_t *iseq = iseqw_check(self);
+ if (0 == ISEQ_BODY(iseq)->iseq_size) {
+ rb_raise(rb_eTypeError, "attempt to evaluate dummy InstructionSequence");
+ }
+ return rb_iseq_eval(iseq);
}
/*
@@ -1615,13 +1606,13 @@ iseqw_inspect(VALUE self)
VALUE klass = rb_class_name(rb_obj_class(self));
if (!body->location.label) {
- return rb_sprintf("#<%"PRIsVALUE": uninitialized>", klass);
+ return rb_sprintf("#<%"PRIsVALUE": uninitialized>", klass);
}
else {
- return rb_sprintf("<%"PRIsVALUE":%"PRIsVALUE"@%"PRIsVALUE":%d>",
- klass,
- body->location.label, rb_iseq_path(iseq),
- FIX2INT(rb_iseq_first_lineno(iseq)));
+ return rb_sprintf("<%"PRIsVALUE":%"PRIsVALUE"@%"PRIsVALUE":%d>",
+ klass,
+ body->location.label, rb_iseq_path(iseq),
+ FIX2INT(rb_iseq_first_lineno(iseq)));
}
}
@@ -1848,38 +1839,38 @@ get_insn_info_binary_search(const rb_iseq_t *iseq, size_t pos)
const int debug = 0;
if (debug) {
- printf("size: %"PRIuSIZE"\n", size);
- printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
- (size_t)0, positions[0], insns_info[0].line_no, pos);
+ printf("size: %"PRIuSIZE"\n", size);
+ printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
+ (size_t)0, positions[0], insns_info[0].line_no, pos);
}
if (size == 0) {
- return NULL;
+ return NULL;
}
else if (size == 1) {
- return &insns_info[0];
+ return &insns_info[0];
}
else {
- size_t l = 1, r = size - 1;
- while (l <= r) {
- size_t m = l + (r - l) / 2;
- if (positions[m] == pos) {
- return &insns_info[m];
- }
- if (positions[m] < pos) {
- l = m + 1;
- }
- else {
- r = m - 1;
- }
- }
- if (l >= size) {
- return &insns_info[size-1];
- }
- if (positions[l] > pos) {
- return &insns_info[l-1];
- }
- return &insns_info[l];
+ size_t l = 1, r = size - 1;
+ while (l <= r) {
+ size_t m = l + (r - l) / 2;
+ if (positions[m] == pos) {
+ return &insns_info[m];
+ }
+ if (positions[m] < pos) {
+ l = m + 1;
+ }
+ else {
+ r = m - 1;
+ }
+ }
+ if (l >= size) {
+ return &insns_info[size-1];
+ }
+ if (positions[l] > pos) {
+ return &insns_info[l-1];
+ }
+ return &insns_info[l];
}
}
@@ -1913,16 +1904,16 @@ get_insn_info_succinct_bitvector(const rb_iseq_t *iseq, size_t pos)
}
if (size == 0) {
- return NULL;
+ return NULL;
}
else if (size == 1) {
- return &insns_info[0];
+ return &insns_info[0];
}
else {
- int index;
- VM_ASSERT(body->insns_info.succ_index_table != NULL);
- index = succ_index_lookup(body->insns_info.succ_index_table, (int)pos);
- return &insns_info[index-1];
+ int index;
+ VM_ASSERT(body->insns_info.succ_index_table != NULL);
+ index = succ_index_lookup(body->insns_info.succ_index_table, (int)pos);
+ return &insns_info[index-1];
}
}
@@ -1944,29 +1935,29 @@ get_insn_info_linear_search(const rb_iseq_t *iseq, size_t pos)
const int debug = 0;
if (debug) {
- printf("size: %"PRIuSIZE"\n", size);
- printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
- i, positions[i], insns_info[i].line_no, pos);
+ printf("size: %"PRIuSIZE"\n", size);
+ printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
+ i, positions[i], insns_info[i].line_no, pos);
}
if (size == 0) {
- return NULL;
+ return NULL;
}
else if (size == 1) {
- return &insns_info[0];
+ return &insns_info[0];
}
else {
- for (i=1; i<size; i++) {
- if (debug) printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
- i, positions[i], insns_info[i].line_no, pos);
-
- if (positions[i] == pos) {
- return &insns_info[i];
- }
- if (positions[i] > pos) {
- return &insns_info[i-1];
- }
- }
+ for (i=1; i<size; i++) {
+ if (debug) printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
+ i, positions[i], insns_info[i].line_no, pos);
+
+ if (positions[i] == pos) {
+ return &insns_info[i];
+ }
+ if (positions[i] > pos) {
+ return &insns_info[i-1];
+ }
+ }
}
return &insns_info[i-1];
}
@@ -1987,9 +1978,9 @@ validate_get_insn_info(const rb_iseq_t *iseq)
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
size_t i;
for (i = 0; i < body->iseq_size; i++) {
- if (get_insn_info_linear_search(iseq, i) != get_insn_info(iseq, i)) {
- rb_bug("validate_get_insn_info: get_insn_info_linear_search(iseq, %"PRIuSIZE") != get_insn_info(iseq, %"PRIuSIZE")", i, i);
- }
+ if (get_insn_info_linear_search(iseq, i) != get_insn_info(iseq, i)) {
+ rb_bug("validate_get_insn_info: get_insn_info_linear_search(iseq, %"PRIuSIZE") != get_insn_info(iseq, %"PRIuSIZE")", i, i);
+ }
}
}
#endif
@@ -2000,10 +1991,10 @@ rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos)
const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pos);
if (entry) {
- return entry->line_no;
+ return entry->line_no;
}
else {
- return 0;
+ return 0;
}
}
@@ -2014,10 +2005,10 @@ rb_iseq_node_id(const rb_iseq_t *iseq, size_t pos)
const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pos);
if (entry) {
- return entry->node_id;
+ return entry->node_id;
}
else {
- return 0;
+ return 0;
}
}
#endif
@@ -2027,10 +2018,10 @@ rb_iseq_event_flags(const rb_iseq_t *iseq, size_t pos)
{
const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pos);
if (entry) {
- return entry->events;
+ return entry->events;
}
else {
- return 0;
+ return 0;
}
}
@@ -2062,13 +2053,13 @@ local_var_name(const rb_iseq_t *diseq, VALUE level, VALUE op)
lid = ISEQ_BODY(diseq)->local_table[idx];
name = rb_id2str(lid);
if (!name) {
- name = rb_str_new_cstr("?");
+ name = rb_str_new_cstr("?");
}
else if (!rb_str_symname_p(name)) {
- name = rb_str_inspect(name);
+ name = rb_str_inspect(name);
}
else {
- name = rb_str_dup(name);
+ name = rb_str_dup(name);
}
rb_str_catf(name, "@%d", idx);
return name;
@@ -2079,8 +2070,8 @@ VALUE rb_dump_literal(VALUE lit);
VALUE
rb_insn_operand_intern(const rb_iseq_t *iseq,
- VALUE insn, int op_no, VALUE op,
- int len, size_t pos, const VALUE *pnop, VALUE child)
+ VALUE insn, int op_no, VALUE op,
+ int len, size_t pos, const VALUE *pnop, VALUE child)
{
const char *types = insn_op_types(insn);
char type = types[op_no];
@@ -2088,157 +2079,167 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
switch (type) {
case TS_OFFSET: /* LONG */
- ret = rb_sprintf("%"PRIdVALUE, (VALUE)(pos + len + op));
- break;
+ ret = rb_sprintf("%"PRIdVALUE, (VALUE)(pos + len + op));
+ break;
case TS_NUM: /* ULONG */
- if (insn == BIN(defined) && op_no == 0) {
- enum defined_type deftype = (enum defined_type)op;
- switch (deftype) {
- case DEFINED_FUNC:
- ret = rb_fstring_lit("func");
- break;
- case DEFINED_REF:
- ret = rb_fstring_lit("ref");
- break;
- case DEFINED_CONST_FROM:
- ret = rb_fstring_lit("constant-from");
- break;
- default:
- ret = rb_iseq_defined_string(deftype);
- break;
- }
- if (ret) break;
- }
- else if (insn == BIN(checktype) && op_no == 0) {
- const char *type_str = rb_type_str((enum ruby_value_type)op);
- if (type_str) {
- ret = rb_str_new_cstr(type_str); break;
- }
- }
- ret = rb_sprintf("%"PRIuVALUE, op);
- break;
+ if (insn == BIN(defined) && op_no == 0) {
+ enum defined_type deftype = (enum defined_type)op;
+ switch (deftype) {
+ case DEFINED_FUNC:
+ ret = rb_fstring_lit("func");
+ break;
+ case DEFINED_REF:
+ ret = rb_fstring_lit("ref");
+ break;
+ case DEFINED_CONST_FROM:
+ ret = rb_fstring_lit("constant-from");
+ break;
+ default:
+ ret = rb_iseq_defined_string(deftype);
+ break;
+ }
+ if (ret) break;
+ }
+ else if (insn == BIN(checktype) && op_no == 0) {
+ const char *type_str = rb_type_str((enum ruby_value_type)op);
+ if (type_str) {
+ ret = rb_str_new_cstr(type_str); break;
+ }
+ }
+ ret = rb_sprintf("%"PRIuVALUE, op);
+ break;
case TS_LINDEX:{
- int level;
- if (types[op_no+1] == TS_NUM && pnop) {
- ret = local_var_name(iseq, *pnop, op - VM_ENV_DATA_SIZE);
- }
- else if ((level = rb_insn_unified_local_var_level(insn)) >= 0) {
- ret = local_var_name(iseq, (VALUE)level, op - VM_ENV_DATA_SIZE);
- }
- else {
- ret = rb_inspect(INT2FIX(op));
- }
- break;
+ int level;
+ if (types[op_no+1] == TS_NUM && pnop) {
+ ret = local_var_name(iseq, *pnop, op - VM_ENV_DATA_SIZE);
+ }
+ else if ((level = rb_insn_unified_local_var_level(insn)) >= 0) {
+ ret = local_var_name(iseq, (VALUE)level, op - VM_ENV_DATA_SIZE);
+ }
+ else {
+ ret = rb_inspect(INT2FIX(op));
+ }
+ break;
}
case TS_ID: /* ID (symbol) */
- ret = rb_inspect(ID2SYM(op));
- break;
+ ret = rb_inspect(ID2SYM(op));
+ break;
case TS_VALUE: /* VALUE */
- op = obj_resurrect(op);
- if (insn == BIN(defined) && op_no == 1 && FIXNUM_P(op)) {
- /* should be DEFINED_REF */
- int type = NUM2INT(op);
- if (type) {
- if (type & 1) {
- ret = rb_sprintf(":$%c", (type >> 1));
- }
- else {
- ret = rb_sprintf(":$%d", (type >> 1));
- }
- break;
- }
- }
- ret = rb_dump_literal(op);
- if (CLASS_OF(op) == rb_cISeq) {
- if (child) {
- rb_ary_push(child, op);
- }
- }
- break;
+ op = obj_resurrect(op);
+ if (insn == BIN(defined) && op_no == 1 && FIXNUM_P(op)) {
+ /* should be DEFINED_REF */
+ int type = NUM2INT(op);
+ if (type) {
+ if (type & 1) {
+ ret = rb_sprintf(":$%c", (type >> 1));
+ }
+ else {
+ ret = rb_sprintf(":$%d", (type >> 1));
+ }
+ break;
+ }
+ }
+ ret = rb_dump_literal(op);
+ if (CLASS_OF(op) == rb_cISeq) {
+ if (child) {
+ rb_ary_push(child, op);
+ }
+ }
+ break;
case TS_ISEQ: /* iseq */
- {
- if (op) {
- const rb_iseq_t *iseq = rb_iseq_check((rb_iseq_t *)op);
+ {
+ if (op) {
+ const rb_iseq_t *iseq = rb_iseq_check((rb_iseq_t *)op);
ret = ISEQ_BODY(iseq)->location.label;
- if (child) {
- rb_ary_push(child, (VALUE)iseq);
- }
- }
- else {
- ret = rb_str_new2("nil");
- }
- break;
- }
+ if (child) {
+ rb_ary_push(child, (VALUE)iseq);
+ }
+ }
+ else {
+ ret = rb_str_new2("nil");
+ }
+ break;
+ }
case TS_IC:
+ {
+ ret = rb_sprintf("<ic:%"PRIdPTRDIFF" ", (union iseq_inline_storage_entry *)op - ISEQ_BODY(iseq)->is_entries);
+ const ID *segments = ((IC)op)->segments;
+ rb_str_cat2(ret, rb_id2name(*segments++));
+ while (*segments) {
+ rb_str_catf(ret, "::%s", rb_id2name(*segments++));
+ }
+ rb_str_cat2(ret, ">");
+ }
+ break;
case TS_IVC:
case TS_ICVARC:
case TS_ISE:
ret = rb_sprintf("<is:%"PRIdPTRDIFF">", (union iseq_inline_storage_entry *)op - ISEQ_BODY(iseq)->is_entries);
- break;
+ break;
case TS_CALLDATA:
- {
+ {
struct rb_call_data *cd = (struct rb_call_data *)op;
const struct rb_callinfo *ci = cd->ci;
- VALUE ary = rb_ary_new();
+ VALUE ary = rb_ary_new();
ID mid = vm_ci_mid(ci);
if (mid) {
- rb_ary_push(ary, rb_sprintf("mid:%"PRIsVALUE, rb_id2str(mid)));
- }
+ rb_ary_push(ary, rb_sprintf("mid:%"PRIsVALUE, rb_id2str(mid)));
+ }
- rb_ary_push(ary, rb_sprintf("argc:%d", vm_ci_argc(ci)));
+ rb_ary_push(ary, rb_sprintf("argc:%d", vm_ci_argc(ci)));
if (vm_ci_flag(ci) & VM_CALL_KWARG) {
const struct rb_callinfo_kwarg *kw_args = vm_ci_kwarg(ci);
VALUE kw_ary = rb_ary_new_from_values(kw_args->keyword_len, kw_args->keywords);
rb_ary_push(ary, rb_sprintf("kw:[%"PRIsVALUE"]", rb_ary_join(kw_ary, rb_str_new2(","))));
- }
+ }
if (vm_ci_flag(ci)) {
- VALUE flags = rb_ary_new();
+ VALUE flags = rb_ary_new();
# define CALL_FLAG(n) if (vm_ci_flag(ci) & VM_CALL_##n) rb_ary_push(flags, rb_str_new2(#n))
- CALL_FLAG(ARGS_SPLAT);
- CALL_FLAG(ARGS_BLOCKARG);
- CALL_FLAG(FCALL);
- CALL_FLAG(VCALL);
- CALL_FLAG(ARGS_SIMPLE);
- CALL_FLAG(BLOCKISEQ);
- CALL_FLAG(TAILCALL);
- CALL_FLAG(SUPER);
- CALL_FLAG(ZSUPER);
- CALL_FLAG(KWARG);
- CALL_FLAG(KW_SPLAT);
+ CALL_FLAG(ARGS_SPLAT);
+ CALL_FLAG(ARGS_BLOCKARG);
+ CALL_FLAG(FCALL);
+ CALL_FLAG(VCALL);
+ CALL_FLAG(ARGS_SIMPLE);
+ CALL_FLAG(BLOCKISEQ);
+ CALL_FLAG(TAILCALL);
+ CALL_FLAG(SUPER);
+ CALL_FLAG(ZSUPER);
+ CALL_FLAG(KWARG);
+ CALL_FLAG(KW_SPLAT);
CALL_FLAG(KW_SPLAT_MUT);
- CALL_FLAG(OPT_SEND); /* maybe not reachable */
- rb_ary_push(ary, rb_ary_join(flags, rb_str_new2("|")));
- }
+ CALL_FLAG(OPT_SEND); /* maybe not reachable */
+ rb_ary_push(ary, rb_ary_join(flags, rb_str_new2("|")));
+ }
ret = rb_sprintf("<calldata!%"PRIsVALUE">", rb_ary_join(ary, rb_str_new2(", ")));
}
- break;
+ break;
case TS_CDHASH:
- ret = rb_str_new2("<cdhash>");
- break;
+ ret = rb_str_new2("<cdhash>");
+ break;
case TS_FUNCPTR:
- {
+ {
#ifdef HAVE_DLADDR
- Dl_info info;
- if (dladdr((void *)op, &info) && info.dli_sname) {
- ret = rb_str_new_cstr(info.dli_sname);
- break;
- }
+ Dl_info info;
+ if (dladdr((void *)op, &info) && info.dli_sname) {
+ ret = rb_str_new_cstr(info.dli_sname);
+ break;
+ }
#endif
- ret = rb_str_new2("<funcptr>");
- }
- break;
+ ret = rb_str_new2("<funcptr>");
+ }
+ break;
case TS_BUILTIN:
{
@@ -2249,7 +2250,7 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
break;
default:
- rb_bug("unknown operand type: %c", type);
+ rb_bug("unknown operand type: %c", type);
}
return ret;
}
@@ -2269,7 +2270,7 @@ right_strip(VALUE str)
*/
int
rb_iseq_disasm_insn(VALUE ret, const VALUE *code, size_t pos,
- const rb_iseq_t *iseq, VALUE child)
+ const rb_iseq_t *iseq, VALUE child)
{
VALUE insn = code[pos];
int len = insn_len(insn);
@@ -2280,60 +2281,60 @@ rb_iseq_disasm_insn(VALUE ret, const VALUE *code, size_t pos,
insn_name_buff = insn_name(insn);
if (1) {
- extern const int rb_vm_max_insn_name_size;
- rb_str_catf(str, "%04"PRIuSIZE" %-*s ", pos, rb_vm_max_insn_name_size, insn_name_buff);
+ extern const int rb_vm_max_insn_name_size;
+ rb_str_catf(str, "%04"PRIuSIZE" %-*s ", pos, rb_vm_max_insn_name_size, insn_name_buff);
}
else {
- rb_str_catf(str, "%04"PRIuSIZE" %-28.*s ", pos,
- (int)strcspn(insn_name_buff, "_"), insn_name_buff);
+ rb_str_catf(str, "%04"PRIuSIZE" %-28.*s ", pos,
+ (int)strcspn(insn_name_buff, "_"), insn_name_buff);
}
for (j = 0; types[j]; j++) {
- VALUE opstr = rb_insn_operand_intern(iseq, insn, j, code[pos + j + 1],
- len, pos, &code[pos + j + 2],
- child);
- rb_str_concat(str, opstr);
+ VALUE opstr = rb_insn_operand_intern(iseq, insn, j, code[pos + j + 1],
+ len, pos, &code[pos + j + 2],
+ child);
+ rb_str_concat(str, opstr);
- if (types[j + 1]) {
- rb_str_cat2(str, ", ");
- }
+ if (types[j + 1]) {
+ rb_str_cat2(str, ", ");
+ }
}
{
- unsigned int line_no = rb_iseq_line_no(iseq, pos);
- unsigned int prev = pos == 0 ? 0 : rb_iseq_line_no(iseq, pos - 1);
- if (line_no && line_no != prev) {
- long slen = RSTRING_LEN(str);
- slen = (slen > 70) ? 0 : (70 - slen);
- str = rb_str_catf(str, "%*s(%4d)", (int)slen, "", line_no);
- }
+ unsigned int line_no = rb_iseq_line_no(iseq, pos);
+ unsigned int prev = pos == 0 ? 0 : rb_iseq_line_no(iseq, pos - 1);
+ if (line_no && line_no != prev) {
+ long slen = RSTRING_LEN(str);
+ slen = (slen > 70) ? 0 : (70 - slen);
+ str = rb_str_catf(str, "%*s(%4d)", (int)slen, "", line_no);
+ }
}
{
- rb_event_flag_t events = rb_iseq_event_flags(iseq, pos);
- if (events) {
+ rb_event_flag_t events = rb_iseq_event_flags(iseq, pos);
+ if (events) {
str = rb_str_catf(str, "[%s%s%s%s%s%s%s%s%s%s%s]",
- events & RUBY_EVENT_LINE ? "Li" : "",
- events & RUBY_EVENT_CLASS ? "Cl" : "",
- events & RUBY_EVENT_END ? "En" : "",
- events & RUBY_EVENT_CALL ? "Ca" : "",
- events & RUBY_EVENT_RETURN ? "Re" : "",
- events & RUBY_EVENT_C_CALL ? "Cc" : "",
- events & RUBY_EVENT_C_RETURN ? "Cr" : "",
- events & RUBY_EVENT_B_CALL ? "Bc" : "",
+ events & RUBY_EVENT_LINE ? "Li" : "",
+ events & RUBY_EVENT_CLASS ? "Cl" : "",
+ events & RUBY_EVENT_END ? "En" : "",
+ events & RUBY_EVENT_CALL ? "Ca" : "",
+ events & RUBY_EVENT_RETURN ? "Re" : "",
+ events & RUBY_EVENT_C_CALL ? "Cc" : "",
+ events & RUBY_EVENT_C_RETURN ? "Cr" : "",
+ events & RUBY_EVENT_B_CALL ? "Bc" : "",
events & RUBY_EVENT_B_RETURN ? "Br" : "",
events & RUBY_EVENT_COVERAGE_LINE ? "Cli" : "",
events & RUBY_EVENT_COVERAGE_BRANCH ? "Cbr" : "");
- }
+ }
}
right_strip(str);
if (ret) {
- rb_str_cat2(str, "\n");
- rb_str_concat(ret, str);
+ rb_str_cat2(str, "\n");
+ rb_str_concat(ret, str);
}
else {
- printf("%.*s\n", (int)RSTRING_LEN(str), RSTRING_PTR(str));
+ printf("%.*s\n", (int)RSTRING_LEN(str), RSTRING_PTR(str));
}
return len;
}
@@ -2343,20 +2344,20 @@ catch_type(int type)
{
switch (type) {
case CATCH_TYPE_RESCUE:
- return "rescue";
+ return "rescue";
case CATCH_TYPE_ENSURE:
- return "ensure";
+ return "ensure";
case CATCH_TYPE_RETRY:
- return "retry";
+ return "retry";
case CATCH_TYPE_BREAK:
- return "break";
+ return "break";
case CATCH_TYPE_REDO:
- return "redo";
+ return "redo";
case CATCH_TYPE_NEXT:
- return "next";
+ return "next";
default:
- rb_bug("unknown catch type: %d", type);
- return 0;
+ rb_bug("unknown catch type: %d", type);
+ return 0;
}
}
@@ -2365,17 +2366,17 @@ iseq_inspect(const rb_iseq_t *iseq)
{
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
if (!body->location.label) {
- return rb_sprintf("#<ISeq: uninitialized>");
+ return rb_sprintf("#<ISeq: uninitialized>");
}
else {
- const rb_code_location_t *loc = &body->location.code_location;
- return rb_sprintf("#<ISeq:%"PRIsVALUE"@%"PRIsVALUE":%d (%d,%d)-(%d,%d)>",
- body->location.label, rb_iseq_path(iseq),
- loc->beg_pos.lineno,
- loc->beg_pos.lineno,
- loc->beg_pos.column,
- loc->end_pos.lineno,
- loc->end_pos.column);
+ const rb_code_location_t *loc = &body->location.code_location;
+ return rb_sprintf("#<ISeq:%"PRIsVALUE"@%"PRIsVALUE":%d (%d,%d)-(%d,%d)>",
+ body->location.label, rb_iseq_path(iseq),
+ loc->beg_pos.lineno,
+ loc->beg_pos.lineno,
+ loc->beg_pos.column,
+ loc->end_pos.lineno,
+ loc->end_pos.column);
}
}
@@ -2391,7 +2392,7 @@ rb_iseq_disasm_recursive(const rb_iseq_t *iseq, VALUE indent)
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
VALUE *code;
VALUE str = rb_str_new(0, 0);
- VALUE child = rb_ary_tmp_new(3);
+ VALUE child = rb_ary_hidden_new(3);
unsigned int size;
unsigned int i;
long l;
@@ -2411,113 +2412,113 @@ rb_iseq_disasm_recursive(const rb_iseq_t *iseq, VALUE indent)
rb_str_cat2(str, "== disasm: ");
rb_str_append(str, iseq_inspect(iseq));
- rb_str_catf(str, " (catch: %s)", body->catch_except_p ? "TRUE" : "FALSE");
+ rb_str_catf(str, " (catch: %s)", body->catch_except_p ? "true" : "false");
if ((l = RSTRING_LEN(str) - indent_len) < header_minlen) {
- rb_str_modify_expand(str, header_minlen - l);
- memset(RSTRING_END(str), '=', header_minlen - l);
+ rb_str_modify_expand(str, header_minlen - l);
+ memset(RSTRING_END(str), '=', header_minlen - l);
}
rb_str_cat2(str, "\n");
/* show catch table information */
if (body->catch_table) {
- rb_str_cat(str, indent_str, indent_len);
- rb_str_cat2(str, "== catch table\n");
+ rb_str_cat(str, indent_str, indent_len);
+ rb_str_cat2(str, "== catch table\n");
}
if (body->catch_table) {
- rb_str_cat_cstr(indent, "| ");
- indent_str = RSTRING_PTR(indent);
- for (i = 0; i < body->catch_table->size; i++) {
- const struct iseq_catch_table_entry *entry =
- UNALIGNED_MEMBER_PTR(body->catch_table, entries[i]);
- rb_str_cat(str, indent_str, indent_len);
- rb_str_catf(str,
- "| catch type: %-6s st: %04d ed: %04d sp: %04d cont: %04d\n",
- catch_type((int)entry->type), (int)entry->start,
- (int)entry->end, (int)entry->sp, (int)entry->cont);
- if (entry->iseq && !(done_iseq && st_is_member(done_iseq, (st_data_t)entry->iseq))) {
- rb_str_concat(str, rb_iseq_disasm_recursive(rb_iseq_check(entry->iseq), indent));
- if (!done_iseq) {
+ rb_str_cat_cstr(indent, "| ");
+ indent_str = RSTRING_PTR(indent);
+ for (i = 0; i < body->catch_table->size; i++) {
+ const struct iseq_catch_table_entry *entry =
+ UNALIGNED_MEMBER_PTR(body->catch_table, entries[i]);
+ rb_str_cat(str, indent_str, indent_len);
+ rb_str_catf(str,
+ "| catch type: %-6s st: %04d ed: %04d sp: %04d cont: %04d\n",
+ catch_type((int)entry->type), (int)entry->start,
+ (int)entry->end, (int)entry->sp, (int)entry->cont);
+ if (entry->iseq && !(done_iseq && st_is_member(done_iseq, (st_data_t)entry->iseq))) {
+ rb_str_concat(str, rb_iseq_disasm_recursive(rb_iseq_check(entry->iseq), indent));
+ if (!done_iseq) {
done_iseq = st_init_numtable();
done_iseq_wrapper = TypedData_Wrap_Struct(0, &tmp_set, done_iseq);
}
- st_insert(done_iseq, (st_data_t)entry->iseq, (st_data_t)0);
- indent_str = RSTRING_PTR(indent);
- }
- }
- rb_str_resize(indent, indent_len);
- indent_str = RSTRING_PTR(indent);
+ st_insert(done_iseq, (st_data_t)entry->iseq, (st_data_t)0);
+ indent_str = RSTRING_PTR(indent);
+ }
+ }
+ rb_str_resize(indent, indent_len);
+ indent_str = RSTRING_PTR(indent);
}
if (body->catch_table) {
- rb_str_cat(str, indent_str, indent_len);
- rb_str_cat2(str, "|-------------------------------------"
- "-----------------------------------\n");
+ rb_str_cat(str, indent_str, indent_len);
+ rb_str_cat2(str, "|-------------------------------------"
+ "-----------------------------------\n");
}
/* show local table information */
if (body->local_table) {
- const struct rb_iseq_param_keyword *const keyword = body->param.keyword;
- rb_str_cat(str, indent_str, indent_len);
- rb_str_catf(str,
- "local table (size: %d, argc: %d "
- "[opts: %d, rest: %d, post: %d, block: %d, kw: %d@%d, kwrest: %d])\n",
- body->local_table_size,
- body->param.lead_num,
- body->param.opt_num,
- body->param.flags.has_rest ? body->param.rest_start : -1,
- body->param.post_num,
- body->param.flags.has_block ? body->param.block_start : -1,
- body->param.flags.has_kw ? keyword->num : -1,
- body->param.flags.has_kw ? keyword->required_num : -1,
- body->param.flags.has_kwrest ? keyword->rest_start : -1);
-
- for (i = body->local_table_size; i > 0;) {
- int li = body->local_table_size - --i - 1;
- long width;
- VALUE name = local_var_name(iseq, 0, i);
+ const struct rb_iseq_param_keyword *const keyword = body->param.keyword;
+ rb_str_cat(str, indent_str, indent_len);
+ rb_str_catf(str,
+ "local table (size: %d, argc: %d "
+ "[opts: %d, rest: %d, post: %d, block: %d, kw: %d@%d, kwrest: %d])\n",
+ body->local_table_size,
+ body->param.lead_num,
+ body->param.opt_num,
+ body->param.flags.has_rest ? body->param.rest_start : -1,
+ body->param.post_num,
+ body->param.flags.has_block ? body->param.block_start : -1,
+ body->param.flags.has_kw ? keyword->num : -1,
+ body->param.flags.has_kw ? keyword->required_num : -1,
+ body->param.flags.has_kwrest ? keyword->rest_start : -1);
+
+ for (i = body->local_table_size; i > 0;) {
+ int li = body->local_table_size - --i - 1;
+ long width;
+ VALUE name = local_var_name(iseq, 0, i);
char argi[0x100];
char opti[0x100];
opti[0] = '\0';
- if (body->param.flags.has_opt) {
- int argc = body->param.lead_num;
- int opts = body->param.opt_num;
- if (li >= argc && li < argc + opts) {
- snprintf(opti, sizeof(opti), "Opt=%"PRIdVALUE,
- body->param.opt_table[li - argc]);
- }
- }
-
- snprintf(argi, sizeof(argi), "%s%s%s%s%s%s", /* arg, opts, rest, post, kwrest, block */
- body->param.lead_num > li ? "Arg" : "",
- opti,
- (body->param.flags.has_rest && body->param.rest_start == li) ? "Rest" : "",
- (body->param.flags.has_post && body->param.post_start <= li && li < body->param.post_start + body->param.post_num) ? "Post" : "",
- (body->param.flags.has_kwrest && keyword->rest_start == li) ? "Kwrest" : "",
- (body->param.flags.has_block && body->param.block_start == li) ? "Block" : "");
-
- rb_str_cat(str, indent_str, indent_len);
- rb_str_catf(str, "[%2d] ", i + 1);
- width = RSTRING_LEN(str) + 11;
- rb_str_append(str, name);
- if (*argi) rb_str_catf(str, "<%s>", argi);
- if ((width -= RSTRING_LEN(str)) > 0) rb_str_catf(str, "%*s", (int)width, "");
- }
- rb_str_cat_cstr(right_strip(str), "\n");
+ if (body->param.flags.has_opt) {
+ int argc = body->param.lead_num;
+ int opts = body->param.opt_num;
+ if (li >= argc && li < argc + opts) {
+ snprintf(opti, sizeof(opti), "Opt=%"PRIdVALUE,
+ body->param.opt_table[li - argc]);
+ }
+ }
+
+ snprintf(argi, sizeof(argi), "%s%s%s%s%s%s", /* arg, opts, rest, post, kwrest, block */
+ body->param.lead_num > li ? "Arg" : "",
+ opti,
+ (body->param.flags.has_rest && body->param.rest_start == li) ? "Rest" : "",
+ (body->param.flags.has_post && body->param.post_start <= li && li < body->param.post_start + body->param.post_num) ? "Post" : "",
+ (body->param.flags.has_kwrest && keyword->rest_start == li) ? "Kwrest" : "",
+ (body->param.flags.has_block && body->param.block_start == li) ? "Block" : "");
+
+ rb_str_cat(str, indent_str, indent_len);
+ rb_str_catf(str, "[%2d] ", i + 1);
+ width = RSTRING_LEN(str) + 11;
+ rb_str_append(str, name);
+ if (*argi) rb_str_catf(str, "<%s>", argi);
+ if ((width -= RSTRING_LEN(str)) > 0) rb_str_catf(str, "%*s", (int)width, "");
+ }
+ rb_str_cat_cstr(right_strip(str), "\n");
}
/* show each line */
code = rb_iseq_original_iseq(iseq);
for (n = 0; n < size;) {
- rb_str_cat(str, indent_str, indent_len);
- n += rb_iseq_disasm_insn(str, code, n, iseq, child);
+ rb_str_cat(str, indent_str, indent_len);
+ n += rb_iseq_disasm_insn(str, code, n, iseq, child);
}
for (l = 0; l < RARRAY_LEN(child); l++) {
- VALUE isv = rb_ary_entry(child, l);
- if (done_iseq && st_is_member(done_iseq, (st_data_t)isv)) continue;
- rb_str_cat_cstr(str, "\n");
- rb_str_concat(str, rb_iseq_disasm_recursive(rb_iseq_check((rb_iseq_t *)isv), indent));
- indent_str = RSTRING_PTR(indent);
+ VALUE isv = rb_ary_entry(child, l);
+ if (done_iseq && st_is_member(done_iseq, (st_data_t)isv)) continue;
+ rb_str_cat_cstr(str, "\n");
+ rb_str_concat(str, rb_iseq_disasm_recursive(rb_iseq_check((rb_iseq_t *)isv), indent));
+ indent_str = RSTRING_PTR(indent);
}
RB_GC_GUARD(done_iseq_wrapper);
@@ -2533,6 +2534,34 @@ rb_iseq_disasm(const rb_iseq_t *iseq)
}
/*
+ * Estimates the number of instance variables that will be set on
+ * a given `class` with the initialize method defined in
+ * `initialize_iseq`
+ */
+attr_index_t
+rb_estimate_iv_count(VALUE klass, const rb_iseq_t * initialize_iseq)
+{
+ struct rb_id_table * iv_names = rb_id_table_create(0);
+
+ for (unsigned int i = 0; i < ISEQ_BODY(initialize_iseq)->ivc_size; i++) {
+ IVC cache = (IVC)&ISEQ_BODY(initialize_iseq)->is_entries[i];
+
+ if (cache->iv_set_name) {
+ rb_id_table_insert(iv_names, cache->iv_set_name, Qtrue);
+ }
+ }
+
+ attr_index_t count = (attr_index_t)rb_id_table_size(iv_names);
+
+ VALUE superclass = rb_class_superclass(klass);
+ count += RCLASS_EXT(superclass)->max_iv_count;
+
+ rb_id_table_free(iv_names);
+
+ return count;
+}
+
+/*
* call-seq:
* iseq.disasm -> str
* iseq.disassemble -> str
@@ -2658,10 +2687,10 @@ iseqw_trace_points(VALUE self)
VALUE ary = rb_ary_new();
for (i=0; i<body->insns_info.size; i++) {
- const struct iseq_insn_info_entry *entry = &body->insns_info.body[i];
- if (entry->events) {
- push_event_info(iseq, entry->events, entry->line_no, ary);
- }
+ const struct iseq_insn_info_entry *entry = &body->insns_info.body[i];
+ if (entry->events) {
+ push_event_info(iseq, entry->events, entry->line_no, ary);
+ }
}
return ary;
}
@@ -2788,8 +2817,8 @@ ruby_node_name(int node)
switch (node) {
#include "node_name.inc"
default:
- rb_bug("unknown node: %d", node);
- return 0;
+ rb_bug("unknown node: %d", node);
+ return 0;
}
}
@@ -2813,7 +2842,7 @@ exception_type2symbol(VALUE type)
case CATCH_TYPE_REDO: CONST_ID(id, "redo"); break;
case CATCH_TYPE_NEXT: CONST_ID(id, "next"); break;
default:
- rb_bug("unknown exception type: %d", (int)type);
+ rb_bug("unknown exception type: %d", (int)type);
}
return ID2SYM(id);
}
@@ -2839,7 +2868,7 @@ static const rb_data_type_t label_wrapper = {
id_##name = rb_intern(#name)
static VALUE
-iseq_type_id(enum iseq_type type)
+iseq_type_id(enum rb_iseq_type type)
{
DECL_ID(top);
DECL_ID(method);
@@ -2914,170 +2943,179 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
/* locals */
for (i=0; i<iseq_body->local_table_size; i++) {
- ID lid = iseq_body->local_table[i];
- if (lid) {
- if (rb_id2str(lid)) {
- rb_ary_push(locals, ID2SYM(lid));
- }
- else { /* hidden variable from id_internal() */
- rb_ary_push(locals, ULONG2NUM(iseq_body->local_table_size-i+1));
- }
- }
- else {
- rb_ary_push(locals, ID2SYM(rb_intern("#arg_rest")));
- }
+ ID lid = iseq_body->local_table[i];
+ if (lid) {
+ if (rb_id2str(lid)) {
+ rb_ary_push(locals, ID2SYM(lid));
+ }
+ else { /* hidden variable from id_internal() */
+ rb_ary_push(locals, ULONG2NUM(iseq_body->local_table_size-i+1));
+ }
+ }
+ else {
+ rb_ary_push(locals, ID2SYM(rb_intern("#arg_rest")));
+ }
}
/* params */
{
- const struct rb_iseq_param_keyword *const keyword = iseq_body->param.keyword;
- int j;
-
- if (iseq_body->param.flags.has_opt) {
- int len = iseq_body->param.opt_num + 1;
- VALUE arg_opt_labels = rb_ary_new2(len);
-
- for (j = 0; j < len; j++) {
- VALUE l = register_label(labels_table, iseq_body->param.opt_table[j]);
- rb_ary_push(arg_opt_labels, l);
- }
- rb_hash_aset(params, ID2SYM(rb_intern("opt")), arg_opt_labels);
- }
-
- /* commit */
- if (iseq_body->param.flags.has_lead) rb_hash_aset(params, ID2SYM(rb_intern("lead_num")), INT2FIX(iseq_body->param.lead_num));
- if (iseq_body->param.flags.has_post) rb_hash_aset(params, ID2SYM(rb_intern("post_num")), INT2FIX(iseq_body->param.post_num));
- if (iseq_body->param.flags.has_post) rb_hash_aset(params, ID2SYM(rb_intern("post_start")), INT2FIX(iseq_body->param.post_start));
- if (iseq_body->param.flags.has_rest) rb_hash_aset(params, ID2SYM(rb_intern("rest_start")), INT2FIX(iseq_body->param.rest_start));
- if (iseq_body->param.flags.has_block) rb_hash_aset(params, ID2SYM(rb_intern("block_start")), INT2FIX(iseq_body->param.block_start));
- if (iseq_body->param.flags.has_kw) {
- VALUE keywords = rb_ary_new();
- int i, j;
- for (i=0; i<keyword->required_num; i++) {
- rb_ary_push(keywords, ID2SYM(keyword->table[i]));
- }
- for (j=0; i<keyword->num; i++, j++) {
- VALUE key = rb_ary_new_from_args(1, ID2SYM(keyword->table[i]));
- if (keyword->default_values[j] != Qundef) {
- rb_ary_push(key, keyword->default_values[j]);
- }
- rb_ary_push(keywords, key);
- }
-
- rb_hash_aset(params, ID2SYM(rb_intern("kwbits")),
- INT2FIX(keyword->bits_start));
- rb_hash_aset(params, ID2SYM(rb_intern("keyword")), keywords);
- }
- if (iseq_body->param.flags.has_kwrest) rb_hash_aset(params, ID2SYM(rb_intern("kwrest")), INT2FIX(keyword->rest_start));
- if (iseq_body->param.flags.ambiguous_param0) rb_hash_aset(params, ID2SYM(rb_intern("ambiguous_param0")), Qtrue);
+ const struct rb_iseq_param_keyword *const keyword = iseq_body->param.keyword;
+ int j;
+
+ if (iseq_body->param.flags.has_opt) {
+ int len = iseq_body->param.opt_num + 1;
+ VALUE arg_opt_labels = rb_ary_new2(len);
+
+ for (j = 0; j < len; j++) {
+ VALUE l = register_label(labels_table, iseq_body->param.opt_table[j]);
+ rb_ary_push(arg_opt_labels, l);
+ }
+ rb_hash_aset(params, ID2SYM(rb_intern("opt")), arg_opt_labels);
+ }
+
+ /* commit */
+ if (iseq_body->param.flags.has_lead) rb_hash_aset(params, ID2SYM(rb_intern("lead_num")), INT2FIX(iseq_body->param.lead_num));
+ if (iseq_body->param.flags.has_post) rb_hash_aset(params, ID2SYM(rb_intern("post_num")), INT2FIX(iseq_body->param.post_num));
+ if (iseq_body->param.flags.has_post) rb_hash_aset(params, ID2SYM(rb_intern("post_start")), INT2FIX(iseq_body->param.post_start));
+ if (iseq_body->param.flags.has_rest) rb_hash_aset(params, ID2SYM(rb_intern("rest_start")), INT2FIX(iseq_body->param.rest_start));
+ if (iseq_body->param.flags.has_block) rb_hash_aset(params, ID2SYM(rb_intern("block_start")), INT2FIX(iseq_body->param.block_start));
+ if (iseq_body->param.flags.has_kw) {
+ VALUE keywords = rb_ary_new();
+ int i, j;
+ for (i=0; i<keyword->required_num; i++) {
+ rb_ary_push(keywords, ID2SYM(keyword->table[i]));
+ }
+ for (j=0; i<keyword->num; i++, j++) {
+ VALUE key = rb_ary_new_from_args(1, ID2SYM(keyword->table[i]));
+ if (!UNDEF_P(keyword->default_values[j])) {
+ rb_ary_push(key, keyword->default_values[j]);
+ }
+ rb_ary_push(keywords, key);
+ }
+
+ rb_hash_aset(params, ID2SYM(rb_intern("kwbits")),
+ INT2FIX(keyword->bits_start));
+ rb_hash_aset(params, ID2SYM(rb_intern("keyword")), keywords);
+ }
+ if (iseq_body->param.flags.has_kwrest) rb_hash_aset(params, ID2SYM(rb_intern("kwrest")), INT2FIX(keyword->rest_start));
+ if (iseq_body->param.flags.ambiguous_param0) rb_hash_aset(params, ID2SYM(rb_intern("ambiguous_param0")), Qtrue);
}
/* body */
iseq_original = rb_iseq_original_iseq((rb_iseq_t *)iseq);
for (seq = iseq_original; seq < iseq_original + iseq_body->iseq_size; ) {
- VALUE insn = *seq++;
- int j, len = insn_len(insn);
- VALUE *nseq = seq + len - 1;
- VALUE ary = rb_ary_new2(len);
+ VALUE insn = *seq++;
+ int j, len = insn_len(insn);
+ VALUE *nseq = seq + len - 1;
+ VALUE ary = rb_ary_new2(len);
rb_ary_push(ary, ID2SYM(insn_syms[insn%numberof(insn_syms)]));
- for (j=0; j<len-1; j++, seq++) {
- enum ruby_insn_type_chars op_type = insn_op_type(insn, j);
-
- switch (op_type) {
- case TS_OFFSET: {
- unsigned long idx = nseq - iseq_original + *seq;
- rb_ary_push(ary, register_label(labels_table, idx));
- break;
- }
- case TS_LINDEX:
- case TS_NUM:
- rb_ary_push(ary, INT2FIX(*seq));
- break;
- case TS_VALUE:
- rb_ary_push(ary, obj_resurrect(*seq));
- break;
- case TS_ISEQ:
- {
- const rb_iseq_t *iseq = (rb_iseq_t *)*seq;
- if (iseq) {
- VALUE val = iseq_data_to_ary(rb_iseq_check(iseq));
- rb_ary_push(ary, val);
- }
- else {
- rb_ary_push(ary, Qnil);
- }
- }
- break;
- case TS_IC:
+ for (j=0; j<len-1; j++, seq++) {
+ enum ruby_insn_type_chars op_type = insn_op_type(insn, j);
+
+ switch (op_type) {
+ case TS_OFFSET: {
+ unsigned long idx = nseq - iseq_original + *seq;
+ rb_ary_push(ary, register_label(labels_table, idx));
+ break;
+ }
+ case TS_LINDEX:
+ case TS_NUM:
+ rb_ary_push(ary, INT2FIX(*seq));
+ break;
+ case TS_VALUE:
+ rb_ary_push(ary, obj_resurrect(*seq));
+ break;
+ case TS_ISEQ:
+ {
+ const rb_iseq_t *iseq = (rb_iseq_t *)*seq;
+ if (iseq) {
+ VALUE val = iseq_data_to_ary(rb_iseq_check(iseq));
+ rb_ary_push(ary, val);
+ }
+ else {
+ rb_ary_push(ary, Qnil);
+ }
+ }
+ break;
+ case TS_IC:
+ {
+ VALUE list = rb_ary_new();
+ const ID *ids = ((IC)*seq)->segments;
+ while (*ids) {
+ rb_ary_push(list, ID2SYM(*ids++));
+ }
+ rb_ary_push(ary, list);
+ }
+ break;
case TS_IVC:
case TS_ICVARC:
- case TS_ISE:
- {
- union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)*seq;
- rb_ary_push(ary, INT2FIX(is - ISEQ_IS_ENTRY_START(ISEQ_BODY(iseq), op_type)));
- }
- break;
+ case TS_ISE:
+ {
+ union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)*seq;
+ rb_ary_push(ary, INT2FIX(is - ISEQ_IS_ENTRY_START(ISEQ_BODY(iseq), op_type)));
+ }
+ break;
case TS_CALLDATA:
- {
+ {
struct rb_call_data *cd = (struct rb_call_data *)*seq;
const struct rb_callinfo *ci = cd->ci;
- VALUE e = rb_hash_new();
+ VALUE e = rb_hash_new();
int argc = vm_ci_argc(ci);
ID mid = vm_ci_mid(ci);
- rb_hash_aset(e, ID2SYM(rb_intern("mid")), mid ? ID2SYM(mid) : Qnil);
- rb_hash_aset(e, ID2SYM(rb_intern("flag")), UINT2NUM(vm_ci_flag(ci)));
+ rb_hash_aset(e, ID2SYM(rb_intern("mid")), mid ? ID2SYM(mid) : Qnil);
+ rb_hash_aset(e, ID2SYM(rb_intern("flag")), UINT2NUM(vm_ci_flag(ci)));
if (vm_ci_flag(ci) & VM_CALL_KWARG) {
const struct rb_callinfo_kwarg *kwarg = vm_ci_kwarg(ci);
int i;
- VALUE kw = rb_ary_new2((long)kwarg->keyword_len);
+ VALUE kw = rb_ary_new2((long)kwarg->keyword_len);
- argc -= kwarg->keyword_len;
+ argc -= kwarg->keyword_len;
for (i = 0; i < kwarg->keyword_len; i++) {
- rb_ary_push(kw, kwarg->keywords[i]);
- }
- rb_hash_aset(e, ID2SYM(rb_intern("kw_arg")), kw);
- }
-
- rb_hash_aset(e, ID2SYM(rb_intern("orig_argc")),
- INT2FIX(argc));
- rb_ary_push(ary, e);
- }
- break;
- case TS_ID:
- rb_ary_push(ary, ID2SYM(*seq));
- break;
- case TS_CDHASH:
- {
- VALUE hash = *seq;
- VALUE val = rb_ary_new();
- int i;
-
- rb_hash_foreach(hash, cdhash_each, val);
-
- for (i=0; i<RARRAY_LEN(val); i+=2) {
- VALUE pos = FIX2INT(rb_ary_entry(val, i+1));
- unsigned long idx = nseq - iseq_original + pos;
-
- rb_ary_store(val, i+1,
- register_label(labels_table, idx));
- }
- rb_ary_push(ary, val);
- }
- break;
- case TS_FUNCPTR:
- {
+ rb_ary_push(kw, kwarg->keywords[i]);
+ }
+ rb_hash_aset(e, ID2SYM(rb_intern("kw_arg")), kw);
+ }
+
+ rb_hash_aset(e, ID2SYM(rb_intern("orig_argc")),
+ INT2FIX(argc));
+ rb_ary_push(ary, e);
+ }
+ break;
+ case TS_ID:
+ rb_ary_push(ary, ID2SYM(*seq));
+ break;
+ case TS_CDHASH:
+ {
+ VALUE hash = *seq;
+ VALUE val = rb_ary_new();
+ int i;
+
+ rb_hash_foreach(hash, cdhash_each, val);
+
+ for (i=0; i<RARRAY_LEN(val); i+=2) {
+ VALUE pos = FIX2INT(rb_ary_entry(val, i+1));
+ unsigned long idx = nseq - iseq_original + pos;
+
+ rb_ary_store(val, i+1,
+ register_label(labels_table, idx));
+ }
+ rb_ary_push(ary, val);
+ }
+ break;
+ case TS_FUNCPTR:
+ {
#if SIZEOF_VALUE <= SIZEOF_LONG
- VALUE val = LONG2NUM((SIGNED_VALUE)*seq);
+ VALUE val = LONG2NUM((SIGNED_VALUE)*seq);
#else
- VALUE val = LL2NUM((SIGNED_VALUE)*seq);
+ VALUE val = LL2NUM((SIGNED_VALUE)*seq);
#endif
- rb_ary_push(ary, val);
- }
- break;
+ rb_ary_push(ary, val);
+ }
+ break;
case TS_BUILTIN:
{
VALUE val = rb_hash_new();
@@ -3093,32 +3131,32 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
rb_ary_push(ary, val);
}
break;
- default:
- rb_bug("unknown operand: %c", insn_op_type(insn, j));
- }
- }
- rb_ary_push(body, ary);
+ default:
+ rb_bug("unknown operand: %c", insn_op_type(insn, j));
+ }
+ }
+ rb_ary_push(body, ary);
}
nbody = body;
/* exception */
if (iseq_body->catch_table) for (i=0; i<iseq_body->catch_table->size; i++) {
- VALUE ary = rb_ary_new();
- const struct iseq_catch_table_entry *entry =
- UNALIGNED_MEMBER_PTR(iseq_body->catch_table, entries[i]);
- rb_ary_push(ary, exception_type2symbol(entry->type));
- if (entry->iseq) {
- rb_ary_push(ary, iseq_data_to_ary(rb_iseq_check(entry->iseq)));
- }
- else {
- rb_ary_push(ary, Qnil);
- }
- rb_ary_push(ary, register_label(labels_table, entry->start));
- rb_ary_push(ary, register_label(labels_table, entry->end));
- rb_ary_push(ary, register_label(labels_table, entry->cont));
- rb_ary_push(ary, UINT2NUM(entry->sp));
- rb_ary_push(exception, ary);
+ VALUE ary = rb_ary_new();
+ const struct iseq_catch_table_entry *entry =
+ UNALIGNED_MEMBER_PTR(iseq_body->catch_table, entries[i]);
+ rb_ary_push(ary, exception_type2symbol(entry->type));
+ if (entry->iseq) {
+ rb_ary_push(ary, iseq_data_to_ary(rb_iseq_check(entry->iseq)));
+ }
+ else {
+ rb_ary_push(ary, Qnil);
+ }
+ rb_ary_push(ary, register_label(labels_table, entry->start));
+ rb_ary_push(ary, register_label(labels_table, entry->end));
+ rb_ary_push(ary, register_label(labels_table, entry->cont));
+ rb_ary_push(ary, UINT2NUM(entry->sp));
+ rb_ary_push(exception, ary);
}
/* make body with labels and insert line number */
@@ -3129,41 +3167,41 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
#endif
for (l=0, pos=0; l<RARRAY_LEN(nbody); l++) {
- const struct iseq_insn_info_entry *info;
- VALUE ary = RARRAY_AREF(nbody, l);
- st_data_t label;
+ const struct iseq_insn_info_entry *info;
+ VALUE ary = RARRAY_AREF(nbody, l);
+ st_data_t label;
- if (st_lookup(labels_table, pos, &label)) {
- rb_ary_push(body, (VALUE)label);
- }
+ if (st_lookup(labels_table, pos, &label)) {
+ rb_ary_push(body, (VALUE)label);
+ }
- info = get_insn_info(iseq, pos);
+ info = get_insn_info(iseq, pos);
#ifdef USE_ISEQ_NODE_ID
rb_ary_push(node_ids, INT2FIX(info->node_id));
#endif
- if (prev_insn_info != info) {
- int line = info->line_no;
- rb_event_flag_t events = info->events;
+ if (prev_insn_info != info) {
+ int line = info->line_no;
+ rb_event_flag_t events = info->events;
- if (line > 0 && last_line != line) {
- rb_ary_push(body, INT2FIX(line));
- last_line = line;
- }
+ if (line > 0 && last_line != line) {
+ rb_ary_push(body, INT2FIX(line));
+ last_line = line;
+ }
#define CHECK_EVENT(ev) if (events & ev) rb_ary_push(body, ID2SYM(rb_intern(#ev)));
- CHECK_EVENT(RUBY_EVENT_LINE);
- CHECK_EVENT(RUBY_EVENT_CLASS);
- CHECK_EVENT(RUBY_EVENT_END);
- CHECK_EVENT(RUBY_EVENT_CALL);
- CHECK_EVENT(RUBY_EVENT_RETURN);
- CHECK_EVENT(RUBY_EVENT_B_CALL);
- CHECK_EVENT(RUBY_EVENT_B_RETURN);
+ CHECK_EVENT(RUBY_EVENT_LINE);
+ CHECK_EVENT(RUBY_EVENT_CLASS);
+ CHECK_EVENT(RUBY_EVENT_END);
+ CHECK_EVENT(RUBY_EVENT_CALL);
+ CHECK_EVENT(RUBY_EVENT_RETURN);
+ CHECK_EVENT(RUBY_EVENT_B_CALL);
+ CHECK_EVENT(RUBY_EVENT_B_RETURN);
#undef CHECK_EVENT
- prev_insn_info = info;
- }
+ prev_insn_info = info;
+ }
- rb_ary_push(body, ary);
- pos += RARRAY_LENINT(ary); /* reject too huge data */
+ rb_ary_push(body, ary);
+ pos += RARRAY_LENINT(ary); /* reject too huge data */
}
RB_GC_GUARD(nbody);
RB_GC_GUARD(labels_wrapper);
@@ -3173,11 +3211,11 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
rb_hash_aset(misc, ID2SYM(rb_intern("stack_max")), INT2FIX(iseq_body->stack_max));
rb_hash_aset(misc, ID2SYM(rb_intern("node_id")), INT2FIX(iseq_body->location.node_id));
rb_hash_aset(misc, ID2SYM(rb_intern("code_location")),
- rb_ary_new_from_args(4,
- INT2FIX(iseq_body->location.code_location.beg_pos.lineno),
- INT2FIX(iseq_body->location.code_location.beg_pos.column),
- INT2FIX(iseq_body->location.code_location.end_pos.lineno),
- INT2FIX(iseq_body->location.code_location.end_pos.column)));
+ rb_ary_new_from_args(4,
+ INT2FIX(iseq_body->location.code_location.beg_pos.lineno),
+ INT2FIX(iseq_body->location.code_location.beg_pos.column),
+ INT2FIX(iseq_body->location.code_location.end_pos.lineno),
+ INT2FIX(iseq_body->location.code_location.end_pos.column)));
#ifdef USE_ISEQ_NODE_ID
rb_hash_aset(misc, ID2SYM(rb_intern("node_ids")), node_ids);
#endif
@@ -3195,7 +3233,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
rb_ary_push(val, iseq_body->location.label);
rb_ary_push(val, rb_iseq_path(iseq));
rb_ary_push(val, rb_iseq_realpath(iseq));
- rb_ary_push(val, iseq_body->location.first_lineno);
+ rb_ary_push(val, RB_INT2NUM(iseq_body->location.first_lineno));
rb_ary_push(val, ID2SYM(type));
rb_ary_push(val, locals);
rb_ary_push(val, params);
@@ -3215,81 +3253,81 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
#define PARAM_TYPE(type) rb_ary_push(a = rb_ary_new2(2), ID2SYM(type))
#define PARAM_ID(i) body->local_table[(i)]
#define PARAM(i, type) ( \
- PARAM_TYPE(type), \
- rb_id2str(PARAM_ID(i)) ? \
- rb_ary_push(a, ID2SYM(PARAM_ID(i))) : \
- a)
+ PARAM_TYPE(type), \
+ rb_id2str(PARAM_ID(i)) ? \
+ rb_ary_push(a, ID2SYM(PARAM_ID(i))) : \
+ a)
CONST_ID(req, "req");
CONST_ID(opt, "opt");
if (is_proc) {
- for (i = 0; i < body->param.lead_num; i++) {
- PARAM_TYPE(opt);
- rb_ary_push(a, rb_id2str(PARAM_ID(i)) ? ID2SYM(PARAM_ID(i)) : Qnil);
- rb_ary_push(args, a);
- }
+ for (i = 0; i < body->param.lead_num; i++) {
+ PARAM_TYPE(opt);
+ rb_ary_push(a, rb_id2str(PARAM_ID(i)) ? ID2SYM(PARAM_ID(i)) : Qnil);
+ rb_ary_push(args, a);
+ }
}
else {
- for (i = 0; i < body->param.lead_num; i++) {
- rb_ary_push(args, PARAM(i, req));
- }
+ for (i = 0; i < body->param.lead_num; i++) {
+ rb_ary_push(args, PARAM(i, req));
+ }
}
r = body->param.lead_num + body->param.opt_num;
for (; i < r; i++) {
- PARAM_TYPE(opt);
- if (rb_id2str(PARAM_ID(i))) {
- rb_ary_push(a, ID2SYM(PARAM_ID(i)));
- }
- rb_ary_push(args, a);
+ PARAM_TYPE(opt);
+ if (rb_id2str(PARAM_ID(i))) {
+ rb_ary_push(a, ID2SYM(PARAM_ID(i)));
+ }
+ rb_ary_push(args, a);
}
if (body->param.flags.has_rest) {
- CONST_ID(rest, "rest");
- rb_ary_push(args, PARAM(body->param.rest_start, rest));
+ CONST_ID(rest, "rest");
+ rb_ary_push(args, PARAM(body->param.rest_start, rest));
}
r = body->param.post_start + body->param.post_num;
if (is_proc) {
- for (i = body->param.post_start; i < r; i++) {
- PARAM_TYPE(opt);
- rb_ary_push(a, rb_id2str(PARAM_ID(i)) ? ID2SYM(PARAM_ID(i)) : Qnil);
- rb_ary_push(args, a);
- }
+ for (i = body->param.post_start; i < r; i++) {
+ PARAM_TYPE(opt);
+ rb_ary_push(a, rb_id2str(PARAM_ID(i)) ? ID2SYM(PARAM_ID(i)) : Qnil);
+ rb_ary_push(args, a);
+ }
}
else {
- for (i = body->param.post_start; i < r; i++) {
- rb_ary_push(args, PARAM(i, req));
- }
+ for (i = body->param.post_start; i < r; i++) {
+ rb_ary_push(args, PARAM(i, req));
+ }
}
if (body->param.flags.accepts_no_kwarg) {
- ID nokey;
- CONST_ID(nokey, "nokey");
- PARAM_TYPE(nokey);
- rb_ary_push(args, a);
+ ID nokey;
+ CONST_ID(nokey, "nokey");
+ PARAM_TYPE(nokey);
+ rb_ary_push(args, a);
}
if (body->param.flags.has_kw) {
- i = 0;
- if (keyword->required_num > 0) {
- ID keyreq;
- CONST_ID(keyreq, "keyreq");
- for (; i < keyword->required_num; i++) {
- PARAM_TYPE(keyreq);
- if (rb_id2str(keyword->table[i])) {
- rb_ary_push(a, ID2SYM(keyword->table[i]));
- }
- rb_ary_push(args, a);
- }
- }
- CONST_ID(key, "key");
- for (; i < keyword->num; i++) {
- PARAM_TYPE(key);
- if (rb_id2str(keyword->table[i])) {
- rb_ary_push(a, ID2SYM(keyword->table[i]));
- }
- rb_ary_push(args, a);
- }
+ i = 0;
+ if (keyword->required_num > 0) {
+ ID keyreq;
+ CONST_ID(keyreq, "keyreq");
+ for (; i < keyword->required_num; i++) {
+ PARAM_TYPE(keyreq);
+ if (rb_id2str(keyword->table[i])) {
+ rb_ary_push(a, ID2SYM(keyword->table[i]));
+ }
+ rb_ary_push(args, a);
+ }
+ }
+ CONST_ID(key, "key");
+ for (; i < keyword->num; i++) {
+ PARAM_TYPE(key);
+ if (rb_id2str(keyword->table[i])) {
+ rb_ary_push(a, ID2SYM(keyword->table[i]));
+ }
+ rb_ary_push(args, a);
+ }
}
if (body->param.flags.has_kwrest || body->param.flags.ruby2_keywords) {
ID param;
- CONST_ID(keyrest, "keyrest");
+ CONST_ID(keyrest, "keyrest");
PARAM_TYPE(keyrest);
if (body->param.flags.has_kwrest &&
rb_id2str(param = PARAM_ID(keyword->rest_start))) {
@@ -3298,11 +3336,11 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
else if (body->param.flags.ruby2_keywords) {
rb_ary_push(a, ID2SYM(idPow));
}
- rb_ary_push(args, a);
+ rb_ary_push(args, a);
}
if (body->param.flags.has_block) {
- CONST_ID(block, "block");
- rb_ary_push(args, PARAM(body->param.block_start, block));
+ CONST_ID(block, "block");
+ rb_ary_push(args, PARAM(body->param.block_start, block));
}
return args;
}
@@ -3311,20 +3349,20 @@ VALUE
rb_iseq_defined_string(enum defined_type type)
{
static const char expr_names[][18] = {
- "nil",
- "instance-variable",
- "local-variable",
- "global-variable",
- "class variable",
- "constant",
- "method",
- "yield",
- "super",
- "self",
- "true",
- "false",
- "assignment",
- "expression",
+ "nil",
+ "instance-variable",
+ "local-variable",
+ "global-variable",
+ "class variable",
+ "constant",
+ "method",
+ "yield",
+ "super",
+ "self",
+ "true",
+ "false",
+ "assignment",
+ "expression",
};
const char *estr;
@@ -3352,7 +3390,7 @@ rb_vm_encoded_insn_data_table_init(void)
const void * const *table = rb_vm_get_insns_address_table();
#define INSN_CODE(insn) ((VALUE)table[insn])
#else
-#define INSN_CODE(insn) (insn)
+#define INSN_CODE(insn) ((VALUE)(insn))
#endif
st_data_t insn;
encoded_insn_data = st_init_numtable_with_size(VM_INSTRUCTION_SIZE / 2);
@@ -3594,17 +3632,17 @@ void
rb_iseq_trace_set(const rb_iseq_t *iseq, rb_event_flag_t turnon_events)
{
if (iseq->aux.exec.global_trace_events == turnon_events) {
- return;
+ return;
}
if (!ISEQ_EXECUTABLE_P(iseq)) {
- /* this is building ISeq */
- return;
+ /* this is building ISeq */
+ return;
}
else {
unsigned int pc;
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
- VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
+ VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
rb_event_flag_t enabled_events;
rb_event_flag_t local_events = iseq->aux.exec.local_hooks ? iseq->aux.exec.local_hooks->events : 0;
((rb_iseq_t *)iseq)->aux.exec.global_trace_events = turnon_events;
@@ -3613,7 +3651,7 @@ rb_iseq_trace_set(const rb_iseq_t *iseq, rb_event_flag_t turnon_events)
for (pc=0; pc<body->iseq_size;) {
rb_event_flag_t pc_events = rb_iseq_event_flags(iseq, pc);
pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & enabled_events, true);
- }
+ }
}
}
@@ -3653,9 +3691,9 @@ trace_set_i(void *vstart, void *vend, size_t stride, void *data)
void *ptr = asan_poisoned_object_p(v);
asan_unpoison_object(v, false);
- if (rb_obj_is_iseq(v)) {
- rb_iseq_trace_set(rb_iseq_check((rb_iseq_t *)v), turnon_events);
- }
+ if (rb_obj_is_iseq(v)) {
+ rb_iseq_trace_set(rb_iseq_check((rb_iseq_t *)v), turnon_events);
+ }
else if (imemo_type_p(v, imemo_callcache) && rb_vm_call_ivar_attrset_p(((const struct rb_callcache *)v)->call_)) {
rb_vm_cc_general((struct rb_callcache *)v);
}
@@ -3770,9 +3808,9 @@ iseqw_s_load_from_binary_extra_data(VALUE self, VALUE str)
struct succ_index_table {
uint64_t imm_part[IMMEDIATE_TABLE_SIZE / 9];
struct succ_dict_block {
- unsigned int rank;
- uint64_t small_block_ranks; /* 9 bits * 7 = 63 bits */
- uint64_t bits[512/64];
+ unsigned int rank;
+ uint64_t small_block_ranks; /* 9 bits * 7 = 63 bits */
+ uint64_t bits[512/64];
} succ_part[FLEX_ARY_LEN];
};
@@ -3794,27 +3832,27 @@ succ_index_table_create(int max_pos, int *data, int size)
r = 0;
for (j = 0; j < imm_size; j++) {
- for (i = 0; i < 9; i++) {
- if (r < size && data[r] == j * 9 + i) r++;
- imm_block_rank_set(sd->imm_part[j], i, r);
- }
+ for (i = 0; i < 9; i++) {
+ if (r < size && data[r] == j * 9 + i) r++;
+ imm_block_rank_set(sd->imm_part[j], i, r);
+ }
}
for (k = 0; k < succ_size; k++) {
- struct succ_dict_block *sd_block = &sd->succ_part[k];
- int small_rank = 0;
- sd_block->rank = r;
- for (j = 0; j < 8; j++) {
- uint64_t bits = 0;
- if (j) small_block_rank_set(sd_block->small_block_ranks, j, small_rank);
- for (i = 0; i < 64; i++) {
- if (r < size && data[r] == k * 512 + j * 64 + i + IMMEDIATE_TABLE_SIZE) {
- bits |= ((uint64_t)1) << i;
- r++;
- }
- }
- sd_block->bits[j] = bits;
- small_rank += rb_popcount64(bits);
- }
+ struct succ_dict_block *sd_block = &sd->succ_part[k];
+ int small_rank = 0;
+ sd_block->rank = r;
+ for (j = 0; j < 8; j++) {
+ uint64_t bits = 0;
+ if (j) small_block_rank_set(sd_block->small_block_ranks, j, small_rank);
+ for (i = 0; i < 64; i++) {
+ if (r < size && data[r] == k * 512 + j * 64 + i + IMMEDIATE_TABLE_SIZE) {
+ bits |= ((uint64_t)1) << i;
+ r++;
+ }
+ }
+ sd_block->bits[j] = bits;
+ small_rank += rb_popcount64(bits);
+ }
}
return sd;
}
@@ -3828,20 +3866,20 @@ succ_index_table_invert(int max_pos, struct succ_index_table *sd, int size)
int i, j, k, r = -1;
p = positions;
for (j = 0; j < imm_size; j++) {
- for (i = 0; i < 9; i++) {
- int nr = imm_block_rank_get(sd->imm_part[j], i);
- if (r != nr) *p++ = j * 9 + i;
- r = nr;
- }
+ for (i = 0; i < 9; i++) {
+ int nr = imm_block_rank_get(sd->imm_part[j], i);
+ if (r != nr) *p++ = j * 9 + i;
+ r = nr;
+ }
}
for (k = 0; k < succ_size; k++) {
- for (j = 0; j < 8; j++) {
- for (i = 0; i < 64; i++) {
- if (sd->succ_part[k].bits[j] & (((uint64_t)1) << i)) {
- *p++ = k * 512 + j * 64 + i + IMMEDIATE_TABLE_SIZE;
- }
- }
- }
+ for (j = 0; j < 8; j++) {
+ for (i = 0; i < 64; i++) {
+ if (sd->succ_part[k].bits[j] & (((uint64_t)1) << i)) {
+ *p++ = k * 512 + j * 64 + i + IMMEDIATE_TABLE_SIZE;
+ }
+ }
+ }
}
return positions;
}
@@ -3850,19 +3888,19 @@ static int
succ_index_lookup(const struct succ_index_table *sd, int x)
{
if (x < IMMEDIATE_TABLE_SIZE) {
- const int i = x / 9;
- const int j = x % 9;
- return imm_block_rank_get(sd->imm_part[i], j);
+ const int i = x / 9;
+ const int j = x % 9;
+ return imm_block_rank_get(sd->imm_part[i], j);
}
else {
- const int block_index = (x - IMMEDIATE_TABLE_SIZE) / 512;
- const struct succ_dict_block *block = &sd->succ_part[block_index];
- const int block_bit_index = (x - IMMEDIATE_TABLE_SIZE) % 512;
- const int small_block_index = block_bit_index / 64;
- const int small_block_popcount = small_block_rank_get(block->small_block_ranks, small_block_index);
- const int popcnt = rb_popcount64(block->bits[small_block_index] << (63 - block_bit_index % 64));
-
- return block->rank + small_block_popcount + popcnt;
+ const int block_index = (x - IMMEDIATE_TABLE_SIZE) / 512;
+ const struct succ_dict_block *block = &sd->succ_part[block_index];
+ const int block_bit_index = (x - IMMEDIATE_TABLE_SIZE) % 512;
+ const int small_block_index = block_bit_index / 64;
+ const int small_block_popcount = small_block_rank_get(block->small_block_ranks, small_block_index);
+ const int popcnt = rb_popcount64(block->bits[small_block_index] << (63 - block_bit_index % 64));
+
+ return block->rank + small_block_popcount + popcnt;
}
}
#endif
diff --git a/iseq.h b/iseq.h
index 4c6d9f6597..2f83e7336d 100644
--- a/iseq.h
+++ b/iseq.h
@@ -31,6 +31,7 @@ RUBY_EXTERN const int ruby_api_version[];
typedef struct rb_iseq_struct rb_iseq_t;
#define rb_iseq_t rb_iseq_t
#endif
+typedef void (*rb_iseq_callback)(const rb_iseq_t *, void *);
extern const ID rb_iseq_shared_exc_local_tbl[];
@@ -76,14 +77,14 @@ ISEQ_ORIGINAL_ISEQ_ALLOC(const rb_iseq_t *iseq, long size)
}
#define ISEQ_TRACE_EVENTS (RUBY_EVENT_LINE | \
- RUBY_EVENT_CLASS | \
- RUBY_EVENT_END | \
- RUBY_EVENT_CALL | \
- RUBY_EVENT_RETURN| \
+ RUBY_EVENT_CLASS | \
+ RUBY_EVENT_END | \
+ RUBY_EVENT_CALL | \
+ RUBY_EVENT_RETURN| \
RUBY_EVENT_C_CALL| \
RUBY_EVENT_C_RETURN| \
- RUBY_EVENT_B_CALL| \
- RUBY_EVENT_B_RETURN| \
+ RUBY_EVENT_B_CALL| \
+ RUBY_EVENT_B_RETURN| \
RUBY_EVENT_COVERAGE_LINE| \
RUBY_EVENT_COVERAGE_BRANCH)
@@ -113,12 +114,14 @@ struct iseq_compile_data {
struct iseq_compile_data_storage *storage_current;
} insn;
bool in_rescue;
+ bool in_masgn;
int loopval_popped; /* used by NODE_BREAK */
int last_line;
int label_no;
int node_level;
int isolated_depth;
unsigned int ci_index;
+ unsigned int ic_index;
const rb_compile_option_t *option;
struct rb_id_table *ivar_cache_table;
const struct rb_builtin_function *builtin_function_table;
@@ -132,10 +135,10 @@ static inline struct iseq_compile_data *
ISEQ_COMPILE_DATA(const rb_iseq_t *iseq)
{
if (iseq->flags & ISEQ_USE_COMPILE_DATA) {
- return iseq->aux.compile_data;
+ return iseq->aux.compile_data;
}
else {
- return NULL;
+ return NULL;
}
}
@@ -182,14 +185,10 @@ VALUE rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node);
VALUE rb_iseq_compile_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback_callback_func * ifunc);
VALUE *rb_iseq_original_iseq(const rb_iseq_t *iseq);
void rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc,
- VALUE locals, VALUE args,
- VALUE exception, VALUE body);
+ VALUE locals, VALUE args,
+ VALUE exception, VALUE body);
void rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *arena);
-/* iseq.c */
-typedef bool rb_iseq_each_i(VALUE *code, VALUE insn, size_t index, void *data);
-void rb_iseq_each(const rb_iseq_t *iseq, size_t start_index, rb_iseq_each_i iterator, void *data);
-
VALUE rb_iseq_load(VALUE data, VALUE parent, VALUE opt);
VALUE rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc);
unsigned int rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos);
@@ -241,28 +240,29 @@ struct iseq_insn_info_entry {
rb_event_flag_t events;
};
+/*
+ * iseq type:
+ * CATCH_TYPE_RESCUE, CATCH_TYPE_ENSURE:
+ * use iseq as continuation.
+ *
+ * CATCH_TYPE_BREAK (iter):
+ * use iseq as key.
+ *
+ * CATCH_TYPE_BREAK (while), CATCH_TYPE_RETRY,
+ * CATCH_TYPE_REDO, CATCH_TYPE_NEXT:
+ * NULL.
+ */
+enum rb_catch_type {
+ CATCH_TYPE_RESCUE = INT2FIX(1),
+ CATCH_TYPE_ENSURE = INT2FIX(2),
+ CATCH_TYPE_RETRY = INT2FIX(3),
+ CATCH_TYPE_BREAK = INT2FIX(4),
+ CATCH_TYPE_REDO = INT2FIX(5),
+ CATCH_TYPE_NEXT = INT2FIX(6)
+};
+
struct iseq_catch_table_entry {
- enum catch_type {
- CATCH_TYPE_RESCUE = INT2FIX(1),
- CATCH_TYPE_ENSURE = INT2FIX(2),
- CATCH_TYPE_RETRY = INT2FIX(3),
- CATCH_TYPE_BREAK = INT2FIX(4),
- CATCH_TYPE_REDO = INT2FIX(5),
- CATCH_TYPE_NEXT = INT2FIX(6)
- } type;
-
- /*
- * iseq type:
- * CATCH_TYPE_RESCUE, CATCH_TYPE_ENSURE:
- * use iseq as continuation.
- *
- * CATCH_TYPE_BREAK (iter):
- * use iseq as key.
- *
- * CATCH_TYPE_BREAK (while), CATCH_TYPE_RETRY,
- * CATCH_TYPE_REDO, CATCH_TYPE_NEXT:
- * NULL.
- */
+ enum rb_catch_type type;
rb_iseq_t *iseq;
unsigned int start;
@@ -280,12 +280,12 @@ static inline int
iseq_catch_table_bytes(int n)
{
enum {
- catch_table_entry_size = sizeof(struct iseq_catch_table_entry),
- catch_table_entries_max = (INT_MAX - offsetof(struct iseq_catch_table, entries)) / catch_table_entry_size
+ catch_table_entry_size = sizeof(struct iseq_catch_table_entry),
+ catch_table_entries_max = (INT_MAX - offsetof(struct iseq_catch_table, entries)) / catch_table_entry_size
};
if (n > catch_table_entries_max) rb_fatal("too large iseq_catch_table - %d", n);
return (int)(offsetof(struct iseq_catch_table, entries) +
- n * catch_table_entry_size);
+ n * catch_table_entry_size);
}
#define INITIAL_ISEQ_COMPILE_DATA_STORAGE_BUFF_SIZE (512)
diff --git a/lex.c.blt b/lex.c.blt
index 92a4793b00..85727ed00f 100644
--- a/lex.c.blt
+++ b/lex.c.blt
@@ -34,7 +34,7 @@
struct kwtable {short name, id[2], state;};
const struct kwtable *rb_reserved_word(const char *, unsigned int);
#ifndef RIPPER
-static const struct kwtable *reserved_word(/*const char *, unsigned int*/);
+static const struct kwtable *reserved_word(register const char *str, register size_t len);
#define rb_reserved_word(str, len) reserved_word(str, len)
#line 9 "defs/keywords"
struct kwtable;
diff --git a/lib/English.gemspec b/lib/English.gemspec
index 274fb047b8..a08542bcda 100644
--- a/lib/English.gemspec
+++ b/lib/English.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "english"
- spec.version = "0.7.1"
+ spec.version = "0.7.2"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
diff --git a/lib/abbrev.gemspec b/lib/abbrev.gemspec
index 72837ed2ab..c28b960c8c 100644
--- a/lib/abbrev.gemspec
+++ b/lib/abbrev.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "abbrev"
- spec.version = "0.1.0"
+ spec.version = "0.1.1"
spec.authors = ["Akinori MUSHA"]
spec.email = ["knu@idaemons.org"]
diff --git a/lib/benchmark/version.rb b/lib/benchmark/version.rb
index 545575f4ab..645966fd80 100644
--- a/lib/benchmark/version.rb
+++ b/lib/benchmark/version.rb
@@ -1,4 +1,4 @@
# frozen_string_literal: true
module Benchmark
- VERSION = "0.2.0"
+ VERSION = "0.2.1"
end
diff --git a/lib/bundler.rb b/lib/bundler.rb
index 9fb9ce3e82..f83268e9cd 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -41,7 +41,6 @@ module Bundler
autoload :Definition, File.expand_path("bundler/definition", __dir__)
autoload :Dependency, File.expand_path("bundler/dependency", __dir__)
- autoload :DepProxy, File.expand_path("bundler/dep_proxy", __dir__)
autoload :Deprecate, File.expand_path("bundler/deprecate", __dir__)
autoload :Digest, File.expand_path("bundler/digest", __dir__)
autoload :Dsl, File.expand_path("bundler/dsl", __dir__)
@@ -58,7 +57,7 @@ module Bundler
autoload :Installer, File.expand_path("bundler/installer", __dir__)
autoload :LazySpecification, File.expand_path("bundler/lazy_specification", __dir__)
autoload :LockfileParser, File.expand_path("bundler/lockfile_parser", __dir__)
- autoload :MatchPlatform, File.expand_path("bundler/match_platform", __dir__)
+ autoload :MatchRemoteMetadata, File.expand_path("bundler/match_remote_metadata", __dir__)
autoload :ProcessLock, File.expand_path("bundler/process_lock", __dir__)
autoload :RemoteSpecification, File.expand_path("bundler/remote_specification", __dir__)
autoload :Resolver, File.expand_path("bundler/resolver", __dir__)
@@ -76,11 +75,12 @@ module Bundler
autoload :StubSpecification, File.expand_path("bundler/stub_specification", __dir__)
autoload :UI, File.expand_path("bundler/ui", __dir__)
autoload :URICredentialsFilter, File.expand_path("bundler/uri_credentials_filter", __dir__)
- autoload :VersionRanges, File.expand_path("bundler/version_ranges", __dir__)
+ autoload :URINormalizer, File.expand_path("bundler/uri_normalizer", __dir__)
+ autoload :SafeMarshal, File.expand_path("bundler/safe_marshal", __dir__)
class << self
def configure
- @configured ||= configure_gem_home_and_path
+ @configure ||= configure_gem_home_and_path
end
def ui
@@ -210,9 +210,10 @@ module Bundler
end
def frozen_bundle?
- frozen = settings[:deployment]
- frozen ||= settings[:frozen]
- frozen
+ frozen = settings[:frozen]
+ return frozen unless frozen.nil?
+
+ settings[:deployment]
end
def locked_gems
@@ -331,9 +332,9 @@ module Bundler
FileUtils.remove_entry_secure(path) if path && File.exist?(path)
rescue ArgumentError
message = <<EOF
-It is a security vulnerability to allow your home directory to be world-writable, and bundler can not continue.
+It is a security vulnerability to allow your home directory to be world-writable, and bundler cannot continue.
You should probably consider fixing this issue by running `chmod o-w ~` on *nix.
-Please refer to https://ruby-doc.org/stdlib-2.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
+Please refer to https://ruby-doc.org/stdlib-3.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
EOF
File.world_writable?(path) ? Bundler.ui.warn(message) : raise
raise PathError, "Please fix the world-writable issue with your #{path} directory"
@@ -488,41 +489,9 @@ EOF
configured_bundle_path.use_system_gems?
end
- def requires_sudo?
- return @requires_sudo if defined?(@requires_sudo_ran)
-
- sudo_present = which "sudo" if settings.allow_sudo?
-
- if sudo_present
- # the bundle path and subdirectories need to be writable for RubyGems
- # to be able to unpack and install gems without exploding
- path = bundle_path
- path = path.parent until path.exist?
-
- # bins are written to a different location on OS X
- bin_dir = Pathname.new(Bundler.system_bindir)
- bin_dir = bin_dir.parent until bin_dir.exist?
-
- # if any directory is not writable, we need sudo
- files = [path, bin_dir] | Dir[bundle_path.join("build_info/*").to_s] | Dir[bundle_path.join("*").to_s]
- unwritable_files = files.reject {|f| File.writable?(f) }
- sudo_needed = !unwritable_files.empty?
- if sudo_needed
- Bundler.ui.warn "Following files may not be writable, so sudo is needed:\n #{unwritable_files.map(&:to_s).sort.join("\n ")}"
- end
- end
-
- @requires_sudo_ran = true
- @requires_sudo = settings.allow_sudo? && sudo_present && sudo_needed
- end
-
def mkdir_p(path, options = {})
- if requires_sudo? && !options[:no_sudo]
- sudo "mkdir -p '#{path}'" unless File.exist?(path)
- else
- SharedHelpers.filesystem_access(path, :write) do |p|
- FileUtils.mkdir_p(p)
- end
+ SharedHelpers.filesystem_access(path, :write) do |p|
+ FileUtils.mkdir_p(p)
end
end
@@ -530,7 +499,7 @@ EOF
if File.file?(executable) && File.executable?(executable)
executable
elsif paths = ENV["PATH"]
- quote = '"'.freeze
+ quote = '"'
paths.split(File::PATH_SEPARATOR).find do |path|
path = path[1..-2] if path.start_with?(quote) && path.end_with?(quote)
executable_path = File.expand_path(executable, path)
@@ -539,41 +508,14 @@ EOF
end
end
- def sudo(str)
- SUDO_MUTEX.synchronize do
- prompt = "\n\n" + <<-PROMPT.gsub(/^ {6}/, "").strip + " "
- Your user account isn't allowed to install to the system RubyGems.
- You can cancel this installation and run:
-
- bundle config set --local path 'vendor/bundle'
- bundle install
-
- to install the gems into ./vendor/bundle/, or you can enter your password
- and install the bundled gems to RubyGems using sudo.
-
- Password:
- PROMPT
-
- unless @prompted_for_sudo ||= system(%(sudo -k -p "#{prompt}" true))
- raise SudoNotPermittedError,
- "Bundler requires sudo access to install at the moment. " \
- "Try installing again, granting Bundler sudo access when prompted, or installing into a different path."
- end
-
- `sudo -p "#{prompt}" #{str}`
- end
- end
-
def read_file(file)
SharedHelpers.filesystem_access(file, :read) do
File.open(file, "r:UTF-8", &:read)
end
end
- def load_marshal(data)
- Marshal.load(data)
- rescue TypeError => e
- raise MarshalError, "#{e.class}: #{e.message}"
+ def safe_load_marshal(data)
+ load_marshal(data, :marshal_proc => SafeMarshal.proc)
end
def load_gemspec(file, validate = false)
@@ -582,7 +524,7 @@ EOF
@gemspec_cache[key] ||= load_gemspec_uncached(file, validate)
# Protect against caching side-effected gemspecs by returning a
# new instance each time.
- @gemspec_cache[key].dup if @gemspec_cache[key]
+ @gemspec_cache[key]&.dup
end
def load_gemspec_uncached(file, validate = false)
@@ -609,7 +551,7 @@ EOF
def git_present?
return @git_present if defined?(@git_present)
- @git_present = Bundler.which("git") || Bundler.which("git.exe")
+ @git_present = Bundler.which("git#{RbConfig::CONFIG["EXEEXT"]}")
end
def feature_flag
@@ -631,7 +573,7 @@ EOF
@bin_path = nil
@bundler_major_version = nil
@bundle_path = nil
- @configured = nil
+ @configure = nil
@configured_bundle_path = nil
@definition = nil
@load = nil
@@ -664,6 +606,12 @@ EOF
private
+ def load_marshal(data, marshal_proc: nil)
+ Marshal.load(data, marshal_proc)
+ rescue TypeError => e
+ raise MarshalError, "#{e.class}: #{e.message}"
+ end
+
def eval_yaml_gemspec(path, contents)
Kernel.require "psych"
diff --git a/lib/bundler/bundler.gemspec b/lib/bundler/bundler.gemspec
index 38c533b0c1..da50b46225 100644
--- a/lib/bundler/bundler.gemspec
+++ b/lib/bundler/bundler.gemspec
@@ -22,17 +22,15 @@ Gem::Specification.new do |s|
s.summary = "The best way to manage your application's dependencies"
s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably"
- if s.respond_to?(:metadata=)
- s.metadata = {
- "bug_tracker_uri" => "https://github.com/rubygems/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler",
- "changelog_uri" => "https://github.com/rubygems/rubygems/blob/master/bundler/CHANGELOG.md",
- "homepage_uri" => "https://bundler.io/",
- "source_code_uri" => "https://github.com/rubygems/rubygems/",
- }
- end
+ s.metadata = {
+ "bug_tracker_uri" => "https://github.com/rubygems/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler",
+ "changelog_uri" => "https://github.com/rubygems/rubygems/blob/master/bundler/CHANGELOG.md",
+ "homepage_uri" => "https://bundler.io/",
+ "source_code_uri" => "https://github.com/rubygems/rubygems/tree/master/bundler",
+ }
- s.required_ruby_version = ">= 2.3.0"
- s.required_rubygems_version = ">= 2.5.2"
+ s.required_ruby_version = ">= 2.6.0"
+ s.required_rubygems_version = ">= 3.0.1"
s.files = Dir.glob("lib/bundler{.rb,/**/*}", File::FNM_DOTMATCH).reject {|f| File.directory?(f) }
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index e1c284130b..a3eb494db2 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -10,6 +10,7 @@ module Bundler
AUTO_INSTALL_CMDS = %w[show binstubs outdated exec open console licenses clean].freeze
PARSEABLE_COMMANDS = %w[check config help exec platform show version].freeze
+ EXTENSIONS = ["c", "rust"].freeze
COMMAND_ALIASES = {
"check" => "c",
@@ -22,6 +23,8 @@ module Bundler
}.freeze
def self.start(*)
+ check_deprecated_ext_option(ARGV) if ARGV.include?("--ext")
+
super
ensure
Bundler::SharedHelpers.print_major_deprecations!
@@ -153,6 +156,7 @@ module Bundler
dependency listed in the gemspec file to the newly created Gemfile.
D
method_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile"
+ method_option "gemfile", :type => :string, :banner => "Use the specified name for the gemfile instead of 'Gemfile'"
def init
require_relative "cli/init"
Init.new(options.dup).run
@@ -218,6 +222,8 @@ module Bundler
"Specify the number of jobs to run in parallel"
method_option "local", :type => :boolean, :banner =>
"Do not attempt to fetch gems remotely and use the gem cache instead"
+ method_option "prefer-local", :type => :boolean, :banner =>
+ "Only attempt to fetch gems remotely if not present locally, even if newer versions are available remotely"
method_option "no-cache", :type => :boolean, :banner =>
"Don't update the existing gem cache."
method_option "redownload", :type => :boolean, :aliases => "--force", :banner =>
@@ -236,7 +242,7 @@ module Bundler
"Install to the system location ($BUNDLE_PATH or $GEM_HOME) even if the bundle was previously installed somewhere else for this application"
method_option "trust-policy", :alias => "P", :type => :string, :banner =>
"Gem trust policy (like gem install -P). Must be one of " +
- Bundler.rubygems.security_policy_keys.join("|")
+ Bundler.rubygems.security_policy_keys.join("|")
method_option "without", :type => :array, :banner =>
"Exclude gems that are part of the specified named group."
method_option "with", :type => :array, :banner =>
@@ -290,6 +296,8 @@ module Bundler
"Prefer updating only to next minor version"
method_option "major", :type => :boolean, :banner =>
"Prefer updating to next major version (default)"
+ method_option "pre", :type => :boolean, :banner =>
+ "Always choose the highest allowed version when updating gems, regardless of prerelease status"
method_option "strict", :type => :boolean, :banner =>
"Do not allow any gem to be updated past latest --patch | --minor | --major"
method_option "conservative", :type => :boolean, :banner =>
@@ -370,6 +378,7 @@ module Bundler
method_option "group", :aliases => "-g", :type => :string
method_option "source", :aliases => "-s", :type => :string
method_option "require", :aliases => "-r", :type => :string, :banner => "Adds require path to gem. Provide false, or a path as a string."
+ method_option "path", :type => :string
method_option "git", :type => :string
method_option "github", :type => :string
method_option "branch", :type => :string
@@ -399,9 +408,9 @@ module Bundler
"Do not attempt to fetch gems remotely and use the gem cache instead"
method_option "pre", :type => :boolean, :banner => "Check for newer pre-release gems"
method_option "source", :type => :array, :banner => "Check against a specific source"
- method_option "filter-strict", :type => :boolean, :banner =>
+ method_option "filter-strict", :type => :boolean, :aliases => "--strict", :banner =>
"Only list newer versions allowed by your Gemfile requirements"
- method_option "strict", :type => :boolean, :aliases => "--update-strict", :banner =>
+ method_option "update-strict", :type => :boolean, :banner =>
"Strict conservative resolution, do not allow any gem to be updated past latest --patch | --minor | --major"
method_option "minor", :type => :boolean, :banner => "Prefer updating only to next minor version"
method_option "major", :type => :boolean, :banner => "Prefer updating to next major version (default)"
@@ -501,6 +510,7 @@ module Bundler
subcommand "config", Config
desc "open GEM", "Opens the source directory of the given bundled gem"
+ method_option "path", :type => :string, :lazy_default => "", :banner => "Open relative path of the gem source."
def open(name)
require_relative "cli/open"
Open.new(options, name).run
@@ -514,7 +524,7 @@ module Bundler
end
end
- desc "version", "Prints the bundler's version information"
+ desc "version", "Prints Bundler version information"
def version
cli_help = current_command.name == "cli_help"
if cli_help || ARGV.include?("version")
@@ -571,7 +581,7 @@ module Bundler
method_option :edit, :type => :string, :aliases => "-e", :required => false, :banner => "EDITOR",
:lazy_default => [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? },
:desc => "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)"
- method_option :ext, :type => :boolean, :default => false, :desc => "Generate the boilerplate for C extension code"
+ method_option :ext, :type => :string, :desc => "Generate the boilerplate for C extension code.", :enum => EXTENSIONS
method_option :git, :type => :boolean, :default => true, :desc => "Initialize a git repo inside your library."
method_option :mit, :type => :boolean, :desc => "Generate an MIT license file. Set a default with `bundle config set --global gem.mit true`."
method_option :rubocop, :type => :boolean, :desc => "Add rubocop to the generated Rakefile and gemspec. Set a default with `bundle config set --global gem.rubocop true`."
@@ -579,7 +589,7 @@ module Bundler
method_option :test, :type => :string, :lazy_default => Bundler.settings["gem.test"] || "", :aliases => "-t", :banner => "Use the specified test framework for your library",
:desc => "Generate a test directory for your library, either rspec, minitest or test-unit. Set a default with `bundle config set --global gem.test (rspec|minitest|test-unit)`."
method_option :ci, :type => :string, :lazy_default => Bundler.settings["gem.ci"] || "",
- :desc => "Generate CI configuration, either GitHub Actions, Travis CI, GitLab CI or CircleCI. Set a default with `bundle config set --global gem.ci (github|travis|gitlab|circle)`"
+ :desc => "Generate CI configuration, either GitHub Actions, GitLab CI or CircleCI. Set a default with `bundle config set --global gem.ci (github|gitlab|circle)`"
method_option :linter, :type => :string, :lazy_default => Bundler.settings["gem.linter"] || "",
:desc => "Add a linter and code formatter, either RuboCop or Standard. Set a default with `bundle config set --global gem.linter (rubocop|standard)`"
method_option :github_username, :type => :string, :default => Bundler.settings["gem.github_username"], :banner => "Set your username on GitHub", :desc => "Fill in GitHub username on README so that you don't have to do it manually. Set a default with `bundle config set --global gem.github_username <your_username>`."
@@ -617,7 +627,7 @@ module Bundler
method_option "dry-run", :type => :boolean, :default => false, :banner =>
"Only print out changes, do not clean gems"
method_option "force", :type => :boolean, :default => false, :banner =>
- "Forces clean even if --path is not set"
+ "Forces cleaning up unused gems even if Bundler is configured to use globally installed gems. As a consequence, removes all system gems except for the ones in the current application."
def clean
require_relative "cli/clean"
Clean.new(options.dup).run
@@ -665,10 +675,14 @@ module Bundler
"If updating, prefer updating only to next minor version"
method_option "major", :type => :boolean, :banner =>
"If updating, prefer updating to next major version (default)"
+ method_option "pre", :type => :boolean, :banner =>
+ "If updating, always choose the highest allowed version, regardless of prerelease status"
method_option "strict", :type => :boolean, :banner =>
"If updating, do not allow any gem to be updated past latest --patch | --minor | --major"
method_option "conservative", :type => :boolean, :banner =>
"If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated"
+ method_option "bundler", :type => :string, :lazy_default => "> 0.a", :banner =>
+ "Update the locked version of bundler"
def lock
require_relative "cli/lock"
Lock.new(options).run
@@ -746,6 +760,38 @@ module Bundler
end
end
+ def self.check_deprecated_ext_option(arguments)
+ # when deprecated version of `--ext` is called
+ # print out deprecation warning and pretend `--ext=c` was provided
+ if deprecated_ext_value?(arguments)
+ SharedHelpers.major_deprecation 2, "Extensions can now be generated using C or Rust, so `--ext` with no arguments has been deprecated. Please select a language, e.g. `--ext=rust` to generate a Rust extension. This gem will now be generated as if `--ext=c` was used."
+ arguments[arguments.index("--ext")] = "--ext=c"
+ end
+ end
+
+ def self.deprecated_ext_value?(arguments)
+ index = arguments.index("--ext")
+ next_argument = arguments[index+1]
+
+ # it is ok when --ext is followed with valid extension value
+ # for example `bundle gem hello --ext c`
+ return false if EXTENSIONS.include?(next_argument)
+
+ # deprecated call when --ext is called with no value in last position
+ # for example `bundle gem hello_gem --ext`
+ return true if next_argument.nil?
+
+ # deprecated call when --ext is followed by other parameter
+ # for example `bundle gem --ext --no-ci hello_gem`
+ return true if next_argument.start_with?("-")
+
+ # deprecated call when --ext is followed by gem name
+ # for example `bundle gem --ext hello_gem`
+ return true if next_argument
+
+ false
+ end
+
private
# Automatically invoke `bundle install` and resume if
diff --git a/lib/bundler/cli/add.rb b/lib/bundler/cli/add.rb
index 5bcf30d82d..08fa6547fb 100644
--- a/lib/bundler/cli/add.rb
+++ b/lib/bundler/cli/add.rb
@@ -40,7 +40,7 @@ module Bundler
raise InvalidOption, "Please specify gems to add." if gems.empty?
version.to_a.each do |v|
- raise InvalidOption, "Invalid gem requirement pattern '#{v}'" unless Gem::Requirement::PATTERN =~ v.to_s
+ raise InvalidOption, "Invalid gem requirement pattern '#{v}'" unless Gem::Requirement::PATTERN.match?(v.to_s)
end
end
end
diff --git a/lib/bundler/cli/binstubs.rb b/lib/bundler/cli/binstubs.rb
index 639c01ff39..fc2fad47a5 100644
--- a/lib/bundler/cli/binstubs.rb
+++ b/lib/bundler/cli/binstubs.rb
@@ -11,7 +11,7 @@ module Bundler
def run
Bundler.definition.validate_runtime!
path_option = options["path"]
- path_option = nil if path_option && path_option.empty?
+ path_option = nil if path_option&.empty?
Bundler.settings.set_command_option :bin, path_option if options["path"]
Bundler.settings.set_command_option_if_given :shebang, options["shebang"]
installer = Installer.new(Bundler.root, Bundler.definition)
@@ -40,7 +40,11 @@ module Bundler
end
if options[:standalone]
- next Bundler.ui.warn("Sorry, Bundler can only be run via RubyGems.") if gem_name == "bundler"
+ if gem_name == "bundler"
+ Bundler.ui.warn("Sorry, Bundler can only be run via RubyGems.") unless options[:all]
+ next
+ end
+
Bundler.settings.temporary(:path => (Bundler.settings[:path] || Bundler.root)) do
installer.generate_standalone_bundler_executable_stubs(spec, installer_opts)
end
diff --git a/lib/bundler/cli/check.rb b/lib/bundler/cli/check.rb
index 65c51337d2..cc1f37f0c3 100644
--- a/lib/bundler/cli/check.rb
+++ b/lib/bundler/cli/check.rb
@@ -17,7 +17,7 @@ module Bundler
begin
definition.resolve_only_locally!
not_installed = definition.missing_specs
- rescue GemNotFound, VersionConflict
+ rescue GemNotFound, SolveFailure
Bundler.ui.error "Bundler can't satisfy your Gemfile's dependencies."
Bundler.ui.warn "Install missing gems with `bundle install`."
exit 1
diff --git a/lib/bundler/cli/common.rb b/lib/bundler/cli/common.rb
index b547619c6a..d654406f65 100644
--- a/lib/bundler/cli/common.rb
+++ b/lib/bundler/cli/common.rb
@@ -15,6 +15,7 @@ module Bundler
end
def self.output_fund_metadata_summary
+ return if Bundler.settings["ignore_funding_requests"]
definition = Bundler.definition
current_dependencies = definition.requested_dependencies
current_specs = definition.specs
@@ -110,6 +111,7 @@ module Bundler
definition.gem_version_promoter.tap do |gvp|
gvp.level = patch_level.first || :major
gvp.strict = options[:strict] || options["filter-strict"]
+ gvp.pre = options[:pre]
end
end
diff --git a/lib/bundler/cli/console.rb b/lib/bundler/cli/console.rb
index 97b8dc0663..1eb8ea8254 100644
--- a/lib/bundler/cli/console.rb
+++ b/lib/bundler/cli/console.rb
@@ -30,9 +30,9 @@ module Bundler
def get_constant(name)
const_name = {
- "pry" => :Pry,
+ "pry" => :Pry,
"ripl" => :Ripl,
- "irb" => :IRB,
+ "irb" => :IRB,
}[name]
Object.const_get(const_name)
rescue NameError
diff --git a/lib/bundler/cli/doctor.rb b/lib/bundler/cli/doctor.rb
index 74444ad0ce..e299a5a8c2 100644
--- a/lib/bundler/cli/doctor.rb
+++ b/lib/bundler/cli/doctor.rb
@@ -73,12 +73,10 @@ module Bundler
definition.specs.each do |spec|
bundles_for_gem(spec).each do |bundle|
bad_paths = dylibs(bundle).select do |f|
- begin
- Fiddle.dlopen(f)
- false
- rescue Fiddle::DLError
- true
- end
+ Fiddle.dlopen(f)
+ false
+ rescue Fiddle::DLError
+ true
end
if bad_paths.any?
broken_links[spec] ||= []
diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb
index c4c76d1b69..7f1200f4a0 100644
--- a/lib/bundler/cli/gem.rb
+++ b/lib/bundler/cli/gem.rb
@@ -15,7 +15,7 @@ module Bundler
"test-unit" => "3.0",
}.freeze
- attr_reader :options, :gem_name, :thor, :name, :target
+ attr_reader :options, :gem_name, :thor, :name, :target, :extension
def initialize(options, gem_name, thor)
@options = options
@@ -28,7 +28,11 @@ module Bundler
@name = @gem_name
@target = SharedHelpers.pwd.join(gem_name)
- validate_ext_name if options[:ext]
+ @extension = options[:ext]
+
+ validate_ext_name if @extension
+ validate_rust_builder_rubygems_version if @extension == "rust"
+ travis_removal_info
end
def run
@@ -55,21 +59,22 @@ module Bundler
end
config = {
- :name => name,
+ :name => name,
:underscored_name => underscored_name,
- :namespaced_path => namespaced_path,
- :makefile_path => "#{underscored_name}/#{underscored_name}",
- :constant_name => constant_name,
- :constant_array => constant_array,
- :author => git_author_name.empty? ? "TODO: Write your name" : git_author_name,
- :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
- :test => options[:test],
- :ext => options[:ext],
- :exe => options[:exe],
- :bundler_version => bundler_dependency_version,
- :git => use_git,
- :github_username => github_username.empty? ? "[USERNAME]" : github_username,
+ :namespaced_path => namespaced_path,
+ :makefile_path => "#{underscored_name}/#{underscored_name}",
+ :constant_name => constant_name,
+ :constant_array => constant_array,
+ :author => git_author_name.empty? ? "TODO: Write your name" : git_author_name,
+ :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
+ :test => options[:test],
+ :ext => extension,
+ :exe => options[:exe],
+ :bundler_version => bundler_dependency_version,
+ :git => use_git,
+ :github_username => github_username.empty? ? "[USERNAME]" : github_username,
:required_ruby_version => required_ruby_version,
+ :rust_builder_required_rubygems_version => rust_builder_required_rubygems_version,
:minitest_constant_name => minitest_constant_name,
}
ensure_safe_gem_name(name, constant_array)
@@ -132,8 +137,6 @@ module Bundler
case config[:ci]
when "github"
templates.merge!("github/workflows/main.yml.tt" => ".github/workflows/main.yml")
- when "travis"
- templates.merge!("travis.yml.tt" => ".travis.yml")
when "gitlab"
templates.merge!("gitlab-ci.yml.tt" => ".gitlab-ci.yml")
when "circle"
@@ -188,14 +191,23 @@ module Bundler
templates.merge!("exe/newgem.tt" => "exe/#{name}") if config[:exe]
- if options[:ext]
+ if extension == "c"
templates.merge!(
- "ext/newgem/extconf.rb.tt" => "ext/#{name}/extconf.rb",
+ "ext/newgem/extconf-c.rb.tt" => "ext/#{name}/extconf.rb",
"ext/newgem/newgem.h.tt" => "ext/#{name}/#{underscored_name}.h",
"ext/newgem/newgem.c.tt" => "ext/#{name}/#{underscored_name}.c"
)
end
+ if extension == "rust"
+ templates.merge!(
+ "Cargo.toml.tt" => "Cargo.toml",
+ "ext/newgem/Cargo.toml.tt" => "ext/#{name}/Cargo.toml",
+ "ext/newgem/extconf-rust.rb.tt" => "ext/#{name}/extconf.rb",
+ "ext/newgem/src/lib.rs.tt" => "ext/#{name}/src/lib.rs",
+ )
+ end
+
if target.exist? && !target.directory?
Bundler.ui.error "Couldn't create a new gem named `#{gem_name}` because there's an existing file named `#{gem_name}`."
exit Bundler::BundlerError.all_errors[Bundler::GenericSystemCallError]
@@ -270,7 +282,7 @@ module Bundler
Bundler.ui.info hint_text("test")
result = Bundler.ui.ask "Enter a test framework. rspec/minitest/test-unit/(none):"
- if result =~ /rspec|minitest|test-unit/
+ if /rspec|minitest|test-unit/.match?(result)
test_framework = result
else
test_framework = false
@@ -306,12 +318,11 @@ module Bundler
"* CircleCI: https://circleci.com/\n" \
"* GitHub Actions: https://github.com/features/actions\n" \
"* GitLab CI: https://docs.gitlab.com/ee/ci/\n" \
- "* Travis CI: https://travis-ci.org/\n" \
"\n"
Bundler.ui.info hint_text("ci")
- result = Bundler.ui.ask "Enter a CI service. github/travis/gitlab/circle/(none):"
- if result =~ /github|travis|gitlab|circle/
+ result = Bundler.ui.ask "Enter a CI service. github/gitlab/circle/(none):"
+ if /github|gitlab|circle/.match?(result)
ci_template = result
else
ci_template = false
@@ -342,7 +353,7 @@ module Bundler
Bundler.ui.info hint_text("linter")
result = Bundler.ui.ask "Enter a linter. rubocop/standard/(none):"
- if result =~ /rubocop|standard/
+ if /rubocop|standard/.match?(result)
linter_template = result
else
linter_template = false
@@ -389,7 +400,7 @@ module Bundler
end
def ensure_safe_gem_name(name, constant_array)
- if name =~ /^\d/
+ if /^\d/.match?(name)
Bundler.ui.error "Invalid gem name #{name} Please give a name which does not start with numbers."
exit 1
end
@@ -415,28 +426,39 @@ module Bundler
thor.run(%(#{editor} "#{file}"))
end
+ def rust_builder_required_rubygems_version
+ "3.3.11"
+ end
+
def required_ruby_version
- if Gem.ruby_version < Gem::Version.new("2.4.a") then "2.3.0"
- elsif Gem.ruby_version < Gem::Version.new("2.5.a") then "2.4.0"
- elsif Gem.ruby_version < Gem::Version.new("2.6.a") then "2.5.0"
- else
- "2.6.0"
- end
+ "2.6.0"
end
def rubocop_version
- if Gem.ruby_version < Gem::Version.new("2.4.a") then "0.81.0"
- elsif Gem.ruby_version < Gem::Version.new("2.5.a") then "1.12"
- else
- "1.21"
- end
+ "1.21"
end
def standard_version
- if Gem.ruby_version < Gem::Version.new("2.4.a") then "0.2.5"
- elsif Gem.ruby_version < Gem::Version.new("2.5.a") then "1.0"
- else
- "1.3"
+ "1.3"
+ end
+
+ # TODO: remove at next minor release
+ def travis_removal_info
+ if options[:ci] == "travis"
+ Bundler.ui.error "Support for Travis CI was removed from gem skeleton generator."
+ exit 1
+ end
+
+ if Bundler.settings["gem.ci"] == "travis"
+ Bundler.ui.error "Support for Travis CI was removed from gem skeleton generator, but it is present in bundle config. Please configure another provider using `bundle config set gem.ci SERVICE` (where SERVICE is one of github/gitlab/circle) or unset configuration using `bundle config unset gem.ci`."
+ exit 1
+ end
+ end
+
+ def validate_rust_builder_rubygems_version
+ if Gem::Version.new(rust_builder_required_rubygems_version) > Gem.rubygems_version
+ Bundler.ui.error "Your RubyGems version (#{Gem.rubygems_version}) is too old to build Rust extension. Please update your RubyGems using `gem update --system` or any other way and try again."
+ exit 1
end
end
end
diff --git a/lib/bundler/cli/info.rb b/lib/bundler/cli/info.rb
index 0545ce8c75..36c7a58f12 100644
--- a/lib/bundler/cli/info.rb
+++ b/lib/bundler/cli/info.rb
@@ -33,7 +33,7 @@ module Bundler
def default_gem_spec(gem_name)
return unless Gem::Specification.respond_to?(:find_all_by_name)
gem_spec = Gem::Specification.find_all_by_name(gem_name).last
- return gem_spec if gem_spec && gem_spec.respond_to?(:default_gem?) && gem_spec.default_gem?
+ return gem_spec if gem_spec&.default_gem?
end
def spec_not_found(gem_name)
diff --git a/lib/bundler/cli/init.rb b/lib/bundler/cli/init.rb
index e4f8229c48..246b9d6460 100644
--- a/lib/bundler/cli/init.rb
+++ b/lib/bundler/cli/init.rb
@@ -32,7 +32,11 @@ module Bundler
file << spec.to_gemfile
end
else
- FileUtils.cp(File.expand_path("../templates/#{gemfile}", __dir__), gemfile)
+ File.open(File.expand_path("../templates/Gemfile", __dir__), "r") do |template|
+ File.open(gemfile, "wb") do |destination|
+ IO.copy_stream(template, destination)
+ end
+ end
end
puts "Writing new #{gemfile} to #{SharedHelpers.pwd}/#{gemfile}"
@@ -41,7 +45,7 @@ module Bundler
private
def gemfile
- @gemfile ||= Bundler.preferred_gemfile_name
+ @gemfile ||= options[:gemfile] || Bundler.preferred_gemfile_name
end
end
end
diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb
index e9b85f7f6f..c71bcf159f 100644
--- a/lib/bundler/cli/install.rb
+++ b/lib/bundler/cli/install.rb
@@ -94,9 +94,8 @@ module Bundler
def warn_if_root
return if Bundler.settings[:silence_root_warning] || Gem.win_platform? || !Process.uid.zero?
- Bundler.ui.warn "Don't run Bundler as root. Bundler can ask for sudo " \
- "if it is needed, and installing your bundle as root will break this " \
- "application for all non-root users on this machine.", :wrap => true
+ Bundler.ui.warn "Don't run Bundler as root. Installing your bundle as root " \
+ "will break this application for all non-root users on this machine.", :wrap => true
end
def dependencies_count_for(definition)
@@ -147,12 +146,15 @@ module Bundler
def normalize_settings
Bundler.settings.set_command_option :path, nil if options[:system]
Bundler.settings.set_command_option_if_given :path, options[:path]
- Bundler.settings.temporary(:path_relative_to_cwd => false) do
- Bundler.settings.set_command_option :path, "bundle" if options["standalone"] && Bundler.settings[:path].nil?
+
+ if options["standalone"] && Bundler.settings[:path].nil? && !options["local"]
+ Bundler.settings.temporary(:path_relative_to_cwd => false) do
+ Bundler.settings.set_command_option :path, "bundle"
+ end
end
bin_option = options["binstubs"]
- bin_option = nil if bin_option && bin_option.empty?
+ bin_option = nil if bin_option&.empty?
Bundler.settings.set_command_option :bin, bin_option if options["binstubs"]
Bundler.settings.set_command_option_if_given :shebang, options["shebang"]
diff --git a/lib/bundler/cli/lock.rb b/lib/bundler/cli/lock.rb
index 7d613a6644..cb3ed27138 100644
--- a/lib/bundler/cli/lock.rb
+++ b/lib/bundler/cli/lock.rb
@@ -15,19 +15,22 @@ module Bundler
end
print = options[:print]
- ui = Bundler.ui
- Bundler.ui = UI::Silent.new if print
+ previous_ui_level = Bundler.ui.level
+ Bundler.ui.level = "silent" if print
Bundler::Fetcher.disable_endpoint = options["full-index"]
update = options[:update]
conservative = options[:conservative]
+ bundler = options[:bundler]
if update.is_a?(Array) # unlocking specific gems
Bundler::CLI::Common.ensure_all_gems_in_lockfile!(update)
update = { :gems => update, :conservative => conservative }
- elsif update
- update = { :conservative => conservative } if conservative
+ elsif update && conservative
+ update = { :conservative => conservative }
+ elsif update && bundler
+ update = { :bundler => bundler }
end
definition = Bundler.definition(update)
@@ -61,7 +64,7 @@ module Bundler
definition.lock(file)
end
- Bundler.ui = ui
+ Bundler.ui.level = previous_ui_level
end
end
end
diff --git a/lib/bundler/cli/open.rb b/lib/bundler/cli/open.rb
index ea504344f3..8522ec92d6 100644
--- a/lib/bundler/cli/open.rb
+++ b/lib/bundler/cli/open.rb
@@ -2,23 +2,25 @@
module Bundler
class CLI::Open
- attr_reader :options, :name
+ attr_reader :options, :name, :path
def initialize(options, name)
@options = options
@name = name
+ @path = options[:path] unless options[:path].nil?
end
def run
+ raise InvalidOption, "Cannot specify `--path` option without a value" if !@path.nil? && @path.empty?
editor = [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? }
return Bundler.ui.info("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR") unless editor
return unless spec = Bundler::CLI::Common.select_spec(name, :regex_match)
if spec.default_gem?
Bundler.ui.info "Unable to open #{name} because it's a default gem, so the directory it would normally be installed to does not exist."
else
- path = spec.full_gem_path
- Dir.chdir(path) do
+ root_path = spec.full_gem_path
+ Dir.chdir(root_path) do
require "shellwords"
- command = Shellwords.split(editor) + [path]
+ command = Shellwords.split(editor) << File.join([root_path, path].compact)
Bundler.with_original_env do
system(*command)
end || Bundler.ui.info("Could not run '#{command.join(" ")}'")
diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb
index d5183b060b..68c701aefb 100644
--- a/lib/bundler/cli/outdated.rb
+++ b/lib/bundler/cli/outdated.rb
@@ -46,7 +46,7 @@ module Bundler
Bundler::CLI::Common.configure_gem_version_promoter(
Bundler.definition,
- options
+ options.merge(:strict => @strict)
)
definition_resolution = proc do
@@ -111,9 +111,7 @@ module Bundler
end.compact
if options[:parseable]
- relevant_outdated_gems.each do |gems|
- print_gems(gems)
- end
+ print_gems(relevant_outdated_gems)
else
print_gems_table(relevant_outdated_gems)
end
@@ -129,6 +127,12 @@ module Bundler
private
+ def loaded_from_for(spec)
+ return unless spec.respond_to?(:loaded_from)
+
+ spec.loaded_from
+ end
+
def groups_text(group_text, groups)
"#{group_text}#{groups.split(",").size > 1 ? "s" : ""} \"#{groups}\""
end
@@ -184,10 +188,13 @@ module Bundler
def print_gem(current_spec, active_spec, dependency, groups)
spec_version = "#{active_spec.version}#{active_spec.git_version}"
- spec_version += " (from #{active_spec.loaded_from})" if Bundler.ui.debug? && active_spec.loaded_from
+ if Bundler.ui.debug?
+ loaded_from = loaded_from_for(active_spec)
+ spec_version += " (from #{loaded_from})" if loaded_from
+ end
current_version = "#{current_spec.version}#{current_spec.git_version}"
- if dependency && dependency.specific?
+ if dependency&.specific?
dependency_version = %(, requested #{dependency.requirement})
end
@@ -211,7 +218,7 @@ module Bundler
dependency = dependency.requirement if dependency
ret_val = [active_spec.name, current_version, spec_version, dependency.to_s, groups.to_s]
- ret_val << active_spec.loaded_from.to_s if Bundler.ui.debug?
+ ret_val << loaded_from_for(active_spec).to_s if Bundler.ui.debug?
ret_val
end
diff --git a/lib/bundler/cli/platform.rb b/lib/bundler/cli/platform.rb
index 068c765aad..32d68abbb1 100644
--- a/lib/bundler/cli/platform.rb
+++ b/lib/bundler/cli/platform.rb
@@ -8,12 +8,12 @@ module Bundler
end
def run
- platforms, ruby_version = Bundler.ui.silence do
- locked_ruby_version = Bundler.locked_gems && Bundler.locked_gems.ruby_version
- gemfile_ruby_version = Bundler.definition.ruby_version && Bundler.definition.ruby_version.single_version_string
- [Bundler.definition.platforms.map {|p| "* #{p}" },
- locked_ruby_version || gemfile_ruby_version]
+ ruby_version = if Bundler.locked_gems
+ Bundler.locked_gems.ruby_version&.gsub(/p\d+\Z/, "")
+ else
+ Bundler.definition.ruby_version&.single_version_string
end
+
output = []
if options[:ruby]
@@ -23,6 +23,8 @@ module Bundler
output << "No ruby version specified"
end
else
+ platforms = Bundler.definition.platforms.map {|p| "* #{p}" }
+
output << "Your platform is: #{Gem::Platform.local}"
output << "Your app has gems that work on these platforms:\n#{platforms.join("\n")}"
diff --git a/lib/bundler/cli/viz.rb b/lib/bundler/cli/viz.rb
index 644f9b25cf..5c09e00995 100644
--- a/lib/bundler/cli/viz.rb
+++ b/lib/bundler/cli/viz.rb
@@ -23,7 +23,7 @@ module Bundler
Bundler.ui.warn "Make sure you have the graphviz ruby gem. You can install it with:"
Bundler.ui.warn "`gem install ruby-graphviz`"
rescue StandardError => e
- raise unless e.message =~ /GraphViz not installed or dot not in PATH/
+ raise unless e.message.to_s.include?("GraphViz not installed or dot not in PATH")
Bundler.ui.error e.message
Bundler.ui.warn "Please install GraphViz. On a Mac with Homebrew, you can run `brew install graphviz`."
end
diff --git a/lib/bundler/compact_index_client/cache.rb b/lib/bundler/compact_index_client/cache.rb
index 2d83777139..0b43581c11 100644
--- a/lib/bundler/compact_index_client/cache.rb
+++ b/lib/bundler/compact_index_client/cache.rb
@@ -68,7 +68,7 @@ module Bundler
def info_path(name)
name = name.to_s
- if name =~ /[^a-z0-9_-]/
+ if /[^a-z0-9_-]/.match?(name)
name += "-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}"
info_roots.last.join(name)
else
diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb
index 5b430dfbe2..0f7bf9bb50 100644
--- a/lib/bundler/compact_index_client/updater.rb
+++ b/lib/bundler/compact_index_client/updater.rb
@@ -20,63 +20,64 @@ module Bundler
def initialize(fetcher)
@fetcher = fetcher
- require_relative "../vendored_tmpdir"
end
def update(local_path, remote_path, retrying = nil)
headers = {}
- Bundler::Dir.mktmpdir("bundler-compact-index-") do |local_temp_dir|
- local_temp_path = Pathname.new(local_temp_dir).join(local_path.basename)
-
- # first try to fetch any new bytes on the existing file
- if retrying.nil? && local_path.file?
- copy_file local_path, local_temp_path
-
- headers["If-None-Match"] = etag_for(local_temp_path)
- headers["Range"] =
- if local_temp_path.size.nonzero?
- # Subtract a byte to ensure the range won't be empty.
- # Avoids 416 (Range Not Satisfiable) responses.
- "bytes=#{local_temp_path.size - 1}-"
- else
- "bytes=#{local_temp_path.size}-"
- end
- end
+ local_temp_path = local_path.sub(/$/, ".#{$$}")
+ local_temp_path = local_temp_path.sub(/$/, ".retrying") if retrying
+ local_temp_path = local_temp_path.sub(/$/, ".tmp")
- response = @fetcher.call(remote_path, headers)
- return nil if response.is_a?(Net::HTTPNotModified)
+ # first try to fetch any new bytes on the existing file
+ if retrying.nil? && local_path.file?
+ copy_file local_path, local_temp_path
- content = response.body
+ headers["If-None-Match"] = etag_for(local_temp_path)
+ headers["Range"] =
+ if local_temp_path.size.nonzero?
+ # Subtract a byte to ensure the range won't be empty.
+ # Avoids 416 (Range Not Satisfiable) responses.
+ "bytes=#{local_temp_path.size - 1}-"
+ else
+ "bytes=#{local_temp_path.size}-"
+ end
+ end
- etag = (response["ETag"] || "").gsub(%r{\AW/}, "")
- correct_response = SharedHelpers.filesystem_access(local_temp_path) do
- if response.is_a?(Net::HTTPPartialContent) && local_temp_path.size.nonzero?
- local_temp_path.open("a") {|f| f << slice_body(content, 1..-1) }
+ response = @fetcher.call(remote_path, headers)
+ return nil if response.is_a?(Net::HTTPNotModified)
- etag_for(local_temp_path) == etag
- else
- local_temp_path.open("wb") {|f| f << content }
+ content = response.body
- etag.length.zero? || etag_for(local_temp_path) == etag
- end
- end
+ etag = (response["ETag"] || "").gsub(%r{\AW/}, "")
+ correct_response = SharedHelpers.filesystem_access(local_temp_path) do
+ if response.is_a?(Net::HTTPPartialContent) && local_temp_path.size.nonzero?
+ local_temp_path.open("a") {|f| f << slice_body(content, 1..-1) }
- if correct_response
- SharedHelpers.filesystem_access(local_path) do
- FileUtils.mv(local_temp_path, local_path)
- end
- return nil
+ etag_for(local_temp_path) == etag
+ else
+ local_temp_path.open("wb") {|f| f << content }
+
+ etag.length.zero? || etag_for(local_temp_path) == etag
end
+ end
- if retrying
- raise MisMatchedChecksumError.new(remote_path, etag, etag_for(local_temp_path))
+ if correct_response
+ SharedHelpers.filesystem_access(local_path) do
+ FileUtils.mv(local_temp_path, local_path)
end
+ return nil
+ end
- update(local_path, remote_path, :retrying)
+ if retrying
+ raise MisMatchedChecksumError.new(remote_path, etag, etag_for(local_temp_path))
end
+
+ update(local_path, remote_path, :retrying)
rescue Zlib::GzipFile::Error
raise Bundler::HTTPError
+ ensure
+ FileUtils.remove_file(local_temp_path) if File.exist?(local_temp_path)
end
def etag_for(path)
diff --git a/lib/bundler/constants.rb b/lib/bundler/constants.rb
index 2e4ebb37ee..8dd8a53815 100644
--- a/lib/bundler/constants.rb
+++ b/lib/bundler/constants.rb
@@ -2,6 +2,6 @@
module Bundler
WINDOWS = RbConfig::CONFIG["host_os"] =~ /(msdos|mswin|djgpp|mingw)/
- FREEBSD = RbConfig::CONFIG["host_os"] =~ /bsd/
+ FREEBSD = RbConfig::CONFIG["host_os"].to_s.include?("bsd")
NULL = WINDOWS ? "NUL" : "/dev/null"
end
diff --git a/lib/bundler/current_ruby.rb b/lib/bundler/current_ruby.rb
index 36f26b7ab4..f009b07ad7 100644
--- a/lib/bundler/current_ruby.rb
+++ b/lib/bundler/current_ruby.rb
@@ -22,6 +22,8 @@ module Bundler
2.7
3.0
3.1
+ 3.2
+ 3.3
].freeze
KNOWN_MAJOR_VERSIONS = KNOWN_MINOR_VERSIONS.map {|v| v.split(".", 2).first }.uniq.freeze
@@ -36,17 +38,18 @@ module Bundler
rbx
ruby
truffleruby
+ windows
x64_mingw
].freeze
def ruby?
return true if Bundler::GemHelpers.generic_local_platform == Gem::Platform::RUBY
- !mswin? && (RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev" || RUBY_ENGINE == "truffleruby")
+ !windows? && (RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev" || RUBY_ENGINE == "truffleruby")
end
def mri?
- !mswin? && RUBY_ENGINE == "ruby"
+ !windows? && RUBY_ENGINE == "ruby"
end
def rbx?
@@ -65,16 +68,24 @@ module Bundler
RUBY_ENGINE == "truffleruby"
end
- def mswin?
+ def windows?
Gem.win_platform?
end
+ def mswin?
+ # For backwards compatibility
+ windows?
+
+ # TODO: This should correctly be:
+ # windows? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin32" && Bundler.local_platform.cpu == "x86"
+ end
+
def mswin64?
- Gem.win_platform? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64"
+ windows? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64"
end
def mingw?
- Gem.win_platform? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64"
+ windows? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64"
end
def x64_mingw?
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 8f43befe35..564530a98c 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -16,7 +16,6 @@ module Bundler
:locked_deps,
:locked_gems,
:platforms,
- :requires,
:ruby_version,
:lockfile,
:gemfiles
@@ -70,15 +69,20 @@ module Bundler
@unlock = unlock
@optional_groups = optional_groups
@remote = false
+ @prefer_local = false
@specs = nil
@ruby_version = ruby_version
@gemfiles = gemfiles
@lockfile = lockfile
@lockfile_contents = String.new
+
@locked_bundler_version = nil
- @locked_ruby_version = nil
+ @resolved_bundler_version = nil
+
+ @locked_ruby_version = nil
@new_platform = nil
+ @removed_platform = nil
if lockfile && File.exist?(lockfile)
@lockfile_contents = Bundler.read_file(lockfile)
@@ -105,6 +109,7 @@ module Bundler
@locked_gems = nil
@locked_deps = {}
@locked_specs = SpecSet.new([])
+ @originally_locked_specs = @locked_specs
@locked_sources = []
@locked_platforms = []
end
@@ -128,7 +133,7 @@ module Bundler
end
@unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version)
- add_current_platform unless current_ruby_platform_locked? || Bundler.frozen_bundle?
+ add_current_platform unless Bundler.frozen_bundle?
converge_path_sources_to_gemspec_sources
@path_changes = converge_paths
@@ -137,31 +142,18 @@ module Bundler
if @unlock[:conservative]
@unlock[:gems] ||= @dependencies.map(&:name)
else
- eager_unlock = expand_dependencies(@unlock[:gems] || [], true)
- @unlock[:gems] = @locked_specs.for(eager_unlock, false, false).map(&:name)
+ eager_unlock = (@unlock[:gems] || []).map {|name| Dependency.new(name, ">= 0") }
+ @unlock[:gems] = @locked_specs.for(eager_unlock, false, platforms).map(&:name).uniq
end
@dependency_changes = converge_dependencies
@local_changes = converge_locals
- @locked_specs_incomplete_for_platform = !@locked_specs.for(requested_dependencies & expand_dependencies(locked_dependencies), true, true)
-
- @requires = compute_requires
+ @missing_lockfile_dep = check_missing_lockfile_dep
end
def gem_version_promoter
- @gem_version_promoter ||= begin
- locked_specs =
- if unlocking? && @locked_specs.empty? && !@lockfile_contents.empty?
- # Definition uses an empty set of locked_specs to indicate all gems
- # are unlocked, but GemVersionPromoter needs the locked_specs
- # for conservative comparison.
- Bundler::SpecSet.new(@locked_gems.specs)
- else
- @locked_specs
- end
- GemVersionPromoter.new(locked_specs, @unlock[:gems])
- end
+ @gem_version_promoter ||= GemVersionPromoter.new
end
def resolve_only_locally!
@@ -181,6 +173,23 @@ module Bundler
resolve
end
+ def resolution_mode=(options)
+ if options["local"]
+ @remote = false
+ else
+ @remote = true
+ @prefer_local = options["prefer-local"]
+ end
+ end
+
+ def setup_sources_for_resolve
+ if @remote == false
+ sources.cached!
+ else
+ sources.remote!
+ end
+ end
+
# For given dependency list returns a SpecSet with Gemspec of all the required
# dependencies.
# 1. The method first resolves the dependencies specified in Gemfile
@@ -210,6 +219,8 @@ module Bundler
true
rescue BundlerError => e
@resolve = nil
+ @resolver = nil
+ @resolution_packages = nil
@specs = nil
@gem_version_promoter = nil
@@ -226,8 +237,16 @@ module Bundler
end
def current_dependencies
+ filter_relevant(dependencies)
+ end
+
+ def current_locked_dependencies
+ filter_relevant(locked_dependencies)
+ end
+
+ def filter_relevant(dependencies)
dependencies.select do |d|
- d.should_include? && !d.gem_platforms(@platforms).empty?
+ d.should_include? && !d.gem_platforms([generic_local_platform]).empty?
end
end
@@ -251,10 +270,9 @@ module Bundler
def dependencies_for(groups)
groups.map!(&:to_sym)
- deps = current_dependencies.reject do |d|
+ current_dependencies.reject do |d|
(d.groups & groups).empty?
end
- expand_dependencies(deps)
end
# Resolve all the dependencies specified in Gemfile. It ensures that
@@ -266,24 +284,21 @@ module Bundler
@resolve ||= if Bundler.frozen_bundle?
Bundler.ui.debug "Frozen, using resolution from the lockfile"
@locked_specs
- elsif !unlocking? && nothing_changed?
+ elsif no_resolve_needed?
if deleted_deps.any?
- Bundler.ui.debug("Some dependencies were deleted, using a subset of the resolution from the lockfile")
+ Bundler.ui.debug "Some dependencies were deleted, using a subset of the resolution from the lockfile"
SpecSet.new(filter_specs(@locked_specs, @dependencies - deleted_deps))
else
- Bundler.ui.debug("Found no changes, using resolution from the lockfile")
- if @locked_gems.may_include_redundant_platform_specific_gems?
+ Bundler.ui.debug "Found no changes, using resolution from the lockfile"
+ if @removed_platform || @locked_gems.may_include_redundant_platform_specific_gems?
SpecSet.new(filter_specs(@locked_specs, @dependencies))
else
@locked_specs
end
end
else
- last_resolve = converge_locked_specs
- # Run a resolve against the locally available gems
- Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}")
- expanded_dependencies = expand_dependencies(dependencies + metadata_dependencies, true)
- Resolver.resolve(expanded_dependencies, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms)
+ Bundler.ui.debug "Found changes from the lockfile, re-resolving dependencies because #{change_reason}"
+ start_resolution
end
end
@@ -302,11 +317,11 @@ module Bundler
# Convert to \r\n if the existing lock has them
# i.e., Windows with `git config core.autocrlf=true`
- contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match("\r\n")
+ contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
if @locked_bundler_version
locked_major = @locked_bundler_version.segments.first
- current_major = Gem::Version.create(Bundler::VERSION).segments.first
+ current_major = bundler_version_to_lock.segments.first
updating_major = locked_major < current_major
end
@@ -346,27 +361,16 @@ module Bundler
end
end
+ def bundler_version_to_lock
+ @resolved_bundler_version || Bundler.gem_version
+ end
+
def to_lock
require_relative "lockfile_generator"
LockfileGenerator.generate(self)
end
def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)
- msg = String.new
- msg << "You are trying to install in deployment mode after changing\n" \
- "your Gemfile. Run `bundle install` elsewhere and add the\n" \
- "updated #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} to version control."
-
- unless explicit_flag
- suggested_command = if Bundler.settings.locations("frozen").keys.&([:global, :local]).any?
- "bundle config unset frozen"
- elsif Bundler.settings.locations("deployment").keys.&([:global, :local]).any?
- "bundle config unset deployment"
- end
- msg << "\n\nIf this is a development machine, remove the #{Bundler.default_gemfile} " \
- "freeze \nby running `#{suggested_command}`."
- end
-
added = []
deleted = []
changed = []
@@ -380,32 +384,36 @@ module Bundler
deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?
both_sources = Hash.new {|h, k| h[k] = [] }
- @dependencies.each {|d| both_sources[d.name][0] = d }
-
- locked_dependencies.each do |d|
- next if !Bundler.feature_flag.bundler_3_mode? && @locked_specs[d.name].empty?
-
- both_sources[d.name][1] = d
- end
+ current_dependencies.each {|d| both_sources[d.name][0] = d }
+ current_locked_dependencies.each {|d| both_sources[d.name][1] = d }
both_sources.each do |name, (dep, lock_dep)|
next if dep.nil? || lock_dep.nil?
- gemfile_source = dep.source || sources.default_source
- lock_source = lock_dep.source || sources.default_source
+ gemfile_source = dep.source || default_source
+ lock_source = lock_dep.source || default_source
next if lock_source.include?(gemfile_source)
- gemfile_source_name = dep.source ? gemfile_source.identifier : "no specified source"
- lockfile_source_name = lock_dep.source ? lock_source.identifier : "no specified source"
+ gemfile_source_name = dep.source ? gemfile_source.to_gemfile : "no specified source"
+ lockfile_source_name = lock_dep.source ? lock_source.to_gemfile : "no specified source"
changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
end
reason = change_reason
- msg << "\n\n#{reason.split(", ").map(&:capitalize).join("\n")}" unless reason.strip.empty?
+ msg = String.new
+ msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because frozen mode is set"
msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
- msg << "\n"
+ msg << "\n\nRun `bundle install` elsewhere and add the updated #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} to version control.\n"
+
+ unless explicit_flag
+ suggested_command = unless Bundler.settings.locations("frozen").keys.include?(:env)
+ "bundle config set frozen false"
+ end
+ msg << "If this is a development machine, remove the #{Bundler.default_gemfile.relative_path_from(SharedHelpers.pwd)} " \
+ "freeze by running `#{suggested_command}`." if suggested_command
+ end
raise ProductionError, msg if added.any? || deleted.any? || changed.any? || !nothing_changed?
end
@@ -454,7 +462,9 @@ module Bundler
end
def remove_platform(platform)
- return if @platforms.delete(Gem::Platform.new(platform))
+ removed_platform = @platforms.delete(Gem::Platform.new(platform))
+ @removed_platform ||= removed_platform
+ return if removed_platform
raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}"
end
@@ -468,7 +478,11 @@ module Bundler
private :sources
def nothing_changed?
- !@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@locked_specs_incomplete_for_platform
+ !@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@missing_lockfile_dep && !@unlocking_bundler
+ end
+
+ def no_resolve_needed?
+ !unlocking? && nothing_changed?
end
def unlocking?
@@ -477,8 +491,32 @@ module Bundler
private
+ def resolver
+ @resolver ||= Resolver.new(resolution_packages, gem_version_promoter)
+ end
+
+ def expanded_dependencies
+ dependencies_with_bundler + metadata_dependencies
+ end
+
+ def dependencies_with_bundler
+ return dependencies unless @unlocking_bundler
+ return dependencies if dependencies.map(&:name).include?("bundler")
+
+ [Dependency.new("bundler", @unlocking_bundler)] + dependencies
+ end
+
+ def resolution_packages
+ @resolution_packages ||= begin
+ last_resolve = converge_locked_specs
+ remove_ruby_from_platforms_if_necessary!(current_dependencies)
+ packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, :locked_specs => @originally_locked_specs, :unlock => @unlock[:gems], :prerelease => gem_version_promoter.pre?)
+ additional_base_requirements_for_resolve(packages, last_resolve)
+ end
+ end
+
def filter_specs(specs, deps)
- SpecSet.new(specs).for(expand_dependencies(deps, true), false, false)
+ SpecSet.new(specs).for(deps, false, platforms)
end
def materialize(dependencies)
@@ -502,16 +540,55 @@ module Bundler
raise GemNotFound, "Could not find #{missing_specs_list.join(" nor ")}"
end
- unless specs["bundler"].any?
- bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last
- specs["bundler"] = bundler
+ incomplete_specs = specs.incomplete_specs
+ loop do
+ break if incomplete_specs.empty?
+
+ Bundler.ui.debug("The lockfile does not have all gems needed for the current platform though, Bundler will still re-resolve dependencies")
+ setup_sources_for_resolve
+ resolution_packages.delete(incomplete_specs)
+ @resolve = start_resolution
+ specs = resolve.materialize(dependencies)
+
+ still_incomplete_specs = specs.incomplete_specs
+
+ if still_incomplete_specs == incomplete_specs
+ package = resolution_packages.get_package(incomplete_specs.first.name)
+ resolver.raise_not_found! package
+ end
+
+ incomplete_specs = still_incomplete_specs
end
+ bundler = sources.metadata_source.specs.search(["bundler", Bundler.gem_version]).last
+ specs["bundler"] = bundler
+
specs
end
+ def start_resolution
+ result = resolver.start
+
+ @resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version
+
+ SpecSet.new(SpecSet.new(result).for(dependencies, false, @platforms))
+ end
+
def precompute_source_requirements_for_indirect_dependencies?
- @remote && sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
+ sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
+ end
+
+ def pin_locally_available_names(source_requirements)
+ source_requirements.each_with_object({}) do |(name, original_source), new_source_requirements|
+ local_source = original_source.dup
+ local_source.local_only!
+
+ new_source_requirements[name] = if local_source.specs.search(name).any?
+ local_source
+ else
+ original_source
+ end
+ end
end
def current_ruby_platform_locked?
@@ -528,6 +605,8 @@ module Bundler
end
def add_current_platform
+ return if current_ruby_platform_locked?
+
add_platform(local_platform)
end
@@ -549,12 +628,13 @@ module Bundler
[@new_platform, "you added a new platform to your gemfile"],
[@path_changes, "the gemspecs for path gems changed"],
[@local_changes, "the gemspecs for git local gems changed"],
- [@locked_specs_incomplete_for_platform, "the lockfile does not have all gems needed for the current platform"],
+ [@missing_lockfile_dep, "your lock file is missing \"#{@missing_lockfile_dep}\""],
+ [@unlocking_bundler, "an update to the version of Bundler itself was requested"],
].select(&:first).map(&:last).join(", ")
end
- def pretty_dep(dep, source = false)
- SharedHelpers.pretty_dependency(dep, source)
+ def pretty_dep(dep)
+ SharedHelpers.pretty_dependency(dep)
end
# Check if the specs of the given source changed
@@ -591,8 +671,8 @@ module Bundler
Bundler.settings.local_overrides.map do |k, v|
spec = @dependencies.find {|s| s.name == k }
- source = spec && spec.source
- if source && source.respond_to?(:local_override!)
+ source = spec&.source
+ if source&.respond_to?(:local_override!)
source.unlock! if @unlock[:gems].include?(spec.name)
locals << [source, source.local_override!(v)]
end
@@ -604,6 +684,26 @@ module Bundler
!sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty?
end
+ def check_missing_lockfile_dep
+ all_locked_specs = @locked_specs.map(&:name) << "bundler"
+
+ missing = @locked_specs.select do |s|
+ s.dependencies.any? {|dep| !all_locked_specs.include?(dep.name) }
+ end
+
+ if missing.any?
+ @locked_specs.delete(missing)
+
+ return missing.first.name
+ end
+
+ return if @dependency_changes
+
+ current_dependencies.find do |d|
+ @locked_specs[d.name].empty? && d.name != "bundler"
+ end&.name
+ end
+
def converge_paths
sources.path_sources.any? do |source|
specs_changed?(source)
@@ -657,6 +757,8 @@ module Bundler
dep.source = sources.get(dep.source)
end
+ next if unlocking?
+
unless locked_dep = @locked_deps[dep.name]
changes = true
next
@@ -682,7 +784,9 @@ module Bundler
# commonly happen if the Gemfile has changed since the lockfile was last
# generated
def converge_locked_specs
- resolve = converge_specs(@locked_specs)
+ converged = converge_specs(@locked_specs)
+
+ resolve = SpecSet.new(converged.reject {|s| @unlock[:gems].include?(s.name) })
diff = nil
@@ -701,85 +805,80 @@ module Bundler
def converge_specs(specs)
converged = []
+ deps = []
- deps = @dependencies.select do |dep|
- specs[dep].any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
- end
+ @specs_that_changed_sources = []
specs.each do |s|
- # Replace the locked dependency's source with the equivalent source from the Gemfile
+ name = s.name
dep = @dependencies.find {|d| s.satisfies?(d) }
+ lockfile_source = s.source
- s.source = (dep && dep.source) || sources.get(s.source) || sources.default_source
+ if dep
+ gemfile_source = dep.source || default_source
- next if @unlock[:sources].include?(s.source.name)
+ @specs_that_changed_sources << s if gemfile_source != lockfile_source
+ deps << dep if !dep.source || lockfile_source.include?(dep.source)
+ @unlock[:gems] << name if lockfile_source.include?(dep.source) && lockfile_source != gemfile_source
+
+ # Replace the locked dependency's source with the equivalent source from the Gemfile
+ s.source = gemfile_source
+ else
+ # Replace the locked dependency's source with the default source, if the locked source is no longer in the Gemfile
+ s.source = default_source unless sources.get(lockfile_source)
+ end
- # If the spec is from a path source and it doesn't exist anymore
- # then we unlock it.
+ next if @unlock[:sources].include?(s.source.name)
# Path sources have special logic
if s.source.instance_of?(Source::Path) || s.source.instance_of?(Source::Gemspec)
new_specs = begin
s.source.specs
- rescue PathError, GitError
+ rescue PathError
# if we won't need the source (according to the lockfile),
- # don't error if the path/git source isn't available
+ # don't error if the path source isn't available
next if specs.
- for(requested_dependencies, false, true).
+ for(requested_dependencies, false).
none? {|locked_spec| locked_spec.source == s.source }
raise
end
new_spec = new_specs[s].first
-
- # If the spec is no longer in the path source, unlock it. This
- # commonly happens if the version changed in the gemspec
- next unless new_spec
-
- s.dependencies.replace(new_spec.dependencies)
+ if new_spec
+ s.dependencies.replace(new_spec.dependencies)
+ else
+ # If the spec is no longer in the path source, unlock it. This
+ # commonly happens if the version changed in the gemspec
+ @unlock[:gems] << name
+ end
end
- if dep.nil? && requested_dependencies.find {|d| s.name == d.name }
+ if dep.nil? && requested_dependencies.find {|d| name == d.name }
@unlock[:gems] << s.name
else
converged << s
end
end
- SpecSet.new(filter_specs(converged, deps).reject {|s| @unlock[:gems].include?(s.name) })
+ filter_specs(converged, deps)
end
def metadata_dependencies
@metadata_dependencies ||= [
- Dependency.new("Ruby\0", RubyVersion.system.gem_version),
+ Dependency.new("Ruby\0", Gem.ruby_version),
Dependency.new("RubyGems\0", Gem::VERSION),
]
end
- def expand_dependencies(dependencies, remote = false)
- deps = []
- dependencies.each do |dep|
- dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name)
- next unless remote || dep.current_platform?
- target_platforms = dep.gem_platforms(remote ? @platforms : [generic_local_platform])
- deps += expand_dependency_with_platforms(dep, target_platforms)
- end
- deps
- end
-
- def expand_dependency_with_platforms(dep, platforms)
- platforms.map do |p|
- DepProxy.get_proxy(dep, p)
- end
- end
-
def source_requirements
# Record the specs available in each gem's source, so that those
# specs will be available later when the resolver knows where to
# look for that gemspec (or its dependencies)
source_requirements = if precompute_source_requirements_for_indirect_dependencies?
- { :default => sources.default_source }.merge(source_map.all_requirements)
+ all_requirements = source_map.all_requirements
+ all_requirements = pin_locally_available_names(all_requirements) if @prefer_local
+ { :default => default_source }.merge(all_requirements)
else
{ :default => Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
end
@@ -787,20 +886,44 @@ module Bundler
metadata_dependencies.each do |dep|
source_requirements[dep.name] = sources.metadata_source
end
- source_requirements[:default_bundler] = source_requirements["bundler"] || sources.default_source
- source_requirements["bundler"] = sources.metadata_source # needs to come last to override
+
+ default_bundler_source = source_requirements["bundler"] || default_source
+
+ if @unlocking_bundler
+ default_bundler_source.add_dependency_names("bundler")
+ else
+ source_requirements[:default_bundler] = default_bundler_source
+ source_requirements["bundler"] = sources.metadata_source # needs to come last to override
+ end
+
+ verify_changed_sources!
source_requirements
end
+ def default_source
+ sources.default_source
+ end
+
+ def verify_changed_sources!
+ @specs_that_changed_sources.each do |s|
+ if s.source.specs.search(s.name).empty?
+ raise GemNotFound, "Could not find gem '#{s.name}' in #{s.source}"
+ end
+ end
+ end
+
def requested_groups
- groups - Bundler.settings[:without] - @optional_groups + Bundler.settings[:with]
+ values = groups - Bundler.settings[:without] - @optional_groups + Bundler.settings[:with]
+ values &= Bundler.settings[:only] unless Bundler.settings[:only].empty?
+ values
end
def lockfiles_equal?(current, proposed, preserve_unknown_sections)
if preserve_unknown_sections
sections_to_ignore = LockfileParser.sections_to_ignore(@locked_bundler_version)
sections_to_ignore += LockfileParser.unknown_sections_in_lockfile(current)
- sections_to_ignore += LockfileParser::ENVIRONMENT_VERSION_SECTIONS
+ sections_to_ignore << LockfileParser::RUBY
+ sections_to_ignore << LockfileParser::BUNDLED unless @unlocking_bundler
pattern = /#{Regexp.union(sections_to_ignore)}\n(\s{2,}.*\n)+/
whitespace_cleanup = /\n{2,}/
current = current.gsub(pattern, "\n").gsub(whitespace_cleanup, "\n\n").strip
@@ -809,24 +932,26 @@ module Bundler
current == proposed
end
- def compute_requires
- dependencies.reduce({}) do |requires, dep|
- next requires unless dep.should_include?
- requires[dep.name] = Array(dep.autorequire || dep.name).map do |file|
- # Allow `require: true` as an alias for `require: <name>`
- file == true ? dep.name : file
- end
- requires
+ def additional_base_requirements_for_resolve(resolution_packages, last_resolve)
+ return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
+ converge_specs(@originally_locked_specs - last_resolve).each do |locked_spec|
+ next if locked_spec.source.is_a?(Source::Path)
+ resolution_packages.base_requirements[locked_spec.name] = Gem::Requirement.new(">= #{locked_spec.version}")
end
+ resolution_packages
end
- def additional_base_requirements_for_resolve
- return [] unless @locked_gems && unlocking? && !sources.expired_sources?(@locked_gems.sources)
- converge_specs(@originally_locked_specs).map do |locked_spec|
- name = locked_spec.name
- dep = Dependency.new(name, ">= #{locked_spec.version}")
- DepProxy.get_proxy(dep, locked_spec.platform)
- end
+ def remove_ruby_from_platforms_if_necessary!(dependencies)
+ return if Bundler.frozen_bundle? ||
+ Bundler.local_platform == Gem::Platform::RUBY ||
+ !platforms.include?(Gem::Platform::RUBY) ||
+ (@new_platform && platforms.last == Gem::Platform::RUBY) ||
+ @path_changes ||
+ @dependency_changes ||
+ !@originally_locked_specs.incomplete_ruby_specs?(dependencies)
+
+ remove_platform(Gem::Platform::RUBY)
+ add_current_platform
end
def source_map
diff --git a/lib/bundler/dep_proxy.rb b/lib/bundler/dep_proxy.rb
deleted file mode 100644
index a32dc37b49..0000000000
--- a/lib/bundler/dep_proxy.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler
- class DepProxy
- attr_reader :__platform, :dep
-
- @proxies = {}
-
- def self.get_proxy(dep, platform)
- @proxies[[dep, platform]] ||= new(dep, platform).freeze
- end
-
- def initialize(dep, platform)
- @dep = dep
- @__platform = platform
- end
-
- private_class_method :new
-
- alias_method :eql?, :==
-
- def type
- @dep.type
- end
-
- def name
- @dep.name
- end
-
- def requirement
- @dep.requirement
- end
-
- def to_s
- s = name.dup
- s << " (#{requirement})" unless requirement == Gem::Requirement.default
- s << " #{__platform}" unless __platform == Gem::Platform::RUBY
- s
- end
-
- def dup
- raise NoMethodError.new("DepProxy cannot be duplicated")
- end
-
- def clone
- raise NoMethodError.new("DepProxy cannot be cloned")
- end
-
- private
-
- def method_missing(*args, &blk)
- @dep.send(*args, &blk)
- end
- end
-end
diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb
index 2449cb6411..5f17943629 100644
--- a/lib/bundler/dependency.rb
+++ b/lib/bundler/dependency.rb
@@ -1,101 +1,30 @@
# frozen_string_literal: true
require "rubygems/dependency"
-require_relative "force_platform"
require_relative "shared_helpers"
require_relative "rubygems_ext"
module Bundler
class Dependency < Gem::Dependency
- include ForcePlatform
-
attr_reader :autorequire
- attr_reader :groups, :platforms, :gemfile, :git, :github, :branch, :ref, :force_ruby_platform
+ attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref
- # rubocop:disable Naming/VariableNumber
+ ALL_RUBY_VERSIONS = ((18..27).to_a + (30..33).to_a).freeze
PLATFORM_MAP = {
- :ruby => Gem::Platform::RUBY,
- :ruby_18 => Gem::Platform::RUBY,
- :ruby_19 => Gem::Platform::RUBY,
- :ruby_20 => Gem::Platform::RUBY,
- :ruby_21 => Gem::Platform::RUBY,
- :ruby_22 => Gem::Platform::RUBY,
- :ruby_23 => Gem::Platform::RUBY,
- :ruby_24 => Gem::Platform::RUBY,
- :ruby_25 => Gem::Platform::RUBY,
- :ruby_26 => Gem::Platform::RUBY,
- :ruby_27 => Gem::Platform::RUBY,
- :ruby_30 => Gem::Platform::RUBY,
- :ruby_31 => Gem::Platform::RUBY,
- :mri => Gem::Platform::RUBY,
- :mri_18 => Gem::Platform::RUBY,
- :mri_19 => Gem::Platform::RUBY,
- :mri_20 => Gem::Platform::RUBY,
- :mri_21 => Gem::Platform::RUBY,
- :mri_22 => Gem::Platform::RUBY,
- :mri_23 => Gem::Platform::RUBY,
- :mri_24 => Gem::Platform::RUBY,
- :mri_25 => Gem::Platform::RUBY,
- :mri_26 => Gem::Platform::RUBY,
- :mri_27 => Gem::Platform::RUBY,
- :mri_30 => Gem::Platform::RUBY,
- :mri_31 => Gem::Platform::RUBY,
- :rbx => Gem::Platform::RUBY,
- :truffleruby => Gem::Platform::RUBY,
- :jruby => Gem::Platform::JAVA,
- :jruby_18 => Gem::Platform::JAVA,
- :jruby_19 => Gem::Platform::JAVA,
- :mswin => Gem::Platform::MSWIN,
- :mswin_18 => Gem::Platform::MSWIN,
- :mswin_19 => Gem::Platform::MSWIN,
- :mswin_20 => Gem::Platform::MSWIN,
- :mswin_21 => Gem::Platform::MSWIN,
- :mswin_22 => Gem::Platform::MSWIN,
- :mswin_23 => Gem::Platform::MSWIN,
- :mswin_24 => Gem::Platform::MSWIN,
- :mswin_25 => Gem::Platform::MSWIN,
- :mswin_26 => Gem::Platform::MSWIN,
- :mswin_27 => Gem::Platform::MSWIN,
- :mswin_30 => Gem::Platform::MSWIN,
- :mswin_31 => Gem::Platform::MSWIN,
- :mswin64 => Gem::Platform::MSWIN64,
- :mswin64_19 => Gem::Platform::MSWIN64,
- :mswin64_20 => Gem::Platform::MSWIN64,
- :mswin64_21 => Gem::Platform::MSWIN64,
- :mswin64_22 => Gem::Platform::MSWIN64,
- :mswin64_23 => Gem::Platform::MSWIN64,
- :mswin64_24 => Gem::Platform::MSWIN64,
- :mswin64_25 => Gem::Platform::MSWIN64,
- :mswin64_26 => Gem::Platform::MSWIN64,
- :mswin64_27 => Gem::Platform::MSWIN64,
- :mswin64_30 => Gem::Platform::MSWIN64,
- :mswin64_31 => Gem::Platform::MSWIN64,
- :mingw => Gem::Platform::MINGW,
- :mingw_18 => Gem::Platform::MINGW,
- :mingw_19 => Gem::Platform::MINGW,
- :mingw_20 => Gem::Platform::MINGW,
- :mingw_21 => Gem::Platform::MINGW,
- :mingw_22 => Gem::Platform::MINGW,
- :mingw_23 => Gem::Platform::MINGW,
- :mingw_24 => Gem::Platform::MINGW,
- :mingw_25 => Gem::Platform::MINGW,
- :mingw_26 => Gem::Platform::MINGW,
- :mingw_27 => Gem::Platform::MINGW,
- :mingw_30 => Gem::Platform::MINGW,
- :mingw_31 => Gem::Platform::MINGW,
- :x64_mingw => Gem::Platform::X64_MINGW,
- :x64_mingw_20 => Gem::Platform::X64_MINGW,
- :x64_mingw_21 => Gem::Platform::X64_MINGW,
- :x64_mingw_22 => Gem::Platform::X64_MINGW,
- :x64_mingw_23 => Gem::Platform::X64_MINGW,
- :x64_mingw_24 => Gem::Platform::X64_MINGW,
- :x64_mingw_25 => Gem::Platform::X64_MINGW,
- :x64_mingw_26 => Gem::Platform::X64_MINGW,
- :x64_mingw_27 => Gem::Platform::X64_MINGW,
- :x64_mingw_30 => Gem::Platform::X64_MINGW,
- :x64_mingw_31 => Gem::Platform::X64_MINGW,
- }.freeze
- # rubocop:enable Naming/VariableNumber
+ :ruby => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
+ :mri => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
+ :rbx => [Gem::Platform::RUBY],
+ :truffleruby => [Gem::Platform::RUBY],
+ :jruby => [Gem::Platform::JAVA, [18, 19]],
+ :windows => [Gem::Platform::WINDOWS, ALL_RUBY_VERSIONS],
+ :mswin => [Gem::Platform::MSWIN, ALL_RUBY_VERSIONS],
+ :mswin64 => [Gem::Platform::MSWIN64, ALL_RUBY_VERSIONS - [18]],
+ :mingw => [Gem::Platform::MINGW, ALL_RUBY_VERSIONS],
+ :x64_mingw => [Gem::Platform::X64_MINGW, ALL_RUBY_VERSIONS - [18, 19]],
+ }.each_with_object({}) do |(platform, spec), hash|
+ hash[platform] = spec[0]
+ spec[1]&.each {|version| hash[:"#{platform}_#{version}"] = spec[0] }
+ end.freeze
def initialize(name, version, options = {}, &blk)
type = options["type"] || :runtime
@@ -104,6 +33,7 @@ module Bundler
@autorequire = nil
@groups = Array(options["group"] || :default).map(&:to_sym)
@source = options["source"]
+ @path = options["path"]
@git = options["git"]
@github = options["github"]
@branch = options["branch"]
@@ -112,7 +42,7 @@ module Bundler
@env = options["env"]
@should_include = options.fetch("should_include", true)
@gemfile = options["gemfile"]
- @force_ruby_platform = options.fetch("force_ruby_platform", default_force_ruby_platform)
+ @force_ruby_platform = options["force_ruby_platform"] if options.key?("force_ruby_platform")
@autorequire = Array(options["require"] || []) if options.key?("require")
end
@@ -120,6 +50,7 @@ module Bundler
# Returns the platforms this dependency is valid for, in the same order as
# passed in the `valid_platforms` parameter
def gem_platforms(valid_platforms)
+ return [Gem::Platform::RUBY] if force_ruby_platform
return valid_platforms if @platforms.empty?
valid_platforms.select {|p| expanded_platforms.include?(GemHelpers.generic(p)) }
@@ -154,7 +85,7 @@ module Bundler
def to_lock
out = super
out << "!" if source
- out << "\n"
+ out
end
def specific?
diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb
index 385fdd4383..03c80a408c 100644
--- a/lib/bundler/dsl.rb
+++ b/lib/bundler/dsl.rb
@@ -41,7 +41,7 @@ module Bundler
end
def eval_gemfile(gemfile, contents = nil)
- expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile && @gemfile.parent)
+ expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile&.parent)
original_gemfile = @gemfile
@gemfile = expanded_gemfile_path
@gemfiles << expanded_gemfile_path
@@ -67,7 +67,6 @@ module Bundler
gemspecs = Gem::Util.glob_files_in_dir("{,*}.gemspec", expanded_path).map {|g| Bundler.load_gemspec(g) }.compact
gemspecs.reject! {|s| s.name != name } if name
- Index.sort_specs(gemspecs)
specs_by_name_and_version = gemspecs.group_by {|s| [s.name, s.version] }
case specs_by_name_and_version.size
@@ -278,8 +277,8 @@ module Bundler
if repo_name =~ GITHUB_PULL_REQUEST_URL
{
"git" => "https://github.com/#{$1}.git",
- "branch" => "refs/pull/#{$2}/head",
- "ref" => nil,
+ "branch" => nil,
+ "ref" => "refs/pull/#{$2}/head",
"tag" => nil,
}
else
@@ -325,7 +324,7 @@ module Bundler
if name.is_a?(Symbol)
raise GemfileError, %(You need to specify gem names as Strings. Use 'gem "#{name}"' instead)
end
- if name =~ /\s/
+ if /\s/.match?(name)
raise GemfileError, %('#{name}' is not a valid gem name because it contains whitespace)
end
raise GemfileError, %(an empty gem name is not valid) if name.empty?
diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb
index e9aa366b41..863544b1f9 100644
--- a/lib/bundler/endpoint_specification.rb
+++ b/lib/bundler/endpoint_specification.rb
@@ -3,7 +3,7 @@
module Bundler
# used for Creating Specifications from the Gemcutter Endpoint
class EndpointSpecification < Gem::Specification
- include MatchPlatform
+ include MatchRemoteMetadata
attr_reader :name, :version, :platform, :checksum
attr_accessor :source, :remote, :dependencies
@@ -12,7 +12,7 @@ module Bundler
super()
@name = name
@version = Gem::Version.create version
- @platform = platform
+ @platform = Gem::Platform.new(platform)
@spec_fetcher = spec_fetcher
@dependencies = dependencies.map {|dep, reqs| build_dependency(dep, reqs) }
@@ -22,17 +22,6 @@ module Bundler
parse_metadata(metadata)
end
- def required_ruby_version
- @required_ruby_version ||= _remote_specification.required_ruby_version
- end
-
- # A fallback is included because the original version of the specification
- # API didn't include that field, so some marshalled specs in the index have it
- # set to +nil+.
- def required_rubygems_version
- @required_rubygems_version ||= _remote_specification.required_rubygems_version || Gem::Requirement.default
- end
-
def fetch_platform
@platform
end
diff --git a/lib/bundler/env.rb b/lib/bundler/env.rb
index 1763035a8a..7b1152930e 100644
--- a/lib/bundler/env.rb
+++ b/lib/bundler/env.rb
@@ -75,7 +75,7 @@ module Bundler
end
def self.git_version
- Bundler::Source::Git::GitProxy.new(nil, nil, nil).full_version
+ Bundler::Source::Git::GitProxy.new(nil, nil).full_version
rescue Bundler::Source::Git::GitNotInstalledError
"not installed"
end
@@ -122,7 +122,7 @@ module Bundler
specs = Bundler.rubygems.find_name(name)
out << [" #{name}", "(#{specs.map(&:version).join(",")})"] unless specs.empty?
end
- if (exe = caller.last.split(":").first) && exe =~ %r{(exe|bin)/bundler?\z}
+ if (exe = caller.last.split(":").first)&.match? %r{(exe|bin)/bundler?\z}
shebang = File.read(exe).lines.first
shebang.sub!(/^#!\s*/, "")
unless shebang.start_with?(Gem.ruby, "/usr/bin/env ruby")
diff --git a/lib/bundler/environment_preserver.rb b/lib/bundler/environment_preserver.rb
index 0f08e049d8..57013f5d50 100644
--- a/lib/bundler/environment_preserver.rb
+++ b/lib/bundler/environment_preserver.rb
@@ -2,11 +2,12 @@
module Bundler
class EnvironmentPreserver
- INTENTIONALLY_NIL = "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL".freeze
+ INTENTIONALLY_NIL = "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL"
BUNDLER_KEYS = %w[
BUNDLE_BIN_PATH
BUNDLE_GEMFILE
BUNDLER_VERSION
+ BUNDLER_SETUP
GEM_HOME
GEM_PATH
MANPATH
@@ -15,7 +16,7 @@ module Bundler
RUBYLIB
RUBYOPT
].map(&:freeze).freeze
- BUNDLER_PREFIX = "BUNDLER_ORIG_".freeze
+ BUNDLER_PREFIX = "BUNDLER_ORIG_"
def self.from_env
new(env_to_hash(ENV), BUNDLER_KEYS)
diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb
index f10b6cc68f..5839fc6a73 100644
--- a/lib/bundler/errors.rb
+++ b/lib/bundler/errors.rb
@@ -21,16 +21,7 @@ module Bundler
class InstallError < BundlerError; status_code(5); end
# Internal error, should be rescued
- class VersionConflict < BundlerError
- attr_reader :conflicts
-
- def initialize(conflicts, msg = nil)
- super(msg)
- @conflicts = conflicts
- end
-
- status_code(6)
- end
+ class SolveFailure < BundlerError; status_code(6); end
class GemNotFound < BundlerError; status_code(7); end
class InstallHookError < BundlerError; status_code(8); end
@@ -55,7 +46,6 @@ module Bundler
class CyclicDependencyError < BundlerError; status_code(21); end
class GemfileLockNotFound < BundlerError; status_code(22); end
class PluginError < BundlerError; status_code(29); end
- class SudoNotPermittedError < BundlerError; status_code(30); end
class ThreadCreationError < BundlerError; status_code(33); end
class APIResponseMismatchError < BundlerError; status_code(34); end
class APIResponseInvalidDependenciesError < BundlerError; status_code(35); end
diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb
index e441b941c2..ab2189f7f0 100644
--- a/lib/bundler/feature_flag.rb
+++ b/lib/bundler/feature_flag.rb
@@ -37,9 +37,7 @@ module Bundler
settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") }
settings_flag(:print_only_version_number) { bundler_3_mode? }
settings_flag(:setup_makes_kernel_gem_public) { !bundler_3_mode? }
- settings_flag(:suppress_install_using_messages) { bundler_3_mode? }
settings_flag(:update_requires_all_flag) { bundler_4_mode? }
- settings_flag(:use_gem_version_promoter_for_major_updates) { bundler_3_mode? }
settings_option(:default_cli_command) { bundler_3_mode? ? :cli_help : :install }
diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb
index e9d5dd505c..2119799f68 100644
--- a/lib/bundler/fetcher.rb
+++ b/lib/bundler/fetcher.rb
@@ -29,9 +29,7 @@ module Bundler
" is a chance you are experiencing a man-in-the-middle attack, but" \
" most likely your system doesn't have the CA certificates needed" \
" for verification. For information about OpenSSL certificates, see" \
- " https://railsapps.github.io/openssl-certificate-verify-failed.html." \
- " To connect without using SSL, edit your Gemfile" \
- " sources and change 'https' to 'http'."
+ " https://railsapps.github.io/openssl-certificate-verify-failed.html."
end
end
@@ -39,9 +37,7 @@ module Bundler
class SSLError < HTTPError
def initialize(msg = nil)
super msg || "Could not load OpenSSL.\n" \
- "You must recompile Ruby with OpenSSL support or change the sources in your " \
- "Gemfile from 'https' to 'http'. Instructions for compiling with OpenSSL " \
- "using RVM are available at rvm.io/packages/openssl."
+ "You must recompile Ruby with OpenSSL support."
end
end
@@ -65,6 +61,16 @@ module Bundler
end
end
+ # This error is raised if HTTP authentication is correct, but lacks
+ # necessary permissions.
+ class AuthenticationForbiddenError < HTTPError
+ def initialize(remote_uri)
+ remote_uri = filter_uri(remote_uri)
+ super "Access token could not be authenticated for #{remote_uri}.\n" \
+ "Make sure it's valid and has the necessary scopes configured."
+ end
+ end
+
# Exceptions classes that should bypass retry attempts. If your password didn't work the
# first time, it's not going to the third time.
NET_ERRORS = [:HTTPBadGateway, :HTTPBadRequest, :HTTPFailedDependency,
@@ -74,7 +80,7 @@ module Bundler
:HTTPRequestURITooLong, :HTTPUnauthorized, :HTTPUnprocessableEntity,
:HTTPUnsupportedMediaType, :HTTPVersionNotSupported].freeze
FAIL_ERRORS = begin
- fail_errors = [AuthenticationRequiredError, BadAuthenticationError, FallbackError]
+ fail_errors = [AuthenticationRequiredError, BadAuthenticationError, AuthenticationForbiddenError, FallbackError]
fail_errors << Gem::Requirement::BadRequirementError
fail_errors.concat(NET_ERRORS.map {|e| Net.const_get(e) })
end.freeze
@@ -106,11 +112,11 @@ module Bundler
uri = Bundler::URI.parse("#{remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
if uri.scheme == "file"
path = Bundler.rubygems.correct_for_windows_path(uri.path)
- Bundler.load_marshal Bundler.rubygems.inflate(Gem.read_binary(path))
+ Bundler.safe_load_marshal Bundler.rubygems.inflate(Gem.read_binary(path))
elsif cached_spec_path = gemspec_cached_path(spec_file_name)
Bundler.load_gemspec(cached_spec_path)
else
- Bundler.load_marshal Bundler.rubygems.inflate(downloader.fetch(uri).body)
+ Bundler.safe_load_marshal Bundler.rubygems.inflate(downloader.fetch(uri).body)
end
rescue MarshalError
raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \
@@ -240,8 +246,8 @@ module Bundler
def connection
@connection ||= begin
needs_ssl = remote_uri.scheme == "https" ||
- Bundler.settings[:ssl_verify_mode] ||
- Bundler.settings[:ssl_client_cert]
+ Bundler.settings[:ssl_verify_mode] ||
+ Bundler.settings[:ssl_client_cert]
raise SSLError if needs_ssl && !defined?(OpenSSL::SSL)
con = PersistentHTTP.new :name => "bundler", :proxy => :ENV
@@ -256,8 +262,8 @@ module Bundler
end
ssl_client_cert = Bundler.settings[:ssl_client_cert] ||
- (Gem.configuration.ssl_client_cert if
- Gem.configuration.respond_to?(:ssl_client_cert))
+ (Gem.configuration.ssl_client_cert if
+ Gem.configuration.respond_to?(:ssl_client_cert))
if ssl_client_cert
pem = File.read(ssl_client_cert)
con.cert = OpenSSL::X509::Certificate.new(pem)
@@ -288,8 +294,8 @@ module Bundler
def bundler_cert_store
store = OpenSSL::X509::Store.new
ssl_ca_cert = Bundler.settings[:ssl_ca_cert] ||
- (Gem.configuration.ssl_ca_cert if
- Gem.configuration.respond_to?(:ssl_ca_cert))
+ (Gem.configuration.ssl_ca_cert if
+ Gem.configuration.respond_to?(:ssl_ca_cert))
if ssl_ca_cert
if File.directory? ssl_ca_cert
store.add_path ssl_ca_cert
diff --git a/lib/bundler/fetcher/compact_index.rb b/lib/bundler/fetcher/compact_index.rb
index b23176588f..8d30dec471 100644
--- a/lib/bundler/fetcher/compact_index.rb
+++ b/lib/bundler/fetcher/compact_index.rb
@@ -12,17 +12,15 @@ module Bundler
method = instance_method(method_name)
undef_method(method_name)
define_method(method_name) do |*args, &blk|
- begin
- method.bind(self).call(*args, &blk)
- rescue NetworkDownError, CompactIndexClient::Updater::MisMatchedChecksumError => e
- raise HTTPError, e.message
- rescue AuthenticationRequiredError
- # Fail since we got a 401 from the server.
- raise
- rescue HTTPError => e
- Bundler.ui.trace(e)
- nil
- end
+ method.bind(self).call(*args, &blk)
+ rescue NetworkDownError, CompactIndexClient::Updater::MisMatchedChecksumError => e
+ raise HTTPError, e.message
+ rescue AuthenticationRequiredError, BadAuthenticationError
+ # Fail since we got a 401 from the server.
+ raise
+ rescue HTTPError => e
+ Bundler.ui.trace(e)
+ nil
end
end
@@ -42,7 +40,7 @@ module Bundler
deps = begin
parallel_compact_index_client.dependencies(remaining_gems)
rescue TooManyRequestsError
- @bundle_worker.stop if @bundle_worker
+ @bundle_worker&.stop
@bundle_worker = nil # reset it. Not sure if necessary
serial_compact_index_client.dependencies(remaining_gems)
end
@@ -51,7 +49,7 @@ module Bundler
complete_gems.concat(deps.map(&:first)).uniq!
remaining_gems = next_gems - complete_gems
end
- @bundle_worker.stop if @bundle_worker
+ @bundle_worker&.stop
@bundle_worker = nil # reset it. Not sure if necessary
gem_info
diff --git a/lib/bundler/fetcher/dependency.rb b/lib/bundler/fetcher/dependency.rb
index c52c32fb5b..18b606abb6 100644
--- a/lib/bundler/fetcher/dependency.rb
+++ b/lib/bundler/fetcher/dependency.rb
@@ -34,14 +34,10 @@ module Bundler
returned_gems = spec_list.map(&:first).uniq
specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list)
- rescue MarshalError
+ rescue MarshalError, HTTPError, GemspecError
Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
Bundler.ui.debug "could not fetch from the dependency API, trying the full index"
nil
- rescue HTTPError, GemspecError
- Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
- Bundler.ui.debug "could not fetch from the dependency API\nit's suggested to retry using the full index via `bundle install --full-index`"
- nil
end
def dependency_specs(gem_names)
@@ -55,7 +51,7 @@ module Bundler
gem_list = []
gem_names.each_slice(Source::Rubygems::API_REQUEST_SIZE) do |names|
marshalled_deps = downloader.fetch(dependency_api_uri(names)).body
- gem_list.concat(Bundler.load_marshal(marshalled_deps))
+ gem_list.concat(Bundler.safe_load_marshal(marshalled_deps))
end
gem_list
end
diff --git a/lib/bundler/fetcher/downloader.rb b/lib/bundler/fetcher/downloader.rb
index f2aad3a500..3062899e0e 100644
--- a/lib/bundler/fetcher/downloader.rb
+++ b/lib/bundler/fetcher/downloader.rb
@@ -41,6 +41,8 @@ module Bundler
when Net::HTTPUnauthorized
raise BadAuthenticationError, uri.host if uri.userinfo
raise AuthenticationRequiredError, uri.host
+ when Net::HTTPForbidden
+ raise AuthenticationForbiddenError, uri.host
when Net::HTTPNotFound
raise FallbackError, "Net::HTTPNotFound: #{filtered_uri}"
else
@@ -61,14 +63,11 @@ module Bundler
req.basic_auth(user, password)
end
connection.request(uri, req)
- rescue NoMethodError => e
- raise unless ["undefined method", "use_ssl="].all? {|snippet| e.message.include? snippet }
- raise LoadError.new("cannot load such file -- openssl")
rescue OpenSSL::SSL::SSLError
raise CertificateFailureError.new(uri)
rescue *HTTP_ERRORS => e
Bundler.ui.trace e
- if e.is_a?(SocketError) || e.message =~ /host down:/
+ if e.is_a?(SocketError) || e.message.to_s.include?("host down:")
raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \
"connection and try again."
else
@@ -80,7 +79,7 @@ module Bundler
private
def validate_uri_scheme!(uri)
- return if uri.scheme =~ /\Ahttps?\z/
+ return if /\Ahttps?\z/.match?(uri.scheme)
raise InvalidOption,
"The request uri `#{uri}` has an invalid scheme (`#{uri.scheme}`). " \
"Did you mean `http` or `https`?"
diff --git a/lib/bundler/fetcher/index.rb b/lib/bundler/fetcher/index.rb
index 6bb9fcc193..c623647f01 100644
--- a/lib/bundler/fetcher/index.rb
+++ b/lib/bundler/fetcher/index.rb
@@ -15,8 +15,7 @@ module Bundler
raise BadAuthenticationError, remote_uri if remote_uri.userinfo
raise AuthenticationRequiredError, remote_uri
when /403/
- raise BadAuthenticationError, remote_uri if remote_uri.userinfo
- raise AuthenticationRequiredError, remote_uri
+ raise AuthenticationForbiddenError, remote_uri
else
raise HTTPError, "Could not fetch specs from #{display_uri} due to underlying error <#{e.message}>"
end
diff --git a/lib/bundler/force_platform.rb b/lib/bundler/force_platform.rb
index 0648ea9737..249a24ecd1 100644
--- a/lib/bundler/force_platform.rb
+++ b/lib/bundler/force_platform.rb
@@ -10,7 +10,7 @@ module Bundler
# variant unless the name is explicitly allowlisted.
def default_force_ruby_platform
- return false unless Bundler.current_ruby.truffleruby?
+ return false unless RUBY_ENGINE == "truffleruby"
!Gem::Platform::REUSE_AS_BINARY_ON_TRUFFLERUBY.include?(name)
end
diff --git a/lib/bundler/friendly_errors.rb b/lib/bundler/friendly_errors.rb
index d0d2a6679a..39afe8a071 100644
--- a/lib/bundler/friendly_errors.rb
+++ b/lib/bundler/friendly_errors.rb
@@ -36,9 +36,6 @@ module Bundler
end
when Thor::Error
Bundler.ui.error error.message
- when LoadError
- raise error unless error.message =~ /cannot load such file -- openssl|openssl.so|libcrypto.so/
- Bundler.ui.error "\nCould not load OpenSSL. #{error.class}: #{error}\n#{error.backtrace.join("\n ")}"
when Interrupt
Bundler.ui.error "\nQuitting..."
Bundler.ui.trace error
@@ -98,7 +95,7 @@ module Bundler
def serialized_exception_for(e)
<<-EOS.gsub(/^ {8}/, "")
#{e.class}: #{e.message}
- #{e.backtrace && e.backtrace.join("\n ").chomp}
+ #{e.backtrace&.join("\n ")&.chomp}
EOS
end
diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb
index 0bbd2d9b75..dcf759cded 100644
--- a/lib/bundler/gem_helper.rb
+++ b/lib/bundler/gem_helper.rb
@@ -21,7 +21,7 @@ module Bundler
def gemspec(&block)
gemspec = instance.gemspec
- block.call(gemspec) if block
+ block&.call(gemspec)
gemspec
end
end
@@ -152,8 +152,7 @@ module Bundler
def gem_push_host
env_rubygems_host = ENV["RUBYGEMS_HOST"]
- env_rubygems_host = nil if
- env_rubygems_host && env_rubygems_host.empty?
+ env_rubygems_host = nil if env_rubygems_host&.empty?
allowed_push_host || env_rubygems_host || "rubygems.org"
end
@@ -218,7 +217,7 @@ module Bundler
SharedHelpers.chdir(base) do
outbuf = IO.popen(cmd, :err => [:child, :out], &:read)
status = $?
- block.call(outbuf) if status.success? && block
+ block&.call(outbuf) if status.success?
[outbuf, status]
end
end
diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb
index 6bc4fb4991..2e6d788f9c 100644
--- a/lib/bundler/gem_helpers.rb
+++ b/lib/bundler/gem_helpers.rb
@@ -5,7 +5,6 @@ module Bundler
GENERIC_CACHE = { Gem::Platform::RUBY => Gem::Platform::RUBY } # rubocop:disable Style/MutableConstant
GENERICS = [
[Gem::Platform.new("java"), Gem::Platform.new("java")],
- [Gem::Platform.new("universal-java"), Gem::Platform.new("java")],
[Gem::Platform.new("mswin32"), Gem::Platform.new("mswin32")],
[Gem::Platform.new("mswin64"), Gem::Platform.new("mswin64")],
[Gem::Platform.new("universal-mingw32"), Gem::Platform.new("universal-mingw32")],
@@ -44,6 +43,12 @@ module Bundler
def select_best_platform_match(specs, platform)
matching = specs.select {|spec| spec.match_platform(platform) }
+
+ sort_best_platform_match(matching, platform)
+ end
+ module_function :select_best_platform_match
+
+ def sort_best_platform_match(matching, platform)
exact = matching.select {|spec| spec.platform == platform }
return exact if exact.any?
@@ -52,7 +57,7 @@ module Bundler
sorted_matching.take_while {|spec| same_specificity(platform, spec, exemplary_spec) && same_deps(spec, exemplary_spec) }
end
- module_function :select_best_platform_match
+ module_function :sort_best_platform_match
class PlatformMatch
def self.specificity_score(spec_platform, user_platform)
diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb
index 3cce3f2139..d281f46eeb 100644
--- a/lib/bundler/gem_version_promoter.rb
+++ b/lib/bundler/gem_version_promoter.rb
@@ -7,14 +7,13 @@ module Bundler
# available dependency versions as found in its index, before returning it to
# to the resolution engine to select the best version.
class GemVersionPromoter
- DEBUG = ENV["BUNDLER_DEBUG_RESOLVER"] || ENV["DEBUG_RESOLVER"]
-
- attr_reader :level, :locked_specs, :unlock_gems
+ attr_reader :level
+ attr_accessor :pre
# By default, strict is false, meaning every available version of a gem
# is returned from sort_versions. The order gives preference to the
# requested level (:patch, :minor, :major) but in complicated requirement
- # cases some gems will by necessity by promoted past the requested level,
+ # cases some gems will by necessity be promoted past the requested level,
# or even reverted to older versions.
#
# If strict is set to true, the results from sort_versions will be
@@ -24,24 +23,13 @@ module Bundler
# existing in the referenced source.
attr_accessor :strict
- attr_accessor :prerelease_specified
-
- # Given a list of locked_specs and a list of gems to unlock creates a
- # GemVersionPromoter instance.
+ # Creates a GemVersionPromoter instance.
#
- # @param locked_specs [SpecSet] All current locked specs. Unlike Definition
- # where this list is empty if all gems are being updated, this should
- # always be populated for all gems so this class can properly function.
- # @param unlock_gems [String] List of gem names being unlocked. If empty,
- # all gems will be considered unlocked.
# @return [GemVersionPromoter]
- def initialize(locked_specs = SpecSet.new([]), unlock_gems = [])
+ def initialize
@level = :major
@strict = false
- @locked_specs = locked_specs
- @unlock_gems = unlock_gems
- @sort_versions = {}
- @prerelease_specified = {}
+ @pre = false
end
# @param value [Symbol] One of three Symbols: :major, :minor or :patch.
@@ -55,37 +43,19 @@ module Bundler
@level = v
end
- # Given a Dependency and an Array of SpecGroups of available versions for a
- # gem, this method will return the Array of SpecGroups sorted (and possibly
- # truncated if strict is true) in an order to give preference to the current
- # level (:major, :minor or :patch) when resolution is deciding what versions
- # best resolve all dependencies in the bundle.
- # @param dep [Dependency] The Dependency of the gem.
- # @param spec_groups [SpecGroup] An array of SpecGroups for the same gem
- # named in the @dep param.
- # @return [SpecGroup] A new instance of the SpecGroup Array sorted and
+ # Given a Resolver::Package and an Array of Specifications of available
+ # versions for a gem, this method will return the Array of Specifications
+ # sorted (and possibly truncated if strict is true) in an order to give
+ # preference to the current level (:major, :minor or :patch) when resolution
+ # is deciding what versions best resolve all dependencies in the bundle.
+ # @param package [Resolver::Package] The package being resolved.
+ # @param specs [Specification] An array of Specifications for the package.
+ # @return [Specification] A new instance of the Specification Array sorted and
# possibly filtered.
- def sort_versions(dep, spec_groups)
- before_result = "before sort_versions: #{debug_format_result(dep, spec_groups).inspect}" if DEBUG
-
- @sort_versions[dep] ||= begin
- gem_name = dep.name
+ def sort_versions(package, specs)
+ specs = filter_dep_specs(specs, package) if strict
- # An Array per version returned, different entries for different platforms.
- # We only need the version here so it's ok to hard code this to the first instance.
- locked_spec = locked_specs[gem_name].first
-
- if strict
- filter_dep_specs(spec_groups, locked_spec)
- else
- sort_dep_specs(spec_groups, locked_spec)
- end.tap do |specs|
- if DEBUG
- puts before_result
- puts " after sort_versions: #{debug_format_result(dep, specs).inspect}"
- end
- end
- end
+ sort_dep_specs(specs, package)
end
# @return [bool] Convenience method for testing value of level variable.
@@ -98,80 +68,72 @@ module Bundler
level == :minor
end
+ # @return [bool] Convenience method for testing value of pre variable.
+ def pre?
+ pre == true
+ end
+
private
- def filter_dep_specs(spec_groups, locked_spec)
- res = spec_groups.select do |spec_group|
- if locked_spec && !major?
- gsv = spec_group.version
- lsv = locked_spec.version
+ def filter_dep_specs(specs, package)
+ locked_version = package.locked_version
+ return specs if locked_version.nil? || major?
- must_match = minor? ? [0] : [0, 1]
+ specs.select do |spec|
+ gsv = spec.version
- matches = must_match.map {|idx| gsv.segments[idx] == lsv.segments[idx] }
- matches.uniq == [true] ? (gsv >= lsv) : false
- else
- true
- end
- end
+ must_match = minor? ? [0] : [0, 1]
- sort_dep_specs(res, locked_spec)
+ all_match = must_match.all? {|idx| gsv.segments[idx] == locked_version.segments[idx] }
+ all_match && gsv >= locked_version
+ end
end
- def sort_dep_specs(spec_groups, locked_spec)
- return spec_groups unless locked_spec
- @gem_name = locked_spec.name
- @locked_version = locked_spec.version
-
- result = spec_groups.sort do |a, b|
- @a_ver = a.version
- @b_ver = b.version
+ def sort_dep_specs(specs, package)
+ locked_version = package.locked_version
- unless @prerelease_specified[@gem_name]
- a_pre = @a_ver.prerelease?
- b_pre = @b_ver.prerelease?
+ result = specs.sort do |a, b|
+ unless package.prerelease_specified? || pre?
+ a_pre = a.prerelease?
+ b_pre = b.prerelease?
next -1 if a_pre && !b_pre
next 1 if b_pre && !a_pre
end
if major?
- @a_ver <=> @b_ver
- elsif either_version_older_than_locked
- @a_ver <=> @b_ver
- elsif segments_do_not_match(:major)
- @b_ver <=> @a_ver
- elsif !minor? && segments_do_not_match(:minor)
- @b_ver <=> @a_ver
+ a <=> b
+ elsif either_version_older_than_locked?(a, b, locked_version)
+ a <=> b
+ elsif segments_do_not_match?(a, b, :major)
+ b <=> a
+ elsif !minor? && segments_do_not_match?(a, b, :minor)
+ b <=> a
else
- @a_ver <=> @b_ver
+ a <=> b
end
end
- post_sort(result)
+ post_sort(result, package.unlock?, locked_version)
end
- def either_version_older_than_locked
- @a_ver < @locked_version || @b_ver < @locked_version
+ def either_version_older_than_locked?(a, b, locked_version)
+ locked_version && (a.version < locked_version || b.version < locked_version)
end
- def segments_do_not_match(level)
+ def segments_do_not_match?(a, b, level)
index = [:major, :minor].index(level)
- @a_ver.segments[index] != @b_ver.segments[index]
- end
-
- def unlocking_gem?
- unlock_gems.empty? || unlock_gems.include?(@gem_name)
+ a.segments[index] != b.segments[index]
end
# Specific version moves can't always reliably be done during sorting
# as not all elements are compared against each other.
- def post_sort(result)
+ def post_sort(result, unlock, locked_version)
# default :major behavior in Bundler does not do this
return result if major?
- if unlocking_gem?
+ if unlock || locked_version.nil?
result
else
- move_version_to_end(result, @locked_version)
+ move_version_to_end(result, locked_version)
end
end
@@ -179,12 +141,5 @@ module Bundler
move, keep = result.partition {|s| s.version.to_s == version.to_s }
keep.concat(move)
end
-
- def debug_format_result(dep, spec_groups)
- a = [dep.to_s,
- spec_groups.map {|sg| [sg.version, sg.dependencies_for_activated_platforms.map {|dp| [dp.name, dp.requirement.to_s] }] }]
- last_map = a.last.map {|sg_data| [sg_data.first.version, sg_data.last.map {|aa| aa.join(" ") }] }
- [a.first, last_map, level, strict ? :strict : :not_strict]
- end
end
end
diff --git a/lib/bundler/graph.rb b/lib/bundler/graph.rb
index 8f52e2f0f0..3c008e63e3 100644
--- a/lib/bundler/graph.rb
+++ b/lib/bundler/graph.rb
@@ -114,10 +114,10 @@ module Bundler
@groups.each do |group|
g.add_nodes(
group, {
- :style => "filled",
+ :style => "filled",
:fillcolor => "#B9B9D5",
- :shape => "box3d",
- :fontsize => 16,
+ :shape => "box3d",
+ :fontsize => 16,
}.merge(@node_options[group])
)
end
diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb
index 5bc24fc0b2..b8c599f63a 100644
--- a/lib/bundler/index.rb
+++ b/lib/bundler/index.rb
@@ -13,8 +13,8 @@ module Bundler
attr_reader :specs, :all_specs, :sources
protected :specs, :all_specs
- RUBY = "ruby".freeze
- NULL = "\0".freeze
+ RUBY = "ruby"
+ NULL = "\0"
def initialize
@sources = []
@@ -56,45 +56,21 @@ module Bundler
# Search this index's specs, and any source indexes that this index knows
# about, returning all of the results.
- def search(query, base = nil)
- sort_specs(unsorted_search(query, base))
- end
-
- def unsorted_search(query, base)
- results = local_search(query, base)
-
- seen = results.map(&:full_name).uniq unless @sources.empty?
+ def search(query)
+ results = local_search(query)
+ return results unless @sources.any?
@sources.each do |source|
- source.unsorted_search(query, base).each do |spec|
- next if seen.include?(spec.full_name)
-
- seen << spec.full_name
- results << spec
- end
- end
-
- results
- end
- protected :unsorted_search
-
- def self.sort_specs(specs)
- specs.sort_by do |s|
- platform_string = s.platform.to_s
- [s.version, platform_string == RUBY ? NULL : platform_string]
+ results.concat(source.search(query))
end
+ results.uniq(&:full_name)
end
- def sort_specs(specs)
- self.class.sort_specs(specs)
- end
-
- def local_search(query, base = nil)
+ def local_search(query)
case query
when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query)
when String then specs_by_name(query)
- when Gem::Dependency then search_by_dependency(query, base)
- when DepProxy then search_by_dependency(query.dep, base)
+ when Array then specs_by_name_and_version(*query)
else
raise "You can't search for a #{query.inspect}."
end
@@ -181,22 +157,12 @@ module Bundler
private
- def specs_by_name(name)
- @specs[name].values
+ def specs_by_name_and_version(name, version)
+ specs_by_name(name).select {|spec| spec.version == version }
end
- def search_by_dependency(dependency, base = nil)
- @cache[base || false] ||= {}
- @cache[base || false][dependency] ||= begin
- specs = specs_by_name(dependency.name)
- specs += base if base
- found = specs.select do |spec|
- next true if spec.source.is_a?(Source::Gemspec)
- dependency.matches_spec?(spec)
- end
-
- found
- end
+ def specs_by_name(name)
+ @specs[name].values
end
EMPTY_SEARCH = [].freeze
diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb
index f6550abe88..cb644a7f69 100644
--- a/lib/bundler/injector.rb
+++ b/lib/bundler/injector.rb
@@ -2,7 +2,7 @@
module Bundler
class Injector
- INJECTED_GEMS = "injected gems".freeze
+ INJECTED_GEMS = "injected gems"
def self.inject(new_deps, options = {})
injector = new(new_deps, options)
@@ -70,7 +70,7 @@ module Bundler
show_warning("No gems were removed from the gemfile.") if deps.empty?
- deps.each {|dep| Bundler.ui.confirm "#{SharedHelpers.pretty_dependency(dep, false)} was removed." }
+ deps.each {|dep| Bundler.ui.confirm "#{SharedHelpers.pretty_dependency(dep)} was removed." }
end
# Invalidate the cached Bundler.definition.
@@ -115,13 +115,14 @@ module Bundler
end
source = ", :source => \"#{d.source}\"" unless d.source.nil?
+ path = ", :path => \"#{d.path}\"" unless d.path.nil?
git = ", :git => \"#{d.git}\"" unless d.git.nil?
github = ", :github => \"#{d.github}\"" unless d.github.nil?
branch = ", :branch => \"#{d.branch}\"" unless d.branch.nil?
ref = ", :ref => \"#{d.ref}\"" unless d.ref.nil?
require_path = ", :require => #{convert_autorequire(d.autorequire)}" unless d.autorequire.nil?
- %(gem #{name}#{requirement}#{group}#{source}#{git}#{github}#{branch}#{ref}#{require_path})
+ %(gem #{name}#{requirement}#{group}#{source}#{path}#{git}#{github}#{branch}#{ref}#{require_path})
end.join("\n")
end
@@ -234,7 +235,7 @@ module Bundler
gemfile.each_with_index do |line, index|
next unless !line.nil? && line.strip.start_with?(block_name)
- if gemfile[index + 1] =~ /^\s*end\s*$/
+ if /^\s*end\s*$/.match?(gemfile[index + 1])
gemfile[index] = nil
gemfile[index + 1] = nil
end
diff --git a/lib/bundler/inline.rb b/lib/bundler/inline.rb
index 8ef580f1f0..5c184f67a1 100644
--- a/lib/bundler/inline.rb
+++ b/lib/bundler/inline.rb
@@ -31,15 +31,16 @@
#
def gemfile(install = false, options = {}, &gemfile)
require_relative "../bundler"
+ Bundler.reset!
opts = options.dup
ui = opts.delete(:ui) { Bundler::UI::Shell.new }
- ui.level = "silent" if opts.delete(:quiet)
+ ui.level = "silent" if opts.delete(:quiet) || !install
+ Bundler.ui = ui
raise ArgumentError, "Unknown options: #{opts.keys.join(", ")}" unless opts.empty?
- begin
+ Bundler.with_unbundled_env do
Bundler.instance_variable_set(:@bundle_path, Pathname.new(Gem.dir))
- old_gemfile = ENV["BUNDLE_GEMFILE"]
Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", "Gemfile"
Bundler::Plugin.gemfile_install(&gemfile) if Bundler.feature_flag.plugins?
@@ -52,9 +53,8 @@ def gemfile(install = false, options = {}, &gemfile)
def definition.lock(*); end
definition.validate_runtime!
- Bundler.ui = install ? ui : Bundler::UI::Silent.new
if install || definition.missing_specs?
- Bundler.settings.temporary(:inline => true) do
+ Bundler.settings.temporary(:inline => true, :no_install => false) do
installer = Bundler::Installer.install(Bundler.root, definition, :system => true)
installer.post_install_messages.each do |name, message|
Bundler.ui.info "Post-install message from #{name}:\n#{message}"
@@ -65,11 +65,9 @@ def gemfile(install = false, options = {}, &gemfile)
runtime = Bundler::Runtime.new(nil, definition)
runtime.setup.require
end
- ensure
- if old_gemfile
- ENV["BUNDLE_GEMFILE"] = old_gemfile
- else
- ENV["BUNDLE_GEMFILE"] = ""
- end
+ end
+
+ if ENV["BUNDLE_GEMFILE"].nil?
+ ENV["BUNDLE_GEMFILE"] = ""
end
end
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
index f195d36600..59b6a6ad22 100644
--- a/lib/bundler/installer.rb
+++ b/lib/bundler/installer.rb
@@ -90,7 +90,7 @@ module Bundler
Gem::Specification.reset # invalidate gem specification cache so that installed gems are immediately available
- lock unless Bundler.frozen_bundle?
+ lock
Standalone.new(options[:standalone], @definition).generate if options[:standalone]
end
end
@@ -136,11 +136,7 @@ module Bundler
mode = Gem.win_platform? ? "wb:UTF-8" : "w"
require "erb"
- content = if RUBY_VERSION >= "2.6"
- ERB.new(template, :trim_mode => "-").result(binding)
- else
- ERB.new(template, nil, "-").result(binding)
- end
+ content = ERB.new(template, :trim_mode => "-").result(binding)
File.write(binstub_path, content, :mode => mode, :perm => 0o777 & ~File.umask)
if Gem.win_platform? || options[:all_platforms]
@@ -183,11 +179,7 @@ module Bundler
mode = Gem.win_platform? ? "wb:UTF-8" : "w"
require "erb"
- content = if RUBY_VERSION >= "2.6"
- ERB.new(template, :trim_mode => "-").result(binding)
- else
- ERB.new(template, nil, "-").result(binding)
- end
+ content = ERB.new(template, :trim_mode => "-").result(binding)
File.write("#{bin_path}/#{executable}", content, :mode => mode, :perm => 0o755)
if Gem.win_platform? || options[:all_platforms]
@@ -226,31 +218,24 @@ module Bundler
requested_path_gems = @definition.requested_specs.select {|s| s.source.is_a?(Source::Path) }
path_plugin_files = requested_path_gems.map do |spec|
- begin
- Bundler.rubygems.spec_matches_for_glob(spec, "rubygems_plugin#{Bundler.rubygems.suffix_pattern}")
- rescue TypeError
- error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
- raise Gem::InvalidSpecificationException, error_message
- end
+ Bundler.rubygems.spec_matches_for_glob(spec, "rubygems_plugin#{Bundler.rubygems.suffix_pattern}")
+ rescue TypeError
+ error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
+ raise Gem::InvalidSpecificationException, error_message
end.flatten
Bundler.rubygems.load_plugin_files(path_plugin_files)
Bundler.rubygems.load_env_plugins
end
def ensure_specs_are_compatible!
- system_ruby = Bundler::RubyVersion.system
- rubygems_version = Bundler.rubygems.version
@definition.specs.each do |spec|
- if required_ruby_version = spec.required_ruby_version
- unless required_ruby_version.satisfied_by?(system_ruby.gem_version)
- raise InstallError, "#{spec.full_name} requires ruby version #{required_ruby_version}, " \
- "which is incompatible with the current version, #{system_ruby}"
- end
+ unless spec.matches_current_ruby?
+ raise InstallError, "#{spec.full_name} requires ruby version #{spec.required_ruby_version}, " \
+ "which is incompatible with the current version, #{Gem.ruby_version}"
end
- next unless required_rubygems_version = spec.required_rubygems_version
- unless required_rubygems_version.satisfied_by?(rubygems_version)
- raise InstallError, "#{spec.full_name} requires rubygems version #{required_rubygems_version}, " \
- "which is incompatible with the current version, #{rubygems_version}"
+ unless spec.matches_current_rubygems?
+ raise InstallError, "#{spec.full_name} requires rubygems version #{spec.required_rubygems_version}, " \
+ "which is incompatible with the current version, #{Gem.rubygems_version}"
end
end
end
@@ -264,11 +249,14 @@ module Bundler
# returns whether or not a re-resolve was needed
def resolve_if_needed(options)
+ @definition.resolution_mode = options
+
if !@definition.unlocking? && !options["force"] && !Bundler.settings[:inline] && Bundler.default_lockfile.file?
return false if @definition.nothing_changed? && !@definition.missing_specs?
end
- options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely!
+ @definition.setup_sources_for_resolve
+
true
end
diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb
index 5b6680e5e1..83a381f592 100644
--- a/lib/bundler/installer/parallel_installer.rb
+++ b/lib/bundler/installer/parallel_installer.rb
@@ -53,10 +53,6 @@ module Bundler
@dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep }
end
- def missing_lockfile_dependencies(all_spec_names)
- dependencies.reject {|dep| all_spec_names.include? dep.name }
- end
-
# Represents all dependencies
def all_dependencies
@spec.dependencies
@@ -84,8 +80,6 @@ module Bundler
end
def call
- check_for_corrupt_lockfile
-
if @rake
do_install(@rake, 0)
Gem::Specification.reset
@@ -102,7 +96,7 @@ module Bundler
handle_error if failed_specs.any?
@specs
ensure
- worker_pool && worker_pool.stop
+ worker_pool&.stop
end
def check_for_unmet_dependencies
@@ -116,43 +110,19 @@ module Bundler
warning = []
warning << "Your lockfile doesn't include a valid resolution."
- warning << "You can fix this by regenerating your lockfile or trying to manually editing the bad locked gems to a version that satisfies all dependencies."
+ warning << "You can fix this by regenerating your lockfile or manually editing the bad locked gems to a version that satisfies all dependencies."
warning << "The unmet dependencies are:"
unmet_dependencies.each do |spec, unmet_spec_dependencies|
unmet_spec_dependencies.each do |unmet_spec_dependency|
- warning << "* #{unmet_spec_dependency}, depended upon #{spec.full_name}, unsatisfied by #{@specs.find {|s| s.name == unmet_spec_dependency.name && !unmet_spec_dependency.matches_spec?(s.spec) }.full_name}"
+ found = @specs.find {|s| s.name == unmet_spec_dependency.name && !unmet_spec_dependency.matches_spec?(s.spec) }
+ warning << "* #{unmet_spec_dependency}, dependency of #{spec.full_name}, unsatisfied by #{found.full_name}"
end
end
Bundler.ui.warn(warning.join("\n"))
end
- def check_for_corrupt_lockfile
- missing_dependencies = @specs.map do |s|
- [
- s,
- s.missing_lockfile_dependencies(@specs.map(&:name)),
- ]
- end.reject {|a| a.last.empty? }
- return if missing_dependencies.empty?
-
- warning = []
- warning << "Your lockfile was created by an old Bundler that left some things out."
- if @size != 1
- warning << "Because of the missing DEPENDENCIES, we can only install gems one at a time, instead of installing #{@size} at a time."
- @size = 1
- end
- warning << "You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile."
- warning << "The missing gems are:"
-
- missing_dependencies.each do |spec, missing|
- warning << "* #{missing.map(&:name).join(", ")} depended upon by #{spec.name}"
- end
-
- Bundler.ui.warn(warning.join("\n"))
- end
-
private
def failed_specs
diff --git a/lib/bundler/installer/standalone.rb b/lib/bundler/installer/standalone.rb
index 248b677233..2a8c9a432d 100644
--- a/lib/bundler/installer/standalone.rb
+++ b/lib/bundler/installer/standalone.rb
@@ -47,12 +47,12 @@ module Bundler
end
def bundler_path
- Bundler.root.join(Bundler.settings[:path], "bundler")
+ Bundler.root.join(Bundler.settings[:path].to_s, "bundler")
end
def gem_path(path, spec)
full_path = Pathname.new(path).absolute? ? path : File.join(spec.full_gem_path, path)
- if spec.source.instance_of?(Source::Path)
+ if spec.source.instance_of?(Source::Path) && spec.source.path.absolute?
full_path
else
Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path)).to_s
@@ -84,13 +84,17 @@ module Bundler
def reverse_rubygems_kernel_mixin
<<~END
- kernel = (class << ::Kernel; self; end)
- [kernel, ::Kernel].each do |k|
- if k.private_method_defined?(:gem_original_require)
- private_require = k.private_method_defined?(:require)
- k.send(:remove_method, :require)
- k.send(:define_method, :require, k.instance_method(:gem_original_require))
- k.send(:private, :require) if private_require
+ if Gem.respond_to?(:discover_gems_on_require=)
+ Gem.discover_gems_on_require = false
+ else
+ kernel = (class << ::Kernel; self; end)
+ [kernel, ::Kernel].each do |k|
+ if k.private_method_defined?(:gem_original_require)
+ private_require = k.private_method_defined?(:require)
+ k.send(:remove_method, :require)
+ k.send(:define_method, :require, k.instance_method(:gem_original_require))
+ k.send(:private, :require) if private_require
+ end
end
end
END
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb
index 89b21efc04..c9b161dc0e 100644
--- a/lib/bundler/lazy_specification.rb
+++ b/lib/bundler/lazy_specification.rb
@@ -1,16 +1,14 @@
# frozen_string_literal: true
require_relative "force_platform"
-require_relative "match_platform"
module Bundler
class LazySpecification
- include ForcePlatform
include MatchPlatform
+ include ForcePlatform
attr_reader :name, :version, :dependencies, :platform
- attr_writer :force_ruby_platform
- attr_accessor :source, :remote
+ attr_accessor :source, :remote, :force_ruby_platform
def initialize(name, version, platform, source = nil)
@name = name
@@ -18,34 +16,27 @@ module Bundler
@dependencies = []
@platform = platform || Gem::Platform::RUBY
@source = source
- @specification = nil
- @force_ruby_platform = nil
+ @force_ruby_platform = default_force_ruby_platform
end
def full_name
- if platform == Gem::Platform::RUBY || platform.nil?
+ @full_name ||= if platform == Gem::Platform::RUBY
"#{@name}-#{@version}"
else
"#{@name}-#{@version}-#{platform}"
end
end
- def force_ruby_platform
- return @force_ruby_platform unless @force_ruby_platform.nil?
-
- default_force_ruby_platform
- end
-
def ==(other)
- identifier == other.identifier
+ full_name == other.full_name
end
def eql?(other)
- identifier.eql?(other.identifier)
+ full_name.eql?(other.full_name)
end
def hash
- identifier.hash
+ full_name.hash
end
##
@@ -71,7 +62,7 @@ module Bundler
def to_lock
out = String.new
- if platform == Gem::Platform::RUBY || platform.nil?
+ if platform == Gem::Platform::RUBY
out << " #{name} (#{version})\n"
else
out << " #{name} (#{version}-#{platform})\n"
@@ -85,76 +76,74 @@ module Bundler
out
end
- def __materialize__
- @specification = if source.is_a?(Source::Gemspec) && source.gemspec.name == name
- source.gemspec.tap {|s| s.source = source }
+ def materialize_for_installation
+ source.local!
+
+ matching_specs = source.specs.search(use_exact_resolved_specifications? ? self : [name, version])
+ return self if matching_specs.empty?
+
+ candidates = if use_exact_resolved_specifications?
+ matching_specs
else
- search_object = if source.is_a?(Source::Path)
- Dependency.new(name, version)
- else
- ruby_platform_materializes_to_ruby_platform? ? self : Dependency.new(name, version)
- end
- platform_object = ruby_platform_materializes_to_ruby_platform? ? Gem::Platform.new(platform) : Gem::Platform.local
- candidates = source.specs.search(search_object)
- same_platform_candidates = candidates.select do |spec|
- MatchPlatform.platforms_match?(spec.platform, platform_object)
- end
- installable_candidates = same_platform_candidates.select do |spec|
- spec.is_a?(StubSpecification) ||
- (spec.required_ruby_version.satisfied_by?(Gem.ruby_version) &&
- spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version))
+ target_platform = ruby_platform_materializes_to_ruby_platform? ? platform : local_platform
+
+ installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform)
+
+ specification = __materialize__(installable_candidates, :fallback_to_non_installable => false)
+ return specification unless specification.nil?
+
+ if target_platform != platform
+ installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform)
end
- search = installable_candidates.last || same_platform_candidates.last
- search.dependencies = dependencies if search && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification))
- search
+
+ installable_candidates
end
+
+ __materialize__(candidates)
end
- def respond_to?(*args)
- super || @specification ? @specification.respond_to?(*args) : nil
+ # If in frozen mode, we fallback to a non-installable candidate because by
+ # doing this we avoid re-resolving and potentially end up changing the
+ # lock file, which is not allowed. In that case, we will give a proper error
+ # about the mismatch higher up the stack, right before trying to install the
+ # bad gem.
+ def __materialize__(candidates, fallback_to_non_installable: Bundler.frozen_bundle?)
+ search = candidates.reverse.find do |spec|
+ spec.is_a?(StubSpecification) ||
+ (spec.matches_current_ruby? &&
+ spec.matches_current_rubygems?)
+ end
+ if search.nil? && fallback_to_non_installable
+ search = candidates.last
+ else
+ search.dependencies = dependencies if search && search.full_name == full_name && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification))
+ end
+ search
end
def to_s
- @__to_s ||= if platform == Gem::Platform::RUBY || platform.nil?
+ @to_s ||= if platform == Gem::Platform::RUBY
"#{name} (#{version})"
else
"#{name} (#{version}-#{platform})"
end
end
- def identifier
- @__identifier ||= [name, version, platform_string]
- end
-
def git_version
return unless source.is_a?(Bundler::Source::Git)
" #{source.revision[0..6]}"
end
- protected
-
- def platform_string
- platform_string = platform.to_s
- platform_string == Index::RUBY ? Index::NULL : platform_string
- end
-
private
- def to_ary
- nil
- end
-
- def method_missing(method, *args, &blk)
- raise "LazySpecification has not been materialized yet (calling :#{method} #{args.inspect})" unless @specification
-
- return super unless respond_to?(method)
-
- @specification.send(method, *args, &blk)
+ def use_exact_resolved_specifications?
+ @use_exact_resolved_specifications ||= !source.is_a?(Source::Path) && ruby_platform_materializes_to_ruby_platform?
end
#
# For backwards compatibility with existing lockfiles, if the most specific
- # locked platform is RUBY, we keep the previous behaviour of resolving the
+ # locked platform is not a specific platform like x86_64-linux or
+ # universal-java-11, then we keep the previous behaviour of resolving the
# best platform variant at materiliazation time. For previous bundler
# versions (before 2.2.0) this was always the case (except when the lockfile
# only included non-ruby platforms), but we're also keeping this behaviour
@@ -162,7 +151,9 @@ module Bundler
# explicitly add a more specific platform.
#
def ruby_platform_materializes_to_ruby_platform?
- !Bundler.most_specific_locked_platform?(generic_local_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform]
+ generic_platform = generic_local_platform == Gem::Platform::JAVA ? Gem::Platform::JAVA : Gem::Platform::RUBY
+
+ !Bundler.most_specific_locked_platform?(generic_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform]
end
end
end
diff --git a/lib/bundler/lockfile_generator.rb b/lib/bundler/lockfile_generator.rb
index 0578a93fdc..f7ba51b3e6 100644
--- a/lib/bundler/lockfile_generator.rb
+++ b/lib/bundler/lockfile_generator.rb
@@ -45,7 +45,7 @@ module Bundler
# gems with the same name, but different platform
# are ordered consistently
specs.sort_by(&:full_name).each do |spec|
- next if spec.name == "bundler".freeze
+ next if spec.name == "bundler"
out << spec.to_lock
end
end
@@ -60,7 +60,7 @@ module Bundler
handled = []
definition.dependencies.sort_by(&:to_s).each do |dep|
next if handled.include?(dep.name)
- out << dep.to_lock
+ out << dep.to_lock << "\n"
handled << dep.name
end
end
@@ -71,7 +71,7 @@ module Bundler
end
def add_bundled_with
- add_section("BUNDLED WITH", Bundler::VERSION)
+ add_section("BUNDLED WITH", definition.bundler_version_to_lock.to_s)
end
def add_section(name, value)
diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb
index 64fff4713d..7360a36752 100644
--- a/lib/bundler/lockfile_parser.rb
+++ b/lib/bundler/lockfile_parser.rb
@@ -4,15 +4,15 @@ module Bundler
class LockfileParser
attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version
- BUNDLED = "BUNDLED WITH".freeze
- DEPENDENCIES = "DEPENDENCIES".freeze
- PLATFORMS = "PLATFORMS".freeze
- RUBY = "RUBY VERSION".freeze
- GIT = "GIT".freeze
- GEM = "GEM".freeze
- PATH = "PATH".freeze
- PLUGIN = "PLUGIN SOURCE".freeze
- SPECS = " specs:".freeze
+ BUNDLED = "BUNDLED WITH"
+ DEPENDENCIES = "DEPENDENCIES"
+ PLATFORMS = "PLATFORMS"
+ RUBY = "RUBY VERSION"
+ GIT = "GIT"
+ GEM = "GEM"
+ PATH = "PATH"
+ PLUGIN = "PLUGIN SOURCE"
+ SPECS = " specs:"
OPTIONS = /^ ([a-z]+): (.*)$/i.freeze
SOURCE = [GIT, GEM, PATH, PLUGIN].freeze
@@ -26,6 +26,7 @@ module Bundler
KNOWN_SECTIONS = SECTIONS_BY_VERSION_INTRODUCED.values.flatten.freeze
ENVIRONMENT_VERSION_SECTIONS = [BUNDLED, RUBY].freeze
+ deprecate_constant(:ENVIRONMENT_VERSION_SECTIONS)
def self.sections_in_lockfile(lockfile_contents)
lockfile_contents.scan(/^\w[\w ]*$/).uniq
@@ -63,7 +64,7 @@ module Bundler
@state = nil
@specs = {}
- if lockfile.match(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/)
+ if lockfile.match?(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/)
raise LockfileError, "Your #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} contains merge conflicts.\n" \
"Run `git checkout HEAD -- #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` first to get a clean lock."
end
@@ -80,13 +81,13 @@ module Bundler
@state = :ruby
elsif line == BUNDLED
@state = :bundled_with
- elsif line =~ /^[^\s]/
+ elsif /^[^\s]/.match?(line)
@state = nil
elsif @state
send("parse_#{@state}", line)
end
end
- @specs = @specs.values.sort_by(&:identifier)
+ @specs = @specs.values.sort_by(&:full_name)
rescue ArgumentError => e
Bundler.ui.debug(e)
raise LockfileError, "Your lockfile is unreadable. Run `rm #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` " \
@@ -100,9 +101,9 @@ module Bundler
private
TYPES = {
- GIT => Bundler::Source::Git,
- GEM => Bundler::Source::Rubygems,
- PATH => Bundler::Source::Path,
+ GIT => Bundler::Source::Git,
+ GEM => Bundler::Source::Rubygems,
+ PATH => Bundler::Source::Path,
PLUGIN => Bundler::Plugin,
}.freeze
@@ -199,7 +200,7 @@ module Bundler
@current_spec.source = @current_source
@current_source.add_dependency_names(name)
- @specs[@current_spec.identifier] = @current_spec
+ @specs[@current_spec.full_name] = @current_spec
elsif spaces.size == 6
version = version.split(",").map(&:strip) if version
dep = Gem::Dependency.new(name, version)
diff --git a/lib/bundler/man/bundle-add.1 b/lib/bundler/man/bundle-add.1
index 1a77124861..8549855b0d 100644
--- a/lib/bundler/man/bundle-add.1
+++ b/lib/bundler/man/bundle-add.1
@@ -1,13 +1,13 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-ADD" "1" "June 2022" "" ""
+.TH "BUNDLE\-ADD" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install
.
.SH "SYNOPSIS"
-\fBbundle add\fR \fIGEM_NAME\fR [\-\-group=GROUP] [\-\-version=VERSION] [\-\-source=SOURCE] [\-\-git=GIT] [\-\-github=GITHUB] [\-\-branch=BRANCH] [\-\-ref=REF] [\-\-skip\-install] [\-\-strict] [\-\-optimistic]
+\fBbundle add\fR \fIGEM_NAME\fR [\-\-group=GROUP] [\-\-version=VERSION] [\-\-source=SOURCE] [\-\-path=PATH] [\-\-git=GIT] [\-\-github=GITHUB] [\-\-branch=BRANCH] [\-\-ref=REF] [\-\-skip\-install] [\-\-strict] [\-\-optimistic]
.
.SH "DESCRIPTION"
Adds the named gem to the Gemfile and run \fBbundle install\fR\. \fBbundle install\fR can be avoided by using the flag \fB\-\-skip\-install\fR\.
@@ -49,6 +49,10 @@ Specify the source for the added gem\.
Adds require path to gem\. Provide false, or a path as a string\.
.
.TP
+\fB\-\-path\fR
+Specify the file system path for the added gem\.
+.
+.TP
\fB\-\-git\fR
Specify the git source for the added gem\.
.
diff --git a/lib/bundler/man/bundle-add.1.ronn b/lib/bundler/man/bundle-add.1.ronn
index 8eead9c569..37c92e5fcd 100644
--- a/lib/bundler/man/bundle-add.1.ronn
+++ b/lib/bundler/man/bundle-add.1.ronn
@@ -3,7 +3,7 @@ bundle-add(1) -- Add gem to the Gemfile and run bundle install
## SYNOPSIS
-`bundle add` <GEM_NAME> [--group=GROUP] [--version=VERSION] [--source=SOURCE] [--git=GIT] [--github=GITHUB] [--branch=BRANCH] [--ref=REF] [--skip-install] [--strict] [--optimistic]
+`bundle add` <GEM_NAME> [--group=GROUP] [--version=VERSION] [--source=SOURCE] [--path=PATH] [--git=GIT] [--github=GITHUB] [--branch=BRANCH] [--ref=REF] [--skip-install] [--strict] [--optimistic]
## DESCRIPTION
Adds the named gem to the Gemfile and run `bundle install`. `bundle install` can be avoided by using the flag `--skip-install`.
@@ -33,6 +33,9 @@ bundle add rails --group "development, test"
* `--require`, `-r`:
Adds require path to gem. Provide false, or a path as a string.
+* `--path`:
+ Specify the file system path for the added gem.
+
* `--git`:
Specify the git source for the added gem.
diff --git a/lib/bundler/man/bundle-binstubs.1 b/lib/bundler/man/bundle-binstubs.1
index d8312c391d..40c338916a 100644
--- a/lib/bundler/man/bundle-binstubs.1
+++ b/lib/bundler/man/bundle-binstubs.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-BINSTUBS" "1" "June 2022" "" ""
+.TH "BUNDLE\-BINSTUBS" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-binstubs\fR \- Install the binstubs of the listed gems
diff --git a/lib/bundler/man/bundle-cache.1 b/lib/bundler/man/bundle-cache.1
index 935477a3de..69b1e1e3dd 100644
--- a/lib/bundler/man/bundle-cache.1
+++ b/lib/bundler/man/bundle-cache.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-CACHE" "1" "June 2022" "" ""
+.TH "BUNDLE\-CACHE" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application
@@ -9,8 +9,11 @@
.SH "SYNOPSIS"
\fBbundle cache\fR
.
+.P
+alias: \fBpackage\fR, \fBpack\fR
+.
.SH "DESCRIPTION"
-Copy all of the \fB\.gem\fR files needed to run the application into the \fBvendor/cache\fR directory\. In the future, when running [bundle install(1)][bundle\-install], use the gems in the cache in preference to the ones on \fBrubygems\.org\fR\.
+Copy all of the \fB\.gem\fR files needed to run the application into the \fBvendor/cache\fR directory\. In the future, when running \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR, use the gems in the cache in preference to the ones on \fBrubygems\.org\fR\.
.
.SH "GIT AND PATH GEMS"
The \fBbundle cache\fR command can also package \fB:git\fR and \fB:path\fR dependencies besides \.gem files\. This needs to be explicitly enabled via the \fB\-\-all\fR option\. Once used, the \fB\-\-all\fR option will be remembered\.
@@ -19,7 +22,7 @@ The \fBbundle cache\fR command can also package \fB:git\fR and \fB:path\fR depen
When using gems that have different packages for different platforms, Bundler supports caching of gems for other platforms where the Gemfile has been resolved (i\.e\. present in the lockfile) in \fBvendor/cache\fR\. This needs to be enabled via the \fB\-\-all\-platforms\fR option\. This setting will be remembered in your local bundler configuration\.
.
.SH "REMOTE FETCHING"
-By default, if you run \fBbundle install(1)\fR](bundle\-install\.1\.html) after running bundle cache(1) \fIbundle\-cache\.1\.html\fR, bundler will still connect to \fBrubygems\.org\fR to check whether a platform\-specific gem exists for any of the gems in \fBvendor/cache\fR\.
+By default, if you run \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR after running bundle cache(1) \fIbundle\-cache\.1\.html\fR, bundler will still connect to \fBrubygems\.org\fR to check whether a platform\-specific gem exists for any of the gems in \fBvendor/cache\fR\.
.
.P
For instance, consider this Gemfile(5):
@@ -53,3 +56,6 @@ One way to be sure that you have the right platformed versions of all your gems
.
.P
By default, bundle cache(1) \fIbundle\-cache\.1\.html\fR fetches and also installs the gems to the default location\. To package the dependencies to \fBvendor/cache\fR without installing them to the local install location, you can run \fBbundle cache \-\-no\-install\fR\.
+.
+.SH "HISTORY"
+In Bundler 2\.1, \fBcache\fR took in the functionalities of \fBpackage\fR and now \fBpackage\fR and \fBpack\fR are aliases of \fBcache\fR\.
diff --git a/lib/bundler/man/bundle-cache.1.ronn b/lib/bundler/man/bundle-cache.1.ronn
index 383adb2ba3..8112c2c551 100644
--- a/lib/bundler/man/bundle-cache.1.ronn
+++ b/lib/bundler/man/bundle-cache.1.ronn
@@ -5,10 +5,12 @@ bundle-cache(1) -- Package your needed `.gem` files into your application
`bundle cache`
+alias: `package`, `pack`
+
## DESCRIPTION
Copy all of the `.gem` files needed to run the application into the
-`vendor/cache` directory. In the future, when running [bundle install(1)][bundle-install],
+`vendor/cache` directory. In the future, when running [`bundle install(1)`](bundle-install.1.html),
use the gems in the cache in preference to the ones on `rubygems.org`.
## GIT AND PATH GEMS
@@ -27,7 +29,7 @@ bundler configuration.
## REMOTE FETCHING
-By default, if you run `bundle install(1)`](bundle-install.1.html) after running
+By default, if you run [`bundle install(1)`](bundle-install.1.html) after running
[bundle cache(1)](bundle-cache.1.html), bundler will still connect to `rubygems.org`
to check whether a platform-specific gem exists for any of the gems
in `vendor/cache`.
@@ -70,3 +72,8 @@ By default, [bundle cache(1)](bundle-cache.1.html) fetches and also
installs the gems to the default location. To package the
dependencies to `vendor/cache` without installing them to the
local install location, you can run `bundle cache --no-install`.
+
+## HISTORY
+
+In Bundler 2.1, `cache` took in the functionalities of `package` and now
+`package` and `pack` are aliases of `cache`.
diff --git a/lib/bundler/man/bundle-check.1 b/lib/bundler/man/bundle-check.1
index 0f38d17bd3..748a37e7d1 100644
--- a/lib/bundler/man/bundle-check.1
+++ b/lib/bundler/man/bundle-check.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-CHECK" "1" "June 2022" "" ""
+.TH "BUNDLE\-CHECK" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems
diff --git a/lib/bundler/man/bundle-clean.1 b/lib/bundler/man/bundle-clean.1
index e0b2aac549..af8f13cd89 100644
--- a/lib/bundler/man/bundle-clean.1
+++ b/lib/bundler/man/bundle-clean.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-CLEAN" "1" "June 2022" "" ""
+.TH "BUNDLE\-CLEAN" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory
@@ -20,5 +20,5 @@ Print the changes, but do not clean the unused gems\.
.
.TP
\fB\-\-force\fR
-Force a clean even if \fB\-\-path\fR is not set\.
+Forces cleaning up unused gems even if Bundler is configured to use globally installed gems\. As a consequence, removes all system gems except for the ones in the current application\.
diff --git a/lib/bundler/man/bundle-clean.1.ronn b/lib/bundler/man/bundle-clean.1.ronn
index de23991782..dae27c21ee 100644
--- a/lib/bundler/man/bundle-clean.1.ronn
+++ b/lib/bundler/man/bundle-clean.1.ronn
@@ -15,4 +15,4 @@ useful when you have made many changes to your gem dependencies.
* `--dry-run`:
Print the changes, but do not clean the unused gems.
* `--force`:
- Force a clean even if `--path` is not set.
+ Forces cleaning up unused gems even if Bundler is configured to use globally installed gems. As a consequence, removes all system gems except for the ones in the current application.
diff --git a/lib/bundler/man/bundle-config.1 b/lib/bundler/man/bundle-config.1
index 9b60c4c961..4442f33105 100644
--- a/lib/bundler/man/bundle-config.1
+++ b/lib/bundler/man/bundle-config.1
@@ -1,13 +1,22 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-CONFIG" "1" "June 2022" "" ""
+.TH "BUNDLE\-CONFIG" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-config\fR \- Set bundler configuration options
.
.SH "SYNOPSIS"
-\fBbundle config\fR [list|get|set|unset] [\fIname\fR [\fIvalue\fR]]
+\fBbundle config\fR list
+.
+.br
+\fBbundle config\fR [get] NAME
+.
+.br
+\fBbundle config\fR [set] NAME VALUE
+.
+.br
+\fBbundle config\fR unset NAME
.
.SH "DESCRIPTION"
This command allows you to interact with Bundler\'s configuration system\.
@@ -30,7 +39,7 @@ Bundler default config
.IP "" 0
.
.P
-Executing \fBbundle config list\fR with will print a list of all bundler configuration for the current bundle, and where that configuration was set\.
+Executing \fBbundle config list\fR will print a list of all bundler configuration for the current bundle, and where that configuration was set\.
.
.P
Executing \fBbundle config get <name>\fR will print the value of that configuration setting, and where it was set\.
@@ -51,7 +60,7 @@ Executing \fBbundle config unset <name>\fR will delete the configuration in both
Executing \fBbundle config unset \-\-global <name>\fR will delete the configuration only from the user configuration\.
.
.P
-Executing \fBbundle config unset \-\-local <name> <value>\fR will delete the configuration only from the local application\.
+Executing \fBbundle config unset \-\-local <name>\fR will delete the configuration only from the local application\.
.
.P
Executing bundle with the \fBBUNDLE_IGNORE_CONFIG\fR environment variable set will cause it to ignore all configuration\.
@@ -74,6 +83,10 @@ Creates a directory (defaults to \fB~/bin\fR) and place any executables from the
In deployment mode, Bundler will \'roll\-out\' the bundle for \fBproduction\fR use\. Please check carefully if you want to have this option enabled in \fBdevelopment\fR or \fBtest\fR environments\.
.
.TP
+\fBonly\fR
+A space\-separated list of groups to install only gems of the specified groups\.
+.
+.TP
\fBpath\fR
The location to install the specified gems to\. This defaults to Rubygems\' setting\. Bundler shares this location with Rubygems, \fBgem install \.\.\.\fR will have gem installed there, too\. Therefore, gems installed without a \fB\-\-path \.\.\.\fR setting will show up by calling \fBgem list\fR\. Accordingly, gems installed to other locations will not get listed\.
.
@@ -205,6 +218,9 @@ The following is a list of all configuration keys and their purpose\. You can le
\fBglobal_gem_cache\fR (\fBBUNDLE_GLOBAL_GEM_CACHE\fR): Whether Bundler should cache all gems globally, rather than locally to the installing Ruby installation\.
.
.IP "\(bu" 4
+\fBignore_funding_requests\fR (\fBBUNDLE_IGNORE_FUNDING_REQUESTS\fR): When set, no funding requests will be printed\.
+.
+.IP "\(bu" 4
\fBignore_messages\fR (\fBBUNDLE_IGNORE_MESSAGES\fR): When set, no post install messages will be printed\. To silence a single gem, use dot notation like \fBignore_messages\.httparty true\fR\.
.
.IP "\(bu" 4
@@ -220,6 +236,9 @@ The following is a list of all configuration keys and their purpose\. You can le
\fBno_prune\fR (\fBBUNDLE_NO_PRUNE\fR): Whether Bundler should leave outdated gems unpruned when caching\.
.
.IP "\(bu" 4
+\fBonly\fR (\fBBUNDLE_ONLY\fR): A space\-separated list of groups to install only gems of the specified groups\.
+.
+.IP "\(bu" 4
\fBpath\fR (\fBBUNDLE_PATH\fR): The location on disk where all gems in your bundle will be located regardless of \fB$GEM_HOME\fR or \fB$GEM_PATH\fR values\. Bundle gems not found in this location will be installed by \fBbundle install\fR\. Defaults to \fBGem\.dir\fR\. When \-\-deployment is used, defaults to vendor/bundle\.
.
.IP "\(bu" 4
@@ -265,9 +284,6 @@ The following is a list of all configuration keys and their purpose\. You can le
\fBssl_verify_mode\fR (\fBBUNDLE_SSL_VERIFY_MODE\fR): The SSL verification mode Bundler uses when making HTTPS requests\. Defaults to verify peer\.
.
.IP "\(bu" 4
-\fBsuppress_install_using_messages\fR (\fBBUNDLE_SUPPRESS_INSTALL_USING_MESSAGES\fR): Avoid printing \fBUsing \.\.\.\fR messages during installation when the version of a gem has not changed\.
-.
-.IP "\(bu" 4
\fBsystem_bindir\fR (\fBBUNDLE_SYSTEM_BINDIR\fR): The location where RubyGems installs binstubs\. Defaults to \fBGem\.bindir\fR\.
.
.IP "\(bu" 4
@@ -288,7 +304,7 @@ The following is a list of all configuration keys and their purpose\. You can le
.IP "" 0
.
.P
-In general, you should set these settings per\-application by using the applicable flag to the bundle install(1) \fIbundle\-install\.1\.html\fR or bundle package(1) \fIbundle\-package\.1\.html\fR command\.
+In general, you should set these settings per\-application by using the applicable flag to the bundle install(1) \fIbundle\-install\.1\.html\fR or bundle cache(1) \fIbundle\-cache\.1\.html\fR command\.
.
.P
You can set them globally either via environment variables or \fBbundle config\fR, whichever is preferable for your setup\. If you use both, environment variables will take preference over global settings\.
diff --git a/lib/bundler/man/bundle-config.1.ronn b/lib/bundler/man/bundle-config.1.ronn
index 31bb3108c8..adc273ec62 100644
--- a/lib/bundler/man/bundle-config.1.ronn
+++ b/lib/bundler/man/bundle-config.1.ronn
@@ -3,7 +3,10 @@ bundle-config(1) -- Set bundler configuration options
## SYNOPSIS
-`bundle config` [list|get|set|unset] [<name> [<value>]]
+`bundle config` list<br>
+`bundle config` [get] NAME<br>
+`bundle config` [set] NAME VALUE<br>
+`bundle config` unset NAME
## DESCRIPTION
@@ -16,7 +19,7 @@ Bundler loads configuration settings in this order:
3. Global config (`~/.bundle/config`)
4. Bundler default config
-Executing `bundle config list` with will print a list of all bundler
+Executing `bundle config list` will print a list of all bundler
configuration for the current bundle, and where that configuration
was set.
@@ -43,8 +46,8 @@ local and global sources.
Executing `bundle config unset --global <name>` will delete the configuration
only from the user configuration.
-Executing `bundle config unset --local <name> <value>` will delete the
-configuration only from the local application.
+Executing `bundle config unset --local <name>` will delete the configuration
+only from the local application.
Executing bundle with the `BUNDLE_IGNORE_CONFIG` environment variable set will
cause it to ignore all configuration.
@@ -74,6 +77,9 @@ The options that can be configured are:
`production` use. Please check carefully if you want to have this option
enabled in `development` or `test` environments.
+* `only`:
+ A space-separated list of groups to install only gems of the specified groups.
+
* `path`:
The location to install the specified gems to. This defaults to Rubygems'
setting. Bundler shares this location with Rubygems, `gem install ...` will
@@ -204,6 +210,8 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
* `global_gem_cache` (`BUNDLE_GLOBAL_GEM_CACHE`):
Whether Bundler should cache all gems globally, rather than locally to the
installing Ruby installation.
+* `ignore_funding_requests` (`BUNDLE_IGNORE_FUNDING_REQUESTS`):
+ When set, no funding requests will be printed.
* `ignore_messages` (`BUNDLE_IGNORE_MESSAGES`):
When set, no post install messages will be printed. To silence a single gem,
use dot notation like `ignore_messages.httparty true`.
@@ -216,6 +224,8 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
Whether `bundle package` should skip installing gems.
* `no_prune` (`BUNDLE_NO_PRUNE`):
Whether Bundler should leave outdated gems unpruned when caching.
+* `only` (`BUNDLE_ONLY`):
+ A space-separated list of groups to install only gems of the specified groups.
* `path` (`BUNDLE_PATH`):
The location on disk where all gems in your bundle will be located regardless
of `$GEM_HOME` or `$GEM_PATH` values. Bundle gems not found in this location
@@ -255,9 +265,6 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
* `ssl_verify_mode` (`BUNDLE_SSL_VERIFY_MODE`):
The SSL verification mode Bundler uses when making HTTPS requests.
Defaults to verify peer.
-* `suppress_install_using_messages` (`BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES`):
- Avoid printing `Using ...` messages during installation when the version of
- a gem has not changed.
* `system_bindir` (`BUNDLE_SYSTEM_BINDIR`):
The location where RubyGems installs binstubs. Defaults to `Gem.bindir`.
* `timeout` (`BUNDLE_TIMEOUT`):
@@ -273,7 +280,7 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
A `:`-separated list of groups whose gems bundler should not install.
In general, you should set these settings per-application by using the applicable
-flag to the [bundle install(1)](bundle-install.1.html) or [bundle package(1)](bundle-package.1.html) command.
+flag to the [bundle install(1)](bundle-install.1.html) or [bundle cache(1)](bundle-cache.1.html) command.
You can set them globally either via environment variables or `bundle config`,
whichever is preferable for your setup. If you use both, environment variables
diff --git a/lib/bundler/man/bundle-console.1 b/lib/bundler/man/bundle-console.1
new file mode 100644
index 0000000000..24fff46cec
--- /dev/null
+++ b/lib/bundler/man/bundle-console.1
@@ -0,0 +1,53 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-CONSOLE" "1" "August 2023" "" ""
+.
+.SH "NAME"
+\fBbundle\-console\fR \- Deprecated way to open an IRB session with the bundle pre\-loaded
+.
+.SH "SYNOPSIS"
+\fBbundle console\fR [GROUP]
+.
+.SH "DESCRIPTION"
+Starts an interactive Ruby console session in the context of the current bundle\.
+.
+.P
+If no \fBGROUP\fR is specified, all gems in the \fBdefault\fR group in the Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR are preliminarily loaded\.
+.
+.P
+If \fBGROUP\fR is specified, all gems in the given group in the Gemfile in addition to the gems in \fBdefault\fR group are loaded\. Even if the given group does not exist in the Gemfile, IRB console starts without any warning or error\.
+.
+.P
+The environment variable \fBBUNDLE_CONSOLE\fR or \fBbundle config set console\fR can be used to change the shell from the following:
+.
+.IP "\(bu" 4
+\fBirb\fR (default)
+.
+.IP "\(bu" 4
+\fBpry\fR (https://github\.com/pry/pry)
+.
+.IP "\(bu" 4
+\fBripl\fR (https://github\.com/cldwalker/ripl)
+.
+.IP "" 0
+.
+.P
+\fBbundle console\fR uses irb by default\. An alternative Pry or Ripl can be used with \fBbundle console\fR by adjusting the \fBconsole\fR Bundler setting\. Also make sure that \fBpry\fR or \fBripl\fR is in your Gemfile\.
+.
+.SH "EXAMPLE"
+.
+.nf
+
+$ bundle config set console pry
+$ bundle console
+Resolving dependencies\.\.\.
+[1] pry(main)>
+.
+.fi
+.
+.SH "NOTES"
+This command was deprecated in Bundler 2\.1 and will be removed in 3\.0\. Use \fBbin/console\fR script, which can be generated by \fBbundle gem <NAME>\fR\.
+.
+.SH "SEE ALSO"
+Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR
diff --git a/lib/bundler/man/bundle-console.1.ronn b/lib/bundler/man/bundle-console.1.ronn
new file mode 100644
index 0000000000..f9096d386a
--- /dev/null
+++ b/lib/bundler/man/bundle-console.1.ronn
@@ -0,0 +1,44 @@
+bundle-console(1) -- Deprecated way to open an IRB session with the bundle pre-loaded
+=====================================================================================
+
+## SYNOPSIS
+
+`bundle console` [GROUP]
+
+## DESCRIPTION
+
+Starts an interactive Ruby console session in the context of the current bundle.
+
+If no `GROUP` is specified, all gems in the `default` group in the [Gemfile(5)](https://bundler.io/man/gemfile.5.html) are
+preliminarily loaded.
+
+If `GROUP` is specified, all gems in the given group in the Gemfile in addition
+to the gems in `default` group are loaded. Even if the given group does not
+exist in the Gemfile, IRB console starts without any warning or error.
+
+The environment variable `BUNDLE_CONSOLE` or `bundle config set console` can be used to change
+the shell from the following:
+
+* `irb` (default)
+* `pry` (https://github.com/pry/pry)
+* `ripl` (https://github.com/cldwalker/ripl)
+
+`bundle console` uses irb by default. An alternative Pry or Ripl can be used with
+`bundle console` by adjusting the `console` Bundler setting. Also make sure that
+`pry` or `ripl` is in your Gemfile.
+
+## EXAMPLE
+
+ $ bundle config set console pry
+ $ bundle console
+ Resolving dependencies...
+ [1] pry(main)>
+
+## NOTES
+
+This command was deprecated in Bundler 2.1 and will be removed in 3.0.
+Use `bin/console` script, which can be generated by `bundle gem <NAME>`.
+
+## SEE ALSO
+
+[Gemfile(5)](https://bundler.io/man/gemfile.5.html)
diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1
index 5e76db89c2..57da8216cb 100644
--- a/lib/bundler/man/bundle-doctor.1
+++ b/lib/bundler/man/bundle-doctor.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-DOCTOR" "1" "June 2022" "" ""
+.TH "BUNDLE\-DOCTOR" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-doctor\fR \- Checks the bundle for common problems
diff --git a/lib/bundler/man/bundle-exec.1 b/lib/bundler/man/bundle-exec.1
index 16ad1c2a74..852788db7a 100644
--- a/lib/bundler/man/bundle-exec.1
+++ b/lib/bundler/man/bundle-exec.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-EXEC" "1" "June 2022" "" ""
+.TH "BUNDLE\-EXEC" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-exec\fR \- Execute a command in the context of the bundle
@@ -74,13 +74,13 @@ Finally, \fBbundle exec\fR also implicitly modifies \fBGemfile\.lock\fR if the l
By default, when attempting to \fBbundle exec\fR to a file with a ruby shebang, Bundler will \fBKernel\.load\fR that file instead of using \fBKernel\.exec\fR\. For the vast majority of cases, this is a performance improvement\. In a rare few cases, this could cause some subtle side\-effects (such as dependence on the exact contents of \fB$0\fR or \fB__FILE__\fR) and the optimization can be disabled by enabling the \fBdisable_exec_load\fR setting\.
.
.SS "Shelling out"
-Any Ruby code that opens a subshell (like \fBsystem\fR, backticks, or \fB%x{}\fR) will automatically use the current Bundler environment\. If you need to shell out to a Ruby command that is not part of your current bundle, use the \fBwith_clean_env\fR method with a block\. Any subshells created inside the block will be given the environment present before Bundler was activated\. For example, Homebrew commands run Ruby, but don\'t work inside a bundle:
+Any Ruby code that opens a subshell (like \fBsystem\fR, backticks, or \fB%x{}\fR) will automatically use the current Bundler environment\. If you need to shell out to a Ruby command that is not part of your current bundle, use the \fBwith_unbundled_env\fR method with a block\. Any subshells created inside the block will be given the environment present before Bundler was activated\. For example, Homebrew commands run Ruby, but don\'t work inside a bundle:
.
.IP "" 4
.
.nf
-Bundler\.with_clean_env do
+Bundler\.with_unbundled_env do
`brew install wget`
end
.
@@ -89,13 +89,13 @@ end
.IP "" 0
.
.P
-Using \fBwith_clean_env\fR is also necessary if you are shelling out to a different bundle\. Any Bundler commands run in a subshell will inherit the current Gemfile, so commands that need to run in the context of a different bundle also need to use \fBwith_clean_env\fR\.
+Using \fBwith_unbundled_env\fR is also necessary if you are shelling out to a different bundle\. Any Bundler commands run in a subshell will inherit the current Gemfile, so commands that need to run in the context of a different bundle also need to use \fBwith_unbundled_env\fR\.
.
.IP "" 4
.
.nf
-Bundler\.with_clean_env do
+Bundler\.with_unbundled_env do
Dir\.chdir "/other/bundler/project" do
`bundle exec \./script`
end
@@ -155,7 +155,7 @@ You can find a list of all the gems containing gem plugins by running
.
.nf
-ruby \-rrubygems \-e "puts Gem\.find_files(\'rubygems_plugin\.rb\')"
+ruby \-e "puts Gem\.find_files(\'rubygems_plugin\.rb\')"
.
.fi
.
diff --git a/lib/bundler/man/bundle-exec.1.ronn b/lib/bundler/man/bundle-exec.1.ronn
index dec3c7cb82..05948095e2 100644
--- a/lib/bundler/man/bundle-exec.1.ronn
+++ b/lib/bundler/man/bundle-exec.1.ronn
@@ -84,20 +84,20 @@ the `disable_exec_load` setting.
Any Ruby code that opens a subshell (like `system`, backticks, or `%x{}`) will
automatically use the current Bundler environment. If you need to shell out to
a Ruby command that is not part of your current bundle, use the
-`with_clean_env` method with a block. Any subshells created inside the block
+`with_unbundled_env` method with a block. Any subshells created inside the block
will be given the environment present before Bundler was activated. For
example, Homebrew commands run Ruby, but don't work inside a bundle:
- Bundler.with_clean_env do
+ Bundler.with_unbundled_env do
`brew install wget`
end
-Using `with_clean_env` is also necessary if you are shelling out to a different
+Using `with_unbundled_env` is also necessary if you are shelling out to a different
bundle. Any Bundler commands run in a subshell will inherit the current
Gemfile, so commands that need to run in the context of a different bundle also
-need to use `with_clean_env`.
+need to use `with_unbundled_env`.
- Bundler.with_clean_env do
+ Bundler.with_unbundled_env do
Dir.chdir "/other/bundler/project" do
`bundle exec ./script`
end
@@ -145,7 +145,7 @@ their plugins.
You can find a list of all the gems containing gem plugins
by running
- ruby -rrubygems -e "puts Gem.find_files('rubygems_plugin.rb')"
+ ruby -e "puts Gem.find_files('rubygems_plugin.rb')"
At the very least, you should remove all but the newest
version of each gem plugin, and also remove all gem plugins
diff --git a/lib/bundler/man/bundle-gem.1 b/lib/bundler/man/bundle-gem.1
index eb66e2d41d..8339b727ce 100644
--- a/lib/bundler/man/bundle-gem.1
+++ b/lib/bundler/man/bundle-gem.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-GEM" "1" "June 2022" "" ""
+.TH "BUNDLE\-GEM" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem
@@ -31,41 +31,32 @@ The generated project skeleton can be customized with OPTIONS, as explained belo
.
.SH "OPTIONS"
.
-.TP
-\fB\-\-exe\fR or \fB\-b\fR or \fB\-\-bin\fR
-Specify that Bundler should create a binary executable (as \fBexe/GEM_NAME\fR) in the generated rubygem project\. This binary will also be added to the \fBGEM_NAME\.gemspec\fR manifest\. This behavior is disabled by default\.
+.IP "\(bu" 4
+\fB\-\-exe\fR or \fB\-b\fR or \fB\-\-bin\fR: Specify that Bundler should create a binary executable (as \fBexe/GEM_NAME\fR) in the generated rubygem project\. This binary will also be added to the \fBGEM_NAME\.gemspec\fR manifest\. This behavior is disabled by default\.
.
-.TP
-\fB\-\-no\-exe\fR
-Do not create a binary (overrides \fB\-\-exe\fR specified in the global config)\.
+.IP "\(bu" 4
+\fB\-\-no\-exe\fR: Do not create a binary (overrides \fB\-\-exe\fR specified in the global config)\.
.
-.TP
-\fB\-\-coc\fR
-Add a \fBCODE_OF_CONDUCT\.md\fR file to the root of the generated project\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
+.IP "\(bu" 4
+\fB\-\-coc\fR: Add a \fBCODE_OF_CONDUCT\.md\fR file to the root of the generated project\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
-.TP
-\fB\-\-no\-coc\fR
-Do not create a \fBCODE_OF_CONDUCT\.md\fR (overrides \fB\-\-coc\fR specified in the global config)\.
+.IP "\(bu" 4
+\fB\-\-no\-coc\fR: Do not create a \fBCODE_OF_CONDUCT\.md\fR (overrides \fB\-\-coc\fR specified in the global config)\.
.
-.TP
-\fB\-\-ext\fR
-Add boilerplate for C extension code to the generated project\. This behavior is disabled by default\.
+.IP "\(bu" 4
+\fB\-\-ext=c\fR, \fB\-\-ext=rust\fR Add boilerplate for C or Rust (currently magnus \fIhttps://docs\.rs/magnus\fR based) extension code to the generated project\. This behavior is disabled by default\.
.
-.TP
-\fB\-\-no\-ext\fR
-Do not add C extension code (overrides \fB\-\-ext\fR specified in the global config)\.
+.IP "\(bu" 4
+\fB\-\-no\-ext\fR: Do not add extension code (overrides \fB\-\-ext\fR specified in the global config)\.
.
-.TP
-\fB\-\-mit\fR
-Add an MIT license to a \fBLICENSE\.txt\fR file in the root of the generated project\. Your name from the global git config is used for the copyright statement\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
+.IP "\(bu" 4
+\fB\-\-mit\fR: Add an MIT license to a \fBLICENSE\.txt\fR file in the root of the generated project\. Your name from the global git config is used for the copyright statement\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
-.TP
-\fB\-\-no\-mit\fR
-Do not create a \fBLICENSE\.txt\fR (overrides \fB\-\-mit\fR specified in the global config)\.
+.IP "\(bu" 4
+\fB\-\-no\-mit\fR: Do not create a \fBLICENSE\.txt\fR (overrides \fB\-\-mit\fR specified in the global config)\.
.
-.TP
-\fB\-t\fR, \fB\-\-test=minitest\fR, \fB\-\-test=rspec\fR, \fB\-\-test=test\-unit\fR
-Specify the test framework that Bundler should use when generating the project\. Acceptable values are \fBminitest\fR, \fBrspec\fR and \fBtest\-unit\fR\. The \fBGEM_NAME\.gemspec\fR will be configured and a skeleton test/spec directory will be created based on this option\. Given no option is specified:
+.IP "\(bu" 4
+\fB\-t\fR, \fB\-\-test=minitest\fR, \fB\-\-test=rspec\fR, \fB\-\-test=test\-unit\fR: Specify the test framework that Bundler should use when generating the project\. Acceptable values are \fBminitest\fR, \fBrspec\fR and \fBtest\-unit\fR\. The \fBGEM_NAME\.gemspec\fR will be configured and a skeleton test/spec directory will be created based on this option\. Given no option is specified:
.
.IP
When Bundler is configured to generate tests, this defaults to Bundler\'s global config setting \fBgem\.test\fR\.
@@ -76,9 +67,8 @@ When Bundler is configured to not generate tests, an interactive prompt will be
.IP
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
-.TP
-\fB\-\-ci\fR, \fB\-\-ci=github\fR, \fB\-\-ci=travis\fR, \fB\-\-ci=gitlab\fR, \fB\-\-ci=circle\fR
-Specify the continuous integration service that Bundler should use when generating the project\. Acceptable values are \fBgithub\fR, \fBtravis\fR, \fBgitlab\fR and \fBcircle\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
+.IP "\(bu" 4
+\fB\-\-ci\fR, \fB\-\-ci=github\fR, \fB\-\-ci=gitlab\fR, \fB\-\-ci=circle\fR: Specify the continuous integration service that Bundler should use when generating the project\. Acceptable values are \fBgithub\fR, \fBgitlab\fR and \fBcircle\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
.
.IP
When Bundler is configured to generate CI files, this defaults to Bundler\'s global config setting \fBgem\.ci\fR\.
@@ -89,9 +79,8 @@ When Bundler is configured to not generate CI files, an interactive prompt will
.IP
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
-.TP
-\fB\-\-linter\fR, \fB\-\-linter=rubocop\fR, \fB\-\-linter=standard\fR
-Specify the linter and code formatter that Bundler should add to the project\'s development dependencies\. Acceptable values are \fBrubocop\fR and \fBstandard\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
+.IP "\(bu" 4
+\fB\-\-linter\fR, \fB\-\-linter=rubocop\fR, \fB\-\-linter=standard\fR: Specify the linter and code formatter that Bundler should add to the project\'s development dependencies\. Acceptable values are \fBrubocop\fR and \fBstandard\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
.
.IP
When Bundler is configured to add a linter, this defaults to Bundler\'s global config setting \fBgem\.linter\fR\.
@@ -102,9 +91,10 @@ When Bundler is configured not to add a linter, an interactive prompt will be di
.IP
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
-.TP
-\fB\-e\fR, \fB\-\-edit[=EDITOR]\fR
-Open the resulting GEM_NAME\.gemspec in EDITOR, or the default editor if not specified\. The default is \fB$BUNDLER_EDITOR\fR, \fB$VISUAL\fR, or \fB$EDITOR\fR\.
+.IP "\(bu" 4
+\fB\-e\fR, \fB\-\-edit[=EDITOR]\fR: Open the resulting GEM_NAME\.gemspec in EDITOR, or the default editor if not specified\. The default is \fB$BUNDLER_EDITOR\fR, \fB$VISUAL\fR, or \fB$EDITOR\fR\.
+.
+.IP "" 0
.
.SH "SEE ALSO"
.
diff --git a/lib/bundler/man/bundle-gem.1.ronn b/lib/bundler/man/bundle-gem.1.ronn
index 61c741fb24..46fa2f179f 100644
--- a/lib/bundler/man/bundle-gem.1.ronn
+++ b/lib/bundler/man/bundle-gem.1.ronn
@@ -41,12 +41,12 @@ configuration file using the following names:
Do not create a `CODE_OF_CONDUCT.md` (overrides `--coc` specified in the
global config).
-* `--ext`:
- Add boilerplate for C extension code to the generated project. This behavior
+* `--ext=c`, `--ext=rust`
+ Add boilerplate for C or Rust (currently [magnus](https://docs.rs/magnus) based) extension code to the generated project. This behavior
is disabled by default.
* `--no-ext`:
- Do not add C extension code (overrides `--ext` specified in the global
+ Do not add extension code (overrides `--ext` specified in the global
config).
* `--mit`:
@@ -76,9 +76,9 @@ configuration file using the following names:
the answer will be saved in Bundler's global config for future `bundle gem`
use.
-* `--ci`, `--ci=github`, `--ci=travis`, `--ci=gitlab`, `--ci=circle`:
+* `--ci`, `--ci=github`, `--ci=gitlab`, `--ci=circle`:
Specify the continuous integration service that Bundler should use when
- generating the project. Acceptable values are `github`, `travis`, `gitlab`
+ generating the project. Acceptable values are `github`, `gitlab`
and `circle`. A configuration file will be generated in the project directory.
Given no option is specified:
diff --git a/lib/bundler/man/bundle-help.1 b/lib/bundler/man/bundle-help.1
new file mode 100644
index 0000000000..9787c2d49f
--- /dev/null
+++ b/lib/bundler/man/bundle-help.1
@@ -0,0 +1,13 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-HELP" "1" "August 2023" "" ""
+.
+.SH "NAME"
+\fBbundle\-help\fR \- Displays detailed help for each subcommand
+.
+.SH "SYNOPSIS"
+\fBbundle help\fR [COMMAND]
+.
+.SH "DESCRIPTION"
+Displays detailed help for the given subcommand\. You can specify a single \fBCOMMAND\fR at the same time\. When \fBCOMMAND\fR is omitted, help for \fBhelp\fR command will be displayed\.
diff --git a/lib/bundler/man/bundle-help.1.ronn b/lib/bundler/man/bundle-help.1.ronn
new file mode 100644
index 0000000000..0e144aead7
--- /dev/null
+++ b/lib/bundler/man/bundle-help.1.ronn
@@ -0,0 +1,12 @@
+bundle-help(1) -- Displays detailed help for each subcommand
+============================================================
+
+## SYNOPSIS
+
+`bundle help` [COMMAND]
+
+## DESCRIPTION
+
+Displays detailed help for the given subcommand.
+You can specify a single `COMMAND` at the same time.
+When `COMMAND` is omitted, help for `help` command will be displayed.
diff --git a/lib/bundler/man/bundle-info.1 b/lib/bundler/man/bundle-info.1
index be1a9e1dcd..2cced71520 100644
--- a/lib/bundler/man/bundle-info.1
+++ b/lib/bundler/man/bundle-info.1
@@ -1,16 +1,16 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-INFO" "1" "June 2022" "" ""
+.TH "BUNDLE\-INFO" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-info\fR \- Show information for the given gem in your bundle
.
.SH "SYNOPSIS"
-\fBbundle info\fR [GEM] [\-\-path]
+\fBbundle info\fR [GEM_NAME] [\-\-path]
.
.SH "DESCRIPTION"
-Print the basic information about the provided GEM such as homepage, version, path and summary\.
+Given a gem name present in your bundle, print the basic information about it such as homepage, version, path and summary\.
.
.SH "OPTIONS"
.
diff --git a/lib/bundler/man/bundle-info.1.ronn b/lib/bundler/man/bundle-info.1.ronn
index 47e457aa3c..cecdeb564f 100644
--- a/lib/bundler/man/bundle-info.1.ronn
+++ b/lib/bundler/man/bundle-info.1.ronn
@@ -3,13 +3,13 @@ bundle-info(1) -- Show information for the given gem in your bundle
## SYNOPSIS
-`bundle info` [GEM]
+`bundle info` [GEM_NAME]
[--path]
## DESCRIPTION
-Print the basic information about the provided GEM such as homepage, version,
-path and summary.
+Given a gem name present in your bundle, print the basic information about it
+ such as homepage, version, path and summary.
## OPTIONS
diff --git a/lib/bundler/man/bundle-init.1 b/lib/bundler/man/bundle-init.1
index 24f1f7543b..c7a9a155b5 100644
--- a/lib/bundler/man/bundle-init.1
+++ b/lib/bundler/man/bundle-init.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-INIT" "1" "June 2022" "" ""
+.TH "BUNDLE\-INIT" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-init\fR \- Generates a Gemfile into the current working directory
@@ -18,6 +18,10 @@ Init generates a default [\fBGemfile(5)\fR][Gemfile(5)] in the current working d
\fB\-\-gemspec\fR
Use the specified \.gemspec to create the [\fBGemfile(5)\fR][Gemfile(5)]
.
+.TP
+\fB\-\-gemfile\fR
+Use the specified name for the gemfile instead of \fBGemfile\fR
+.
.SH "FILES"
Included in the default [\fBGemfile(5)\fR][Gemfile(5)] generated is the line \fB# frozen_string_literal: true\fR\. This is a magic comment supported for the first time in Ruby 2\.3\. The presence of this line results in all string literals in the file being implicitly frozen\.
.
diff --git a/lib/bundler/man/bundle-init.1.ronn b/lib/bundler/man/bundle-init.1.ronn
index 9d3d97deea..7d3cede1f6 100644
--- a/lib/bundler/man/bundle-init.1.ronn
+++ b/lib/bundler/man/bundle-init.1.ronn
@@ -16,6 +16,8 @@ created [`Gemfile(5)`][Gemfile(5)].
* `--gemspec`:
Use the specified .gemspec to create the [`Gemfile(5)`][Gemfile(5)]
+* `--gemfile`:
+ Use the specified name for the gemfile instead of `Gemfile`
## FILES
diff --git a/lib/bundler/man/bundle-inject.1 b/lib/bundler/man/bundle-inject.1
index 2dbcd6e376..9e25c29085 100644
--- a/lib/bundler/man/bundle-inject.1
+++ b/lib/bundler/man/bundle-inject.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-INJECT" "1" "June 2022" "" ""
+.TH "BUNDLE\-INJECT" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-inject\fR \- Add named gem(s) with version requirements to Gemfile
@@ -30,4 +30,7 @@ bundle inject \'rack\' \'> 0\'
.IP "" 0
.
.P
-This will inject the \'rack\' gem with a version greater than 0 in your [\fBGemfile(5)\fR][Gemfile(5)] and Gemfile\.lock
+This will inject the \'rack\' gem with a version greater than 0 in your [\fBGemfile(5)\fR][Gemfile(5)] and Gemfile\.lock\.
+.
+.P
+The \fBbundle inject\fR command was deprecated in Bundler 2\.1 and will be removed in Bundler 3\.0\.
diff --git a/lib/bundler/man/bundle-inject.1.ronn b/lib/bundler/man/bundle-inject.1.ronn
index f454341896..95704eddad 100644
--- a/lib/bundler/man/bundle-inject.1.ronn
+++ b/lib/bundler/man/bundle-inject.1.ronn
@@ -19,4 +19,6 @@ Example:
bundle inject 'rack' '> 0'
This will inject the 'rack' gem with a version greater than 0 in your
-[`Gemfile(5)`][Gemfile(5)] and Gemfile.lock
+[`Gemfile(5)`][Gemfile(5)] and Gemfile.lock.
+
+The `bundle inject` command was deprecated in Bundler 2.1 and will be removed in Bundler 3.0.
diff --git a/lib/bundler/man/bundle-install.1 b/lib/bundler/man/bundle-install.1
index e8184fd510..337683af06 100644
--- a/lib/bundler/man/bundle-install.1
+++ b/lib/bundler/man/bundle-install.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-INSTALL" "1" "June 2022" "" ""
+.TH "BUNDLE\-INSTALL" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-install\fR \- Install the dependencies specified in your Gemfile
@@ -70,6 +70,10 @@ The maximum number of parallel download and install jobs\. The default is the nu
Do not attempt to connect to \fBrubygems\.org\fR\. Instead, Bundler will use the gems already present in Rubygems\' cache or in \fBvendor/cache\fR\. Note that if an appropriate platform\-specific gem exists on \fBrubygems\.org\fR it will not be found\.
.
.TP
+\fB\-\-prefer\-local\fR
+Force using locally installed gems, or gems already present in Rubygems\' cache or in \fBvendor/cache\fR, when resolving, even if newer versions are available remotely\. Only attempt to connect to \fBrubygems\.org\fR for gems that are not present locally\.
+.
+.TP
\fB\-\-no\-cache\fR
Do not update the cache in \fBvendor/cache\fR with the newly bundled gems\. This does not remove any gems in the cache but keeps the newly bundled gems from being cached during the install\.
.
@@ -166,35 +170,6 @@ As a result, \fBbundle install \-\-deployment\fR installs gems to the \fBvendor/
.
.IP "" 0
.
-.SH "SUDO USAGE"
-By default, Bundler installs gems to the same location as \fBgem install\fR\.
-.
-.P
-In some cases, that location may not be writable by your Unix user\. In that case, Bundler will stage everything in a temporary directory, then ask you for your \fBsudo\fR password in order to copy the gems into their system location\.
-.
-.P
-From your perspective, this is identical to installing the gems directly into the system\.
-.
-.P
-You should never use \fBsudo bundle install\fR\. This is because several other steps in \fBbundle install\fR must be performed as the current user:
-.
-.IP "\(bu" 4
-Updating your \fBGemfile\.lock\fR
-.
-.IP "\(bu" 4
-Updating your \fBvendor/cache\fR, if necessary
-.
-.IP "\(bu" 4
-Checking out private git repositories using your user\'s SSH keys
-.
-.IP "" 0
-.
-.P
-Of these three, the first two could theoretically be performed by \fBchown\fRing the resulting files to \fB$SUDO_USER\fR\. The third, however, can only be performed by invoking the \fBgit\fR command as the current user\. Therefore, git gems are downloaded and installed into \fB~/\.bundle\fR rather than $GEM_HOME or $BUNDLE_PATH\.
-.
-.P
-As a result, you should run \fBbundle install\fR as the current user, and Bundler will ask for your password if it is needed to put the gems into their final location\.
-.
.SH "INSTALLING GROUPS"
By default, \fBbundle install\fR will install all gems in all groups in your Gemfile(5), except those declared for a different platform\.
.
diff --git a/lib/bundler/man/bundle-install.1.ronn b/lib/bundler/man/bundle-install.1.ronn
index bec05187f3..be9ed0f974 100644
--- a/lib/bundler/man/bundle-install.1.ronn
+++ b/lib/bundler/man/bundle-install.1.ronn
@@ -109,6 +109,12 @@ automatically and that requires `bundler` to silently remember them. Since
appropriate platform-specific gem exists on `rubygems.org` it will not be
found.
+* `--prefer-local`:
+ Force using locally installed gems, or gems already present in Rubygems' cache
+ or in `vendor/cache`, when resolving, even if newer versions are available
+ remotely. Only attempt to connect to `rubygems.org` for gems that are not
+ present locally.
+
* `--no-cache`:
Do not update the cache in `vendor/cache` with the newly bundled gems. This
does not remove any gems in the cache but keeps the newly bundled gems from
@@ -218,35 +224,6 @@ will cause an error when the Gemfile(5) is modified.
the `vendor/bundle` directory in the application. This may be
overridden using the `--path` option.
-## SUDO USAGE
-
-By default, Bundler installs gems to the same location as `gem install`.
-
-In some cases, that location may not be writable by your Unix user. In
-that case, Bundler will stage everything in a temporary directory,
-then ask you for your `sudo` password in order to copy the gems into
-their system location.
-
-From your perspective, this is identical to installing the gems
-directly into the system.
-
-You should never use `sudo bundle install`. This is because several
-other steps in `bundle install` must be performed as the current user:
-
-* Updating your `Gemfile.lock`
-* Updating your `vendor/cache`, if necessary
-* Checking out private git repositories using your user's SSH keys
-
-Of these three, the first two could theoretically be performed by
-`chown`ing the resulting files to `$SUDO_USER`. The third, however,
-can only be performed by invoking the `git` command as
-the current user. Therefore, git gems are downloaded and installed
-into `~/.bundle` rather than $GEM_HOME or $BUNDLE_PATH.
-
-As a result, you should run `bundle install` as the current user,
-and Bundler will ask for your password if it is needed to put the
-gems into their final location.
-
## INSTALLING GROUPS
By default, `bundle install` will install all gems in all groups
diff --git a/lib/bundler/man/bundle-list.1 b/lib/bundler/man/bundle-list.1
index 5bc3f43943..1680e6007a 100644
--- a/lib/bundler/man/bundle-list.1
+++ b/lib/bundler/man/bundle-list.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-LIST" "1" "June 2022" "" ""
+.TH "BUNDLE\-LIST" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-list\fR \- List all the gems in the bundle
diff --git a/lib/bundler/man/bundle-lock.1 b/lib/bundler/man/bundle-lock.1
index 2934b44171..8722c44b3d 100644
--- a/lib/bundler/man/bundle-lock.1
+++ b/lib/bundler/man/bundle-lock.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-LOCK" "1" "June 2022" "" ""
+.TH "BUNDLE\-LOCK" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-lock\fR \- Creates / Updates a lockfile without installing
diff --git a/lib/bundler/man/bundle-open.1 b/lib/bundler/man/bundle-open.1
index c4e58fb854..3513f0d09b 100644
--- a/lib/bundler/man/bundle-open.1
+++ b/lib/bundler/man/bundle-open.1
@@ -1,13 +1,13 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-OPEN" "1" "June 2022" "" ""
+.TH "BUNDLE\-OPEN" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-open\fR \- Opens the source directory for a gem in your bundle
.
.SH "SYNOPSIS"
-\fBbundle open\fR [GEM]
+\fBbundle open\fR [GEM] [\-\-path=PATH]
.
.SH "DESCRIPTION"
Opens the source directory of the provided GEM in your editor\.
@@ -30,3 +30,23 @@ bundle open \'rack\'
.
.P
Will open the source directory for the \'rack\' gem in your bundle\.
+.
+.IP "" 4
+.
+.nf
+
+bundle open \'rack\' \-\-path \'README\.md\'
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Will open the README\.md file of the \'rack\' gem source in your bundle\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-path\fR
+Specify GEM source relative path to open\.
+
diff --git a/lib/bundler/man/bundle-open.1.ronn b/lib/bundler/man/bundle-open.1.ronn
index 497beac93f..a857f3a965 100644
--- a/lib/bundler/man/bundle-open.1.ronn
+++ b/lib/bundler/man/bundle-open.1.ronn
@@ -3,7 +3,7 @@ bundle-open(1) -- Opens the source directory for a gem in your bundle
## SYNOPSIS
-`bundle open` [GEM]
+`bundle open` [GEM] [--path=PATH]
## DESCRIPTION
@@ -17,3 +17,11 @@ Example:
bundle open 'rack'
Will open the source directory for the 'rack' gem in your bundle.
+
+ bundle open 'rack' --path 'README.md'
+
+Will open the README.md file of the 'rack' gem source in your bundle.
+
+## OPTIONS
+* `--path`:
+ Specify GEM source relative path to open.
diff --git a/lib/bundler/man/bundle-outdated.1 b/lib/bundler/man/bundle-outdated.1
index ee865b3e97..129ff00f58 100644
--- a/lib/bundler/man/bundle-outdated.1
+++ b/lib/bundler/man/bundle-outdated.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-OUTDATED" "1" "June 2022" "" ""
+.TH "BUNDLE\-OUTDATED" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-outdated\fR \- List installed gems with newer versions available
@@ -83,9 +83,10 @@ If the regular output shows the following:
.
.nf
-* faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test"
-* hashie (newest 3\.4\.6, installed 1\.2\.0, requested = 1\.2\.0) in groups "default"
-* headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test"
+* Gem Current Latest Requested Groups
+* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
+* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default
+* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test
.
.fi
.
@@ -98,7 +99,8 @@ If the regular output shows the following:
.
.nf
-* hashie (newest 3\.4\.6, installed 1\.2\.0, requested = 1\.2\.0) in groups "default"
+* Gem Current Latest Requested Groups
+* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default
.
.fi
.
@@ -111,7 +113,8 @@ If the regular output shows the following:
.
.nf
-* headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test"
+* Gem Current Latest Requested Groups
+* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test
.
.fi
.
@@ -124,7 +127,8 @@ If the regular output shows the following:
.
.nf
-* faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test"
+* Gem Current Latest Requested Groups
+* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
.
.fi
.
@@ -137,8 +141,8 @@ Filter options can be combined\. \fB\-\-filter\-minor\fR and \fB\-\-filter\-patc
.
.nf
-* faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test"
-* headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test"
+* Gem Current Latest Requested Groups
+* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
.
.fi
.
diff --git a/lib/bundler/man/bundle-outdated.1.ronn b/lib/bundler/man/bundle-outdated.1.ronn
index 04096ffbb6..27bf21ab9d 100644
--- a/lib/bundler/man/bundle-outdated.1.ronn
+++ b/lib/bundler/man/bundle-outdated.1.ronn
@@ -78,25 +78,28 @@ in the output.
If the regular output shows the following:
- * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
- * hashie (newest 3.4.6, installed 1.2.0, requested = 1.2.0) in groups "default"
- * headless (newest 2.3.1, installed 2.2.3) in groups "test"
+ * Gem Current Latest Requested Groups
+ * faker 1.6.5 1.6.6 ~> 1.4 development, test
+ * hashie 1.2.0 3.4.6 = 1.2.0 default
+ * headless 2.2.3 2.3.1 = 2.2.3 test
`--filter-major` would only show:
- * hashie (newest 3.4.6, installed 1.2.0, requested = 1.2.0) in groups "default"
+ * Gem Current Latest Requested Groups
+ * hashie 1.2.0 3.4.6 = 1.2.0 default
`--filter-minor` would only show:
- * headless (newest 2.3.1, installed 2.2.3) in groups "test"
+ * Gem Current Latest Requested Groups
+ * headless 2.2.3 2.3.1 = 2.2.3 test
`--filter-patch` would only show:
- * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
+ * Gem Current Latest Requested Groups
+ * faker 1.6.5 1.6.6 ~> 1.4 development, test
Filter options can be combined. `--filter-minor` and `--filter-patch` would show:
- * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
- * headless (newest 2.3.1, installed 2.2.3) in groups "test"
-
+ * Gem Current Latest Requested Groups
+ * faker 1.6.5 1.6.6 ~> 1.4 development, test
Combining all three `filter` options would be the same result as providing none of them.
diff --git a/lib/bundler/man/bundle-platform.1 b/lib/bundler/man/bundle-platform.1
index aab17a429a..5021c46b4c 100644
--- a/lib/bundler/man/bundle-platform.1
+++ b/lib/bundler/man/bundle-platform.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-PLATFORM" "1" "June 2022" "" ""
+.TH "BUNDLE\-PLATFORM" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-platform\fR \- Displays platform compatibility information
@@ -10,7 +10,7 @@
\fBbundle platform\fR [\-\-ruby]
.
.SH "DESCRIPTION"
-\fBplatform\fR will display information from your Gemfile, Gemfile\.lock, and Ruby VM about your platform\.
+\fBplatform\fR displays information from your Gemfile, Gemfile\.lock, and Ruby VM about your platform\.
.
.P
For instance, using this Gemfile(5):
@@ -21,7 +21,7 @@ For instance, using this Gemfile(5):
source "https://rubygems\.org"
-ruby "1\.9\.3"
+ruby "3\.1\.2"
gem "rack"
.
@@ -30,7 +30,7 @@ gem "rack"
.IP "" 0
.
.P
-If you run \fBbundle platform\fR on Ruby 1\.9\.3, it will display the following output:
+If you run \fBbundle platform\fR on Ruby 3\.1\.2, it displays the following output:
.
.IP "" 4
.
@@ -39,10 +39,13 @@ If you run \fBbundle platform\fR on Ruby 1\.9\.3, it will display the following
Your platform is: x86_64\-linux
Your app has gems that work on these platforms:
+* arm64\-darwin\-21
* ruby
+* x64\-mingw\-ucrt
+* x86_64\-linux
Your Gemfile specifies a Ruby version requirement:
-* ruby 1\.9\.3
+* ruby 3\.1\.2
Your current platform satisfies the Ruby version requirement\.
.
@@ -51,11 +54,18 @@ Your current platform satisfies the Ruby version requirement\.
.IP "" 0
.
.P
-\fBplatform\fR will list all the platforms in your \fBGemfile\.lock\fR as well as the \fBruby\fR directive if applicable from your Gemfile(5)\. It will also let you know if the \fBruby\fR directive requirement has been met\. If \fBruby\fR directive doesn\'t match the running Ruby VM, it will tell you what part does not\.
+\fBplatform\fR lists all the platforms in your \fBGemfile\.lock\fR as well as the \fBruby\fR directive if applicable from your Gemfile(5)\. It also lets you know if the \fBruby\fR directive requirement has been met\. If \fBruby\fR directive doesn\'t match the running Ruby VM, it tells you what part does not\.
.
.SH "OPTIONS"
.
.TP
\fB\-\-ruby\fR
It will display the ruby directive information, so you don\'t have to parse it from the Gemfile(5)\.
+.
+.SH "SEE ALSO"
+.
+.IP "\(bu" 4
+bundle\-lock(1) \fIbundle\-lock\.1\.html\fR
+.
+.IP "" 0
diff --git a/lib/bundler/man/bundle-platform.1.ronn b/lib/bundler/man/bundle-platform.1.ronn
index b5d3283fb6..744acd1b43 100644
--- a/lib/bundler/man/bundle-platform.1.ronn
+++ b/lib/bundler/man/bundle-platform.1.ronn
@@ -7,36 +7,43 @@ bundle-platform(1) -- Displays platform compatibility information
## DESCRIPTION
-`platform` will display information from your Gemfile, Gemfile.lock, and Ruby
+`platform` displays information from your Gemfile, Gemfile.lock, and Ruby
VM about your platform.
For instance, using this Gemfile(5):
source "https://rubygems.org"
- ruby "1.9.3"
+ ruby "3.1.2"
gem "rack"
-If you run `bundle platform` on Ruby 1.9.3, it will display the following output:
+If you run `bundle platform` on Ruby 3.1.2, it displays the following output:
Your platform is: x86_64-linux
Your app has gems that work on these platforms:
+ * arm64-darwin-21
* ruby
+ * x64-mingw-ucrt
+ * x86_64-linux
Your Gemfile specifies a Ruby version requirement:
- * ruby 1.9.3
+ * ruby 3.1.2
Your current platform satisfies the Ruby version requirement.
-`platform` will list all the platforms in your `Gemfile.lock` as well as the
-`ruby` directive if applicable from your Gemfile(5). It will also let you know
+`platform` lists all the platforms in your `Gemfile.lock` as well as the
+`ruby` directive if applicable from your Gemfile(5). It also lets you know
if the `ruby` directive requirement has been met. If `ruby` directive doesn't
-match the running Ruby VM, it will tell you what part does not.
+match the running Ruby VM, it tells you what part does not.
## OPTIONS
* `--ruby`:
It will display the ruby directive information, so you don't have to
parse it from the Gemfile(5).
+
+## SEE ALSO
+
+* [bundle-lock(1)](bundle-lock.1.html)
diff --git a/lib/bundler/man/bundle-plugin.1 b/lib/bundler/man/bundle-plugin.1
new file mode 100644
index 0000000000..ec30e1d0fd
--- /dev/null
+++ b/lib/bundler/man/bundle-plugin.1
@@ -0,0 +1,81 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-PLUGIN" "1" "August 2023" "" ""
+.
+.SH "NAME"
+\fBbundle\-plugin\fR \- Manage Bundler plugins
+.
+.SH "SYNOPSIS"
+\fBbundle plugin\fR install PLUGINS [\-\-source=\fISOURCE\fR] [\-\-version=\fIversion\fR] [\-\-git|\-\-local_git=\fIgit\-url\fR] [\-\-branch=\fIbranch\fR|\-\-ref=\fIrev\fR]
+.
+.br
+\fBbundle plugin\fR uninstall PLUGINS
+.
+.br
+\fBbundle plugin\fR list
+.
+.br
+\fBbundle plugin\fR help [COMMAND]
+.
+.SH "DESCRIPTION"
+You can install, uninstall, and list plugin(s) with this command to extend functionalities of Bundler\.
+.
+.SH "SUB\-COMMANDS"
+.
+.SS "install"
+Install the given plugin(s)\.
+.
+.IP "\(bu" 4
+\fBbundle plugin install bundler\-graph\fR: Install bundler\-graph gem from RubyGems\.org\. The global source, specified in source in Gemfile is ignored\.
+.
+.IP "\(bu" 4
+\fBbundle plugin install bundler\-graph \-\-source https://example\.com\fR: Install bundler\-graph gem from example\.com\. The global source, specified in source in Gemfile is not considered\.
+.
+.IP "\(bu" 4
+\fBbundle plugin install bundler\-graph \-\-version 0\.2\.1\fR: You can specify the version of the gem via \fB\-\-version\fR\.
+.
+.IP "\(bu" 4
+\fBbundle plugin install bundler\-graph \-\-git https://github\.com/rubygems/bundler\-graph\fR: Install bundler\-graph gem from Git repository\. \fB\-\-git\fR can be replaced with \fB\-\-local\-git\fR\. You cannot use both \fB\-\-git\fR and \fB\-\-local\-git\fR\. You can use standard Git URLs like:
+.
+.IP "\(bu" 4
+\fBssh://[user@]host\.xz[:port]/path/to/repo\.git\fR
+.
+.IP "\(bu" 4
+\fBhttp[s]://host\.xz[:port]/path/to/repo\.git\fR
+.
+.IP "\(bu" 4
+\fB/path/to/repo\fR
+.
+.IP "\(bu" 4
+\fBfile:///path/to/repo\fR
+.
+.IP "" 0
+.
+.IP
+When you specify \fB\-\-git\fR/\fB\-\-local\-git\fR, you can use \fB\-\-branch\fR or \fB\-\-ref\fR to specify any branch, tag, or commit hash (revision) to use\. When you specify both, only the latter is used\.
+.
+.IP "" 0
+.
+.SS "uninstall"
+Uninstall the plugin(s) specified in PLUGINS\.
+.
+.SS "list"
+List the installed plugins and available commands\.
+.
+.P
+No options\.
+.
+.SS "help"
+Describe subcommands or one specific subcommand\.
+.
+.P
+No options\.
+.
+.SH "SEE ALSO"
+.
+.IP "\(bu" 4
+How to write a Bundler plugin \fIhttps://bundler\.io/guides/bundler_plugins\.html\fR
+.
+.IP "" 0
+
diff --git a/lib/bundler/man/bundle-plugin.1.ronn b/lib/bundler/man/bundle-plugin.1.ronn
new file mode 100644
index 0000000000..4f234eeba7
--- /dev/null
+++ b/lib/bundler/man/bundle-plugin.1.ronn
@@ -0,0 +1,59 @@
+bundle-plugin(1) -- Manage Bundler plugins
+==========================================
+
+## SYNOPSIS
+
+`bundle plugin` install PLUGINS [--source=<SOURCE>] [--version=<version>]
+ [--git|--local_git=<git-url>] [--branch=<branch>|--ref=<rev>]<br>
+`bundle plugin` uninstall PLUGINS<br>
+`bundle plugin` list<br>
+`bundle plugin` help [COMMAND]
+
+## DESCRIPTION
+
+You can install, uninstall, and list plugin(s) with this command to extend functionalities of Bundler.
+
+## SUB-COMMANDS
+
+### install
+
+Install the given plugin(s).
+
+* `bundle plugin install bundler-graph`:
+ Install bundler-graph gem from RubyGems.org. The global source, specified in source in Gemfile is ignored.
+
+* `bundle plugin install bundler-graph --source https://example.com`:
+ Install bundler-graph gem from example.com. The global source, specified in source in Gemfile is not considered.
+
+* `bundle plugin install bundler-graph --version 0.2.1`:
+ You can specify the version of the gem via `--version`.
+
+* `bundle plugin install bundler-graph --git https://github.com/rubygems/bundler-graph`:
+ Install bundler-graph gem from Git repository. `--git` can be replaced with `--local-git`. You cannot use both `--git` and `--local-git`. You can use standard Git URLs like:
+
+ * `ssh://[user@]host.xz[:port]/path/to/repo.git`
+ * `http[s]://host.xz[:port]/path/to/repo.git`
+ * `/path/to/repo`
+ * `file:///path/to/repo`
+
+ When you specify `--git`/`--local-git`, you can use `--branch` or `--ref` to specify any branch, tag, or commit hash (revision) to use. When you specify both, only the latter is used.
+
+### uninstall
+
+Uninstall the plugin(s) specified in PLUGINS.
+
+### list
+
+List the installed plugins and available commands.
+
+No options.
+
+### help
+
+Describe subcommands or one specific subcommand.
+
+No options.
+
+## SEE ALSO
+
+* [How to write a Bundler plugin](https://bundler.io/guides/bundler_plugins.html)
diff --git a/lib/bundler/man/bundle-pristine.1 b/lib/bundler/man/bundle-pristine.1
index a0a5ac1a9b..af81c48d2b 100644
--- a/lib/bundler/man/bundle-pristine.1
+++ b/lib/bundler/man/bundle-pristine.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-PRISTINE" "1" "June 2022" "" ""
+.TH "BUNDLE\-PRISTINE" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-pristine\fR \- Restores installed gems to their pristine condition
diff --git a/lib/bundler/man/bundle-remove.1 b/lib/bundler/man/bundle-remove.1
index f6055716c7..d86cf134bd 100644
--- a/lib/bundler/man/bundle-remove.1
+++ b/lib/bundler/man/bundle-remove.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-REMOVE" "1" "June 2022" "" ""
+.TH "BUNDLE\-REMOVE" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-remove\fR \- Removes gems from the Gemfile
diff --git a/lib/bundler/man/bundle-show.1 b/lib/bundler/man/bundle-show.1
index 14db8075f6..aa91176bf2 100644
--- a/lib/bundler/man/bundle-show.1
+++ b/lib/bundler/man/bundle-show.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-SHOW" "1" "June 2022" "" ""
+.TH "BUNDLE\-SHOW" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem
diff --git a/lib/bundler/man/bundle-update.1 b/lib/bundler/man/bundle-update.1
index b7e2b78812..e4e10ad23b 100644
--- a/lib/bundler/man/bundle-update.1
+++ b/lib/bundler/man/bundle-update.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-UPDATE" "1" "June 2022" "" ""
+.TH "BUNDLE\-UPDATE" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-update\fR \- Update your gems to the latest available versions
diff --git a/lib/bundler/man/bundle-version.1 b/lib/bundler/man/bundle-version.1
new file mode 100644
index 0000000000..5e3ed44600
--- /dev/null
+++ b/lib/bundler/man/bundle-version.1
@@ -0,0 +1,35 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-VERSION" "1" "August 2023" "" ""
+.
+.SH "NAME"
+\fBbundle\-version\fR \- Prints Bundler version information
+.
+.SH "SYNOPSIS"
+\fBbundle version\fR
+.
+.SH "DESCRIPTION"
+Prints Bundler version information\.
+.
+.SH "OPTIONS"
+No options\.
+.
+.SH "EXAMPLE"
+Print the version of Bundler with build date and commit hash of the in the Git source\.
+.
+.IP "" 4
+.
+.nf
+
+bundle version
+.
+.fi
+.
+.IP "" 0
+.
+.P
+shows \fBBundler version 2\.3\.21 (2022\-08\-24 commit d54be5fdd8)\fR for example\.
+.
+.P
+cf\. \fBbundle \-\-version\fR shows \fBBundler version 2\.3\.21\fR\.
diff --git a/lib/bundler/man/bundle-version.1.ronn b/lib/bundler/man/bundle-version.1.ronn
new file mode 100644
index 0000000000..46c6f0b30a
--- /dev/null
+++ b/lib/bundler/man/bundle-version.1.ronn
@@ -0,0 +1,24 @@
+bundle-version(1) -- Prints Bundler version information
+=======================================================
+
+## SYNOPSIS
+
+`bundle version`
+
+## DESCRIPTION
+
+Prints Bundler version information.
+
+## OPTIONS
+
+No options.
+
+## EXAMPLE
+
+Print the version of Bundler with build date and commit hash of the in the Git source.
+
+ bundle version
+
+shows `Bundler version 2.3.21 (2022-08-24 commit d54be5fdd8)` for example.
+
+cf. `bundle --version` shows `Bundler version 2.3.21`.
diff --git a/lib/bundler/man/bundle-viz.1 b/lib/bundler/man/bundle-viz.1
index 68ea9e7e42..d5330ec754 100644
--- a/lib/bundler/man/bundle-viz.1
+++ b/lib/bundler/man/bundle-viz.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-VIZ" "1" "June 2022" "" ""
+.TH "BUNDLE\-VIZ" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile
@@ -15,6 +15,9 @@
.P
The associated gems must also be installed via \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR\.
.
+.P
+\fBviz\fR command was deprecated in Bundler 2\.2\. Use bundler\-graph plugin \fIhttps://github\.com/rubygems/bundler\-graph\fR instead\.
+.
.SH "OPTIONS"
.
.TP
diff --git a/lib/bundler/man/bundle-viz.1.ronn b/lib/bundler/man/bundle-viz.1.ronn
index 701df5415e..f220256943 100644
--- a/lib/bundler/man/bundle-viz.1.ronn
+++ b/lib/bundler/man/bundle-viz.1.ronn
@@ -16,6 +16,8 @@ bundle-viz(1) -- Generates a visual dependency graph for your Gemfile
The associated gems must also be installed via [`bundle install(1)`](bundle-install.1.html).
+`viz` command was deprecated in Bundler 2.2. Use [bundler-graph plugin](https://github.com/rubygems/bundler-graph) instead.
+
## OPTIONS
* `--file`, `-f`:
diff --git a/lib/bundler/man/bundle.1 b/lib/bundler/man/bundle.1
index 31d1c12962..99c65a72b5 100644
--- a/lib/bundler/man/bundle.1
+++ b/lib/bundler/man/bundle.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE" "1" "June 2022" "" ""
+.TH "BUNDLE" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\fR \- Ruby Dependency Management
@@ -43,8 +43,8 @@ Install the gems specified by the \fBGemfile\fR or \fBGemfile\.lock\fR
Update dependencies to their latest versions
.
.TP
-\fBbundle package(1)\fR \fIbundle\-package\.1\.html\fR
-Package the \.gem files required by your application into the \fBvendor/cache\fR directory
+\fBbundle cache(1)\fR \fIbundle\-cache\.1\.html\fR
+Package the \.gem files required by your application into the \fBvendor/cache\fR directory (aliases: \fBbundle package\fR, \fBbundle pack\fR)
.
.TP
\fBbundle exec(1)\fR \fIbundle\-exec\.1\.html\fR
@@ -55,7 +55,7 @@ Execute a script in the current bundle
Specify and read configuration options for Bundler
.
.TP
-\fBbundle help(1)\fR
+\fBbundle help(1)\fR \fIbundle\-help\.1\.html\fR
Display detailed help for each subcommand
.
.SH "UTILITIES"
@@ -81,7 +81,7 @@ Show the source location of a particular gem in the bundle
Show all of the outdated gems in the current bundle
.
.TP
-\fBbundle console(1)\fR
+\fBbundle console(1)\fR (deprecated)
Start an IRB session in the current bundle
.
.TP
@@ -93,7 +93,7 @@ Open an installed gem in the editor
Generate a lockfile for your dependencies
.
.TP
-\fBbundle viz(1)\fR \fIbundle\-viz\.1\.html\fR
+\fBbundle viz(1)\fR \fIbundle\-viz\.1\.html\fR (deprecated)
Generate a visual representation of your dependencies
.
.TP
@@ -120,6 +120,14 @@ Display warnings about common problems
\fBbundle remove(1)\fR \fIbundle\-remove\.1\.html\fR
Removes gems from the Gemfile
.
+.TP
+\fBbundle plugin(1)\fR \fIbundle\-plugin\.1\.html\fR
+Manage Bundler plugins
+.
+.TP
+\fBbundle version(1)\fR \fIbundle\-version\.1\.html\fR
+Prints Bundler version information
+.
.SH "PLUGINS"
When running a command that isn\'t listed in PRIMARY COMMANDS or UTILITIES, Bundler will try to find an executable on your path named \fBbundler\-<command>\fR and execute it, passing down any extra arguments to it\.
.
@@ -127,10 +135,7 @@ When running a command that isn\'t listed in PRIMARY COMMANDS or UTILITIES, Bund
These commands are obsolete and should no longer be used:
.
.IP "\(bu" 4
-\fBbundle cache(1)\fR
-.
-.IP "\(bu" 4
-\fBbundle show(1)\fR
+\fBbundle inject(1)\fR
.
.IP "" 0
diff --git a/lib/bundler/man/bundle.1.ronn b/lib/bundler/man/bundle.1.ronn
index 5b1712394a..8245effabd 100644
--- a/lib/bundler/man/bundle.1.ronn
+++ b/lib/bundler/man/bundle.1.ronn
@@ -36,9 +36,9 @@ We divide `bundle` subcommands into primary commands and utilities:
* [`bundle update(1)`](bundle-update.1.html):
Update dependencies to their latest versions
-* [`bundle package(1)`](bundle-package.1.html):
+* [`bundle cache(1)`](bundle-cache.1.html):
Package the .gem files required by your application into the
- `vendor/cache` directory
+ `vendor/cache` directory (aliases: `bundle package`, `bundle pack`)
* [`bundle exec(1)`](bundle-exec.1.html):
Execute a script in the current bundle
@@ -46,7 +46,7 @@ We divide `bundle` subcommands into primary commands and utilities:
* [`bundle config(1)`](bundle-config.1.html):
Specify and read configuration options for Bundler
-* `bundle help(1)`:
+* [`bundle help(1)`](bundle-help.1.html):
Display detailed help for each subcommand
## UTILITIES
@@ -67,7 +67,7 @@ We divide `bundle` subcommands into primary commands and utilities:
* [`bundle outdated(1)`](bundle-outdated.1.html):
Show all of the outdated gems in the current bundle
-* `bundle console(1)`:
+* `bundle console(1)` (deprecated):
Start an IRB session in the current bundle
* [`bundle open(1)`](bundle-open.1.html):
@@ -76,7 +76,7 @@ We divide `bundle` subcommands into primary commands and utilities:
* [`bundle lock(1)`](bundle-lock.1.html):
Generate a lockfile for your dependencies
-* [`bundle viz(1)`](bundle-viz.1.html):
+* [`bundle viz(1)`](bundle-viz.1.html) (deprecated):
Generate a visual representation of your dependencies
* [`bundle init(1)`](bundle-init.1.html):
@@ -97,6 +97,12 @@ We divide `bundle` subcommands into primary commands and utilities:
* [`bundle remove(1)`](bundle-remove.1.html):
Removes gems from the Gemfile
+* [`bundle plugin(1)`](bundle-plugin.1.html):
+ Manage Bundler plugins
+
+* [`bundle version(1)`](bundle-version.1.html):
+ Prints Bundler version information
+
## PLUGINS
When running a command that isn't listed in PRIMARY COMMANDS or UTILITIES,
@@ -107,5 +113,4 @@ and execute it, passing down any extra arguments to it.
These commands are obsolete and should no longer be used:
-* `bundle cache(1)`
-* `bundle show(1)`
+* `bundle inject(1)`
diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5
index 6ea1602ea4..352fa0f545 100644
--- a/lib/bundler/man/gemfile.5
+++ b/lib/bundler/man/gemfile.5
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "GEMFILE" "5" "June 2022" "" ""
+.TH "GEMFILE" "5" "August 2023" "" ""
.
.SH "NAME"
\fBGemfile\fR \- A format for describing gem dependencies for Ruby programs
@@ -73,13 +73,26 @@ Credentials in the source URL will take precedence over credentials set using \f
If your application requires a specific Ruby version or engine, specify your requirements using the \fBruby\fR method, with the following arguments\. All parameters are \fBOPTIONAL\fR unless otherwise specified\.
.
.SS "VERSION (required)"
-The version of Ruby that your application requires\. If your application requires an alternate Ruby engine, such as JRuby, Rubinius or TruffleRuby, this should be the Ruby version that the engine is compatible with\.
+The version of Ruby that your application requires\. If your application requires an alternate Ruby engine, such as JRuby, TruffleRuby, etc\., this should be the Ruby version that the engine is compatible with\.
.
.IP "" 4
.
.nf
-ruby "1\.9\.3"
+ruby "3\.1\.2"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+If you wish to derive your Ruby version from a version file (ie \.ruby\-version), you can use the \fBfile\fR option instead\.
+.
+.IP "" 4
+.
+.nf
+
+ruby file: "\.ruby\-version"
.
.fi
.
@@ -95,7 +108,7 @@ What exactly is an Engine? \- A Ruby engine is an implementation of the Ruby lan
For background: the reference or original implementation of the Ruby programming language is called Matz\'s Ruby Interpreter \fIhttps://en\.wikipedia\.org/wiki/Ruby_MRI\fR, or MRI for short\. This is named after Ruby creator Yukihiro Matsumoto, also known as Matz\. MRI is also known as CRuby, because it is written in C\. MRI is the most widely used Ruby engine\.
.
.IP "\(bu" 4
-Other implementations \fIhttps://www\.ruby\-lang\.org/en/about/\fR of Ruby exist\. Some of the more well\-known implementations include Rubinius \fIhttps://rubinius\.com/\fR, and JRuby \fIhttp://jruby\.org/\fR\. Rubinius is an alternative implementation of Ruby written in Ruby\. JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine\.
+Other implementations \fIhttps://www\.ruby\-lang\.org/en/about/\fR of Ruby exist\. Some of the more well\-known implementations include JRuby \fIhttp://jruby\.org/\fR and TruffleRuby \fIhttps://www\.graalvm\.org/ruby/\fR\. Rubinius is an alternative implementation of Ruby written in Ruby\. JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine\. TruffleRuby is a Ruby implementation on the GraalVM, a language toolkit built on the JVM\.
.
.IP "" 0
.
@@ -106,20 +119,23 @@ Each application \fImay\fR specify a Ruby engine version\. If an engine version
.
.nf
-ruby "1\.8\.7", engine: "jruby", engine_version: "1\.6\.7"
+ruby "2\.6\.8", engine: "jruby", engine_version: "9\.3\.8\.0"
.
.fi
.
.IP "" 0
.
.SS "PATCHLEVEL"
-Each application \fImay\fR specify a Ruby patchlevel\.
+Each application \fImay\fR specify a Ruby patchlevel\. Specifying the patchlevel has been meaningless since Ruby 2\.1\.0 was released as the patchlevel is now uniquely determined by a combination of major, minor, and teeny version numbers\.
+.
+.P
+This option was implemented in Bundler 1\.4\.0 for Ruby 2\.0 or earlier\.
.
.IP "" 4
.
.nf
-ruby "2\.0\.0", patchlevel: "247"
+ruby "3\.1\.2", patchlevel: "20"
.
.fi
.
@@ -254,19 +270,23 @@ There are a number of \fBGemfile\fR platforms:
.
.TP
\fBruby\fR
-C Ruby (MRI), Rubinius or TruffleRuby, but \fBNOT\fR Windows
+C Ruby (MRI), Rubinius, or TruffleRuby, but not Windows
.
.TP
\fBmri\fR
-Same as \fIruby\fR, but only C Ruby (MRI)
+C Ruby (MRI) only, but not Windows
.
.TP
-\fBmingw\fR
-Windows 32 bit \'mingw32\' platform (aka RubyInstaller)
+\fBwindows\fR
+Windows C Ruby (MRI), including RubyInstaller 32\-bit and 64\-bit versions
.
.TP
-\fBx64_mingw\fR
-Windows 64 bit \'mingw32\' platform (aka RubyInstaller x64)
+\fBmswin\fR
+Windows C Ruby (MRI), including RubyInstaller 32\-bit versions
+.
+.TP
+\fBmswin64\fR
+Windows C Ruby (MRI), including RubyInstaller 64\-bit versions
.
.TP
\fBrbx\fR
@@ -280,55 +300,29 @@ JRuby
\fBtruffleruby\fR
TruffleRuby
.
-.TP
-\fBmswin\fR
-Windows
-.
-.P
-You can restrict further by platform and version for all platforms \fIexcept\fR for \fBrbx\fR, \fBjruby\fR, \fBtruffleruby\fR and \fBmswin\fR\.
-.
.P
-To specify a version in addition to a platform, append the version number without the delimiter to the platform\. For example, to specify that a gem should only be used on platforms with Ruby 2\.3, use:
+On platforms \fBruby\fR, \fBmri\fR, \fBmswin\fR, \fBmswin64\fR, and \fBwindows\fR, you may additionally specify a version by appending the major and minor version numbers without a delimiter\. For example, to specify that a gem should only be used on platform \fBruby\fR version 3\.1, use:
.
.IP "" 4
.
.nf
-ruby_23
+ruby_31
.
.fi
.
.IP "" 0
.
.P
-The full list of platforms and supported versions includes:
-.
-.TP
-\fBruby\fR
-1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6
-.
-.TP
-\fBmri\fR
-1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6
-.
-.TP
-\fBmingw\fR
-1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6
-.
-.TP
-\fBx64_mingw\fR
-2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6
-.
-.P
-As with groups, you can specify one or more platforms:
+As with groups (above), you may specify one or more platforms:
.
.IP "" 4
.
.nf
gem "weakling", platforms: :jruby
-gem "ruby\-debug", platforms: :mri_18
-gem "nokogiri", platforms: [:mri_18, :jruby]
+gem "ruby\-debug", platforms: :mri_31
+gem "nokogiri", platforms: [:windows_31, :jruby]
.
.fi
.
@@ -527,7 +521,7 @@ Are both equivalent to
.
.nf
-gem "rails", git: "git://github\.com/rails/rails\.git"
+gem "rails", git: "https://github\.com/rails/rails\.git"
.
.fi
.
@@ -721,7 +715,7 @@ If you wish to use Bundler to help install dependencies for a gem while it is be
The \fBgemspec\fR method adds any runtime dependencies as gem requirements in the default group\. It also adds development dependencies as gem requirements in the \fBdevelopment\fR group\. Finally, it adds a gem requirement on your project (\fBpath: \'\.\'\fR)\. In conjunction with \fBBundler\.setup\fR, this allows you to require project files in your test code as you would if the project were installed as a gem; you need not manipulate the load path manually or require project files via relative paths\.
.
.P
-The \fBgemspec\fR method supports optional \fB:path\fR, \fB:glob\fR, \fB:name\fR, and \fB:development_group\fR options, which control where bundler looks for the \fB\.gemspec\fR, the glob it uses to look for the gemspec (defaults to: "{,\fI,\fR/*}\.gemspec"), what named \fB\.gemspec\fR it uses (if more than one is present), and which group development dependencies are included in\.
+The \fBgemspec\fR method supports optional \fB:path\fR, \fB:glob\fR, \fB:name\fR, and \fB:development_group\fR options, which control where bundler looks for the \fB\.gemspec\fR, the glob it uses to look for the gemspec (defaults to: \fB{,*,*/*}\.gemspec\fR), what named \fB\.gemspec\fR it uses (if more than one is present), and which group development dependencies are included in\.
.
.P
When a \fBgemspec\fR dependency encounters version conflicts during resolution, the local version under development will always be selected \-\- even if there are remote versions that better match other requirements for the \fBgemspec\fR gem\.
diff --git a/lib/bundler/man/gemfile.5.ronn b/lib/bundler/man/gemfile.5.ronn
index c2f9141c65..6749c33f59 100644
--- a/lib/bundler/man/gemfile.5.ronn
+++ b/lib/bundler/man/gemfile.5.ronn
@@ -64,10 +64,15 @@ All parameters are `OPTIONAL` unless otherwise specified.
### VERSION (required)
The version of Ruby that your application requires. If your application
-requires an alternate Ruby engine, such as JRuby, Rubinius or TruffleRuby, this
+requires an alternate Ruby engine, such as JRuby, TruffleRuby, etc., this
should be the Ruby version that the engine is compatible with.
- ruby "1.9.3"
+ ruby "3.1.2"
+
+If you wish to derive your Ruby version from a version file (ie .ruby-version),
+you can use the `file` option instead.
+
+ ruby file: ".ruby-version"
### ENGINE
@@ -86,9 +91,10 @@ What exactly is an Engine?
- [Other implementations](https://www.ruby-lang.org/en/about/) of Ruby exist.
Some of the more well-known implementations include
- [Rubinius](https://rubinius.com/), and [JRuby](http://jruby.org/).
+ [JRuby](http://jruby.org/) and [TruffleRuby](https://www.graalvm.org/ruby/).
Rubinius is an alternative implementation of Ruby written in Ruby.
JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine.
+ TruffleRuby is a Ruby implementation on the GraalVM, a language toolkit built on the JVM.
### ENGINE VERSION
@@ -96,13 +102,17 @@ Each application _may_ specify a Ruby engine version. If an engine version is
specified, an engine _must_ also be specified. If the engine is "ruby" the
engine version specified _must_ match the Ruby version.
- ruby "1.8.7", engine: "jruby", engine_version: "1.6.7"
+ ruby "2.6.8", engine: "jruby", engine_version: "9.3.8.0"
### PATCHLEVEL
-Each application _may_ specify a Ruby patchlevel.
+Each application _may_ specify a Ruby patchlevel. Specifying the patchlevel has
+been meaningless since Ruby 2.1.0 was released as the patchlevel is now
+uniquely determined by a combination of major, minor, and teeny version numbers.
+
+This option was implemented in Bundler 1.4.0 for Ruby 2.0 or earlier.
- ruby "2.0.0", patchlevel: "247"
+ ruby "3.1.2", patchlevel: "20"
## GEMS
@@ -190,47 +200,34 @@ platforms.
There are a number of `Gemfile` platforms:
* `ruby`:
- C Ruby (MRI), Rubinius or TruffleRuby, but `NOT` Windows
+ C Ruby (MRI), Rubinius, or TruffleRuby, but not Windows
* `mri`:
- Same as _ruby_, but only C Ruby (MRI)
- * `mingw`:
- Windows 32 bit 'mingw32' platform (aka RubyInstaller)
- * `x64_mingw`:
- Windows 64 bit 'mingw32' platform (aka RubyInstaller x64)
+ C Ruby (MRI) only, but not Windows
+ * `windows`:
+ Windows C Ruby (MRI), including RubyInstaller 32-bit and 64-bit versions
+ * `mswin`:
+ Windows C Ruby (MRI), including RubyInstaller 32-bit versions
+ * `mswin64`:
+ Windows C Ruby (MRI), including RubyInstaller 64-bit versions
* `rbx`:
Rubinius
* `jruby`:
JRuby
* `truffleruby`:
TruffleRuby
- * `mswin`:
- Windows
-
-You can restrict further by platform and version for all platforms *except* for
-`rbx`, `jruby`, `truffleruby` and `mswin`.
-To specify a version in addition to a platform, append the version number without
-the delimiter to the platform. For example, to specify that a gem should only be
-used on platforms with Ruby 2.3, use:
+On platforms `ruby`, `mri`, `mswin`, `mswin64`, and `windows`, you may
+additionally specify a version by appending the major and minor version numbers
+without a delimiter. For example, to specify that a gem should only be used on
+platform `ruby` version 3.1, use:
- ruby_23
+ ruby_31
-The full list of platforms and supported versions includes:
-
- * `ruby`:
- 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6
- * `mri`:
- 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6
- * `mingw`:
- 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6
- * `x64_mingw`:
- 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6
-
-As with groups, you can specify one or more platforms:
+As with groups (above), you may specify one or more platforms:
gem "weakling", platforms: :jruby
- gem "ruby-debug", platforms: :mri_18
- gem "nokogiri", platforms: [:mri_18, :jruby]
+ gem "ruby-debug", platforms: :mri_31
+ gem "nokogiri", platforms: [:windows_31, :jruby]
All operations involving groups ([`bundle install`](bundle-install.1.html), `Bundler.setup`,
`Bundler.require`) behave exactly the same as if any groups not
@@ -387,7 +384,7 @@ same, you can omit one.
Are both equivalent to
- gem "rails", git: "git://github.com/rails/rails.git"
+ gem "rails", git: "https://github.com/rails/rails.git"
Since the `github` method is a specialization of `git_source`, it accepts a `:branch` named argument.
@@ -522,7 +519,7 @@ paths.
The `gemspec` method supports optional `:path`, `:glob`, `:name`, and `:development_group`
options, which control where bundler looks for the `.gemspec`, the glob it uses to look
-for the gemspec (defaults to: "{,*,*/*}.gemspec"), what named `.gemspec` it uses
+for the gemspec (defaults to: `{,*,*/*}.gemspec`), what named `.gemspec` it uses
(if more than one is present), and which group development dependencies are included in.
When a `gemspec` dependency encounters version conflicts during resolution, the
diff --git a/lib/bundler/man/index.txt b/lib/bundler/man/index.txt
index ef2956b2f9..24f7633e66 100644
--- a/lib/bundler/man/index.txt
+++ b/lib/bundler/man/index.txt
@@ -6,9 +6,11 @@ bundle-cache(1) bundle-cache.1
bundle-check(1) bundle-check.1
bundle-clean(1) bundle-clean.1
bundle-config(1) bundle-config.1
+bundle-console(1) bundle-console.1
bundle-doctor(1) bundle-doctor.1
bundle-exec(1) bundle-exec.1
bundle-gem(1) bundle-gem.1
+bundle-help(1) bundle-help.1
bundle-info(1) bundle-info.1
bundle-init(1) bundle-init.1
bundle-inject(1) bundle-inject.1
@@ -18,8 +20,10 @@ bundle-lock(1) bundle-lock.1
bundle-open(1) bundle-open.1
bundle-outdated(1) bundle-outdated.1
bundle-platform(1) bundle-platform.1
+bundle-plugin(1) bundle-plugin.1
bundle-pristine(1) bundle-pristine.1
bundle-remove(1) bundle-remove.1
bundle-show(1) bundle-show.1
bundle-update(1) bundle-update.1
+bundle-version(1) bundle-version.1
bundle-viz(1) bundle-viz.1
diff --git a/lib/bundler/match_metadata.rb b/lib/bundler/match_metadata.rb
new file mode 100644
index 0000000000..499036ca93
--- /dev/null
+++ b/lib/bundler/match_metadata.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Bundler
+ module MatchMetadata
+ def matches_current_ruby?
+ @required_ruby_version.satisfied_by?(Gem.ruby_version)
+ end
+
+ def matches_current_rubygems?
+ @required_rubygems_version.satisfied_by?(Gem.rubygems_version)
+ end
+ end
+end
diff --git a/lib/bundler/match_remote_metadata.rb b/lib/bundler/match_remote_metadata.rb
new file mode 100644
index 0000000000..5e46d52441
--- /dev/null
+++ b/lib/bundler/match_remote_metadata.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Bundler
+ module FetchMetadata
+ # A fallback is included because the original version of the specification
+ # API didn't include that field, so some marshalled specs in the index have it
+ # set to +nil+.
+ def matches_current_ruby?
+ @required_ruby_version ||= _remote_specification.required_ruby_version || Gem::Requirement.default
+
+ super
+ end
+
+ def matches_current_rubygems?
+ # A fallback is included because the original version of the specification
+ # API didn't include that field, so some marshalled specs in the index have it
+ # set to +nil+.
+ @required_rubygems_version ||= _remote_specification.required_rubygems_version || Gem::Requirement.default
+
+ super
+ end
+ end
+
+ module MatchRemoteMetadata
+ include MatchMetadata
+
+ prepend FetchMetadata
+ end
+end
diff --git a/lib/bundler/mirror.rb b/lib/bundler/mirror.rb
index a63b45b47d..9d437a0951 100644
--- a/lib/bundler/mirror.rb
+++ b/lib/bundler/mirror.rb
@@ -148,13 +148,11 @@ module Bundler
class TCPSocketProbe
def replies?(mirror)
MirrorSockets.new(mirror).any? do |socket, address, timeout|
- begin
- socket.connect_nonblock(address)
- rescue Errno::EINPROGRESS
- wait_for_writtable_socket(socket, address, timeout)
- rescue RuntimeError # Connection failed somehow, again
- false
- end
+ socket.connect_nonblock(address)
+ rescue Errno::EINPROGRESS
+ wait_for_writtable_socket(socket, address, timeout)
+ rescue RuntimeError # Connection failed somehow, again
+ false
end
end
diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb
index 158c69e1a1..f3caff8963 100644
--- a/lib/bundler/plugin.rb
+++ b/lib/bundler/plugin.rb
@@ -15,7 +15,7 @@ module Bundler
class UnknownSourceError < PluginError; end
class PluginInstallError < PluginError; end
- PLUGIN_FILE_NAME = "plugins.rb".freeze
+ PLUGIN_FILE_NAME = "plugins.rb"
module_function
@@ -36,6 +36,8 @@ module Bundler
# @param [Hash] options various parameters as described in description.
# Refer to cli/plugin for available options
def install(names, options)
+ raise InvalidOption, "You cannot specify `--branch` and `--ref` at the same time." if options["branch"] && options["ref"]
+
specs = Installer.new.install(names, options)
save_plugins names, specs
diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb
index 29d33be718..a2d5eaa38a 100644
--- a/lib/bundler/plugin/index.rb
+++ b/lib/bundler/plugin/index.rb
@@ -146,7 +146,7 @@ module Bundler
# @param [Boolean] is the index file global index
def load_index(index_file, global = false)
SharedHelpers.filesystem_access(index_file, :read) do |index_f|
- valid_file = index_f && index_f.exist? && !index_f.size.zero?
+ valid_file = index_f&.exist? && !index_f.size.zero?
break unless valid_file
data = index_f.read
@@ -167,11 +167,11 @@ module Bundler
# to be only String key value pairs)
def save_index
index = {
- "commands" => @commands,
- "hooks" => @hooks,
- "load_paths" => @load_paths,
+ "commands" => @commands,
+ "hooks" => @hooks,
+ "load_paths" => @load_paths,
"plugin_paths" => @plugin_paths,
- "sources" => @sources,
+ "sources" => @sources,
}
require_relative "../yaml_serializer"
diff --git a/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb
index 81ecafa470..c9ff12ce4b 100644
--- a/lib/bundler/plugin/installer.rb
+++ b/lib/bundler/plugin/installer.rb
@@ -83,8 +83,11 @@ module Bundler
Bundler.configure_gem_home_and_path(Plugin.root)
- definition = Definition.new(nil, deps, source_list, true)
- install_definition(definition)
+ Bundler.settings.temporary(:deployment => false, :frozen => false) do
+ definition = Definition.new(nil, deps, source_list, true)
+
+ install_definition(definition)
+ end
end
# Installs the plugins and deps from the provided specs and returns map of
diff --git a/lib/bundler/plugin/installer/rubygems.rb b/lib/bundler/plugin/installer/rubygems.rb
index 7277234d9a..cb5db9c30e 100644
--- a/lib/bundler/plugin/installer/rubygems.rb
+++ b/lib/bundler/plugin/installer/rubygems.rb
@@ -6,10 +6,6 @@ module Bundler
class Rubygems < Bundler::Source::Rubygems
private
- def requires_sudo?
- false # Will change on implementation of project level plugins
- end
-
def rubygems_dir
Plugin.root
end
diff --git a/lib/bundler/remote_specification.rb b/lib/bundler/remote_specification.rb
index 4e966b820a..f626a3218e 100644
--- a/lib/bundler/remote_specification.rb
+++ b/lib/bundler/remote_specification.rb
@@ -6,6 +6,7 @@ module Bundler
# be seeded with what we're given from the source's abbreviated index - the
# full specification will only be fetched when necessary.
class RemoteSpecification
+ include MatchRemoteMetadata
include MatchPlatform
include Comparable
@@ -16,7 +17,8 @@ module Bundler
def initialize(name, version, platform, spec_fetcher)
@name = name
@version = Gem::Version.create version
- @platform = platform
+ @original_platform = platform || Gem::Platform::RUBY
+ @platform = Gem::Platform.new(platform)
@spec_fetcher = spec_fetcher
@dependencies = nil
end
@@ -27,18 +29,11 @@ module Bundler
@platform = _remote_specification.platform
end
- # A fallback is included because the original version of the specification
- # API didn't include that field, so some marshalled specs in the index have it
- # set to +nil+.
- def required_rubygems_version
- @required_rubygems_version ||= _remote_specification.required_rubygems_version || Gem::Requirement.default
- end
-
def full_name
- if platform == Gem::Platform::RUBY || platform.nil?
+ @full_name ||= if @platform == Gem::Platform::RUBY
"#{@name}-#{@version}"
else
- "#{@name}-#{@version}-#{platform}"
+ "#{@name}-#{@version}-#{@platform}"
end
end
@@ -105,9 +100,9 @@ module Bundler
end
def _remote_specification
- @_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, @platform])
+ @_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, @original_platform])
@_remote_specification || raise(GemspecError, "Gemspec data for #{full_name} was" \
- " missing from the server! Try installing with `--full-index` as a workaround.")
+ " missing from the server!")
end
def method_missing(method, *args, &blk)
diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb
index 972a4c254d..2ad35bc931 100644
--- a/lib/bundler/resolver.rb
+++ b/lib/bundler/resolver.rb
@@ -1,420 +1,427 @@
# frozen_string_literal: true
module Bundler
+ #
+ # This class implements the interface needed by PubGrub for resolution. It is
+ # equivalent to the `PubGrub::BasicPackageSource` class provided by PubGrub by
+ # default and used by the most simple PubGrub consumers.
+ #
class Resolver
- require_relative "vendored_molinillo"
- require_relative "resolver/spec_group"
+ require_relative "vendored_pub_grub"
+ require_relative "resolver/base"
+ require_relative "resolver/candidate"
+ require_relative "resolver/incompatibility"
+ require_relative "resolver/root"
include GemHelpers
- # Figures out the best possible configuration of gems that satisfies
- # the list of passed dependencies and any child dependencies without
- # causing any gem activation errors.
- #
- # ==== Parameters
- # *dependencies<Gem::Dependency>:: The list of dependencies to resolve
- #
- # ==== Returns
- # <GemBundle>,nil:: If the list of dependencies can be resolved, a
- # collection of gemspecs is returned. Otherwise, nil is returned.
- def self.resolve(requirements, source_requirements = {}, base = [], gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [], platforms = nil)
- base = SpecSet.new(base) unless base.is_a?(SpecSet)
- metadata_requirements, regular_requirements = requirements.partition {|dep| dep.name.end_with?("\0") }
- resolver = new(source_requirements, base, gem_version_promoter, additional_base_requirements, platforms, metadata_requirements)
- result = resolver.start(requirements)
- SpecSet.new(SpecSet.new(result).for(regular_requirements))
- end
-
- def initialize(source_requirements, base, gem_version_promoter, additional_base_requirements, platforms, metadata_requirements)
- @source_requirements = source_requirements
- @metadata_requirements = metadata_requirements
+ def initialize(base, gem_version_promoter)
+ @source_requirements = base.source_requirements
@base = base
- @resolver = Molinillo::Resolver.new(self, self)
- @search_for = {}
- @base_dg = Molinillo::DependencyGraph.new
- @base.each do |ls|
- dep = Dependency.new(ls.name, ls.version)
- @base_dg.add_vertex(ls.name, DepProxy.get_proxy(dep, ls.platform), true)
- end
- additional_base_requirements.each {|d| @base_dg.add_vertex(d.name, d) }
- @platforms = platforms.reject {|p| p != Gem::Platform::RUBY && (platforms - [p]).any? {|pl| generic(pl) == p } }
- @resolving_only_for_ruby = platforms == [Gem::Platform::RUBY]
@gem_version_promoter = gem_version_promoter
- @use_gvp = Bundler.feature_flag.use_gem_version_promoter_for_major_updates? || !@gem_version_promoter.major?
end
- def start(requirements)
- @gem_version_promoter.prerelease_specified = @prerelease_specified = {}
- requirements.each {|dep| @prerelease_specified[dep.name] ||= dep.prerelease? }
-
- verify_gemfile_dependencies_are_found!(requirements)
- dg = @resolver.resolve(requirements, @base_dg)
- dg.
- map(&:payload).
- reject {|sg| sg.name.end_with?("\0") }.
- map(&:to_specs).
- flatten
- rescue Molinillo::VersionConflict => e
- message = version_conflict_message(e)
- raise VersionConflict.new(e.conflicts.keys.uniq, message)
- rescue Molinillo::CircularDependencyError => e
- names = e.dependencies.sort_by(&:name).map {|d| "gem '#{d.name}'" }
- raise CyclicDependencyError, "Your bundle requires gems that depend" \
- " on each other, creating an infinite loop. Please remove" \
- " #{names.count > 1 ? "either " : ""}#{names.join(" or ")}" \
- " and try again."
- end
+ def start
+ @requirements = @base.requirements
+ @packages = @base.packages
- include Molinillo::UI
-
- # Conveys debug information to the user.
- #
- # @param [Integer] depth the current depth of the resolution process.
- # @return [void]
- def debug(depth = 0)
- return unless debug?
- debug_info = yield
- debug_info = debug_info.inspect unless debug_info.is_a?(String)
- puts debug_info.split("\n").map {|s| depth == 0 ? "BUNDLER: #{s}" : "BUNDLER(#{depth}): #{s}" }
- end
+ root, logger = setup_solver
- def debug?
- return @debug_mode if defined?(@debug_mode)
- @debug_mode =
- ENV["BUNDLER_DEBUG_RESOLVER"] ||
- ENV["BUNDLER_DEBUG_RESOLVER_TREE"] ||
- ENV["DEBUG_RESOLVER"] ||
- ENV["DEBUG_RESOLVER_TREE"] ||
- false
- end
+ Bundler.ui.info "Resolving dependencies...", true
- def before_resolution
- Bundler.ui.info "Resolving dependencies...", debug?
+ solve_versions(:root => root, :logger => logger)
end
- def after_resolution
- Bundler.ui.info ""
- end
+ def setup_solver
+ root = Resolver::Root.new(name_for_explicit_dependency_source)
+ root_version = Resolver::Candidate.new(0)
- def indicate_progress
- Bundler.ui.info ".", false unless debug?
- end
+ @all_specs = Hash.new do |specs, name|
+ specs[name] = source_for(name).specs.search(name).reject do |s|
+ s.dependencies.any? {|d| d.name == name && !d.requirement.satisfied_by?(s.version) } # ignore versions that depend on themselves incorrectly
+ end.sort_by {|s| [s.version, s.platform.to_s] }
+ end
+
+ @sorted_versions = Hash.new do |candidates, package|
+ candidates[package] = if package.root?
+ [root_version]
+ else
+ all_versions_for(package).sort
+ end
+ end
- include Molinillo::SpecificationProvider
+ root_dependencies = prepare_dependencies(@requirements, @packages)
- def dependencies_for(specification)
- specification.dependencies_for_activated_platforms
+ @cached_dependencies = Hash.new do |dependencies, package|
+ dependencies[package] = if package.root?
+ { root_version => root_dependencies }
+ else
+ Hash.new do |versions, version|
+ versions[version] = to_dependency_hash(version.dependencies.reject {|d| d.name == package.name }, @packages)
+ end
+ end
+ end
+
+ logger = Bundler::UI::Shell.new
+ logger.level = debug? ? "debug" : "warn"
+
+ [root, logger]
end
- def search_for(dependency_proxy)
- platform = dependency_proxy.__platform
- dependency = dependency_proxy.dep
- name = dependency.name
- @search_for[dependency_proxy] ||= begin
- results = results_for(dependency, @base[name])
+ def solve_versions(root:, logger:)
+ solver = PubGrub::VersionSolver.new(:source => self, :root => root, :logger => logger)
+ result = solver.solve
+ result.map {|package, version| version.to_specs(package) }.flatten.uniq
+ rescue PubGrub::SolveFailure => e
+ incompatibility = e.incompatibility
+
+ names_to_unlock, names_to_allow_prereleases_for, extended_explanation = find_names_to_relax(incompatibility)
+
+ names_to_relax = names_to_unlock + names_to_allow_prereleases_for
+
+ if names_to_relax.any?
+ if names_to_unlock.any?
+ Bundler.ui.debug "Found conflicts with locked dependencies. Will retry with #{names_to_unlock.join(", ")} unlocked...", true
- if vertex = @base_dg.vertex_named(name)
- locked_requirement = vertex.payload.requirement
+ @base.unlock_names(names_to_unlock)
end
- if !@prerelease_specified[name] && (!@use_gvp || locked_requirement.nil?)
- # Move prereleases to the beginning of the list, so they're considered
- # last during resolution.
- pre, results = results.partition {|spec| spec.version.prerelease? }
- results = pre + results
+ if names_to_allow_prereleases_for.any?
+ Bundler.ui.debug "Found conflicts with dependencies with prereleases. Will retrying considering prereleases for #{names_to_allow_prereleases_for.join(", ")}...", true
+
+ @base.include_prereleases(names_to_allow_prereleases_for)
end
- spec_groups = if results.any?
- nested = []
- results.each do |spec|
- version, specs = nested.last
- if version == spec.version
- specs << spec
- else
- nested << [spec.version, [spec]]
- end
- end
- nested.reduce([]) do |groups, (version, specs)|
- next groups if locked_requirement && !locked_requirement.satisfied_by?(version)
- next groups unless specs.any? {|spec| spec.match_platform(platform) }
+ root, logger = setup_solver
- specs_by_platform = Hash.new do |current_specs, current_platform|
- current_specs[current_platform] = select_best_platform_match(specs, current_platform)
- end
+ Bundler.ui.debug "Retrying resolution...", true
+ retry
+ end
- spec_group_ruby = SpecGroup.create_for(specs_by_platform, [Gem::Platform::RUBY], Gem::Platform::RUBY)
- if spec_group_ruby
- spec_group_ruby.force_ruby_platform = dependency.force_ruby_platform
- groups << spec_group_ruby
- end
+ explanation = e.message
- next groups if @resolving_only_for_ruby || dependency.force_ruby_platform
+ if extended_explanation
+ explanation << "\n\n"
+ explanation << extended_explanation
+ end
- spec_group = SpecGroup.create_for(specs_by_platform, @platforms, platform)
- groups << spec_group
+ raise SolveFailure.new(explanation)
+ end
- groups
+ def find_names_to_relax(incompatibility)
+ names_to_unlock = []
+ names_to_allow_prereleases_for = []
+ extended_explanation = nil
+
+ while incompatibility.conflict?
+ cause = incompatibility.cause
+ incompatibility = cause.incompatibility
+
+ incompatibility.terms.each do |term|
+ package = term.package
+ name = package.name
+
+ if base_requirements[name]
+ names_to_unlock << name
+ elsif package.ignores_prereleases?
+ names_to_allow_prereleases_for << name
end
- else
- []
- end
- # GVP handles major itself, but it's still a bit risky to trust it with it
- # until we get it settled with new behavior. For 2.x it can take over all cases.
- if !@use_gvp
- spec_groups
- else
- @gem_version_promoter.sort_versions(dependency, spec_groups)
+
+ no_versions_incompat = [cause.incompatibility, cause.satisfier].find {|incompat| incompat.cause.is_a?(PubGrub::Incompatibility::NoVersions) }
+ next unless no_versions_incompat
+
+ extended_explanation = no_versions_incompat.extended_explanation
end
end
- end
- def index_for(dependency)
- source_for(dependency.name).specs
+ [names_to_unlock.uniq, names_to_allow_prereleases_for.uniq, extended_explanation]
end
- def source_for(name)
- @source_requirements[name] || @source_requirements[:default]
- end
+ def parse_dependency(package, dependency)
+ range = if repository_for(package).is_a?(Source::Gemspec)
+ PubGrub::VersionRange.any
+ else
+ requirement_to_range(dependency)
+ end
- def results_for(dependency, base)
- index_for(dependency).search(dependency, base)
+ PubGrub::VersionConstraint.new(package, :range => range)
end
- def name_for(dependency)
- dependency.name
- end
+ def versions_for(package, range=VersionRange.any)
+ versions = range.select_versions(@sorted_versions[package])
- def name_for_explicit_dependency_source
- Bundler.default_gemfile.basename.to_s
- rescue StandardError
- "Gemfile"
+ sort_versions(package, versions)
end
- def name_for_locking_dependency_source
- Bundler.default_lockfile.basename.to_s
- rescue StandardError
- "Gemfile.lock"
- end
+ def no_versions_incompatibility_for(package, unsatisfied_term)
+ cause = PubGrub::Incompatibility::NoVersions.new(unsatisfied_term)
+ name = package.name
+ constraint = unsatisfied_term.constraint
+ constraint_string = constraint.constraint_string
+ requirements = constraint_string.split(" OR ").map {|req| Gem::Requirement.new(req.split(",")) }
- def requirement_satisfied_by?(requirement, activated, spec)
- requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec)
- end
+ if name == "bundler" && bundler_pinned_to_current_version?
+ custom_explanation = "the current Bundler version (#{Bundler::VERSION}) does not satisfy #{constraint}"
+ extended_explanation = bundler_not_found_message(requirements)
+ else
+ specs_matching_other_platforms = filter_matching_specs(@all_specs[name], requirements)
- def dependencies_equal?(dependencies, other_dependencies)
- dependencies.map(&:dep) == other_dependencies.map(&:dep)
- end
+ platforms_explanation = specs_matching_other_platforms.any? ? " for any resolution platforms (#{package.platforms.join(", ")})" : ""
+ custom_explanation = "#{constraint} could not be found in #{repository_for(package)}#{platforms_explanation}"
- def sort_dependencies(dependencies, activated, conflicts)
- dependencies.sort_by do |dependency|
- name = name_for(dependency)
- vertex = activated.vertex_named(name)
- [
- @base_dg.vertex_named(name) ? 0 : 1,
- vertex.payload ? 0 : 1,
- vertex.root? ? 0 : 1,
- amount_constrained(dependency),
- conflicts[name] ? 0 : 1,
- vertex.payload ? 0 : search_for(dependency).count,
- self.class.platform_sort_key(dependency.__platform),
- ]
+ label = "#{name} (#{constraint_string})"
+ extended_explanation = other_specs_matching_message(specs_matching_other_platforms, label) if specs_matching_other_platforms.any?
end
+
+ Incompatibility.new([unsatisfied_term], :cause => cause, :custom_explanation => custom_explanation, :extended_explanation => extended_explanation)
end
- def self.platform_sort_key(platform)
- # Prefer specific platform to not specific platform
- return ["99-LAST", "", "", ""] if Gem::Platform::RUBY == platform
- ["00", *platform.to_a.map {|part| part || "" }]
+ def debug?
+ ENV["BUNDLER_DEBUG_RESOLVER"] ||
+ ENV["BUNDLER_DEBUG_RESOLVER_TREE"] ||
+ ENV["DEBUG_RESOLVER"] ||
+ ENV["DEBUG_RESOLVER_TREE"] ||
+ false
end
- private
+ def incompatibilities_for(package, version)
+ package_deps = @cached_dependencies[package]
+ sorted_versions = @sorted_versions[package]
+ package_deps[version].map do |dep_package, dep_constraint|
+ low = high = sorted_versions.index(version)
- # returns an integer \in (-\infty, 0]
- # a number closer to 0 means the dependency is less constraining
- #
- # dependencies w/ 0 or 1 possibilities (ignoring version requirements)
- # are given very negative values, so they _always_ sort first,
- # before dependencies that are unconstrained
- def amount_constrained(dependency)
- @amount_constrained ||= {}
- @amount_constrained[dependency.name] ||= if (base = @base[dependency.name]) && !base.empty?
- dependency.requirement.satisfied_by?(base.first.version) ? 0 : 1
- else
- all = index_for(dependency).search(dependency.name).size
+ # find version low such that all >= low share the same dep
+ while low > 0 && package_deps[sorted_versions[low - 1]][dep_package] == dep_constraint
+ low -= 1
+ end
+ low =
+ if low == 0
+ nil
+ else
+ sorted_versions[low]
+ end
- if all <= 1
- all - 1_000_000
- else
- search = search_for(dependency)
- search = @prerelease_specified[dependency.name] ? search.count : search.count {|s| !s.version.prerelease? }
- search - all
+ # find version high such that all < high share the same dep
+ while high < sorted_versions.length && package_deps[sorted_versions[high]][dep_package] == dep_constraint
+ high += 1
+ end
+ high =
+ if high == sorted_versions.length
+ nil
+ else
+ sorted_versions[high]
+ end
+
+ range = PubGrub::VersionRange.new(:min => low, :max => high, :include_min => true)
+
+ self_constraint = PubGrub::VersionConstraint.new(package, :range => range)
+
+ dep_term = PubGrub::Term.new(dep_constraint, false)
+ self_term = PubGrub::Term.new(self_constraint, true)
+
+ custom_explanation = if dep_package.meta? && package.root?
+ "current #{dep_package} version is #{dep_constraint.constraint_string}"
end
+
+ PubGrub::Incompatibility.new([self_term, dep_term], :cause => :dependency, :custom_explanation => custom_explanation)
end
end
- def verify_gemfile_dependencies_are_found!(requirements)
- requirements.map! do |requirement|
- name = requirement.name
- next requirement if name == "bundler"
- next requirement unless search_for(requirement).empty?
- next unless requirement.current_platform?
-
- if (base = @base[name]) && !base.empty?
- version = base.first.version
- message = "You have requested:\n" \
- " #{name} #{requirement.requirement}\n\n" \
- "The bundle currently has #{name} locked at #{version}.\n" \
- "Try running `bundle update #{name}`\n\n" \
- "If you are updating multiple gems in your Gemfile at once,\n" \
- "try passing them all to `bundle update`"
- else
- message = gem_not_found_message(name, requirement, source_for(name))
- end
- raise GemNotFound, message
- end.compact!
+ def all_versions_for(package)
+ name = package.name
+ results = (@base[name] + filter_prereleases(@all_specs[name], package)).uniq {|spec| [spec.version.hash, spec.platform] }
+
+ if name == "bundler" && !bundler_pinned_to_current_version?
+ bundler_spec = Gem.loaded_specs["bundler"]
+ results << bundler_spec if bundler_spec
+ end
+
+ locked_requirement = base_requirements[name]
+ results = filter_matching_specs(results, locked_requirement) if locked_requirement
+
+ versions = results.group_by(&:version).reduce([]) do |groups, (version, specs)|
+ platform_specs = package.platforms.flat_map {|platform| select_best_platform_match(specs, platform) }
+ next groups if platform_specs.empty?
+
+ ruby_specs = select_best_platform_match(specs, Gem::Platform::RUBY)
+ groups << Resolver::Candidate.new(version, :specs => ruby_specs) if ruby_specs.any?
+
+ next groups if platform_specs == ruby_specs || package.force_ruby_platform?
+
+ groups << Resolver::Candidate.new(version, :specs => platform_specs)
+
+ groups
+ end
+
+ sort_versions(package, versions)
+ end
+
+ def source_for(name)
+ @source_requirements[name] || @source_requirements[:default]
+ end
+
+ def default_bundler_source
+ @source_requirements[:default_bundler]
+ end
+
+ def bundler_pinned_to_current_version?
+ !default_bundler_source.nil?
+ end
+
+ def name_for_explicit_dependency_source
+ Bundler.default_gemfile.basename.to_s
+ rescue StandardError
+ "Gemfile"
end
- def gem_not_found_message(name, requirement, source, extra_message = "")
- specs = source.specs.search(name)
+ def raise_not_found!(package)
+ name = package.name
+ source = source_for(name)
+ specs = @all_specs[name]
matching_part = name
- requirement_label = SharedHelpers.pretty_dependency(requirement)
+ requirement_label = SharedHelpers.pretty_dependency(package.dependency)
cache_message = begin
" or in gems cached in #{Bundler.settings.app_cache_path}" if Bundler.app_cache.exist?
rescue GemfileNotFound
nil
end
- specs_matching_requirement = specs.select {| spec| requirement.matches_spec?(spec) }
+ specs_matching_requirement = filter_matching_specs(specs, package.dependency.requirement)
if specs_matching_requirement.any?
specs = specs_matching_requirement
matching_part = requirement_label
- requirement_label = "#{requirement_label}' with platform '#{requirement.__platform}"
+ platforms = package.platforms
+ platform_label = platforms.size == 1 ? "platform '#{platforms.first}" : "platforms '#{platforms.join("', '")}"
+ requirement_label = "#{requirement_label}' with #{platform_label}"
end
- message = String.new("Could not find gem '#{requirement_label}'#{extra_message} in #{source}#{cache_message}.\n")
+ message = String.new("Could not find gem '#{requirement_label}' in #{source}#{cache_message}.\n")
if specs.any?
- message << "\nThe source contains the following gems matching '#{matching_part}':\n"
- message << specs.map {|s| " * #{s.full_name}" }.join("\n")
+ message << "\n#{other_specs_matching_message(specs, matching_part)}"
end
- message
+ raise GemNotFound, message
end
- def version_conflict_message(e)
- # only show essential conflicts, if possible
- conflicts = e.conflicts.dup
+ private
+
+ def filter_matching_specs(specs, requirements)
+ Array(requirements).flat_map do |requirement|
+ specs.select {| spec| requirement_satisfied_by?(requirement, spec) }
+ end
+ end
- if conflicts["bundler"]
- conflicts.replace("bundler" => conflicts["bundler"])
+ def filter_prereleases(specs, package)
+ return specs unless package.ignores_prereleases? && specs.size > 1
+
+ specs.reject {|s| s.version.prerelease? }
+ end
+
+ def requirement_satisfied_by?(requirement, spec)
+ requirement.satisfied_by?(spec.version) || spec.source.is_a?(Source::Gemspec)
+ end
+
+ def sort_versions(package, versions)
+ if versions.size > 1
+ @gem_version_promoter.sort_versions(package, versions).reverse
else
- conflicts.delete_if do |_name, conflict|
- deps = conflict.requirement_trees.map(&:last).flatten(1)
- !Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement)))
- end
+ versions
end
+ end
- e = Molinillo::VersionConflict.new(conflicts, e.specification_provider) unless conflicts.empty?
+ def repository_for(package)
+ source_for(package.name)
+ end
- e.message_with_trees(
- :full_message_for_conflict => lambda do |name, conflict|
- o = if name.end_with?("\0")
- String.new("Bundler found conflicting requirements for the #{name} version:")
- else
- String.new("Bundler could not find compatible versions for gem \"#{name}\":")
- end
- o << %(\n)
- if conflict.locked_requirement
- o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
- o << %( #{SharedHelpers.pretty_dependency(conflict.locked_requirement)}\n)
- o << %(\n)
- end
- o << %( In #{name_for_explicit_dependency_source}:\n)
- trees = conflict.requirement_trees
+ def base_requirements
+ @base.base_requirements
+ end
- # called first, because we want to reduce the amount of work required to find maximal empty sets
- trees = trees.uniq {|t| t.flatten.map {|dep| [dep.name, dep.requirement] } }
+ def prepare_dependencies(requirements, packages)
+ to_dependency_hash(requirements, packages).map do |dep_package, dep_constraint|
+ name = dep_package.name
- # bail out if tree size is too big for Array#combination to make any sense
- if trees.size <= 15
- maximal = 1.upto(trees.size).map do |size|
- trees.map(&:last).flatten(1).combination(size).to_a
- end.flatten(1).select do |deps|
- Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement)))
- end.min_by(&:size)
+ next [dep_package, dep_constraint] if name == "bundler"
- trees.reject! {|t| !maximal.include?(t.last) } if maximal
+ versions = versions_for(dep_package, dep_constraint.range)
+ if versions.empty? && dep_package.ignores_prereleases?
+ @sorted_versions.delete(dep_package)
+ dep_package.consider_prereleases!
+ versions = versions_for(dep_package, dep_constraint.range)
+ end
+ next [dep_package, dep_constraint] unless versions.empty?
- trees.sort_by! {|t| t.reverse.map(&:name) }
- end
+ next unless dep_package.current_platform?
- o << trees.map do |tree|
- t = "".dup
- depth = 2
-
- base_tree = tree.first
- base_tree_name = base_tree.name
-
- if base_tree_name.end_with?("\0")
- t = nil
- else
- tree.each do |req|
- t << " " * depth << SharedHelpers.pretty_dependency(req)
- unless tree.last == req
- if spec = conflict.activated_by_name[req.name]
- t << %( was resolved to #{spec.version}, which)
- end
- t << %( depends on)
- end
- t << %(\n)
- depth += 1
- end
- end
- t
- end.compact.join("\n")
-
- if name == "bundler"
- o << %(\n Current Bundler version:\n bundler (#{Bundler::VERSION}))
-
- conflict_dependency = conflict.requirement
- conflict_requirement = conflict_dependency.requirement
- other_bundler_required = !conflict_requirement.satisfied_by?(Gem::Version.new(Bundler::VERSION))
-
- if other_bundler_required
- o << "\n\n"
-
- candidate_specs = source_for(:default_bundler).specs.search(conflict_dependency)
- if candidate_specs.any?
- target_version = candidate_specs.last.version
- new_command = [File.basename($PROGRAM_NAME), "_#{target_version}_", *ARGV].join(" ")
- o << "Your bundle requires a different version of Bundler than the one you're running.\n"
- o << "Install the necessary version with `gem install bundler:#{target_version}` and rerun bundler using `#{new_command}`\n"
- else
- o << "Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.\n"
- end
- end
- elsif name.end_with?("\0")
- o << %(\n Current #{name} version:\n #{SharedHelpers.pretty_dependency(@metadata_requirements.find {|req| req.name == name })}\n\n)
- elsif conflict.locked_requirement
- o << "\n"
- o << %(Deleting your #{name_for_locking_dependency_source} file and running `bundle install` will rebuild your snapshot from scratch, using only\n)
- o << %(the gems in your Gemfile, which may resolve the conflict.\n)
- elsif !conflict.existing
- o << "\n"
-
- relevant_source = conflict.requirement.source || source_for(name)
-
- extra_message = if conflict.requirement_trees.first.size > 1
- ", which is required by gem '#{SharedHelpers.pretty_dependency(conflict.requirement_trees.first[-2])}',"
- else
- ""
- end
-
- o << gem_not_found_message(name, conflict.requirement, relevant_source, extra_message)
- end
+ raise_not_found!(dep_package)
+ end.compact.to_h
+ end
- o
+ def other_specs_matching_message(specs, requirement)
+ message = String.new("The source contains the following gems matching '#{requirement}':\n")
+ message << specs.map {|s| " * #{s.full_name}" }.join("\n")
+ message
+ end
+
+ def requirement_to_range(requirement)
+ ranges = requirement.requirements.map do |(op, version)|
+ ver = Resolver::Candidate.new(version).generic!
+ platform_ver = Resolver::Candidate.new(version).platform_specific!
+
+ case op
+ when "~>"
+ name = "~> #{ver}"
+ bump = Resolver::Candidate.new(version.bump.to_s + ".A")
+ PubGrub::VersionRange.new(:name => name, :min => ver, :max => bump, :include_min => true)
+ when ">"
+ PubGrub::VersionRange.new(:min => platform_ver)
+ when ">="
+ PubGrub::VersionRange.new(:min => ver, :include_min => true)
+ when "<"
+ PubGrub::VersionRange.new(:max => ver)
+ when "<="
+ PubGrub::VersionRange.new(:max => platform_ver, :include_max => true)
+ when "="
+ PubGrub::VersionRange.new(:min => ver, :max => platform_ver, :include_min => true, :include_max => true)
+ when "!="
+ PubGrub::VersionRange.new(:min => ver, :max => platform_ver, :include_min => true, :include_max => true).invert
+ else
+ raise "bad version specifier: #{op}"
end
- )
+ end
+
+ ranges.inject(&:intersect)
+ end
+
+ def to_dependency_hash(dependencies, packages)
+ dependencies.inject({}) do |deps, dep|
+ package = packages[dep.name]
+
+ current_req = deps[package]
+ new_req = parse_dependency(package, dep.requirement)
+
+ deps[package] = if current_req
+ current_req.intersect(new_req)
+ else
+ new_req
+ end
+
+ deps
+ end
+ end
+
+ def bundler_not_found_message(conflict_dependencies)
+ candidate_specs = filter_matching_specs(default_bundler_source.specs.search("bundler"), conflict_dependencies)
+
+ if candidate_specs.any?
+ target_version = candidate_specs.last.version
+ new_command = [File.basename($PROGRAM_NAME), "_#{target_version}_", *ARGV].join(" ")
+ "Your bundle requires a different version of Bundler than the one you're running.\n" \
+ "Install the necessary version with `gem install bundler:#{target_version}` and rerun bundler using `#{new_command}`\n"
+ else
+ "Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.\n"
+ end
end
end
end
diff --git a/lib/bundler/resolver/base.rb b/lib/bundler/resolver/base.rb
new file mode 100644
index 0000000000..e5c3763c3f
--- /dev/null
+++ b/lib/bundler/resolver/base.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+require_relative "package"
+
+module Bundler
+ class Resolver
+ class Base
+ attr_reader :packages, :requirements, :source_requirements
+
+ def initialize(source_requirements, dependencies, base, platforms, options)
+ @source_requirements = source_requirements
+
+ @base = base
+
+ @packages = Hash.new do |hash, name|
+ hash[name] = Package.new(name, platforms, **options)
+ end
+
+ @requirements = dependencies.map do |dep|
+ dep_platforms = dep.gem_platforms(platforms)
+
+ # Dependencies scoped to external platforms are ignored
+ next if dep_platforms.empty?
+
+ name = dep.name
+
+ @packages[name] = Package.new(name, dep_platforms, **options.merge(:dependency => dep))
+
+ dep
+ end.compact
+ end
+
+ def [](name)
+ @base[name]
+ end
+
+ def delete(specs)
+ @base.delete(specs)
+ end
+
+ def get_package(name)
+ @packages[name]
+ end
+
+ def base_requirements
+ @base_requirements ||= build_base_requirements
+ end
+
+ def unlock_names(names)
+ indirect_pins = indirect_pins(names)
+
+ if indirect_pins.any?
+ loosen_names(indirect_pins)
+ else
+ pins = pins(names)
+
+ if pins.any?
+ loosen_names(pins)
+ else
+ unrestrict_names(names)
+ end
+ end
+ end
+
+ def include_prereleases(names)
+ names.each do |name|
+ get_package(name).consider_prereleases!
+ end
+ end
+
+ private
+
+ def indirect_pins(names)
+ names.select {|name| @base_requirements[name].exact? && @requirements.none? {|dep| dep.name == name } }
+ end
+
+ def pins(names)
+ names.select {|name| @base_requirements[name].exact? }
+ end
+
+ def loosen_names(names)
+ names.each do |name|
+ version = @base_requirements[name].requirements.first[1]
+
+ @base_requirements[name] = Gem::Requirement.new(">= #{version}")
+
+ @base.delete_by_name(name)
+ end
+ end
+
+ def unrestrict_names(names)
+ names.each do |name|
+ @base_requirements.delete(name)
+ end
+ end
+
+ def build_base_requirements
+ base_requirements = {}
+ @base.each do |ls|
+ req = Gem::Requirement.new(ls.version)
+ base_requirements[ls.name] = req
+ end
+ base_requirements
+ end
+ end
+ end
+end
diff --git a/lib/bundler/resolver/candidate.rb b/lib/bundler/resolver/candidate.rb
new file mode 100644
index 0000000000..e695ef08ee
--- /dev/null
+++ b/lib/bundler/resolver/candidate.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require_relative "spec_group"
+
+module Bundler
+ class Resolver
+ #
+ # This class is a PubGrub compatible "Version" class that takes Bundler
+ # resolution complexities into account.
+ #
+ # Each Resolver::Candidate has a underlying `Gem::Version` plus a set of
+ # platforms. For example, 1.1.0-x86_64-linux is a different resolution candidate
+ # from 1.1.0 (generic). This is because different platform variants of the
+ # same gem version can bring different dependencies, so they need to be
+ # considered separately.
+ #
+ # Some candidates may also keep some information explicitly about the
+ # package the refer to. These candidates are referred to as "canonical" and
+ # are used when materializing resolution results back into RubyGems
+ # specifications that can be installed, written to lock files, and so on.
+ #
+ class Candidate
+ include Comparable
+
+ attr_reader :version
+
+ def initialize(version, specs: [])
+ @spec_group = Resolver::SpecGroup.new(specs)
+ @version = Gem::Version.new(version)
+ @ruby_only = specs.map(&:platform).uniq == [Gem::Platform::RUBY]
+ end
+
+ def dependencies
+ @spec_group.dependencies
+ end
+
+ def to_specs(package)
+ return [] if package.meta?
+
+ @spec_group.to_specs(package.force_ruby_platform?)
+ end
+
+ def generic!
+ @ruby_only = true
+
+ self
+ end
+
+ def platform_specific!
+ @ruby_only = false
+
+ self
+ end
+
+ def prerelease?
+ @version.prerelease?
+ end
+
+ def segments
+ @version.segments
+ end
+
+ def sort_obj
+ [@version, @ruby_only ? -1 : 1]
+ end
+
+ def <=>(other)
+ return unless other.is_a?(self.class)
+
+ sort_obj <=> other.sort_obj
+ end
+
+ def ==(other)
+ return unless other.is_a?(self.class)
+
+ sort_obj == other.sort_obj
+ end
+
+ def eql?(other)
+ return unless other.is_a?(self.class)
+
+ sort_obj.eql?(other.sort_obj)
+ end
+
+ def hash
+ sort_obj.hash
+ end
+
+ def to_s
+ @version.to_s
+ end
+ end
+ end
+end
diff --git a/lib/bundler/resolver/incompatibility.rb b/lib/bundler/resolver/incompatibility.rb
new file mode 100644
index 0000000000..c61151fbeb
--- /dev/null
+++ b/lib/bundler/resolver/incompatibility.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Resolver
+ class Incompatibility < PubGrub::Incompatibility
+ attr_reader :extended_explanation
+
+ def initialize(terms, cause:, custom_explanation: nil, extended_explanation: nil)
+ @extended_explanation = extended_explanation
+
+ super(terms, :cause => cause, :custom_explanation => custom_explanation)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/resolver/package.rb b/lib/bundler/resolver/package.rb
new file mode 100644
index 0000000000..7499a75006
--- /dev/null
+++ b/lib/bundler/resolver/package.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Resolver
+ #
+ # Represents a gem being resolved, in a format PubGrub likes.
+ #
+ # The class holds the following information:
+ #
+ # * Platforms this gem will be resolved on.
+ # * The locked version of this gem resolution should favor (if any).
+ # * Whether the gem should be unlocked to its latest version.
+ # * The dependency explicit set in the Gemfile for this gem (if any).
+ #
+ class Package
+ attr_reader :name, :platforms, :dependency, :locked_version
+
+ def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, dependency: nil)
+ @name = name
+ @platforms = platforms
+ @locked_version = locked_specs[name].first&.version
+ @unlock = unlock
+ @dependency = dependency || Dependency.new(name, @locked_version)
+ @prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore
+ end
+
+ def to_s
+ @name.delete("\0")
+ end
+
+ def root?
+ false
+ end
+
+ def meta?
+ @name.end_with?("\0")
+ end
+
+ def ==(other)
+ self.class == other.class && @name == other.name
+ end
+
+ def hash
+ @name.hash
+ end
+
+ def unlock?
+ @unlock.empty? || @unlock.include?(name)
+ end
+
+ def ignores_prereleases?
+ @prerelease == :ignore
+ end
+
+ def prerelease_specified?
+ @prerelease == :consider_first
+ end
+
+ def consider_prereleases!
+ @prerelease = :consider_last
+ end
+
+ def force_ruby_platform?
+ @dependency.force_ruby_platform
+ end
+
+ def current_platform?
+ @dependency.current_platform?
+ end
+ end
+ end
+end
diff --git a/lib/bundler/resolver/root.rb b/lib/bundler/resolver/root.rb
new file mode 100644
index 0000000000..e5eb634fb8
--- /dev/null
+++ b/lib/bundler/resolver/root.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require_relative "package"
+
+module Bundler
+ class Resolver
+ #
+ # Represents the Gemfile from the resolver's perspective. It's the root
+ # package and Gemfile entries depend on it.
+ #
+ class Root < Package
+ def initialize(name)
+ @name = name
+ end
+
+ def meta?
+ true
+ end
+
+ def root?
+ true
+ end
+ end
+ end
+end
diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb
index 4de5b91aa6..b44c19a73f 100644
--- a/lib/bundler/resolver/spec_group.rb
+++ b/lib/bundler/resolver/spec_group.rb
@@ -3,108 +3,79 @@
module Bundler
class Resolver
class SpecGroup
- attr_accessor :name, :version, :source
- attr_accessor :activated_platforms, :force_ruby_platform
-
- def self.create_for(specs, all_platforms, specific_platform)
- specific_platform_specs = specs[specific_platform]
- return unless specific_platform_specs.any?
-
- platforms = all_platforms.select {|p| specs[p].any? }
-
- new(specific_platform_specs.first, specs, platforms)
+ def initialize(specs)
+ @specs = specs
end
- def initialize(exemplary_spec, specs, relevant_platforms)
- @exemplary_spec = exemplary_spec
- @name = exemplary_spec.name
- @version = exemplary_spec.version
- @source = exemplary_spec.source
-
- @activated_platforms = relevant_platforms
- @dependencies = Hash.new do |dependencies, platforms|
- dependencies[platforms] = dependencies_for(platforms)
- end
- @specs = specs
+ def empty?
+ @specs.empty?
end
- def to_specs
- activated_platforms.map do |p|
- specs = @specs[p]
- next unless specs.any?
-
- specs.map do |s|
- lazy_spec = LazySpecification.new(name, version, s.platform, source)
- lazy_spec.force_ruby_platform = force_ruby_platform
- lazy_spec.dependencies.replace s.dependencies
- lazy_spec
- end
- end.flatten.compact.uniq
+ def name
+ @name ||= exemplary_spec.name
end
- def to_s
- activated_platforms_string = sorted_activated_platforms.join(", ")
- "#{name} (#{version}) (#{activated_platforms_string})"
+ def version
+ @version ||= exemplary_spec.version
end
- def dependencies_for_activated_platforms
- @dependencies[activated_platforms]
+ def source
+ @source ||= exemplary_spec.source
end
- def ==(other)
- return unless other.is_a?(SpecGroup)
- name == other.name &&
- version == other.version &&
- sorted_activated_platforms == other.sorted_activated_platforms &&
- source == other.source
+ def to_specs(force_ruby_platform)
+ @specs.map do |s|
+ lazy_spec = LazySpecification.new(name, version, s.platform, source)
+ lazy_spec.force_ruby_platform = force_ruby_platform
+ lazy_spec.dependencies.replace s.dependencies
+ lazy_spec
+ end
end
- def eql?(other)
- return unless other.is_a?(SpecGroup)
- name.eql?(other.name) &&
- version.eql?(other.version) &&
- sorted_activated_platforms.eql?(other.sorted_activated_platforms) &&
- source.eql?(other.source)
+ def to_s
+ sorted_spec_names.join(", ")
end
- def hash
- name.hash ^ version.hash ^ sorted_activated_platforms.hash ^ source.hash
+ def dependencies
+ @dependencies ||= @specs.map do |spec|
+ __dependencies(spec) + metadata_dependencies(spec)
+ end.flatten.uniq
end
protected
- def sorted_activated_platforms
- activated_platforms.sort_by(&:to_s)
+ def sorted_spec_names
+ @sorted_spec_names ||= @specs.map(&:full_name).sort
end
private
- def dependencies_for(platforms)
- platforms.map do |platform|
- __dependencies(platform) + metadata_dependencies(platform)
- end.flatten
+ def exemplary_spec
+ @specs.first
end
- def __dependencies(platform)
+ def __dependencies(spec)
dependencies = []
- @specs[platform].first.dependencies.each do |dep|
+ spec.dependencies.each do |dep|
next if dep.type == :development
- dependencies << DepProxy.get_proxy(Dependency.new(dep.name, dep.requirement), platform)
+ dependencies << Dependency.new(dep.name, dep.requirement)
end
dependencies
end
- def metadata_dependencies(platform)
- spec = @specs[platform].first
+ def metadata_dependencies(spec)
return [] if spec.is_a?(LazySpecification)
- dependencies = []
- unless spec.required_ruby_version.none?
- dependencies << DepProxy.get_proxy(Dependency.new("Ruby\0", spec.required_ruby_version), platform)
- end
- unless spec.required_rubygems_version.none?
- dependencies << DepProxy.get_proxy(Dependency.new("RubyGems\0", spec.required_rubygems_version), platform)
- end
- dependencies
+
+ [
+ metadata_dependency("Ruby", spec.required_ruby_version),
+ metadata_dependency("RubyGems", spec.required_rubygems_version),
+ ].compact
+ end
+
+ def metadata_dependency(name, requirement)
+ return if requirement.nil? || requirement.none?
+
+ Dependency.new("#{name}\0", requirement)
end
end
end
diff --git a/lib/bundler/ruby_dsl.rb b/lib/bundler/ruby_dsl.rb
index f6ba220cd5..d054969e8d 100644
--- a/lib/bundler/ruby_dsl.rb
+++ b/lib/bundler/ruby_dsl.rb
@@ -5,11 +5,17 @@ module Bundler
def ruby(*ruby_version)
options = ruby_version.last.is_a?(Hash) ? ruby_version.pop : {}
ruby_version.flatten!
+
raise GemfileError, "Please define :engine_version" if options[:engine] && options[:engine_version].nil?
raise GemfileError, "Please define :engine" if options[:engine_version] && options[:engine].nil?
+ if options[:file]
+ raise GemfileError, "Cannot specify version when using the file option" if ruby_version.any?
+ ruby_version << Bundler.read_file(options[:file]).strip
+ end
+
if options[:engine] == "ruby" && options[:engine_version] &&
- ruby_version != Array(options[:engine_version])
+ ruby_version != Array(options[:engine_version])
raise GemfileEvalError, "ruby_version must match the :engine_version for MRI"
end
@ruby_version = RubyVersion.new(ruby_version, options[:patchlevel], options[:engine], options[:engine_version])
diff --git a/lib/bundler/ruby_version.rb b/lib/bundler/ruby_version.rb
index 3f51cf4528..b5396abb6e 100644
--- a/lib/bundler/ruby_version.rb
+++ b/lib/bundler/ruby_version.rb
@@ -28,16 +28,16 @@ module Bundler
end
@gem_version = Gem::Requirement.create(@versions.first).requirements.first.last
- @input_engine = engine && engine.to_s
- @engine = engine && engine.to_s || "ruby"
+ @input_engine = engine&.to_s
+ @engine = engine&.to_s || "ruby"
@engine_versions = (engine_version && Array(engine_version)) || @versions
@engine_gem_version = Gem::Requirement.create(@engine_versions.first).requirements.first.last
- @patchlevel = patchlevel
+ @patchlevel = patchlevel || (@gem_version.prerelease? ? "-1" : nil)
end
def to_s(versions = self.versions)
output = String.new("ruby #{versions_string(versions)}")
- output << "p#{patchlevel}" if patchlevel
+ output << "p#{patchlevel}" if patchlevel && patchlevel != "-1"
output << " (#{engine} #{versions_string(engine_versions)})" unless engine == "ruby"
output
@@ -46,7 +46,7 @@ module Bundler
# @private
PATTERN = /
ruby\s
- ([\d.]+) # ruby version
+ (\d+\.\d+\.\d+(?:\.\S+)?) # ruby version
(?:p(-?\d+))? # optional patchlevel
(?:\s\((\S+)\s(.+)\))? # optional engine info
/xo.freeze
@@ -103,11 +103,11 @@ module Bundler
def self.system
ruby_engine = RUBY_ENGINE.dup
- ruby_version = RUBY_VERSION.dup
- ruby_engine_version = RUBY_ENGINE_VERSION.dup
+ ruby_version = Gem.ruby_version.to_s
+ ruby_engine_version = RUBY_ENGINE == "ruby" ? ruby_version : RUBY_ENGINE_VERSION.dup
patchlevel = RUBY_PATCHLEVEL.to_s
- @ruby_version ||= RubyVersion.new(ruby_version, patchlevel, ruby_engine, ruby_engine_version)
+ @system ||= RubyVersion.new(ruby_version, patchlevel, ruby_engine, ruby_engine_version)
end
private
diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb
index d679d20c21..8981612706 100644
--- a/lib/bundler/rubygems_ext.rb
+++ b/lib/bundler/rubygems_ext.rb
@@ -15,10 +15,22 @@ require "rubygems/specification"
# `Gem::Source` from the redefined `Gem::Specification#source`.
require "rubygems/source"
+require_relative "match_metadata"
+require_relative "force_platform"
require_relative "match_platform"
+# Cherry-pick fixes to `Gem.ruby_version` to be useful for modern Bundler
+# versions and ignore patchlevels
+# (https://github.com/rubygems/rubygems/pull/5472,
+# https://github.com/rubygems/rubygems/pull/5486). May be removed once RubyGems
+# 3.3.12 support is dropped.
+unless Gem.ruby_version.to_s == RUBY_VERSION || RUBY_PATCHLEVEL == -1
+ Gem.instance_variable_set(:@ruby_version, Gem::Version.new(RUBY_VERSION))
+end
+
module Gem
class Specification
+ include ::Bundler::MatchMetadata
include ::Bundler::MatchPlatform
attr_accessor :remote, :location, :relative_loaded_from
@@ -54,7 +66,9 @@ module Gem
alias_method :rg_extension_dir, :extension_dir
def extension_dir
- @bundler_extension_dir ||= if source.respond_to?(:extension_dir_name)
+ # following instance variable is already used in original method
+ # and that is the reason to prefix it with bundler_ and add rubocop exception
+ @bundler_extension_dir ||= if source.respond_to?(:extension_dir_name) # rubocop:disable Naming/MemoizedInstanceVariableName
unique_extension_dir = [source.extension_dir_name, File.basename(full_gem_path)].uniq.join("-")
File.expand_path(File.join(extensions_dir, unique_extension_dir))
else
@@ -142,10 +156,18 @@ module Gem
end
class Dependency
+ include ::Bundler::ForcePlatform
+
attr_accessor :source, :groups
alias_method :eql?, :==
+ def force_ruby_platform
+ return @force_ruby_platform if defined?(@force_ruby_platform) && !@force_ruby_platform.nil?
+
+ @force_ruby_platform = default_force_ruby_platform
+ end
+
def encode_with(coder)
to_yaml_properties.each do |ivar|
coder[ivar.to_s.sub(/^@/, "")] = instance_variable_get(ivar)
@@ -183,9 +205,9 @@ module Gem
protected
def _requirements_sorted?
- return @_are_requirements_sorted if defined?(@_are_requirements_sorted)
+ return @_requirements_sorted if defined?(@_requirements_sorted)
strings = as_list
- @_are_requirements_sorted = strings == strings.sort
+ @_requirements_sorted = strings == strings.sort
end
def _with_sorted_requirements
@@ -222,9 +244,100 @@ module Gem
MINGW = Gem::Platform.new("x86-mingw32")
X64_MINGW = [Gem::Platform.new("x64-mingw32"),
Gem::Platform.new("x64-mingw-ucrt")].freeze
+ WINDOWS = [MSWIN, MSWIN64, MINGW, X64_MINGW].flatten.freeze
+ X64_LINUX = Gem::Platform.new("x86_64-linux")
+ X64_LINUX_MUSL = Gem::Platform.new("x86_64-linux-musl")
+
+ if X64_LINUX === X64_LINUX_MUSL
+ remove_method :===
+
+ def ===(other)
+ return nil unless Gem::Platform === other
+
+ # universal-mingw32 matches x64-mingw-ucrt
+ return true if (@cpu == "universal" || other.cpu == "universal") &&
+ @os.start_with?("mingw") && other.os.start_with?("mingw")
+
+ # cpu
+ ([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu ||
+ (@cpu == "arm" && other.cpu.start_with?("arm"))) &&
+
+ # os
+ @os == other.os &&
+
+ # version
+ (
+ (@os != "linux" && (@version.nil? || other.version.nil?)) ||
+ (@os == "linux" && (normalized_linux_version_ext == other.normalized_linux_version_ext || ["musl#{@version}", "musleabi#{@version}", "musleabihf#{@version}"].include?(other.version))) ||
+ @version == other.version
+ )
+ end
+
+ # This is a copy of RubyGems 3.3.23 or higher `normalized_linux_method`.
+ # Once only 3.3.23 is supported, we can use the method in RubyGems.
+ def normalized_linux_version_ext
+ return nil unless @version
+
+ without_gnu_nor_abi_modifiers = @version.sub(/\Agnu/, "").sub(/eabi(hf)?\Z/, "")
+ return nil if without_gnu_nor_abi_modifiers.empty?
+
+ without_gnu_nor_abi_modifiers
+ end
+ end
if RUBY_ENGINE == "truffleruby" && !defined?(REUSE_AS_BINARY_ON_TRUFFLERUBY)
- REUSE_AS_BINARY_ON_TRUFFLERUBY = %w[libv8 sorbet-static].freeze
+ REUSE_AS_BINARY_ON_TRUFFLERUBY = %w[libv8 libv8-node sorbet-static].freeze
+ end
+ end
+
+ Platform.singleton_class.module_eval do
+ unless Platform.singleton_methods.include?(:match_spec?)
+ def match_spec?(spec)
+ match_gem?(spec.platform, spec.name)
+ end
+
+ def match_gem?(platform, gem_name)
+ match_platforms?(platform, Gem.platforms)
+ end
+ end
+
+ match_platforms_defined = Gem::Platform.respond_to?(:match_platforms?, true)
+
+ if !match_platforms_defined || Gem::Platform.send(:match_platforms?, Gem::Platform::X64_LINUX_MUSL, [Gem::Platform::X64_LINUX])
+
+ private
+
+ remove_method :match_platforms? if match_platforms_defined
+
+ def match_platforms?(platform, platforms)
+ platforms.any? do |local_platform|
+ platform.nil? ||
+ local_platform == platform ||
+ (local_platform != Gem::Platform::RUBY && platform =~ local_platform)
+ end
+ end
+ end
+ end
+
+ # On universal Rubies, resolve the "universal" arch to the real CPU arch, without changing the extension directory.
+ class Specification
+ if /^universal\.(?<arch>.*?)-/ =~ (CROSS_COMPILING || RUBY_PLATFORM)
+ local_platform = Platform.local
+ if local_platform.cpu == "universal"
+ ORIGINAL_LOCAL_PLATFORM = local_platform.to_s.freeze
+
+ local_platform.cpu = if arch == "arm64e" # arm64e is only permitted for Apple system binaries
+ "arm64"
+ else
+ arch
+ end
+
+ def extensions_dir
+ Gem.default_ext_dir_for(base_dir) ||
+ File.join(base_dir, "extensions", ORIGINAL_LOCAL_PLATFORM,
+ Gem.extension_api_version)
+ end
+ end
end
end
@@ -236,11 +349,7 @@ module Gem
end
def glob_files_in_dir(glob, base_path)
- if RUBY_VERSION >= "2.5"
- Dir.glob(glob, :base => base_path).map! {|f| File.expand_path(f, base_path) }
- else
- Dir.glob(File.join(base_path.to_s.gsub(/[\[\]]/, '\\\\\\&'), glob)).map! {|f| File.expand_path(f) }
- end
+ Dir.glob(glob, :base => base_path).map! {|f| File.expand_path(f, base_path) }
end
end
end
diff --git a/lib/bundler/rubygems_gem_installer.rb b/lib/bundler/rubygems_gem_installer.rb
index 12cc809664..38035a00ac 100644
--- a/lib/bundler/rubygems_gem_installer.rb
+++ b/lib/bundler/rubygems_gem_installer.rb
@@ -72,10 +72,11 @@ module Bundler
return super
end
- extension_dir = Pathname.new(extension_dir)
build_complete = SharedHelpers.filesystem_access(extension_cache_path.join("gem.build_complete"), :read, &:file?)
if build_complete && !options[:force]
- SharedHelpers.filesystem_access(extension_dir.parent, &:mkpath)
+ SharedHelpers.filesystem_access(File.dirname(extension_dir)) do |p|
+ FileUtils.mkpath p
+ end
SharedHelpers.filesystem_access(extension_cache_path) do
FileUtils.cp_r extension_cache_path, extension_dir
end
@@ -108,8 +109,10 @@ module Bundler
def strict_rm_rf(dir)
Bundler.rm_rf dir
- rescue Errno::ENOTEMPTY => e
- raise DirectoryRemovalError.new(e.cause, "Could not delete previous installation of `#{dir}`")
+ rescue StandardError => e
+ raise unless File.exist?(dir)
+
+ raise DirectoryRemovalError.new(e, "Could not delete previous installation of `#{dir}`")
end
def validate_bundler_checksum(checksum)
diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb
index 08af0610c6..d8b7886af7 100644
--- a/lib/bundler/rubygems_integration.rb
+++ b/lib/bundler/rubygems_integration.rb
@@ -227,10 +227,14 @@ module Bundler
def reverse_rubygems_kernel_mixin
# Disable rubygems' gem activation system
- kernel = (class << ::Kernel; self; end)
- [kernel, ::Kernel].each do |k|
- if k.private_method_defined?(:gem_original_require)
- redefine_method(k, :require, k.instance_method(:gem_original_require))
+ if Gem.respond_to?(:discover_gems_on_require=)
+ Gem.discover_gems_on_require = false
+ else
+ kernel = (class << ::Kernel; self; end)
+ [kernel, ::Kernel].each do |k|
+ if k.private_method_defined?(:gem_original_require)
+ redefine_method(k, :require, k.instance_method(:gem_original_require))
+ end
end
end
end
@@ -243,7 +247,7 @@ module Bundler
kernel = (class << ::Kernel; self; end)
[kernel, ::Kernel].each do |kernel_class|
redefine_method(kernel_class, :gem) do |dep, *reqs|
- if executables && executables.include?(File.basename(caller.first.split(":").first))
+ if executables&.include?(File.basename(caller.first.split(":").first))
break
end
@@ -449,7 +453,7 @@ module Bundler
fetcher = gem_remote_fetcher
fetcher.headers = { "X-Gemfile-Source" => remote.original_uri.to_s } if remote.original_uri
string = fetcher.fetch_path(path)
- Bundler.load_marshal(string)
+ Bundler.safe_load_marshal(string)
rescue Gem::RemoteFetcher::FetchError
# it's okay for prerelease to fail
raise unless name == "prerelease_specs"
@@ -504,10 +508,6 @@ module Bundler
Gem::Package.build(spec, skip_validation)
end
- def repository_subdirectories
- Gem::REPOSITORY_SUBDIRECTORIES
- end
-
def path_separator
Gem.path_separator
end
diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb
index bd38353d3c..95cf78dd41 100644
--- a/lib/bundler/runtime.rb
+++ b/lib/bundler/runtime.rb
@@ -94,7 +94,7 @@ module Bundler
definition_method :requires
def lock(opts = {})
- return if @definition.nothing_changed? && !@definition.unlocking?
+ return if @definition.no_resolve_needed?
@definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
end
diff --git a/lib/bundler/safe_marshal.rb b/lib/bundler/safe_marshal.rb
new file mode 100644
index 0000000000..50aa0f60a6
--- /dev/null
+++ b/lib/bundler/safe_marshal.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Bundler
+ module SafeMarshal
+ ALLOWED_CLASSES = [
+ Array,
+ FalseClass,
+ Gem::Specification,
+ Gem::Version,
+ Hash,
+ String,
+ Symbol,
+ Time,
+ TrueClass,
+ ].freeze
+
+ ERROR = "Unexpected class %s present in marshaled data. Only %s are allowed."
+
+ PROC = proc do |object|
+ object.tap do
+ unless ALLOWED_CLASSES.include?(object.class)
+ raise TypeError, format(ERROR, object.class, ALLOWED_CLASSES.join(", "))
+ end
+ end
+ end
+
+ def self.proc
+ PROC
+ end
+ end
+end
diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb
index 398c66055a..0af2236a45 100644
--- a/lib/bundler/settings.rb
+++ b/lib/bundler/settings.rb
@@ -43,9 +43,7 @@ module Bundler
setup_makes_kernel_gem_public
silence_deprecations
silence_root_warning
- suppress_install_using_messages
update_requires_all_flag
- use_gem_version_promoter_for_major_updates
].freeze
NUMBER_KEYS = %w[
@@ -57,6 +55,7 @@ module Bundler
].freeze
ARRAY_KEYS = %w[
+ only
with
without
].freeze
@@ -219,7 +218,6 @@ module Bundler
def path
configs.each do |_level, settings|
path = value_for("path", settings)
- path = "vendor/bundle" if value_for("deployment", settings) && path.nil?
path_system = value_for("path.system", settings)
disabled_shared_gems = value_for("disable_shared_gems", settings)
next if path.nil? && path_system.nil? && disabled_shared_gems.nil?
@@ -227,7 +225,9 @@ module Bundler
return Path.new(path, system_path)
end
- Path.new(nil, false)
+ path = "vendor/bundle" if self[:deployment]
+
+ Path.new(path, false)
end
Path = Struct.new(:explicit_path, :system_path) do
@@ -277,12 +277,6 @@ module Bundler
end
end
- def allow_sudo?
- key = key_for(:path)
- path_configured = @temporary.key?(key) || @local_config.key?(key)
- !path_configured
- end
-
def ignore_config?
ENV["BUNDLE_IGNORE_CONFIG"]
end
@@ -501,7 +495,7 @@ module Bundler
uri = $2
suffix = $3
end
- uri = "#{uri}/" unless uri.end_with?("/")
+ uri = URINormalizer.normalize_suffix(uri)
require_relative "vendored_uri"
uri = Bundler::URI(uri)
unless uri.absolute?
diff --git a/lib/bundler/setup.rb b/lib/bundler/setup.rb
index 32e9b2d7c0..801fd5312a 100644
--- a/lib/bundler/setup.rb
+++ b/lib/bundler/setup.rb
@@ -12,7 +12,10 @@ if Bundler::SharedHelpers.in_bundle?
Bundler.ui.error e.message
Bundler.ui.warn e.backtrace.join("\n") if ENV["DEBUG"]
if e.is_a?(Bundler::GemNotFound)
- Bundler.ui.warn "Run `bundle install` to install missing gems."
+ suggested_cmd = "bundle install"
+ original_gemfile = Bundler.original_env["BUNDLE_GEMFILE"]
+ suggested_cmd += " --gemfile #{original_gemfile}" if original_gemfile
+ Bundler.ui.warn "Run `#{suggested_cmd}` to install missing gems."
end
exit e.status_code
end
diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb
index 8c4e26f074..d1d4e1d07a 100644
--- a/lib/bundler/shared_helpers.rb
+++ b/lib/bundler/shared_helpers.rb
@@ -160,10 +160,10 @@ module Bundler
" (was expecting #{old_deps.map(&:to_s)}, but the real spec has #{new_deps.map(&:to_s)})"
raise APIResponseMismatchError,
"Downloading #{spec.full_name} revealed dependencies not in the API or the lockfile (#{extra_deps.join(", ")})." \
- "\nEither installing with `--full-index` or running `bundle update #{spec.name}` should fix the problem."
+ "\nRunning `bundle update #{spec.name}` should fix the problem."
end
- def pretty_dependency(dep, print_source = false)
+ def pretty_dependency(dep)
msg = String.new(dep.name)
msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default
@@ -172,7 +172,6 @@ module Bundler
msg << " " << platform_string if !platform_string.empty? && platform_string != Gem::Platform::RUBY
end
- msg << " from the `#{dep.source}` source" if print_source && dep.source
msg
end
@@ -285,6 +284,7 @@ module Bundler
Bundler::SharedHelpers.set_env "BUNDLE_BIN_PATH", exe_file
Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", find_gemfile.to_s
Bundler::SharedHelpers.set_env "BUNDLER_VERSION", Bundler::VERSION
+ Bundler::SharedHelpers.set_env "BUNDLER_SETUP", File.expand_path("setup", __dir__) unless RUBY_VERSION < "2.7"
end
def set_path
diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb
index 69804a2e63..f7f5ea7865 100644
--- a/lib/bundler/source.rb
+++ b/lib/bundler/source.rb
@@ -100,7 +100,7 @@ module Bundler
end
def print_using_message(message)
- if !message.include?("(was ") && Bundler.feature_flag.suppress_install_using_messages?
+ if !message.include?("(was ")
Bundler.ui.debug message
else
Bundler.ui.info message
diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb
index ed66dcdc12..adbce5fce4 100644
--- a/lib/bundler/source/git.rb
+++ b/lib/bundler/source/git.rb
@@ -19,7 +19,7 @@ module Bundler
# Stringify options that could be set as symbols
%w[ref branch tag revision].each {|k| options[k] = options[k].to_s if options[k] }
- @uri = options["uri"] || ""
+ @uri = URINormalizer.normalize_suffix(options["uri"] || "", :trailing_slash => false)
@safe_uri = URICredentialsFilter.credential_filtered_uri(@uri)
@branch = options["branch"]
@ref = options["ref"] || options["branch"] || options["tag"]
@@ -46,6 +46,14 @@ module Bundler
out << " specs:\n"
end
+ def to_gemfile
+ specifiers = %w[ref branch tag submodules glob].map do |opt|
+ "#{opt}: #{options[opt]}" if options[opt]
+ end
+
+ uri_with_specifiers(specifiers)
+ end
+
def hash
[self.class, uri, ref, branch, name, version, glob, submodules].hash
end
@@ -59,28 +67,32 @@ module Bundler
alias_method :==, :eql?
+ def include?(other)
+ other.is_a?(Git) && uri == other.uri &&
+ name == other.name &&
+ glob == other.glob &&
+ submodules == other.submodules
+ end
+
def to_s
begin
- at = if local?
- path
- elsif user_ref = options["ref"]
- if ref =~ /\A[a-z0-9]{4,}\z/i
- shortref_for_display(user_ref)
- else
- user_ref
- end
- elsif ref
- ref
- else
- git_proxy.branch
- end
+ at = humanized_ref || current_branch
rev = "at #{at}@#{shortref_for_display(revision)}"
rescue GitError
""
end
- specifiers = [rev, glob_for_display].compact
+ uri_with_specifiers([rev, glob_for_display])
+ end
+
+ def identifier
+ uri_with_specifiers([humanized_ref, cached_revision, glob_for_display])
+ end
+
+ def uri_with_specifiers(specifiers)
+ specifiers.compact!
+
suffix =
if specifiers.any?
" (#{specifiers.join(", ")})"
@@ -102,13 +114,7 @@ module Bundler
@install_path ||= begin
git_scope = "#{base_name}-#{shortref_for_path(revision)}"
- path = Bundler.install_path.join(git_scope)
-
- if !path.exist? && Bundler.requires_sudo?
- Bundler.user_bundle_path.join(Bundler.ruby_scope).join(git_scope)
- else
- path
- end
+ Bundler.install_path.join(git_scope)
end
end
@@ -132,7 +138,7 @@ module Bundler
path = Pathname.new(path)
path = path.expand_path(Bundler.root) unless path.relative?
- unless options["branch"] || Bundler.settings[:disable_local_branch_check]
+ unless branch || Bundler.settings[:disable_local_branch_check]
raise GitError, "Cannot use local override for #{name} at #{path} because " \
":branch is not specified in Gemfile. Specify a branch or run " \
"`bundle config unset local.#{override_for(original_path)}` to remove the local override"
@@ -147,14 +153,14 @@ module Bundler
# Create a new git proxy without the cached revision
# so the Gemfile.lock always picks up the new revision.
- @git_proxy = GitProxy.new(path, uri, ref)
+ @git_proxy = GitProxy.new(path, uri, options)
- if git_proxy.branch != options["branch"] && !Bundler.settings[:disable_local_branch_check]
+ if current_branch != branch && !Bundler.settings[:disable_local_branch_check]
raise GitError, "Local override for #{name} at #{path} is using branch " \
- "#{git_proxy.branch} but Gemfile specifies #{options["branch"]}"
+ "#{current_branch} but Gemfile specifies #{branch}"
end
- changed = cached_revision && cached_revision != git_proxy.revision
+ changed = cached_revision && cached_revision != revision
if !Bundler.settings[:disable_local_revision_check] && changed && !@unlocked && !git_proxy.contains?(cached_revision)
raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \
@@ -179,6 +185,7 @@ module Bundler
end
def install(spec, options = {})
+ return if Bundler.settings[:no_install]
force = options[:force]
print_using_message "Using #{version_message(spec, options[:previous_spec])} from #{self}"
@@ -219,7 +226,7 @@ module Bundler
# across different projects, this cache will be shared.
# When using local git repos, this is set to the local repo.
def cache_path
- @cache_path ||= if Bundler.requires_sudo? || Bundler.feature_flag.global_gem_cache?
+ @cache_path ||= if Bundler.feature_flag.global_gem_cache?
Bundler.user_cache
else
Bundler.bundle_path.join("cache", "bundler")
@@ -234,6 +241,10 @@ module Bundler
git_proxy.revision
end
+ def current_branch
+ git_proxy.current_branch
+ end
+
def allow_git_ops?
@allow_remote || @allow_cached
end
@@ -244,6 +255,20 @@ module Bundler
private
+ def humanized_ref
+ if local?
+ path
+ elsif user_ref = options["ref"]
+ if /\A[a-z0-9]{4,}\z/i.match?(ref)
+ shortref_for_display(user_ref)
+ else
+ user_ref
+ end
+ elsif ref
+ ref
+ end
+ end
+
def serialize_gemspecs_in(destination)
destination = destination.expand_path(Bundler.root) if destination.relative?
Dir["#{destination}/#{@glob}"].each do |spec_path|
@@ -297,7 +322,7 @@ module Bundler
end
def uri_hash
- if uri =~ %r{^\w+://(\w+@)?}
+ if %r{^\w+://(\w+@)?}.match?(uri)
# Downcase the domain component of the URI
# and strip off a trailing slash, if one is present
input = Bundler::URI.parse(uri).normalize.to_s.sub(%r{/$}, "")
@@ -319,7 +344,7 @@ module Bundler
end
def git_proxy
- @git_proxy ||= GitProxy.new(cache_path, uri, ref, cached_revision, self)
+ @git_proxy ||= GitProxy.new(cache_path, uri, options, cached_revision, self)
end
def fetch
diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb
index 745a7fe118..fdb738e52e 100644
--- a/lib/bundler/source/git/git_proxy.rb
+++ b/lib/bundler/source/git/git_proxy.rb
@@ -28,10 +28,10 @@ module Bundler
def initialize(command, path, extra_info = nil)
@command = command
- msg = String.new
- msg << "Git error: command `#{command}` in directory #{path} has failed."
+ msg = String.new("Git error: command `#{command}`")
+ msg << " in directory #{path}" if path
+ msg << " has failed."
msg << "\n#{extra_info}" if extra_info
- msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path.exist?
super msg
end
end
@@ -47,24 +47,28 @@ module Bundler
# All actions required by the Git source is encapsulated in this
# object.
class GitProxy
- attr_accessor :path, :uri, :ref
+ attr_accessor :path, :uri, :branch, :tag, :ref, :explicit_ref
attr_writer :revision
- def initialize(path, uri, ref, revision = nil, git = nil)
+ def initialize(path, uri, options = {}, revision = nil, git = nil)
@path = path
@uri = uri
- @ref = ref
+ @branch = options["branch"]
+ @tag = options["tag"]
+ @ref = options["ref"]
+ @explicit_ref = branch || tag || ref
@revision = revision
@git = git
+ @commit_ref = nil
end
def revision
- @revision ||= find_local_revision
+ @revision ||= allowed_with_path { find_local_revision }
end
- def branch
- @branch ||= allowed_with_path do
- git("rev-parse", "--abbrev-ref", "HEAD", :dir => path).strip
+ def current_branch
+ @current_branch ||= with_path do
+ git_local("rev-parse", "--abbrev-ref", "HEAD", :dir => path).strip
end
end
@@ -76,36 +80,26 @@ module Bundler
end
def version
- git("--version").match(/(git version\s*)?((\.?\d+)+).*/)[2]
+ @version ||= full_version.match(/((\.?\d+)+).*/)[1]
end
def full_version
- git("--version").sub("git version", "").strip
+ @full_version ||= git_local("--version").sub(/git version\s*/, "").strip
end
def checkout
- return if path.exist? && has_revision_cached?
- extra_ref = "#{ref}:#{ref}" if ref && ref.start_with?("refs/")
-
- Bundler.ui.info "Fetching #{URICredentialsFilter.credential_filtered_uri(uri)}"
+ return if has_revision_cached?
- configured_uri = configured_uri_for(uri).to_s
+ Bundler.ui.info "Fetching #{credential_filtered_uri}"
- unless path.exist?
- SharedHelpers.filesystem_access(path.dirname) do |p|
- FileUtils.mkdir_p(p)
- end
- git_retry "clone", "--bare", "--no-hardlinks", "--quiet", "--", configured_uri, path.to_s
- return unless extra_ref
- end
+ extra_fetch_needed = clone_needs_extra_fetch?
+ unshallow_needed = clone_needs_unshallow?
+ return unless extra_fetch_needed || unshallow_needed
- with_path do
- git_retry(*["fetch", "--force", "--quiet", "--tags", "--", configured_uri, "refs/heads/*:refs/heads/*", extra_ref].compact, :dir => path)
- end
+ git_remote_fetch(unshallow_needed ? ["--unshallow"] : depth_args)
end
def copy_to(destination, submodules = false)
- # method 1
unless File.exist?(destination.join(".git"))
begin
SharedHelpers.filesystem_access(destination.dirname) do |p|
@@ -114,7 +108,7 @@ module Bundler
SharedHelpers.filesystem_access(destination) do |p|
FileUtils.rm_rf(p)
end
- git_retry "clone", "--no-checkout", "--quiet", path.to_s, destination.to_s
+ git "clone", "--no-checkout", "--quiet", path.to_s, destination.to_s
File.chmod(((File.stat(destination).mode | 0o777) & ~File.umask), destination)
rescue Errno::EEXIST => e
file_path = e.message[%r{.*?((?:[a-zA-Z]:)?/.*)}, 1]
@@ -123,14 +117,10 @@ module Bundler
"this file and try again."
end
end
- # method 2
- git_retry "fetch", "--force", "--quiet", "--tags", path.to_s, :dir => destination
- begin
- git "reset", "--hard", @revision, :dir => destination
- rescue GitCommandError => e
- raise MissingGitRevisionError.new(e.command, destination, @revision, URICredentialsFilter.credential_filtered_uri(uri))
- end
+ git "fetch", "--force", "--quiet", *extra_fetch_args, :dir => destination if @commit_ref
+
+ git "reset", "--hard", @revision, :dir => destination
if submodules
git_retry "submodule", "update", "--init", "--recursive", :dir => destination
@@ -142,14 +132,117 @@ module Bundler
private
- def git_null(*command, dir: nil)
- check_allowed(command)
+ def git_remote_fetch(args)
+ command = ["fetch", "--force", "--quiet", "--no-tags", *args, "--", configured_uri, refspec].compact
+ command_with_no_credentials = check_allowed(command)
+
+ Bundler::Retry.new("`#{command_with_no_credentials}` at #{path}", [MissingGitRevisionError]).attempts do
+ out, err, status = capture(command, path)
+ return out if status.success?
+
+ if err.include?("couldn't find remote ref") || err.include?("not our ref")
+ raise MissingGitRevisionError.new(command_with_no_credentials, path, commit || explicit_ref, credential_filtered_uri)
+ else
+ raise GitCommandError.new(command_with_no_credentials, path, err)
+ end
+ end
+ end
+
+ def clone_needs_extra_fetch?
+ return true if path.exist?
+
+ SharedHelpers.filesystem_access(path.dirname) do |p|
+ FileUtils.mkdir_p(p)
+ end
+
+ command = ["clone", "--bare", "--no-hardlinks", "--quiet", *extra_clone_args, "--", configured_uri, path.to_s]
+ command_with_no_credentials = check_allowed(command)
+
+ Bundler::Retry.new("`#{command_with_no_credentials}`", [MissingGitRevisionError]).attempts do
+ _, err, status = capture(command, nil)
+ return extra_ref if status.success?
+
+ if err.include?("Could not find remote branch") || # git up to 2.49
+ err.include?("Remote branch #{branch_option} not found") # git 2.49 or higher
+ raise MissingGitRevisionError.new(command_with_no_credentials, nil, explicit_ref, credential_filtered_uri)
+ else
+ raise GitCommandError.new(command_with_no_credentials, path, err)
+ end
+ end
+ end
- out, status = SharedHelpers.with_clean_git_env do
- capture_and_ignore_stderr(*capture3_args_for(command, dir))
+ def clone_needs_unshallow?
+ return false unless path.join("shallow").exist?
+ return true if full_clone?
+
+ @revision && @revision != head_revision
+ end
+
+ def extra_ref
+ return false if not_pinned?
+ return true unless full_clone?
+
+ ref.start_with?("refs/")
+ end
+
+ def depth
+ return @depth if defined?(@depth)
+
+ @depth = if !supports_fetching_unreachable_refs?
+ nil
+ elsif not_pinned? || pinned_to_full_sha?
+ 1
+ elsif ref.include?("~")
+ parsed_depth = ref.split("~").last
+ parsed_depth.to_i + 1
end
+ end
+
+ def refspec
+ if commit
+ @commit_ref = "refs/#{commit}-sha"
+ return "#{commit}:#{@commit_ref}"
+ end
+
+ reference = fully_qualified_ref
+
+ reference ||= if ref.include?("~")
+ ref.split("~").first
+ elsif ref.start_with?("refs/")
+ ref
+ else
+ "refs/*"
+ end
+
+ "#{reference}:#{reference}"
+ end
+
+ def commit
+ @commit ||= pinned_to_full_sha? ? ref : @revision
+ end
+
+ def fully_qualified_ref
+ if branch
+ "refs/heads/#{branch}"
+ elsif tag
+ "refs/tags/#{tag}"
+ elsif ref.nil?
+ "refs/heads/#{current_branch}"
+ end
+ end
+
+ def not_pinned?
+ branch_option || ref.nil?
+ end
+
+ def pinned_to_full_sha?
+ ref =~ /\A\h{40}\z/
+ end
- [URICredentialsFilter.credential_filtered_string(out, uri), status]
+ def git_null(*command, dir: nil)
+ check_allowed(command)
+
+ capture(command, dir, :ignore_err => true)
end
def git_retry(*command, dir: nil)
@@ -161,51 +254,64 @@ module Bundler
end
def git(*command, dir: nil)
- command_with_no_credentials = check_allowed(command)
-
- out, status = SharedHelpers.with_clean_git_env do
- capture_and_filter_stderr(*capture3_args_for(command, dir))
+ run_command(*command, :dir => dir) do |unredacted_command|
+ check_allowed(unredacted_command)
end
+ end
- filtered_out = URICredentialsFilter.credential_filtered_string(out, uri)
-
- raise GitCommandError.new(command_with_no_credentials, dir || SharedHelpers.pwd, filtered_out) unless status.success?
-
- filtered_out
+ def git_local(*command, dir: nil)
+ run_command(*command, :dir => dir) do |unredacted_command|
+ redact_and_check_presence(unredacted_command)
+ end
end
def has_revision_cached?
- return unless @revision
- with_path { git("cat-file", "-e", @revision, :dir => path) }
+ return unless @revision && path.exist?
+ git("cat-file", "-e", @revision, :dir => path)
true
rescue GitError
false
end
- def remove_cache
- FileUtils.rm_rf(path)
+ def find_local_revision
+ return head_revision if explicit_ref.nil?
+
+ find_revision_for(explicit_ref)
end
- def find_local_revision
- allowed_with_path do
- git("rev-parse", "--verify", ref || "HEAD", :dir => path).strip
- end
+ def head_revision
+ verify("HEAD")
+ end
+
+ def find_revision_for(reference)
+ verify(reference)
rescue GitCommandError => e
- raise MissingGitRevisionError.new(e.command, path, ref, URICredentialsFilter.credential_filtered_uri(uri))
+ raise MissingGitRevisionError.new(e.command, path, reference, credential_filtered_uri)
end
- # Adds credentials to the URI as Fetcher#configured_uri_for does
- def configured_uri_for(uri)
- if /https?:/ =~ uri
+ def verify(reference)
+ git("rev-parse", "--verify", reference, :dir => path).strip
+ end
+
+ # Adds credentials to the URI
+ def configured_uri
+ if /https?:/.match?(uri)
remote = Bundler::URI(uri)
config_auth = Bundler.settings[remote.to_s] || Bundler.settings[remote.host]
remote.userinfo ||= config_auth
remote.to_s
+ elsif File.exist?(uri)
+ "file://#{uri}"
else
- uri
+ uri.to_s
end
end
+ # Removes credentials from the URI
+ def credential_filtered_uri
+ URICredentialsFilter.credential_filtered_uri(uri)
+ end
+
def allow?
allowed = @git ? @git.allow_git_ops? : true
@@ -225,23 +331,41 @@ module Bundler
end
def check_allowed(command)
- require "shellwords"
- command_with_no_credentials = URICredentialsFilter.credential_filtered_string("git #{command.shelljoin}", uri)
+ command_with_no_credentials = redact_and_check_presence(command)
raise GitNotAllowedError.new(command_with_no_credentials) unless allow?
command_with_no_credentials
end
- def capture_and_filter_stderr(*cmd)
- require "open3"
- return_value, captured_err, status = Open3.capture3(*cmd)
- Bundler.ui.warn URICredentialsFilter.credential_filtered_string(captured_err, uri) unless captured_err.empty?
- [return_value, status]
+ def redact_and_check_presence(command)
+ raise GitNotInstalledError.new unless Bundler.git_present?
+
+ require "shellwords"
+ URICredentialsFilter.credential_filtered_string("git #{command.shelljoin}", uri)
+ end
+
+ def run_command(*command, dir: nil)
+ command_with_no_credentials = yield(command)
+
+ out, err, status = capture(command, dir)
+
+ raise GitCommandError.new(command_with_no_credentials, dir || SharedHelpers.pwd, err) unless status.success?
+
+ Bundler.ui.warn err unless err.empty?
+
+ out
end
- def capture_and_ignore_stderr(*cmd)
- require "open3"
- return_value, _, status = Open3.capture3(*cmd)
- [return_value, status]
+ def capture(cmd, dir, ignore_err: false)
+ SharedHelpers.with_clean_git_env do
+ require "open3"
+ out, err, status = Open3.capture3(*capture3_args_for(cmd, dir))
+
+ filtered_out = URICredentialsFilter.credential_filtered_string(out, uri)
+ return [filtered_out, status] if ignore_err
+
+ filtered_err = URICredentialsFilter.credential_filtered_string(err, uri)
+ [filtered_out, filtered_err, status]
+ end
end
def capture3_args_for(cmd, dir)
@@ -254,9 +378,53 @@ module Bundler
end
end
+ def extra_clone_args
+ args = depth_args
+ return [] if args.empty?
+
+ args += ["--single-branch"]
+ args.unshift("--no-tags") if supports_cloning_with_no_tags?
+
+ # If there's a locked revision, no need to clone any specific branch
+ # or tag, since we will end up checking out that locked revision
+ # anyways.
+ return args if @revision
+
+ args += ["--branch", branch_option] if branch_option
+ args
+ end
+
+ def depth_args
+ return [] if full_clone?
+
+ ["--depth", depth.to_s]
+ end
+
+ def extra_fetch_args
+ extra_args = [path.to_s, *depth_args]
+ extra_args.push(@commit_ref)
+ extra_args
+ end
+
+ def branch_option
+ branch || tag
+ end
+
+ def full_clone?
+ depth.nil?
+ end
+
def supports_minus_c?
@supports_minus_c ||= Gem::Version.new(version) >= Gem::Version.new("1.8.5")
end
+
+ def supports_fetching_unreachable_refs?
+ @supports_fetching_unreachable_refs ||= Gem::Version.new(version) >= Gem::Version.new("2.5.0")
+ end
+
+ def supports_cloning_with_no_tags?
+ @supports_cloning_with_no_tags ||= Gem::Version.new(version) >= Gem::Version.new("2.14.0-rc0")
+ end
end
end
end
diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb
index 185fe70824..593da6d1a7 100644
--- a/lib/bundler/source/metadata.rb
+++ b/lib/bundler/source/metadata.rb
@@ -5,7 +5,7 @@ module Bundler
class Metadata < Source
def specs
@specs ||= Index.build do |idx|
- idx << Gem::Specification.new("Ruby\0", RubyVersion.system.gem_version)
+ idx << Gem::Specification.new("Ruby\0", Gem.ruby_version)
idx << Gem::Specification.new("RubyGems\0", Gem::VERSION) do |s|
s.required_rubygems_version = Gem::Requirement.default
end
@@ -15,7 +15,6 @@ module Bundler
s.version = VERSION
s.license = "MIT"
s.platform = Gem::Platform::RUBY
- s.source = self
s.authors = ["bundler team"]
s.bindir = "exe"
s.homepage = "https://bundler.io"
diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb
index 672ecfd13b..bdfcf8274a 100644
--- a/lib/bundler/source/path.rb
+++ b/lib/bundler/source/path.rb
@@ -11,7 +11,7 @@ module Bundler
protected :original_path
- DEFAULT_GLOB = "{,*,*/*}.gemspec".freeze
+ DEFAULT_GLOB = "{,*,*/*}.gemspec"
def initialize(options)
@options = options.dup
@@ -224,13 +224,13 @@ module Bundler
# Some gem authors put absolute paths in their gemspec
# and we have to save them from themselves
- spec.files = spec.files.map do |p|
- next p unless p =~ /\A#{Pathname::SEPARATOR_PAT}/
- next if File.directory?(p)
+ spec.files = spec.files.map do |path|
+ next path unless /\A#{Pathname::SEPARATOR_PAT}/.match?(path)
+ next if File.directory?(path)
begin
- Pathname.new(p).relative_path_from(gem_dir).to_s
+ Pathname.new(path).relative_path_from(gem_dir).to_s
rescue ArgumentError
- p
+ path
end
end.compact
diff --git a/lib/bundler/source/path/installer.rb b/lib/bundler/source/path/installer.rb
index a70973bde7..0af28fe770 100644
--- a/lib/bundler/source/path/installer.rb
+++ b/lib/bundler/source/path/installer.rb
@@ -18,13 +18,7 @@ module Bundler
@build_args = options[:build_args] || Bundler.rubygems.build_args
@gem_bin_dir = "#{Bundler.rubygems.gem_dir}/bin"
@disable_extensions = options[:disable_extensions]
-
- if Bundler.requires_sudo?
- @tmp_dir = Bundler.tmp(spec.full_name).to_s
- @bin_dir = "#{@tmp_dir}/bin"
- else
- @bin_dir = @gem_bin_dir
- end
+ @bin_dir = @gem_bin_dir
end
def post_install
@@ -38,25 +32,10 @@ module Bundler
generate_bin unless spec.executables.empty?
run_hooks(:post_install)
- ensure
- Bundler.rm_rf(@tmp_dir) if Bundler.requires_sudo?
end
private
- def generate_bin
- super
-
- if Bundler.requires_sudo?
- SharedHelpers.filesystem_access(@gem_bin_dir) do |p|
- Bundler.mkdir_p(p)
- end
- spec.executables.each do |exe|
- Bundler.sudo "cp -R #{@bin_dir}/#{exe} #{@gem_bin_dir}"
- end
- end
- end
-
def run_hooks(type)
hooks_meth = "#{type}_hooks"
return unless Gem.respond_to?(hooks_meth)
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
index a50934b315..af57acbbc2 100644
--- a/lib/bundler/source/rubygems.rb
+++ b/lib/bundler/source/rubygems.rb
@@ -7,12 +7,10 @@ module Bundler
class Rubygems < Source
autoload :Remote, File.expand_path("rubygems/remote", __dir__)
- # Use the API when installing less than X gems
- API_REQUEST_LIMIT = 500
# Ask for X gems per API request
API_REQUEST_SIZE = 50
- attr_reader :remotes, :caches
+ attr_reader :remotes
def initialize(options = {})
@options = options
@@ -21,11 +19,14 @@ module Bundler
@allow_remote = false
@allow_cached = false
@allow_local = options["allow_local"] || false
- @caches = [cache_path, *Bundler.rubygems.gem_cache]
Array(options["remotes"]).reverse_each {|r| add_remote(r) }
end
+ def caches
+ @caches ||= [cache_path, *Bundler.rubygems.gem_cache]
+ end
+
def local_only!
@specs = nil
@allow_local = true
@@ -122,6 +123,7 @@ module Bundler
end
end
alias_method :name, :identifier
+ alias_method :to_gemfile, :identifier
def specs
@specs ||= begin
@@ -145,7 +147,7 @@ module Bundler
end
if installed?(spec) && !force
- print_using_message "Using #{version_message(spec)}"
+ print_using_message "Using #{version_message(spec, options[:previous_spec])}"
return nil # no post-install message
end
@@ -160,27 +162,20 @@ module Bundler
return if Bundler.settings[:no_install]
- if requires_sudo?
- install_path = Bundler.tmp(spec.full_name)
- bin_path = install_path.join("bin")
- else
- install_path = rubygems_dir
- bin_path = Bundler.system_bindir
- end
-
- Bundler.mkdir_p bin_path, :no_sudo => true unless spec.executables.empty? || Bundler.rubygems.provides?(">= 2.7.5")
+ install_path = rubygems_dir
+ bin_path = Bundler.system_bindir
require_relative "../rubygems_gem_installer"
installer = Bundler::RubyGemsGemInstaller.at(
path,
- :security_policy => Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]],
- :install_dir => install_path.to_s,
- :bin_dir => bin_path.to_s,
+ :security_policy => Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]],
+ :install_dir => install_path.to_s,
+ :bin_dir => bin_path.to_s,
:ignore_dependencies => true,
- :wrappers => true,
- :env_shebang => true,
- :build_args => options[:build_args],
+ :wrappers => true,
+ :env_shebang => true,
+ :build_args => options[:build_args],
:bundler_expected_checksum => spec.respond_to?(:checksum) && spec.checksum,
:bundler_extension_cache_path => extension_cache_path(spec)
)
@@ -209,34 +204,7 @@ module Bundler
spec.full_gem_path = installed_spec.full_gem_path
spec.loaded_from = installed_spec.loaded_from
- # SUDO HAX
- if requires_sudo?
- Bundler.rubygems.repository_subdirectories.each do |name|
- src = File.join(install_path, name, "*")
- dst = File.join(rubygems_dir, name)
- if name == "extensions" && Dir.glob(src).any?
- src = File.join(src, "*/*")
- ext_src = Dir.glob(src).first
- ext_src.gsub!(src[0..-6], "")
- dst = File.dirname(File.join(dst, ext_src))
- end
- SharedHelpers.filesystem_access(dst) do |p|
- Bundler.mkdir_p(p)
- end
- Bundler.sudo "cp -R #{src} #{dst}" if Dir[src].any?
- end
-
- spec.executables.each do |exe|
- SharedHelpers.filesystem_access(Bundler.system_bindir) do |p|
- Bundler.mkdir_p(p)
- end
- Bundler.sudo "cp -R #{install_path}/bin/#{exe} #{Bundler.system_bindir}/"
- end
- end
-
spec.post_install_message
- ensure
- Bundler.rm_rf(install_path) if requires_sudo?
end
def cache(spec, custom_path = nil)
@@ -326,7 +294,7 @@ module Bundler
end
def dependency_api_available?
- api_fetchers.any?
+ @allow_remote && api_fetchers.any?
end
protected
@@ -360,9 +328,9 @@ module Bundler
def cached_path(spec)
global_cache_path = download_cache_path(spec)
- @caches << global_cache_path if global_cache_path
+ caches << global_cache_path if global_cache_path
- possibilities = @caches.map {|p| package_path(p, spec) }
+ possibilities = caches.map {|p| package_path(p, spec) }
possibilities.find {|p| File.exist?(p) }
end
@@ -371,8 +339,7 @@ module Bundler
end
def normalize_uri(uri)
- uri = uri.to_s
- uri = "#{uri}/" unless uri =~ %r{/$}
+ uri = URINormalizer.normalize_suffix(uri.to_s)
require_relative "../vendored_uri"
uri = Bundler::URI(uri)
raise ArgumentError, "The source must be an absolute URI. For example:\n" \
@@ -415,7 +382,6 @@ module Bundler
idx = @allow_local ? installed_specs.dup : Index.new
Dir["#{cache_path}/*.gem"].each do |gemfile|
- next if gemfile =~ /^bundler\-[\d\.]+?\.gem/
s ||= Bundler.rubygems.spec_from_gem(gemfile)
s.source = self
idx << s
@@ -436,12 +402,11 @@ module Bundler
# gather lists from non-api sites
fetch_names(index_fetchers, nil, idx, false)
- # because ensuring we have all the gems we need involves downloading
- # the gemspecs of those gems, if the non-api sites contain more than
- # about 500 gems, we treat all sites as non-api for speed.
- allow_api = idx.size < API_REQUEST_LIMIT && dependency_names.size < API_REQUEST_LIMIT
- Bundler.ui.debug "Need to query more than #{API_REQUEST_LIMIT} gems." \
- " Downloading full index instead..." unless allow_api
+ # legacy multi-remote sources need special logic to figure out
+ # dependency names and that logic can be very costly if one remote
+ # uses the dependency API but others don't. So use full indexes
+ # consistently in that particular case.
+ allow_api = !multiple_remotes?
fetch_names(api_fetchers, allow_api && dependency_names, idx, false)
end
@@ -475,38 +440,18 @@ module Bundler
gem_path = package_path(cache_path, spec)
return gem_path if File.exist?(gem_path)
- if requires_sudo?
- download_path = Bundler.tmp(spec.full_name)
- download_cache_path = default_cache_path_for(download_path)
- else
- download_cache_path = cache_path
- end
-
- SharedHelpers.filesystem_access(download_cache_path) do |p|
+ SharedHelpers.filesystem_access(cache_path) do |p|
FileUtils.mkdir_p(p)
end
- download_gem(spec, download_cache_path, previous_spec)
-
- if requires_sudo?
- SharedHelpers.filesystem_access(cache_path) do |p|
- Bundler.mkdir_p(p)
- end
- Bundler.sudo "mv #{package_path(download_cache_path, spec)} #{gem_path}"
- end
+ download_gem(spec, cache_path, previous_spec)
gem_path
- ensure
- Bundler.rm_rf(download_path) if requires_sudo?
end
def installed?(spec)
installed_specs[spec].any? && !spec.deleted_gem?
end
- def requires_sudo?
- Bundler.requires_sudo?
- end
-
def rubygems_dir
Bundler.bundle_path
end
diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb
index a4773397c7..4419695b7f 100644
--- a/lib/bundler/source_list.rb
+++ b/lib/bundler/source_list.rb
@@ -157,11 +157,17 @@ module Bundler
end
def map_sources(replacement_sources)
- [@rubygems_sources, @path_sources, @git_sources, @plugin_sources].map do |sources|
+ rubygems, git, plugin = [@rubygems_sources, @git_sources, @plugin_sources].map do |sources|
sources.map do |source|
replacement_sources.find {|s| s == source } || source
end
end
+
+ path = @path_sources.map do |source|
+ replacement_sources.find {|s| s == (source.is_a?(Source::Gemspec) ? source.as_path_source : source) } || source
+ end
+
+ [rubygems, path, git, plugin]
end
def global_replacement_source(replacement_sources)
@@ -202,7 +208,7 @@ module Bundler
def warn_on_git_protocol(source)
return if Bundler.settings["git.allow_insecure"]
- if source.uri =~ /^git\:/
+ if /^git\:/.match?(source.uri)
Bundler.ui.warn "The git source `#{source.uri}` uses the `git` protocol, " \
"which transmits data without encryption. Disable this warning with " \
"`bundle config set --local git.allow_insecure true`, or switch to the `https` " \
diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
index 06257ac93f..21630e3a3e 100644
--- a/lib/bundler/spec_set.rb
+++ b/lib/bundler/spec_set.rb
@@ -7,44 +7,49 @@ module Bundler
include Enumerable
include TSort
- def initialize(specs)
+ attr_reader :incomplete_specs
+
+ def initialize(specs, incomplete_specs = [])
@specs = specs
+ @incomplete_specs = incomplete_specs
end
- def for(dependencies, check = false, match_current_platform = false)
- # dep.name => [list, of, deps]
- handled = Hash.new {|h, k| h[k] = [] }
- deps = dependencies.dup
+ def for(dependencies, check = false, platforms = [nil])
+ handled = ["bundler"].product(platforms).map {|k| [k, true] }.to_h
+ deps = dependencies.product(platforms)
specs = []
loop do
break unless dep = deps.shift
- next if handled[dep.name].any? {|d| match_current_platform || d.__platform == dep.__platform } || dep.name == "bundler"
- # use a hash here to ensure constant lookup time in the `any?` call above
- handled[dep.name] << dep
+ name = dep[0].name
+ platform = dep[1]
+ incomplete = false
+
+ key = [name, platform]
+ next if handled.key?(key)
+
+ handled[key] = true
- specs_for_dep = specs_for_dependency(dep, match_current_platform)
+ specs_for_dep = specs_for_dependency(*dep)
if specs_for_dep.any?
specs.concat(specs_for_dep)
specs_for_dep.first.dependencies.each do |d|
next if d.type == :development
- d = DepProxy.get_proxy(Dependency.new(d.name, d.requirement), dep.__platform) unless match_current_platform
- deps << d
+ incomplete = true if d.name != "bundler" && lookup[d.name].empty?
+ deps << [d, dep[1]]
end
- elsif check
- return false
+ else
+ incomplete = true
end
- end
- if spec = lookup["bundler"].first
- specs << spec
+ if incomplete && check
+ @incomplete_specs += lookup[name].any? ? lookup[name] : [LazySpecification.new(name, nil, nil)]
+ end
end
- specs.uniq! unless match_current_platform
-
- check ? true : specs
+ specs.uniq
end
def [](key)
@@ -58,6 +63,12 @@ module Bundler
@sorted = nil
end
+ def delete(specs)
+ specs.each {|spec| @specs.delete(spec) }
+ @lookup = nil
+ @sorted = nil
+ end
+
def sort!
self
end
@@ -71,14 +82,9 @@ module Bundler
end
def materialize(deps)
- materialized = self.for(deps, false, true)
+ materialized = self.for(deps, true)
- materialized.map! do |s|
- next s unless s.is_a?(LazySpecification)
- s.source.local!
- s.__materialize__ || s
- end
- SpecSet.new(materialized)
+ SpecSet.new(materialized, incomplete_specs)
end
# Materialize for all the specs in the spec set, regardless of what platform they're for
@@ -87,14 +93,23 @@ module Bundler
def materialized_for_all_platforms
@specs.map do |s|
next s unless s.is_a?(LazySpecification)
- s.source.local!
s.source.remote!
- spec = s.__materialize__
+ spec = s.materialize_for_installation
raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec
spec
end
end
+ def incomplete_ruby_specs?(deps)
+ return false if @specs.empty?
+
+ @incomplete_specs = []
+
+ self.for(deps, true, [Gem::Platform::RUBY])
+
+ @incomplete_specs.any?
+ end
+
def missing_specs
@specs.select {|s| s.is_a?(LazySpecification) }
end
@@ -109,10 +124,20 @@ module Bundler
SpecSet.new(arr)
end
+ def -(other)
+ SpecSet.new(to_a - other.to_a)
+ end
+
def find_by_name_and_platform(name, platform)
@specs.detect {|spec| spec.name == name && spec.match_platform(platform) }
end
+ def delete_by_name(name)
+ @specs.reject! {|spec| spec.name == name }
+ @lookup = nil
+ @sorted = nil
+ end
+
def what_required(spec)
unless req = find {|s| s.dependencies.any? {|d| d.type == :runtime && d.name == spec.name } }
return [spec]
@@ -150,7 +175,7 @@ module Bundler
cgems = extract_circular_gems(error)
raise CyclicDependencyError, "Your bundle requires gems that depend" \
" on each other, creating an infinite loop. Please remove either" \
- " gem '#{cgems[1]}' or gem '#{cgems[0]}' and try again."
+ " gem '#{cgems[0]}' or gem '#{cgems[1]}' and try again."
end
end
@@ -161,7 +186,7 @@ module Bundler
def lookup
@lookup ||= begin
lookup = Hash.new {|h, k| h[k] = [] }
- Index.sort_specs(@specs).reverse_each do |s|
+ @specs.each do |s|
lookup[s.name] << s
end
lookup
@@ -173,14 +198,12 @@ module Bundler
@specs.sort_by(&:name).each {|s| yield s }
end
- def specs_for_dependency(dep, match_current_platform)
+ def specs_for_dependency(dep, platform)
specs_for_name = lookup[dep.name]
- if match_current_platform
- GemHelpers.select_best_platform_match(specs_for_name, Bundler.local_platform)
- else
- specs_for_name_and_platform = GemHelpers.select_best_platform_match(specs_for_name, dep.force_ruby_platform ? Gem::Platform::RUBY : dep.__platform)
- specs_for_name_and_platform.any? ? specs_for_name_and_platform : specs_for_name
- end
+ target_platform = dep.force_ruby_platform ? Gem::Platform::RUBY : (platform || Bundler.local_platform)
+ matching_specs = GemHelpers.select_best_platform_match(specs_for_name, target_platform)
+ matching_specs.map!(&:materialize_for_installation).compact! if platform.nil?
+ matching_specs
end
def tsort_each_child(s)
diff --git a/lib/bundler/templates/Executable b/lib/bundler/templates/Executable
index f6487e3c89..9ff6f00898 100644
--- a/lib/bundler/templates/Executable
+++ b/lib/bundler/templates/Executable
@@ -13,7 +13,7 @@ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("<%= relative_gemfile_path %>", __dir
bundle_binstub = File.expand_path("bundle", __dir__)
if File.file?(bundle_binstub)
- if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
diff --git a/lib/bundler/templates/Executable.bundler b/lib/bundler/templates/Executable.bundler
index 51650c7664..e290fe91eb 100644
--- a/lib/bundler/templates/Executable.bundler
+++ b/lib/bundler/templates/Executable.bundler
@@ -47,7 +47,7 @@ m = Module.new do
def lockfile
lockfile =
case File.basename(gemfile)
- when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
+ when "gems.rb" then gemfile.sub(/\.rb$/, ".locked")
else "#{gemfile}.lock"
end
File.expand_path(lockfile)
@@ -62,8 +62,9 @@ m = Module.new do
def bundler_requirement
@bundler_requirement ||=
- env_var_version || cli_arg_version ||
- bundler_requirement_for(lockfile_version)
+ env_var_version ||
+ cli_arg_version ||
+ bundler_requirement_for(lockfile_version)
end
def bundler_requirement_for(version)
@@ -71,13 +72,7 @@ m = Module.new do
bundler_gem_version = Gem::Version.new(version)
- requirement = bundler_gem_version.approximate_recommendation
-
- return requirement unless Gem.rubygems_version < Gem::Version.new("2.7.0")
-
- requirement += ".a" if bundler_gem_version.prerelease?
-
- requirement
+ bundler_gem_version.approximate_recommendation
end
def load_bundler!
diff --git a/lib/bundler/templates/Executable.standalone b/lib/bundler/templates/Executable.standalone
index d591e3fc04..3117a27e86 100644
--- a/lib/bundler/templates/Executable.standalone
+++ b/lib/bundler/templates/Executable.standalone
@@ -1,4 +1,6 @@
#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG["ruby_install_name"] %>
+# frozen_string_literal: true
+
#
# This file was generated by Bundler.
#
diff --git a/lib/bundler/templates/gems.rb b/lib/bundler/templates/gems.rb
deleted file mode 100644
index d2403f18b2..0000000000
--- a/lib/bundler/templates/gems.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-source "https://rubygems.org"
-
-# gem "rails"
diff --git a/lib/bundler/templates/newgem/Cargo.toml.tt b/lib/bundler/templates/newgem/Cargo.toml.tt
new file mode 100644
index 0000000000..f5a460c9bb
--- /dev/null
+++ b/lib/bundler/templates/newgem/Cargo.toml.tt
@@ -0,0 +1,7 @@
+# This Cargo.toml is here to let externals tools (IDEs, etc.) know that this is
+# a Rust project. Your extensions dependencies should be added to the Cargo.toml
+# in the ext/ directory.
+
+[workspace]
+members = ["./ext/<%= config[:name] %>"]
+resolver = "2"
diff --git a/lib/bundler/templates/newgem/Gemfile.tt b/lib/bundler/templates/newgem/Gemfile.tt
index de82a63c5f..a0d2ac2826 100644
--- a/lib/bundler/templates/newgem/Gemfile.tt
+++ b/lib/bundler/templates/newgem/Gemfile.tt
@@ -9,6 +9,9 @@ gem "rake", "~> 13.0"
<%- if config[:ext] -%>
gem "rake-compiler"
+<%- if config[:ext] == 'rust' -%>
+gem "rb_sys", "~> 0.9.63"
+<%- end -%>
<%- end -%>
<%- if config[:test] -%>
diff --git a/lib/bundler/templates/newgem/README.md.tt b/lib/bundler/templates/newgem/README.md.tt
index a60c7967ec..20eaac8a62 100644
--- a/lib/bundler/templates/newgem/README.md.tt
+++ b/lib/bundler/templates/newgem/README.md.tt
@@ -1,18 +1,20 @@
# <%= config[:constant_name] %>
-Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/<%= config[:namespaced_path] %>`. To experiment with that code, run `bin/console` for an interactive prompt.
+TODO: Delete this and the text below, and describe your gem
-TODO: Delete this and the text above, and describe your gem
+Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/<%= config[:namespaced_path] %>`. To experiment with that code, run `bin/console` for an interactive prompt.
## Installation
+TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
+
Install the gem and add to the application's Gemfile by executing:
- $ bundle add <%= config[:name] %>
+ $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
If bundler is not being used to manage dependencies, install the gem by executing:
- $ gem install <%= config[:name] %>
+ $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
## Usage
diff --git a/lib/bundler/templates/newgem/Rakefile.tt b/lib/bundler/templates/newgem/Rakefile.tt
index b02ada9b6c..b5a5c4e392 100644
--- a/lib/bundler/templates/newgem/Rakefile.tt
+++ b/lib/bundler/templates/newgem/Rakefile.tt
@@ -39,7 +39,17 @@ require "standard/rake"
<% end -%>
<% if config[:ext] -%>
-<% default_task_names.unshift(:clobber, :compile) -%>
+<% default_task_names.unshift(:compile) -%>
+<% default_task_names.unshift(:clobber) unless config[:ext] == 'rust' -%>
+<% if config[:ext] == 'rust' -%>
+require "rb_sys/extensiontask"
+
+task build: :compile
+
+RbSys::ExtensionTask.new(<%= config[:name].inspect %>) do |ext|
+ ext.lib_dir = "lib/<%= config[:namespaced_path] %>"
+end
+<% else -%>
require "rake/extensiontask"
task build: :compile
@@ -47,6 +57,7 @@ task build: :compile
Rake::ExtensionTask.new("<%= config[:underscored_name] %>") do |ext|
ext.lib_dir = "lib/<%= config[:namespaced_path] %>"
end
+<% end -%>
<% end -%>
<% if default_task_names.size == 1 -%>
diff --git a/lib/bundler/templates/newgem/bin/console.tt b/lib/bundler/templates/newgem/bin/console.tt
index 08dfaaef69..c91ee65f93 100644
--- a/lib/bundler/templates/newgem/bin/console.tt
+++ b/lib/bundler/templates/newgem/bin/console.tt
@@ -7,9 +7,5 @@ require "<%= config[:namespaced_path] %>"
# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.
-# (If you use this, don't forget to add pry to your Gemfile!)
-# require "pry"
-# Pry.start
-
require "irb"
IRB.start(__FILE__)
diff --git a/lib/bundler/templates/newgem/circleci/config.yml.tt b/lib/bundler/templates/newgem/circleci/config.yml.tt
index 79fd0dcc0f..f40f029bf1 100644
--- a/lib/bundler/templates/newgem/circleci/config.yml.tt
+++ b/lib/bundler/templates/newgem/circleci/config.yml.tt
@@ -3,8 +3,20 @@ jobs:
build:
docker:
- image: ruby:<%= RUBY_VERSION %>
+<%- if config[:ext] == 'rust' -%>
+ environment:
+ RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN: 'true'
+<%- end -%>
steps:
- checkout
+<%- if config[:ext] == 'rust' -%>
+ - run:
+ name: Install Rust/Cargo dependencies
+ command: apt-get update && apt-get install -y clang
+ - run:
+ name: Install a RubyGems version that can compile rust extensions
+ command: gem update --system '<%= ::Gem.rubygems_version %>'
+<%- end -%>
- run:
name: Run the default task
command: |
diff --git a/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt b/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt
new file mode 100644
index 0000000000..c64385486e
--- /dev/null
+++ b/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt
@@ -0,0 +1,15 @@
+[package]
+name = <%= config[:name].inspect %>
+version = "0.1.0"
+edition = "2021"
+authors = ["<%= config[:author] %> <<%= config[:email] %>>"]
+<%- if config[:mit] -%>
+license = "MIT"
+<%- end -%>
+publish = false
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+magnus = { version = "0.6" }
diff --git a/lib/bundler/templates/newgem/ext/newgem/extconf-c.rb.tt b/lib/bundler/templates/newgem/ext/newgem/extconf-c.rb.tt
new file mode 100644
index 0000000000..0a0c5a3d09
--- /dev/null
+++ b/lib/bundler/templates/newgem/ext/newgem/extconf-c.rb.tt
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require "mkmf"
+
+# Makes all symbols private by default to avoid unintended conflict
+# with other gems. To explicitly export symbols you can use RUBY_FUNC_EXPORTED
+# selectively, or entirely remove this flag.
+append_cflags("-fvisibility=hidden")
+
+create_makefile(<%= config[:makefile_path].inspect %>)
diff --git a/lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt b/lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt
new file mode 100644
index 0000000000..e24566a17a
--- /dev/null
+++ b/lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+require "mkmf"
+require "rb_sys/mkmf"
+
+create_rust_makefile(<%= config[:makefile_path].inspect %>)
diff --git a/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt b/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt
deleted file mode 100644
index e918063ddf..0000000000
--- a/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-require "mkmf"
-
-create_makefile(<%= config[:makefile_path].inspect %>)
diff --git a/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt b/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt
index 8177c4d202..bcd5148569 100644
--- a/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt
+++ b/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt
@@ -2,7 +2,7 @@
VALUE rb_m<%= config[:constant_array].join %>;
-void
+RUBY_FUNC_EXPORTED void
Init_<%= config[:underscored_name] %>(void)
{
rb_m<%= config[:constant_array].join %> = rb_define_module(<%= config[:constant_name].inspect %>);
diff --git a/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt b/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt
new file mode 100644
index 0000000000..ba234529a3
--- /dev/null
+++ b/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt
@@ -0,0 +1,12 @@
+use magnus::{function, prelude::*, Error, Ruby};
+
+fn hello(subject: String) -> String {
+ format!("Hello from Rust, {subject}!")
+}
+
+#[magnus::init]
+fn init(ruby: &Ruby) -> Result<(), Error> {
+ let module = ruby.<%= config[:constant_array].map {|c| "define_module(#{c.dump})?"}.join(".") %>;
+ module.define_singleton_method("hello", function!(hello, 1))?;
+ Ok(())
+}
diff --git a/lib/bundler/templates/newgem/github/workflows/main.yml.tt b/lib/bundler/templates/newgem/github/workflows/main.yml.tt
index 1ff4b58b7b..be58dd8156 100644
--- a/lib/bundler/templates/newgem/github/workflows/main.yml.tt
+++ b/lib/bundler/templates/newgem/github/workflows/main.yml.tt
@@ -18,10 +18,20 @@ jobs:
steps:
- uses: actions/checkout@v3
+<%- if config[:ext] == 'rust' -%>
+ - name: Set up Ruby & Rust
+ uses: oxidize-rb/actions/setup-ruby-and-rust@v1
+ with:
+ ruby-version: ${{ matrix.ruby }}
+ bundler-cache: true
+ cargo-cache: true
+ rubygems: '<%= ::Gem.rubygems_version %>'
+<%- else -%>
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
+<%- end -%>
- name: Run the default task
run: bundle exec rake
diff --git a/lib/bundler/templates/newgem/gitignore.tt b/lib/bundler/templates/newgem/gitignore.tt
index b1c9f9986c..9b40ba5a58 100644
--- a/lib/bundler/templates/newgem/gitignore.tt
+++ b/lib/bundler/templates/newgem/gitignore.tt
@@ -12,6 +12,9 @@
*.o
*.a
mkmf.log
+<%- if config[:ext] == 'rust' -%>
+target/
+<%- end -%>
<%- end -%>
<%- if config[:test] == "rspec" -%>
diff --git a/lib/bundler/templates/newgem/gitlab-ci.yml.tt b/lib/bundler/templates/newgem/gitlab-ci.yml.tt
index 0e71ff26a4..d2e1f33736 100644
--- a/lib/bundler/templates/newgem/gitlab-ci.yml.tt
+++ b/lib/bundler/templates/newgem/gitlab-ci.yml.tt
@@ -1,9 +1,18 @@
-image: ruby:<%= RUBY_VERSION %>
+default:
+ image: ruby:<%= RUBY_VERSION %>
-before_script:
- - gem install bundler -v <%= Bundler::VERSION %>
- - bundle install
+ before_script:
+<%- if config[:ext] == 'rust' -%>
+ - apt-get update && apt-get install -y clang
+ - gem update --system '<%= ::Gem.rubygems_version %>'
+<%- end -%>
+ - gem install bundler -v <%= Bundler::VERSION %>
+ - bundle install
example_job:
+<%- if config[:ext] == 'rust' -%>
+ variables:
+ RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN: 'true'
+<%- end -%>
script:
- bundle exec rake
diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt
index ceb2e9b28d..bb76680379 100644
--- a/lib/bundler/templates/newgem/newgem.gemspec.tt
+++ b/lib/bundler/templates/newgem/newgem.gemspec.tt
@@ -15,6 +15,9 @@ Gem::Specification.new do |spec|
spec.license = "MIT"
<%- end -%>
spec.required_ruby_version = ">= <%= config[:required_ruby_version] %>"
+<%- if config[:ext] == 'rust' -%>
+ spec.required_rubygems_version = ">= <%= config[:rust_builder_required_rubygems_version] %>"
+<%- end -%>
spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
@@ -26,15 +29,19 @@ Gem::Specification.new do |spec|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
spec.files = Dir.chdir(__dir__) do
`git ls-files -z`.split("\x0").reject do |f|
- (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
+ (File.expand_path(f) == __FILE__) ||
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
end
end
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
-<%- if config[:ext] -%>
+<%- if config[:ext] == 'c' -%>
spec.extensions = ["ext/<%= config[:underscored_name] %>/extconf.rb"]
<%- end -%>
+<%- if config[:ext] == 'rust' -%>
+ spec.extensions = ["ext/<%= config[:underscored_name] %>/Cargo.toml"]
+<%- end -%>
# Uncomment to register a new dependency of your gem
# spec.add_dependency "example-gem", "~> 1.0"
diff --git a/lib/bundler/templates/newgem/travis.yml.tt b/lib/bundler/templates/newgem/travis.yml.tt
deleted file mode 100644
index eab16addca..0000000000
--- a/lib/bundler/templates/newgem/travis.yml.tt
+++ /dev/null
@@ -1,6 +0,0 @@
----
-language: ruby
-cache: bundler
-rvm:
- - <%= RUBY_VERSION %>
-before_install: gem install bundler -v <%= Bundler::VERSION %>
diff --git a/lib/bundler/ui/rg_proxy.rb b/lib/bundler/ui/rg_proxy.rb
index ef6def225b..b17ca65f53 100644
--- a/lib/bundler/ui/rg_proxy.rb
+++ b/lib/bundler/ui/rg_proxy.rb
@@ -12,7 +12,7 @@ module Bundler
end
def say(message)
- @ui && @ui.debug(message)
+ @ui&.debug(message)
end
end
end
diff --git a/lib/bundler/ui/shell.rb b/lib/bundler/ui/shell.rb
index 384752a340..4139585c47 100644
--- a/lib/bundler/ui/shell.rb
+++ b/lib/bundler/ui/shell.rb
@@ -20,29 +20,52 @@ module Bundler
@shell.set_color(string, *color)
end
- def info(msg, newline = nil)
- tell_me(msg, nil, newline) if level("info")
+ def info(msg = nil, newline = nil)
+ return unless info?
+
+ tell_me(msg || yield, nil, newline)
end
- def confirm(msg, newline = nil)
- tell_me(msg, :green, newline) if level("confirm")
+ def confirm(msg = nil, newline = nil)
+ return unless confirm?
+
+ tell_me(msg || yield, :green, newline)
end
- def warn(msg, newline = nil, color = :yellow)
- return unless level("warn")
+ def warn(msg = nil, newline = nil, color = :yellow)
+ return unless warn?
return if @warning_history.include? msg
@warning_history << msg
- tell_err(msg, color, newline)
+ tell_err(msg || yield, color, newline)
+ end
+
+ def error(msg = nil, newline = nil, color = :red)
+ return unless error?
+
+ tell_err(msg || yield, color, newline)
+ end
+
+ def debug(msg = nil, newline = nil)
+ return unless debug?
+
+ tell_me(msg || yield, nil, newline)
+ end
+
+ def info?
+ level("info")
+ end
+
+ def confirm?
+ level("confirm")
end
- def error(msg, newline = nil, color = :red)
- return unless level("error")
- tell_err(msg, color, newline)
+ def warn?
+ level("warn")
end
- def debug(msg, newline = nil)
- tell_me(msg, nil, newline) if debug?
+ def error?
+ level("error")
end
def debug?
diff --git a/lib/bundler/ui/silent.rb b/lib/bundler/ui/silent.rb
index dca1b2ac86..fa3292bdc9 100644
--- a/lib/bundler/ui/silent.rb
+++ b/lib/bundler/ui/silent.rb
@@ -13,30 +13,46 @@ module Bundler
string
end
- def info(message, newline = nil)
+ def info(message = nil, newline = nil)
end
- def confirm(message, newline = nil)
+ def confirm(message = nil, newline = nil)
end
- def warn(message, newline = nil)
+ def warn(message = nil, newline = nil)
@warnings |= [message]
end
- def error(message, newline = nil)
+ def error(message = nil, newline = nil)
end
- def debug(message, newline = nil)
+ def debug(message = nil, newline = nil)
+ end
+
+ def confirm?
+ false
+ end
+
+ def error?
+ false
end
def debug?
false
end
+ def info?
+ false
+ end
+
def quiet?
false
end
+ def warn?
+ false
+ end
+
def ask(message)
end
diff --git a/lib/bundler/uri_normalizer.rb b/lib/bundler/uri_normalizer.rb
new file mode 100644
index 0000000000..ad08593256
--- /dev/null
+++ b/lib/bundler/uri_normalizer.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Bundler
+ module URINormalizer
+ module_function
+
+ # Normalizes uri to a consistent version, either with or without trailing
+ # slash.
+ #
+ # TODO: Currently gem sources are locked with a trailing slash, while git
+ # sources are locked without a trailing slash. This should be normalized but
+ # the inconsistency is there for now to avoid changing all lockfiles
+ # including GIT sources. We could normalize this on the next major.
+ #
+ def normalize_suffix(uri, trailing_slash: true)
+ if trailing_slash
+ uri.end_with?("/") ? uri : "#{uri}/"
+ else
+ uri.end_with?("/") ? uri.delete_suffix("/") : uri
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb
index 984c1c3dcb..455319efe3 100644
--- a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb
+++ b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb
@@ -3,7 +3,9 @@ require_relative "connection_pool/version"
class Bundler::ConnectionPool
class Error < ::RuntimeError; end
+
class PoolShuttingDownError < ::Bundler::ConnectionPool::Error; end
+
class TimeoutError < ::Timeout::Error; end
end
@@ -67,7 +69,7 @@ class Bundler::ConnectionPool
end
end
end
- alias then with
+ alias_method :then, :with
def checkout(options = {})
if ::Thread.current[@key]
diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb
index a7b1cf06a8..35d1d7cc35 100644
--- a/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb
+++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb
@@ -49,7 +49,7 @@ class Bundler::ConnectionPool::TimedStack
@resource.broadcast
end
end
- alias << push
+ alias_method :<<, :push
##
# Retrieves a connection from the stack. If a connection is available it is
@@ -74,7 +74,7 @@ class Bundler::ConnectionPool::TimedStack
return connection if connection
to_wait = deadline - current_time
- raise Bundler::ConnectionPool::TimeoutError, "Waited #{timeout} sec" if to_wait <= 0
+ raise Bundler::ConnectionPool::TimeoutError, "Waited #{timeout} sec, #{length}/#{@max} available" if to_wait <= 0
@resource.wait(@mutex, to_wait)
end
end
@@ -87,7 +87,7 @@ class Bundler::ConnectionPool::TimedStack
# +:reload+ is +true+.
def shutdown(reload: false, &block)
- raise ArgumentError, "shutdown must receive a block" unless block_given?
+ raise ArgumentError, "shutdown must receive a block" unless block
@mutex.synchronize do
@shutdown_block = block
diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb
index 880170c06b..dd796d1021 100644
--- a/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb
+++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb
@@ -30,7 +30,6 @@ class Bundler::ConnectionPool
METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
end
- # rubocop:disable Style/MethodMissingSuper
# rubocop:disable Style/MissingRespondToMissing
if ::RUBY_VERSION >= "3.0.0"
def method_missing(name, *args, **kwargs, &block)
diff --git a/lib/bundler/vendor/fileutils/lib/fileutils.rb b/lib/bundler/vendor/fileutils/lib/fileutils.rb
index 8f8faf30c8..211311c069 100644
--- a/lib/bundler/vendor/fileutils/lib/fileutils.rb
+++ b/lib/bundler/vendor/fileutils/lib/fileutils.rb
@@ -3,106 +3,184 @@
begin
require 'rbconfig'
rescue LoadError
- # for make mjit-headers
+ # for make rjit-headers
end
+# Namespace for file utility methods for copying, moving, removing, etc.
#
-# = fileutils.rb
+# == What's Here
#
-# Copyright (c) 2000-2007 Minero Aoki
+# First, what’s elsewhere. \Module \Bundler::FileUtils:
#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
+# - Inherits from {class Object}[rdoc-ref:Object].
+# - Supplements {class File}[rdoc-ref:File]
+# (but is not included or extended there).
#
-# == module Bundler::FileUtils
+# Here, module \Bundler::FileUtils provides methods that are useful for:
#
-# Namespace for several file utility methods for copying, moving, removing, etc.
+# - {Creating}[rdoc-ref:FileUtils@Creating].
+# - {Deleting}[rdoc-ref:FileUtils@Deleting].
+# - {Querying}[rdoc-ref:FileUtils@Querying].
+# - {Setting}[rdoc-ref:FileUtils@Setting].
+# - {Comparing}[rdoc-ref:FileUtils@Comparing].
+# - {Copying}[rdoc-ref:FileUtils@Copying].
+# - {Moving}[rdoc-ref:FileUtils@Moving].
+# - {Options}[rdoc-ref:FileUtils@Options].
#
-# === Module Functions
+# === Creating
#
-# require 'bundler/vendor/fileutils/lib/fileutils'
+# - ::mkdir: Creates directories.
+# - ::mkdir_p, ::makedirs, ::mkpath: Creates directories,
+# also creating ancestor directories as needed.
+# - ::link_entry: Creates a hard link.
+# - ::ln, ::link: Creates hard links.
+# - ::ln_s, ::symlink: Creates symbolic links.
+# - ::ln_sf: Creates symbolic links, overwriting if necessary.
+# - ::ln_sr: Creates symbolic links relative to targets
#
-# Bundler::FileUtils.cd(dir, **options)
-# Bundler::FileUtils.cd(dir, **options) {|dir| block }
-# Bundler::FileUtils.pwd()
-# Bundler::FileUtils.mkdir(dir, **options)
-# Bundler::FileUtils.mkdir(list, **options)
-# Bundler::FileUtils.mkdir_p(dir, **options)
-# Bundler::FileUtils.mkdir_p(list, **options)
-# Bundler::FileUtils.rmdir(dir, **options)
-# Bundler::FileUtils.rmdir(list, **options)
-# Bundler::FileUtils.ln(target, link, **options)
-# Bundler::FileUtils.ln(targets, dir, **options)
-# Bundler::FileUtils.ln_s(target, link, **options)
-# Bundler::FileUtils.ln_s(targets, dir, **options)
-# Bundler::FileUtils.ln_sf(target, link, **options)
-# Bundler::FileUtils.cp(src, dest, **options)
-# Bundler::FileUtils.cp(list, dir, **options)
-# Bundler::FileUtils.cp_r(src, dest, **options)
-# Bundler::FileUtils.cp_r(list, dir, **options)
-# Bundler::FileUtils.mv(src, dest, **options)
-# Bundler::FileUtils.mv(list, dir, **options)
-# Bundler::FileUtils.rm(list, **options)
-# Bundler::FileUtils.rm_r(list, **options)
-# Bundler::FileUtils.rm_rf(list, **options)
-# Bundler::FileUtils.install(src, dest, **options)
-# Bundler::FileUtils.chmod(mode, list, **options)
-# Bundler::FileUtils.chmod_R(mode, list, **options)
-# Bundler::FileUtils.chown(user, group, list, **options)
-# Bundler::FileUtils.chown_R(user, group, list, **options)
-# Bundler::FileUtils.touch(list, **options)
+# === Deleting
#
-# Possible <tt>options</tt> are:
+# - ::remove_dir: Removes a directory and its descendants.
+# - ::remove_entry: Removes an entry, including its descendants if it is a directory.
+# - ::remove_entry_secure: Like ::remove_entry, but removes securely.
+# - ::remove_file: Removes a file entry.
+# - ::rm, ::remove: Removes entries.
+# - ::rm_f, ::safe_unlink: Like ::rm, but removes forcibly.
+# - ::rm_r: Removes entries and their descendants.
+# - ::rm_rf, ::rmtree: Like ::rm_r, but removes forcibly.
+# - ::rmdir: Removes directories.
#
-# <tt>:force</tt> :: forced operation (rewrite files if exist, remove
-# directories if not empty, etc.);
-# <tt>:verbose</tt> :: print command to be run, in bash syntax, before
-# performing it;
-# <tt>:preserve</tt> :: preserve object's group, user and modification
-# time on copying;
-# <tt>:noop</tt> :: no changes are made (usable in combination with
-# <tt>:verbose</tt> which will print the command to run)
+# === Querying
#
-# Each method documents the options that it honours. See also ::commands,
-# ::options and ::options_of methods to introspect which command have which
-# options.
+# - ::pwd, ::getwd: Returns the path to the working directory.
+# - ::uptodate?: Returns whether a given entry is newer than given other entries.
#
-# All methods that have the concept of a "source" file or directory can take
-# either one file or a list of files in that argument. See the method
-# documentation for examples.
+# === Setting
#
-# There are some `low level' methods, which do not accept keyword arguments:
+# - ::cd, ::chdir: Sets the working directory.
+# - ::chmod: Sets permissions for an entry.
+# - ::chmod_R: Sets permissions for an entry and its descendants.
+# - ::chown: Sets the owner and group for entries.
+# - ::chown_R: Sets the owner and group for entries and their descendants.
+# - ::touch: Sets modification and access times for entries,
+# creating if necessary.
#
-# Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
-# Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true)
-# Bundler::FileUtils.copy_stream(srcstream, deststream)
-# Bundler::FileUtils.remove_entry(path, force = false)
-# Bundler::FileUtils.remove_entry_secure(path, force = false)
-# Bundler::FileUtils.remove_file(path, force = false)
-# Bundler::FileUtils.compare_file(path_a, path_b)
-# Bundler::FileUtils.compare_stream(stream_a, stream_b)
-# Bundler::FileUtils.uptodate?(file, cmp_list)
+# === Comparing
#
-# == module Bundler::FileUtils::Verbose
+# - ::compare_file, ::cmp, ::identical?: Returns whether two entries are identical.
+# - ::compare_stream: Returns whether two streams are identical.
#
-# This module has all methods of Bundler::FileUtils module, but it outputs messages
-# before acting. This equates to passing the <tt>:verbose</tt> flag to methods
-# in Bundler::FileUtils.
+# === Copying
#
-# == module Bundler::FileUtils::NoWrite
+# - ::copy_entry: Recursively copies an entry.
+# - ::copy_file: Copies an entry.
+# - ::copy_stream: Copies a stream.
+# - ::cp, ::copy: Copies files.
+# - ::cp_lr: Recursively creates hard links.
+# - ::cp_r: Recursively copies files, retaining mode, owner, and group.
+# - ::install: Recursively copies files, optionally setting mode,
+# owner, and group.
#
-# This module has all methods of Bundler::FileUtils module, but never changes
-# files/directories. This equates to passing the <tt>:noop</tt> flag to methods
-# in Bundler::FileUtils.
+# === Moving
#
-# == module Bundler::FileUtils::DryRun
+# - ::mv, ::move: Moves entries.
#
-# This module has all methods of Bundler::FileUtils module, but never changes
-# files/directories. This equates to passing the <tt>:noop</tt> and
-# <tt>:verbose</tt> flags to methods in Bundler::FileUtils.
+# === Options
+#
+# - ::collect_method: Returns the names of methods that accept a given option.
+# - ::commands: Returns the names of methods that accept options.
+# - ::have_option?: Returns whether a given method accepts a given option.
+# - ::options: Returns all option names.
+# - ::options_of: Returns the names of the options for a given method.
+#
+# == Path Arguments
+#
+# Some methods in \Bundler::FileUtils accept _path_ arguments,
+# which are interpreted as paths to filesystem entries:
+#
+# - If the argument is a string, that value is the path.
+# - If the argument has method +:to_path+, it is converted via that method.
+# - If the argument has method +:to_str+, it is converted via that method.
+#
+# == About the Examples
+#
+# Some examples here involve trees of file entries.
+# For these, we sometimes display trees using the
+# {tree command-line utility}[https://en.wikipedia.org/wiki/Tree_(command)],
+# which is a recursive directory-listing utility that produces
+# a depth-indented listing of files and directories.
+#
+# We use a helper method to launch the command and control the format:
+#
+# def tree(dirpath = '.')
+# command = "tree --noreport --charset=ascii #{dirpath}"
+# system(command)
+# end
+#
+# To illustrate:
+#
+# tree('src0')
+# # => src0
+# # |-- sub0
+# # | |-- src0.txt
+# # | `-- src1.txt
+# # `-- sub1
+# # |-- src2.txt
+# # `-- src3.txt
+#
+# == Avoiding the TOCTTOU Vulnerability
+#
+# For certain methods that recursively remove entries,
+# there is a potential vulnerability called the
+# {Time-of-check to time-of-use}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use],
+# or TOCTTOU, vulnerability that can exist when:
+#
+# - An ancestor directory of the entry at the target path is world writable;
+# such directories include <tt>/tmp</tt>.
+# - The directory tree at the target path includes:
+#
+# - A world-writable descendant directory.
+# - A symbolic link.
+#
+# To avoid that vulnerability, you can use this method to remove entries:
+#
+# - Bundler::FileUtils.remove_entry_secure: removes recursively
+# if the target path points to a directory.
+#
+# Also available are these methods,
+# each of which calls \Bundler::FileUtils.remove_entry_secure:
+#
+# - Bundler::FileUtils.rm_r with keyword argument <tt>secure: true</tt>.
+# - Bundler::FileUtils.rm_rf with keyword argument <tt>secure: true</tt>.
+#
+# Finally, this method for moving entries calls \Bundler::FileUtils.remove_entry_secure
+# if the source and destination are on different file systems
+# (which means that the "move" is really a copy and remove):
+#
+# - Bundler::FileUtils.mv with keyword argument <tt>secure: true</tt>.
+#
+# \Method \Bundler::FileUtils.remove_entry_secure removes securely
+# by applying a special pre-process:
+#
+# - If the target path points to a directory, this method uses methods
+# {File#chown}[rdoc-ref:File#chown]
+# and {File#chmod}[rdoc-ref:File#chmod]
+# in removing directories.
+# - The owner of the target directory should be either the current process
+# or the super user (root).
+#
+# WARNING: You must ensure that *ALL* parent directories cannot be
+# moved by other untrusted users. For example, parent directories
+# should not be owned by untrusted users, and should not be world
+# writable except when the sticky bit is set.
+#
+# For details of this security vulnerability, see Perl cases:
+#
+# - {CVE-2005-0448}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448].
+# - {CVE-2004-0452}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452].
#
module Bundler::FileUtils
- VERSION = "1.4.1"
+ VERSION = "1.7.0"
def self.private_module_function(name) #:nodoc:
module_function name
@@ -110,7 +188,13 @@ module Bundler::FileUtils
end
#
- # Returns the name of the current directory.
+ # Returns a string containing the path to the current directory:
+ #
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
+ #
+ # Bundler::FileUtils.getwd is an alias for Bundler::FileUtils.pwd.
+ #
+ # Related: Bundler::FileUtils.cd.
#
def pwd
Dir.pwd
@@ -120,19 +204,40 @@ module Bundler::FileUtils
alias getwd pwd
module_function :getwd
+ # Changes the working directory to the given +dir+, which
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments]:
+ #
+ # With no block given,
+ # changes the current directory to the directory at +dir+; returns zero:
+ #
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
+ # Bundler::FileUtils.cd('..')
+ # Bundler::FileUtils.pwd # => "/rdoc"
+ # Bundler::FileUtils.cd('fileutils')
+ #
+ # With a block given, changes the current directory to the directory
+ # at +dir+, calls the block with argument +dir+,
+ # and restores the original current directory; returns the block's value:
#
- # Changes the current directory to the directory +dir+.
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
+ # Bundler::FileUtils.cd('..') { |arg| [arg, Bundler::FileUtils.pwd] } # => ["..", "/rdoc"]
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
#
- # If this method is called with block, resumes to the previous
- # working directory after the block execution has finished.
+ # Keyword arguments:
#
- # Bundler::FileUtils.cd('/') # change directory
+ # - <tt>verbose: true</tt> - prints an equivalent command:
#
- # Bundler::FileUtils.cd('/', verbose: true) # change directory and report it
+ # Bundler::FileUtils.cd('..')
+ # Bundler::FileUtils.cd('fileutils')
#
- # Bundler::FileUtils.cd('/') do # change directory
- # # ... # do something
- # end # return to original directory
+ # Output:
+ #
+ # cd ..
+ # cd fileutils
+ #
+ # Bundler::FileUtils.chdir is an alias for Bundler::FileUtils.cd.
+ #
+ # Related: Bundler::FileUtils.pwd.
#
def cd(dir, verbose: nil, &block) # :yield: dir
fu_output_message "cd #{dir}" if verbose
@@ -146,11 +251,19 @@ module Bundler::FileUtils
module_function :chdir
#
- # Returns true if +new+ is newer than all +old_list+.
- # Non-existent files are older than any file.
+ # Returns +true+ if the file at path +new+
+ # is newer than all the files at paths in array +old_list+;
+ # +false+ otherwise.
+ #
+ # Argument +new+ and the elements of +old_list+
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments]:
#
- # Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
- # system 'make hello.o'
+ # Bundler::FileUtils.uptodate?('Rakefile', ['Gemfile', 'README.md']) # => true
+ # Bundler::FileUtils.uptodate?('Gemfile', ['Rakefile', 'README.md']) # => false
+ #
+ # A non-existent file is considered to be infinitely old.
+ #
+ # Related: Bundler::FileUtils.touch.
#
def uptodate?(new, old_list)
return false unless File.exist?(new)
@@ -170,12 +283,39 @@ module Bundler::FileUtils
private_module_function :remove_trailing_slash
#
- # Creates one or more directories.
+ # Creates directories at the paths in the given +list+
+ # (a single path or an array of paths);
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # With no keyword arguments, creates a directory at each +path+ in +list+
+ # by calling: <tt>Dir.mkdir(path, mode)</tt>;
+ # see {Dir.mkdir}[rdoc-ref:Dir.mkdir]:
+ #
+ # Bundler::FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"]
+ # Bundler::FileUtils.mkdir('tmp4') # => ["tmp4"]
+ #
+ # Keyword arguments:
+ #
+ # - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
+ # see {File.chmod}[rdoc-ref:File.chmod].
+ # - <tt>noop: true</tt> - does not create directories.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.mkdir(%w[tmp0 tmp1], verbose: true)
+ # Bundler::FileUtils.mkdir(%w[tmp2 tmp3], mode: 0700, verbose: true)
+ #
+ # Output:
#
- # Bundler::FileUtils.mkdir 'test'
- # Bundler::FileUtils.mkdir %w(tmp data)
- # Bundler::FileUtils.mkdir 'notexist', noop: true # Does not really create.
- # Bundler::FileUtils.mkdir 'tmp', mode: 0700
+ # mkdir tmp0 tmp1
+ # mkdir -m 700 tmp2 tmp3
+ #
+ # Raises an exception if any path points to an existing
+ # file or directory, or if for any reason a directory cannot be created.
+ #
+ # Related: Bundler::FileUtils.mkdir_p.
#
def mkdir(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
@@ -189,40 +329,56 @@ module Bundler::FileUtils
module_function :mkdir
#
- # Creates a directory and all its parent directories.
- # For example,
+ # Creates directories at the paths in the given +list+
+ # (a single path or an array of paths),
+ # also creating ancestor directories as needed;
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # With no keyword arguments, creates a directory at each +path+ in +list+,
+ # along with any needed ancestor directories,
+ # by calling: <tt>Dir.mkdir(path, mode)</tt>;
+ # see {Dir.mkdir}[rdoc-ref:Dir.mkdir]:
+ #
+ # Bundler::FileUtils.mkdir_p(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
+ # Bundler::FileUtils.mkdir_p('tmp4/tmp5') # => ["tmp4/tmp5"]
+ #
+ # Keyword arguments:
+ #
+ # - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
+ # see {File.chmod}[rdoc-ref:File.chmod].
+ # - <tt>noop: true</tt> - does not create directories.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.mkdir_p(%w[tmp0 tmp1], verbose: true)
+ # Bundler::FileUtils.mkdir_p(%w[tmp2 tmp3], mode: 0700, verbose: true)
#
- # Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby'
+ # Output:
#
- # causes to make following directories, if they do not exist.
+ # mkdir -p tmp0 tmp1
+ # mkdir -p -m 700 tmp2 tmp3
#
- # * /usr
- # * /usr/local
- # * /usr/local/lib
- # * /usr/local/lib/ruby
+ # Raises an exception if for any reason a directory cannot be created.
#
- # You can pass several directories at a time in a list.
+ # Bundler::FileUtils.mkpath and Bundler::FileUtils.makedirs are aliases for Bundler::FileUtils.mkdir_p.
+ #
+ # Related: Bundler::FileUtils.mkdir.
#
def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
return *list if noop
- list.map {|path| remove_trailing_slash(path)}.each do |path|
- # optimize for the most common case
- begin
- fu_mkdir path, mode
- next
- rescue SystemCallError
- next if File.directory?(path)
- end
+ list.each do |item|
+ path = remove_trailing_slash(item)
stack = []
- until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
+ until File.directory?(path) || File.dirname(path) == path
stack.push path
path = File.dirname(path)
end
- stack.pop # root directory should exist
stack.reverse_each do |dir|
begin
fu_mkdir dir, mode
@@ -253,12 +409,39 @@ module Bundler::FileUtils
private_module_function :fu_mkdir
#
- # Removes one or more directories.
+ # Removes directories at the paths in the given +list+
+ # (a single path or an array of paths);
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # With no keyword arguments, removes the directory at each +path+ in +list+,
+ # by calling: <tt>Dir.rmdir(path)</tt>;
+ # see {Dir.rmdir}[rdoc-ref:Dir.rmdir]:
+ #
+ # Bundler::FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
+ # Bundler::FileUtils.rmdir('tmp4/tmp5') # => ["tmp4/tmp5"]
+ #
+ # Keyword arguments:
+ #
+ # - <tt>parents: true</tt> - removes successive ancestor directories
+ # if empty.
+ # - <tt>noop: true</tt> - does not remove directories.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
#
- # Bundler::FileUtils.rmdir 'somedir'
- # Bundler::FileUtils.rmdir %w(somedir anydir otherdir)
- # # Does not really remove directory; outputs message.
- # Bundler::FileUtils.rmdir 'somedir', verbose: true, noop: true
+ # Bundler::FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3], parents: true, verbose: true)
+ # Bundler::FileUtils.rmdir('tmp4/tmp5', parents: true, verbose: true)
+ #
+ # Output:
+ #
+ # rmdir -p tmp0/tmp1 tmp2/tmp3
+ # rmdir -p tmp4/tmp5
+ #
+ # Raises an exception if a directory does not exist
+ # or if for any reason a directory cannot be removed.
+ #
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rmdir(list, parents: nil, noop: nil, verbose: nil)
list = fu_list(list)
@@ -279,26 +462,62 @@ module Bundler::FileUtils
end
module_function :rmdir
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link].
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # When +src+ is the path to an existing file
+ # and +dest+ is the path to a non-existent file,
+ # creates a hard link at +dest+ pointing to +src+; returns zero:
+ #
+ # Dir.children('tmp0/') # => ["t.txt"]
+ # Dir.children('tmp1/') # => []
+ # Bundler::FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk') # => 0
+ # Dir.children('tmp1/') # => ["t.lnk"]
+ #
+ # When +src+ is the path to an existing file
+ # and +dest+ is the path to an existing directory,
+ # creates a hard link at <tt>dest/src</tt> pointing to +src+; returns zero:
#
- # :call-seq:
- # Bundler::FileUtils.ln(target, link, force: nil, noop: nil, verbose: nil)
- # Bundler::FileUtils.ln(target, dir, force: nil, noop: nil, verbose: nil)
- # Bundler::FileUtils.ln(targets, dir, force: nil, noop: nil, verbose: nil)
+ # Dir.children('tmp2') # => ["t.dat"]
+ # Dir.children('tmp3') # => []
+ # Bundler::FileUtils.ln('tmp2/t.dat', 'tmp3') # => 0
+ # Dir.children('tmp3') # => ["t.dat"]
#
- # In the first form, creates a hard link +link+ which points to +target+.
- # If +link+ already exists, raises Errno::EEXIST.
- # But if the +force+ option is set, overwrites +link+.
+ # When +src+ is an array of paths to existing files
+ # and +dest+ is the path to an existing directory,
+ # then for each path +target+ in +src+,
+ # creates a hard link at <tt>dest/target</tt> pointing to +target+;
+ # returns +src+:
#
- # Bundler::FileUtils.ln 'gcc', 'cc', verbose: true
- # Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
+ # Dir.children('tmp4/') # => []
+ # Bundler::FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/') # => ["tmp0/t.txt", "tmp2/t.dat"]
+ # Dir.children('tmp4/') # => ["t.dat", "t.txt"]
#
- # In the second form, creates a link +dir/target+ pointing to +target+.
- # In the third form, creates several hard links in the directory +dir+,
- # pointing to each item in +targets+.
- # If +dir+ is not a directory, raises Errno::ENOTDIR.
+ # Keyword arguments:
#
- # Bundler::FileUtils.cd '/sbin'
- # Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
+ # - <tt>force: true</tt> - overwrites +dest+ if it exists.
+ # - <tt>noop: true</tt> - does not create links.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk', verbose: true)
+ # Bundler::FileUtils.ln('tmp2/t.dat', 'tmp3', verbose: true)
+ # Bundler::FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/', verbose: true)
+ #
+ # Output:
+ #
+ # ln tmp0/t.txt tmp1/t.lnk
+ # ln tmp2/t.dat tmp3
+ # ln tmp0/t.txt tmp2/t.dat tmp4/
+ #
+ # Raises an exception if +dest+ is the path to an existing file
+ # and keyword argument +force+ is not +true+.
+ #
+ # Bundler::FileUtils#link is an alias for Bundler::FileUtils#ln.
+ #
+ # Related: Bundler::FileUtils.link_entry (has different options).
#
def ln(src, dest, force: nil, noop: nil, verbose: nil)
fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -313,28 +532,103 @@ module Bundler::FileUtils
alias link ln
module_function :link
- #
- # Hard link +src+ to +dest+. If +src+ is a directory, this method links
- # all its contents recursively. If +dest+ is a directory, links
- # +src+ to +dest/src+.
- #
- # +src+ can be a list of files.
- #
- # If +dereference_root+ is true, this method dereference tree root.
- #
- # If +remove_destination+ is true, this method removes each destination file before copy.
- #
- # Bundler::FileUtils.rm_r site_ruby + '/mylib', force: true
- # Bundler::FileUtils.cp_lr 'lib/', site_ruby + '/mylib'
- #
- # # Examples of linking several files to target directory.
- # Bundler::FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail'
- # Bundler::FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', noop: true, verbose: true
- #
- # # If you want to link all contents of a directory instead of the
- # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
- # # use the following code.
- # Bundler::FileUtils.cp_lr 'src/.', 'dest' # cp_lr('src', 'dest') makes dest/src, but this doesn't.
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link].
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # If +src+ is the path to a directory and +dest+ does not exist,
+ # creates links +dest+ and descendents pointing to +src+ and its descendents:
+ #
+ # tree('src0')
+ # # => src0
+ # # |-- sub0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- sub1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # File.exist?('dest0') # => false
+ # Bundler::FileUtils.cp_lr('src0', 'dest0')
+ # tree('dest0')
+ # # => dest0
+ # # |-- sub0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- sub1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ #
+ # If +src+ and +dest+ are both paths to directories,
+ # creates links <tt>dest/src</tt> and descendents
+ # pointing to +src+ and its descendents:
+ #
+ # tree('src1')
+ # # => src1
+ # # |-- sub0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- sub1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # Bundler::FileUtils.mkdir('dest1')
+ # Bundler::FileUtils.cp_lr('src1', 'dest1')
+ # tree('dest1')
+ # # => dest1
+ # # `-- src1
+ # # |-- sub0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- sub1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ #
+ # If +src+ is an array of paths to entries and +dest+ is the path to a directory,
+ # for each path +filepath+ in +src+, creates a link at <tt>dest/filepath</tt>
+ # pointing to that path:
+ #
+ # tree('src2')
+ # # => src2
+ # # |-- sub0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- sub1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # Bundler::FileUtils.mkdir('dest2')
+ # Bundler::FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2')
+ # tree('dest2')
+ # # => dest2
+ # # |-- sub0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- sub1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ #
+ # Keyword arguments:
+ #
+ # - <tt>dereference_root: false</tt> - if +src+ is a symbolic link,
+ # does not dereference it.
+ # - <tt>noop: true</tt> - does not create links.
+ # - <tt>remove_destination: true</tt> - removes +dest+ before creating links.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.cp_lr('src0', 'dest0', noop: true, verbose: true)
+ # Bundler::FileUtils.cp_lr('src1', 'dest1', noop: true, verbose: true)
+ # Bundler::FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # cp -lr src0 dest0
+ # cp -lr src1 dest1
+ # cp -lr src2/sub0 src2/sub1 dest2
+ #
+ # Raises an exception if +dest+ is the path to an existing file or directory
+ # and keyword argument <tt>remove_destination: true</tt> is not given.
+ #
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def cp_lr(src, dest, noop: nil, verbose: nil,
dereference_root: true, remove_destination: false)
@@ -346,27 +640,81 @@ module Bundler::FileUtils
end
module_function :cp_lr
+ # Creates {symbolic links}[https://en.wikipedia.org/wiki/Symbolic_link].
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # If +src+ is the path to an existing file:
+ #
+ # - When +dest+ is the path to a non-existent file,
+ # creates a symbolic link at +dest+ pointing to +src+:
+ #
+ # Bundler::FileUtils.touch('src0.txt')
+ # File.exist?('dest0.txt') # => false
+ # Bundler::FileUtils.ln_s('src0.txt', 'dest0.txt')
+ # File.symlink?('dest0.txt') # => true
#
- # :call-seq:
- # Bundler::FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil)
- # Bundler::FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil)
- # Bundler::FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil)
+ # - When +dest+ is the path to an existing file,
+ # creates a symbolic link at +dest+ pointing to +src+
+ # if and only if keyword argument <tt>force: true</tt> is given
+ # (raises an exception otherwise):
#
- # In the first form, creates a symbolic link +link+ which points to +target+.
- # If +link+ already exists, raises Errno::EEXIST.
- # But if the <tt>force</tt> option is set, overwrites +link+.
+ # Bundler::FileUtils.touch('src1.txt')
+ # Bundler::FileUtils.touch('dest1.txt')
+ # Bundler::FileUtils.ln_s('src1.txt', 'dest1.txt', force: true)
+ # FileTest.symlink?('dest1.txt') # => true
#
- # Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
- # Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
+ # Bundler::FileUtils.ln_s('src1.txt', 'dest1.txt') # Raises Errno::EEXIST.
#
- # In the second form, creates a link +dir/target+ pointing to +target+.
- # In the third form, creates several symbolic links in the directory +dir+,
- # pointing to each item in +targets+.
- # If +dir+ is not a directory, raises Errno::ENOTDIR.
+ # If +dest+ is the path to a directory,
+ # creates a symbolic link at <tt>dest/src</tt> pointing to +src+:
#
- # Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
+ # Bundler::FileUtils.touch('src2.txt')
+ # Bundler::FileUtils.mkdir('destdir2')
+ # Bundler::FileUtils.ln_s('src2.txt', 'destdir2')
+ # File.symlink?('destdir2/src2.txt') # => true
#
- def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
+ # If +src+ is an array of paths to existing files and +dest+ is a directory,
+ # for each child +child+ in +src+ creates a symbolic link <tt>dest/child</tt>
+ # pointing to +child+:
+ #
+ # Bundler::FileUtils.mkdir('srcdir3')
+ # Bundler::FileUtils.touch('srcdir3/src0.txt')
+ # Bundler::FileUtils.touch('srcdir3/src1.txt')
+ # Bundler::FileUtils.mkdir('destdir3')
+ # Bundler::FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3')
+ # File.symlink?('destdir3/src0.txt') # => true
+ # File.symlink?('destdir3/src1.txt') # => true
+ #
+ # Keyword arguments:
+ #
+ # - <tt>force: true</tt> - overwrites +dest+ if it exists.
+ # - <tt>relative: false</tt> - create links relative to +dest+.
+ # - <tt>noop: true</tt> - does not create links.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.ln_s('src0.txt', 'dest0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.ln_s('src1.txt', 'destdir1', noop: true, verbose: true)
+ # Bundler::FileUtils.ln_s('src2.txt', 'dest2.txt', force: true, noop: true, verbose: true)
+ # Bundler::FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # ln -s src0.txt dest0.txt
+ # ln -s src1.txt destdir1
+ # ln -sf src2.txt dest2.txt
+ # ln -s srcdir3/src0.txt srcdir3/src1.txt destdir3
+ #
+ # Bundler::FileUtils.symlink is an alias for Bundler::FileUtils.ln_s.
+ #
+ # Related: Bundler::FileUtils.ln_sf.
+ #
+ def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil)
+ if relative
+ return ln_sr(src, dest, force: force, noop: noop, verbose: verbose)
+ end
fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest0(src, dest) do |s,d|
@@ -379,29 +727,95 @@ module Bundler::FileUtils
alias symlink ln_s
module_function :symlink
- #
- # :call-seq:
- # Bundler::FileUtils.ln_sf(*args)
- #
- # Same as
- #
- # Bundler::FileUtils.ln_s(*args, force: true)
+ # Like Bundler::FileUtils.ln_s, but always with keyword argument <tt>force: true</tt> given.
#
def ln_sf(src, dest, noop: nil, verbose: nil)
ln_s src, dest, force: true, noop: noop, verbose: verbose
end
module_function :ln_sf
+ # Like Bundler::FileUtils.ln_s, but create links relative to +dest+.
+ #
+ def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil)
+ options = "#{force ? 'f' : ''}#{target_directory ? '' : 'T'}"
+ dest = File.path(dest)
+ srcs = Array(src)
+ link = proc do |s, target_dir_p = true|
+ s = File.path(s)
+ if target_dir_p
+ d = File.join(destdirs = dest, File.basename(s))
+ else
+ destdirs = File.dirname(d = dest)
+ end
+ destdirs = fu_split_path(File.realpath(destdirs))
+ if fu_starting_path?(s)
+ srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s)))
+ base = fu_relative_components_from(srcdirs, destdirs)
+ s = File.join(*base)
+ else
+ srcdirs = fu_clean_components(*fu_split_path(s))
+ base = fu_relative_components_from(fu_split_path(Dir.pwd), destdirs)
+ while srcdirs.first&. == ".." and base.last&.!=("..") and !fu_starting_path?(base.last)
+ srcdirs.shift
+ base.pop
+ end
+ s = File.join(*base, *srcdirs)
+ end
+ fu_output_message "ln -s#{options} #{s} #{d}" if verbose
+ next if noop
+ remove_file d, true if force
+ File.symlink s, d
+ end
+ case srcs.size
+ when 0
+ when 1
+ link[srcs[0], target_directory && File.directory?(dest)]
+ else
+ srcs.each(&link)
+ end
+ end
+ module_function :ln_sr
+
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link]; returns +nil+.
#
- # Hard links a file system entry +src+ to +dest+.
- # If +src+ is a directory, this method links its contents recursively.
+ # Arguments +src+ and +dest+
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
#
- # Both of +src+ and +dest+ must be a path name.
- # +src+ must exist, +dest+ must not exist.
+ # If +src+ is the path to a file and +dest+ does not exist,
+ # creates a hard link at +dest+ pointing to +src+:
#
- # If +dereference_root+ is true, this method dereferences the tree root.
+ # Bundler::FileUtils.touch('src0.txt')
+ # File.exist?('dest0.txt') # => false
+ # Bundler::FileUtils.link_entry('src0.txt', 'dest0.txt')
+ # File.file?('dest0.txt') # => true
#
- # If +remove_destination+ is true, this method removes each destination file before copy.
+ # If +src+ is the path to a directory and +dest+ does not exist,
+ # recursively creates hard links at +dest+ pointing to paths in +src+:
+ #
+ # Bundler::FileUtils.mkdir_p(['src1/dir0', 'src1/dir1'])
+ # src_file_paths = [
+ # 'src1/dir0/t0.txt',
+ # 'src1/dir0/t1.txt',
+ # 'src1/dir1/t2.txt',
+ # 'src1/dir1/t3.txt',
+ # ]
+ # Bundler::FileUtils.touch(src_file_paths)
+ # File.directory?('dest1') # => true
+ # Bundler::FileUtils.link_entry('src1', 'dest1')
+ # File.file?('dest1/dir0/t0.txt') # => true
+ # File.file?('dest1/dir0/t1.txt') # => true
+ # File.file?('dest1/dir1/t2.txt') # => true
+ # File.file?('dest1/dir1/t3.txt') # => true
+ #
+ # Keyword arguments:
+ #
+ # - <tt>dereference_root: true</tt> - dereferences +src+ if it is a symbolic link.
+ # - <tt>remove_destination: true</tt> - removes +dest+ before creating links.
+ #
+ # Raises an exception if +dest+ is the path to an existing file or directory
+ # and keyword argument <tt>remove_destination: true</tt> is not given.
+ #
+ # Related: Bundler::FileUtils.ln (has different options).
#
def link_entry(src, dest, dereference_root = false, remove_destination = false)
Entry_.new(src, nil, dereference_root).traverse do |ent|
@@ -412,16 +826,59 @@ module Bundler::FileUtils
end
module_function :link_entry
+ # Copies files.
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # If +src+ is the path to a file and +dest+ is not the path to a directory,
+ # copies +src+ to +dest+:
+ #
+ # Bundler::FileUtils.touch('src0.txt')
+ # File.exist?('dest0.txt') # => false
+ # Bundler::FileUtils.cp('src0.txt', 'dest0.txt')
+ # File.file?('dest0.txt') # => true
#
- # Copies a file content +src+ to +dest+. If +dest+ is a directory,
- # copies +src+ to +dest/src+.
+ # If +src+ is the path to a file and +dest+ is the path to a directory,
+ # copies +src+ to <tt>dest/src</tt>:
#
- # If +src+ is a list of files, then +dest+ must be a directory.
+ # Bundler::FileUtils.touch('src1.txt')
+ # Bundler::FileUtils.mkdir('dest1')
+ # Bundler::FileUtils.cp('src1.txt', 'dest1')
+ # File.file?('dest1/src1.txt') # => true
#
- # Bundler::FileUtils.cp 'eval.c', 'eval.c.org'
- # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
- # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', verbose: true
- # Bundler::FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
+ # If +src+ is an array of paths to files and +dest+ is the path to a directory,
+ # copies from each +src+ to +dest+:
+ #
+ # src_file_paths = ['src2.txt', 'src2.dat']
+ # Bundler::FileUtils.touch(src_file_paths)
+ # Bundler::FileUtils.mkdir('dest2')
+ # Bundler::FileUtils.cp(src_file_paths, 'dest2')
+ # File.file?('dest2/src2.txt') # => true
+ # File.file?('dest2/src2.dat') # => true
+ #
+ # Keyword arguments:
+ #
+ # - <tt>preserve: true</tt> - preserves file times.
+ # - <tt>noop: true</tt> - does not copy files.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.cp('src0.txt', 'dest0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.cp('src1.txt', 'dest1', noop: true, verbose: true)
+ # Bundler::FileUtils.cp(src_file_paths, 'dest2', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # cp src0.txt dest0.txt
+ # cp src1.txt dest1
+ # cp src2.txt src2.dat dest2
+ #
+ # Raises an exception if +src+ is a directory.
+ #
+ # Bundler::FileUtils.copy is an alias for Bundler::FileUtils.cp.
+ #
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -435,30 +892,105 @@ module Bundler::FileUtils
alias copy cp
module_function :copy
- #
- # Copies +src+ to +dest+. If +src+ is a directory, this method copies
- # all its contents recursively. If +dest+ is a directory, copies
- # +src+ to +dest/src+.
- #
- # +src+ can be a list of files.
- #
- # If +dereference_root+ is true, this method dereference tree root.
- #
- # If +remove_destination+ is true, this method removes each destination file before copy.
- #
- # # Installing Ruby library "mylib" under the site_ruby
- # Bundler::FileUtils.rm_r site_ruby + '/mylib', force: true
- # Bundler::FileUtils.cp_r 'lib/', site_ruby + '/mylib'
- #
- # # Examples of copying several files to target directory.
- # Bundler::FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
- # Bundler::FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', noop: true, verbose: true
- #
- # # If you want to copy all contents of a directory instead of the
- # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
- # # use following code.
- # Bundler::FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
- # # but this doesn't.
+ # Recursively copies files.
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # The mode, owner, and group are retained in the copy;
+ # to change those, use Bundler::FileUtils.install instead.
+ #
+ # If +src+ is the path to a file and +dest+ is not the path to a directory,
+ # copies +src+ to +dest+:
+ #
+ # Bundler::FileUtils.touch('src0.txt')
+ # File.exist?('dest0.txt') # => false
+ # Bundler::FileUtils.cp_r('src0.txt', 'dest0.txt')
+ # File.file?('dest0.txt') # => true
+ #
+ # If +src+ is the path to a file and +dest+ is the path to a directory,
+ # copies +src+ to <tt>dest/src</tt>:
+ #
+ # Bundler::FileUtils.touch('src1.txt')
+ # Bundler::FileUtils.mkdir('dest1')
+ # Bundler::FileUtils.cp_r('src1.txt', 'dest1')
+ # File.file?('dest1/src1.txt') # => true
+ #
+ # If +src+ is the path to a directory and +dest+ does not exist,
+ # recursively copies +src+ to +dest+:
+ #
+ # tree('src2')
+ # # => src2
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # Bundler::FileUtils.exist?('dest2') # => false
+ # Bundler::FileUtils.cp_r('src2', 'dest2')
+ # tree('dest2')
+ # # => dest2
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ #
+ # If +src+ and +dest+ are paths to directories,
+ # recursively copies +src+ to <tt>dest/src</tt>:
+ #
+ # tree('src3')
+ # # => src3
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # Bundler::FileUtils.mkdir('dest3')
+ # Bundler::FileUtils.cp_r('src3', 'dest3')
+ # tree('dest3')
+ # # => dest3
+ # # `-- src3
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ #
+ # If +src+ is an array of paths and +dest+ is a directory,
+ # recursively copies from each path in +src+ to +dest+;
+ # the paths in +src+ may point to files and/or directories.
+ #
+ # Keyword arguments:
+ #
+ # - <tt>dereference_root: false</tt> - if +src+ is a symbolic link,
+ # does not dereference it.
+ # - <tt>noop: true</tt> - does not copy files.
+ # - <tt>preserve: true</tt> - preserves file times.
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.cp_r('src0.txt', 'dest0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.cp_r('src1.txt', 'dest1', noop: true, verbose: true)
+ # Bundler::FileUtils.cp_r('src2', 'dest2', noop: true, verbose: true)
+ # Bundler::FileUtils.cp_r('src3', 'dest3', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # cp -r src0.txt dest0.txt
+ # cp -r src1.txt dest1
+ # cp -r src2 dest2
+ # cp -r src3 dest3
+ #
+ # Raises an exception of +src+ is the path to a directory
+ # and +dest+ is the path to a file.
+ #
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
dereference_root: true, remove_destination: nil)
@@ -470,21 +1002,50 @@ module Bundler::FileUtils
end
module_function :cp_r
+ # Recursively copies files from +src+ to +dest+.
+ #
+ # Arguments +src+ and +dest+
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
#
- # Copies a file system entry +src+ to +dest+.
- # If +src+ is a directory, this method copies its contents recursively.
- # This method preserves file types, c.f. symlink, directory...
- # (FIFO, device files and etc. are not supported yet)
+ # If +src+ is the path to a file, copies +src+ to +dest+:
#
- # Both of +src+ and +dest+ must be a path name.
- # +src+ must exist, +dest+ must not exist.
+ # Bundler::FileUtils.touch('src0.txt')
+ # File.exist?('dest0.txt') # => false
+ # Bundler::FileUtils.copy_entry('src0.txt', 'dest0.txt')
+ # File.file?('dest0.txt') # => true
#
- # If +preserve+ is true, this method preserves owner, group, and
- # modified time. Permissions are copied regardless +preserve+.
+ # If +src+ is a directory, recursively copies +src+ to +dest+:
#
- # If +dereference_root+ is true, this method dereference tree root.
+ # tree('src1')
+ # # => src1
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # Bundler::FileUtils.copy_entry('src1', 'dest1')
+ # tree('dest1')
+ # # => dest1
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
#
- # If +remove_destination+ is true, this method removes each destination file before copy.
+ # The recursive copying preserves file types for regular files,
+ # directories, and symbolic links;
+ # other file types (FIFO streams, device files, etc.) are not supported.
+ #
+ # Keyword arguments:
+ #
+ # - <tt>dereference_root: true</tt> - if +src+ is a symbolic link,
+ # follows the link.
+ # - <tt>preserve: true</tt> - preserves file times.
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
+ #
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
if dereference_root
@@ -502,9 +1063,25 @@ module Bundler::FileUtils
end
module_function :copy_entry
+ # Copies file from +src+ to +dest+, which should not be directories.
+ #
+ # Arguments +src+ and +dest+
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # Examples:
#
- # Copies file contents of +src+ to +dest+.
- # Both of +src+ and +dest+ must be a path name.
+ # Bundler::FileUtils.touch('src0.txt')
+ # Bundler::FileUtils.copy_file('src0.txt', 'dest0.txt')
+ # File.file?('dest0.txt') # => true
+ #
+ # Keyword arguments:
+ #
+ # - <tt>dereference: false</tt> - if +src+ is a symbolic link,
+ # does not follow the link.
+ # - <tt>preserve: true</tt> - preserves file times.
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
+ #
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def copy_file(src, dest, preserve = false, dereference = true)
ent = Entry_.new(src, nil, dereference)
@@ -513,25 +1090,81 @@ module Bundler::FileUtils
end
module_function :copy_file
+ # Copies \IO stream +src+ to \IO stream +dest+ via
+ # {IO.copy_stream}[rdoc-ref:IO.copy_stream].
#
- # Copies stream +src+ to +dest+.
- # +src+ must respond to #read(n) and
- # +dest+ must respond to #write(str).
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def copy_stream(src, dest)
IO.copy_stream(src, dest)
end
module_function :copy_stream
- #
- # Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different
- # disk partition, the file is copied then the original file is removed.
- #
- # Bundler::FileUtils.mv 'badname.rb', 'goodname.rb'
- # Bundler::FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', force: true # no error
- #
- # Bundler::FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
- # Bundler::FileUtils.mv Dir.glob('test*.rb'), 'test', noop: true, verbose: true
+ # Moves entries.
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # If +src+ and +dest+ are on different file systems,
+ # first copies, then removes +src+.
+ #
+ # May cause a local vulnerability if not called with keyword argument
+ # <tt>secure: true</tt>;
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
+ #
+ # If +src+ is the path to a single file or directory and +dest+ does not exist,
+ # moves +src+ to +dest+:
+ #
+ # tree('src0')
+ # # => src0
+ # # |-- src0.txt
+ # # `-- src1.txt
+ # File.exist?('dest0') # => false
+ # Bundler::FileUtils.mv('src0', 'dest0')
+ # File.exist?('src0') # => false
+ # tree('dest0')
+ # # => dest0
+ # # |-- src0.txt
+ # # `-- src1.txt
+ #
+ # If +src+ is an array of paths to files and directories
+ # and +dest+ is the path to a directory,
+ # copies from each path in the array to +dest+:
+ #
+ # File.file?('src1.txt') # => true
+ # tree('src1')
+ # # => src1
+ # # |-- src.dat
+ # # `-- src.txt
+ # Dir.empty?('dest1') # => true
+ # Bundler::FileUtils.mv(['src1.txt', 'src1'], 'dest1')
+ # tree('dest1')
+ # # => dest1
+ # # |-- src1
+ # # | |-- src.dat
+ # # | `-- src.txt
+ # # `-- src1.txt
+ #
+ # Keyword arguments:
+ #
+ # - <tt>force: true</tt> - if the move includes removing +src+
+ # (that is, if +src+ and +dest+ are on different file systems),
+ # ignores raised exceptions of StandardError and its descendants.
+ # - <tt>noop: true</tt> - does not move files.
+ # - <tt>secure: true</tt> - removes +src+ securely;
+ # see details at Bundler::FileUtils.remove_entry_secure.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.mv('src0', 'dest0', noop: true, verbose: true)
+ # Bundler::FileUtils.mv(['src1.txt', 'src1'], 'dest1', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # mv src0 dest0
+ # mv src1.txt src1 dest1
+ #
+ # Bundler::FileUtils.move is an alias for Bundler::FileUtils.mv.
#
def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -565,13 +1198,34 @@ module Bundler::FileUtils
alias move mv
module_function :move
+ # Removes entries at the paths in the given +list+
+ # (a single path or an array of paths)
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # With no keyword arguments, removes files at the paths given in +list+:
+ #
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
+ # Bundler::FileUtils.rm(['src0.dat', 'src0.txt']) # => ["src0.dat", "src0.txt"]
+ #
+ # Keyword arguments:
+ #
+ # - <tt>force: true</tt> - ignores raised exceptions of StandardError
+ # and its descendants.
+ # - <tt>noop: true</tt> - does not remove files; returns +nil+.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.rm(['src0.dat', 'src0.txt'], noop: true, verbose: true)
#
- # Remove file(s) specified in +list+. This method cannot remove directories.
- # All StandardErrors are ignored when the :force option is set.
+ # Output:
#
- # Bundler::FileUtils.rm %w( junk.txt dust.txt )
- # Bundler::FileUtils.rm Dir.glob('*.so')
- # Bundler::FileUtils.rm 'NotExistFile', force: true # never raises exception
+ # rm src0.dat src0.txt
+ #
+ # Bundler::FileUtils.remove is an alias for Bundler::FileUtils.rm.
+ #
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm(list, force: nil, noop: nil, verbose: nil)
list = fu_list(list)
@@ -587,10 +1241,18 @@ module Bundler::FileUtils
alias remove rm
module_function :remove
+ # Equivalent to:
+ #
+ # Bundler::FileUtils.rm(list, force: true, **kwargs)
+ #
+ # Argument +list+ (a single path or an array of paths)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # See Bundler::FileUtils.rm for keyword arguments.
#
- # Equivalent to
+ # Bundler::FileUtils.safe_unlink is an alias for Bundler::FileUtils.rm_f.
#
- # Bundler::FileUtils.rm(list, force: true)
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm_f(list, noop: nil, verbose: nil)
rm list, force: true, noop: noop, verbose: verbose
@@ -600,24 +1262,55 @@ module Bundler::FileUtils
alias safe_unlink rm_f
module_function :safe_unlink
+ # Removes entries at the paths in the given +list+
+ # (a single path or an array of paths);
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # May cause a local vulnerability if not called with keyword argument
+ # <tt>secure: true</tt>;
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
+ #
+ # For each file path, removes the file at that path:
+ #
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
+ # Bundler::FileUtils.rm_r(['src0.dat', 'src0.txt'])
+ # File.exist?('src0.txt') # => false
+ # File.exist?('src0.dat') # => false
#
- # remove files +list+[0] +list+[1]... If +list+[n] is a directory,
- # removes its all contents recursively. This method ignores
- # StandardError when :force option is set.
+ # For each directory path, recursively removes files and directories:
#
- # Bundler::FileUtils.rm_r Dir.glob('/tmp/*')
- # Bundler::FileUtils.rm_r 'some_dir', force: true
+ # tree('src1')
+ # # => src1
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # Bundler::FileUtils.rm_r('src1')
+ # File.exist?('src1') # => false
#
- # WARNING: This method causes local vulnerability
- # if one of parent directories or removing directory tree are world
- # writable (including /tmp, whose permission is 1777), and the current
- # process has strong privilege such as Unix super user (root), and the
- # system has symbolic link. For secure removing, read the documentation
- # of remove_entry_secure carefully, and set :secure option to true.
- # Default is <tt>secure: false</tt>.
+ # Keyword arguments:
#
- # NOTE: This method calls remove_entry_secure if :secure option is set.
- # See also remove_entry_secure.
+ # - <tt>force: true</tt> - ignores raised exceptions of StandardError
+ # and its descendants.
+ # - <tt>noop: true</tt> - does not remove entries; returns +nil+.
+ # - <tt>secure: true</tt> - removes +src+ securely;
+ # see details at Bundler::FileUtils.remove_entry_secure.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.rm_r(['src0.dat', 'src0.txt'], noop: true, verbose: true)
+ # Bundler::FileUtils.rm_r('src1', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # rm -r src0.dat src0.txt
+ # rm -r src1
+ #
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
list = fu_list(list)
@@ -633,13 +1326,22 @@ module Bundler::FileUtils
end
module_function :rm_r
+ # Equivalent to:
#
- # Equivalent to
+ # Bundler::FileUtils.rm_r(list, force: true, **kwargs)
#
- # Bundler::FileUtils.rm_r(list, force: true)
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
#
- # WARNING: This method causes local vulnerability.
- # Read the documentation of rm_r first.
+ # May cause a local vulnerability if not called with keyword argument
+ # <tt>secure: true</tt>;
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
+ #
+ # See Bundler::FileUtils.rm_r for keyword arguments.
+ #
+ # Bundler::FileUtils.rmtree is an alias for Bundler::FileUtils.rm_rf.
+ #
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm_rf(list, noop: nil, verbose: nil, secure: nil)
rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
@@ -649,37 +1351,20 @@ module Bundler::FileUtils
alias rmtree rm_rf
module_function :rmtree
+ # Securely removes the entry given by +path+,
+ # which should be the entry for a regular file, a symbolic link,
+ # or a directory.
#
- # This method removes a file system entry +path+. +path+ shall be a
- # regular file, a directory, or something. If +path+ is a directory,
- # remove it recursively. This method is required to avoid TOCTTOU
- # (time-of-check-to-time-of-use) local security vulnerability of rm_r.
- # #rm_r causes security hole when:
- #
- # * Parent directory is world writable (including /tmp).
- # * Removing directory tree includes world writable directory.
- # * The system has symbolic link.
+ # Argument +path+
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
- # To avoid this security hole, this method applies special preprocess.
- # If +path+ is a directory, this method chown(2) and chmod(2) all
- # removing directories. This requires the current process is the
- # owner of the removing whole directory tree, or is the super user (root).
+ # Avoids a local vulnerability that can exist in certain circumstances;
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
#
- # WARNING: You must ensure that *ALL* parent directories cannot be
- # moved by other untrusted users. For example, parent directories
- # should not be owned by untrusted users, and should not be world
- # writable except when the sticky bit set.
+ # Optional argument +force+ specifies whether to ignore
+ # raised exceptions of StandardError and its descendants.
#
- # WARNING: Only the owner of the removing directory tree, or Unix super
- # user (root) should invoke this method. Otherwise this method does not
- # work.
- #
- # For details of this security vulnerability, see Perl's case:
- #
- # * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
- # * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
- #
- # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def remove_entry_secure(path, force = false)
unless fu_have_symlink?
@@ -767,12 +1452,17 @@ module Bundler::FileUtils
end
private_module_function :fu_stat_identical_entry?
+ # Removes the entry given by +path+,
+ # which should be the entry for a regular file, a symbolic link,
+ # or a directory.
#
- # This method removes a file system entry +path+.
- # +path+ might be a regular file, a directory, or something.
- # If +path+ is a directory, remove it recursively.
+ # Argument +path+
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
- # See also remove_entry_secure.
+ # Optional argument +force+ specifies whether to ignore
+ # raised exceptions of StandardError and its descendants.
+ #
+ # Related: Bundler::FileUtils.remove_entry_secure.
#
def remove_entry(path, force = false)
Entry_.new(path).postorder_traverse do |ent|
@@ -787,9 +1477,16 @@ module Bundler::FileUtils
end
module_function :remove_entry
+ # Removes the file entry given by +path+,
+ # which should be the entry for a regular file or a symbolic link.
+ #
+ # Argument +path+
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
- # Removes a file +path+.
- # This method ignores StandardError if +force+ is true.
+ # Optional argument +force+ specifies whether to ignore
+ # raised exceptions of StandardError and its descendants.
+ #
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def remove_file(path, force = false)
Entry_.new(path).remove_file
@@ -798,20 +1495,32 @@ module Bundler::FileUtils
end
module_function :remove_file
+ # Recursively removes the directory entry given by +path+,
+ # which should be the entry for a regular file, a symbolic link,
+ # or a directory.
+ #
+ # Argument +path+
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
- # Removes a directory +dir+ and its contents recursively.
- # This method ignores StandardError if +force+ is true.
+ # Optional argument +force+ specifies whether to ignore
+ # raised exceptions of StandardError and its descendants.
+ #
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def remove_dir(path, force = false)
remove_entry path, force # FIXME?? check if it is a directory
end
module_function :remove_dir
+ # Returns +true+ if the contents of files +a+ and +b+ are identical,
+ # +false+ otherwise.
+ #
+ # Arguments +a+ and +b+
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
- # Returns true if the contents of a file +a+ and a file +b+ are identical.
+ # Bundler::FileUtils.identical? and Bundler::FileUtils.cmp are aliases for Bundler::FileUtils.compare_file.
#
- # Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true
- # Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
+ # Related: Bundler::FileUtils.compare_stream.
#
def compare_file(a, b)
return false unless File.size(a) == File.size(b)
@@ -828,19 +1537,19 @@ module Bundler::FileUtils
module_function :identical?
module_function :cmp
+ # Returns +true+ if the contents of streams +a+ and +b+ are identical,
+ # +false+ otherwise.
#
- # Returns true if the contents of a stream +a+ and +b+ are identical.
+ # Arguments +a+ and +b+
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # Related: Bundler::FileUtils.compare_file.
#
def compare_stream(a, b)
bsize = fu_stream_blksize(a, b)
- if RUBY_VERSION > "2.4"
- sa = String.new(capacity: bsize)
- sb = String.new(capacity: bsize)
- else
- sa = String.new
- sb = String.new
- end
+ sa = String.new(capacity: bsize)
+ sb = String.new(capacity: bsize)
begin
a.read(bsize, sa)
@@ -851,13 +1560,69 @@ module Bundler::FileUtils
end
module_function :compare_stream
+ # Copies a file entry.
+ # See {install(1)}[https://man7.org/linux/man-pages/man1/install.1.html].
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments];
+ #
+ # If the entry at +dest+ does not exist, copies from +src+ to +dest+:
+ #
+ # File.read('src0.txt') # => "aaa\n"
+ # File.exist?('dest0.txt') # => false
+ # Bundler::FileUtils.install('src0.txt', 'dest0.txt')
+ # File.read('dest0.txt') # => "aaa\n"
+ #
+ # If +dest+ is a file entry, copies from +src+ to +dest+, overwriting:
+ #
+ # File.read('src1.txt') # => "aaa\n"
+ # File.read('dest1.txt') # => "bbb\n"
+ # Bundler::FileUtils.install('src1.txt', 'dest1.txt')
+ # File.read('dest1.txt') # => "aaa\n"
#
- # If +src+ is not same as +dest+, copies it and changes the permission
- # mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+.
- # This method removes destination before copy.
+ # If +dest+ is a directory entry, copies from +src+ to <tt>dest/src</tt>,
+ # overwriting if necessary:
#
- # Bundler::FileUtils.install 'ruby', '/usr/local/bin/ruby', mode: 0755, verbose: true
- # Bundler::FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', verbose: true
+ # File.read('src2.txt') # => "aaa\n"
+ # File.read('dest2/src2.txt') # => "bbb\n"
+ # Bundler::FileUtils.install('src2.txt', 'dest2')
+ # File.read('dest2/src2.txt') # => "aaa\n"
+ #
+ # If +src+ is an array of paths and +dest+ points to a directory,
+ # copies each path +path+ in +src+ to <tt>dest/path</tt>:
+ #
+ # File.file?('src3.txt') # => true
+ # File.file?('src3.dat') # => true
+ # Bundler::FileUtils.mkdir('dest3')
+ # Bundler::FileUtils.install(['src3.txt', 'src3.dat'], 'dest3')
+ # File.file?('dest3/src3.txt') # => true
+ # File.file?('dest3/src3.dat') # => true
+ #
+ # Keyword arguments:
+ #
+ # - <tt>group: <i>group</i></tt> - changes the group if not +nil+,
+ # using {File.chown}[rdoc-ref:File.chown].
+ # - <tt>mode: <i>permissions</i></tt> - changes the permissions.
+ # using {File.chmod}[rdoc-ref:File.chmod].
+ # - <tt>noop: true</tt> - does not copy entries; returns +nil+.
+ # - <tt>owner: <i>owner</i></tt> - changes the owner if not +nil+,
+ # using {File.chown}[rdoc-ref:File.chown].
+ # - <tt>preserve: true</tt> - preserve timestamps
+ # using {File.utime}[rdoc-ref:File.utime].
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.install('src0.txt', 'dest0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.install('src1.txt', 'dest1.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.install('src2.txt', 'dest2', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # install -c src0.txt dest0.txt
+ # install -c src1.txt dest1.txt
+ # install -c src2.txt dest2
+ #
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
noop: nil, verbose: nil)
@@ -917,11 +1682,8 @@ module Bundler::FileUtils
private_module_function :apply_mask
def symbolic_modes_to_i(mode_sym, path) #:nodoc:
- mode = if File::Stat === path
- path.mode
- else
- File.stat(path).mode
- end
+ path = File.stat(path) unless File::Stat === path
+ mode = path.mode
mode_sym.split(/,/).inject(mode & 07777) do |current_mode, clause|
target, *actions = clause.split(/([=+-])/)
raise ArgumentError, "invalid file mode: #{mode_sym}" if actions.empty?
@@ -938,7 +1700,7 @@ module Bundler::FileUtils
when "x"
mask | 0111
when "X"
- if FileTest.directory? path
+ if path.directory?
mask | 0111
else
mask
@@ -978,37 +1740,78 @@ module Bundler::FileUtils
end
private_module_function :mode_to_s
+ # Changes permissions on the entries at the paths given in +list+
+ # (a single path or an array of paths)
+ # to the permissions given by +mode+;
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise:
+ #
+ # - Modifies each entry that is a regular file using
+ # {File.chmod}[rdoc-ref:File.chmod].
+ # - Modifies each entry that is a symbolic link using
+ # {File.lchmod}[rdoc-ref:File.lchmod].
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # Argument +mode+ may be either an integer or a string:
+ #
+ # - \Integer +mode+: represents the permission bits to be set:
+ #
+ # Bundler::FileUtils.chmod(0755, 'src0.txt')
+ # Bundler::FileUtils.chmod(0644, ['src0.txt', 'src0.dat'])
+ #
+ # - \String +mode+: represents the permissions to be set:
+ #
+ # The string is of the form <tt>[targets][[operator][perms[,perms]]</tt>, where:
+ #
+ # - +targets+ may be any combination of these letters:
+ #
+ # - <tt>'u'</tt>: permissions apply to the file's owner.
+ # - <tt>'g'</tt>: permissions apply to users in the file's group.
+ # - <tt>'o'</tt>: permissions apply to other users not in the file's group.
+ # - <tt>'a'</tt> (the default): permissions apply to all users.
+ #
+ # - +operator+ may be one of these letters:
+ #
+ # - <tt>'+'</tt>: adds permissions.
+ # - <tt>'-'</tt>: removes permissions.
+ # - <tt>'='</tt>: sets (replaces) permissions.
+ #
+ # - +perms+ (may be repeated, with separating commas)
+ # may be any combination of these letters:
+ #
+ # - <tt>'r'</tt>: Read.
+ # - <tt>'w'</tt>: Write.
+ # - <tt>'x'</tt>: Execute (search, for a directory).
+ # - <tt>'X'</tt>: Search (for a directories only;
+ # must be used with <tt>'+'</tt>)
+ # - <tt>'s'</tt>: Uid or gid.
+ # - <tt>'t'</tt>: Sticky bit.
+ #
+ # Examples:
+ #
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', 'src1.txt')
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby')
+ #
+ # Keyword arguments:
+ #
+ # - <tt>noop: true</tt> - does not change permissions; returns +nil+.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.chmod(0755, 'src0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.chmod(0644, ['src0.txt', 'src0.dat'], noop: true, verbose: true)
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', 'src1.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # chmod 755 src0.txt
+ # chmod 644 src0.txt src0.dat
+ # chmod u=wrx,go=rx src1.txt
+ # chmod u=wrx,go=rx /usr/bin/ruby
+ #
+ # Related: Bundler::FileUtils.chmod_R.
#
- # Changes permission bits on the named files (in +list+) to the bit pattern
- # represented by +mode+.
- #
- # +mode+ is the symbolic and absolute mode can be used.
- #
- # Absolute mode is
- # Bundler::FileUtils.chmod 0755, 'somecommand'
- # Bundler::FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
- # Bundler::FileUtils.chmod 0755, '/usr/bin/ruby', verbose: true
- #
- # Symbolic mode is
- # Bundler::FileUtils.chmod "u=wrx,go=rx", 'somecommand'
- # Bundler::FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb)
- # Bundler::FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', verbose: true
- #
- # "a" :: is user, group, other mask.
- # "u" :: is user's mask.
- # "g" :: is group's mask.
- # "o" :: is other's mask.
- # "w" :: is write permission.
- # "r" :: is read permission.
- # "x" :: is execute permission.
- # "X" ::
- # is execute permission for directories only, must be used in conjunction with "+"
- # "s" :: is uid, gid.
- # "t" :: is sticky bit.
- # "+" :: is added to a class given the specified mode.
- # "-" :: Is removed from a given class given mode.
- # "=" :: Is the exact nature of the class will be given a specified mode.
-
def chmod(mode, list, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
@@ -1019,12 +1822,7 @@ module Bundler::FileUtils
end
module_function :chmod
- #
- # Changes permission bits on the named files (in +list+)
- # to the bit pattern represented by +mode+.
- #
- # Bundler::FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
- # Bundler::FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
+ # Like Bundler::FileUtils.chmod, but changes permissions recursively.
#
def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
@@ -1044,15 +1842,68 @@ module Bundler::FileUtils
end
module_function :chmod_R
+ # Changes the owner and group on the entries at the paths given in +list+
+ # (a single path or an array of paths)
+ # to the given +user+ and +group+;
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise:
+ #
+ # - Modifies each entry that is a regular file using
+ # {File.chown}[rdoc-ref:File.chown].
+ # - Modifies each entry that is a symbolic link using
+ # {File.lchown}[rdoc-ref:File.lchown].
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
#
- # Changes owner and group on the named files (in +list+)
- # to the user +user+ and the group +group+. +user+ and +group+
- # may be an ID (Integer/String) or a name (String).
- # If +user+ or +group+ is nil, this method does not change
- # the attribute.
+ # User and group:
#
- # Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
- # Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), verbose: true
+ # - Argument +user+ may be a user name or a user id;
+ # if +nil+ or +-1+, the user is not changed.
+ # - Argument +group+ may be a group name or a group id;
+ # if +nil+ or +-1+, the group is not changed.
+ # - The user must be a member of the group.
+ #
+ # Examples:
+ #
+ # # One path.
+ # # User and group as string names.
+ # File.stat('src0.txt').uid # => 1004
+ # File.stat('src0.txt').gid # => 1004
+ # Bundler::FileUtils.chown('user2', 'group1', 'src0.txt')
+ # File.stat('src0.txt').uid # => 1006
+ # File.stat('src0.txt').gid # => 1005
+ #
+ # # User and group as uid and gid.
+ # Bundler::FileUtils.chown(1004, 1004, 'src0.txt')
+ # File.stat('src0.txt').uid # => 1004
+ # File.stat('src0.txt').gid # => 1004
+ #
+ # # Array of paths.
+ # Bundler::FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'])
+ #
+ # # Directory (not recursive).
+ # Bundler::FileUtils.chown('user2', 'group1', '.')
+ #
+ # Keyword arguments:
+ #
+ # - <tt>noop: true</tt> - does not change permissions; returns +nil+.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.chown('user2', 'group1', 'src0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.chown(1004, 1004, 'src0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'], noop: true, verbose: true)
+ # Bundler::FileUtils.chown('user2', 'group1', path, noop: true, verbose: true)
+ # Bundler::FileUtils.chown('user2', 'group1', '.', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # chown user2:group1 src0.txt
+ # chown 1004:1004 src0.txt
+ # chown 1006:1005 src0.txt src0.dat
+ # chown user2:group1 src0.txt
+ # chown user2:group1 .
+ #
+ # Related: Bundler::FileUtils.chown_R.
#
def chown(user, group, list, noop: nil, verbose: nil)
list = fu_list(list)
@@ -1068,15 +1919,7 @@ module Bundler::FileUtils
end
module_function :chown
- #
- # Changes owner and group on the named files (in +list+)
- # to the user +user+ and the group +group+ recursively.
- # +user+ and +group+ may be an ID (Integer/String) or
- # a name (String). If +user+ or +group+ is nil, this
- # method does not change the attribute.
- #
- # Bundler::FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
- # Bundler::FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', verbose: true
+ # Like Bundler::FileUtils.chown, but changes owner and group recursively.
#
def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
@@ -1127,12 +1970,50 @@ module Bundler::FileUtils
end
private_module_function :fu_get_gid
+ # Updates modification times (mtime) and access times (atime)
+ # of the entries given by the paths in +list+
+ # (a single path or an array of paths);
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
+ #
+ # By default, creates an empty file for any path to a non-existent entry;
+ # use keyword argument +nocreate+ to raise an exception instead.
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # Examples:
+ #
+ # # Single path.
+ # f = File.new('src0.txt') # Existing file.
+ # f.atime # => 2022-06-10 11:11:21.200277 -0700
+ # f.mtime # => 2022-06-10 11:11:21.200277 -0700
+ # Bundler::FileUtils.touch('src0.txt')
+ # f = File.new('src0.txt')
+ # f.atime # => 2022-06-11 08:28:09.8185343 -0700
+ # f.mtime # => 2022-06-11 08:28:09.8185343 -0700
#
- # Updates modification time (mtime) and access time (atime) of file(s) in
- # +list+. Files are created if they don't exist.
+ # # Array of paths.
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
#
- # Bundler::FileUtils.touch 'timestamp'
- # Bundler::FileUtils.touch Dir.glob('*.c'); system 'make'
+ # Keyword arguments:
+ #
+ # - <tt>mtime: <i>time</i></tt> - sets the entry's mtime to the given time,
+ # instead of the current time.
+ # - <tt>nocreate: true</tt> - raises an exception if the entry does not exist.
+ # - <tt>noop: true</tt> - does not touch entries; returns +nil+.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.touch('src0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'], noop: true, verbose: true)
+ # Bundler::FileUtils.touch(path, noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # touch src0.txt
+ # touch src0.txt src0.dat
+ # touch src0.txt
+ #
+ # Related: Bundler::FileUtils.uptodate?.
#
def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
list = fu_list(list)
@@ -1290,14 +2171,9 @@ module Bundler::FileUtils
def entries
opts = {}
- opts[:encoding] = ::Encoding::UTF_8 if fu_windows?
+ opts[:encoding] = fu_windows? ? ::Encoding::UTF_8 : path.encoding
- files = if Dir.respond_to?(:children)
- Dir.children(path, **opts)
- else
- Dir.entries(path(), **opts)
- .reject {|n| n == '.' or n == '..' }
- end
+ files = Dir.children(path, **opts)
untaint = RUBY_VERSION < '2.7'
files.map {|n| Entry_.new(prefix(), join(rel(), untaint ? n.untaint : n)) }
@@ -1345,6 +2221,7 @@ module Bundler::FileUtils
else
File.chmod mode, path()
end
+ rescue Errno::EOPNOTSUPP
end
def chown(uid, gid)
@@ -1439,7 +2316,7 @@ module Bundler::FileUtils
if st.symlink?
begin
File.lchmod mode, path
- rescue NotImplementedError
+ rescue NotImplementedError, Errno::EOPNOTSUPP
end
else
File.chmod mode, path
@@ -1498,13 +2375,21 @@ module Bundler::FileUtils
def postorder_traverse
if directory?
- entries().each do |ent|
+ begin
+ children = entries()
+ rescue Errno::EACCES
+ # Failed to get the list of children.
+ # Assuming there is no children, try to process the parent directory.
+ yield self
+ return
+ end
+
+ children.each do |ent|
ent.postorder_traverse do |e|
yield e
end
end
end
- ensure
yield self
end
@@ -1559,7 +2444,15 @@ module Bundler::FileUtils
def join(dir, base)
return File.path(dir) if not base or base == '.'
return File.path(base) if not dir or dir == '.'
- File.join(dir, base)
+ begin
+ File.join(dir, base)
+ rescue EncodingError
+ if fu_windows?
+ File.join(dir.encode(::Encoding::UTF_8), base.encode(::Encoding::UTF_8))
+ else
+ raise
+ end
+ end
end
if File::ALT_SEPARATOR
@@ -1590,15 +2483,15 @@ module Bundler::FileUtils
end
private_module_function :fu_each_src_dest
- def fu_each_src_dest0(src, dest) #:nodoc:
+ def fu_each_src_dest0(src, dest, target_directory = true) #:nodoc:
if tmp = Array.try_convert(src)
tmp.each do |s|
s = File.path(s)
- yield s, File.join(dest, File.basename(s))
+ yield s, (target_directory ? File.join(dest, File.basename(s)) : dest)
end
else
src = File.path(src)
- if File.directory?(dest)
+ if target_directory and File.directory?(dest)
yield src, File.join(dest, File.basename(src))
else
yield src, File.path(dest)
@@ -1614,7 +2507,7 @@ module Bundler::FileUtils
def fu_output_message(msg) #:nodoc:
output = @fileutils_output if defined?(@fileutils_output)
- output ||= $stderr
+ output ||= $stdout
if defined?(@fileutils_label)
msg = @fileutils_label + msg
end
@@ -1622,6 +2515,56 @@ module Bundler::FileUtils
end
private_module_function :fu_output_message
+ def fu_split_path(path)
+ path = File.path(path)
+ list = []
+ until (parent, base = File.split(path); parent == path or parent == ".")
+ list << base
+ path = parent
+ end
+ list << path
+ list.reverse!
+ end
+ private_module_function :fu_split_path
+
+ def fu_relative_components_from(target, base) #:nodoc:
+ i = 0
+ while target[i]&.== base[i]
+ i += 1
+ end
+ Array.new(base.size-i, '..').concat(target[i..-1])
+ end
+ private_module_function :fu_relative_components_from
+
+ def fu_clean_components(*comp)
+ comp.shift while comp.first == "."
+ return comp if comp.empty?
+ clean = [comp.shift]
+ path = File.join(*clean, "") # ending with File::SEPARATOR
+ while c = comp.shift
+ if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
+ clean.pop
+ path.chomp!(%r((?<=\A|/)[^/]+/\z), "")
+ else
+ clean << c
+ path << c << "/"
+ end
+ end
+ clean
+ end
+ private_module_function :fu_clean_components
+
+ if fu_windows?
+ def fu_starting_path?(path)
+ path&.start_with?(%r(\w:|/))
+ end
+ else
+ def fu_starting_path?(path)
+ path&.start_with?("/")
+ end
+ end
+ private_module_function :fu_starting_path?
+
# This hash table holds command options.
OPT_TABLE = {} #:nodoc: internal use only
(private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|
@@ -1631,50 +2574,49 @@ module Bundler::FileUtils
public
+ # Returns an array of the string names of \Bundler::FileUtils methods
+ # that accept one or more keyword arguments:
#
- # Returns an Array of names of high-level methods that accept any keyword
- # arguments.
- #
- # p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
+ # Bundler::FileUtils.commands.sort.take(3) # => ["cd", "chdir", "chmod"]
#
def self.commands
OPT_TABLE.keys
end
+ # Returns an array of the string keyword names:
#
- # Returns an Array of option names.
- #
- # p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
+ # Bundler::FileUtils.options.take(3) # => ["noop", "verbose", "force"]
#
def self.options
OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
end
+ # Returns +true+ if method +mid+ accepts the given option +opt+, +false+ otherwise;
+ # the arguments may be strings or symbols:
#
- # Returns true if the method +mid+ have an option +opt+.
- #
- # p Bundler::FileUtils.have_option?(:cp, :noop) #=> true
- # p Bundler::FileUtils.have_option?(:rm, :force) #=> true
- # p Bundler::FileUtils.have_option?(:rm, :preserve) #=> false
+ # Bundler::FileUtils.have_option?(:chmod, :noop) # => true
+ # Bundler::FileUtils.have_option?('chmod', 'secure') # => false
#
def self.have_option?(mid, opt)
li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
li.include?(opt)
end
+ # Returns an array of the string keyword name for method +mid+;
+ # the argument may be a string or a symbol:
#
- # Returns an Array of option names of the method +mid+.
- #
- # p Bundler::FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"]
+ # Bundler::FileUtils.options_of(:rm) # => ["force", "noop", "verbose"]
+ # Bundler::FileUtils.options_of('mv') # => ["force", "noop", "verbose", "secure"]
#
def self.options_of(mid)
OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
end
+ # Returns an array of the string method names of the methods
+ # that accept the given keyword option +opt+;
+ # the argument must be a symbol:
#
- # Returns an Array of methods names which have the option +opt+.
- #
- # p Bundler::FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
+ # Bundler::FileUtils.collect_method(:preserve) # => ["cp", "copy", "cp_r", "install"]
#
def self.collect_method(opt)
OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo.rb b/lib/bundler/vendor/molinillo/lib/molinillo.rb
deleted file mode 100644
index a52b96deaf..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'molinillo/gem_metadata'
-require_relative 'molinillo/errors'
-require_relative 'molinillo/resolver'
-require_relative 'molinillo/modules/ui'
-require_relative 'molinillo/modules/specification_provider'
-
-# Bundler::Molinillo is a generic dependency resolution algorithm.
-module Bundler::Molinillo
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb b/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb
deleted file mode 100644
index bcacf35243..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- # @!visibility private
- module Delegates
- # Delegates all {Bundler::Molinillo::ResolutionState} methods to a `#state` property.
- module ResolutionState
- # (see Bundler::Molinillo::ResolutionState#name)
- def name
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.name
- end
-
- # (see Bundler::Molinillo::ResolutionState#requirements)
- def requirements
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.requirements
- end
-
- # (see Bundler::Molinillo::ResolutionState#activated)
- def activated
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.activated
- end
-
- # (see Bundler::Molinillo::ResolutionState#requirement)
- def requirement
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.requirement
- end
-
- # (see Bundler::Molinillo::ResolutionState#possibilities)
- def possibilities
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.possibilities
- end
-
- # (see Bundler::Molinillo::ResolutionState#depth)
- def depth
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.depth
- end
-
- # (see Bundler::Molinillo::ResolutionState#conflicts)
- def conflicts
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.conflicts
- end
-
- # (see Bundler::Molinillo::ResolutionState#unused_unwind_options)
- def unused_unwind_options
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.unused_unwind_options
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb b/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb
deleted file mode 100644
index f8c695c1ed..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- module Delegates
- # Delegates all {Bundler::Molinillo::SpecificationProvider} methods to a
- # `#specification_provider` property.
- module SpecificationProvider
- # (see Bundler::Molinillo::SpecificationProvider#search_for)
- def search_for(dependency)
- with_no_such_dependency_error_handling do
- specification_provider.search_for(dependency)
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#dependencies_for)
- def dependencies_for(specification)
- with_no_such_dependency_error_handling do
- specification_provider.dependencies_for(specification)
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#requirement_satisfied_by?)
- def requirement_satisfied_by?(requirement, activated, spec)
- with_no_such_dependency_error_handling do
- specification_provider.requirement_satisfied_by?(requirement, activated, spec)
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#dependencies_equal?)
- def dependencies_equal?(dependencies, other_dependencies)
- with_no_such_dependency_error_handling do
- specification_provider.dependencies_equal?(dependencies, other_dependencies)
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#name_for)
- def name_for(dependency)
- with_no_such_dependency_error_handling do
- specification_provider.name_for(dependency)
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#name_for_explicit_dependency_source)
- def name_for_explicit_dependency_source
- with_no_such_dependency_error_handling do
- specification_provider.name_for_explicit_dependency_source
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#name_for_locking_dependency_source)
- def name_for_locking_dependency_source
- with_no_such_dependency_error_handling do
- specification_provider.name_for_locking_dependency_source
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#sort_dependencies)
- def sort_dependencies(dependencies, activated, conflicts)
- with_no_such_dependency_error_handling do
- specification_provider.sort_dependencies(dependencies, activated, conflicts)
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#allow_missing?)
- def allow_missing?(dependency)
- with_no_such_dependency_error_handling do
- specification_provider.allow_missing?(dependency)
- end
- end
-
- private
-
- # Ensures any raised {NoSuchDependencyError} has its
- # {NoSuchDependencyError#required_by} set.
- # @yield
- def with_no_such_dependency_error_handling
- yield
- rescue NoSuchDependencyError => error
- if state
- vertex = activated.vertex_named(name_for(error.dependency))
- error.required_by += vertex.incoming_edges.map { |e| e.origin.name }
- error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty?
- end
- raise
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
deleted file mode 100644
index 4d577213b9..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
+++ /dev/null
@@ -1,255 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../../../vendored_tsort'
-
-require_relative 'dependency_graph/log'
-require_relative 'dependency_graph/vertex'
-
-module Bundler::Molinillo
- # A directed acyclic graph that is tuned to hold named dependencies
- class DependencyGraph
- include Enumerable
-
- # Enumerates through the vertices of the graph.
- # @return [Array<Vertex>] The graph's vertices.
- def each
- return vertices.values.each unless block_given?
- vertices.values.each { |v| yield v }
- end
-
- include Bundler::TSort
-
- # @!visibility private
- alias tsort_each_node each
-
- # @!visibility private
- def tsort_each_child(vertex, &block)
- vertex.successors.each(&block)
- end
-
- # Topologically sorts the given vertices.
- # @param [Enumerable<Vertex>] vertices the vertices to be sorted, which must
- # all belong to the same graph.
- # @return [Array<Vertex>] The sorted vertices.
- def self.tsort(vertices)
- Bundler::TSort.tsort(
- lambda { |b| vertices.each(&b) },
- lambda { |v, &b| (v.successors & vertices).each(&b) }
- )
- end
-
- # A directed edge of a {DependencyGraph}
- # @attr [Vertex] origin The origin of the directed edge
- # @attr [Vertex] destination The destination of the directed edge
- # @attr [Object] requirement The requirement the directed edge represents
- Edge = Struct.new(:origin, :destination, :requirement)
-
- # @return [{String => Vertex}] the vertices of the dependency graph, keyed
- # by {Vertex#name}
- attr_reader :vertices
-
- # @return [Log] the op log for this graph
- attr_reader :log
-
- # Initializes an empty dependency graph
- def initialize
- @vertices = {}
- @log = Log.new
- end
-
- # Tags the current state of the dependency as the given tag
- # @param [Object] tag an opaque tag for the current state of the graph
- # @return [Void]
- def tag(tag)
- log.tag(self, tag)
- end
-
- # Rewinds the graph to the state tagged as `tag`
- # @param [Object] tag the tag to rewind to
- # @return [Void]
- def rewind_to(tag)
- log.rewind_to(self, tag)
- end
-
- # Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices}
- # are properly copied.
- # @param [DependencyGraph] other the graph to copy.
- def initialize_copy(other)
- super
- @vertices = {}
- @log = other.log.dup
- traverse = lambda do |new_v, old_v|
- return if new_v.outgoing_edges.size == old_v.outgoing_edges.size
- old_v.outgoing_edges.each do |edge|
- destination = add_vertex(edge.destination.name, edge.destination.payload)
- add_edge_no_circular(new_v, destination, edge.requirement)
- traverse.call(destination, edge.destination)
- end
- end
- other.vertices.each do |name, vertex|
- new_vertex = add_vertex(name, vertex.payload, vertex.root?)
- new_vertex.explicit_requirements.replace(vertex.explicit_requirements)
- traverse.call(new_vertex, vertex)
- end
- end
-
- # @return [String] a string suitable for debugging
- def inspect
- "#{self.class}:#{vertices.values.inspect}"
- end
-
- # @param [Hash] options options for dot output.
- # @return [String] Returns a dot format representation of the graph
- def to_dot(options = {})
- edge_label = options.delete(:edge_label)
- raise ArgumentError, "Unknown options: #{options.keys}" unless options.empty?
-
- dot_vertices = []
- dot_edges = []
- vertices.each do |n, v|
- dot_vertices << " #{n} [label=\"{#{n}|#{v.payload}}\"]"
- v.outgoing_edges.each do |e|
- label = edge_label ? edge_label.call(e) : e.requirement
- dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=#{label.to_s.dump}]"
- end
- end
-
- dot_vertices.uniq!
- dot_vertices.sort!
- dot_edges.uniq!
- dot_edges.sort!
-
- dot = dot_vertices.unshift('digraph G {').push('') + dot_edges.push('}')
- dot.join("\n")
- end
-
- # @param [DependencyGraph] other
- # @return [Boolean] whether the two dependency graphs are equal, determined
- # by a recursive traversal of each {#root_vertices} and its
- # {Vertex#successors}
- def ==(other)
- return false unless other
- return true if equal?(other)
- vertices.each do |name, vertex|
- other_vertex = other.vertex_named(name)
- return false unless other_vertex
- return false unless vertex.payload == other_vertex.payload
- return false unless other_vertex.successors.to_set == vertex.successors.to_set
- end
- end
-
- # @param [String] name
- # @param [Object] payload
- # @param [Array<String>] parent_names
- # @param [Object] requirement the requirement that is requiring the child
- # @return [void]
- def add_child_vertex(name, payload, parent_names, requirement)
- root = !parent_names.delete(nil) { true }
- vertex = add_vertex(name, payload, root)
- vertex.explicit_requirements << requirement if root
- parent_names.each do |parent_name|
- parent_vertex = vertex_named(parent_name)
- add_edge(parent_vertex, vertex, requirement)
- end
- vertex
- end
-
- # Adds a vertex with the given name, or updates the existing one.
- # @param [String] name
- # @param [Object] payload
- # @return [Vertex] the vertex that was added to `self`
- def add_vertex(name, payload, root = false)
- log.add_vertex(self, name, payload, root)
- end
-
- # Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively
- # removing any non-root vertices that were orphaned in the process
- # @param [String] name
- # @return [Array<Vertex>] the vertices which have been detached
- def detach_vertex_named(name)
- log.detach_vertex_named(self, name)
- end
-
- # @param [String] name
- # @return [Vertex,nil] the vertex with the given name
- def vertex_named(name)
- vertices[name]
- end
-
- # @param [String] name
- # @return [Vertex,nil] the root vertex with the given name
- def root_vertex_named(name)
- vertex = vertex_named(name)
- vertex if vertex && vertex.root?
- end
-
- # Adds a new {Edge} to the dependency graph
- # @param [Vertex] origin
- # @param [Vertex] destination
- # @param [Object] requirement the requirement that this edge represents
- # @return [Edge] the added edge
- def add_edge(origin, destination, requirement)
- if destination.path_to?(origin)
- raise CircularDependencyError.new(path(destination, origin))
- end
- add_edge_no_circular(origin, destination, requirement)
- end
-
- # Deletes an {Edge} from the dependency graph
- # @param [Edge] edge
- # @return [Void]
- def delete_edge(edge)
- log.delete_edge(self, edge.origin.name, edge.destination.name, edge.requirement)
- end
-
- # Sets the payload of the vertex with the given name
- # @param [String] name the name of the vertex
- # @param [Object] payload the payload
- # @return [Void]
- def set_payload(name, payload)
- log.set_payload(self, name, payload)
- end
-
- private
-
- # Adds a new {Edge} to the dependency graph without checking for
- # circularity.
- # @param (see #add_edge)
- # @return (see #add_edge)
- def add_edge_no_circular(origin, destination, requirement)
- log.add_edge_no_circular(self, origin.name, destination.name, requirement)
- end
-
- # Returns the path between two vertices
- # @raise [ArgumentError] if there is no path between the vertices
- # @param [Vertex] from
- # @param [Vertex] to
- # @return [Array<Vertex>] the shortest path from `from` to `to`
- def path(from, to)
- distances = Hash.new(vertices.size + 1)
- distances[from.name] = 0
- predecessors = {}
- each do |vertex|
- vertex.successors.each do |successor|
- if distances[successor.name] > distances[vertex.name] + 1
- distances[successor.name] = distances[vertex.name] + 1
- predecessors[successor] = vertex
- end
- end
- end
-
- path = [to]
- while before = predecessors[to]
- path << before
- to = before
- break if to == from
- end
-
- unless path.last.equal?(from)
- raise ArgumentError, "There is no path from #{from.name} to #{to.name}"
- end
-
- path.reverse
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb
deleted file mode 100644
index c04c7eec9c..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- class DependencyGraph
- # An action that modifies a {DependencyGraph} that is reversible.
- # @abstract
- class Action
- # rubocop:disable Lint/UnusedMethodArgument
-
- # @return [Symbol] The name of the action.
- def self.action_name
- raise 'Abstract'
- end
-
- # Performs the action on the given graph.
- # @param [DependencyGraph] graph the graph to perform the action on.
- # @return [Void]
- def up(graph)
- raise 'Abstract'
- end
-
- # Reverses the action on the given graph.
- # @param [DependencyGraph] graph the graph to reverse the action on.
- # @return [Void]
- def down(graph)
- raise 'Abstract'
- end
-
- # @return [Action,Nil] The previous action
- attr_accessor :previous
-
- # @return [Action,Nil] The next action
- attr_accessor :next
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
deleted file mode 100644
index 946a08236e..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Bundler::Molinillo
- class DependencyGraph
- # @!visibility private
- # (see DependencyGraph#add_edge_no_circular)
- class AddEdgeNoCircular < Action
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :add_vertex
- end
-
- # (see Action#up)
- def up(graph)
- edge = make_edge(graph)
- edge.origin.outgoing_edges << edge
- edge.destination.incoming_edges << edge
- edge
- end
-
- # (see Action#down)
- def down(graph)
- edge = make_edge(graph)
- delete_first(edge.origin.outgoing_edges, edge)
- delete_first(edge.destination.incoming_edges, edge)
- end
-
- # @!group AddEdgeNoCircular
-
- # @return [String] the name of the origin of the edge
- attr_reader :origin
-
- # @return [String] the name of the destination of the edge
- attr_reader :destination
-
- # @return [Object] the requirement that the edge represents
- attr_reader :requirement
-
- # @param [DependencyGraph] graph the graph to find vertices from
- # @return [Edge] The edge this action adds
- def make_edge(graph)
- Edge.new(graph.vertex_named(origin), graph.vertex_named(destination), requirement)
- end
-
- # Initialize an action to add an edge to a dependency graph
- # @param [String] origin the name of the origin of the edge
- # @param [String] destination the name of the destination of the edge
- # @param [Object] requirement the requirement that the edge represents
- def initialize(origin, destination, requirement)
- @origin = origin
- @destination = destination
- @requirement = requirement
- end
-
- private
-
- def delete_first(array, item)
- return unless index = array.index(item)
- array.delete_at(index)
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
deleted file mode 100644
index 483527daf8..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Bundler::Molinillo
- class DependencyGraph
- # @!visibility private
- # (see DependencyGraph#add_vertex)
- class AddVertex < Action # :nodoc:
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :add_vertex
- end
-
- # (see Action#up)
- def up(graph)
- if existing = graph.vertices[name]
- @existing_payload = existing.payload
- @existing_root = existing.root
- end
- vertex = existing || Vertex.new(name, payload)
- graph.vertices[vertex.name] = vertex
- vertex.payload ||= payload
- vertex.root ||= root
- vertex
- end
-
- # (see Action#down)
- def down(graph)
- if defined?(@existing_payload)
- vertex = graph.vertices[name]
- vertex.payload = @existing_payload
- vertex.root = @existing_root
- else
- graph.vertices.delete(name)
- end
- end
-
- # @!group AddVertex
-
- # @return [String] the name of the vertex
- attr_reader :name
-
- # @return [Object] the payload for the vertex
- attr_reader :payload
-
- # @return [Boolean] whether the vertex is root or not
- attr_reader :root
-
- # Initialize an action to add a vertex to a dependency graph
- # @param [String] name the name of the vertex
- # @param [Object] payload the payload for the vertex
- # @param [Boolean] root whether the vertex is root or not
- def initialize(name, payload, root)
- @name = name
- @payload = payload
- @root = root
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
deleted file mode 100644
index d81940585a..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Bundler::Molinillo
- class DependencyGraph
- # @!visibility private
- # (see DependencyGraph#delete_edge)
- class DeleteEdge < Action
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :delete_edge
- end
-
- # (see Action#up)
- def up(graph)
- edge = make_edge(graph)
- edge.origin.outgoing_edges.delete(edge)
- edge.destination.incoming_edges.delete(edge)
- end
-
- # (see Action#down)
- def down(graph)
- edge = make_edge(graph)
- edge.origin.outgoing_edges << edge
- edge.destination.incoming_edges << edge
- edge
- end
-
- # @!group DeleteEdge
-
- # @return [String] the name of the origin of the edge
- attr_reader :origin_name
-
- # @return [String] the name of the destination of the edge
- attr_reader :destination_name
-
- # @return [Object] the requirement that the edge represents
- attr_reader :requirement
-
- # @param [DependencyGraph] graph the graph to find vertices from
- # @return [Edge] The edge this action adds
- def make_edge(graph)
- Edge.new(
- graph.vertex_named(origin_name),
- graph.vertex_named(destination_name),
- requirement
- )
- end
-
- # Initialize an action to add an edge to a dependency graph
- # @param [String] origin_name the name of the origin of the edge
- # @param [String] destination_name the name of the destination of the edge
- # @param [Object] requirement the requirement that the edge represents
- def initialize(origin_name, destination_name, requirement)
- @origin_name = origin_name
- @destination_name = destination_name
- @requirement = requirement
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
deleted file mode 100644
index 36fce7c526..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Bundler::Molinillo
- class DependencyGraph
- # @!visibility private
- # @see DependencyGraph#detach_vertex_named
- class DetachVertexNamed < Action
- # @!group Action
-
- # (see Action#name)
- def self.action_name
- :add_vertex
- end
-
- # (see Action#up)
- def up(graph)
- return [] unless @vertex = graph.vertices.delete(name)
-
- removed_vertices = [@vertex]
- @vertex.outgoing_edges.each do |e|
- v = e.destination
- v.incoming_edges.delete(e)
- if !v.root? && v.incoming_edges.empty?
- removed_vertices.concat graph.detach_vertex_named(v.name)
- end
- end
-
- @vertex.incoming_edges.each do |e|
- v = e.origin
- v.outgoing_edges.delete(e)
- end
-
- removed_vertices
- end
-
- # (see Action#down)
- def down(graph)
- return unless @vertex
- graph.vertices[@vertex.name] = @vertex
- @vertex.outgoing_edges.each do |e|
- e.destination.incoming_edges << e
- end
- @vertex.incoming_edges.each do |e|
- e.origin.outgoing_edges << e
- end
- end
-
- # @!group DetachVertexNamed
-
- # @return [String] the name of the vertex to detach
- attr_reader :name
-
- # Initialize an action to detach a vertex from a dependency graph
- # @param [String] name the name of the vertex to detach
- def initialize(name)
- @name = name
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb
deleted file mode 100644
index 6f0de19886..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb
+++ /dev/null
@@ -1,126 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'add_edge_no_circular'
-require_relative 'add_vertex'
-require_relative 'delete_edge'
-require_relative 'detach_vertex_named'
-require_relative 'set_payload'
-require_relative 'tag'
-
-module Bundler::Molinillo
- class DependencyGraph
- # A log for dependency graph actions
- class Log
- # Initializes an empty log
- def initialize
- @current_action = @first_action = nil
- end
-
- # @!macro [new] action
- # {include:DependencyGraph#$0}
- # @param [Graph] graph the graph to perform the action on
- # @param (see DependencyGraph#$0)
- # @return (see DependencyGraph#$0)
-
- # @macro action
- def tag(graph, tag)
- push_action(graph, Tag.new(tag))
- end
-
- # @macro action
- def add_vertex(graph, name, payload, root)
- push_action(graph, AddVertex.new(name, payload, root))
- end
-
- # @macro action
- def detach_vertex_named(graph, name)
- push_action(graph, DetachVertexNamed.new(name))
- end
-
- # @macro action
- def add_edge_no_circular(graph, origin, destination, requirement)
- push_action(graph, AddEdgeNoCircular.new(origin, destination, requirement))
- end
-
- # {include:DependencyGraph#delete_edge}
- # @param [Graph] graph the graph to perform the action on
- # @param [String] origin_name
- # @param [String] destination_name
- # @param [Object] requirement
- # @return (see DependencyGraph#delete_edge)
- def delete_edge(graph, origin_name, destination_name, requirement)
- push_action(graph, DeleteEdge.new(origin_name, destination_name, requirement))
- end
-
- # @macro action
- def set_payload(graph, name, payload)
- push_action(graph, SetPayload.new(name, payload))
- end
-
- # Pops the most recent action from the log and undoes the action
- # @param [DependencyGraph] graph
- # @return [Action] the action that was popped off the log
- def pop!(graph)
- return unless action = @current_action
- unless @current_action = action.previous
- @first_action = nil
- end
- action.down(graph)
- action
- end
-
- extend Enumerable
-
- # @!visibility private
- # Enumerates each action in the log
- # @yield [Action]
- def each
- return enum_for unless block_given?
- action = @first_action
- loop do
- break unless action
- yield action
- action = action.next
- end
- self
- end
-
- # @!visibility private
- # Enumerates each action in the log in reverse order
- # @yield [Action]
- def reverse_each
- return enum_for(:reverse_each) unless block_given?
- action = @current_action
- loop do
- break unless action
- yield action
- action = action.previous
- end
- self
- end
-
- # @macro action
- def rewind_to(graph, tag)
- loop do
- action = pop!(graph)
- raise "No tag #{tag.inspect} found" unless action
- break if action.class.action_name == :tag && action.tag == tag
- end
- end
-
- private
-
- # Adds the given action to the log, running the action
- # @param [DependencyGraph] graph
- # @param [Action] action
- # @return The value returned by `action.up`
- def push_action(graph, action)
- action.previous = @current_action
- @current_action.next = action if @current_action
- @current_action = action
- @first_action ||= action
- action.up(graph)
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb
deleted file mode 100644
index 2e9b90e6cd..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Bundler::Molinillo
- class DependencyGraph
- # @!visibility private
- # @see DependencyGraph#set_payload
- class SetPayload < Action # :nodoc:
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :set_payload
- end
-
- # (see Action#up)
- def up(graph)
- vertex = graph.vertex_named(name)
- @old_payload = vertex.payload
- vertex.payload = payload
- end
-
- # (see Action#down)
- def down(graph)
- graph.vertex_named(name).payload = @old_payload
- end
-
- # @!group SetPayload
-
- # @return [String] the name of the vertex
- attr_reader :name
-
- # @return [Object] the payload for the vertex
- attr_reader :payload
-
- # Initialize an action to add set the payload for a vertex in a dependency
- # graph
- # @param [String] name the name of the vertex
- # @param [Object] payload the payload for the vertex
- def initialize(name, payload)
- @name = name
- @payload = payload
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb
deleted file mode 100644
index 5b5da3e4f9..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Bundler::Molinillo
- class DependencyGraph
- # @!visibility private
- # @see DependencyGraph#tag
- class Tag < Action
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :tag
- end
-
- # (see Action#up)
- def up(graph)
- end
-
- # (see Action#down)
- def down(graph)
- end
-
- # @!group Tag
-
- # @return [Object] An opaque tag
- attr_reader :tag
-
- # Initialize an action to tag a state of a dependency graph
- # @param [Object] tag an opaque tag
- def initialize(tag)
- @tag = tag
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb
deleted file mode 100644
index 1185a8ab05..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb
+++ /dev/null
@@ -1,164 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- class DependencyGraph
- # A vertex in a {DependencyGraph} that encapsulates a {#name} and a
- # {#payload}
- class Vertex
- # @return [String] the name of the vertex
- attr_accessor :name
-
- # @return [Object] the payload the vertex holds
- attr_accessor :payload
-
- # @return [Array<Object>] the explicit requirements that required
- # this vertex
- attr_reader :explicit_requirements
-
- # @return [Boolean] whether the vertex is considered a root vertex
- attr_accessor :root
- alias root? root
-
- # Initializes a vertex with the given name and payload.
- # @param [String] name see {#name}
- # @param [Object] payload see {#payload}
- def initialize(name, payload)
- @name = name.frozen? ? name : name.dup.freeze
- @payload = payload
- @explicit_requirements = []
- @outgoing_edges = []
- @incoming_edges = []
- end
-
- # @return [Array<Object>] all of the requirements that required
- # this vertex
- def requirements
- (incoming_edges.map(&:requirement) + explicit_requirements).uniq
- end
-
- # @return [Array<Edge>] the edges of {#graph} that have `self` as their
- # {Edge#origin}
- attr_accessor :outgoing_edges
-
- # @return [Array<Edge>] the edges of {#graph} that have `self` as their
- # {Edge#destination}
- attr_accessor :incoming_edges
-
- # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
- # `self` as their {Edge#destination}
- def predecessors
- incoming_edges.map(&:origin)
- end
-
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
- # {#descendent?}
- def recursive_predecessors
- _recursive_predecessors
- end
-
- # @param [Set<Vertex>] vertices the set to add the predecessors to
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
- # {#descendent?}
- def _recursive_predecessors(vertices = new_vertex_set)
- incoming_edges.each do |edge|
- vertex = edge.origin
- next unless vertices.add?(vertex)
- vertex._recursive_predecessors(vertices)
- end
-
- vertices
- end
- protected :_recursive_predecessors
-
- # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
- # `self` as their {Edge#origin}
- def successors
- outgoing_edges.map(&:destination)
- end
-
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
- # {#ancestor?}
- def recursive_successors
- _recursive_successors
- end
-
- # @param [Set<Vertex>] vertices the set to add the successors to
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
- # {#ancestor?}
- def _recursive_successors(vertices = new_vertex_set)
- outgoing_edges.each do |edge|
- vertex = edge.destination
- next unless vertices.add?(vertex)
- vertex._recursive_successors(vertices)
- end
-
- vertices
- end
- protected :_recursive_successors
-
- # @return [String] a string suitable for debugging
- def inspect
- "#{self.class}:#{name}(#{payload.inspect})"
- end
-
- # @return [Boolean] whether the two vertices are equal, determined
- # by a recursive traversal of each {Vertex#successors}
- def ==(other)
- return true if equal?(other)
- shallow_eql?(other) &&
- successors.to_set == other.successors.to_set
- end
-
- # @param [Vertex] other the other vertex to compare to
- # @return [Boolean] whether the two vertices are equal, determined
- # solely by {#name} and {#payload} equality
- def shallow_eql?(other)
- return true if equal?(other)
- other &&
- name == other.name &&
- payload == other.payload
- end
-
- alias eql? ==
-
- # @return [Fixnum] a hash for the vertex based upon its {#name}
- def hash
- name.hash
- end
-
- # Is there a path from `self` to `other` following edges in the
- # dependency graph?
- # @return whether there is a path following edges within this {#graph}
- def path_to?(other)
- _path_to?(other)
- end
-
- alias descendent? path_to?
-
- # @param [Vertex] other the vertex to check if there's a path to
- # @param [Set<Vertex>] visited the vertices of {#graph} that have been visited
- # @return [Boolean] whether there is a path to `other` from `self`
- def _path_to?(other, visited = new_vertex_set)
- return false unless visited.add?(self)
- return true if equal?(other)
- successors.any? { |v| v._path_to?(other, visited) }
- end
- protected :_path_to?
-
- # Is there a path from `other` to `self` following edges in the
- # dependency graph?
- # @return whether there is a path following edges within this {#graph}
- def ancestor?(other)
- other.path_to?(self)
- end
-
- alias is_reachable_from? ancestor?
-
- def new_vertex_set
- require 'set'
- Set.new
- end
- private :new_vertex_set
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb
deleted file mode 100644
index 8c8cafb447..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb
+++ /dev/null
@@ -1,149 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- # An error that occurred during the resolution process
- class ResolverError < StandardError; end
-
- # An error caused by searching for a dependency that is completely unknown,
- # i.e. has no versions available whatsoever.
- class NoSuchDependencyError < ResolverError
- # @return [Object] the dependency that could not be found
- attr_accessor :dependency
-
- # @return [Array<Object>] the specifications that depended upon {#dependency}
- attr_accessor :required_by
-
- # Initializes a new error with the given missing dependency.
- # @param [Object] dependency @see {#dependency}
- # @param [Array<Object>] required_by @see {#required_by}
- def initialize(dependency, required_by = [])
- @dependency = dependency
- @required_by = required_by.uniq
- super()
- end
-
- # The error message for the missing dependency, including the specifications
- # that had this dependency.
- def message
- sources = required_by.map { |r| "`#{r}`" }.join(' and ')
- message = "Unable to find a specification for `#{dependency}`"
- message += " depended upon by #{sources}" unless sources.empty?
- message
- end
- end
-
- # An error caused by attempting to fulfil a dependency that was circular
- #
- # @note This exception will be thrown if and only if a {Vertex} is added to a
- # {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an
- # existing {DependencyGraph::Vertex}
- class CircularDependencyError < ResolverError
- # [Set<Object>] the dependencies responsible for causing the error
- attr_reader :dependencies
-
- # Initializes a new error with the given circular vertices.
- # @param [Array<DependencyGraph::Vertex>] vertices the vertices in the dependency
- # that caused the error
- def initialize(vertices)
- super "There is a circular dependency between #{vertices.map(&:name).join(' and ')}"
- @dependencies = vertices.map { |vertex| vertex.payload.possibilities.last }.to_set
- end
- end
-
- # An error caused by conflicts in version
- class VersionConflict < ResolverError
- # @return [{String => Resolution::Conflict}] the conflicts that caused
- # resolution to fail
- attr_reader :conflicts
-
- # @return [SpecificationProvider] the specification provider used during
- # resolution
- attr_reader :specification_provider
-
- # Initializes a new error with the given version conflicts.
- # @param [{String => Resolution::Conflict}] conflicts see {#conflicts}
- # @param [SpecificationProvider] specification_provider see {#specification_provider}
- def initialize(conflicts, specification_provider)
- pairs = []
- conflicts.values.flat_map(&:requirements).each do |conflicting|
- conflicting.each do |source, conflict_requirements|
- conflict_requirements.each do |c|
- pairs << [c, source]
- end
- end
- end
-
- super "Unable to satisfy the following requirements:\n\n" \
- "#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}"
-
- @conflicts = conflicts
- @specification_provider = specification_provider
- end
-
- require_relative 'delegates/specification_provider'
- include Delegates::SpecificationProvider
-
- # @return [String] An error message that includes requirement trees,
- # which is much more detailed & customizable than the default message
- # @param [Hash] opts the options to create a message with.
- # @option opts [String] :solver_name The user-facing name of the solver
- # @option opts [String] :possibility_type The generic name of a possibility
- # @option opts [Proc] :reduce_trees A proc that reduced the list of requirement trees
- # @option opts [Proc] :printable_requirement A proc that pretty-prints requirements
- # @option opts [Proc] :additional_message_for_conflict A proc that appends additional
- # messages for each conflict
- # @option opts [Proc] :version_for_spec A proc that returns the version number for a
- # possibility
- def message_with_trees(opts = {})
- solver_name = opts.delete(:solver_name) { self.class.name.split('::').first }
- possibility_type = opts.delete(:possibility_type) { 'possibility named' }
- reduce_trees = opts.delete(:reduce_trees) { proc { |trees| trees.uniq.sort_by(&:to_s) } }
- printable_requirement = opts.delete(:printable_requirement) { proc { |req| req.to_s } }
- additional_message_for_conflict = opts.delete(:additional_message_for_conflict) { proc {} }
- version_for_spec = opts.delete(:version_for_spec) { proc(&:to_s) }
- incompatible_version_message_for_conflict = opts.delete(:incompatible_version_message_for_conflict) do
- proc do |name, _conflict|
- %(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":)
- end
- end
-
- full_message_for_conflict = opts.delete(:full_message_for_conflict) do
- proc do |name, conflict|
- o = "\n".dup << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
- if conflict.locked_requirement
- o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
- o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
- o << %(\n)
- end
- o << %( In #{name_for_explicit_dependency_source}:\n)
- trees = reduce_trees.call(conflict.requirement_trees)
-
- o << trees.map do |tree|
- t = ''.dup
- depth = 2
- tree.each do |req|
- t << ' ' * depth << printable_requirement.call(req)
- unless tree.last == req
- if spec = conflict.activated_by_name[name_for(req)]
- t << %( was resolved to #{version_for_spec.call(spec)}, which)
- end
- t << %( depends on)
- end
- t << %(\n)
- depth += 1
- end
- t
- end.join("\n")
-
- additional_message_for_conflict.call(o, name, conflict)
-
- o
- end
- end
-
- conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
- o << full_message_for_conflict.call(name, conflict)
- end.strip
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb b/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb
deleted file mode 100644
index a0cfc21672..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- # The version of Bundler::Molinillo.
- VERSION = '0.8.0'.freeze
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb
deleted file mode 100644
index eeae79af3c..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- # Provides information about specifications and dependencies to the resolver,
- # allowing the {Resolver} class to remain generic while still providing power
- # and flexibility.
- #
- # This module contains the methods that users of Bundler::Molinillo must to implement,
- # using knowledge of their own model classes.
- module SpecificationProvider
- # Search for the specifications that match the given dependency.
- # The specifications in the returned array will be considered in reverse
- # order, so the latest version ought to be last.
- # @note This method should be 'pure', i.e. the return value should depend
- # only on the `dependency` parameter.
- #
- # @param [Object] dependency
- # @return [Array<Object>] the specifications that satisfy the given
- # `dependency`.
- def search_for(dependency)
- []
- end
-
- # Returns the dependencies of `specification`.
- # @note This method should be 'pure', i.e. the return value should depend
- # only on the `specification` parameter.
- #
- # @param [Object] specification
- # @return [Array<Object>] the dependencies that are required by the given
- # `specification`.
- def dependencies_for(specification)
- []
- end
-
- # Determines whether the given `requirement` is satisfied by the given
- # `spec`, in the context of the current `activated` dependency graph.
- #
- # @param [Object] requirement
- # @param [DependencyGraph] activated the current dependency graph in the
- # resolution process.
- # @param [Object] spec
- # @return [Boolean] whether `requirement` is satisfied by `spec` in the
- # context of the current `activated` dependency graph.
- def requirement_satisfied_by?(requirement, activated, spec)
- true
- end
-
- # Determines whether two arrays of dependencies are equal, and thus can be
- # grouped.
- #
- # @param [Array<Object>] dependencies
- # @param [Array<Object>] other_dependencies
- # @return [Boolean] whether `dependencies` and `other_dependencies` should
- # be considered equal.
- def dependencies_equal?(dependencies, other_dependencies)
- dependencies == other_dependencies
- end
-
- # Returns the name for the given `dependency`.
- # @note This method should be 'pure', i.e. the return value should depend
- # only on the `dependency` parameter.
- #
- # @param [Object] dependency
- # @return [String] the name for the given `dependency`.
- def name_for(dependency)
- dependency.to_s
- end
-
- # @return [String] the name of the source of explicit dependencies, i.e.
- # those passed to {Resolver#resolve} directly.
- def name_for_explicit_dependency_source
- 'user-specified dependency'
- end
-
- # @return [String] the name of the source of 'locked' dependencies, i.e.
- # those passed to {Resolver#resolve} directly as the `base`
- def name_for_locking_dependency_source
- 'Lockfile'
- end
-
- # Sort dependencies so that the ones that are easiest to resolve are first.
- # Easiest to resolve is (usually) defined by:
- # 1) Is this dependency already activated?
- # 2) How relaxed are the requirements?
- # 3) Are there any conflicts for this dependency?
- # 4) How many possibilities are there to satisfy this dependency?
- #
- # @param [Array<Object>] dependencies
- # @param [DependencyGraph] activated the current dependency graph in the
- # resolution process.
- # @param [{String => Array<Conflict>}] conflicts
- # @return [Array<Object>] a sorted copy of `dependencies`.
- def sort_dependencies(dependencies, activated, conflicts)
- dependencies.sort_by do |dependency|
- name = name_for(dependency)
- [
- activated.vertex_named(name).payload ? 0 : 1,
- conflicts[name] ? 0 : 1,
- ]
- end
- end
-
- # Returns whether this dependency, which has no possible matching
- # specifications, can safely be ignored.
- #
- # @param [Object] dependency
- # @return [Boolean] whether this dependency can safely be skipped.
- def allow_missing?(dependency)
- false
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb
deleted file mode 100644
index a166bc6991..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- # Conveys information about the resolution process to a user.
- module UI
- # The {IO} object that should be used to print output. `STDOUT`, by default.
- #
- # @return [IO]
- def output
- STDOUT
- end
-
- # Called roughly every {#progress_rate}, this method should convey progress
- # to the user.
- #
- # @return [void]
- def indicate_progress
- output.print '.' unless debug?
- end
-
- # How often progress should be conveyed to the user via
- # {#indicate_progress}, in seconds. A third of a second, by default.
- #
- # @return [Float]
- def progress_rate
- 0.33
- end
-
- # Called before resolution begins.
- #
- # @return [void]
- def before_resolution
- output.print 'Resolving dependencies...'
- end
-
- # Called after resolution ends (either successfully or with an error).
- # By default, prints a newline.
- #
- # @return [void]
- def after_resolution
- output.puts
- end
-
- # Conveys debug information to the user.
- #
- # @param [Integer] depth the current depth of the resolution process.
- # @return [void]
- def debug(depth = 0)
- if debug?
- debug_info = yield
- debug_info = debug_info.inspect unless debug_info.is_a?(String)
- debug_info = debug_info.split("\n").map { |s| ":#{depth.to_s.rjust 4}: #{s}" }
- output.puts debug_info
- end
- end
-
- # Whether or not debug messages should be printed.
- # By default, whether or not the `MOLINILLO_DEBUG` environment variable is
- # set.
- #
- # @return [Boolean]
- def debug?
- return @debug_mode if defined?(@debug_mode)
- @debug_mode = ENV['MOLINILLO_DEBUG']
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb
deleted file mode 100644
index c689ca7635..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb
+++ /dev/null
@@ -1,839 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- class Resolver
- # A specific resolution from a given {Resolver}
- class Resolution
- # A conflict that the resolution process encountered
- # @attr [Object] requirement the requirement that immediately led to the conflict
- # @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict
- # @attr [Object, nil] existing the existing spec that was in conflict with
- # the {#possibility}
- # @attr [Object] possibility_set the set of specs that was unable to be
- # activated due to a conflict.
- # @attr [Object] locked_requirement the relevant locking requirement.
- # @attr [Array<Array<Object>>] requirement_trees the different requirement
- # trees that led to every requirement for the conflicting name.
- # @attr [{String=>Object}] activated_by_name the already-activated specs.
- # @attr [Object] underlying_error an error that has occurred during resolution, and
- # will be raised at the end of it if no resolution is found.
- Conflict = Struct.new(
- :requirement,
- :requirements,
- :existing,
- :possibility_set,
- :locked_requirement,
- :requirement_trees,
- :activated_by_name,
- :underlying_error
- )
-
- class Conflict
- # @return [Object] a spec that was unable to be activated due to a conflict
- def possibility
- possibility_set && possibility_set.latest_version
- end
- end
-
- # A collection of possibility states that share the same dependencies
- # @attr [Array] dependencies the dependencies for this set of possibilities
- # @attr [Array] possibilities the possibilities
- PossibilitySet = Struct.new(:dependencies, :possibilities)
-
- class PossibilitySet
- # String representation of the possibility set, for debugging
- def to_s
- "[#{possibilities.join(', ')}]"
- end
-
- # @return [Object] most up-to-date dependency in the possibility set
- def latest_version
- possibilities.last
- end
- end
-
- # Details of the state to unwind to when a conflict occurs, and the cause of the unwind
- # @attr [Integer] state_index the index of the state to unwind to
- # @attr [Object] state_requirement the requirement of the state we're unwinding to
- # @attr [Array] requirement_tree for the requirement we're relaxing
- # @attr [Array] conflicting_requirements the requirements that combined to cause the conflict
- # @attr [Array] requirement_trees for the conflict
- # @attr [Array] requirements_unwound_to_instead array of unwind requirements that were chosen over this unwind
- UnwindDetails = Struct.new(
- :state_index,
- :state_requirement,
- :requirement_tree,
- :conflicting_requirements,
- :requirement_trees,
- :requirements_unwound_to_instead
- )
-
- class UnwindDetails
- include Comparable
-
- # We compare UnwindDetails when choosing which state to unwind to. If
- # two options have the same state_index we prefer the one most
- # removed from a requirement that caused the conflict. Both options
- # would unwind to the same state, but a `grandparent` option will
- # filter out fewer of its possibilities after doing so - where a state
- # is both a `parent` and a `grandparent` to requirements that have
- # caused a conflict this is the correct behaviour.
- # @param [UnwindDetail] other UnwindDetail to be compared
- # @return [Integer] integer specifying ordering
- def <=>(other)
- if state_index > other.state_index
- 1
- elsif state_index == other.state_index
- reversed_requirement_tree_index <=> other.reversed_requirement_tree_index
- else
- -1
- end
- end
-
- # @return [Integer] index of state requirement in reversed requirement tree
- # (the conflicting requirement itself will be at position 0)
- def reversed_requirement_tree_index
- @reversed_requirement_tree_index ||=
- if state_requirement
- requirement_tree.reverse.index(state_requirement)
- else
- 999_999
- end
- end
-
- # @return [Boolean] where the requirement of the state we're unwinding
- # to directly caused the conflict. Note: in this case, it is
- # impossible for the state we're unwinding to to be a parent of
- # any of the other conflicting requirements (or we would have
- # circularity)
- def unwinding_to_primary_requirement?
- requirement_tree.last == state_requirement
- end
-
- # @return [Array] array of sub-dependencies to avoid when choosing a
- # new possibility for the state we've unwound to. Only relevant for
- # non-primary unwinds
- def sub_dependencies_to_avoid
- @requirements_to_avoid ||=
- requirement_trees.map do |tree|
- index = tree.index(state_requirement)
- tree[index + 1] if index
- end.compact
- end
-
- # @return [Array] array of all the requirements that led to the need for
- # this unwind
- def all_requirements
- @all_requirements ||= requirement_trees.flatten(1)
- end
- end
-
- # @return [SpecificationProvider] the provider that knows about
- # dependencies, requirements, specifications, versions, etc.
- attr_reader :specification_provider
-
- # @return [UI] the UI that knows how to communicate feedback about the
- # resolution process back to the user
- attr_reader :resolver_ui
-
- # @return [DependencyGraph] the base dependency graph to which
- # dependencies should be 'locked'
- attr_reader :base
-
- # @return [Array] the dependencies that were explicitly required
- attr_reader :original_requested
-
- # Initializes a new resolution.
- # @param [SpecificationProvider] specification_provider
- # see {#specification_provider}
- # @param [UI] resolver_ui see {#resolver_ui}
- # @param [Array] requested see {#original_requested}
- # @param [DependencyGraph] base see {#base}
- def initialize(specification_provider, resolver_ui, requested, base)
- @specification_provider = specification_provider
- @resolver_ui = resolver_ui
- @original_requested = requested
- @base = base
- @states = []
- @iteration_counter = 0
- @parents_of = Hash.new { |h, k| h[k] = [] }
- end
-
- # Resolves the {#original_requested} dependencies into a full dependency
- # graph
- # @raise [ResolverError] if successful resolution is impossible
- # @return [DependencyGraph] the dependency graph of successfully resolved
- # dependencies
- def resolve
- start_resolution
-
- while state
- break if !state.requirement && state.requirements.empty?
- indicate_progress
- if state.respond_to?(:pop_possibility_state) # DependencyState
- debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
- state.pop_possibility_state.tap do |s|
- if s
- states.push(s)
- activated.tag(s)
- end
- end
- end
- process_topmost_state
- end
-
- resolve_activated_specs
- ensure
- end_resolution
- end
-
- # @return [Integer] the number of resolver iterations in between calls to
- # {#resolver_ui}'s {UI#indicate_progress} method
- attr_accessor :iteration_rate
- private :iteration_rate
-
- # @return [Time] the time at which resolution began
- attr_accessor :started_at
- private :started_at
-
- # @return [Array<ResolutionState>] the stack of states for the resolution
- attr_accessor :states
- private :states
-
- private
-
- # Sets up the resolution process
- # @return [void]
- def start_resolution
- @started_at = Time.now
-
- push_initial_state
-
- debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
- resolver_ui.before_resolution
- end
-
- def resolve_activated_specs
- activated.vertices.each do |_, vertex|
- next unless vertex.payload
-
- latest_version = vertex.payload.possibilities.reverse_each.find do |possibility|
- vertex.requirements.all? { |req| requirement_satisfied_by?(req, activated, possibility) }
- end
-
- activated.set_payload(vertex.name, latest_version)
- end
- activated.freeze
- end
-
- # Ends the resolution process
- # @return [void]
- def end_resolution
- resolver_ui.after_resolution
- debug do
- "Finished resolution (#{@iteration_counter} steps) " \
- "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})"
- end
- debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state
- debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
- end
-
- require_relative 'state'
- require_relative 'modules/specification_provider'
-
- require_relative 'delegates/resolution_state'
- require_relative 'delegates/specification_provider'
-
- include Bundler::Molinillo::Delegates::ResolutionState
- include Bundler::Molinillo::Delegates::SpecificationProvider
-
- # Processes the topmost available {RequirementState} on the stack
- # @return [void]
- def process_topmost_state
- if possibility
- attempt_to_activate
- else
- create_conflict
- unwind_for_conflict
- end
- rescue CircularDependencyError => underlying_error
- create_conflict(underlying_error)
- unwind_for_conflict
- end
-
- # @return [Object] the current possibility that the resolution is trying
- # to activate
- def possibility
- possibilities.last
- end
-
- # @return [RequirementState] the current state the resolution is
- # operating upon
- def state
- states.last
- end
-
- # Creates and pushes the initial state for the resolution, based upon the
- # {#requested} dependencies
- # @return [void]
- def push_initial_state
- graph = DependencyGraph.new.tap do |dg|
- original_requested.each do |requested|
- vertex = dg.add_vertex(name_for(requested), nil, true)
- vertex.explicit_requirements << requested
- end
- dg.tag(:initial_state)
- end
-
- push_state_for_requirements(original_requested, true, graph)
- end
-
- # Unwinds the states stack because a conflict has been encountered
- # @return [void]
- def unwind_for_conflict
- details_for_unwind = build_details_for_unwind
- unwind_options = unused_unwind_options
- debug(depth) { "Unwinding for conflict: #{requirement} to #{details_for_unwind.state_index / 2}" }
- conflicts.tap do |c|
- sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1)
- raise_error_unless_state(c)
- activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
- state.conflicts = c
- state.unused_unwind_options = unwind_options
- filter_possibilities_after_unwind(details_for_unwind)
- index = states.size - 1
- @parents_of.each { |_, a| a.reject! { |i| i >= index } }
- state.unused_unwind_options.reject! { |uw| uw.state_index >= index }
- end
- end
-
- # Raises a VersionConflict error, or any underlying error, if there is no
- # current state
- # @return [void]
- def raise_error_unless_state(conflicts)
- return if state
-
- error = conflicts.values.map(&:underlying_error).compact.first
- raise error || VersionConflict.new(conflicts, specification_provider)
- end
-
- # @return [UnwindDetails] Details of the nearest index to which we could unwind
- def build_details_for_unwind
- # Get the possible unwinds for the current conflict
- current_conflict = conflicts[name]
- binding_requirements = binding_requirements_for_conflict(current_conflict)
- unwind_details = unwind_options_for_requirements(binding_requirements)
-
- last_detail_for_current_unwind = unwind_details.sort.last
- current_detail = last_detail_for_current_unwind
-
- # Look for past conflicts that could be unwound to affect the
- # requirement tree for the current conflict
- all_reqs = last_detail_for_current_unwind.all_requirements
- all_reqs_size = all_reqs.size
- relevant_unused_unwinds = unused_unwind_options.select do |alternative|
- diff_reqs = all_reqs - alternative.requirements_unwound_to_instead
- next if diff_reqs.size == all_reqs_size
- # Find the highest index unwind whilst looping through
- current_detail = alternative if alternative > current_detail
- alternative
- end
-
- # Add the current unwind options to the `unused_unwind_options` array.
- # The "used" option will be filtered out during `unwind_for_conflict`.
- state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 }
-
- # Update the requirements_unwound_to_instead on any relevant unused unwinds
- relevant_unused_unwinds.each do |d|
- (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
- end
- unwind_details.each do |d|
- (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
- end
-
- current_detail
- end
-
- # @param [Array<Object>] binding_requirements array of requirements that combine to create a conflict
- # @return [Array<UnwindDetails>] array of UnwindDetails that have a chance
- # of resolving the passed requirements
- def unwind_options_for_requirements(binding_requirements)
- unwind_details = []
-
- trees = []
- binding_requirements.reverse_each do |r|
- partial_tree = [r]
- trees << partial_tree
- unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, [])
-
- # If this requirement has alternative possibilities, check if any would
- # satisfy the other requirements that created this conflict
- requirement_state = find_state_for(r)
- if conflict_fixing_possibilities?(requirement_state, binding_requirements)
- unwind_details << UnwindDetails.new(
- states.index(requirement_state),
- r,
- partial_tree,
- binding_requirements,
- trees,
- []
- )
- end
-
- # Next, look at the parent of this requirement, and check if the requirement
- # could have been avoided if an alternative PossibilitySet had been chosen
- parent_r = parent_of(r)
- next if parent_r.nil?
- partial_tree.unshift(parent_r)
- requirement_state = find_state_for(parent_r)
- if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) }
- unwind_details << UnwindDetails.new(
- states.index(requirement_state),
- parent_r,
- partial_tree,
- binding_requirements,
- trees,
- []
- )
- end
-
- # Finally, look at the grandparent and up of this requirement, looking
- # for any possibilities that wouldn't create their parent requirement
- grandparent_r = parent_of(parent_r)
- until grandparent_r.nil?
- partial_tree.unshift(grandparent_r)
- requirement_state = find_state_for(grandparent_r)
- if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) }
- unwind_details << UnwindDetails.new(
- states.index(requirement_state),
- grandparent_r,
- partial_tree,
- binding_requirements,
- trees,
- []
- )
- end
- parent_r = grandparent_r
- grandparent_r = parent_of(parent_r)
- end
- end
-
- unwind_details
- end
-
- # @param [DependencyState] state
- # @param [Array] binding_requirements array of requirements
- # @return [Boolean] whether or not the given state has any possibilities
- # that could satisfy the given requirements
- def conflict_fixing_possibilities?(state, binding_requirements)
- return false unless state
-
- state.possibilities.any? do |possibility_set|
- possibility_set.possibilities.any? do |poss|
- possibility_satisfies_requirements?(poss, binding_requirements)
- end
- end
- end
-
- # Filter's a state's possibilities to remove any that would not fix the
- # conflict we've just rewound from
- # @param [UnwindDetails] unwind_details details of the conflict just
- # unwound from
- # @return [void]
- def filter_possibilities_after_unwind(unwind_details)
- return unless state && !state.possibilities.empty?
-
- if unwind_details.unwinding_to_primary_requirement?
- filter_possibilities_for_primary_unwind(unwind_details)
- else
- filter_possibilities_for_parent_unwind(unwind_details)
- end
- end
-
- # Filter's a state's possibilities to remove any that would not satisfy
- # the requirements in the conflict we've just rewound from
- # @param [UnwindDetails] unwind_details details of the conflict just unwound from
- # @return [void]
- def filter_possibilities_for_primary_unwind(unwind_details)
- unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
- unwinds_to_state << unwind_details
- unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements)
-
- state.possibilities.reject! do |possibility_set|
- possibility_set.possibilities.none? do |poss|
- unwind_requirement_sets.any? do |requirements|
- possibility_satisfies_requirements?(poss, requirements)
- end
- end
- end
- end
-
- # @param [Object] possibility a single possibility
- # @param [Array] requirements an array of requirements
- # @return [Boolean] whether the possibility satisfies all of the
- # given requirements
- def possibility_satisfies_requirements?(possibility, requirements)
- name = name_for(possibility)
-
- activated.tag(:swap)
- activated.set_payload(name, possibility) if activated.vertex_named(name)
- satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) }
- activated.rewind_to(:swap)
-
- satisfied
- end
-
- # Filter's a state's possibilities to remove any that would (eventually)
- # create a requirement in the conflict we've just rewound from
- # @param [UnwindDetails] unwind_details details of the conflict just unwound from
- # @return [void]
- def filter_possibilities_for_parent_unwind(unwind_details)
- unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
- unwinds_to_state << unwind_details
-
- primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
- parent_unwinds = unwinds_to_state.uniq - primary_unwinds
-
- allowed_possibility_sets = primary_unwinds.flat_map do |unwind|
- states[unwind.state_index].possibilities.select do |possibility_set|
- possibility_set.possibilities.any? do |poss|
- possibility_satisfies_requirements?(poss, unwind.conflicting_requirements)
- end
- end
- end
-
- requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid)
-
- state.possibilities.reject! do |possibility_set|
- !allowed_possibility_sets.include?(possibility_set) &&
- (requirements_to_avoid - possibility_set.dependencies).empty?
- end
- end
-
- # @param [Conflict] conflict
- # @return [Array] minimal array of requirements that would cause the passed
- # conflict to occur.
- def binding_requirements_for_conflict(conflict)
- return [conflict.requirement] if conflict.possibility.nil?
-
- possible_binding_requirements = conflict.requirements.values.flatten(1).uniq
-
- # When there's a `CircularDependency` error the conflicting requirement
- # (the one causing the circular) won't be `conflict.requirement`
- # (which won't be for the right state, because we won't have created it,
- # because it's circular).
- # We need to make sure we have that requirement in the conflict's list,
- # otherwise we won't be able to unwind properly, so we just return all
- # the requirements for the conflict.
- return possible_binding_requirements if conflict.underlying_error
-
- possibilities = search_for(conflict.requirement)
-
- # If all the requirements together don't filter out all possibilities,
- # then the only two requirements we need to consider are the initial one
- # (where the dependency's version was first chosen) and the last
- if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities)
- return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact
- end
-
- # Loop through the possible binding requirements, removing each one
- # that doesn't bind. Use a `reverse_each` as we want the earliest set of
- # binding requirements, and don't use `reject!` as we wish to refine the
- # array *on each iteration*.
- binding_requirements = possible_binding_requirements.dup
- possible_binding_requirements.reverse_each do |req|
- next if req == conflict.requirement
- unless binding_requirement_in_set?(req, binding_requirements, possibilities)
- binding_requirements -= [req]
- end
- end
-
- binding_requirements
- end
-
- # @param [Object] requirement we wish to check
- # @param [Array] possible_binding_requirements array of requirements
- # @param [Array] possibilities array of possibilities the requirements will be used to filter
- # @return [Boolean] whether or not the given requirement is required to filter
- # out all elements of the array of possibilities.
- def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
- possibilities.any? do |poss|
- possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement])
- end
- end
-
- # @param [Object] requirement
- # @return [Object] the requirement that led to `requirement` being added
- # to the list of requirements.
- def parent_of(requirement)
- return unless requirement
- return unless index = @parents_of[requirement].last
- return unless parent_state = @states[index]
- parent_state.requirement
- end
-
- # @param [String] name
- # @return [Object] the requirement that led to a version of a possibility
- # with the given name being activated.
- def requirement_for_existing_name(name)
- return nil unless vertex = activated.vertex_named(name)
- return nil unless vertex.payload
- states.find { |s| s.name == name }.requirement
- end
-
- # @param [Object] requirement
- # @return [ResolutionState] the state whose `requirement` is the given
- # `requirement`.
- def find_state_for(requirement)
- return nil unless requirement
- states.find { |i| requirement == i.requirement }
- end
-
- # @param [Object] underlying_error
- # @return [Conflict] a {Conflict} that reflects the failure to activate
- # the {#possibility} in conjunction with the current {#state}
- def create_conflict(underlying_error = nil)
- vertex = activated.vertex_named(name)
- locked_requirement = locked_requirement_named(name)
-
- requirements = {}
- unless vertex.explicit_requirements.empty?
- requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements
- end
- requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement
- vertex.incoming_edges.each do |edge|
- (requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement)
- end
-
- activated_by_name = {}
- activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload }
- conflicts[name] = Conflict.new(
- requirement,
- requirements,
- vertex.payload && vertex.payload.latest_version,
- possibility,
- locked_requirement,
- requirement_trees,
- activated_by_name,
- underlying_error
- )
- end
-
- # @return [Array<Array<Object>>] The different requirement
- # trees that led to every requirement for the current spec.
- def requirement_trees
- vertex = activated.vertex_named(name)
- vertex.requirements.map { |r| requirement_tree_for(r) }
- end
-
- # @param [Object] requirement
- # @return [Array<Object>] the list of requirements that led to
- # `requirement` being required.
- def requirement_tree_for(requirement)
- tree = []
- while requirement
- tree.unshift(requirement)
- requirement = parent_of(requirement)
- end
- tree
- end
-
- # Indicates progress roughly once every second
- # @return [void]
- def indicate_progress
- @iteration_counter += 1
- @progress_rate ||= resolver_ui.progress_rate
- if iteration_rate.nil?
- if Time.now - started_at >= @progress_rate
- self.iteration_rate = @iteration_counter
- end
- end
-
- if iteration_rate && (@iteration_counter % iteration_rate) == 0
- resolver_ui.indicate_progress
- end
- end
-
- # Calls the {#resolver_ui}'s {UI#debug} method
- # @param [Integer] depth the depth of the {#states} stack
- # @param [Proc] block a block that yields a {#to_s}
- # @return [void]
- def debug(depth = 0, &block)
- resolver_ui.debug(depth, &block)
- end
-
- # Attempts to activate the current {#possibility}
- # @return [void]
- def attempt_to_activate
- debug(depth) { 'Attempting to activate ' + possibility.to_s }
- existing_vertex = activated.vertex_named(name)
- if existing_vertex.payload
- debug(depth) { "Found existing spec (#{existing_vertex.payload})" }
- attempt_to_filter_existing_spec(existing_vertex)
- else
- latest = possibility.latest_version
- possibility.possibilities.select! do |possibility|
- requirement_satisfied_by?(requirement, activated, possibility)
- end
- if possibility.latest_version.nil?
- # ensure there's a possibility for better error messages
- possibility.possibilities << latest if latest
- create_conflict
- unwind_for_conflict
- else
- activate_new_spec
- end
- end
- end
-
- # Attempts to update the existing vertex's `PossibilitySet` with a filtered version
- # @return [void]
- def attempt_to_filter_existing_spec(vertex)
- filtered_set = filtered_possibility_set(vertex)
- if !filtered_set.possibilities.empty?
- activated.set_payload(name, filtered_set)
- new_requirements = requirements.dup
- push_state_for_requirements(new_requirements, false)
- else
- create_conflict
- debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" }
- unwind_for_conflict
- end
- end
-
- # Generates a filtered version of the existing vertex's `PossibilitySet` using the
- # current state's `requirement`
- # @param [Object] vertex existing vertex
- # @return [PossibilitySet] filtered possibility set
- def filtered_possibility_set(vertex)
- PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
- end
-
- # @param [String] requirement_name the spec name to search for
- # @return [Object] the locked spec named `requirement_name`, if one
- # is found on {#base}
- def locked_requirement_named(requirement_name)
- vertex = base.vertex_named(requirement_name)
- vertex && vertex.payload
- end
-
- # Add the current {#possibility} to the dependency graph of the current
- # {#state}
- # @return [void]
- def activate_new_spec
- conflicts.delete(name)
- debug(depth) { "Activated #{name} at #{possibility}" }
- activated.set_payload(name, possibility)
- require_nested_dependencies_for(possibility)
- end
-
- # Requires the dependencies that the recently activated spec has
- # @param [Object] possibility_set the PossibilitySet that has just been
- # activated
- # @return [void]
- def require_nested_dependencies_for(possibility_set)
- nested_dependencies = dependencies_for(possibility_set.latest_version)
- debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
- nested_dependencies.each do |d|
- activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d)
- parent_index = states.size - 1
- parents = @parents_of[d]
- parents << parent_index if parents.empty?
- end
-
- push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?)
- end
-
- # Pushes a new {DependencyState} that encapsulates both existing and new
- # requirements
- # @param [Array] new_requirements
- # @param [Boolean] requires_sort
- # @param [Object] new_activated
- # @return [void]
- def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated)
- new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
- new_requirement = nil
- loop do
- new_requirement = new_requirements.shift
- break if new_requirement.nil? || states.none? { |s| s.requirement == new_requirement }
- end
- new_name = new_requirement ? name_for(new_requirement) : ''.freeze
- possibilities = possibilities_for_requirement(new_requirement)
- handle_missing_or_push_dependency_state DependencyState.new(
- new_name, new_requirements, new_activated,
- new_requirement, possibilities, depth, conflicts.dup, unused_unwind_options.dup
- )
- end
-
- # Checks a proposed requirement with any existing locked requirement
- # before generating an array of possibilities for it.
- # @param [Object] requirement the proposed requirement
- # @param [Object] activated
- # @return [Array] possibilities
- def possibilities_for_requirement(requirement, activated = self.activated)
- return [] unless requirement
- if locked_requirement_named(name_for(requirement))
- return locked_requirement_possibility_set(requirement, activated)
- end
-
- group_possibilities(search_for(requirement))
- end
-
- # @param [Object] requirement the proposed requirement
- # @param [Object] activated
- # @return [Array] possibility set containing only the locked requirement, if any
- def locked_requirement_possibility_set(requirement, activated = self.activated)
- all_possibilities = search_for(requirement)
- locked_requirement = locked_requirement_named(name_for(requirement))
-
- # Longwinded way to build a possibilities array with either the locked
- # requirement or nothing in it. Required, since the API for
- # locked_requirement isn't guaranteed.
- locked_possibilities = all_possibilities.select do |possibility|
- requirement_satisfied_by?(locked_requirement, activated, possibility)
- end
-
- group_possibilities(locked_possibilities)
- end
-
- # Build an array of PossibilitySets, with each element representing a group of
- # dependency versions that all have the same sub-dependency version constraints
- # and are contiguous.
- # @param [Array] possibilities an array of possibilities
- # @return [Array<PossibilitySet>] an array of possibility sets
- def group_possibilities(possibilities)
- possibility_sets = []
- current_possibility_set = nil
-
- possibilities.reverse_each do |possibility|
- dependencies = dependencies_for(possibility)
- if current_possibility_set && dependencies_equal?(current_possibility_set.dependencies, dependencies)
- current_possibility_set.possibilities.unshift(possibility)
- else
- possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility]))
- current_possibility_set = possibility_sets.first
- end
- end
-
- possibility_sets
- end
-
- # Pushes a new {DependencyState}.
- # If the {#specification_provider} says to
- # {SpecificationProvider#allow_missing?} that particular requirement, and
- # there are no possibilities for that requirement, then `state` is not
- # pushed, and the vertex in {#activated} is removed, and we continue
- # resolving the remaining requirements.
- # @param [DependencyState] state
- # @return [void]
- def handle_missing_or_push_dependency_state(state)
- if state.requirement && state.possibilities.empty? && allow_missing?(state.requirement)
- state.activated.detach_vertex_named(state.name)
- push_state_for_requirements(state.requirements.dup, false, state.activated)
- else
- states.push(state).tap { activated.tag(state) }
- end
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb
deleted file mode 100644
index 95eaab5991..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'dependency_graph'
-
-module Bundler::Molinillo
- # This class encapsulates a dependency resolver.
- # The resolver is responsible for determining which set of dependencies to
- # activate, with feedback from the {#specification_provider}
- #
- #
- class Resolver
- require_relative 'resolution'
-
- # @return [SpecificationProvider] the specification provider used
- # in the resolution process
- attr_reader :specification_provider
-
- # @return [UI] the UI module used to communicate back to the user
- # during the resolution process
- attr_reader :resolver_ui
-
- # Initializes a new resolver.
- # @param [SpecificationProvider] specification_provider
- # see {#specification_provider}
- # @param [UI] resolver_ui
- # see {#resolver_ui}
- def initialize(specification_provider, resolver_ui)
- @specification_provider = specification_provider
- @resolver_ui = resolver_ui
- end
-
- # Resolves the requested dependencies into a {DependencyGraph},
- # locking to the base dependency graph (if specified)
- # @param [Array] requested an array of 'requested' dependencies that the
- # {#specification_provider} can understand
- # @param [DependencyGraph,nil] base the base dependency graph to which
- # dependencies should be 'locked'
- def resolve(requested, base = DependencyGraph.new)
- Resolution.new(specification_provider,
- resolver_ui,
- requested,
- base).
- resolve
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/state.rb b/lib/bundler/vendor/molinillo/lib/molinillo/state.rb
deleted file mode 100644
index 68fa1f54e3..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/state.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- # A state that a {Resolution} can be in
- # @attr [String] name the name of the current requirement
- # @attr [Array<Object>] requirements currently unsatisfied requirements
- # @attr [DependencyGraph] activated the graph of activated dependencies
- # @attr [Object] requirement the current requirement
- # @attr [Object] possibilities the possibilities to satisfy the current requirement
- # @attr [Integer] depth the depth of the resolution
- # @attr [Hash] conflicts unresolved conflicts, indexed by dependency name
- # @attr [Array<UnwindDetails>] unused_unwind_options unwinds for previous conflicts that weren't explored
- ResolutionState = Struct.new(
- :name,
- :requirements,
- :activated,
- :requirement,
- :possibilities,
- :depth,
- :conflicts,
- :unused_unwind_options
- )
-
- class ResolutionState
- # Returns an empty resolution state
- # @return [ResolutionState] an empty state
- def self.empty
- new(nil, [], DependencyGraph.new, nil, nil, 0, {}, [])
- end
- end
-
- # A state that encapsulates a set of {#requirements} with an {Array} of
- # possibilities
- class DependencyState < ResolutionState
- # Removes a possibility from `self`
- # @return [PossibilityState] a state with a single possibility,
- # the possibility that was removed from `self`
- def pop_possibility_state
- PossibilityState.new(
- name,
- requirements.dup,
- activated,
- requirement,
- [possibilities.pop],
- depth + 1,
- conflicts.dup,
- unused_unwind_options.dup
- ).tap do |state|
- state.activated.tag(state)
- end
- end
- end
-
- # A state that encapsulates a single possibility to fulfill the given
- # {#requirement}
- class PossibilityState < ResolutionState
- end
-end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub.rb
new file mode 100644
index 0000000000..eaaba3fc98
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub.rb
@@ -0,0 +1,31 @@
+require_relative "pub_grub/package"
+require_relative "pub_grub/static_package_source"
+require_relative "pub_grub/term"
+require_relative "pub_grub/version_range"
+require_relative "pub_grub/version_constraint"
+require_relative "pub_grub/version_union"
+require_relative "pub_grub/version_solver"
+require_relative "pub_grub/incompatibility"
+require_relative 'pub_grub/solve_failure'
+require_relative 'pub_grub/failure_writer'
+require_relative 'pub_grub/version'
+
+module Bundler::PubGrub
+ class << self
+ attr_writer :logger
+
+ def logger
+ @logger || default_logger
+ end
+
+ private
+
+ def default_logger
+ require "logger"
+
+ logger = ::Logger.new(STDERR)
+ logger.level = $DEBUG ? ::Logger::DEBUG : ::Logger::WARN
+ @logger = logger
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb
new file mode 100644
index 0000000000..2236a97b5b
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb
@@ -0,0 +1,20 @@
+module Bundler::PubGrub
+ class Assignment
+ attr_reader :term, :cause, :decision_level, :index
+ def initialize(term, cause, decision_level, index)
+ @term = term
+ @cause = cause
+ @decision_level = decision_level
+ @index = index
+ end
+
+ def self.decision(package, version, decision_level, index)
+ term = Term.new(VersionConstraint.exact(package, version), true)
+ new(term, :decision, decision_level, index)
+ end
+
+ def decision?
+ cause == :decision
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb
new file mode 100644
index 0000000000..dce20d37ad
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb
@@ -0,0 +1,189 @@
+require_relative 'version_constraint'
+require_relative 'incompatibility'
+
+module Bundler::PubGrub
+ # Types:
+ #
+ # Where possible, Bundler::PubGrub will accept user-defined types, so long as they quack.
+ #
+ # ## "Package":
+ #
+ # This class will be used to represent the various packages being solved for.
+ # .to_s will be called when displaying errors and debugging info, it should
+ # probably return the package's name.
+ # It must also have a reasonable definition of #== and #hash
+ #
+ # Example classes: String ("rails")
+ #
+ #
+ # ## "Version":
+ #
+ # This class will be used to represent a single version number.
+ #
+ # Versions don't need to store their associated package, however they will
+ # only be compared against other versions of the same package.
+ #
+ # It must be Comparible (and implement <=> reasonably)
+ #
+ # Example classes: Gem::Version, Integer
+ #
+ #
+ # ## "Dependency"
+ #
+ # This class represents the requirement one package has on another. It is
+ # returned by dependencies_for(package, version) and will be passed to
+ # parse_dependency to convert it to a format Bundler::PubGrub understands.
+ #
+ # It must also have a reasonable definition of #==
+ #
+ # Example classes: String ("~> 1.0"), Gem::Requirement
+ #
+ class BasicPackageSource
+ # Override me!
+ #
+ # This is called per package to find all possible versions of a package.
+ #
+ # It is called at most once per-package
+ #
+ # Returns: Array of versions for a package, in preferred order of selection
+ def all_versions_for(package)
+ raise NotImplementedError
+ end
+
+ # Override me!
+ #
+ # Returns: Hash in the form of { package => requirement, ... }
+ def dependencies_for(package, version)
+ raise NotImplementedError
+ end
+
+ # Override me!
+ #
+ # Convert a (user-defined) dependency into a format Bundler::PubGrub understands.
+ #
+ # Package is passed to this method but for many implementations is not
+ # needed.
+ #
+ # Returns: either a Bundler::PubGrub::VersionRange, Bundler::PubGrub::VersionUnion, or a
+ # Bundler::PubGrub::VersionConstraint
+ def parse_dependency(package, dependency)
+ raise NotImplementedError
+ end
+
+ # Override me!
+ #
+ # If not overridden, this will call dependencies_for with the root package.
+ #
+ # Returns: Hash in the form of { package => requirement, ... } (see dependencies_for)
+ def root_dependencies
+ dependencies_for(@root_package, @root_version)
+ end
+
+ # Override me (maybe)
+ #
+ # If not overridden, the order returned by all_versions_for will be used
+ #
+ # Returns: Array of versions in preferred order
+ def sort_versions_by_preferred(package, sorted_versions)
+ indexes = @version_indexes[package]
+ sorted_versions.sort_by { |version| indexes[version] }
+ end
+
+ def initialize
+ @root_package = Package.root
+ @root_version = Package.root_version
+
+ @cached_versions = Hash.new do |h,k|
+ if k == @root_package
+ h[k] = [@root_version]
+ else
+ h[k] = all_versions_for(k)
+ end
+ end
+ @sorted_versions = Hash.new { |h,k| h[k] = @cached_versions[k].sort }
+ @version_indexes = Hash.new { |h,k| h[k] = @cached_versions[k].each.with_index.to_h }
+
+ @cached_dependencies = Hash.new do |packages, package|
+ if package == @root_package
+ packages[package] = {
+ @root_version => root_dependencies
+ }
+ else
+ packages[package] = Hash.new do |versions, version|
+ versions[version] = dependencies_for(package, version)
+ end
+ end
+ end
+ end
+
+ def versions_for(package, range=VersionRange.any)
+ versions = range.select_versions(@sorted_versions[package])
+
+ # Conditional avoids (among other things) calling
+ # sort_versions_by_preferred with the root package
+ if versions.size > 1
+ sort_versions_by_preferred(package, versions)
+ else
+ versions
+ end
+ end
+
+ def no_versions_incompatibility_for(_package, unsatisfied_term)
+ cause = Incompatibility::NoVersions.new(unsatisfied_term)
+
+ Incompatibility.new([unsatisfied_term], cause: cause)
+ end
+
+ def incompatibilities_for(package, version)
+ package_deps = @cached_dependencies[package]
+ sorted_versions = @sorted_versions[package]
+ package_deps[version].map do |dep_package, dep_constraint_name|
+ low = high = sorted_versions.index(version)
+
+ # find version low such that all >= low share the same dep
+ while low > 0 &&
+ package_deps[sorted_versions[low - 1]][dep_package] == dep_constraint_name
+ low -= 1
+ end
+ low =
+ if low == 0
+ nil
+ else
+ sorted_versions[low]
+ end
+
+ # find version high such that all < high share the same dep
+ while high < sorted_versions.length &&
+ package_deps[sorted_versions[high]][dep_package] == dep_constraint_name
+ high += 1
+ end
+ high =
+ if high == sorted_versions.length
+ nil
+ else
+ sorted_versions[high]
+ end
+
+ range = VersionRange.new(min: low, max: high, include_min: true)
+
+ self_constraint = VersionConstraint.new(package, range: range)
+
+ if !@packages.include?(dep_package)
+ # no such package -> this version is invalid
+ end
+
+ dep_constraint = parse_dependency(dep_package, dep_constraint_name)
+ if !dep_constraint
+ # falsey indicates this dependency was invalid
+ cause = Bundler::PubGrub::Incompatibility::InvalidDependency.new(dep_package, dep_constraint_name)
+ return [Incompatibility.new([Term.new(self_constraint, true)], cause: cause)]
+ elsif !dep_constraint.is_a?(VersionConstraint)
+ # Upgrade range/union to VersionConstraint
+ dep_constraint = VersionConstraint.new(dep_package, range: dep_constraint)
+ end
+
+ Incompatibility.new([Term.new(self_constraint, true), Term.new(dep_constraint, false)], cause: :dependency)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb
new file mode 100644
index 0000000000..ee099b23f4
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb
@@ -0,0 +1,182 @@
+module Bundler::PubGrub
+ class FailureWriter
+ def initialize(root)
+ @root = root
+
+ # { Incompatibility => Integer }
+ @derivations = {}
+
+ # [ [ String, Integer or nil ] ]
+ @lines = []
+
+ # { Incompatibility => Integer }
+ @line_numbers = {}
+
+ count_derivations(root)
+ end
+
+ def write
+ return @root.to_s unless @root.conflict?
+
+ visit(@root)
+
+ padding = @line_numbers.empty? ? 0 : "(#{@line_numbers.values.last}) ".length
+
+ @lines.map do |message, number|
+ next "" if message.empty?
+
+ lead = number ? "(#{number}) " : ""
+ lead = lead.ljust(padding)
+ message = message.gsub("\n", "\n" + " " * (padding + 2))
+ "#{lead}#{message}"
+ end.join("\n")
+ end
+
+ private
+
+ def write_line(incompatibility, message, numbered:)
+ if numbered
+ number = @line_numbers.length + 1
+ @line_numbers[incompatibility] = number
+ end
+
+ @lines << [message, number]
+ end
+
+ def visit(incompatibility, conclusion: false)
+ raise unless incompatibility.conflict?
+
+ numbered = conclusion || @derivations[incompatibility] > 1;
+ conjunction = conclusion || incompatibility == @root ? "So," : "And"
+
+ cause = incompatibility.cause
+
+ if cause.conflict.conflict? && cause.other.conflict?
+ conflict_line = @line_numbers[cause.conflict]
+ other_line = @line_numbers[cause.other]
+
+ if conflict_line && other_line
+ write_line(
+ incompatibility,
+ "Because #{cause.conflict} (#{conflict_line})\nand #{cause.other} (#{other_line}),\n#{incompatibility}.",
+ numbered: numbered
+ )
+ elsif conflict_line || other_line
+ with_line = conflict_line ? cause.conflict : cause.other
+ without_line = conflict_line ? cause.other : cause.conflict
+ line = @line_numbers[with_line]
+
+ visit(without_line);
+ write_line(
+ incompatibility,
+ "#{conjunction} because #{with_line} (#{line}),\n#{incompatibility}.",
+ numbered: numbered
+ )
+ else
+ single_line_conflict = single_line?(cause.conflict.cause)
+ single_line_other = single_line?(cause.other.cause)
+
+ if single_line_conflict || single_line_other
+ first = single_line_other ? cause.conflict : cause.other
+ second = single_line_other ? cause.other : cause.conflict
+ visit(first)
+ visit(second)
+ write_line(
+ incompatibility,
+ "Thus, #{incompatibility}.",
+ numbered: numbered
+ )
+ else
+ visit(cause.conflict, conclusion: true)
+ @lines << ["", nil]
+ visit(cause.other)
+
+ write_line(
+ incompatibility,
+ "#{conjunction} because #{cause.conflict} (#{@line_numbers[cause.conflict]}),\n#{incompatibility}.",
+ numbered: numbered
+ )
+ end
+ end
+ elsif cause.conflict.conflict? || cause.other.conflict?
+ derived = cause.conflict.conflict? ? cause.conflict : cause.other
+ ext = cause.conflict.conflict? ? cause.other : cause.conflict
+
+ derived_line = @line_numbers[derived]
+ if derived_line
+ write_line(
+ incompatibility,
+ "Because #{ext}\nand #{derived} (#{derived_line}),\n#{incompatibility}.",
+ numbered: numbered
+ )
+ elsif collapsible?(derived)
+ derived_cause = derived.cause
+ if derived_cause.conflict.conflict?
+ collapsed_derived = derived_cause.conflict
+ collapsed_ext = derived_cause.other
+ else
+ collapsed_derived = derived_cause.other
+ collapsed_ext = derived_cause.conflict
+ end
+
+ visit(collapsed_derived)
+
+ write_line(
+ incompatibility,
+ "#{conjunction} because #{collapsed_ext}\nand #{ext},\n#{incompatibility}.",
+ numbered: numbered
+ )
+ else
+ visit(derived)
+ write_line(
+ incompatibility,
+ "#{conjunction} because #{ext},\n#{incompatibility}.",
+ numbered: numbered
+ )
+ end
+ else
+ write_line(
+ incompatibility,
+ "Because #{cause.conflict}\nand #{cause.other},\n#{incompatibility}.",
+ numbered: numbered
+ )
+ end
+ end
+
+ def single_line?(cause)
+ !cause.conflict.conflict? && !cause.other.conflict?
+ end
+
+ def collapsible?(incompatibility)
+ return false if @derivations[incompatibility] > 1
+
+ cause = incompatibility.cause
+ # If incompatibility is derived from two derived incompatibilities,
+ # there are too many transitive causes to display concisely.
+ return false if cause.conflict.conflict? && cause.other.conflict?
+
+ # If incompatibility is derived from two external incompatibilities, it
+ # tends to be confusing to collapse it.
+ return false unless cause.conflict.conflict? || cause.other.conflict?
+
+ # If incompatibility's internal cause is numbered, collapsing it would
+ # get too noisy.
+ complex = cause.conflict.conflict? ? cause.conflict : cause.other
+
+ !@line_numbers.has_key?(complex)
+ end
+
+ def count_derivations(incompatibility)
+ if @derivations.has_key?(incompatibility)
+ @derivations[incompatibility] += 1
+ else
+ @derivations[incompatibility] = 1
+ if incompatibility.conflict?
+ cause = incompatibility.cause
+ count_derivations(cause.conflict)
+ count_derivations(cause.other)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb
new file mode 100644
index 0000000000..239eaf3401
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb
@@ -0,0 +1,150 @@
+module Bundler::PubGrub
+ class Incompatibility
+ ConflictCause = Struct.new(:incompatibility, :satisfier) do
+ alias_method :conflict, :incompatibility
+ alias_method :other, :satisfier
+ end
+
+ InvalidDependency = Struct.new(:package, :constraint) do
+ end
+
+ NoVersions = Struct.new(:constraint) do
+ end
+
+ attr_reader :terms, :cause
+
+ def initialize(terms, cause:, custom_explanation: nil)
+ @cause = cause
+ @terms = cleanup_terms(terms)
+ @custom_explanation = custom_explanation
+
+ if cause == :dependency && @terms.length != 2
+ raise ArgumentError, "a dependency Incompatibility must have exactly two terms. Got #{@terms.inspect}"
+ end
+ end
+
+ def hash
+ cause.hash ^ terms.hash
+ end
+
+ def eql?(other)
+ cause.eql?(other.cause) &&
+ terms.eql?(other.terms)
+ end
+
+ def failure?
+ terms.empty? || (terms.length == 1 && Package.root?(terms[0].package) && terms[0].positive?)
+ end
+
+ def conflict?
+ ConflictCause === cause
+ end
+
+ # Returns all external incompatibilities in this incompatibility's
+ # derivation graph
+ def external_incompatibilities
+ if conflict?
+ [
+ cause.conflict,
+ cause.other
+ ].flat_map(&:external_incompatibilities)
+ else
+ [this]
+ end
+ end
+
+ def to_s
+ return @custom_explanation if @custom_explanation
+
+ case cause
+ when :root
+ "(root dependency)"
+ when :dependency
+ "#{terms[0].to_s(allow_every: true)} depends on #{terms[1].invert}"
+ when Bundler::PubGrub::Incompatibility::InvalidDependency
+ "#{terms[0].to_s(allow_every: true)} depends on unknown package #{cause.package}"
+ when Bundler::PubGrub::Incompatibility::NoVersions
+ "no versions satisfy #{cause.constraint}"
+ when Bundler::PubGrub::Incompatibility::ConflictCause
+ if failure?
+ "version solving has failed"
+ elsif terms.length == 1
+ term = terms[0]
+ if term.positive?
+ if term.constraint.any?
+ "#{term.package} cannot be used"
+ else
+ "#{term.to_s(allow_every: true)} cannot be used"
+ end
+ else
+ "#{term.invert} is required"
+ end
+ else
+ if terms.all?(&:positive?)
+ if terms.length == 2
+ "#{terms[0].to_s(allow_every: true)} is incompatible with #{terms[1]}"
+ else
+ "one of #{terms.map(&:to_s).join(" or ")} must be false"
+ end
+ elsif terms.all?(&:negative?)
+ if terms.length == 2
+ "either #{terms[0].invert} or #{terms[1].invert}"
+ else
+ "one of #{terms.map(&:invert).join(" or ")} must be true";
+ end
+ else
+ positive = terms.select(&:positive?)
+ negative = terms.select(&:negative?).map(&:invert)
+
+ if positive.length == 1
+ "#{positive[0].to_s(allow_every: true)} requires #{negative.join(" or ")}"
+ else
+ "if #{positive.join(" and ")} then #{negative.join(" or ")}"
+ end
+ end
+ end
+ else
+ raise "unhandled cause: #{cause.inspect}"
+ end
+ end
+
+ def inspect
+ "#<#{self.class} #{to_s}>"
+ end
+
+ def pretty_print(q)
+ q.group 2, "#<#{self.class}", ">" do
+ q.breakable
+ q.text to_s
+
+ q.breakable
+ q.text " caused by "
+ q.pp @cause
+ end
+ end
+
+ private
+
+ def cleanup_terms(terms)
+ terms.each do |term|
+ raise "#{term.inspect} must be a term" unless term.is_a?(Term)
+ end
+
+ if terms.length != 1 && ConflictCause === cause
+ terms = terms.reject do |term|
+ term.positive? && Package.root?(term.package)
+ end
+ end
+
+ # Optimized simple cases
+ return terms if terms.length <= 1
+ return terms if terms.length == 2 && terms[0].package != terms[1].package
+
+ terms.group_by(&:package).map do |package, common_terms|
+ common_terms.inject do |acc, term|
+ acc.intersect(term)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb
new file mode 100644
index 0000000000..efb9d3da16
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Bundler::PubGrub
+ class Package
+
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ def inspect
+ "#<#{self.class} #{name.inspect}>"
+ end
+
+ def <=>(other)
+ name <=> other.name
+ end
+
+ ROOT = Package.new(:root)
+ ROOT_VERSION = 0
+
+ def self.root
+ ROOT
+ end
+
+ def self.root_version
+ ROOT_VERSION
+ end
+
+ def self.root?(package)
+ if package.respond_to?(:root?)
+ package.root?
+ else
+ package == root
+ end
+ end
+
+ def to_s
+ name.to_s
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb
new file mode 100644
index 0000000000..4c4b8ca844
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb
@@ -0,0 +1,121 @@
+require_relative 'assignment'
+
+module Bundler::PubGrub
+ class PartialSolution
+ attr_reader :assignments, :decisions
+ attr_reader :attempted_solutions
+
+ def initialize
+ reset!
+
+ @attempted_solutions = 1
+ @backtracking = false
+ end
+
+ def decision_level
+ @decisions.length
+ end
+
+ def relation(term)
+ package = term.package
+ return :overlap if !@terms.key?(package)
+
+ @relation_cache[package][term] ||=
+ @terms[package].relation(term)
+ end
+
+ def satisfies?(term)
+ relation(term) == :subset
+ end
+
+ def derive(term, cause)
+ add_assignment(Assignment.new(term, cause, decision_level, assignments.length))
+ end
+
+ def satisfier(term)
+ assignment =
+ @assignments_by[term.package].bsearch do |assignment_by|
+ @cumulative_assignments[assignment_by].satisfies?(term)
+ end
+
+ assignment || raise("#{term} unsatisfied")
+ end
+
+ # A list of unsatisfied terms
+ def unsatisfied
+ @required.keys.reject do |package|
+ @decisions.key?(package)
+ end.map do |package|
+ @terms[package]
+ end
+ end
+
+ def decide(package, version)
+ @attempted_solutions += 1 if @backtracking
+ @backtracking = false;
+
+ decisions[package] = version
+ assignment = Assignment.decision(package, version, decision_level, assignments.length)
+ add_assignment(assignment)
+ end
+
+ def backtrack(previous_level)
+ @backtracking = true
+
+ new_assignments = assignments.select do |assignment|
+ assignment.decision_level <= previous_level
+ end
+
+ new_decisions = Hash[decisions.first(previous_level)]
+
+ reset!
+
+ @decisions = new_decisions
+
+ new_assignments.each do |assignment|
+ add_assignment(assignment)
+ end
+ end
+
+ private
+
+ def reset!
+ # { Array<Assignment> }
+ @assignments = []
+
+ # { Package => Array<Assignment> }
+ @assignments_by = Hash.new { |h,k| h[k] = [] }
+ @cumulative_assignments = {}.compare_by_identity
+
+ # { Package => Package::Version }
+ @decisions = {}
+
+ # { Package => Term }
+ @terms = {}
+ @relation_cache = Hash.new { |h,k| h[k] = {} }
+
+ # { Package => Boolean }
+ @required = {}
+ end
+
+ def add_assignment(assignment)
+ term = assignment.term
+ package = term.package
+
+ @assignments << assignment
+ @assignments_by[package] << assignment
+
+ @required[package] = true if term.positive?
+
+ if @terms.key?(package)
+ old_term = @terms[package]
+ @terms[package] = old_term.intersect(term)
+ else
+ @terms[package] = term
+ end
+ @relation_cache[package].clear
+
+ @cumulative_assignments[assignment] = @terms[package]
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb
new file mode 100644
index 0000000000..245c23be22
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb
@@ -0,0 +1,45 @@
+module Bundler::PubGrub
+ module RubyGems
+ extend self
+
+ def requirement_to_range(requirement)
+ ranges = requirement.requirements.map do |(op, ver)|
+ case op
+ when "~>"
+ name = "~> #{ver}"
+ bump = ver.class.new(ver.bump.to_s + ".A")
+ VersionRange.new(name: name, min: ver, max: bump, include_min: true)
+ when ">"
+ VersionRange.new(min: ver)
+ when ">="
+ VersionRange.new(min: ver, include_min: true)
+ when "<"
+ VersionRange.new(max: ver)
+ when "<="
+ VersionRange.new(max: ver, include_max: true)
+ when "="
+ VersionRange.new(min: ver, max: ver, include_min: true, include_max: true)
+ when "!="
+ VersionRange.new(min: ver, max: ver, include_min: true, include_max: true).invert
+ else
+ raise "bad version specifier: #{op}"
+ end
+ end
+
+ ranges.inject(&:intersect)
+ end
+
+ def requirement_to_constraint(package, requirement)
+ Bundler::PubGrub::VersionConstraint.new(package, range: requirement_to_range(requirement))
+ end
+
+ def parse_range(dep)
+ requirement_to_range(Gem::Requirement.new(dep))
+ end
+
+ def parse_constraint(package, dep)
+ range = parse_range(dep)
+ Bundler::PubGrub::VersionConstraint.new(package, range: range)
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb
new file mode 100644
index 0000000000..961a7a7c0c
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb
@@ -0,0 +1,19 @@
+require_relative 'failure_writer'
+
+module Bundler::PubGrub
+ class SolveFailure < StandardError
+ attr_reader :incompatibility
+
+ def initialize(incompatibility)
+ @incompatibility = incompatibility
+ end
+
+ def to_s
+ "Could not find compatible versions\n\n#{explanation}"
+ end
+
+ def explanation
+ @explanation ||= FailureWriter.new(@incompatibility).write
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb
new file mode 100644
index 0000000000..4bf61461b2
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb
@@ -0,0 +1,60 @@
+require_relative 'package'
+require_relative 'version_constraint'
+require_relative 'incompatibility'
+require_relative 'basic_package_source'
+
+module Bundler::PubGrub
+ class StaticPackageSource < BasicPackageSource
+ class DSL
+ def initialize(packages, root_deps)
+ @packages = packages
+ @root_deps = root_deps
+ end
+
+ def root(deps:)
+ @root_deps.update(deps)
+ end
+
+ def add(name, version, deps: {})
+ version = Gem::Version.new(version)
+ @packages[name] ||= {}
+ raise ArgumentError, "#{name} #{version} declared twice" if @packages[name].key?(version)
+ @packages[name][version] = clean_deps(name, version, deps)
+ end
+
+ private
+
+ # Exclude redundant self-referencing dependencies
+ def clean_deps(name, version, deps)
+ deps.reject {|dep_name, req| name == dep_name && Bundler::PubGrub::RubyGems.parse_range(req).include?(version) }
+ end
+ end
+
+ def initialize
+ @root_deps = {}
+ @packages = {}
+
+ yield DSL.new(@packages, @root_deps)
+
+ super()
+ end
+
+ def all_versions_for(package)
+ @packages[package].keys
+ end
+
+ def root_dependencies
+ @root_deps
+ end
+
+ def dependencies_for(package, version)
+ @packages[package][version]
+ end
+
+ def parse_dependency(package, dependency)
+ return false unless @packages.key?(package)
+
+ Bundler::PubGrub::RubyGems.parse_constraint(package, dependency)
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb
new file mode 100644
index 0000000000..1d0f763378
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb
@@ -0,0 +1,105 @@
+module Bundler::PubGrub
+ class Term
+ attr_reader :package, :constraint, :positive
+
+ def initialize(constraint, positive)
+ @constraint = constraint
+ @package = @constraint.package
+ @positive = positive
+ end
+
+ def to_s(allow_every: false)
+ if positive
+ @constraint.to_s(allow_every: allow_every)
+ else
+ "not #{@constraint}"
+ end
+ end
+
+ def hash
+ constraint.hash ^ positive.hash
+ end
+
+ def eql?(other)
+ positive == other.positive &&
+ constraint.eql?(other.constraint)
+ end
+
+ def invert
+ self.class.new(@constraint, !@positive)
+ end
+ alias_method :inverse, :invert
+
+ def intersect(other)
+ raise ArgumentError, "packages must match" if package != other.package
+
+ if positive? && other.positive?
+ self.class.new(constraint.intersect(other.constraint), true)
+ elsif negative? && other.negative?
+ self.class.new(constraint.union(other.constraint), false)
+ else
+ positive = positive? ? self : other
+ negative = negative? ? self : other
+ self.class.new(positive.constraint.intersect(negative.constraint.invert), true)
+ end
+ end
+
+ def difference(other)
+ intersect(other.invert)
+ end
+
+ def relation(other)
+ if positive? && other.positive?
+ constraint.relation(other.constraint)
+ elsif negative? && other.positive?
+ if constraint.allows_all?(other.constraint)
+ :disjoint
+ else
+ :overlap
+ end
+ elsif positive? && other.negative?
+ if !other.constraint.allows_any?(constraint)
+ :subset
+ elsif other.constraint.allows_all?(constraint)
+ :disjoint
+ else
+ :overlap
+ end
+ elsif negative? && other.negative?
+ if constraint.allows_all?(other.constraint)
+ :subset
+ else
+ :overlap
+ end
+ else
+ raise
+ end
+ end
+
+ def normalized_constraint
+ @normalized_constraint ||= positive ? constraint : constraint.invert
+ end
+
+ def satisfies?(other)
+ raise ArgumentError, "packages must match" unless package == other.package
+
+ relation(other) == :subset
+ end
+
+ def positive?
+ @positive
+ end
+
+ def negative?
+ !positive?
+ end
+
+ def empty?
+ @empty ||= normalized_constraint.empty?
+ end
+
+ def inspect
+ "#<#{self.class} #{self}>"
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb
new file mode 100644
index 0000000000..d7984b3863
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb
@@ -0,0 +1,3 @@
+module Bundler::PubGrub
+ VERSION = "0.5.0"
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb
new file mode 100644
index 0000000000..b71f3eaf53
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb
@@ -0,0 +1,129 @@
+require_relative 'version_range'
+
+module Bundler::PubGrub
+ class VersionConstraint
+ attr_reader :package, :range
+
+ # @param package [Bundler::PubGrub::Package]
+ # @param range [Bundler::PubGrub::VersionRange]
+ def initialize(package, range: nil)
+ @package = package
+ @range = range
+ end
+
+ def hash
+ package.hash ^ range.hash
+ end
+
+ def ==(other)
+ package == other.package &&
+ range == other.range
+ end
+
+ def eql?(other)
+ package.eql?(other.package) &&
+ range.eql?(other.range)
+ end
+
+ class << self
+ def exact(package, version)
+ range = VersionRange.new(min: version, max: version, include_min: true, include_max: true)
+ new(package, range: range)
+ end
+
+ def any(package)
+ new(package, range: VersionRange.any)
+ end
+
+ def empty(package)
+ new(package, range: VersionRange.empty)
+ end
+ end
+
+ def intersect(other)
+ unless package == other.package
+ raise ArgumentError, "Can only intersect between VersionConstraint of the same package"
+ end
+
+ self.class.new(package, range: range.intersect(other.range))
+ end
+
+ def union(other)
+ unless package == other.package
+ raise ArgumentError, "Can only intersect between VersionConstraint of the same package"
+ end
+
+ self.class.new(package, range: range.union(other.range))
+ end
+
+ def invert
+ new_range = range.invert
+ self.class.new(package, range: new_range)
+ end
+
+ def difference(other)
+ intersect(other.invert)
+ end
+
+ def allows_all?(other)
+ range.allows_all?(other.range)
+ end
+
+ def allows_any?(other)
+ range.intersects?(other.range)
+ end
+
+ def subset?(other)
+ other.allows_all?(self)
+ end
+
+ def overlap?(other)
+ other.allows_any?(self)
+ end
+
+ def disjoint?(other)
+ !overlap?(other)
+ end
+
+ def relation(other)
+ if subset?(other)
+ :subset
+ elsif overlap?(other)
+ :overlap
+ else
+ :disjoint
+ end
+ end
+
+ def to_s(allow_every: false)
+ if Package.root?(package)
+ package.to_s
+ elsif allow_every && any?
+ "every version of #{package}"
+ else
+ "#{package} #{constraint_string}"
+ end
+ end
+
+ def constraint_string
+ if any?
+ ">= 0"
+ else
+ range.to_s
+ end
+ end
+
+ def empty?
+ range.empty?
+ end
+
+ # Does this match every version of the package
+ def any?
+ range.any?
+ end
+
+ def inspect
+ "#<#{self.class} #{self}>"
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb
new file mode 100644
index 0000000000..8d73c3f7b5
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb
@@ -0,0 +1,411 @@
+# frozen_string_literal: true
+
+module Bundler::PubGrub
+ class VersionRange
+ attr_reader :min, :max, :include_min, :include_max
+
+ alias_method :include_min?, :include_min
+ alias_method :include_max?, :include_max
+
+ class Empty < VersionRange
+ undef_method :min, :max
+ undef_method :include_min, :include_min?
+ undef_method :include_max, :include_max?
+
+ def initialize
+ end
+
+ def empty?
+ true
+ end
+
+ def eql?(other)
+ other.empty?
+ end
+
+ def hash
+ [].hash
+ end
+
+ def intersects?(_)
+ false
+ end
+
+ def intersect(other)
+ self
+ end
+
+ def allows_all?(other)
+ other.empty?
+ end
+
+ def include?(_)
+ false
+ end
+
+ def any?
+ false
+ end
+
+ def to_s
+ "(no versions)"
+ end
+
+ def ==(other)
+ other.class == self.class
+ end
+
+ def invert
+ VersionRange.any
+ end
+
+ def select_versions(_)
+ []
+ end
+ end
+
+ EMPTY = Empty.new
+ Empty.singleton_class.undef_method(:new)
+
+ def self.empty
+ EMPTY
+ end
+
+ def self.any
+ new
+ end
+
+ def initialize(min: nil, max: nil, include_min: false, include_max: false, name: nil)
+ @min = min
+ @max = max
+ @include_min = include_min
+ @include_max = include_max
+ @name = name
+ end
+
+ def hash
+ @hash ||= min.hash ^ max.hash ^ include_min.hash ^ include_max.hash
+ end
+
+ def eql?(other)
+ if other.is_a?(VersionRange)
+ !other.empty? &&
+ min.eql?(other.min) &&
+ max.eql?(other.max) &&
+ include_min.eql?(other.include_min) &&
+ include_max.eql?(other.include_max)
+ else
+ ranges.eql?(other.ranges)
+ end
+ end
+
+ def ranges
+ [self]
+ end
+
+ def include?(version)
+ compare_version(version) == 0
+ end
+
+ # Partitions passed versions into [lower, within, higher]
+ #
+ # versions must be sorted
+ def partition_versions(versions)
+ min_index =
+ if !min || versions.empty?
+ 0
+ elsif include_min?
+ (0..versions.size).bsearch { |i| versions[i].nil? || versions[i] >= min }
+ else
+ (0..versions.size).bsearch { |i| versions[i].nil? || versions[i] > min }
+ end
+
+ lower = versions.slice(0, min_index)
+ versions = versions.slice(min_index, versions.size)
+
+ max_index =
+ if !max || versions.empty?
+ versions.size
+ elsif include_max?
+ (0..versions.size).bsearch { |i| versions[i].nil? || versions[i] > max }
+ else
+ (0..versions.size).bsearch { |i| versions[i].nil? || versions[i] >= max }
+ end
+
+ [
+ lower,
+ versions.slice(0, max_index),
+ versions.slice(max_index, versions.size)
+ ]
+ end
+
+ # Returns versions which are included by this range.
+ #
+ # versions must be sorted
+ def select_versions(versions)
+ return versions if any?
+
+ partition_versions(versions)[1]
+ end
+
+ def compare_version(version)
+ if min
+ case version <=> min
+ when -1
+ return -1
+ when 0
+ return -1 if !include_min
+ when 1
+ end
+ end
+
+ if max
+ case version <=> max
+ when -1
+ when 0
+ return 1 if !include_max
+ when 1
+ return 1
+ end
+ end
+
+ 0
+ end
+
+ def strictly_lower?(other)
+ return false if !max || !other.min
+
+ case max <=> other.min
+ when 0
+ !include_max || !other.include_min
+ when -1
+ true
+ when 1
+ false
+ end
+ end
+
+ def strictly_higher?(other)
+ other.strictly_lower?(self)
+ end
+
+ def intersects?(other)
+ return false if other.empty?
+ return other.intersects?(self) if other.is_a?(VersionUnion)
+ !strictly_lower?(other) && !strictly_higher?(other)
+ end
+ alias_method :allows_any?, :intersects?
+
+ def intersect(other)
+ return other if other.empty?
+ return other.intersect(self) if other.is_a?(VersionUnion)
+
+ min_range =
+ if !min
+ other
+ elsif !other.min
+ self
+ else
+ case min <=> other.min
+ when 0
+ include_min ? other : self
+ when -1
+ other
+ when 1
+ self
+ end
+ end
+
+ max_range =
+ if !max
+ other
+ elsif !other.max
+ self
+ else
+ case max <=> other.max
+ when 0
+ include_max ? other : self
+ when -1
+ self
+ when 1
+ other
+ end
+ end
+
+ if !min_range.equal?(max_range) && min_range.min && max_range.max
+ case min_range.min <=> max_range.max
+ when -1
+ when 0
+ if !min_range.include_min || !max_range.include_max
+ return EMPTY
+ end
+ when 1
+ return EMPTY
+ end
+ end
+
+ VersionRange.new(
+ min: min_range.min,
+ include_min: min_range.include_min,
+ max: max_range.max,
+ include_max: max_range.include_max
+ )
+ end
+
+ # The span covered by two ranges
+ #
+ # If self and other are contiguous, this builds a union of the two ranges.
+ # (if they aren't you are probably calling the wrong method)
+ def span(other)
+ return self if other.empty?
+
+ min_range =
+ if !min
+ self
+ elsif !other.min
+ other
+ else
+ case min <=> other.min
+ when 0
+ include_min ? self : other
+ when -1
+ self
+ when 1
+ other
+ end
+ end
+
+ max_range =
+ if !max
+ self
+ elsif !other.max
+ other
+ else
+ case max <=> other.max
+ when 0
+ include_max ? self : other
+ when -1
+ other
+ when 1
+ self
+ end
+ end
+
+ VersionRange.new(
+ min: min_range.min,
+ include_min: min_range.include_min,
+ max: max_range.max,
+ include_max: max_range.include_max
+ )
+ end
+
+ def union(other)
+ return other.union(self) if other.is_a?(VersionUnion)
+
+ if contiguous_to?(other)
+ span(other)
+ else
+ VersionUnion.union([self, other])
+ end
+ end
+
+ def contiguous_to?(other)
+ return false if other.empty?
+
+ intersects?(other) ||
+ (min == other.max && (include_min || other.include_max)) ||
+ (max == other.min && (include_max || other.include_min))
+ end
+
+ def allows_all?(other)
+ return true if other.empty?
+
+ if other.is_a?(VersionUnion)
+ return VersionUnion.new([self]).allows_all?(other)
+ end
+
+ return false if max && !other.max
+ return false if min && !other.min
+
+ if min
+ case min <=> other.min
+ when -1
+ when 0
+ return false if !include_min && other.include_min
+ when 1
+ return false
+ end
+ end
+
+ if max
+ case max <=> other.max
+ when -1
+ return false
+ when 0
+ return false if !include_max && other.include_max
+ when 1
+ end
+ end
+
+ true
+ end
+
+ def any?
+ !min && !max
+ end
+
+ def empty?
+ false
+ end
+
+ def to_s
+ @name ||= constraints.join(", ")
+ end
+
+ def inspect
+ "#<#{self.class} #{to_s}>"
+ end
+
+ def upper_invert
+ return self.class.empty unless max
+
+ VersionRange.new(min: max, include_min: !include_max)
+ end
+
+ def invert
+ return self.class.empty if any?
+
+ low = VersionRange.new(max: min, include_max: !include_min)
+ high = VersionRange.new(min: max, include_min: !include_max)
+
+ if !min
+ high
+ elsif !max
+ low
+ else
+ low.union(high)
+ end
+ end
+
+ def ==(other)
+ self.class == other.class &&
+ min == other.min &&
+ max == other.max &&
+ include_min == other.include_min &&
+ include_max == other.include_max
+ end
+
+ private
+
+ def constraints
+ return ["any"] if any?
+ return ["= #{min}"] if min.to_s == max.to_s
+
+ c = []
+ c << "#{include_min ? ">=" : ">"} #{min}" if min
+ c << "#{include_max ? "<=" : "<"} #{max}" if max
+ c
+ end
+
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb
new file mode 100644
index 0000000000..4caf6b355b
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb
@@ -0,0 +1,248 @@
+require_relative 'partial_solution'
+require_relative 'term'
+require_relative 'incompatibility'
+require_relative 'solve_failure'
+
+module Bundler::PubGrub
+ class VersionSolver
+ attr_reader :logger
+ attr_reader :source
+ attr_reader :solution
+
+ def initialize(source:, root: Package.root, logger: Bundler::PubGrub.logger)
+ @logger = logger
+
+ @source = source
+
+ # { package => [incompatibility, ...]}
+ @incompatibilities = Hash.new do |h, k|
+ h[k] = []
+ end
+
+ @seen_incompatibilities = {}
+
+ @solution = PartialSolution.new
+
+ add_incompatibility Incompatibility.new([
+ Term.new(VersionConstraint.any(root), false)
+ ], cause: :root)
+
+ propagate(root)
+ end
+
+ def solved?
+ solution.unsatisfied.empty?
+ end
+
+ # Returns true if there is more work to be done, false otherwise
+ def work
+ return false if solved?
+
+ next_package = choose_package_version
+ propagate(next_package)
+
+ if solved?
+ logger.info { "Solution found after #{solution.attempted_solutions} attempts:" }
+ solution.decisions.each do |package, version|
+ next if Package.root?(package)
+ logger.info { "* #{package} #{version}" }
+ end
+
+ false
+ else
+ true
+ end
+ end
+
+ def solve
+ work until solved?
+
+ solution.decisions
+ end
+
+ alias_method :result, :solve
+
+ private
+
+ def propagate(initial_package)
+ changed = [initial_package]
+ while package = changed.shift
+ @incompatibilities[package].reverse_each do |incompatibility|
+ result = propagate_incompatibility(incompatibility)
+ if result == :conflict
+ root_cause = resolve_conflict(incompatibility)
+ changed.clear
+ changed << propagate_incompatibility(root_cause)
+ elsif result # should be a Package
+ changed << result
+ end
+ end
+ changed.uniq!
+ end
+ end
+
+ def propagate_incompatibility(incompatibility)
+ unsatisfied = nil
+ incompatibility.terms.each do |term|
+ relation = solution.relation(term)
+ if relation == :disjoint
+ return nil
+ elsif relation == :overlap
+ # If more than one term is inconclusive, we can't deduce anything
+ return nil if unsatisfied
+ unsatisfied = term
+ end
+ end
+
+ if !unsatisfied
+ return :conflict
+ end
+
+ logger.debug { "derived: #{unsatisfied.invert}" }
+
+ solution.derive(unsatisfied.invert, incompatibility)
+
+ unsatisfied.package
+ end
+
+ def next_package_to_try
+ solution.unsatisfied.min_by do |term|
+ package = term.package
+ range = term.constraint.range
+ matching_versions = source.versions_for(package, range)
+ higher_versions = source.versions_for(package, range.upper_invert)
+
+ [matching_versions.count <= 1 ? 0 : 1, higher_versions.count]
+ end.package
+ end
+
+ def choose_package_version
+ if solution.unsatisfied.empty?
+ logger.info "No packages unsatisfied. Solving complete!"
+ return nil
+ end
+
+ package = next_package_to_try
+ unsatisfied_term = solution.unsatisfied.find { |t| t.package == package }
+ version = source.versions_for(package, unsatisfied_term.constraint.range).first
+ logger.debug { "attempting #{package} #{version}" }
+
+ if version.nil?
+ add_incompatibility source.no_versions_incompatibility_for(package, unsatisfied_term)
+ return package
+ end
+
+ conflict = false
+
+ source.incompatibilities_for(package, version).each do |incompatibility|
+ if @seen_incompatibilities.include?(incompatibility)
+ logger.debug { "knew: #{incompatibility}" }
+ next
+ end
+ @seen_incompatibilities[incompatibility] = true
+
+ add_incompatibility incompatibility
+
+ conflict ||= incompatibility.terms.all? do |term|
+ term.package == package || solution.satisfies?(term)
+ end
+ end
+
+ unless conflict
+ logger.info { "selected #{package} #{version}" }
+
+ solution.decide(package, version)
+ else
+ logger.info { "conflict: #{conflict.inspect}" }
+ end
+
+ package
+ end
+
+ def resolve_conflict(incompatibility)
+ logger.info { "conflict: #{incompatibility}" }
+
+ new_incompatibility = nil
+
+ while !incompatibility.failure?
+ most_recent_term = nil
+ most_recent_satisfier = nil
+ difference = nil
+
+ previous_level = 1
+
+ incompatibility.terms.each do |term|
+ satisfier = solution.satisfier(term)
+
+ if most_recent_satisfier.nil?
+ most_recent_term = term
+ most_recent_satisfier = satisfier
+ elsif most_recent_satisfier.index < satisfier.index
+ previous_level = [previous_level, most_recent_satisfier.decision_level].max
+ most_recent_term = term
+ most_recent_satisfier = satisfier
+ difference = nil
+ else
+ previous_level = [previous_level, satisfier.decision_level].max
+ end
+
+ if most_recent_term == term
+ difference = most_recent_satisfier.term.difference(most_recent_term)
+ if difference.empty?
+ difference = nil
+ else
+ difference_satisfier = solution.satisfier(difference.inverse)
+ previous_level = [previous_level, difference_satisfier.decision_level].max
+ end
+ end
+ end
+
+ if previous_level < most_recent_satisfier.decision_level ||
+ most_recent_satisfier.decision?
+
+ logger.info { "backtracking to #{previous_level}" }
+ solution.backtrack(previous_level)
+
+ if new_incompatibility
+ add_incompatibility(new_incompatibility)
+ end
+
+ return incompatibility
+ end
+
+ new_terms = []
+ new_terms += incompatibility.terms - [most_recent_term]
+ new_terms += most_recent_satisfier.cause.terms.reject { |term|
+ term.package == most_recent_satisfier.term.package
+ }
+ if difference
+ new_terms << difference.invert
+ end
+
+ new_incompatibility = Incompatibility.new(new_terms, cause: Incompatibility::ConflictCause.new(incompatibility, most_recent_satisfier.cause))
+
+ if incompatibility.to_s == new_incompatibility.to_s
+ logger.info { "!! failed to resolve conflicts, this shouldn't have happened" }
+ break
+ end
+
+ incompatibility = new_incompatibility
+
+ partially = difference ? " partially" : ""
+ logger.info { "! #{most_recent_term} is#{partially} satisfied by #{most_recent_satisfier.term}" }
+ logger.info { "! which is caused by #{most_recent_satisfier.cause}" }
+ logger.info { "! thus #{incompatibility}" }
+ end
+
+ raise SolveFailure.new(incompatibility)
+ end
+
+ def add_incompatibility(incompatibility)
+ logger.debug { "fact: #{incompatibility}" }
+ incompatibility.terms.each do |term|
+ package = term.package
+ @incompatibilities[package] << incompatibility
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb
new file mode 100644
index 0000000000..bbc10c3804
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb
@@ -0,0 +1,178 @@
+# frozen_string_literal: true
+
+module Bundler::PubGrub
+ class VersionUnion
+ attr_reader :ranges
+
+ def self.normalize_ranges(ranges)
+ ranges = ranges.flat_map do |range|
+ range.ranges
+ end
+
+ ranges.reject!(&:empty?)
+
+ return [] if ranges.empty?
+
+ mins, ranges = ranges.partition { |r| !r.min }
+ original_ranges = mins + ranges.sort_by { |r| [r.min, r.include_min ? 0 : 1] }
+ ranges = [original_ranges.shift]
+ original_ranges.each do |range|
+ if ranges.last.contiguous_to?(range)
+ ranges << ranges.pop.span(range)
+ else
+ ranges << range
+ end
+ end
+
+ ranges
+ end
+
+ def self.union(ranges, normalize: true)
+ ranges = normalize_ranges(ranges) if normalize
+
+ if ranges.size == 0
+ VersionRange.empty
+ elsif ranges.size == 1
+ ranges[0]
+ else
+ new(ranges)
+ end
+ end
+
+ def initialize(ranges)
+ raise ArgumentError unless ranges.all? { |r| r.instance_of?(VersionRange) }
+ @ranges = ranges
+ end
+
+ def hash
+ ranges.hash
+ end
+
+ def eql?(other)
+ ranges.eql?(other.ranges)
+ end
+
+ def include?(version)
+ !!ranges.bsearch {|r| r.compare_version(version) }
+ end
+
+ def select_versions(all_versions)
+ versions = []
+ ranges.inject(all_versions) do |acc, range|
+ _, matching, higher = range.partition_versions(acc)
+ versions.concat matching
+ higher
+ end
+ versions
+ end
+
+ def intersects?(other)
+ my_ranges = ranges.dup
+ other_ranges = other.ranges.dup
+
+ my_range = my_ranges.shift
+ other_range = other_ranges.shift
+ while my_range && other_range
+ if my_range.intersects?(other_range)
+ return true
+ end
+
+ if !my_range.max || other_range.empty? || (other_range.max && other_range.max < my_range.max)
+ other_range = other_ranges.shift
+ else
+ my_range = my_ranges.shift
+ end
+ end
+ end
+ alias_method :allows_any?, :intersects?
+
+ def allows_all?(other)
+ my_ranges = ranges.dup
+
+ my_range = my_ranges.shift
+
+ other.ranges.all? do |other_range|
+ while my_range
+ break if my_range.allows_all?(other_range)
+ my_range = my_ranges.shift
+ end
+
+ !!my_range
+ end
+ end
+
+ def empty?
+ false
+ end
+
+ def any?
+ false
+ end
+
+ def intersect(other)
+ my_ranges = ranges.dup
+ other_ranges = other.ranges.dup
+ new_ranges = []
+
+ my_range = my_ranges.shift
+ other_range = other_ranges.shift
+ while my_range && other_range
+ new_ranges << my_range.intersect(other_range)
+
+ if !my_range.max || other_range.empty? || (other_range.max && other_range.max < my_range.max)
+ other_range = other_ranges.shift
+ else
+ my_range = my_ranges.shift
+ end
+ end
+ new_ranges.reject!(&:empty?)
+ VersionUnion.union(new_ranges, normalize: false)
+ end
+
+ def upper_invert
+ ranges.last.upper_invert
+ end
+
+ def invert
+ ranges.map(&:invert).inject(:intersect)
+ end
+
+ def union(other)
+ VersionUnion.union([self, other])
+ end
+
+ def to_s
+ output = []
+
+ ranges = self.ranges.dup
+ while !ranges.empty?
+ ne = []
+ range = ranges.shift
+ while !ranges.empty? && ranges[0].min.to_s == range.max.to_s
+ ne << range.max
+ range = range.span(ranges.shift)
+ end
+
+ ne.map! {|x| "!= #{x}" }
+ if ne.empty?
+ output << range.to_s
+ elsif range.any?
+ output << ne.join(', ')
+ else
+ output << "#{range}, #{ne.join(', ')}"
+ end
+ end
+
+ output.join(" OR ")
+ end
+
+ def inspect
+ "#<#{self.class} #{to_s}>"
+ end
+
+ def ==(other)
+ self.class == other.class &&
+ self.ranges == other.ranges
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
index 8eff00bf3d..ef97d52ae7 100644
--- a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
+++ b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
@@ -425,7 +425,7 @@ class Bundler::Thor
end
def unix?
- RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris)/i
end
def truncate(string, width)
diff --git a/lib/bundler/vendor/tmpdir/lib/tmpdir.rb b/lib/bundler/vendor/tmpdir/lib/tmpdir.rb
deleted file mode 100644
index 70d43e0c6b..0000000000
--- a/lib/bundler/vendor/tmpdir/lib/tmpdir.rb
+++ /dev/null
@@ -1,154 +0,0 @@
-# frozen_string_literal: true
-#
-# tmpdir - retrieve temporary directory path
-#
-# $Id$
-#
-
-require_relative '../../fileutils/lib/fileutils'
-begin
- require 'etc.so'
-rescue LoadError # rescue LoadError for miniruby
-end
-
-class Bundler::Dir < Dir
-
- @systmpdir ||= defined?(Etc.systmpdir) ? Etc.systmpdir : '/tmp'
-
- ##
- # Returns the operating system's temporary file path.
-
- def self.tmpdir
- tmp = nil
- ['TMPDIR', 'TMP', 'TEMP', ['system temporary path', @systmpdir], ['/tmp']*2, ['.']*2].each do |name, dir = ENV[name]|
- next if !dir
- dir = File.expand_path(dir)
- stat = File.stat(dir) rescue next
- case
- when !stat.directory?
- warn "#{name} is not a directory: #{dir}"
- when !stat.writable?
- warn "#{name} is not writable: #{dir}"
- when stat.world_writable? && !stat.sticky?
- warn "#{name} is world-writable: #{dir}"
- else
- tmp = dir
- break
- end
- end
- raise ArgumentError, "could not find a temporary directory" unless tmp
- tmp
- end
-
- # Bundler::Dir.mktmpdir creates a temporary directory.
- #
- # The directory is created with 0700 permission.
- # Application should not change the permission to make the temporary directory accessible from other users.
- #
- # The prefix and suffix of the name of the directory is specified by
- # the optional first argument, <i>prefix_suffix</i>.
- # - If it is not specified or nil, "d" is used as the prefix and no suffix is used.
- # - If it is a string, it is used as the prefix and no suffix is used.
- # - If it is an array, first element is used as the prefix and second element is used as a suffix.
- #
- # Bundler::Dir.mktmpdir {|dir| dir is ".../d..." }
- # Bundler::Dir.mktmpdir("foo") {|dir| dir is ".../foo..." }
- # Bundler::Dir.mktmpdir(["foo", "bar"]) {|dir| dir is ".../foo...bar" }
- #
- # The directory is created under Bundler::Dir.tmpdir or
- # the optional second argument <i>tmpdir</i> if non-nil value is given.
- #
- # Bundler::Dir.mktmpdir {|dir| dir is "#{Bundler::Dir.tmpdir}/d..." }
- # Bundler::Dir.mktmpdir(nil, "/var/tmp") {|dir| dir is "/var/tmp/d..." }
- #
- # If a block is given,
- # it is yielded with the path of the directory.
- # The directory and its contents are removed
- # using Bundler::FileUtils.remove_entry before Bundler::Dir.mktmpdir returns.
- # The value of the block is returned.
- #
- # Bundler::Dir.mktmpdir {|dir|
- # # use the directory...
- # open("#{dir}/foo", "w") { ... }
- # }
- #
- # If a block is not given,
- # The path of the directory is returned.
- # In this case, Bundler::Dir.mktmpdir doesn't remove the directory.
- #
- # dir = Bundler::Dir.mktmpdir
- # begin
- # # use the directory...
- # open("#{dir}/foo", "w") { ... }
- # ensure
- # # remove the directory.
- # Bundler::FileUtils.remove_entry dir
- # end
- #
- def self.mktmpdir(prefix_suffix=nil, *rest, **options)
- base = nil
- path = Tmpname.create(prefix_suffix || "d", *rest, **options) {|p, _, _, d|
- base = d
- mkdir(p, 0700)
- }
- if block_given?
- begin
- yield path.dup
- ensure
- unless base
- stat = File.stat(File.dirname(path))
- if stat.world_writable? and !stat.sticky?
- raise ArgumentError, "parent directory is world writable but not sticky"
- end
- end
- Bundler::FileUtils.remove_entry path
- end
- else
- path
- end
- end
-
- module Tmpname # :nodoc:
- module_function
-
- def tmpdir
- Bundler::Dir.tmpdir
- end
-
- UNUSABLE_CHARS = "^,-.0-9A-Z_a-z~"
-
- class << (RANDOM = Random.new)
- MAX = 36**6 # < 0x100000000
- def next
- rand(MAX).to_s(36)
- end
- end
- private_constant :RANDOM
-
- def create(basename, tmpdir=nil, max_try: nil, **opts)
- origdir = tmpdir
- tmpdir ||= tmpdir()
- n = nil
- prefix, suffix = basename
- prefix = (String.try_convert(prefix) or
- raise ArgumentError, "unexpected prefix: #{prefix.inspect}")
- prefix = prefix.delete(UNUSABLE_CHARS)
- suffix &&= (String.try_convert(suffix) or
- raise ArgumentError, "unexpected suffix: #{suffix.inspect}")
- suffix &&= suffix.delete(UNUSABLE_CHARS)
- begin
- t = Time.now.strftime("%Y%m%d")
- path = "#{prefix}#{t}-#{$$}-#{RANDOM.next}"\
- "#{n ? %[-#{n}] : ''}#{suffix||''}"
- path = File.join(tmpdir, path)
- yield(path, n, opts, origdir)
- rescue Errno::EEXIST
- n ||= 0
- n += 1
- retry if !max_try or n < max_try
- raise "cannot generate temporary name using `#{basename}' under `#{tmpdir}'"
- end
- path
- end
- end
-end
diff --git a/lib/bundler/vendor/uri/lib/uri.rb b/lib/bundler/vendor/uri/lib/uri.rb
index 0e574dd2b1..976320f6bd 100644
--- a/lib/bundler/vendor/uri/lib/uri.rb
+++ b/lib/bundler/vendor/uri/lib/uri.rb
@@ -30,7 +30,7 @@
# class RSYNC < Generic
# DEFAULT_PORT = 873
# end
-# @@schemes['RSYNC'] = RSYNC
+# register_scheme 'RSYNC', RSYNC
# end
# #=> Bundler::URI::RSYNC
#
@@ -70,7 +70,6 @@
# - Bundler::URI::REGEXP - (in uri/common.rb)
# - Bundler::URI::REGEXP::PATTERN - (in uri/common.rb)
# - Bundler::URI::Util - (in uri/common.rb)
-# - Bundler::URI::Escape - (in uri/common.rb)
# - Bundler::URI::Error - (in uri/common.rb)
# - Bundler::URI::InvalidURIError - (in uri/common.rb)
# - Bundler::URI::InvalidComponentError - (in uri/common.rb)
@@ -101,3 +100,5 @@ require_relative 'uri/https'
require_relative 'uri/ldap'
require_relative 'uri/ldaps'
require_relative 'uri/mailto'
+require_relative 'uri/ws'
+require_relative 'uri/wss'
diff --git a/lib/bundler/vendor/uri/lib/uri/common.rb b/lib/bundler/vendor/uri/lib/uri/common.rb
index 6539e1810f..914a4c7581 100644
--- a/lib/bundler/vendor/uri/lib/uri/common.rb
+++ b/lib/bundler/vendor/uri/lib/uri/common.rb
@@ -13,9 +13,12 @@ require_relative "rfc2396_parser"
require_relative "rfc3986_parser"
module Bundler::URI
+ include RFC2396_REGEXP
+
REGEXP = RFC2396_REGEXP
Parser = RFC2396_Parser
RFC3986_PARSER = RFC3986_Parser.new
+ Ractor.make_shareable(RFC3986_PARSER) if defined?(Ractor)
# Bundler::URI::Parser.new
DEFAULT_PARSER = Parser.new
@@ -27,6 +30,7 @@ module Bundler::URI
DEFAULT_PARSER.regexp.each_pair do |sym, str|
const_set(sym, str)
end
+ Ractor.make_shareable(DEFAULT_PARSER) if defined?(Ractor)
module Util # :nodoc:
def make_components_hash(klass, array_hash)
@@ -60,24 +64,42 @@ module Bundler::URI
module_function :make_components_hash
end
- include REGEXP
+ module Schemes
+ end
+ private_constant :Schemes
+
+ #
+ # Register the given +klass+ to be instantiated when parsing URLs with the given +scheme+.
+ # Note that currently only schemes which after .upcase are valid constant names
+ # can be registered (no -/+/. allowed).
+ #
+ def self.register_scheme(scheme, klass)
+ Schemes.const_set(scheme.to_s.upcase, klass)
+ end
- @@schemes = {}
# Returns a Hash of the defined schemes.
def self.scheme_list
- @@schemes
+ Schemes.constants.map { |name|
+ [name.to_s.upcase, Schemes.const_get(name)]
+ }.to_h
end
+ INITIAL_SCHEMES = scheme_list
+ private_constant :INITIAL_SCHEMES
+ Ractor.make_shareable(INITIAL_SCHEMES) if defined?(Ractor)
+
#
# Construct a Bundler::URI instance, using the scheme to detect the appropriate class
# from +Bundler::URI.scheme_list+.
#
def self.for(scheme, *arguments, default: Generic)
- if scheme
- uri_class = @@schemes[scheme.upcase] || default
- else
- uri_class = default
+ const_name = scheme.to_s.upcase
+
+ uri_class = INITIAL_SCHEMES[const_name]
+ uri_class ||= if /\A[A-Z]\w*\z/.match?(const_name) && Schemes.const_defined?(const_name, false)
+ Schemes.const_get(const_name, false)
end
+ uri_class ||= default
return uri_class.new(scheme, *arguments)
end
@@ -278,6 +300,7 @@ module Bundler::URI
256.times do |i|
TBLENCWWWCOMP_[-i.chr] = -('%%%02X' % i)
end
+ TBLENCURICOMP_ = TBLENCWWWCOMP_.dup.freeze
TBLENCWWWCOMP_[' '] = '+'
TBLENCWWWCOMP_.freeze
TBLDECWWWCOMP_ = {} # :nodoc:
@@ -303,6 +326,33 @@ module Bundler::URI
#
# See Bundler::URI.decode_www_form_component, Bundler::URI.encode_www_form.
def self.encode_www_form_component(str, enc=nil)
+ _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_, str, enc)
+ end
+
+ # Decodes given +str+ of URL-encoded form data.
+ #
+ # This decodes + to SP.
+ #
+ # See Bundler::URI.encode_www_form_component, Bundler::URI.decode_www_form.
+ def self.decode_www_form_component(str, enc=Encoding::UTF_8)
+ _decode_uri_component(/\+|%\h\h/, str, enc)
+ end
+
+ # Encodes +str+ using URL encoding
+ #
+ # This encodes SP to %20 instead of +.
+ def self.encode_uri_component(str, enc=nil)
+ _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCURICOMP_, str, enc)
+ end
+
+ # Decodes given +str+ of URL-encoded data.
+ #
+ # This does not decode + to SP.
+ def self.decode_uri_component(str, enc=Encoding::UTF_8)
+ _decode_uri_component(/%\h\h/, str, enc)
+ end
+
+ def self._encode_uri_component(regexp, table, str, enc)
str = str.to_s.dup
if str.encoding != Encoding::ASCII_8BIT
if enc && enc != Encoding::ASCII_8BIT
@@ -311,19 +361,16 @@ module Bundler::URI
end
str.force_encoding(Encoding::ASCII_8BIT)
end
- str.gsub!(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_)
+ str.gsub!(regexp, table)
str.force_encoding(Encoding::US_ASCII)
end
+ private_class_method :_encode_uri_component
- # Decodes given +str+ of URL-encoded form data.
- #
- # This decodes + to SP.
- #
- # See Bundler::URI.encode_www_form_component, Bundler::URI.decode_www_form.
- def self.decode_www_form_component(str, enc=Encoding::UTF_8)
- raise ArgumentError, "invalid %-encoding (#{str})" if /%(?!\h\h)/ =~ str
- str.b.gsub(/\+|%\h\h/, TBLDECWWWCOMP_).force_encoding(enc)
+ def self._decode_uri_component(regexp, str, enc)
+ raise ArgumentError, "invalid %-encoding (#{str})" if /%(?!\h\h)/.match?(str)
+ str.b.gsub(regexp, TBLDECWWWCOMP_).force_encoding(enc)
end
+ private_class_method :_decode_uri_component
# Generates URL-encoded form data from given +enum+.
#
@@ -653,6 +700,7 @@ module Bundler::URI
"utf-16"=>"utf-16le",
"utf-16le"=>"utf-16le",
} # :nodoc:
+ Ractor.make_shareable(WEB_ENCODINGS_) if defined?(Ractor)
# :nodoc:
# return encoding or nil
diff --git a/lib/bundler/vendor/uri/lib/uri/file.rb b/lib/bundler/vendor/uri/lib/uri/file.rb
index df42f8bcdd..8d75a9de7a 100644
--- a/lib/bundler/vendor/uri/lib/uri/file.rb
+++ b/lib/bundler/vendor/uri/lib/uri/file.rb
@@ -33,6 +33,9 @@ module Bundler::URI
# If an Array is used, the components must be passed in the
# order <code>[host, path]</code>.
#
+ # A path from e.g. the File class should be escaped before
+ # being passed.
+ #
# Examples:
#
# require 'bundler/vendor/uri/lib/uri'
@@ -44,6 +47,9 @@ module Bundler::URI
# :path => '/ruby/src'})
# uri2.to_s # => "file://host.example.com/ruby/src"
#
+ # uri3 = Bundler::URI::File.build({:path => Bundler::URI::escape('/path/my file.txt')})
+ # uri3.to_s # => "file:///path/my%20file.txt"
+ #
def self.build(args)
tmp = Util::make_components_hash(self, args)
super(tmp)
@@ -90,5 +96,5 @@ module Bundler::URI
end
end
- @@schemes['FILE'] = File
+ register_scheme 'FILE', File
end
diff --git a/lib/bundler/vendor/uri/lib/uri/ftp.rb b/lib/bundler/vendor/uri/lib/uri/ftp.rb
index 2252e405d5..48b4c6718d 100644
--- a/lib/bundler/vendor/uri/lib/uri/ftp.rb
+++ b/lib/bundler/vendor/uri/lib/uri/ftp.rb
@@ -262,5 +262,6 @@ module Bundler::URI
return str
end
end
- @@schemes['FTP'] = FTP
+
+ register_scheme 'FTP', FTP
end
diff --git a/lib/bundler/vendor/uri/lib/uri/generic.rb b/lib/bundler/vendor/uri/lib/uri/generic.rb
index f29ba6cf18..9ae6915826 100644
--- a/lib/bundler/vendor/uri/lib/uri/generic.rb
+++ b/lib/bundler/vendor/uri/lib/uri/generic.rb
@@ -564,16 +564,26 @@ module Bundler::URI
end
end
- # Returns the user component.
+ # Returns the user component (without Bundler::URI decoding).
def user
@user
end
- # Returns the password component.
+ # Returns the password component (without Bundler::URI decoding).
def password
@password
end
+ # Returns the user component after Bundler::URI decoding.
+ def decoded_user
+ Bundler::URI.decode_uri_component(@user) if @user
+ end
+
+ # Returns the password component after Bundler::URI decoding.
+ def decoded_password
+ Bundler::URI.decode_uri_component(@password) if @password
+ end
+
#
# Checks the host +v+ component for RFC2396 compliance
# and against the Bundler::URI::Parser Regexp for :HOST.
@@ -643,7 +653,7 @@ module Bundler::URI
#
def hostname
v = self.host
- /\A\[(.*)\]\z/ =~ v ? $1 : v
+ v&.start_with?('[') && v.end_with?(']') ? v[1..-2] : v
end
# Sets the host part of the Bundler::URI as the argument with brackets for IPv6 addresses.
@@ -659,7 +669,7 @@ module Bundler::URI
# it is wrapped with brackets.
#
def hostname=(v)
- v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v
+ v = "[#{v}]" if !(v&.start_with?('[') && v&.end_with?(']')) && v&.index(':')
self.host = v
end
@@ -1514,9 +1524,19 @@ module Bundler::URI
proxy_uri = env["CGI_#{name.upcase}"]
end
elsif name == 'http_proxy'
- unless proxy_uri = env[name]
- if proxy_uri = env[name.upcase]
- warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.', uplevel: 1
+ if RUBY_ENGINE == 'jruby' && p_addr = ENV_JAVA['http.proxyHost']
+ p_port = ENV_JAVA['http.proxyPort']
+ if p_user = ENV_JAVA['http.proxyUser']
+ p_pass = ENV_JAVA['http.proxyPass']
+ proxy_uri = "http://#{p_user}:#{p_pass}@#{p_addr}:#{p_port}"
+ else
+ proxy_uri = "http://#{p_addr}:#{p_port}"
+ end
+ else
+ unless proxy_uri = env[name]
+ if proxy_uri = env[name.upcase]
+ warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.', uplevel: 1
+ end
end
end
else
diff --git a/lib/bundler/vendor/uri/lib/uri/http.rb b/lib/bundler/vendor/uri/lib/uri/http.rb
index 50d7e427a5..2c44810644 100644
--- a/lib/bundler/vendor/uri/lib/uri/http.rb
+++ b/lib/bundler/vendor/uri/lib/uri/http.rb
@@ -80,8 +80,46 @@ module Bundler::URI
url = @query ? "#@path?#@query" : @path.dup
url.start_with?(?/.freeze) ? url : ?/ + url
end
- end
- @@schemes['HTTP'] = HTTP
+ #
+ # == Description
+ #
+ # Returns the authority for an HTTP uri, as defined in
+ # https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.
+ #
+ #
+ # Example:
+ #
+ # Bundler::URI::HTTP.build(host: 'www.example.com', path: '/foo/bar').authority #=> "www.example.com"
+ # Bundler::URI::HTTP.build(host: 'www.example.com', port: 8000, path: '/foo/bar').authority #=> "www.example.com:8000"
+ # Bundler::URI::HTTP.build(host: 'www.example.com', port: 80, path: '/foo/bar').authority #=> "www.example.com"
+ #
+ def authority
+ if port == default_port
+ host
+ else
+ "#{host}:#{port}"
+ end
+ end
+
+ #
+ # == Description
+ #
+ # Returns the origin for an HTTP uri, as defined in
+ # https://datatracker.ietf.org/doc/html/rfc6454.
+ #
+ #
+ # Example:
+ #
+ # Bundler::URI::HTTP.build(host: 'www.example.com', path: '/foo/bar').origin #=> "http://www.example.com"
+ # Bundler::URI::HTTP.build(host: 'www.example.com', port: 8000, path: '/foo/bar').origin #=> "http://www.example.com:8000"
+ # Bundler::URI::HTTP.build(host: 'www.example.com', port: 80, path: '/foo/bar').origin #=> "http://www.example.com"
+ # Bundler::URI::HTTPS.build(host: 'www.example.com', path: '/foo/bar').origin #=> "https://www.example.com"
+ #
+ def origin
+ "#{scheme}://#{authority}"
+ end
+ end
+ register_scheme 'HTTP', HTTP
end
diff --git a/lib/bundler/vendor/uri/lib/uri/https.rb b/lib/bundler/vendor/uri/lib/uri/https.rb
index 4fd4e9af7b..e4556e3ecb 100644
--- a/lib/bundler/vendor/uri/lib/uri/https.rb
+++ b/lib/bundler/vendor/uri/lib/uri/https.rb
@@ -18,5 +18,6 @@ module Bundler::URI
# A Default port of 443 for Bundler::URI::HTTPS
DEFAULT_PORT = 443
end
- @@schemes['HTTPS'] = HTTPS
+
+ register_scheme 'HTTPS', HTTPS
end
diff --git a/lib/bundler/vendor/uri/lib/uri/ldap.rb b/lib/bundler/vendor/uri/lib/uri/ldap.rb
index 6e9e1918f6..9811b6e2f5 100644
--- a/lib/bundler/vendor/uri/lib/uri/ldap.rb
+++ b/lib/bundler/vendor/uri/lib/uri/ldap.rb
@@ -257,5 +257,5 @@ module Bundler::URI
end
end
- @@schemes['LDAP'] = LDAP
+ register_scheme 'LDAP', LDAP
end
diff --git a/lib/bundler/vendor/uri/lib/uri/ldaps.rb b/lib/bundler/vendor/uri/lib/uri/ldaps.rb
index 0af35bb16b..c786168450 100644
--- a/lib/bundler/vendor/uri/lib/uri/ldaps.rb
+++ b/lib/bundler/vendor/uri/lib/uri/ldaps.rb
@@ -17,5 +17,6 @@ module Bundler::URI
# A Default port of 636 for Bundler::URI::LDAPS
DEFAULT_PORT = 636
end
- @@schemes['LDAPS'] = LDAPS
+
+ register_scheme 'LDAPS', LDAPS
end
diff --git a/lib/bundler/vendor/uri/lib/uri/mailto.rb b/lib/bundler/vendor/uri/lib/uri/mailto.rb
index ff7ab7e114..ff2e30be86 100644
--- a/lib/bundler/vendor/uri/lib/uri/mailto.rb
+++ b/lib/bundler/vendor/uri/lib/uri/mailto.rb
@@ -15,7 +15,7 @@ module Bundler::URI
# RFC6068, the mailto URL scheme.
#
class MailTo < Generic
- include REGEXP
+ include RFC2396_REGEXP
# A Default port of nil for Bundler::URI::MailTo.
DEFAULT_PORT = nil
@@ -289,5 +289,5 @@ module Bundler::URI
alias to_rfc822text to_mailtext
end
- @@schemes['MAILTO'] = MailTo
+ register_scheme 'MAILTO', MailTo
end
diff --git a/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb b/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb
index e48e164f4c..09c22c9906 100644
--- a/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb
+++ b/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb
@@ -116,7 +116,7 @@ module Bundler::URI
# See also Bundler::URI::Parser.initialize_regexp.
attr_reader :regexp
- # Returns a split Bundler::URI against regexp[:ABS_URI].
+ # Returns a split Bundler::URI against +regexp[:ABS_URI]+.
def split(uri)
case uri
when ''
@@ -257,8 +257,8 @@ module Bundler::URI
end
end
- # Returns Regexp that is default self.regexp[:ABS_URI_REF],
- # unless +schemes+ is provided. Then it is a Regexp.union with self.pattern[:X_ABS_URI].
+ # Returns Regexp that is default +self.regexp[:ABS_URI_REF]+,
+ # unless +schemes+ is provided. Then it is a Regexp.union with +self.pattern[:X_ABS_URI]+.
def make_regexp(schemes = nil)
unless schemes
@regexp[:ABS_URI_REF]
@@ -277,7 +277,7 @@ module Bundler::URI
# +str+::
# String to make safe
# +unsafe+::
- # Regexp to apply. Defaults to self.regexp[:UNSAFE]
+ # Regexp to apply. Defaults to +self.regexp[:UNSAFE]+
#
# == Description
#
@@ -309,7 +309,7 @@ module Bundler::URI
# +str+::
# String to remove escapes from
# +escaped+::
- # Regexp to apply. Defaults to self.regexp[:ESCAPED]
+ # Regexp to apply. Defaults to +self.regexp[:ESCAPED]+
#
# == Description
#
@@ -322,8 +322,14 @@ module Bundler::URI
end
@@to_s = Kernel.instance_method(:to_s)
- def inspect
- @@to_s.bind_call(self)
+ if @@to_s.respond_to?(:bind_call)
+ def inspect
+ @@to_s.bind_call(self)
+ end
+ else
+ def inspect
+ @@to_s.bind(self).call
+ end
end
private
@@ -491,8 +497,8 @@ module Bundler::URI
ret = {}
# for Bundler::URI::split
- ret[:ABS_URI] = Regexp.new('\A\s*' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
- ret[:REL_URI] = Regexp.new('\A\s*' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:ABS_URI] = Regexp.new('\A\s*+' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:REL_URI] = Regexp.new('\A\s*+' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
# for Bundler::URI::extract
ret[:URI_REF] = Regexp.new(pattern[:URI_REF])
diff --git a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb
index 2029cfd056..a85511c146 100644
--- a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb
+++ b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb
@@ -2,9 +2,8 @@
module Bundler::URI
class RFC3986_Parser # :nodoc:
# Bundler::URI defined in RFC3986
- # this regexp is modified not to host is not empty string
- RFC3986_URI = /\A(?<Bundler::URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
- RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+)\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
+ RFC3986_URI = /\A(?<Bundler::URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*+):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+))(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
+ RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
attr_reader :regexp
def initialize
@@ -79,8 +78,14 @@ module Bundler::URI
end
@@to_s = Kernel.instance_method(:to_s)
- def inspect
- @@to_s.bind_call(self)
+ if @@to_s.respond_to?(:bind_call)
+ def inspect
+ @@to_s.bind_call(self)
+ end
+ else
+ def inspect
+ @@to_s.bind(self).call
+ end
end
private
@@ -95,7 +100,7 @@ module Bundler::URI
QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
OPAQUE: /\A(?:[^\/].*)?\z/,
- PORT: /\A[\x09\x0a\x0c\x0d ]*\d*[\x09\x0a\x0c\x0d ]*\z/,
+ PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/,
}
end
diff --git a/lib/bundler/vendor/uri/lib/uri/version.rb b/lib/bundler/vendor/uri/lib/uri/version.rb
index f2bb0ebad2..84b08eee30 100644
--- a/lib/bundler/vendor/uri/lib/uri/version.rb
+++ b/lib/bundler/vendor/uri/lib/uri/version.rb
@@ -1,6 +1,6 @@
module Bundler::URI
# :stopdoc:
- VERSION_CODE = '001001'.freeze
+ VERSION_CODE = '001202'.freeze
VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
# :startdoc:
end
diff --git a/lib/bundler/vendor/uri/lib/uri/ws.rb b/lib/bundler/vendor/uri/lib/uri/ws.rb
index 58e08bf98e..10ae6f5834 100644
--- a/lib/bundler/vendor/uri/lib/uri/ws.rb
+++ b/lib/bundler/vendor/uri/lib/uri/ws.rb
@@ -79,6 +79,5 @@ module Bundler::URI
end
end
- @@schemes['WS'] = WS
-
+ register_scheme 'WS', WS
end
diff --git a/lib/bundler/vendor/uri/lib/uri/wss.rb b/lib/bundler/vendor/uri/lib/uri/wss.rb
index 3827053c7b..e8db1ceabf 100644
--- a/lib/bundler/vendor/uri/lib/uri/wss.rb
+++ b/lib/bundler/vendor/uri/lib/uri/wss.rb
@@ -18,5 +18,6 @@ module Bundler::URI
# A Default port of 443 for Bundler::URI::WSS
DEFAULT_PORT = 443
end
- @@schemes['WSS'] = WSS
+
+ register_scheme 'WSS', WSS
end
diff --git a/lib/bundler/vendored_molinillo.rb b/lib/bundler/vendored_molinillo.rb
deleted file mode 100644
index d1976f5cb4..0000000000
--- a/lib/bundler/vendored_molinillo.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler; end
-require_relative "vendor/molinillo/lib/molinillo"
diff --git a/lib/bundler/vendored_persistent.rb b/lib/bundler/vendored_persistent.rb
index dc9573e025..e29f27cdfd 100644
--- a/lib/bundler/vendored_persistent.rb
+++ b/lib/bundler/vendored_persistent.rb
@@ -11,37 +11,5 @@ end
require_relative "vendor/net-http-persistent/lib/net/http/persistent"
module Bundler
- class PersistentHTTP < Persistent::Net::HTTP::Persistent
- def connection_for(uri)
- super(uri) do |connection|
- result = yield connection
- warn_old_tls_version_rubygems_connection(uri, connection)
- result
- end
- end
-
- def warn_old_tls_version_rubygems_connection(uri, connection)
- return unless connection.http.use_ssl?
- return unless (uri.host || "").end_with?("rubygems.org")
-
- socket = connection.instance_variable_get(:@socket)
- return unless socket
- socket_io = socket.io
- return unless socket_io.respond_to?(:ssl_version)
- ssl_version = socket_io.ssl_version
-
- case ssl_version
- when /TLSv([\d\.]+)/
- version = Gem::Version.new($1)
- if version < Gem::Version.new("1.2")
- Bundler.ui.warn \
- "Warning: Your Ruby version is compiled against a copy of OpenSSL that is very old. " \
- "Starting in January 2018, RubyGems.org will refuse connection requests from these " \
- "very old versions of OpenSSL. If you will need to continue installing gems after " \
- "January 2018, please follow this guide to upgrade: http://ruby.to/tls-outdated.",
- :wrap => true
- end
- end
- end
- end
+ PersistentHTTP = Persistent::Net::HTTP::Persistent
end
diff --git a/lib/bundler/vendored_tmpdir.rb b/lib/bundler/vendored_pub_grub.rb
index 43b4fa75fe..b36a996b29 100644
--- a/lib/bundler/vendored_tmpdir.rb
+++ b/lib/bundler/vendored_pub_grub.rb
@@ -1,4 +1,4 @@
# frozen_string_literal: true
module Bundler; end
-require_relative "vendor/tmpdir/lib/tmpdir"
+require_relative "vendor/pub_grub/lib/pub_grub"
diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb
index 1acb00fd3a..8ef7be935b 100644
--- a/lib/bundler/version.rb
+++ b/lib/bundler/version.rb
@@ -1,9 +1,13 @@
# frozen_string_literal: false
module Bundler
- VERSION = "2.4.0.dev".freeze
+ VERSION = "2.4.19".freeze
def self.bundler_major_version
@bundler_major_version ||= VERSION.split(".").first.to_i
end
+
+ def self.gem_version
+ @gem_version ||= Gem::Version.create(VERSION)
+ end
end
diff --git a/lib/bundler/version_ranges.rb b/lib/bundler/version_ranges.rb
deleted file mode 100644
index 12a956d6a0..0000000000
--- a/lib/bundler/version_ranges.rb
+++ /dev/null
@@ -1,122 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler
- module VersionRanges
- NEq = Struct.new(:version)
- ReqR = Struct.new(:left, :right)
- class ReqR
- Endpoint = Struct.new(:version, :inclusive) do
- def <=>(other)
- if version.equal?(INFINITY)
- return 0 if other.version.equal?(INFINITY)
- return 1
- elsif other.version.equal?(INFINITY)
- return -1
- end
-
- comp = version <=> other.version
- return comp unless comp.zero?
-
- if inclusive && !other.inclusive
- 1
- elsif !inclusive && other.inclusive
- -1
- else
- 0
- end
- end
- end
-
- def to_s
- "#{left.inclusive ? "[" : "("}#{left.version}, #{right.version}#{right.inclusive ? "]" : ")"}"
- end
- INFINITY = begin
- inf = Object.new
- def inf.to_s
- "∞"
- end
- def inf.<=>(other)
- return 0 if other.equal?(self)
- 1
- end
- inf.freeze
- end
- ZERO = Gem::Version.new("0.a")
-
- def cover?(v)
- return false if left.inclusive && left.version > v
- return false if !left.inclusive && left.version >= v
-
- if right.version != INFINITY
- return false if right.inclusive && right.version < v
- return false if !right.inclusive && right.version <= v
- end
-
- true
- end
-
- def empty?
- left.version == right.version && !(left.inclusive && right.inclusive)
- end
-
- def single?
- left.version == right.version
- end
-
- def <=>(other)
- return -1 if other.equal?(INFINITY)
-
- comp = left <=> other.left
- return comp unless comp.zero?
-
- right <=> other.right
- end
-
- UNIVERSAL = ReqR.new(ReqR::Endpoint.new(Gem::Version.new("0.a"), true), ReqR::Endpoint.new(ReqR::INFINITY, false)).freeze
- end
-
- def self.for_many(requirements)
- requirements = requirements.map(&:requirements).flatten(1).map {|r| r.join(" ") }
- requirements << ">= 0.a" if requirements.empty?
- requirement = Gem::Requirement.new(requirements)
- self.for(requirement)
- end
-
- def self.for(requirement)
- ranges = requirement.requirements.map do |op, v|
- case op
- when "=" then ReqR.new(ReqR::Endpoint.new(v, true), ReqR::Endpoint.new(v, true))
- when "!=" then NEq.new(v)
- when ">=" then ReqR.new(ReqR::Endpoint.new(v, true), ReqR::Endpoint.new(ReqR::INFINITY, false))
- when ">" then ReqR.new(ReqR::Endpoint.new(v, false), ReqR::Endpoint.new(ReqR::INFINITY, false))
- when "<" then ReqR.new(ReqR::Endpoint.new(ReqR::ZERO, true), ReqR::Endpoint.new(v, false))
- when "<=" then ReqR.new(ReqR::Endpoint.new(ReqR::ZERO, true), ReqR::Endpoint.new(v, true))
- when "~>" then ReqR.new(ReqR::Endpoint.new(v, true), ReqR::Endpoint.new(v.bump, false))
- else raise "unknown version op #{op} in requirement #{requirement}"
- end
- end.uniq
- ranges, neqs = ranges.partition {|r| !r.is_a?(NEq) }
-
- [ranges.sort, neqs.map(&:version)]
- end
-
- def self.empty?(ranges, neqs)
- !ranges.reduce(ReqR::UNIVERSAL) do |last_range, curr_range|
- next false unless last_range
- next false if curr_range.single? && neqs.include?(curr_range.left.version)
- next curr_range if last_range.right.version == ReqR::INFINITY
- case last_range.right.version <=> curr_range.left.version
- # higher
- when 1 then next ReqR.new(curr_range.left, last_range.right)
- # equal
- when 0
- if last_range.right.inclusive && curr_range.left.inclusive && !neqs.include?(curr_range.left.version)
- ReqR.new(curr_range.left, [curr_range.right, last_range.right].max)
- end
- # lower
- when -1 then next false
- end
- end
- end
- end
-end
diff --git a/lib/bundler/worker.rb b/lib/bundler/worker.rb
index 5e4ee21c51..3ebd6f01db 100644
--- a/lib/bundler/worker.rb
+++ b/lib/bundler/worker.rb
@@ -87,14 +87,12 @@ module Bundler
creation_errors = []
@threads = Array.new(@size) do |i|
- begin
- Thread.start { process_queue(i) }.tap do |thread|
- thread.name = "#{name} Worker ##{i}" if thread.respond_to?(:name=)
- end
- rescue ThreadError => e
- creation_errors << e
- nil
+ Thread.start { process_queue(i) }.tap do |thread|
+ thread.name = "#{name} Worker ##{i}" if thread.respond_to?(:name=)
end
+ rescue ThreadError => e
+ creation_errors << e
+ nil
end.compact
add_interrupt_handler unless @threads.empty?
diff --git a/lib/cgi.rb b/lib/cgi.rb
index affe8fd3fc..7dc3a64941 100644
--- a/lib/cgi.rb
+++ b/lib/cgi.rb
@@ -288,7 +288,7 @@
#
class CGI
- VERSION = "0.3.2"
+ VERSION = "0.3.7"
end
require 'cgi/core'
diff --git a/lib/cgi/cookie.rb b/lib/cgi/cookie.rb
index 6b0d89ca3b..1c4ef6a600 100644
--- a/lib/cgi/cookie.rb
+++ b/lib/cgi/cookie.rb
@@ -40,6 +40,10 @@ class CGI
class Cookie < Array
@@accept_charset="UTF-8" unless defined?(@@accept_charset)
+ TOKEN_RE = %r"\A[[!-~]&&[^()<>@,;:\\\"/?=\[\]{}]]+\z"
+ PATH_VALUE_RE = %r"\A[[ -~]&&[^;]]*\z"
+ DOMAIN_VALUE_RE = %r"\A\.?(?<label>(?!-)[-A-Za-z0-9]+(?<!-))(?:\.\g<label>)*\z"
+
# Create a new CGI::Cookie object.
#
# :call-seq:
@@ -72,8 +76,8 @@ class CGI
@domain = nil
@expires = nil
if name.kind_of?(String)
- @name = name
- @path = (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
+ self.name = name
+ self.path = (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
@secure = false
@httponly = false
return super(value)
@@ -84,11 +88,11 @@ class CGI
raise ArgumentError, "`name' required"
end
- @name = options["name"]
+ self.name = options["name"]
value = Array(options["value"])
# simple support for IE
- @path = options["path"] || (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
- @domain = options["domain"]
+ self.path = options["path"] || (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
+ self.domain = options["domain"]
@expires = options["expires"]
@secure = options["secure"] == true
@httponly = options["httponly"] == true
@@ -97,11 +101,35 @@ class CGI
end
# Name of this cookie, as a +String+
- attr_accessor :name
+ attr_reader :name
+ # Set name of this cookie
+ def name=(str)
+ if str and !TOKEN_RE.match?(str)
+ raise ArgumentError, "invalid name: #{str.dump}"
+ end
+ @name = str
+ end
+
# Path for which this cookie applies, as a +String+
- attr_accessor :path
+ attr_reader :path
+ # Set path for which this cookie applies
+ def path=(str)
+ if str and !PATH_VALUE_RE.match?(str)
+ raise ArgumentError, "invalid path: #{str.dump}"
+ end
+ @path = str
+ end
+
# Domain for which this cookie applies, as a +String+
- attr_accessor :domain
+ attr_reader :domain
+ # Set domain for which this cookie applies
+ def domain=(str)
+ if str and ((str = str.b).bytesize > 255 or !DOMAIN_VALUE_RE.match?(str))
+ raise ArgumentError, "invalid domain: #{str.dump}"
+ end
+ @domain = str
+ end
+
# Time at which this cookie expires, as a +Time+
attr_accessor :expires
# True if this cookie is secure; false otherwise
@@ -162,9 +190,10 @@ class CGI
values ||= ""
values = values.split('&').collect{|v| CGI.unescape(v,@@accept_charset) }
if cookies.has_key?(name)
- values = cookies[name].value + values
+ cookies[name].concat(values)
+ else
+ cookies[name] = Cookie.new(name, *values)
end
- cookies[name] = Cookie.new(name, *values)
end
cookies
diff --git a/lib/cgi/core.rb b/lib/cgi/core.rb
index bec76e0749..62e606837a 100644
--- a/lib/cgi/core.rb
+++ b/lib/cgi/core.rb
@@ -188,17 +188,28 @@ class CGI
# Using #header with the HTML5 tag maker will create a <header> element.
alias :header :http_header
+ def _no_crlf_check(str)
+ if str
+ str = str.to_s
+ raise "A HTTP status or header field must not include CR and LF" if str =~ /[\r\n]/
+ str
+ else
+ nil
+ end
+ end
+ private :_no_crlf_check
+
def _header_for_string(content_type) #:nodoc:
buf = ''.dup
if nph?()
- buf << "#{$CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'} 200 OK#{EOL}"
+ buf << "#{_no_crlf_check($CGI_ENV['SERVER_PROTOCOL']) || 'HTTP/1.0'} 200 OK#{EOL}"
buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
- buf << "Server: #{$CGI_ENV['SERVER_SOFTWARE']}#{EOL}"
+ buf << "Server: #{_no_crlf_check($CGI_ENV['SERVER_SOFTWARE'])}#{EOL}"
buf << "Connection: close#{EOL}"
end
- buf << "Content-Type: #{content_type}#{EOL}"
+ buf << "Content-Type: #{_no_crlf_check(content_type)}#{EOL}"
if @output_cookies
- @output_cookies.each {|cookie| buf << "Set-Cookie: #{cookie}#{EOL}" }
+ @output_cookies.each {|cookie| buf << "Set-Cookie: #{_no_crlf_check(cookie)}#{EOL}" }
end
return buf
end # _header_for_string
@@ -213,9 +224,9 @@ class CGI
## NPH
options.delete('nph') if defined?(MOD_RUBY)
if options.delete('nph') || nph?()
- protocol = $CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'
+ protocol = _no_crlf_check($CGI_ENV['SERVER_PROTOCOL']) || 'HTTP/1.0'
status = options.delete('status')
- status = HTTP_STATUS[status] || status || '200 OK'
+ status = HTTP_STATUS[status] || _no_crlf_check(status) || '200 OK'
buf << "#{protocol} #{status}#{EOL}"
buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
options['server'] ||= $CGI_ENV['SERVER_SOFTWARE'] || ''
@@ -223,38 +234,38 @@ class CGI
end
## common headers
status = options.delete('status')
- buf << "Status: #{HTTP_STATUS[status] || status}#{EOL}" if status
+ buf << "Status: #{HTTP_STATUS[status] || _no_crlf_check(status)}#{EOL}" if status
server = options.delete('server')
- buf << "Server: #{server}#{EOL}" if server
+ buf << "Server: #{_no_crlf_check(server)}#{EOL}" if server
connection = options.delete('connection')
- buf << "Connection: #{connection}#{EOL}" if connection
+ buf << "Connection: #{_no_crlf_check(connection)}#{EOL}" if connection
type = options.delete('type')
- buf << "Content-Type: #{type}#{EOL}" #if type
+ buf << "Content-Type: #{_no_crlf_check(type)}#{EOL}" #if type
length = options.delete('length')
- buf << "Content-Length: #{length}#{EOL}" if length
+ buf << "Content-Length: #{_no_crlf_check(length)}#{EOL}" if length
language = options.delete('language')
- buf << "Content-Language: #{language}#{EOL}" if language
+ buf << "Content-Language: #{_no_crlf_check(language)}#{EOL}" if language
expires = options.delete('expires')
buf << "Expires: #{CGI.rfc1123_date(expires)}#{EOL}" if expires
## cookie
if cookie = options.delete('cookie')
case cookie
when String, Cookie
- buf << "Set-Cookie: #{cookie}#{EOL}"
+ buf << "Set-Cookie: #{_no_crlf_check(cookie)}#{EOL}"
when Array
arr = cookie
- arr.each {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ arr.each {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
when Hash
hash = cookie
- hash.each_value {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ hash.each_value {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
end
end
if @output_cookies
- @output_cookies.each {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ @output_cookies.each {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
end
## other headers
options.each do |key, value|
- buf << "#{key}: #{value}#{EOL}"
+ buf << "#{_no_crlf_check(key)}: #{_no_crlf_check(value)}#{EOL}"
end
return buf
end # _header_for_hash
diff --git a/lib/cgi/util.rb b/lib/cgi/util.rb
index 55e61bf984..ce77a0ccd5 100644
--- a/lib/cgi/util.rb
+++ b/lib/cgi/util.rb
@@ -5,24 +5,57 @@ class CGI
extend Util
end
module CGI::Util
- @@accept_charset="UTF-8" unless defined?(@@accept_charset)
- # URL-encode a string.
+ @@accept_charset = Encoding::UTF_8 unless defined?(@@accept_charset)
+
+ # URL-encode a string into application/x-www-form-urlencoded.
+ # Space characters (+" "+) are encoded with plus signs (+"+"+)
# url_encoded_string = CGI.escape("'Stop!' said Fred")
# # => "%27Stop%21%27+said+Fred"
def escape(string)
encoding = string.encoding
- string.b.gsub(/([^ a-zA-Z0-9_.\-~]+)/) do |m|
+ buffer = string.b
+ buffer.gsub!(/([^ a-zA-Z0-9_.\-~]+)/) do |m|
'%' + m.unpack('H2' * m.bytesize).join('%').upcase
- end.tr(' ', '+').force_encoding(encoding)
+ end
+ buffer.tr!(' ', '+')
+ buffer.force_encoding(encoding)
end
- # URL-decode a string with encoding(optional).
+ # URL-decode an application/x-www-form-urlencoded string with encoding(optional).
# string = CGI.unescape("%27Stop%21%27+said+Fred")
# # => "'Stop!' said Fred"
- def unescape(string,encoding=@@accept_charset)
- str=string.tr('+', ' ').b.gsub(/((?:%[0-9a-fA-F]{2})+)/) do |m|
+ def unescape(string, encoding = @@accept_charset)
+ str = string.tr('+', ' ')
+ str = str.b
+ str.gsub!(/((?:%[0-9a-fA-F]{2})+)/) do |m|
+ [m.delete('%')].pack('H*')
+ end
+ str.force_encoding(encoding)
+ str.valid_encoding? ? str : str.force_encoding(string.encoding)
+ end
+
+ # URL-encode a string following RFC 3986
+ # Space characters (+" "+) are encoded with (+"%20"+)
+ # url_encoded_string = CGI.escape("'Stop!' said Fred")
+ # # => "%27Stop%21%27%20said%20Fred"
+ def escapeURIComponent(string)
+ encoding = string.encoding
+ buffer = string.b
+ buffer.gsub!(/([^a-zA-Z0-9_.\-~]+)/) do |m|
+ '%' + m.unpack('H2' * m.bytesize).join('%').upcase
+ end
+ buffer.force_encoding(encoding)
+ end
+
+ # URL-decode a string following RFC 3986 with encoding(optional).
+ # string = CGI.unescape("%27Stop%21%27+said%20Fred")
+ # # => "'Stop!'+said Fred"
+ def unescapeURIComponent(string, encoding = @@accept_charset)
+ str = string.b
+ str.gsub!(/((?:%[0-9a-fA-F]{2})+)/) do |m|
[m.delete('%')].pack('H*')
- end.force_encoding(encoding)
+ end
+ str.force_encoding(encoding)
str.valid_encoding? ? str : str.force_encoding(string.encoding)
end
@@ -145,7 +178,7 @@ module CGI::Util
def escapeElement(string, *elements)
elements = elements[0] if elements[0].kind_of?(Array)
unless elements.empty?
- string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/i) do
+ string.gsub(/<\/?(?:#{elements.join("|")})\b[^<>]*+>?/im) do
CGI.escapeHTML($&)
end
else
@@ -165,7 +198,7 @@ module CGI::Util
def unescapeElement(string, *elements)
elements = elements[0] if elements[0].kind_of?(Array)
unless elements.empty?
- string.gsub(/&lt;\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?&gt;/i) do
+ string.gsub(/&lt;\/?(?:#{elements.join("|")})\b(?>[^&]+|&(?![gl]t;)\w+;)*(?:&gt;)?/im) do
unescapeHTML($&)
end
else
diff --git a/lib/csv.rb b/lib/csv.rb
index 06a490f34c..0307033941 100644
--- a/lib/csv.rb
+++ b/lib/csv.rb
@@ -48,7 +48,7 @@
#
# === Interface
#
-# * CSV now uses Hash-style parameters to set options.
+# * CSV now uses keyword parameters to set options.
# * CSV no longer has generate_row() or parse_row().
# * The old CSV's Reader and Writer classes have been dropped.
# * CSV::open() is now more like Ruby's open().
@@ -95,16 +95,24 @@ require "stringio"
require_relative "csv/fields_converter"
require_relative "csv/input_record_separator"
-require_relative "csv/match_p"
require_relative "csv/parser"
require_relative "csv/row"
require_relative "csv/table"
require_relative "csv/writer"
-using CSV::MatchP if CSV.const_defined?(:MatchP)
-
# == \CSV
-# \CSV (comma-separated variables) data is a text representation of a table:
+#
+# === In a Hurry?
+#
+# If you are familiar with \CSV data and have a particular task in mind,
+# you may want to go directly to the:
+# - {Recipes for CSV}[doc/csv/recipes/recipes_rdoc.html].
+#
+# Otherwise, read on here, about the API: classes, methods, and constants.
+#
+# === \CSV Data
+#
+# \CSV (comma-separated values) data is a text representation of a table:
# - A _row_ _separator_ delimits table rows.
# A common row separator is the newline character <tt>"\n"</tt>.
# - A _column_ _separator_ delimits fields in a row.
@@ -346,7 +354,9 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
# - +row_sep+: Specifies the row separator; used to delimit rows.
# - +col_sep+: Specifies the column separator; used to delimit fields.
# - +quote_char+: Specifies the quote character; used to quote fields.
-# - +field_size_limit+: Specifies the maximum field size allowed.
+# - +field_size_limit+: Specifies the maximum field size + 1 allowed.
+# Deprecated since 3.2.3. Use +max_field_size+ instead.
+# - +max_field_size+: Specifies the maximum field size allowed.
# - +converters+: Specifies the field converters to be used.
# - +unconverted_fields+: Specifies whether unconverted fields are to be available.
# - +headers+: Specifies whether data contains headers,
@@ -703,7 +713,7 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
# Header converters operate only on headers (and not on other rows).
#
# There are three ways to use header \converters;
-# these examples use built-in header converter +:dowhcase+,
+# these examples use built-in header converter +:downcase+,
# which downcases each parsed header.
#
# - Option +header_converters+ with a singleton parsing method:
@@ -853,8 +863,9 @@ class CSV
# <b><tt>index</tt></b>:: The zero-based index of the field in its row.
# <b><tt>line</tt></b>:: The line of the data source this row is from.
# <b><tt>header</tt></b>:: The header for the column, when available.
+ # <b><tt>quoted?</tt></b>:: True or false, whether the original value is quoted or not.
#
- FieldInfo = Struct.new(:index, :line, :header)
+ FieldInfo = Struct.new(:index, :line, :header, :quoted?)
# A Regexp used to find and convert some common Date formats.
DateMatcher = / \A(?: (\w+,?\s+)?\w+\s+\d{1,2},?\s+\d{2,4} |
@@ -862,10 +873,9 @@ class CSV
# A Regexp used to find and convert some common DateTime formats.
DateTimeMatcher =
/ \A(?: (\w+,?\s+)?\w+\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2},?\s+\d{2,4} |
- \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2} |
- # ISO-8601
+ # ISO-8601 and RFC-3339 (space instead of T) recognized by DateTime.parse
\d{4}-\d{2}-\d{2}
- (?:T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?(?:[+-]\d{2}(?::\d{2})|Z)?)?)?
+ (?:[T\s]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?(?:[+-]\d{2}(?::\d{2})|Z)?)?)?
)\z /x
# The encoding used by all converters.
@@ -915,7 +925,8 @@ class CSV
symbol: lambda { |h|
h.encode(ConverterEncoding).downcase.gsub(/[^\s\w]+/, "").strip.
gsub(/\s+/, "_").to_sym
- }
+ },
+ symbol_raw: lambda { |h| h.encode(ConverterEncoding).to_sym }
}
# Default values for method options.
@@ -926,6 +937,7 @@ class CSV
quote_char: '"',
# For parsing.
field_size_limit: nil,
+ max_field_size: nil,
converters: nil,
unconverted_fields: nil,
headers: false,
@@ -993,7 +1005,7 @@ class CSV
def instance(data = $stdout, **options)
# create a _signature_ for this method call, data object and options
sig = [data.object_id] +
- options.values_at(*DEFAULT_OPTIONS.keys.sort_by { |sym| sym.to_s })
+ options.values_at(*DEFAULT_OPTIONS.keys)
# fetch or create the instance for this signature
@@instances ||= Hash.new
@@ -1007,65 +1019,190 @@ class CSV
end
# :call-seq:
- # filter(**options) {|row| ... }
- # filter(in_string, **options) {|row| ... }
- # filter(in_io, **options) {|row| ... }
- # filter(in_string, out_string, **options) {|row| ... }
- # filter(in_string, out_io, **options) {|row| ... }
- # filter(in_io, out_string, **options) {|row| ... }
- # filter(in_io, out_io, **options) {|row| ... }
- #
- # Reads \CSV input and writes \CSV output.
- #
- # For each input row:
- # - Forms the data into:
- # - A CSV::Row object, if headers are in use.
- # - An \Array of Arrays, otherwise.
- # - Calls the block with that object.
- # - Appends the block's return value to the output.
+ # filter(in_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
+ # filter(in_string_or_io, out_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
+ # filter(**options) {|row| ... } -> array_of_arrays or csv_table
#
- # Arguments:
- # * \CSV source:
- # * Argument +in_string+, if given, should be a \String object;
- # it will be put into a new StringIO object positioned at the beginning.
- # * Argument +in_io+, if given, should be an IO object that is
- # open for reading; on return, the IO object will be closed.
- # * If neither +in_string+ nor +in_io+ is given,
- # the input stream defaults to {ARGF}[https://ruby-doc.org/core/ARGF.html].
- # * \CSV output:
- # * Argument +out_string+, if given, should be a \String object;
- # it will be put into a new StringIO object positioned at the beginning.
- # * Argument +out_io+, if given, should be an IO object that is
- # ppen for writing; on return, the IO object will be closed.
- # * If neither +out_string+ nor +out_io+ is given,
- # the output stream defaults to <tt>$stdout</tt>.
- # * Argument +options+ should be keyword arguments.
- # - Each argument name that is prefixed with +in_+ or +input_+
- # is stripped of its prefix and is treated as an option
- # for parsing the input.
- # Option +input_row_sep+ defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>.
- # - Each argument name that is prefixed with +out_+ or +output_+
- # is stripped of its prefix and is treated as an option
- # for generating the output.
- # Option +output_row_sep+ defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>.
- # - Each argument not prefixed as above is treated as an option
- # both for parsing the input and for generating the output.
- # - See {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
- # and {Options for Generating}[#class-CSV-label-Options+for+Generating].
+ # - Parses \CSV from a source (\String, \IO stream, or ARGF).
+ # - Calls the given block with each parsed row:
+ # - Without headers, each row is an \Array.
+ # - With headers, each row is a CSV::Row.
+ # - Generates \CSV to an output (\String, \IO stream, or STDOUT).
+ # - Returns the parsed source:
+ # - Without headers, an \Array of \Arrays.
+ # - With headers, a CSV::Table.
#
- # Example:
- # in_string = "foo,0\nbar,1\nbaz,2\n"
+ # When +in_string_or_io+ is given, but not +out_string_or_io+,
+ # parses from the given +in_string_or_io+
+ # and generates to STDOUT.
+ #
+ # \String input without headers:
+ #
+ # in_string = "foo,0\nbar,1\nbaz,2"
+ # CSV.filter(in_string) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
+ #
+ # Output (to STDOUT):
+ #
+ # FOO,0
+ # BAR,-1
+ # BAZ,-2
+ #
+ # \String input with headers:
+ #
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
+ # CSV.filter(in_string, headers: true) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
+ #
+ # Output (to STDOUT):
+ #
+ # Name,Value
+ # FOO,0
+ # BAR,-1
+ # BAZ,-2
+ #
+ # \IO stream input without headers:
+ #
+ # File.write('t.csv', "foo,0\nbar,1\nbaz,2")
+ # File.open('t.csv') do |in_io|
+ # CSV.filter(in_io) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
+ #
+ # Output (to STDOUT):
+ #
+ # FOO,0
+ # BAR,-1
+ # BAZ,-2
+ #
+ # \IO stream input with headers:
+ #
+ # File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
+ # File.open('t.csv') do |in_io|
+ # CSV.filter(in_io, headers: true) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
+ #
+ # Output (to STDOUT):
+ #
+ # Name,Value
+ # FOO,0
+ # BAR,-1
+ # BAZ,-2
+ #
+ # When both +in_string_or_io+ and +out_string_or_io+ are given,
+ # parses from +in_string_or_io+ and generates to +out_string_or_io+.
+ #
+ # \String output without headers:
+ #
+ # in_string = "foo,0\nbar,1\nbaz,2"
# out_string = ''
# CSV.filter(in_string, out_string) do |row|
- # row[0] = row[0].upcase
- # row[1] *= 4
- # end
- # out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n"
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
+ # out_string # => "FOO,0\nBAR,-1\nBAZ,-2\n"
+ #
+ # \String output with headers:
+ #
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
+ # out_string = ''
+ # CSV.filter(in_string, out_string, headers: true) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
+ # out_string # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
+ #
+ # \IO stream output without headers:
+ #
+ # in_string = "foo,0\nbar,1\nbaz,2"
+ # File.open('t.csv', 'w') do |out_io|
+ # CSV.filter(in_string, out_io) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
+ # File.read('t.csv') # => "FOO,0\nBAR,-1\nBAZ,-2\n"
+ #
+ # \IO stream output with headers:
+ #
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
+ # File.open('t.csv', 'w') do |out_io|
+ # CSV.filter(in_string, out_io, headers: true) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
+ # File.read('t.csv') # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
+ #
+ # When neither +in_string_or_io+ nor +out_string_or_io+ given,
+ # parses from {ARGF}[rdoc-ref:ARGF]
+ # and generates to STDOUT.
+ #
+ # Without headers:
+ #
+ # # Put Ruby code into a file.
+ # ruby = <<-EOT
+ # require 'csv'
+ # CSV.filter do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end
+ # EOT
+ # File.write('t.rb', ruby)
+ # # Put some CSV into a file.
+ # File.write('t.csv', "foo,0\nbar,1\nbaz,2")
+ # # Run the Ruby code with CSV filename as argument.
+ # system(Gem.ruby, "t.rb", "t.csv")
+ #
+ # Output (to STDOUT):
+ #
+ # FOO,0
+ # BAR,-1
+ # BAZ,-2
+ #
+ # With headers:
+ #
+ # # Put Ruby code into a file.
+ # ruby = <<-EOT
+ # require 'csv'
+ # CSV.filter(headers: true) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end
+ # EOT
+ # File.write('t.rb', ruby)
+ # # Put some CSV into a file.
+ # File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
+ # # Run the Ruby code with CSV filename as argument.
+ # system(Gem.ruby, "t.rb", "t.csv")
+ #
+ # Output (to STDOUT):
+ #
+ # Name,Value
+ # FOO,0
+ # BAR,-1
+ # BAZ,-2
+ #
+ # Arguments:
+ #
+ # * Argument +in_string_or_io+ must be a \String or an \IO stream.
+ # * Argument +out_string_or_io+ must be a \String or an \IO stream.
+ # * Arguments <tt>**options</tt> must be keyword options.
+ # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
def filter(input=nil, output=nil, **options)
# parse options for input, output, or both
in_options, out_options = Hash.new, {row_sep: InputRecordSeparator.value}
options.each do |key, value|
- case key.to_s
+ case key
when /\Ain(?:put)?_(.+)\Z/
in_options[$1.to_sym] = value
when /\Aout(?:put)?_(.+)\Z/
@@ -1107,111 +1244,90 @@ class CSV
#
# :call-seq:
- # foreach(path, mode='r', **options) {|row| ... )
- # foreach(io, mode='r', **options {|row| ... )
- # foreach(path, mode='r', headers: ..., **options) {|row| ... )
- # foreach(io, mode='r', headers: ..., **options {|row| ... )
- # foreach(path, mode='r', **options) -> new_enumerator
- # foreach(io, mode='r', **options -> new_enumerator
+ # foreach(path_or_io, mode='r', **options) {|row| ... )
+ # foreach(path_or_io, mode='r', **options) -> new_enumerator
#
- # Calls the block with each row read from source +path+ or +io+.
+ # Calls the block with each row read from source +path_or_io+.
#
- # * Argument +path+, if given, must be the path to a file.
- # :include: ../doc/csv/arguments/io.rdoc
- # * Argument +mode+, if given, must be a \File mode
- # See {Open Mode}[IO.html#method-c-new-label-Open+Mode].
- # * Arguments <tt>**options</tt> must be keyword options.
- # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
- # * This method optionally accepts an additional <tt>:encoding</tt> option
- # that you can use to specify the Encoding of the data read from +path+ or +io+.
- # You must provide this unless your data is in the encoding
- # given by <tt>Encoding::default_external</tt>.
- # Parsing will use this to determine how to parse the data.
- # You may provide a second Encoding to
- # have the data transcoded as it is read. For example,
- # encoding: 'UTF-32BE:UTF-8'
- # would read +UTF-32BE+ data from the file
- # but transcode it to +UTF-8+ before parsing.
- #
- # ====== Without Option +headers+
+ # \Path input without headers:
#
- # Without option +headers+, returns each row as an \Array object.
- #
- # These examples assume prior execution of:
# string = "foo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
+ # in_path = 't.csv'
+ # File.write(in_path, string)
+ # CSV.foreach(in_path) {|row| p row }
#
- # Read rows from a file at +path+:
- # CSV.foreach(path) {|row| p row }
# Output:
- # ["foo", "0"]
- # ["bar", "1"]
- # ["baz", "2"]
#
- # Read rows from an \IO object:
- # File.open(path) do |file|
- # CSV.foreach(file) {|row| p row }
- # end
- #
- # Output:
# ["foo", "0"]
# ["bar", "1"]
# ["baz", "2"]
#
- # Returns a new \Enumerator if no block given:
- # CSV.foreach(path) # => #<Enumerator: CSV:foreach("t.csv", "r")>
- # CSV.foreach(File.open(path)) # => #<Enumerator: CSV:foreach(#<File:t.csv>, "r")>
+ # \Path input with headers:
+ #
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # in_path = 't.csv'
+ # File.write(in_path, string)
+ # CSV.foreach(in_path, headers: true) {|row| p row }
#
- # Issues a warning if an encoding is unsupported:
- # CSV.foreach(File.open(path), encoding: 'foo:bar') {|row| }
# Output:
- # warning: Unsupported encoding foo ignored
- # warning: Unsupported encoding bar ignored
#
- # ====== With Option +headers+
+ # <CSV::Row "Name":"foo" "Value":"0">
+ # <CSV::Row "Name":"bar" "Value":"1">
+ # <CSV::Row "Name":"baz" "Value":"2">
#
- # With {option +headers+}[#class-CSV-label-Option+headers],
- # returns each row as a CSV::Row object.
+ # \IO stream input without headers:
#
- # These examples assume prior execution of:
- # string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
+ # string = "foo,0\nbar,1\nbaz,2\n"
# path = 't.csv'
# File.write(path, string)
- #
- # Read rows from a file at +path+:
- # CSV.foreach(path, headers: true) {|row| p row }
+ # File.open('t.csv') do |in_io|
+ # CSV.foreach(in_io) {|row| p row }
+ # end
#
# Output:
- # #<CSV::Row "Name":"foo" "Count":"0">
- # #<CSV::Row "Name":"bar" "Count":"1">
- # #<CSV::Row "Name":"baz" "Count":"2">
#
- # Read rows from an \IO object:
- # File.open(path) do |file|
- # CSV.foreach(file, headers: true) {|row| p row }
+ # ["foo", "0"]
+ # ["bar", "1"]
+ # ["baz", "2"]
+ #
+ # \IO stream input with headers:
+ #
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # path = 't.csv'
+ # File.write(path, string)
+ # File.open('t.csv') do |in_io|
+ # CSV.foreach(in_io, headers: true) {|row| p row }
# end
#
# Output:
- # #<CSV::Row "Name":"foo" "Count":"0">
- # #<CSV::Row "Name":"bar" "Count":"1">
- # #<CSV::Row "Name":"baz" "Count":"2">
- #
- # ---
#
- # Raises an exception if +path+ is a \String, but not the path to a readable file:
- # # Raises Errno::ENOENT (No such file or directory @ rb_sysopen - nosuch.csv):
- # CSV.foreach('nosuch.csv') {|row| }
+ # <CSV::Row "Name":"foo" "Value":"0">
+ # <CSV::Row "Name":"bar" "Value":"1">
+ # <CSV::Row "Name":"baz" "Value":"2">
#
- # Raises an exception if +io+ is an \IO object, but not open for reading:
- # io = File.open(path, 'w') {|row| }
- # # Raises TypeError (no implicit conversion of nil into String):
- # CSV.foreach(io) {|row| }
+ # With no block given, returns an \Enumerator:
#
- # Raises an exception if +mode+ is invalid:
- # # Raises ArgumentError (invalid access mode nosuch):
- # CSV.foreach(path, 'nosuch') {|row| }
+ # string = "foo,0\nbar,1\nbaz,2\n"
+ # path = 't.csv'
+ # File.write(path, string)
+ # CSV.foreach(path) # => #<Enumerator: CSV:foreach("t.csv", "r")>
#
+ # Arguments:
+ # * Argument +path_or_io+ must be a file path or an \IO stream.
+ # * Argument +mode+, if given, must be a \File mode
+ # See {Open Mode}[https://ruby-doc.org/core/IO.html#method-c-new-label-Open+Mode].
+ # * Arguments <tt>**options</tt> must be keyword options.
+ # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
+ # * This method optionally accepts an additional <tt>:encoding</tt> option
+ # that you can use to specify the Encoding of the data read from +path+ or +io+.
+ # You must provide this unless your data is in the encoding
+ # given by <tt>Encoding::default_external</tt>.
+ # Parsing will use this to determine how to parse the data.
+ # You may provide a second Encoding to
+ # have the data transcoded as it is read. For example,
+ # encoding: 'UTF-32BE:UTF-8'
+ # would read +UTF-32BE+ data from the file
+ # but transcode it to +UTF-8+ before parsing.
def foreach(path, mode="r", **options, &block)
return to_enum(__method__, path, mode, **options) unless block_given?
open(path, mode, **options) do |csv|
@@ -1349,6 +1465,46 @@ class CSV
(new(str, **options) << row).string
end
+ # :call-seq:
+ # CSV.generate_lines(rows)
+ # CSV.generate_lines(rows, **options)
+ #
+ # Returns the \String created by generating \CSV from
+ # using the specified +options+.
+ #
+ # Argument +rows+ must be an \Array of row. Row is \Array of \String or \CSV::Row.
+ #
+ # Special options:
+ # * Option <tt>:row_sep</tt> defaults to <tt>"\n"</tt> on Ruby 3.0 or later
+ # and <tt>$INPUT_RECORD_SEPARATOR</tt> (<tt>$/</tt>) otherwise.:
+ # $INPUT_RECORD_SEPARATOR # => "\n"
+ # * This method accepts an additional option, <tt>:encoding</tt>, which sets the base
+ # Encoding for the output. This method will try to guess your Encoding from
+ # the first non-+nil+ field in +row+, if possible, but you may need to use
+ # this parameter as a backup plan.
+ #
+ # For other +options+,
+ # see {Options for Generating}[#class-CSV-label-Options+for+Generating].
+ #
+ # ---
+ #
+ # Returns the \String generated from an
+ # CSV.generate_lines([['foo', '0'], ['bar', '1'], ['baz', '2']]) # => "foo,0\nbar,1\nbaz,2\n"
+ #
+ # ---
+ #
+ # Raises an exception
+ # # Raises NoMethodError (undefined method `each' for :foo:Symbol)
+ # CSV.generate_lines(:foo)
+ #
+ def generate_lines(rows, **options)
+ self.generate(**options) do |csv|
+ rows.each do |row|
+ csv << row
+ end
+ end
+ end
+
#
# :call-seq:
# open(file_path, mode = "rb", **options ) -> new_csv
@@ -1357,7 +1513,7 @@ class CSV
# open(io, mode = "rb", **options ) { |csv| ... } -> object
#
# possible options elements:
- # hash form:
+ # keyword form:
# :invalid => nil # raise error on invalid byte sequence (default)
# :invalid => :replace # replace invalid byte sequence
# :undef => :replace # replace undefined conversion
@@ -1424,10 +1580,14 @@ class CSV
def open(filename, mode="r", **options)
# wrap a File opened with the remaining +args+ with no newline
# decorator
- file_opts = {universal_newline: false}.merge(options)
+ file_opts = options.dup
+ unless file_opts.key?(:newline)
+ file_opts[:universal_newline] ||= false
+ end
options.delete(:invalid)
options.delete(:undef)
options.delete(:replace)
+ options.delete_if {|k, _| /newline\z/.match?(k)}
begin
f = File.open(filename, mode, **file_opts)
@@ -1746,6 +1906,7 @@ class CSV
row_sep: :auto,
quote_char: '"',
field_size_limit: nil,
+ max_field_size: nil,
converters: nil,
unconverted_fields: nil,
headers: false,
@@ -1769,8 +1930,19 @@ class CSV
raise ArgumentError.new("Cannot parse nil as CSV") if data.nil?
if data.is_a?(String)
+ if encoding
+ if encoding.is_a?(String)
+ data_external_encoding, data_internal_encoding = encoding.split(":", 2)
+ if data_internal_encoding
+ data = data.encode(data_internal_encoding, data_external_encoding)
+ else
+ data = data.dup.force_encoding(data_external_encoding)
+ end
+ else
+ data = data.dup.force_encoding(encoding)
+ end
+ end
@io = StringIO.new(data)
- @io.set_encoding(encoding || data.encoding)
else
@io = data
end
@@ -1788,11 +1960,14 @@ class CSV
@initial_header_converters = header_converters
@initial_write_converters = write_converters
+ if max_field_size.nil? and field_size_limit
+ max_field_size = field_size_limit - 1
+ end
@parser_options = {
column_separator: col_sep,
row_separator: row_sep,
quote_character: quote_char,
- field_size_limit: field_size_limit,
+ max_field_size: max_field_size,
unconverted_fields: unconverted_fields,
headers: headers,
return_headers: return_headers,
@@ -1860,11 +2035,25 @@ class CSV
# Returns the limit for field size; used for parsing;
# see {Option +field_size_limit+}[#class-CSV-label-Option+field_size_limit]:
# CSV.new('').field_size_limit # => nil
+ #
+ # Deprecated since 3.2.3. Use +max_field_size+ instead.
def field_size_limit
parser.field_size_limit
end
# :call-seq:
+ # csv.max_field_size -> integer or nil
+ #
+ # Returns the limit for field size; used for parsing;
+ # see {Option +max_field_size+}[#class-CSV-label-Option+max_field_size]:
+ # CSV.new('').max_field_size # => nil
+ #
+ # Since 3.2.3.
+ def max_field_size
+ parser.max_field_size
+ end
+
+ # :call-seq:
# csv.skip_lines -> regexp or nil
#
# Returns the \Regexp used to identify comment lines; used for parsing;
@@ -1994,7 +2183,7 @@ class CSV
end
# :call-seq:
- # csv.encoding -> endcoding
+ # csv.encoding -> encoding
#
# Returns the encoding used for parsing and generating;
# see {Character Encodings (M17n or Multilingualization)}[#class-CSV-label-Character+Encodings+-28M17n+or+Multilingualization-29]:
@@ -2362,7 +2551,13 @@ class CSV
# p row
# end
def each(&block)
- parser_enumerator.each(&block)
+ return to_enum(__method__) unless block_given?
+ begin
+ while true
+ yield(parser_enumerator.next)
+ end
+ rescue StopIteration
+ end
end
# :call-seq:
diff --git a/lib/csv/fields_converter.rb b/lib/csv/fields_converter.rb
index b206118d99..d15977d379 100644
--- a/lib/csv/fields_converter.rb
+++ b/lib/csv/fields_converter.rb
@@ -44,7 +44,7 @@ class CSV
@converters.empty?
end
- def convert(fields, headers, lineno)
+ def convert(fields, headers, lineno, quoted_fields)
return fields unless need_convert?
fields.collect.with_index do |field, index|
@@ -63,7 +63,8 @@ class CSV
else
header = nil
end
- field = converter[field, FieldInfo.new(index, lineno, header)]
+ quoted = quoted_fields[index]
+ field = converter[field, FieldInfo.new(index, lineno, header, quoted)]
end
break unless field.is_a?(String) # short-circuit pipeline for speed
end
diff --git a/lib/csv/input_record_separator.rb b/lib/csv/input_record_separator.rb
index bbf13479f7..7a99343c0c 100644
--- a/lib/csv/input_record_separator.rb
+++ b/lib/csv/input_record_separator.rb
@@ -4,20 +4,7 @@ require "stringio"
class CSV
module InputRecordSeparator
class << self
- is_input_record_separator_deprecated = false
- verbose, $VERBOSE = $VERBOSE, true
- stderr, $stderr = $stderr, StringIO.new
- input_record_separator = $INPUT_RECORD_SEPARATOR
- begin
- $INPUT_RECORD_SEPARATOR = "\r\n"
- is_input_record_separator_deprecated = (not $stderr.string.empty?)
- ensure
- $INPUT_RECORD_SEPARATOR = input_record_separator
- $stderr = stderr
- $VERBOSE = verbose
- end
-
- if is_input_record_separator_deprecated
+ if RUBY_VERSION >= "3.0.0"
def value
"\n"
end
diff --git a/lib/csv/parser.rb b/lib/csv/parser.rb
index 7e943acf21..afb3131cd5 100644
--- a/lib/csv/parser.rb
+++ b/lib/csv/parser.rb
@@ -2,15 +2,10 @@
require "strscan"
-require_relative "delete_suffix"
require_relative "input_record_separator"
-require_relative "match_p"
require_relative "row"
require_relative "table"
-using CSV::DeleteSuffix if CSV.const_defined?(:DeleteSuffix)
-using CSV::MatchP if CSV.const_defined?(:MatchP)
-
class CSV
# Note: Don't use this class directly. This is an internal class.
class Parser
@@ -27,6 +22,10 @@ class CSV
class InvalidEncoding < StandardError
end
+ # Raised when unexpected case is happen.
+ class UnexpectedError < StandardError
+ end
+
#
# CSV::Scanner receives a CSV output, scans it and return the content.
# It also controls the life cycle of the object with its methods +keep_start+,
@@ -78,10 +77,10 @@ class CSV
# +keep_end+, +keep_back+, +keep_drop+.
#
# CSV::InputsScanner.scan() 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.
+ # If there's a match, the scanner advances the "scan pointer" and returns the matched string.
# Otherwise, the scanner returns nil.
#
- # CSV::InputsScanner.rest() returns the “rest” of the string (i.e. everything after the scan pointer).
+ # CSV::InputsScanner.rest() returns the "rest" of the string (i.e. everything after the scan pointer).
# If there is no more data (eos? = true), it returns "".
#
class InputsScanner
@@ -96,11 +95,13 @@ class CSV
end
def each_line(row_separator)
+ return enum_for(__method__, row_separator) unless block_given?
buffer = nil
input = @scanner.rest
position = @scanner.pos
offset = 0
n_row_separator_chars = row_separator.size
+ # trace(__method__, :start, line, input)
while true
input.each_line(row_separator) do |line|
@scanner.pos += line.bytesize
@@ -140,25 +141,28 @@ class CSV
end
def scan(pattern)
+ # trace(__method__, pattern, :start)
value = @scanner.scan(pattern)
+ # trace(__method__, pattern, :done, :last, value) if @last_scanner
return value if @last_scanner
- if value
- read_chunk if @scanner.eos?
- return value
- else
- nil
- end
+ read_chunk if value and @scanner.eos?
+ # trace(__method__, pattern, :done, value)
+ value
end
def scan_all(pattern)
+ # trace(__method__, pattern, :start)
value = @scanner.scan(pattern)
+ # trace(__method__, pattern, :done, :last, value) if @last_scanner
return value if @last_scanner
return nil if value.nil?
while @scanner.eos? and read_chunk and (sub_value = @scanner.scan(pattern))
+ # trace(__method__, pattern, :sub, sub_value)
value << sub_value
end
+ # trace(__method__, pattern, :done, value)
value
end
@@ -167,68 +171,126 @@ class CSV
end
def keep_start
- @keeps.push([@scanner.pos, nil])
+ # trace(__method__, :start)
+ adjust_last_keep
+ @keeps.push([@scanner, @scanner.pos, nil])
+ # trace(__method__, :done)
end
def keep_end
- start, buffer = @keeps.pop
- keep = @scanner.string.byteslice(start, @scanner.pos - start)
+ # trace(__method__, :start)
+ scanner, start, buffer = @keeps.pop
+ if scanner == @scanner
+ keep = @scanner.string.byteslice(start, @scanner.pos - start)
+ else
+ keep = @scanner.string.byteslice(0, @scanner.pos)
+ end
if buffer
buffer << keep
keep = buffer
end
+ # trace(__method__, :done, keep)
keep
end
def keep_back
- start, buffer = @keeps.pop
+ # trace(__method__, :start)
+ scanner, start, buffer = @keeps.pop
if buffer
+ # trace(__method__, :rescan, start, buffer)
string = @scanner.string
- keep = string.byteslice(start, string.bytesize - start)
+ if scanner == @scanner
+ keep = string.byteslice(start, string.bytesize - start)
+ else
+ keep = string
+ end
if keep and not keep.empty?
@inputs.unshift(StringIO.new(keep))
@last_scanner = false
end
@scanner = StringScanner.new(buffer)
else
+ if @scanner != scanner
+ message = "scanners are different but no buffer: "
+ message += "#{@scanner.inspect}(#{@scanner.object_id}): "
+ message += "#{scanner.inspect}(#{scanner.object_id})"
+ raise UnexpectedError, message
+ end
+ # trace(__method__, :repos, start, buffer)
@scanner.pos = start
end
read_chunk if @scanner.eos?
end
def keep_drop
- @keeps.pop
+ _, _, buffer = @keeps.pop
+ # trace(__method__, :done, :empty) unless buffer
+ return unless buffer
+
+ last_keep = @keeps.last
+ # trace(__method__, :done, :no_last_keep) unless last_keep
+ return unless last_keep
+
+ if last_keep[2]
+ last_keep[2] << buffer
+ else
+ last_keep[2] = buffer
+ end
+ # trace(__method__, :done)
end
def rest
@scanner.rest
end
+ def check(pattern)
+ @scanner.check(pattern)
+ end
+
private
- def read_chunk
- return false if @last_scanner
+ def trace(*args)
+ pp([*args, @scanner, @scanner&.string, @scanner&.pos, @keeps])
+ end
- unless @keeps.empty?
- keep = @keeps.last
- keep_start = keep[0]
- string = @scanner.string
- keep_data = string.byteslice(keep_start, @scanner.pos - keep_start)
- if keep_data
- keep_buffer = keep[1]
- if keep_buffer
- keep_buffer << keep_data
- else
- keep[1] = keep_data.dup
- end
+ def adjust_last_keep
+ # trace(__method__, :start)
+
+ keep = @keeps.last
+ # trace(__method__, :done, :empty) if keep.nil?
+ return if keep.nil?
+
+ scanner, start, buffer = keep
+ string = @scanner.string
+ if @scanner != scanner
+ start = 0
+ end
+ if start == 0 and @scanner.eos?
+ keep_data = string
+ else
+ keep_data = string.byteslice(start, @scanner.pos - start)
+ end
+ if keep_data
+ if buffer
+ buffer << keep_data
+ else
+ keep[2] = keep_data.dup
end
- keep[0] = 0
end
+ # trace(__method__, :done)
+ end
+
+ def read_chunk
+ return false if @last_scanner
+
+ adjust_last_keep
+
input = @inputs.first
case input
when StringIO
string = input.read
raise InvalidEncoding unless string.valid_encoding?
+ # trace(__method__, :stringio, string)
@scanner = StringScanner.new(string)
@inputs.shift
@last_scanner = @inputs.empty?
@@ -237,6 +299,7 @@ class CSV
chunk = input.gets(@row_separator, @chunk_size)
if chunk
raise InvalidEncoding unless chunk.valid_encoding?
+ # trace(__method__, :chunk, chunk)
@scanner = StringScanner.new(chunk)
if input.respond_to?(:eof?) and input.eof?
@inputs.shift
@@ -244,6 +307,7 @@ class CSV
end
true
else
+ # trace(__method__, :no_chunk)
@scanner = StringScanner.new("".encode(@encoding))
@inputs.shift
@last_scanner = @inputs.empty?
@@ -278,7 +342,11 @@ class CSV
end
def field_size_limit
- @field_size_limit
+ @max_field_size&.succ
+ end
+
+ def max_field_size
+ @max_field_size
end
def skip_lines
@@ -346,6 +414,16 @@ class CSV
end
message = "Invalid byte sequence in #{@encoding}"
raise MalformedCSVError.new(message, lineno)
+ rescue UnexpectedError => error
+ if @scanner
+ ignore_broken_line
+ lineno = @lineno
+ else
+ lineno = @lineno + 1
+ end
+ message = "This should not be happen: #{error.message}: "
+ message += "Please report this to https://github.com/ruby/csv/issues"
+ raise MalformedCSVError.new(message, lineno)
end
end
@@ -390,7 +468,7 @@ class CSV
@backslash_quote = false
end
@unconverted_fields = @options[:unconverted_fields]
- @field_size_limit = @options[:field_size_limit]
+ @max_field_size = @options[:max_field_size]
@skip_blanks = @options[:skip_blanks]
@fields_converter = @options[:fields_converter]
@header_fields_converter = @options[:header_fields_converter]
@@ -680,9 +758,10 @@ class CSV
case headers
when Array
@raw_headers = headers
+ quoted_fields = [false] * @raw_headers.size
@use_headers = true
when String
- @raw_headers = parse_headers(headers)
+ @raw_headers, quoted_fields = parse_headers(headers)
@use_headers = true
when nil, false
@raw_headers = nil
@@ -692,21 +771,28 @@ class CSV
@use_headers = true
end
if @raw_headers
- @headers = adjust_headers(@raw_headers)
+ @headers = adjust_headers(@raw_headers, quoted_fields)
else
@headers = nil
end
end
def parse_headers(row)
- CSV.parse_line(row,
- col_sep: @column_separator,
- row_sep: @row_separator,
- quote_char: @quote_character)
+ quoted_fields = []
+ converter = lambda do |field, info|
+ quoted_fields << info.quoted?
+ field
+ end
+ headers = CSV.parse_line(row,
+ col_sep: @column_separator,
+ row_sep: @row_separator,
+ quote_char: @quote_character,
+ converters: [converter])
+ [headers, quoted_fields]
end
- def adjust_headers(headers)
- adjusted_headers = @header_fields_converter.convert(headers, nil, @lineno)
+ def adjust_headers(headers, quoted_fields)
+ adjusted_headers = @header_fields_converter.convert(headers, nil, @lineno, quoted_fields)
adjusted_headers.each {|h| h.freeze if h.is_a? String}
adjusted_headers
end
@@ -729,28 +815,28 @@ class CSV
sample[0, 128].index(@quote_character)
end
- SCANNER_TEST = (ENV["CSV_PARSER_SCANNER_TEST"] == "yes")
- if SCANNER_TEST
- class UnoptimizedStringIO
- def initialize(string)
- @io = StringIO.new(string, "rb:#{string.encoding}")
- end
+ class UnoptimizedStringIO # :nodoc:
+ def initialize(string)
+ @io = StringIO.new(string, "rb:#{string.encoding}")
+ end
- def gets(*args)
- @io.gets(*args)
- end
+ def gets(*args)
+ @io.gets(*args)
+ end
- def each_line(*args, &block)
- @io.each_line(*args, &block)
- end
+ def each_line(*args, &block)
+ @io.each_line(*args, &block)
+ end
- def eof?
- @io.eof?
- end
+ def eof?
+ @io.eof?
end
+ end
- SCANNER_TEST_CHUNK_SIZE =
- Integer((ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] || "1"), 10)
+ SCANNER_TEST = (ENV["CSV_PARSER_SCANNER_TEST"] == "yes")
+ if SCANNER_TEST
+ SCANNER_TEST_CHUNK_SIZE_NAME = "CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"
+ SCANNER_TEST_CHUNK_SIZE_VALUE = ENV[SCANNER_TEST_CHUNK_SIZE_NAME]
def build_scanner
inputs = @samples.collect do |sample|
UnoptimizedStringIO.new(sample)
@@ -760,10 +846,17 @@ class CSV
else
inputs << @input
end
+ begin
+ chunk_size_value = ENV[SCANNER_TEST_CHUNK_SIZE_NAME]
+ rescue # Ractor::IsolationError
+ # Ractor on Ruby 3.0 can't read ENV value.
+ chunk_size_value = SCANNER_TEST_CHUNK_SIZE_VALUE
+ end
+ chunk_size = Integer((chunk_size_value || "1"), 10)
InputsScanner.new(inputs,
@encoding,
@row_separator,
- chunk_size: SCANNER_TEST_CHUNK_SIZE)
+ chunk_size: chunk_size)
end
else
def build_scanner
@@ -826,6 +919,14 @@ class CSV
end
end
+ def validate_field_size(field)
+ return unless @max_field_size
+ return if field.size <= @max_field_size
+ ignore_broken_line
+ message = "Field size exceeded: #{field.size} > #{@max_field_size}"
+ raise MalformedCSVError.new(message, @lineno)
+ end
+
def parse_no_quote(&block)
@scanner.each_line(@row_separator) do |line|
next if @skip_lines and skip_line?(line)
@@ -835,9 +936,16 @@ class CSV
if line.empty?
next if @skip_blanks
row = []
+ quoted_fields = []
else
line = strip_value(line)
row = line.split(@split_column_separator, -1)
+ quoted_fields = [false] * row.size
+ if @max_field_size
+ row.each do |column|
+ validate_field_size(column)
+ end
+ end
n_columns = row.size
i = 0
while i < n_columns
@@ -846,7 +954,7 @@ class CSV
end
end
@last_line = original_line
- emit_row(row, &block)
+ emit_row(row, quoted_fields, &block)
end
end
@@ -868,31 +976,37 @@ class CSV
next
end
row = []
+ quoted_fields = []
elsif line.include?(@cr) or line.include?(@lf)
@scanner.keep_back
@need_robust_parsing = true
return parse_quotable_robust(&block)
else
row = line.split(@split_column_separator, -1)
+ quoted_fields = []
n_columns = row.size
i = 0
while i < n_columns
column = row[i]
if column.empty?
+ quoted_fields << false
row[i] = nil
else
n_quotes = column.count(@quote_character)
if n_quotes.zero?
+ quoted_fields << false
# no quote
elsif n_quotes == 2 and
column.start_with?(@quote_character) and
column.end_with?(@quote_character)
+ quoted_fields << true
row[i] = column[1..-2]
else
@scanner.keep_back
@need_robust_parsing = true
return parse_quotable_robust(&block)
end
+ validate_field_size(row[i])
end
i += 1
end
@@ -900,13 +1014,14 @@ class CSV
@scanner.keep_drop
@scanner.keep_start
@last_line = original_line
- emit_row(row, &block)
+ emit_row(row, quoted_fields, &block)
end
@scanner.keep_drop
end
def parse_quotable_robust(&block)
row = []
+ quoted_fields = []
skip_needless_lines
start_row
while true
@@ -916,32 +1031,39 @@ class CSV
value = parse_column_value
if value
@scanner.scan_all(@strip_value) if @strip_value
- if @field_size_limit and value.size >= @field_size_limit
- ignore_broken_line
- raise MalformedCSVError.new("Field size exceeded", @lineno)
- end
+ validate_field_size(value)
end
if parse_column_end
row << value
+ quoted_fields << @quoted_column_value
elsif parse_row_end
if row.empty? and value.nil?
- emit_row([], &block) unless @skip_blanks
+ emit_row([], [], &block) unless @skip_blanks
else
row << value
- emit_row(row, &block)
+ quoted_fields << @quoted_column_value
+ emit_row(row, quoted_fields, &block)
row = []
+ quoted_fields = []
end
skip_needless_lines
start_row
elsif @scanner.eos?
break if row.empty? and value.nil?
row << value
- emit_row(row, &block)
+ quoted_fields << @quoted_column_value
+ emit_row(row, quoted_fields, &block)
break
else
if @quoted_column_value
+ if liberal_parsing? and (new_line = @scanner.check(@line_end))
+ message =
+ "Illegal end-of-line sequence outside of a quoted field " +
+ "<#{new_line.inspect}>"
+ else
+ message = "Any value after quoted field isn't allowed"
+ end
ignore_broken_line
- message = "Any value after quoted field isn't allowed"
raise MalformedCSVError.new(message, @lineno)
elsif @unquoted_column_value and
(new_line = @scanner.scan(@line_end))
@@ -1034,7 +1156,7 @@ class CSV
if (n_quotes % 2).zero?
quotes[0, (n_quotes - 2) / 2]
else
- value = quotes[0, (n_quotes - 1) / 2]
+ value = quotes[0, n_quotes / 2]
while true
quoted_value = @scanner.scan_all(@quoted_value)
value << quoted_value if quoted_value
@@ -1058,11 +1180,9 @@ class CSV
n_quotes = quotes.size
if n_quotes == 1
break
- elsif (n_quotes % 2) == 1
- value << quotes[0, (n_quotes - 1) / 2]
- break
else
value << quotes[0, n_quotes / 2]
+ break if (n_quotes % 2) == 1
end
end
value
@@ -1098,18 +1218,15 @@ class CSV
def strip_value(value)
return value unless @strip
- return nil if value.nil?
+ return value if value.nil?
case @strip
when String
- size = value.size
- while value.start_with?(@strip)
- size -= 1
- value = value[1, size]
+ while value.delete_prefix!(@strip)
+ # do nothing
end
- while value.end_with?(@strip)
- size -= 1
- value = value[0, size]
+ while value.delete_suffix!(@strip)
+ # do nothing
end
else
value.strip!
@@ -1132,22 +1249,22 @@ class CSV
@scanner.keep_start
end
- def emit_row(row, &block)
+ def emit_row(row, quoted_fields, &block)
@lineno += 1
raw_row = row
if @use_headers
if @headers.nil?
- @headers = adjust_headers(row)
+ @headers = adjust_headers(row, quoted_fields)
return unless @return_headers
row = Row.new(@headers, row, true)
else
row = Row.new(@headers,
- @fields_converter.convert(raw_row, @headers, @lineno))
+ @fields_converter.convert(raw_row, @headers, @lineno, quoted_fields))
end
else
# convert fields, if needed...
- row = @fields_converter.convert(raw_row, nil, @lineno)
+ row = @fields_converter.convert(raw_row, nil, @lineno, quoted_fields)
end
# inject unconverted fields and accessor, if requested...
diff --git a/lib/csv/row.rb b/lib/csv/row.rb
index 0f465ea2a3..86323f7d0a 100644
--- a/lib/csv/row.rb
+++ b/lib/csv/row.rb
@@ -3,30 +3,105 @@
require "forwardable"
class CSV
+ # = \CSV::Row
+ # A \CSV::Row instance represents a \CSV table row.
+ # (see {class CSV}[../CSV.html]).
#
- # A CSV::Row is part Array and part Hash. It retains an order for the fields
- # and allows duplicates just as an Array would, but also allows you to access
- # fields by name just as you could if they were in a Hash.
+ # The instance may have:
+ # - Fields: each is an object, not necessarily a \String.
+ # - Headers: each serves a key, and also need not be a \String.
#
- # All rows returned by CSV will be constructed from this class, if header row
- # processing is activated.
+ # === Instance Methods
+ #
+ # \CSV::Row has three groups of instance methods:
+ # - Its own internally defined instance methods.
+ # - Methods included by module Enumerable.
+ # - Methods delegated to class Array.:
+ # * Array#empty?
+ # * Array#length
+ # * Array#size
+ #
+ # == Creating a \CSV::Row Instance
+ #
+ # Commonly, a new \CSV::Row instance is created by parsing \CSV source
+ # that has headers:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.each {|row| p row }
+ # Output:
+ # #<CSV::Row "Name":"foo" "Value":"0">
+ # #<CSV::Row "Name":"bar" "Value":"1">
+ # #<CSV::Row "Name":"baz" "Value":"2">
+ #
+ # You can also create a row directly. See ::new.
+ #
+ # == Headers
+ #
+ # Like a \CSV::Table, a \CSV::Row has headers.
+ #
+ # A \CSV::Row that was created by parsing \CSV source
+ # inherits its headers from the table:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # row = table.first
+ # row.headers # => ["Name", "Value"]
+ #
+ # You can also create a new row with headers;
+ # like the keys in a \Hash, the headers need not be Strings:
+ # row = CSV::Row.new([:name, :value], ['foo', 0])
+ # row.headers # => [:name, :value]
+ #
+ # The new row retains its headers even if added to a table
+ # that has headers:
+ # table << row # => #<CSV::Table mode:col_or_row row_count:5>
+ # row.headers # => [:name, :value]
+ # row[:name] # => "foo"
+ # row['Name'] # => nil
+ #
+ #
+ #
+ # == Accessing Fields
+ #
+ # You may access a field in a \CSV::Row with either its \Integer index
+ # (\Array-style) or its header (\Hash-style).
+ #
+ # Fetch a field using method #[]:
+ # row = CSV::Row.new(['Name', 'Value'], ['foo', 0])
+ # row[1] # => 0
+ # row['Value'] # => 0
+ #
+ # Set a field using method #[]=:
+ # row = CSV::Row.new(['Name', 'Value'], ['foo', 0])
+ # row # => #<CSV::Row "Name":"foo" "Value":0>
+ # row[0] = 'bar'
+ # row['Value'] = 1
+ # row # => #<CSV::Row "Name":"bar" "Value":1>
#
class Row
- #
- # Constructs a new CSV::Row from +headers+ and +fields+, which are expected
- # to be Arrays. If one Array is shorter than the other, it will be padded
- # with +nil+ objects.
- #
- # The optional +header_row+ parameter can be set to +true+ to indicate, via
- # CSV::Row.header_row?() and CSV::Row.field_row?(), that this is a header
- # row. Otherwise, the row assumes to be a field row.
- #
- # A CSV::Row object supports the following Array methods through delegation:
- #
- # * empty?()
- # * length()
- # * size()
- #
+ # :call-seq:
+ # CSV::Row.new(headers, fields, header_row = false) -> csv_row
+ #
+ # Returns the new \CSV::Row instance constructed from
+ # arguments +headers+ and +fields+; both should be Arrays;
+ # note that the fields need not be Strings:
+ # row = CSV::Row.new(['Name', 'Value'], ['foo', 0])
+ # row # => #<CSV::Row "Name":"foo" "Value":0>
+ #
+ # If the \Array lengths are different, the shorter is +nil+-filled:
+ # row = CSV::Row.new(['Name', 'Value', 'Date', 'Size'], ['foo', 0])
+ # row # => #<CSV::Row "Name":"foo" "Value":0 "Date":nil "Size":nil>
+ #
+ # Each \CSV::Row object is either a <i>field row</i> or a <i>header row</i>;
+ # by default, a new row is a field row; for the row created above:
+ # row.field_row? # => true
+ # row.header_row? # => false
+ #
+ # If the optional argument +header_row+ is given as +true+,
+ # the created row is a header row:
+ # row = CSV::Row.new(['Name', 'Value'], ['foo', 0], header_row = true)
+ # row # => #<CSV::Row "Name":"foo" "Value":0>
+ # row.field_row? # => false
+ # row.header_row? # => true
def initialize(headers, fields, header_row = false)
@header_row = header_row
headers.each { |h| h.freeze if h.is_a? String }
@@ -48,6 +123,10 @@ class CSV
extend Forwardable
def_delegators :@row, :empty?, :length, :size
+ # :call-seq:
+ # row.initialize_copy(other_row) -> self
+ #
+ # Calls superclass method.
def initialize_copy(other)
super_return_value = super
@row = @row.collect(&:dup)
@@ -71,7 +150,7 @@ class CSV
end
# :call-seq:
- # row.headers
+ # row.headers -> array_of_headers
#
# Returns the headers for this row:
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
@@ -83,9 +162,9 @@ class CSV
end
# :call-seq:
- # field(index)
- # field(header)
- # field(header, offset)
+ # field(index) -> value
+ # field(header) -> value
+ # field(header, offset) -> value
#
# Returns the field value for the given +index+ or +header+.
#
@@ -137,9 +216,9 @@ class CSV
#
# :call-seq:
- # fetch(header)
- # fetch(header, default)
- # fetch(header) {|row| ... }
+ # fetch(header) -> value
+ # fetch(header, default) -> value
+ # fetch(header) {|row| ... } -> value
#
# Returns the field value as specified by +header+.
#
@@ -193,7 +272,7 @@ class CSV
end
# :call-seq:
- # row.has_key?(header)
+ # row.has_key?(header) -> true or false
#
# Returns +true+ if there is a field with the given +header+,
# +false+ otherwise.
@@ -320,7 +399,7 @@ class CSV
end
# :call-seq:
- # row.push(*values) ->self
+ # row.push(*values) -> self
#
# Appends each of the given +values+ to +self+ as a field; returns +self+:
# source = "Name,Name,Name\nFoo,Bar,Baz\n"
@@ -403,7 +482,7 @@ class CSV
end
# :call-seq:
- # self.fields(*specifiers)
+ # self.fields(*specifiers) -> array_of_fields
#
# Returns field values per the given +specifiers+, which may be any mixture of:
# - \Integer index.
@@ -471,15 +550,26 @@ class CSV
end
alias_method :values_at, :fields
- #
# :call-seq:
- # index( header )
- # index( header, offset )
+ # index(header) -> index
+ # index(header, offset) -> index
#
- # This method will return the index of a field with the provided +header+.
- # The +offset+ can be used to locate duplicate header names, as described in
- # CSV::Row.field().
+ # Returns the index for the given header, if it exists;
+ # otherwise returns +nil+.
#
+ # With the single argument +header+, returns the index
+ # of the first-found field with the given +header+:
+ # source = "Name,Name,Name\nFoo,Bar,Baz\n"
+ # table = CSV.parse(source, headers: true)
+ # row = table[0]
+ # row.index('Name') # => 0
+ # row.index('NAME') # => nil
+ #
+ # With arguments +header+ and +offset+,
+ # returns the index of the first-found field with given +header+,
+ # but ignoring the first +offset+ fields:
+ # row.index('Name', 1) # => 1
+ # row.index('Name', 3) # => nil
def index(header, minimum_index = 0)
# find the pair
index = headers[minimum_index..-1].index(header)
@@ -487,24 +577,36 @@ class CSV
index.nil? ? nil : index + minimum_index
end
+ # :call-seq:
+ # row.field?(value) -> true or false
#
- # Returns +true+ if +data+ matches a field in this row, and +false+
- # otherwise.
- #
+ # Returns +true+ if +value+ is a field in this row, +false+ otherwise:
+ # source = "Name,Name,Name\nFoo,Bar,Baz\n"
+ # table = CSV.parse(source, headers: true)
+ # row = table[0]
+ # row.field?('Bar') # => true
+ # row.field?('BAR') # => false
def field?(data)
fields.include? data
end
include Enumerable
+ # :call-seq:
+ # row.each {|header, value| ... } -> self
#
- # Yields each pair of the row as header and field tuples (much like
- # iterating over a Hash). This method returns the row for chaining.
- #
- # If no block is given, an Enumerator is returned.
- #
- # Support for Enumerable.
+ # Calls the block with each header-value pair; returns +self+:
+ # source = "Name,Name,Name\nFoo,Bar,Baz\n"
+ # table = CSV.parse(source, headers: true)
+ # row = table[0]
+ # row.each {|header, value| p [header, value] }
+ # Output:
+ # ["Name", "Foo"]
+ # ["Name", "Bar"]
+ # ["Name", "Baz"]
#
+ # If no block is given, returns a new Enumerator:
+ # row.each # => #<Enumerator: #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":"Baz">:each>
def each(&block)
return enum_for(__method__) { size } unless block_given?
@@ -515,10 +617,19 @@ class CSV
alias_method :each_pair, :each
+ # :call-seq:
+ # row == other -> true or false
#
- # Returns +true+ if this row contains the same headers and fields in the
- # same order as +other+.
- #
+ # Returns +true+ if +other+ is a /CSV::Row that has the same
+ # fields (headers and values) in the same order as +self+;
+ # otherwise returns +false+:
+ # source = "Name,Name,Name\nFoo,Bar,Baz\n"
+ # table = CSV.parse(source, headers: true)
+ # row = table[0]
+ # other_row = table[0]
+ # row == other_row # => true
+ # other_row = table[1]
+ # row == other_row # => false
def ==(other)
return @row == other.row if other.is_a? CSV::Row
@row == other
@@ -548,9 +659,31 @@ class CSV
end
alias_method :to_hash, :to_h
+ # :call-seq:
+ # row.deconstruct_keys(keys) -> hash
+ #
+ # Returns the new \Hash suitable for pattern matching containing only the
+ # keys specified as an argument.
+ def deconstruct_keys(keys)
+ if keys.nil?
+ to_h
+ else
+ keys.to_h { |key| [key, self[key]] }
+ end
+ end
+
alias_method :to_ary, :to_a
# :call-seq:
+ # row.deconstruct -> array
+ #
+ # Returns the new \Array suitable for pattern matching containing the values
+ # of the row.
+ def deconstruct
+ fields
+ end
+
+ # :call-seq:
# row.to_csv -> csv_string
#
# Returns the row as a \CSV String. Headers are not included:
@@ -570,7 +703,7 @@ class CSV
# by +index_or_header+ and +specifiers+.
#
# The nested objects may be instances of various classes.
- # See {Dig Methods}[https://docs.ruby-lang.org/en/master/doc/dig_methods_rdoc.html].
+ # See {Dig Methods}[rdoc-ref:dig_methods.rdoc].
#
# Examples:
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
diff --git a/lib/csv/table.rb b/lib/csv/table.rb
index 1ce0dd6daf..fb19f5453f 100644
--- a/lib/csv/table.rb
+++ b/lib/csv/table.rb
@@ -3,31 +3,199 @@
require "forwardable"
class CSV
+ # = \CSV::Table
+ # A \CSV::Table instance represents \CSV data.
+ # (see {class CSV}[../CSV.html]).
#
- # A CSV::Table is a two-dimensional data structure for representing CSV
- # documents. Tables allow you to work with the data by row or column,
- # manipulate the data, and even convert the results back to CSV, if needed.
+ # The instance may have:
+ # - Rows: each is a Table::Row object.
+ # - Headers: names for the columns.
#
- # All tables returned by CSV will be constructed from this class, if header
- # row processing is activated.
+ # === Instance Methods
#
+ # \CSV::Table has three groups of instance methods:
+ # - Its own internally defined instance methods.
+ # - Methods included by module Enumerable.
+ # - Methods delegated to class Array.:
+ # * Array#empty?
+ # * Array#length
+ # * Array#size
+ #
+ # == Creating a \CSV::Table Instance
+ #
+ # Commonly, a new \CSV::Table instance is created by parsing \CSV source
+ # using headers:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.class # => CSV::Table
+ #
+ # You can also create an instance directly. See ::new.
+ #
+ # == Headers
+ #
+ # If a table has headers, the headers serve as labels for the columns of data.
+ # Each header serves as the label for its column.
+ #
+ # The headers for a \CSV::Table object are stored as an \Array of Strings.
+ #
+ # Commonly, headers are defined in the first row of \CSV source:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.headers # => ["Name", "Value"]
+ #
+ # If no headers are defined, the \Array is empty:
+ # table = CSV::Table.new([])
+ # table.headers # => []
+ #
+ # == Access Modes
+ #
+ # \CSV::Table provides three modes for accessing table data:
+ # - \Row mode.
+ # - Column mode.
+ # - Mixed mode (the default for a new table).
+ #
+ # The access mode for a\CSV::Table instance affects the behavior
+ # of some of its instance methods:
+ # - #[]
+ # - #[]=
+ # - #delete
+ # - #delete_if
+ # - #each
+ # - #values_at
+ #
+ # === \Row Mode
+ #
+ # Set a table to row mode with method #by_row!:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
+ #
+ # Specify a single row by an \Integer index:
+ # # Get a row.
+ # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
+ # # Set a row, then get it.
+ # table[1] = CSV::Row.new(['Name', 'Value'], ['bam', 3])
+ # table[1] # => #<CSV::Row "Name":"bam" "Value":3>
+ #
+ # Specify a sequence of rows by a \Range:
+ # # Get rows.
+ # table[1..2] # => [#<CSV::Row "Name":"bam" "Value":3>, #<CSV::Row "Name":"baz" "Value":"2">]
+ # # Set rows, then get them.
+ # table[1..2] = [
+ # CSV::Row.new(['Name', 'Value'], ['bat', 4]),
+ # CSV::Row.new(['Name', 'Value'], ['bad', 5]),
+ # ]
+ # table[1..2] # => [["Name", #<CSV::Row "Name":"bat" "Value":4>], ["Value", #<CSV::Row "Name":"bad" "Value":5>]]
+ #
+ # === Column Mode
+ #
+ # Set a table to column mode with method #by_col!:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
+ #
+ # Specify a column by an \Integer index:
+ # # Get a column.
+ # table[0]
+ # # Set a column, then get it.
+ # table[0] = ['FOO', 'BAR', 'BAZ']
+ # table[0] # => ["FOO", "BAR", "BAZ"]
+ #
+ # Specify a column by its \String header:
+ # # Get a column.
+ # table['Name'] # => ["FOO", "BAR", "BAZ"]
+ # # Set a column, then get it.
+ # table['Name'] = ['Foo', 'Bar', 'Baz']
+ # table['Name'] # => ["Foo", "Bar", "Baz"]
+ #
+ # === Mixed Mode
+ #
+ # In mixed mode, you can refer to either rows or columns:
+ # - An \Integer index refers to a row.
+ # - A \Range index refers to multiple rows.
+ # - A \String index refers to a column.
+ #
+ # Set a table to mixed mode with method #by_col_or_row!:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
+ #
+ # Specify a single row by an \Integer index:
+ # # Get a row.
+ # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
+ # # Set a row, then get it.
+ # table[1] = CSV::Row.new(['Name', 'Value'], ['bam', 3])
+ # table[1] # => #<CSV::Row "Name":"bam" "Value":3>
+ #
+ # Specify a sequence of rows by a \Range:
+ # # Get rows.
+ # table[1..2] # => [#<CSV::Row "Name":"bam" "Value":3>, #<CSV::Row "Name":"baz" "Value":"2">]
+ # # Set rows, then get them.
+ # table[1] = CSV::Row.new(['Name', 'Value'], ['bat', 4])
+ # table[2] = CSV::Row.new(['Name', 'Value'], ['bad', 5])
+ # table[1..2] # => [["Name", #<CSV::Row "Name":"bat" "Value":4>], ["Value", #<CSV::Row "Name":"bad" "Value":5>]]
+ #
+ # Specify a column by its \String header:
+ # # Get a column.
+ # table['Name'] # => ["foo", "bat", "bad"]
+ # # Set a column, then get it.
+ # table['Name'] = ['Foo', 'Bar', 'Baz']
+ # table['Name'] # => ["Foo", "Bar", "Baz"]
class Table
+ # :call-seq:
+ # CSV::Table.new(array_of_rows, headers = nil) -> csv_table
+ #
+ # Returns a new \CSV::Table object.
+ #
+ # - Argument +array_of_rows+ must be an \Array of CSV::Row objects.
+ # - Argument +headers+, if given, may be an \Array of Strings.
+ #
+ # ---
+ #
+ # Create an empty \CSV::Table object:
+ # table = CSV::Table.new([])
+ # table # => #<CSV::Table mode:col_or_row row_count:1>
+ #
+ # Create a non-empty \CSV::Table object:
+ # rows = [
+ # CSV::Row.new([], []),
+ # CSV::Row.new([], []),
+ # CSV::Row.new([], []),
+ # ]
+ # table = CSV::Table.new(rows)
+ # table # => #<CSV::Table mode:col_or_row row_count:4>
+ #
+ # ---
#
- # Constructs a new CSV::Table from +array_of_rows+, which are expected
- # to be CSV::Row objects. All rows are assumed to have the same headers.
+ # If argument +headers+ is an \Array of Strings,
+ # those Strings become the table's headers:
+ # table = CSV::Table.new([], headers: ['Name', 'Age'])
+ # table.headers # => ["Name", "Age"]
#
- # The optional +headers+ parameter can be set to Array of headers.
- # If headers aren't set, headers are fetched from CSV::Row objects.
- # Otherwise, headers() method will return headers being set in
- # headers argument.
+ # If argument +headers+ is not given and the table has rows,
+ # the headers are taken from the first row:
+ # rows = [
+ # CSV::Row.new(['Foo', 'Bar'], []),
+ # CSV::Row.new(['foo', 'bar'], []),
+ # CSV::Row.new(['FOO', 'BAR'], []),
+ # ]
+ # table = CSV::Table.new(rows)
+ # table.headers # => ["Foo", "Bar"]
#
- # A CSV::Table object supports the following Array methods through
- # delegation:
+ # If argument +headers+ is not given and the table is empty (has no rows),
+ # the headers are also empty:
+ # table = CSV::Table.new([])
+ # table.headers # => []
#
- # * empty?()
- # * length()
- # * size()
+ # ---
#
+ # Raises an exception if argument +array_of_rows+ is not an \Array object:
+ # # Raises NoMethodError (undefined method `first' for :foo:Symbol):
+ # CSV::Table.new(:foo)
+ #
+ # Raises an exception if an element of +array_of_rows+ is not a \CSV::Table object:
+ # # Raises NoMethodError (undefined method `headers' for :foo:Symbol):
+ # CSV::Table.new([:foo])
def initialize(array_of_rows, headers: nil)
@table = array_of_rows
@headers = headers
@@ -54,88 +222,141 @@ class CSV
extend Forwardable
def_delegators :@table, :empty?, :length, :size
+ # :call-seq:
+ # table.by_col -> table_dup
#
- # Returns a duplicate table object, in column mode. This is handy for
- # chaining in a single call without changing the table mode, but be aware
- # that this method can consume a fair amount of memory for bigger data sets.
+ # Returns a duplicate of +self+, in column mode
+ # (see {Column Mode}[#class-CSV::Table-label-Column+Mode]):
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.mode # => :col_or_row
+ # dup_table = table.by_col
+ # dup_table.mode # => :col
+ # dup_table.equal?(table) # => false # It's a dup
#
- # This method returns the duplicate table for chaining. Don't chain
- # destructive methods (like []=()) this way though, since you are working
- # with a duplicate.
+ # This may be used to chain method calls without changing the mode
+ # (but also will affect performance and memory usage):
+ # dup_table.by_col['Name']
#
+ # Also note that changes to the duplicate table will not affect the original.
def by_col
self.class.new(@table.dup).by_col!
end
+ # :call-seq:
+ # table.by_col! -> self
#
- # Switches the mode of this table to column mode. All calls to indexing and
- # iteration methods will work with columns until the mode is changed again.
- #
- # This method returns the table and is safe to chain.
- #
+ # Sets the mode for +self+ to column mode
+ # (see {Column Mode}[#class-CSV::Table-label-Column+Mode]); returns +self+:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.mode # => :col_or_row
+ # table1 = table.by_col!
+ # table.mode # => :col
+ # table1.equal?(table) # => true # Returned self
def by_col!
@mode = :col
self
end
+ # :call-seq:
+ # table.by_col_or_row -> table_dup
#
- # Returns a duplicate table object, in mixed mode. This is handy for
- # chaining in a single call without changing the table mode, but be aware
- # that this method can consume a fair amount of memory for bigger data sets.
+ # Returns a duplicate of +self+, in mixed mode
+ # (see {Mixed Mode}[#class-CSV::Table-label-Mixed+Mode]):
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true).by_col!
+ # table.mode # => :col
+ # dup_table = table.by_col_or_row
+ # dup_table.mode # => :col_or_row
+ # dup_table.equal?(table) # => false # It's a dup
#
- # This method returns the duplicate table for chaining. Don't chain
- # destructive methods (like []=()) this way though, since you are working
- # with a duplicate.
+ # This may be used to chain method calls without changing the mode
+ # (but also will affect performance and memory usage):
+ # dup_table.by_col_or_row['Name']
#
+ # Also note that changes to the duplicate table will not affect the original.
def by_col_or_row
self.class.new(@table.dup).by_col_or_row!
end
+ # :call-seq:
+ # table.by_col_or_row! -> self
#
- # Switches the mode of this table to mixed mode. All calls to indexing and
- # iteration methods will use the default intelligent indexing system until
- # the mode is changed again. In mixed mode an index is assumed to be a row
- # reference while anything else is assumed to be column access by headers.
- #
- # This method returns the table and is safe to chain.
- #
+ # Sets the mode for +self+ to mixed mode
+ # (see {Mixed Mode}[#class-CSV::Table-label-Mixed+Mode]); returns +self+:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true).by_col!
+ # table.mode # => :col
+ # table1 = table.by_col_or_row!
+ # table.mode # => :col_or_row
+ # table1.equal?(table) # => true # Returned self
def by_col_or_row!
@mode = :col_or_row
self
end
+ # :call-seq:
+ # table.by_row -> table_dup
#
- # Returns a duplicate table object, in row mode. This is handy for chaining
- # in a single call without changing the table mode, but be aware that this
- # method can consume a fair amount of memory for bigger data sets.
+ # Returns a duplicate of +self+, in row mode
+ # (see {Row Mode}[#class-CSV::Table-label-Row+Mode]):
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.mode # => :col_or_row
+ # dup_table = table.by_row
+ # dup_table.mode # => :row
+ # dup_table.equal?(table) # => false # It's a dup
#
- # This method returns the duplicate table for chaining. Don't chain
- # destructive methods (like []=()) this way though, since you are working
- # with a duplicate.
+ # This may be used to chain method calls without changing the mode
+ # (but also will affect performance and memory usage):
+ # dup_table.by_row[1]
#
+ # Also note that changes to the duplicate table will not affect the original.
def by_row
self.class.new(@table.dup).by_row!
end
+ # :call-seq:
+ # table.by_row! -> self
#
- # Switches the mode of this table to row mode. All calls to indexing and
- # iteration methods will work with rows until the mode is changed again.
- #
- # This method returns the table and is safe to chain.
- #
+ # Sets the mode for +self+ to row mode
+ # (see {Row Mode}[#class-CSV::Table-label-Row+Mode]); returns +self+:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.mode # => :col_or_row
+ # table1 = table.by_row!
+ # table.mode # => :row
+ # table1.equal?(table) # => true # Returned self
def by_row!
@mode = :row
self
end
+ # :call-seq:
+ # table.headers -> array_of_headers
#
- # Returns the headers for the first row of this table (assumed to match all
- # other rows). The headers Array passed to CSV::Table.new is returned for
- # empty tables.
+ # Returns a new \Array containing the \String headers for the table.
#
+ # If the table is not empty, returns the headers from the first row:
+ # rows = [
+ # CSV::Row.new(['Foo', 'Bar'], []),
+ # CSV::Row.new(['FOO', 'BAR'], []),
+ # CSV::Row.new(['foo', 'bar'], []),
+ # ]
+ # table = CSV::Table.new(rows)
+ # table.headers # => ["Foo", "Bar"]
+ # table.delete(0)
+ # table.headers # => ["FOO", "BAR"]
+ # table.delete(0)
+ # table.headers # => ["foo", "bar"]
+ #
+ # If the table is empty, returns a copy of the headers in the table itself:
+ # table.delete(0)
+ # table.headers # => ["Foo", "Bar"]
def headers
if @table.empty?
@headers.dup
@@ -145,17 +366,21 @@ class CSV
end
# :call-seq:
- # table[n] -> row
- # table[range] -> array_of_rows
- # table[header] -> array_of_fields
+ # table[n] -> row or column_data
+ # table[range] -> array_of_rows or array_of_column_data
+ # table[header] -> array_of_column_data
#
# Returns data from the table; does not modify the table.
#
# ---
#
- # The expression <tt>table[n]</tt>, where +n+ is a non-negative \Integer,
- # returns the +n+th row of the table, if that row exists,
- # and if the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>:
+ # Fetch a \Row by Its \Integer Index::
+ # - Form: <tt>table[n]</tt>, +n+ an integer.
+ # - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
+ # - Return value: _nth_ row of the table, if that row exists;
+ # otherwise +nil+.
+ #
+ # Returns the _nth_ row of the table if that row exists:
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
# table = CSV.parse(source, headers: true)
# table.by_row! # => #<CSV::Table mode:row row_count:4>
@@ -168,20 +393,45 @@ class CSV
#
# Returns +nil+ if +n+ is too large or too small:
# table[4] # => nil
- # table[-4] => nil
+ # table[-4] # => nil
#
# Raises an exception if the access mode is <tt>:row</tt>
- # and +n+ is not an
- # {Integer-convertible object}[https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html#label-Integer-Convertible+Objects].
+ # and +n+ is not an \Integer:
# table.by_row! # => #<CSV::Table mode:row row_count:4>
# # Raises TypeError (no implicit conversion of String into Integer):
# table['Name']
#
# ---
#
- # The expression <tt>table[range]</tt>, where +range+ is a Range object,
- # returns rows from the table, beginning at row <tt>range.first</tt>,
- # if those rows exist, and if the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>:
+ # Fetch a Column by Its \Integer Index::
+ # - Form: <tt>table[n]</tt>, +n+ an \Integer.
+ # - Access mode: <tt>:col</tt>.
+ # - Return value: _nth_ column of the table, if that column exists;
+ # otherwise an \Array of +nil+ fields of length <tt>self.size</tt>.
+ #
+ # Returns the _nth_ column of the table if that column exists:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
+ # table[1] # => ["0", "1", "2"]
+ #
+ # Counts backward from the last column if +n+ is negative:
+ # table[-2] # => ["foo", "bar", "baz"]
+ #
+ # Returns an \Array of +nil+ fields if +n+ is too large or too small:
+ # table[4] # => [nil, nil, nil]
+ # table[-4] # => [nil, nil, nil]
+ #
+ # ---
+ #
+ # Fetch Rows by \Range::
+ # - Form: <tt>table[range]</tt>, +range+ a \Range object.
+ # - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
+ # - Return value: rows from the table, beginning at row <tt>range.start</tt>,
+ # if those rows exists.
+ #
+ # Returns rows from the table, beginning at row <tt>range.first</tt>,
+ # if those rows exist:
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
# table = CSV.parse(source, headers: true)
# table.by_row! # => #<CSV::Table mode:row row_count:4>
@@ -191,11 +441,11 @@ class CSV
# rows = table[1..2] # => #<CSV::Row "Name":"bar" "Value":"1">
# rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
#
- # If there are too few rows, returns all from <tt>range.first</tt> to the end:
+ # If there are too few rows, returns all from <tt>range.start</tt> to the end:
# rows = table[1..50] # => #<CSV::Row "Name":"bar" "Value":"1">
# rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
#
- # Special case: if <tt>range.start == table.size</tt>, returns an empty \Array:
+ # Special case: if <tt>range.start == table.size</tt>, returns an empty \Array:
# table[table.size..50] # => []
#
# If <tt>range.end</tt> is negative, calculates the ending index from the end:
@@ -211,9 +461,41 @@ class CSV
#
# ---
#
- # The expression <tt>table[header]</tt>, where +header+ is a \String,
- # returns column values (\Array of \Strings) if the column exists
- # and if the access mode is <tt>:col</tt> or <tt>:col_or_row</tt>:
+ # Fetch Columns by \Range::
+ # - Form: <tt>table[range]</tt>, +range+ a \Range object.
+ # - Access mode: <tt>:col</tt>.
+ # - Return value: column data from the table, beginning at column <tt>range.start</tt>,
+ # if those columns exist.
+ #
+ # Returns column values from the table, if the column exists;
+ # the values are arranged by row:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.by_col!
+ # table[0..1] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
+ #
+ # Special case: if <tt>range.start == headers.size</tt>,
+ # returns an \Array (size: <tt>table.size</tt>) of empty \Arrays:
+ # table[table.headers.size..50] # => [[], [], []]
+ #
+ # If <tt>range.end</tt> is negative, calculates the ending index from the end:
+ # table[0..-1] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
+ #
+ # If <tt>range.start</tt> is negative, calculates the starting index from the end:
+ # table[-2..2] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
+ #
+ # If <tt>range.start</tt> is larger than <tt>table.size</tt>,
+ # returns an \Array of +nil+ values:
+ # table[4..4] # => [nil, nil, nil]
+ #
+ # ---
+ #
+ # Fetch a Column by Its \String Header::
+ # - Form: <tt>table[header]</tt>, +header+ a \String header.
+ # - Access mode: <tt>:col</tt> or <tt>:col_or_row</tt>
+ # - Return value: column data from the table, if that +header+ exists.
+ #
+ # Returns column values from the table, if the column exists:
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
# table = CSV.parse(source, headers: true)
# table.by_col! # => #<CSV::Table mode:col row_count:4>
@@ -238,22 +520,132 @@ class CSV
end
end
+ # :call-seq:
+ # table[n] = row -> row
+ # table[n] = field_or_array_of_fields -> field_or_array_of_fields
+ # table[header] = field_or_array_of_fields -> field_or_array_of_fields
#
- # In the default mixed mode, this method assigns rows for index access and
- # columns for header access. You can force the index association by first
- # calling by_col!() or by_row!().
+ # Puts data onto the table.
#
- # Rows may be set to an Array of values (which will inherit the table's
- # headers()) or a CSV::Row.
+ # ---
+ #
+ # Set a \Row by Its \Integer Index::
+ # - Form: <tt>table[n] = row</tt>, +n+ an \Integer,
+ # +row+ a \CSV::Row instance or an \Array of fields.
+ # - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
+ # - Return value: +row+.
+ #
+ # If the row exists, it is replaced:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # new_row = CSV::Row.new(['Name', 'Value'], ['bat', 3])
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
+ # return_value = table[0] = new_row
+ # return_value.equal?(new_row) # => true # Returned the row
+ # table[0].to_h # => {"Name"=>"bat", "Value"=>3}
+ #
+ # With access mode <tt>:col_or_row</tt>:
+ # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
+ # table[0] = CSV::Row.new(['Name', 'Value'], ['bam', 4])
+ # table[0].to_h # => {"Name"=>"bam", "Value"=>4}
+ #
+ # With an \Array instead of a \CSV::Row, inherits headers from the table:
+ # array = ['bad', 5]
+ # return_value = table[0] = array
+ # return_value.equal?(array) # => true # Returned the array
+ # table[0].to_h # => {"Name"=>"bad", "Value"=>5}
#
- # Columns may be set to a single value, which is copied to each row of the
- # column, or an Array of values. Arrays of values are assigned to rows top
- # to bottom in row major order. Excess values are ignored and if the Array
- # does not have a value for each row the extra rows will receive a +nil+.
+ # If the row does not exist, extends the table by adding rows:
+ # assigns rows with +nil+ as needed:
+ # table.size # => 3
+ # table[5] = ['bag', 6]
+ # table.size # => 6
+ # table[3] # => nil
+ # table[4]# => nil
+ # table[5].to_h # => {"Name"=>"bag", "Value"=>6}
+ #
+ # Note that the +nil+ rows are actually +nil+, not a row of +nil+ fields.
#
- # Assigning to an existing column or row clobbers the data. Assigning to
- # new columns creates them at the right end of the table.
+ # ---
#
+ # Set a Column by Its \Integer Index::
+ # - Form: <tt>table[n] = array_of_fields</tt>, +n+ an \Integer,
+ # +array_of_fields+ an \Array of \String fields.
+ # - Access mode: <tt>:col</tt>.
+ # - Return value: +array_of_fields+.
+ #
+ # If the column exists, it is replaced:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # new_col = [3, 4, 5]
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
+ # return_value = table[1] = new_col
+ # return_value.equal?(new_col) # => true # Returned the column
+ # table[1] # => [3, 4, 5]
+ # # The rows, as revised:
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
+ # table[0].to_h # => {"Name"=>"foo", "Value"=>3}
+ # table[1].to_h # => {"Name"=>"bar", "Value"=>4}
+ # table[2].to_h # => {"Name"=>"baz", "Value"=>5}
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
+ #
+ # If there are too few values, fills with +nil+ values:
+ # table[1] = [0]
+ # table[1] # => [0, nil, nil]
+ #
+ # If there are too many values, ignores the extra values:
+ # table[1] = [0, 1, 2, 3, 4]
+ # table[1] # => [0, 1, 2]
+ #
+ # If a single value is given, replaces all fields in the column with that value:
+ # table[1] = 'bat'
+ # table[1] # => ["bat", "bat", "bat"]
+ #
+ # ---
+ #
+ # Set a Column by Its \String Header::
+ # - Form: <tt>table[header] = field_or_array_of_fields</tt>,
+ # +header+ a \String header, +field_or_array_of_fields+ a field value
+ # or an \Array of \String fields.
+ # - Access mode: <tt>:col</tt> or <tt>:col_or_row</tt>.
+ # - Return value: +field_or_array_of_fields+.
+ #
+ # If the column exists, it is replaced:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # new_col = [3, 4, 5]
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
+ # return_value = table['Value'] = new_col
+ # return_value.equal?(new_col) # => true # Returned the column
+ # table['Value'] # => [3, 4, 5]
+ # # The rows, as revised:
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
+ # table[0].to_h # => {"Name"=>"foo", "Value"=>3}
+ # table[1].to_h # => {"Name"=>"bar", "Value"=>4}
+ # table[2].to_h # => {"Name"=>"baz", "Value"=>5}
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
+ #
+ # If there are too few values, fills with +nil+ values:
+ # table['Value'] = [0]
+ # table['Value'] # => [0, nil, nil]
+ #
+ # If there are too many values, ignores the extra values:
+ # table['Value'] = [0, 1, 2, 3, 4]
+ # table['Value'] # => [0, 1, 2]
+ #
+ # If the column does not exist, extends the table by adding columns:
+ # table['Note'] = ['x', 'y', 'z']
+ # table['Note'] # => ["x", "y", "z"]
+ # # The rows, as revised:
+ # table.by_row!
+ # table[0].to_h # => {"Name"=>"foo", "Value"=>0, "Note"=>"x"}
+ # table[1].to_h # => {"Name"=>"bar", "Value"=>1, "Note"=>"y"}
+ # table[2].to_h # => {"Name"=>"baz", "Value"=>2, "Note"=>"z"}
+ # table.by_col!
+ #
+ # If a single value is given, replaces all fields in the column with that value:
+ # table['Value'] = 'bat'
+ # table['Value'] # => ["bat", "bat", "bat"]
def []=(index_or_header, value)
if @mode == :row or # by index
(@mode == :col_or_row and index_or_header.is_a? Integer)
@@ -463,6 +855,9 @@ class CSV
end
end
+ # :call-seq:
+ # table.delete_if {|row_or_column| ... } -> self
+ #
# Removes rows or columns for which the block returns a truthy value;
# returns +self+.
#
@@ -495,9 +890,8 @@ class CSV
if @mode == :row or @mode == :col_or_row # by index
@table.delete_if(&block)
else # by header
- deleted = []
headers.each do |header|
- deleted << delete(header) if yield([header, self[header]])
+ delete(header) if yield([header, self[header]])
end
end
@@ -506,6 +900,9 @@ class CSV
include Enumerable
+ # :call-seq:
+ # table.each {|row_or_column| ... ) -> self
+ #
# Calls the block with each row or column; returns +self+.
#
# When the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>,
@@ -534,7 +931,9 @@ class CSV
return enum_for(__method__) { @mode == :col ? headers.size : size } unless block_given?
if @mode == :col
- headers.each { |header| yield([header, self[header]]) }
+ headers.each.with_index do |header, i|
+ yield([header, @table.map {|row| row[header, i]}])
+ end
else
@table.each(&block)
end
@@ -542,6 +941,9 @@ class CSV
self # for chaining
end
+ # :call-seq:
+ # table == other_table -> true or false
+ #
# Returns +true+ if all each row of +self+ <tt>==</tt>
# the corresponding row of +other_table+, otherwise, +false+.
#
@@ -565,10 +967,14 @@ class CSV
@table == other
end
+ # :call-seq:
+ # table.to_a -> array_of_arrays
#
- # Returns the table as an Array of Arrays. Headers will be the first row,
- # then all of the field rows will follow.
- #
+ # Returns the table as an \Array of \Arrays;
+ # the headers are in the first row:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.to_a # => [["Name", "Value"], ["foo", "0"], ["bar", "1"], ["baz", "2"]]
def to_a
array = [headers]
@table.each do |row|
@@ -578,16 +984,29 @@ class CSV
array
end
+ # :call-seq:
+ # table.to_csv(**options) -> csv_string
#
- # Returns the table as a complete CSV String. Headers will be listed first,
- # then all of the field rows.
+ # Returns the table as \CSV string.
+ # See {Options for Generating}[../CSV.html#class-CSV-label-Options+for+Generating].
#
- # This method assumes you want the Table.headers(), unless you explicitly
- # pass <tt>:write_headers => false</tt>.
+ # Defaults option +write_headers+ to +true+:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.to_csv # => "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
#
- def to_csv(write_headers: true, **options)
+ # Omits the headers if option +write_headers+ is given as +false+
+ # (see {Option +write_headers+}[../CSV.html#class-CSV-label-Option+write_headers]):
+ # table.to_csv(write_headers: false) # => "foo,0\nbar,1\nbaz,2\n"
+ #
+ # Limit rows if option +limit+ is given like +2+:
+ # table.to_csv(limit: 2) # => "Name,Value\nfoo,0\nbar,1\n"
+ def to_csv(write_headers: true, limit: nil, **options)
array = write_headers ? [headers.to_csv(**options)] : []
- @table.each do |row|
+ limit ||= @table.size
+ limit = @table.size + 1 + limit if limit < 0
+ limit = 0 if limit < 0
+ @table.first(limit).each do |row|
array.push(row.fields.to_csv(**options)) unless row.header_row?
end
@@ -613,9 +1032,24 @@ class CSV
end
end
- # Shows the mode and size of this table in a US-ASCII String.
+ # :call-seq:
+ # table.inspect => string
+ #
+ # Returns a <tt>US-ASCII</tt>-encoded \String showing table:
+ # - Class: <tt>CSV::Table</tt>.
+ # - Access mode: <tt>:row</tt>, <tt>:col</tt>, or <tt>:col_or_row</tt>.
+ # - Size: Row count, including the header row.
+ #
+ # Example:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.inspect # => "#<CSV::Table mode:col_or_row row_count:4>\nName,Value\nfoo,0\nbar,1\nbaz,2\n"
+ #
def inspect
- "#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>".encode("US-ASCII")
+ inspected = +"#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>"
+ summary = to_csv(limit: 5)
+ inspected << "\n" << summary if summary.encoding.ascii_compatible?
+ inspected
end
end
end
diff --git a/lib/csv/version.rb b/lib/csv/version.rb
index d1d0dc0e02..e05d63d801 100644
--- a/lib/csv/version.rb
+++ b/lib/csv/version.rb
@@ -2,5 +2,5 @@
class CSV
# The version of the installed library.
- VERSION = "3.2.2"
+ VERSION = "3.2.6"
end
diff --git a/lib/csv/writer.rb b/lib/csv/writer.rb
index 4a9a35c5af..030a295bc9 100644
--- a/lib/csv/writer.rb
+++ b/lib/csv/writer.rb
@@ -1,11 +1,8 @@
# frozen_string_literal: true
require_relative "input_record_separator"
-require_relative "match_p"
require_relative "row"
-using CSV::MatchP if CSV.const_defined?(:MatchP)
-
class CSV
# Note: Don't use this class directly. This is an internal class.
class Writer
@@ -42,7 +39,10 @@ class CSV
@headers ||= row if @use_headers
@lineno += 1
- row = @fields_converter.convert(row, nil, lineno) if @fields_converter
+ if @fields_converter
+ quoted_fields = [false] * row.size
+ row = @fields_converter.convert(row, nil, lineno, quoted_fields)
+ end
i = -1
converted_row = row.collect do |field|
@@ -97,7 +97,7 @@ class CSV
return unless @headers
converter = @options[:header_fields_converter]
- @headers = converter.convert(@headers, nil, 0)
+ @headers = converter.convert(@headers, nil, 0, [])
@headers.each do |header|
header.freeze if header.is_a?(String)
end
diff --git a/lib/delegate.rb b/lib/delegate.rb
index 70d4e4ad1d..387a5f063d 100644
--- a/lib/delegate.rb
+++ b/lib/delegate.rb
@@ -39,7 +39,7 @@
# Be advised, RDoc will not detect delegated methods.
#
class Delegator < BasicObject
- VERSION = "0.2.0"
+ VERSION = "0.3.0"
kernel = ::Kernel.dup
kernel.class_eval do
diff --git a/lib/did_you_mean/spell_checkers/method_name_checker.rb b/lib/did_you_mean/spell_checkers/method_name_checker.rb
index d8ebaa4616..b5cbbb5da6 100644
--- a/lib/did_you_mean/spell_checkers/method_name_checker.rb
+++ b/lib/did_you_mean/spell_checkers/method_name_checker.rb
@@ -59,6 +59,13 @@ module DidYouMean
method_names = receiver.methods + receiver.singleton_methods
method_names += receiver.private_methods if @private_call
method_names.uniq!
+ # Assume that people trying to use a writer are not interested in a reader
+ # and vice versa
+ if method_name.match?(/=\Z/)
+ method_names.select! { |name| name.match?(/=\Z/) }
+ else
+ method_names.reject! { |name| name.match?(/=\Z/) }
+ end
method_names
else
[]
diff --git a/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb b/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
index 36d00349c6..9a6e04fe64 100644
--- a/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
+++ b/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
@@ -79,7 +79,7 @@ module DidYouMean
def corrections
@corrections ||= SpellChecker
.new(dictionary: (RB_RESERVED_WORDS + lvar_names + method_names + ivar_names + cvar_names))
- .correct(name) - NAMES_TO_EXCLUDE[@name]
+ .correct(name).uniq - NAMES_TO_EXCLUDE[@name]
end
end
end
diff --git a/lib/did_you_mean/version.rb b/lib/did_you_mean/version.rb
index b5fe50b5ed..5745ca1efd 100644
--- a/lib/did_you_mean/version.rb
+++ b/lib/did_you_mean/version.rb
@@ -1,3 +1,3 @@
module DidYouMean
- VERSION = "1.6.1".freeze
+ VERSION = "1.6.3".freeze
end
diff --git a/lib/drb/version.rb b/lib/drb/version.rb
index efaccf0319..10d33445b6 100644
--- a/lib/drb/version.rb
+++ b/lib/drb/version.rb
@@ -1,3 +1,3 @@
module DRb
- VERSION = "2.1.0"
+ VERSION = "2.1.1"
end
diff --git a/lib/erb.gemspec b/lib/erb.gemspec
index 43ffc89c69..d973cc10de 100644
--- a/lib/erb.gemspec
+++ b/lib/erb.gemspec
@@ -8,8 +8,8 @@ end
Gem::Specification.new do |spec|
spec.name = 'erb'
spec.version = ERB.const_get(:VERSION, false)
- spec.authors = ['Masatoshi SEKI']
- spec.email = ['seki@ruby-lang.org']
+ spec.authors = ['Masatoshi SEKI', 'Takashi Kokubun']
+ spec.email = ['seki@ruby-lang.org', 'takashikkbn@gmail.com']
spec.summary = %q{An easy to use but powerful templating system for Ruby.}
spec.description = %q{An easy to use but powerful templating system for Ruby.}
@@ -27,5 +27,12 @@ Gem::Specification.new do |spec|
spec.executables = ['erb']
spec.require_paths = ['lib']
- spec.add_dependency 'cgi'
+ if RUBY_ENGINE == 'jruby'
+ spec.platform = 'java'
+ else
+ spec.required_ruby_version = '>= 2.7.0'
+ spec.extensions = ['ext/erb/escape/extconf.rb']
+ end
+
+ spec.add_dependency 'cgi', '>= 0.3.3'
end
diff --git a/lib/erb.rb b/lib/erb.rb
index 0e42425a60..754419f819 100644
--- a/lib/erb.rb
+++ b/lib/erb.rb
@@ -14,6 +14,9 @@
require 'cgi/util'
require 'erb/version'
+require 'erb/compiler'
+require 'erb/def_method'
+require 'erb/util'
#
# = ERB -- Ruby Templating
@@ -264,486 +267,7 @@ class ERB
def self.version
VERSION
end
-end
-#--
-# ERB::Compiler
-class ERB
- # = ERB::Compiler
- #
- # Compiles ERB templates into Ruby code; the compiled code produces the
- # template result when evaluated. ERB::Compiler provides hooks to define how
- # generated output is handled.
- #
- # Internally ERB does something like this to generate the code returned by
- # ERB#src:
- #
- # compiler = ERB::Compiler.new('<>')
- # compiler.pre_cmd = ["_erbout=+''"]
- # compiler.put_cmd = "_erbout.<<"
- # compiler.insert_cmd = "_erbout.<<"
- # compiler.post_cmd = ["_erbout"]
- #
- # code, enc = compiler.compile("Got <%= obj %>!\n")
- # puts code
- #
- # <i>Generates</i>:
- #
- # #coding:UTF-8
- # _erbout=+''; _erbout.<< "Got ".freeze; _erbout.<<(( obj ).to_s); _erbout.<< "!\n".freeze; _erbout
- #
- # By default the output is sent to the print method. For example:
- #
- # compiler = ERB::Compiler.new('<>')
- # code, enc = compiler.compile("Got <%= obj %>!\n")
- # puts code
- #
- # <i>Generates</i>:
- #
- # #coding:UTF-8
- # print "Got ".freeze; print(( obj ).to_s); print "!\n".freeze
- #
- # == Evaluation
- #
- # The compiled code can be used in any context where the names in the code
- # correctly resolve. Using the last example, each of these print 'Got It!'
- #
- # Evaluate using a variable:
- #
- # obj = 'It'
- # eval code
- #
- # Evaluate using an input:
- #
- # mod = Module.new
- # mod.module_eval %{
- # def get(obj)
- # #{code}
- # end
- # }
- # extend mod
- # get('It')
- #
- # Evaluate using an accessor:
- #
- # klass = Class.new Object
- # klass.class_eval %{
- # attr_accessor :obj
- # def initialize(obj)
- # @obj = obj
- # end
- # def get_it
- # #{code}
- # end
- # }
- # klass.new('It').get_it
- #
- # Good! See also ERB#def_method, ERB#def_module, and ERB#def_class.
- class Compiler # :nodoc:
- class PercentLine # :nodoc:
- def initialize(str)
- @value = str
- end
- attr_reader :value
- alias :to_s :value
- end
-
- class Scanner # :nodoc:
- @scanner_map = {}
- class << self
- def register_scanner(klass, trim_mode, percent)
- @scanner_map[[trim_mode, percent]] = klass
- end
- alias :regist_scanner :register_scanner
- end
-
- def self.default_scanner=(klass)
- @default_scanner = klass
- end
-
- def self.make_scanner(src, trim_mode, percent)
- klass = @scanner_map.fetch([trim_mode, percent], @default_scanner)
- klass.new(src, trim_mode, percent)
- end
-
- DEFAULT_STAGS = %w(<%% <%= <%# <%).freeze
- DEFAULT_ETAGS = %w(%%> %>).freeze
- def initialize(src, trim_mode, percent)
- @src = src
- @stag = nil
- @stags = DEFAULT_STAGS
- @etags = DEFAULT_ETAGS
- end
- attr_accessor :stag
- attr_reader :stags, :etags
-
- def scan; end
- end
-
- class TrimScanner < Scanner # :nodoc:
- def initialize(src, trim_mode, percent)
- super
- @trim_mode = trim_mode
- @percent = percent
- if @trim_mode == '>'
- @scan_reg = /(.*?)(%>\r?\n|#{(stags + etags).join('|')}|\n|\z)/m
- @scan_line = self.method(:trim_line1)
- elsif @trim_mode == '<>'
- @scan_reg = /(.*?)(%>\r?\n|#{(stags + etags).join('|')}|\n|\z)/m
- @scan_line = self.method(:trim_line2)
- elsif @trim_mode == '-'
- @scan_reg = /(.*?)(^[ \t]*<%\-|<%\-|-%>\r?\n|-%>|#{(stags + etags).join('|')}|\z)/m
- @scan_line = self.method(:explicit_trim_line)
- else
- @scan_reg = /(.*?)(#{(stags + etags).join('|')}|\n|\z)/m
- @scan_line = self.method(:scan_line)
- end
- end
-
- def scan(&block)
- @stag = nil
- if @percent
- @src.each_line do |line|
- percent_line(line, &block)
- end
- else
- @scan_line.call(@src, &block)
- end
- nil
- end
-
- def percent_line(line, &block)
- if @stag || line[0] != ?%
- return @scan_line.call(line, &block)
- end
-
- line[0] = ''
- if line[0] == ?%
- @scan_line.call(line, &block)
- else
- yield(PercentLine.new(line.chomp))
- end
- end
-
- def scan_line(line)
- line.scan(@scan_reg) do |tokens|
- tokens.each do |token|
- next if token.empty?
- yield(token)
- end
- end
- end
-
- def trim_line1(line)
- line.scan(@scan_reg) do |tokens|
- tokens.each do |token|
- next if token.empty?
- if token == "%>\n" || token == "%>\r\n"
- yield('%>')
- yield(:cr)
- else
- yield(token)
- end
- end
- end
- end
-
- def trim_line2(line)
- head = nil
- line.scan(@scan_reg) do |tokens|
- tokens.each do |token|
- next if token.empty?
- head = token unless head
- if token == "%>\n" || token == "%>\r\n"
- yield('%>')
- if is_erb_stag?(head)
- yield(:cr)
- else
- yield("\n")
- end
- head = nil
- else
- yield(token)
- head = nil if token == "\n"
- end
- end
- end
- end
-
- def explicit_trim_line(line)
- line.scan(@scan_reg) do |tokens|
- tokens.each do |token|
- next if token.empty?
- if @stag.nil? && /[ \t]*<%-/ =~ token
- yield('<%')
- elsif @stag && (token == "-%>\n" || token == "-%>\r\n")
- yield('%>')
- yield(:cr)
- elsif @stag && token == '-%>'
- yield('%>')
- else
- yield(token)
- end
- end
- end
- end
-
- ERB_STAG = %w(<%= <%# <%)
- def is_erb_stag?(s)
- ERB_STAG.member?(s)
- end
- end
-
- Scanner.default_scanner = TrimScanner
-
- begin
- require 'strscan'
- rescue LoadError
- else
- class SimpleScanner < Scanner # :nodoc:
- def scan
- stag_reg = (stags == DEFAULT_STAGS) ? /(.*?)(<%[%=#]?|\z)/m : /(.*?)(#{stags.join('|')}|\z)/m
- etag_reg = (etags == DEFAULT_ETAGS) ? /(.*?)(%%?>|\z)/m : /(.*?)(#{etags.join('|')}|\z)/m
- scanner = StringScanner.new(@src)
- while ! scanner.eos?
- scanner.scan(@stag ? etag_reg : stag_reg)
- yield(scanner[1])
- yield(scanner[2])
- end
- end
- end
- Scanner.register_scanner(SimpleScanner, nil, false)
-
- class ExplicitScanner < Scanner # :nodoc:
- def scan
- stag_reg = /(.*?)(^[ \t]*<%-|<%-|#{stags.join('|')}|\z)/m
- etag_reg = /(.*?)(-%>|#{etags.join('|')}|\z)/m
- scanner = StringScanner.new(@src)
- while ! scanner.eos?
- scanner.scan(@stag ? etag_reg : stag_reg)
- yield(scanner[1])
-
- elem = scanner[2]
- if /[ \t]*<%-/ =~ elem
- yield('<%')
- elsif elem == '-%>'
- yield('%>')
- yield(:cr) if scanner.scan(/(\r?\n|\z)/)
- else
- yield(elem)
- end
- end
- end
- end
- Scanner.register_scanner(ExplicitScanner, '-', false)
- end
-
- class Buffer # :nodoc:
- def initialize(compiler, enc=nil, frozen=nil)
- @compiler = compiler
- @line = []
- @script = +''
- @script << "#coding:#{enc}\n" if enc
- @script << "#frozen-string-literal:#{frozen}\n" unless frozen.nil?
- @compiler.pre_cmd.each do |x|
- push(x)
- end
- end
- attr_reader :script
-
- def push(cmd)
- @line << cmd
- end
-
- def cr
- @script << (@line.join('; '))
- @line = []
- @script << "\n"
- end
-
- def close
- return unless @line
- @compiler.post_cmd.each do |x|
- push(x)
- end
- @script << (@line.join('; '))
- @line = nil
- end
- end
-
- def add_put_cmd(out, content)
- out.push("#{@put_cmd} #{content.dump}.freeze#{"\n" * content.count("\n")}")
- end
-
- def add_insert_cmd(out, content)
- out.push("#{@insert_cmd}((#{content}).to_s)")
- end
-
- # Compiles an ERB template into Ruby code. Returns an array of the code
- # and encoding like ["code", Encoding].
- def compile(s)
- enc = s.encoding
- raise ArgumentError, "#{enc} is not ASCII compatible" if enc.dummy?
- s = s.b # see String#b
- magic_comment = detect_magic_comment(s, enc)
- out = Buffer.new(self, *magic_comment)
-
- self.content = +''
- scanner = make_scanner(s)
- scanner.scan do |token|
- next if token.nil?
- next if token == ''
- if scanner.stag.nil?
- compile_stag(token, out, scanner)
- else
- compile_etag(token, out, scanner)
- end
- end
- add_put_cmd(out, content) if content.size > 0
- out.close
- return out.script, *magic_comment
- end
-
- def compile_stag(stag, out, scanner)
- case stag
- when PercentLine
- add_put_cmd(out, content) if content.size > 0
- self.content = +''
- out.push(stag.to_s)
- out.cr
- when :cr
- out.cr
- when '<%', '<%=', '<%#'
- scanner.stag = stag
- add_put_cmd(out, content) if content.size > 0
- self.content = +''
- when "\n"
- content << "\n"
- add_put_cmd(out, content)
- self.content = +''
- when '<%%'
- content << '<%'
- else
- content << stag
- end
- end
-
- def compile_etag(etag, out, scanner)
- case etag
- when '%>'
- compile_content(scanner.stag, out)
- scanner.stag = nil
- self.content = +''
- when '%%>'
- content << '%>'
- else
- content << etag
- end
- end
-
- def compile_content(stag, out)
- case stag
- when '<%'
- if content[-1] == ?\n
- content.chop!
- out.push(content)
- out.cr
- else
- out.push(content)
- end
- when '<%='
- add_insert_cmd(out, content)
- when '<%#'
- # commented out
- end
- end
-
- def prepare_trim_mode(mode) # :nodoc:
- case mode
- when 1
- return [false, '>']
- when 2
- return [false, '<>']
- when 0, nil
- return [false, nil]
- when String
- unless mode.match?(/\A(%|-|>|<>){1,2}\z/)
- warn_invalid_trim_mode(mode, uplevel: 5)
- end
-
- perc = mode.include?('%')
- if mode.include?('-')
- return [perc, '-']
- elsif mode.include?('<>')
- return [perc, '<>']
- elsif mode.include?('>')
- return [perc, '>']
- else
- [perc, nil]
- end
- else
- warn_invalid_trim_mode(mode, uplevel: 5)
- return [false, nil]
- end
- end
-
- def make_scanner(src) # :nodoc:
- Scanner.make_scanner(src, @trim_mode, @percent)
- end
-
- # Construct a new compiler using the trim_mode. See ERB::new for available
- # trim modes.
- def initialize(trim_mode)
- @percent, @trim_mode = prepare_trim_mode(trim_mode)
- @put_cmd = 'print'
- @insert_cmd = @put_cmd
- @pre_cmd = []
- @post_cmd = []
- end
- attr_reader :percent, :trim_mode
-
- # The command to handle text that ends with a newline
- attr_accessor :put_cmd
-
- # The command to handle text that is inserted prior to a newline
- attr_accessor :insert_cmd
-
- # An array of commands prepended to compiled code
- attr_accessor :pre_cmd
-
- # An array of commands appended to compiled code
- attr_accessor :post_cmd
-
- private
-
- # A buffered text in #compile
- attr_accessor :content
-
- def detect_magic_comment(s, enc = nil)
- re = @percent ? /\G(?:<%#(.*)%>|%#(.*)\n)/ : /\G<%#(.*)%>/
- frozen = nil
- s.scan(re) do
- comment = $+
- comment = $1 if comment[/-\*-\s*([^\s].*?)\s*-\*-$/]
- case comment
- when %r"coding\s*[=:]\s*([[:alnum:]\-_]+)"
- enc = Encoding.find($1.sub(/-(?:mac|dos|unix)/i, ''))
- when %r"frozen[-_]string[-_]literal\s*:\s*([[:alnum:]]+)"
- frozen = $1
- end
- end
- return enc, frozen
- end
-
- def warn_invalid_trim_mode(mode, uplevel:)
- warn "Invalid ERB trim mode: #{mode.inspect} (trim_mode: nil, 0, 1, 2, or String composed of '%' and/or '-', '>', '<>')", uplevel: uplevel + 1
- end
- end
-end
-
-#--
-# ERB
-class ERB
#
# Constructs a new ERB object with the template specified in _str_.
#
@@ -980,100 +504,3 @@ class ERB
cls
end
end
-
-#--
-# ERB::Util
-class ERB
- # A utility module for conversion routines, often handy in HTML generation.
- module Util
- public
- #
- # A utility method for escaping HTML tag characters in _s_.
- #
- # require "erb"
- # include ERB::Util
- #
- # puts html_escape("is a > 0 & a < 10?")
- #
- # _Generates_
- #
- # is a &gt; 0 &amp; a &lt; 10?
- #
- def html_escape(s)
- CGI.escapeHTML(s.to_s)
- end
- alias h html_escape
- module_function :h
- module_function :html_escape
-
- #
- # A utility method for encoding the String _s_ as a URL.
- #
- # require "erb"
- # include ERB::Util
- #
- # puts url_encode("Programming Ruby: The Pragmatic Programmer's Guide")
- #
- # _Generates_
- #
- # Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide
- #
- def url_encode(s)
- s.to_s.b.gsub(/[^a-zA-Z0-9_\-.~]/n) { |m|
- sprintf("%%%02X", m.unpack1("C"))
- }
- end
- alias u url_encode
- module_function :u
- module_function :url_encode
- end
-end
-
-#--
-# ERB::DefMethod
-class ERB
- # Utility module to define eRuby script as instance method.
- #
- # === Example
- #
- # example.rhtml:
- # <% for item in @items %>
- # <b><%= item %></b>
- # <% end %>
- #
- # example.rb:
- # require 'erb'
- # class MyClass
- # extend ERB::DefMethod
- # def_erb_method('render()', 'example.rhtml')
- # def initialize(items)
- # @items = items
- # end
- # end
- # print MyClass.new([10,20,30]).render()
- #
- # result:
- #
- # <b>10</b>
- #
- # <b>20</b>
- #
- # <b>30</b>
- #
- module DefMethod
- public
- # define _methodname_ as instance method of current module, using ERB
- # object or eRuby file
- def def_erb_method(methodname, erb_or_fname)
- if erb_or_fname.kind_of? String
- fname = erb_or_fname
- erb = ERB.new(File.read(fname))
- erb.def_method(self, methodname, fname)
- else
- erb = erb_or_fname
- erb.def_method(self, methodname, erb.filename || '(ERB)')
- end
- end
- module_function :def_erb_method
- end
-end
diff --git a/lib/erb/compiler.rb b/lib/erb/compiler.rb
new file mode 100644
index 0000000000..547d2c4c44
--- /dev/null
+++ b/lib/erb/compiler.rb
@@ -0,0 +1,471 @@
+#--
+# ERB::Compiler
+#
+# Compiles ERB templates into Ruby code; the compiled code produces the
+# template result when evaluated. ERB::Compiler provides hooks to define how
+# generated output is handled.
+#
+# Internally ERB does something like this to generate the code returned by
+# ERB#src:
+#
+# compiler = ERB::Compiler.new('<>')
+# compiler.pre_cmd = ["_erbout=+''"]
+# compiler.put_cmd = "_erbout.<<"
+# compiler.insert_cmd = "_erbout.<<"
+# compiler.post_cmd = ["_erbout"]
+#
+# code, enc = compiler.compile("Got <%= obj %>!\n")
+# puts code
+#
+# <i>Generates</i>:
+#
+# #coding:UTF-8
+# _erbout=+''; _erbout.<< "Got ".freeze; _erbout.<<(( obj ).to_s); _erbout.<< "!\n".freeze; _erbout
+#
+# By default the output is sent to the print method. For example:
+#
+# compiler = ERB::Compiler.new('<>')
+# code, enc = compiler.compile("Got <%= obj %>!\n")
+# puts code
+#
+# <i>Generates</i>:
+#
+# #coding:UTF-8
+# print "Got ".freeze; print(( obj ).to_s); print "!\n".freeze
+#
+# == Evaluation
+#
+# The compiled code can be used in any context where the names in the code
+# correctly resolve. Using the last example, each of these print 'Got It!'
+#
+# Evaluate using a variable:
+#
+# obj = 'It'
+# eval code
+#
+# Evaluate using an input:
+#
+# mod = Module.new
+# mod.module_eval %{
+# def get(obj)
+# #{code}
+# end
+# }
+# extend mod
+# get('It')
+#
+# Evaluate using an accessor:
+#
+# klass = Class.new Object
+# klass.class_eval %{
+# attr_accessor :obj
+# def initialize(obj)
+# @obj = obj
+# end
+# def get_it
+# #{code}
+# end
+# }
+# klass.new('It').get_it
+#
+# Good! See also ERB#def_method, ERB#def_module, and ERB#def_class.
+class ERB::Compiler # :nodoc:
+ class PercentLine # :nodoc:
+ def initialize(str)
+ @value = str
+ end
+ attr_reader :value
+ alias :to_s :value
+ end
+
+ class Scanner # :nodoc:
+ @scanner_map = {}
+ class << self
+ def register_scanner(klass, trim_mode, percent)
+ @scanner_map[[trim_mode, percent]] = klass
+ end
+ alias :regist_scanner :register_scanner
+ end
+
+ def self.default_scanner=(klass)
+ @default_scanner = klass
+ end
+
+ def self.make_scanner(src, trim_mode, percent)
+ klass = @scanner_map.fetch([trim_mode, percent], @default_scanner)
+ klass.new(src, trim_mode, percent)
+ end
+
+ DEFAULT_STAGS = %w(<%% <%= <%# <%).freeze
+ DEFAULT_ETAGS = %w(%%> %>).freeze
+ def initialize(src, trim_mode, percent)
+ @src = src
+ @stag = nil
+ @stags = DEFAULT_STAGS
+ @etags = DEFAULT_ETAGS
+ end
+ attr_accessor :stag
+ attr_reader :stags, :etags
+
+ def scan; end
+ end
+
+ class TrimScanner < Scanner # :nodoc:
+ def initialize(src, trim_mode, percent)
+ super
+ @trim_mode = trim_mode
+ @percent = percent
+ if @trim_mode == '>'
+ @scan_reg = /(.*?)(%>\r?\n|#{(stags + etags).join('|')}|\n|\z)/m
+ @scan_line = self.method(:trim_line1)
+ elsif @trim_mode == '<>'
+ @scan_reg = /(.*?)(%>\r?\n|#{(stags + etags).join('|')}|\n|\z)/m
+ @scan_line = self.method(:trim_line2)
+ elsif @trim_mode == '-'
+ @scan_reg = /(.*?)(^[ \t]*<%\-|<%\-|-%>\r?\n|-%>|#{(stags + etags).join('|')}|\z)/m
+ @scan_line = self.method(:explicit_trim_line)
+ else
+ @scan_reg = /(.*?)(#{(stags + etags).join('|')}|\n|\z)/m
+ @scan_line = self.method(:scan_line)
+ end
+ end
+
+ def scan(&block)
+ @stag = nil
+ if @percent
+ @src.each_line do |line|
+ percent_line(line, &block)
+ end
+ else
+ @scan_line.call(@src, &block)
+ end
+ nil
+ end
+
+ def percent_line(line, &block)
+ if @stag || line[0] != ?%
+ return @scan_line.call(line, &block)
+ end
+
+ line[0] = ''
+ if line[0] == ?%
+ @scan_line.call(line, &block)
+ else
+ yield(PercentLine.new(line.chomp))
+ end
+ end
+
+ def scan_line(line)
+ line.scan(@scan_reg) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ yield(token)
+ end
+ end
+ end
+
+ def trim_line1(line)
+ line.scan(@scan_reg) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ if token == "%>\n" || token == "%>\r\n"
+ yield('%>')
+ yield(:cr)
+ else
+ yield(token)
+ end
+ end
+ end
+ end
+
+ def trim_line2(line)
+ head = nil
+ line.scan(@scan_reg) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ head = token unless head
+ if token == "%>\n" || token == "%>\r\n"
+ yield('%>')
+ if is_erb_stag?(head)
+ yield(:cr)
+ else
+ yield("\n")
+ end
+ head = nil
+ else
+ yield(token)
+ head = nil if token == "\n"
+ end
+ end
+ end
+ end
+
+ def explicit_trim_line(line)
+ line.scan(@scan_reg) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ if @stag.nil? && /[ \t]*<%-/ =~ token
+ yield('<%')
+ elsif @stag && (token == "-%>\n" || token == "-%>\r\n")
+ yield('%>')
+ yield(:cr)
+ elsif @stag && token == '-%>'
+ yield('%>')
+ else
+ yield(token)
+ end
+ end
+ end
+ end
+
+ ERB_STAG = %w(<%= <%# <%)
+ def is_erb_stag?(s)
+ ERB_STAG.member?(s)
+ end
+ end
+
+ Scanner.default_scanner = TrimScanner
+
+ begin
+ require 'strscan'
+ rescue LoadError
+ else
+ class SimpleScanner < Scanner # :nodoc:
+ def scan
+ stag_reg = (stags == DEFAULT_STAGS) ? /(.*?)(<%[%=#]?|\z)/m : /(.*?)(#{stags.join('|')}|\z)/m
+ etag_reg = (etags == DEFAULT_ETAGS) ? /(.*?)(%%?>|\z)/m : /(.*?)(#{etags.join('|')}|\z)/m
+ scanner = StringScanner.new(@src)
+ while ! scanner.eos?
+ scanner.scan(@stag ? etag_reg : stag_reg)
+ yield(scanner[1])
+ yield(scanner[2])
+ end
+ end
+ end
+ Scanner.register_scanner(SimpleScanner, nil, false)
+
+ class ExplicitScanner < Scanner # :nodoc:
+ def scan
+ stag_reg = /(.*?)(^[ \t]*<%-|<%-|#{stags.join('|')}|\z)/m
+ etag_reg = /(.*?)(-%>|#{etags.join('|')}|\z)/m
+ scanner = StringScanner.new(@src)
+ while ! scanner.eos?
+ scanner.scan(@stag ? etag_reg : stag_reg)
+ yield(scanner[1])
+
+ elem = scanner[2]
+ if /[ \t]*<%-/ =~ elem
+ yield('<%')
+ elsif elem == '-%>'
+ yield('%>')
+ yield(:cr) if scanner.scan(/(\r?\n|\z)/)
+ else
+ yield(elem)
+ end
+ end
+ end
+ end
+ Scanner.register_scanner(ExplicitScanner, '-', false)
+ end
+
+ class Buffer # :nodoc:
+ def initialize(compiler, enc=nil, frozen=nil)
+ @compiler = compiler
+ @line = []
+ @script = +''
+ @script << "#coding:#{enc}\n" if enc
+ @script << "#frozen-string-literal:#{frozen}\n" unless frozen.nil?
+ @compiler.pre_cmd.each do |x|
+ push(x)
+ end
+ end
+ attr_reader :script
+
+ def push(cmd)
+ @line << cmd
+ end
+
+ def cr
+ @script << (@line.join('; '))
+ @line = []
+ @script << "\n"
+ end
+
+ def close
+ return unless @line
+ @compiler.post_cmd.each do |x|
+ push(x)
+ end
+ @script << (@line.join('; '))
+ @line = nil
+ end
+ end
+
+ def add_put_cmd(out, content)
+ out.push("#{@put_cmd} #{content.dump}.freeze#{"\n" * content.count("\n")}")
+ end
+
+ def add_insert_cmd(out, content)
+ out.push("#{@insert_cmd}((#{content}).to_s)")
+ end
+
+ # Compiles an ERB template into Ruby code. Returns an array of the code
+ # and encoding like ["code", Encoding].
+ def compile(s)
+ enc = s.encoding
+ raise ArgumentError, "#{enc} is not ASCII compatible" if enc.dummy?
+ s = s.b # see String#b
+ magic_comment = detect_magic_comment(s, enc)
+ out = Buffer.new(self, *magic_comment)
+
+ self.content = +''
+ scanner = make_scanner(s)
+ scanner.scan do |token|
+ next if token.nil?
+ next if token == ''
+ if scanner.stag.nil?
+ compile_stag(token, out, scanner)
+ else
+ compile_etag(token, out, scanner)
+ end
+ end
+ add_put_cmd(out, content) if content.size > 0
+ out.close
+ return out.script, *magic_comment
+ end
+
+ def compile_stag(stag, out, scanner)
+ case stag
+ when PercentLine
+ add_put_cmd(out, content) if content.size > 0
+ self.content = +''
+ out.push(stag.to_s)
+ out.cr
+ when :cr
+ out.cr
+ when '<%', '<%=', '<%#'
+ scanner.stag = stag
+ add_put_cmd(out, content) if content.size > 0
+ self.content = +''
+ when "\n"
+ content << "\n"
+ add_put_cmd(out, content)
+ self.content = +''
+ when '<%%'
+ content << '<%'
+ else
+ content << stag
+ end
+ end
+
+ def compile_etag(etag, out, scanner)
+ case etag
+ when '%>'
+ compile_content(scanner.stag, out)
+ scanner.stag = nil
+ self.content = +''
+ when '%%>'
+ content << '%>'
+ else
+ content << etag
+ end
+ end
+
+ def compile_content(stag, out)
+ case stag
+ when '<%'
+ if content[-1] == ?\n
+ content.chop!
+ out.push(content)
+ out.cr
+ else
+ out.push(content)
+ end
+ when '<%='
+ add_insert_cmd(out, content)
+ when '<%#'
+ out.push("\n" * content.count("\n")) # only adjust lineno
+ end
+ end
+
+ def prepare_trim_mode(mode) # :nodoc:
+ case mode
+ when 1
+ return [false, '>']
+ when 2
+ return [false, '<>']
+ when 0, nil
+ return [false, nil]
+ when String
+ unless mode.match?(/\A(%|-|>|<>){1,2}\z/)
+ warn_invalid_trim_mode(mode, uplevel: 5)
+ end
+
+ perc = mode.include?('%')
+ if mode.include?('-')
+ return [perc, '-']
+ elsif mode.include?('<>')
+ return [perc, '<>']
+ elsif mode.include?('>')
+ return [perc, '>']
+ else
+ [perc, nil]
+ end
+ else
+ warn_invalid_trim_mode(mode, uplevel: 5)
+ return [false, nil]
+ end
+ end
+
+ def make_scanner(src) # :nodoc:
+ Scanner.make_scanner(src, @trim_mode, @percent)
+ end
+
+ # Construct a new compiler using the trim_mode. See ERB::new for available
+ # trim modes.
+ def initialize(trim_mode)
+ @percent, @trim_mode = prepare_trim_mode(trim_mode)
+ @put_cmd = 'print'
+ @insert_cmd = @put_cmd
+ @pre_cmd = []
+ @post_cmd = []
+ end
+ attr_reader :percent, :trim_mode
+
+ # The command to handle text that ends with a newline
+ attr_accessor :put_cmd
+
+ # The command to handle text that is inserted prior to a newline
+ attr_accessor :insert_cmd
+
+ # An array of commands prepended to compiled code
+ attr_accessor :pre_cmd
+
+ # An array of commands appended to compiled code
+ attr_accessor :post_cmd
+
+ private
+
+ # A buffered text in #compile
+ attr_accessor :content
+
+ def detect_magic_comment(s, enc = nil)
+ re = @percent ? /\G(?:<%#(.*)%>|%#(.*)\n)/ : /\G<%#(.*)%>/
+ frozen = nil
+ s.scan(re) do
+ comment = $+
+ comment = $1 if comment[/-\*-\s*([^\s].*?)\s*-\*-$/]
+ case comment
+ when %r"coding\s*[=:]\s*([[:alnum:]\-_]+)"
+ enc = Encoding.find($1.sub(/-(?:mac|dos|unix)/i, ''))
+ when %r"frozen[-_]string[-_]literal\s*:\s*([[:alnum:]]+)"
+ frozen = $1
+ end
+ end
+ return enc, frozen
+ end
+
+ def warn_invalid_trim_mode(mode, uplevel:)
+ warn "Invalid ERB trim mode: #{mode.inspect} (trim_mode: nil, 0, 1, 2, or String composed of '%' and/or '-', '>', '<>')", uplevel: uplevel + 1
+ end
+end
diff --git a/lib/erb/def_method.rb b/lib/erb/def_method.rb
new file mode 100644
index 0000000000..17f9c0f9fa
--- /dev/null
+++ b/lib/erb/def_method.rb
@@ -0,0 +1,46 @@
+#--
+# ERB::DefMethod
+#
+# Utility module to define eRuby script as instance method.
+#
+# === Example
+#
+# example.rhtml:
+# <% for item in @items %>
+# <b><%= item %></b>
+# <% end %>
+#
+# example.rb:
+# require 'erb'
+# class MyClass
+# extend ERB::DefMethod
+# def_erb_method('render()', 'example.rhtml')
+# def initialize(items)
+# @items = items
+# end
+# end
+# print MyClass.new([10,20,30]).render()
+#
+# result:
+#
+# <b>10</b>
+#
+# <b>20</b>
+#
+# <b>30</b>
+#
+module ERB::DefMethod
+ # define _methodname_ as instance method of current module, using ERB
+ # object or eRuby file
+ def def_erb_method(methodname, erb_or_fname)
+ if erb_or_fname.kind_of? String
+ fname = erb_or_fname
+ erb = ERB.new(File.read(fname))
+ erb.def_method(self, methodname, fname)
+ else
+ erb = erb_or_fname
+ erb.def_method(self, methodname, erb.filename || '(ERB)')
+ end
+ end
+ module_function :def_erb_method
+end
diff --git a/lib/erb/util.rb b/lib/erb/util.rb
new file mode 100644
index 0000000000..0c1e7482a8
--- /dev/null
+++ b/lib/erb/util.rb
@@ -0,0 +1,62 @@
+#--
+# ERB::Escape
+#
+# A subset of ERB::Util. Unlike ERB::Util#html_escape, we expect/hope
+# Rails will not monkey-patch ERB::Escape#html_escape.
+begin
+ # We don't build the C extension for JRuby, TruffleRuby, and WASM
+ if $LOAD_PATH.resolve_feature_path('erb/escape')
+ require 'erb/escape'
+ end
+rescue LoadError # resolve_feature_path raises LoadError on TruffleRuby 22.3.0
+end
+unless defined?(ERB::Escape)
+ module ERB::Escape
+ def html_escape(s)
+ CGI.escapeHTML(s.to_s)
+ end
+ module_function :html_escape
+ end
+end
+
+#--
+# ERB::Util
+#
+# A utility module for conversion routines, often handy in HTML generation.
+module ERB::Util
+ #
+ # A utility method for escaping HTML tag characters in _s_.
+ #
+ # require "erb"
+ # include ERB::Util
+ #
+ # puts html_escape("is a > 0 & a < 10?")
+ #
+ # _Generates_
+ #
+ # is a &gt; 0 &amp; a &lt; 10?
+ #
+ include ERB::Escape # html_escape
+ module_function :html_escape
+ alias h html_escape
+ module_function :h
+
+ #
+ # A utility method for encoding the String _s_ as a URL.
+ #
+ # require "erb"
+ # include ERB::Util
+ #
+ # puts url_encode("Programming Ruby: The Pragmatic Programmer's Guide")
+ #
+ # _Generates_
+ #
+ # Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide
+ #
+ def url_encode(s)
+ CGI.escapeURIComponent(s.to_s)
+ end
+ alias u url_encode
+ module_function :u
+ module_function :url_encode
+end
diff --git a/lib/erb/version.rb b/lib/erb/version.rb
index 0aaa38258f..38e1b76ff4 100644
--- a/lib/erb/version.rb
+++ b/lib/erb/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class ERB
- VERSION = '2.2.3'
+ VERSION = '4.0.2'
private_constant :VERSION
end
diff --git a/lib/error_highlight/base.rb b/lib/error_highlight/base.rb
index 8392979e24..062871ee16 100644
--- a/lib/error_highlight/base.rb
+++ b/lib/error_highlight/base.rb
@@ -1,12 +1,17 @@
require_relative "version"
module ErrorHighlight
- # Identify the code fragment that seems associated with a given error
+ # Identify the code fragment at that a given exception occurred.
#
- # Arguments:
- # node: RubyVM::AbstractSyntaxTree::Node (script_lines should be enabled)
- # point_type: :name | :args
- # name: The name associated with the NameError/NoMethodError
+ # Options:
+ #
+ # point_type: :name | :args
+ # :name (default) points the method/variable name that the exception occurred.
+ # :args points the arguments of the method call that the exception occurred.
+ #
+ # backtrace_location: Thread::Backtrace::Location
+ # It locates the code fragment of the given backtrace_location.
+ # By default, it uses the first frame of backtrace_locations of the given exception.
#
# Returns:
# {
@@ -15,9 +20,56 @@ module ErrorHighlight
# last_lineno: Integer,
# last_column: Integer,
# snippet: String,
+ # script_lines: [String],
# } | nil
- def self.spot(...)
- Spotter.new(...).spot
+ #
+ # Limitations:
+ #
+ # Currently, ErrorHighlight.spot only supports a single-line code fragment.
+ # Therefore, if the return value is not nil, first_lineno and last_lineno will have
+ # the same value. If the relevant code fragment spans multiple lines
+ # (e.g., Array#[] of +ary[(newline)expr(newline)]+), the method will return nil.
+ # This restriction may be removed in the future.
+ def self.spot(obj, **opts)
+ case obj
+ when Exception
+ exc = obj
+ loc = opts[:backtrace_location]
+ opts = { point_type: opts.fetch(:point_type, :name) }
+
+ unless loc
+ case exc
+ when TypeError, ArgumentError
+ opts[:point_type] = :args
+ end
+
+ locs = exc.backtrace_locations
+ return nil unless locs
+
+ loc = locs.first
+ return nil unless loc
+
+ opts[:name] = exc.name if NameError === obj
+ end
+
+ return nil unless Thread::Backtrace::Location === loc
+
+ node = RubyVM::AbstractSyntaxTree.of(loc, keep_script_lines: true)
+
+ Spotter.new(node, **opts).spot
+
+ when RubyVM::AbstractSyntaxTree::Node
+ Spotter.new(obj, **opts).spot
+
+ else
+ raise TypeError, "Exception is expected"
+ end
+
+ rescue SyntaxError,
+ SystemCallError, # file not found or something
+ ArgumentError # eval'ed code
+
+ return nil
end
class Spotter
@@ -122,6 +174,7 @@ module ErrorHighlight
last_lineno: @end_lineno,
last_column: @end_column,
snippet: @snippet,
+ script_lines: @node.script_lines,
}
else
return nil
diff --git a/lib/error_highlight/core_ext.rb b/lib/error_highlight/core_ext.rb
index 53e409dd8f..b69093f74e 100644
--- a/lib/error_highlight/core_ext.rb
+++ b/lib/error_highlight/core_ext.rb
@@ -3,36 +3,9 @@ require_relative "formatter"
module ErrorHighlight
module CoreExt
private def generate_snippet
- locs = backtrace_locations
- return "" unless locs
-
- loc = locs.first
- return "" unless loc
-
- begin
- node = RubyVM::AbstractSyntaxTree.of(loc, keep_script_lines: true)
- opts = {}
-
- case self
- when NoMethodError, NameError
- opts[:point_type] = :name
- opts[:name] = name
- when TypeError, ArgumentError
- opts[:point_type] = :args
- end
-
- spot = ErrorHighlight.spot(node, **opts)
-
- rescue SyntaxError
- rescue SystemCallError # file not found or something
- rescue ArgumentError # eval'ed code
- end
-
- if spot
- return ErrorHighlight.formatter.message_for(spot)
- end
-
- ""
+ spot = ErrorHighlight.spot(self)
+ return "" unless spot
+ return ErrorHighlight.formatter.message_for(spot)
end
if Exception.method_defined?(:detailed_message)
@@ -65,8 +38,10 @@ module ErrorHighlight
NameError.prepend(CoreExt)
- # The extension for TypeError/ArgumentError is temporarily disabled due to many test failures
-
- #TypeError.prepend(CoreExt)
- #ArgumentError.prepend(CoreExt)
+ if Exception.method_defined?(:detailed_message)
+ # ErrorHighlight is enabled for TypeError and ArgumentError only when Exception#detailed_message is available.
+ # This is because changing ArgumentError#message is highly incompatible.
+ TypeError.prepend(CoreExt)
+ ArgumentError.prepend(CoreExt)
+ end
end
diff --git a/lib/error_highlight/version.rb b/lib/error_highlight/version.rb
index 49a34502cb..5afe5f06d6 100644
--- a/lib/error_highlight/version.rb
+++ b/lib/error_highlight/version.rb
@@ -1,3 +1,3 @@
module ErrorHighlight
- VERSION = "0.3.0"
+ VERSION = "0.5.1"
end
diff --git a/lib/fileutils.rb b/lib/fileutils.rb
index 003b4bdd82..b495078f93 100644
--- a/lib/fileutils.rb
+++ b/lib/fileutils.rb
@@ -12,8 +12,8 @@ end
#
# First, what’s elsewhere. \Module \FileUtils:
#
-# - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html].
-# - Supplements {class File}[https://docs.ruby-lang.org/en/master/File.html]
+# - Inherits from {class Object}[rdoc-ref:Object].
+# - Supplements {class File}[rdoc-ref:File]
# (but is not included or extended there).
#
# Here, module \FileUtils provides methods that are useful for:
@@ -36,6 +36,7 @@ end
# - ::ln, ::link: Creates hard links.
# - ::ln_s, ::symlink: Creates symbolic links.
# - ::ln_sf: Creates symbolic links, overwriting if necessary.
+# - ::ln_sr: Creates symbolic links relative to targets
#
# === Deleting
#
@@ -162,8 +163,8 @@ end
# by applying a special pre-process:
#
# - If the target path points to a directory, this method uses methods
-# {File#chown}[https://docs.ruby-lang.org/en/master/File.html#method-i-chown]
-# and {File#chmod}[https://docs.ruby-lang.org/en/master/File.html#method-i-chmod]
+# {File#chown}[rdoc-ref:File#chown]
+# and {File#chmod}[rdoc-ref:File#chmod]
# in removing directories.
# - The owner of the target directory should be either the current process
# or the super user (root).
@@ -179,7 +180,7 @@ end
# - {CVE-2004-0452}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452].
#
module FileUtils
- VERSION = "1.6.0"
+ VERSION = "1.7.0"
def self.private_module_function(name) #:nodoc:
module_function name
@@ -291,7 +292,7 @@ module FileUtils
#
# With no keyword arguments, creates a directory at each +path+ in +list+
# by calling: <tt>Dir.mkdir(path, mode)</tt>;
- # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
+ # see {Dir.mkdir}[rdoc-ref:Dir.mkdir]:
#
# FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"]
# FileUtils.mkdir('tmp4') # => ["tmp4"]
@@ -299,7 +300,7 @@ module FileUtils
# Keyword arguments:
#
# - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
- # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
+ # see {File.chmod}[rdoc-ref:File.chmod].
# - <tt>noop: true</tt> - does not create directories.
# - <tt>verbose: true</tt> - prints an equivalent command:
#
@@ -339,7 +340,7 @@ module FileUtils
# With no keyword arguments, creates a directory at each +path+ in +list+,
# along with any needed ancestor directories,
# by calling: <tt>Dir.mkdir(path, mode)</tt>;
- # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
+ # see {Dir.mkdir}[rdoc-ref:Dir.mkdir]:
#
# FileUtils.mkdir_p(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
# FileUtils.mkdir_p('tmp4/tmp5') # => ["tmp4/tmp5"]
@@ -347,7 +348,7 @@ module FileUtils
# Keyword arguments:
#
# - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
- # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
+ # see {File.chmod}[rdoc-ref:File.chmod].
# - <tt>noop: true</tt> - does not create directories.
# - <tt>verbose: true</tt> - prints an equivalent command:
#
@@ -374,7 +375,7 @@ module FileUtils
path = remove_trailing_slash(item)
stack = []
- until File.directory?(path)
+ until File.directory?(path) || File.dirname(path) == path
stack.push path
path = File.dirname(path)
end
@@ -417,7 +418,7 @@ module FileUtils
#
# With no keyword arguments, removes the directory at each +path+ in +list+,
# by calling: <tt>Dir.rmdir(path)</tt>;
- # see {Dir.rmdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-rmdir]:
+ # see {Dir.rmdir}[rdoc-ref:Dir.rmdir]:
#
# FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
# FileUtils.rmdir('tmp4/tmp5') # => ["tmp4/tmp5"]
@@ -440,6 +441,8 @@ module FileUtils
# Raises an exception if a directory does not exist
# or if for any reason a directory cannot be removed.
#
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
+ #
def rmdir(list, parents: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
@@ -688,6 +691,7 @@ module FileUtils
# Keyword arguments:
#
# - <tt>force: true</tt> - overwrites +dest+ if it exists.
+ # - <tt>relative: false</tt> - create links relative to +dest+.
# - <tt>noop: true</tt> - does not create links.
# - <tt>verbose: true</tt> - prints an equivalent command:
#
@@ -707,7 +711,10 @@ module FileUtils
#
# Related: FileUtils.ln_sf.
#
- def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
+ def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil)
+ if relative
+ return ln_sr(src, dest, force: force, noop: noop, verbose: verbose)
+ end
fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest0(src, dest) do |s,d|
@@ -727,6 +734,48 @@ module FileUtils
end
module_function :ln_sf
+ # Like FileUtils.ln_s, but create links relative to +dest+.
+ #
+ def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil)
+ options = "#{force ? 'f' : ''}#{target_directory ? '' : 'T'}"
+ dest = File.path(dest)
+ srcs = Array(src)
+ link = proc do |s, target_dir_p = true|
+ s = File.path(s)
+ if target_dir_p
+ d = File.join(destdirs = dest, File.basename(s))
+ else
+ destdirs = File.dirname(d = dest)
+ end
+ destdirs = fu_split_path(File.realpath(destdirs))
+ if fu_starting_path?(s)
+ srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s)))
+ base = fu_relative_components_from(srcdirs, destdirs)
+ s = File.join(*base)
+ else
+ srcdirs = fu_clean_components(*fu_split_path(s))
+ base = fu_relative_components_from(fu_split_path(Dir.pwd), destdirs)
+ while srcdirs.first&. == ".." and base.last&.!=("..") and !fu_starting_path?(base.last)
+ srcdirs.shift
+ base.pop
+ end
+ s = File.join(*base, *srcdirs)
+ end
+ fu_output_message "ln -s#{options} #{s} #{d}" if verbose
+ next if noop
+ remove_file d, true if force
+ File.symlink s, d
+ end
+ case srcs.size
+ when 0
+ when 1
+ link[srcs[0], target_directory && File.directory?(dest)]
+ else
+ srcs.each(&link)
+ end
+ end
+ module_function :ln_sr
+
# Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link]; returns +nil+.
#
# Arguments +src+ and +dest+
@@ -1042,7 +1091,7 @@ module FileUtils
module_function :copy_file
# Copies \IO stream +src+ to \IO stream +dest+ via
- # {IO.copy_stream}[https://docs.ruby-lang.org/en/master/IO.html#method-c-copy_stream].
+ # {IO.copy_stream}[rdoc-ref:IO.copy_stream].
#
# Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
@@ -1261,6 +1310,8 @@ module FileUtils
# rm -r src0.dat src0.txt
# rm -r src1
#
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
+ #
def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
list = fu_list(list)
fu_output_message "rm -r#{force ? 'f' : ''} #{list.join ' '}" if verbose
@@ -1290,6 +1341,8 @@ module FileUtils
#
# FileUtils.rmtree is an alias for FileUtils.rm_rf.
#
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
+ #
def rm_rf(list, noop: nil, verbose: nil, secure: nil)
rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
end
@@ -1311,6 +1364,8 @@ module FileUtils
# Optional argument +force+ specifies whether to ignore
# raised exceptions of StandardError and its descendants.
#
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
+ #
def remove_entry_secure(path, force = false)
unless fu_have_symlink?
remove_entry path, force
@@ -1431,6 +1486,8 @@ module FileUtils
# Optional argument +force+ specifies whether to ignore
# raised exceptions of StandardError and its descendants.
#
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
+ #
def remove_file(path, force = false)
Entry_.new(path).remove_file
rescue
@@ -1448,6 +1505,8 @@ module FileUtils
# Optional argument +force+ specifies whether to ignore
# raised exceptions of StandardError and its descendants.
#
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
+ #
def remove_dir(path, force = false)
remove_entry path, force # FIXME?? check if it is a directory
end
@@ -1543,14 +1602,14 @@ module FileUtils
# Keyword arguments:
#
# - <tt>group: <i>group</i></tt> - changes the group if not +nil+,
- # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
+ # using {File.chown}[rdoc-ref:File.chown].
# - <tt>mode: <i>permissions</i></tt> - changes the permissions.
- # using {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
- # - <tt>noop: true</tt> - does not remove entries; returns +nil+.
+ # using {File.chmod}[rdoc-ref:File.chmod].
+ # - <tt>noop: true</tt> - does not copy entries; returns +nil+.
# - <tt>owner: <i>owner</i></tt> - changes the owner if not +nil+,
- # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
+ # using {File.chown}[rdoc-ref:File.chown].
# - <tt>preserve: true</tt> - preserve timestamps
- # using {File.utime}[https://docs.ruby-lang.org/en/master/File.html#method-c-utime].
+ # using {File.utime}[rdoc-ref:File.utime].
# - <tt>verbose: true</tt> - prints an equivalent command:
#
# FileUtils.install('src0.txt', 'dest0.txt', noop: true, verbose: true)
@@ -1687,9 +1746,9 @@ module FileUtils
# returns +list+ if it is an array, <tt>[list]</tt> otherwise:
#
# - Modifies each entry that is a regular file using
- # {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
+ # {File.chmod}[rdoc-ref:File.chmod].
# - Modifies each entry that is a symbolic link using
- # {File.lchmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchmod].
+ # {File.lchmod}[rdoc-ref:File.lchmod].
#
# Argument +list+ or its elements
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
@@ -1789,9 +1848,9 @@ module FileUtils
# returns +list+ if it is an array, <tt>[list]</tt> otherwise:
#
# - Modifies each entry that is a regular file using
- # {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
+ # {File.chown}[rdoc-ref:File.chown].
# - Modifies each entry that is a symbolic link using
- # {File.lchown}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchown].
+ # {File.lchown}[rdoc-ref:File.lchown].
#
# Argument +list+ or its elements
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
@@ -2316,13 +2375,21 @@ module FileUtils
def postorder_traverse
if directory?
- entries().each do |ent|
+ begin
+ children = entries()
+ rescue Errno::EACCES
+ # Failed to get the list of children.
+ # Assuming there is no children, try to process the parent directory.
+ yield self
+ return
+ end
+
+ children.each do |ent|
ent.postorder_traverse do |e|
yield e
end
end
end
- ensure
yield self
end
@@ -2416,15 +2483,15 @@ module FileUtils
end
private_module_function :fu_each_src_dest
- def fu_each_src_dest0(src, dest) #:nodoc:
+ def fu_each_src_dest0(src, dest, target_directory = true) #:nodoc:
if tmp = Array.try_convert(src)
tmp.each do |s|
s = File.path(s)
- yield s, File.join(dest, File.basename(s))
+ yield s, (target_directory ? File.join(dest, File.basename(s)) : dest)
end
else
src = File.path(src)
- if File.directory?(dest)
+ if target_directory and File.directory?(dest)
yield src, File.join(dest, File.basename(src))
else
yield src, File.path(dest)
@@ -2448,6 +2515,56 @@ module FileUtils
end
private_module_function :fu_output_message
+ def fu_split_path(path)
+ path = File.path(path)
+ list = []
+ until (parent, base = File.split(path); parent == path or parent == ".")
+ list << base
+ path = parent
+ end
+ list << path
+ list.reverse!
+ end
+ private_module_function :fu_split_path
+
+ def fu_relative_components_from(target, base) #:nodoc:
+ i = 0
+ while target[i]&.== base[i]
+ i += 1
+ end
+ Array.new(base.size-i, '..').concat(target[i..-1])
+ end
+ private_module_function :fu_relative_components_from
+
+ def fu_clean_components(*comp)
+ comp.shift while comp.first == "."
+ return comp if comp.empty?
+ clean = [comp.shift]
+ path = File.join(*clean, "") # ending with File::SEPARATOR
+ while c = comp.shift
+ if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
+ clean.pop
+ path.chomp!(%r((?<=\A|/)[^/]+/\z), "")
+ else
+ clean << c
+ path << c << "/"
+ end
+ end
+ clean
+ end
+ private_module_function :fu_clean_components
+
+ if fu_windows?
+ def fu_starting_path?(path)
+ path&.start_with?(%r(\w:|/))
+ end
+ else
+ def fu_starting_path?(path)
+ path&.start_with?("/")
+ end
+ end
+ private_module_function :fu_starting_path?
+
# This hash table holds command options.
OPT_TABLE = {} #:nodoc: internal use only
(private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|
diff --git a/lib/forwardable.rb b/lib/forwardable.rb
index c9c4128f9f..71b4e6adad 100644
--- a/lib/forwardable.rb
+++ b/lib/forwardable.rb
@@ -112,8 +112,10 @@ module Forwardable
require 'forwardable/impl'
# Version of +forwardable.rb+
- VERSION = "1.3.2"
+ VERSION = "1.3.3"
+ VERSION.freeze
FORWARDABLE_VERSION = VERSION
+ FORWARDABLE_VERSION.freeze
@debug = nil
class << self
diff --git a/lib/getoptlong.rb b/lib/getoptlong.rb
index 0810ed7a57..5ae0e1497c 100644
--- a/lib/getoptlong.rb
+++ b/lib/getoptlong.rb
@@ -368,7 +368,7 @@
#
class GetoptLong
# Version.
- VERSION = "0.1.1"
+ VERSION = "0.2.0"
#
# Orderings.
diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb
index 459b8478a0..7a5cf94830 100644
--- a/lib/ipaddr.rb
+++ b/lib/ipaddr.rb
@@ -40,14 +40,14 @@ require 'socket'
# p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
class IPAddr
- VERSION = "1.2.4"
+ VERSION = "1.2.5"
# 32 bit mask for IPv4
IN4MASK = 0xffffffff
# 128 bit mask for IPv6
IN6MASK = 0xffffffffffffffffffffffffffffffff
# Format string for IPv6
- IN6FORMAT = (["%.4x"] * 8).join(':')
+ IN6FORMAT = (["%.4x"] * 8).join(':').freeze
# Regexp _internally_ used for parsing IPv4 address.
RE_IPV4ADDRLIKE = %r{
@@ -736,7 +736,7 @@ end
unless Socket.const_defined? :AF_INET6
class Socket < BasicSocket
# IPv6 protocol family
- AF_INET6 = Object.new
+ AF_INET6 = Object.new.freeze
end
class << IPSocket
diff --git a/lib/irb.rb b/lib/irb.rb
index 9a3a491641..2db99bcd43 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -53,6 +53,52 @@ require_relative "irb/easter-egg"
#
# :include: ./irb/lc/help-message
#
+# == Commands
+#
+# The following commands are available on IRB.
+#
+# * cwws
+# * Show the current workspace.
+# * cb, cws, chws
+# * Change the current workspace to an object.
+# * bindings, workspaces
+# * Show workspaces.
+# * pushb, pushws
+# * Push an object to the workspace stack.
+# * popb, popws
+# * Pop a workspace from the workspace stack.
+# * load
+# * Load a Ruby file.
+# * require
+# * Require a Ruby file.
+# * source
+# * Loads a given file in the current session.
+# * irb
+# * Start a child IRB.
+# * jobs
+# * List of current sessions.
+# * fg
+# * Switches to the session of the given number.
+# * kill
+# * Kills the session with the given number.
+# * help
+# * Enter the mode to look up RI documents.
+# * irb_info
+# * Show information about IRB.
+# * ls
+# * Show methods, constants, and variables.
+# -g [query] or -G [query] allows you to filter out the output.
+# * measure
+# * measure enables the mode to measure processing time. measure :off disables it.
+# * $, show_source
+# * Show the source code of a given method or constant.
+# * @, whereami
+# * Show the source code around binding.irb again.
+# * debug
+# * Start the debugger of debug.gem.
+# * break, delete, next, step, continue, finish, backtrace, info, catch
+# * Start the debugger of debug.gem and run the command on it.
+#
# == Configuration
#
# IRB reads a personal initialization file when it's invoked.
@@ -93,9 +139,9 @@ require_relative "irb/easter-egg"
#
# === Autocompletion
#
-# To enable autocompletion for irb, add the following to your +.irbrc+:
+# To disable autocompletion for irb, add the following to your +.irbrc+:
#
-# require 'irb/completion'
+# IRB.conf[:USE_AUTOCOMPLETE] = false
#
# === History
#
@@ -389,11 +435,7 @@ module IRB
#
# Will raise an Abort exception, or the given +exception+.
def IRB.irb_abort(irb, exception = Abort)
- if defined? Thread
- irb.context.thread.raise exception, "abort then interrupt!"
- else
- raise exception, "abort then interrupt!"
- end
+ irb.context.thread.raise exception, "abort then interrupt!"
end
class Irb
@@ -434,6 +476,17 @@ module IRB
@scanner = RubyLex.new
end
+ # A hook point for `debug` command's TracePoint after :IRB_EXIT as well as its clean-up
+ def debug_break
+ # it means the debug command is executed
+ if defined?(DEBUGGER__) && DEBUGGER__.respond_to?(:capture_frames_without_irb)
+ # after leaving this initial breakpoint, revert the capture_frames patch
+ DEBUGGER__.singleton_class.send(:alias_method, :capture_frames, :capture_frames_without_irb)
+ # and remove the redundant method
+ DEBUGGER__.singleton_class.send(:undef_method, :capture_frames_without_irb)
+ end
+ end
+
def run(conf = IRB.conf)
conf[:IRB_RC].call(context) if conf[:IRB_RC]
conf[:MAIN_CONTEXT] = context
@@ -510,13 +563,15 @@ module IRB
@scanner.set_auto_indent(@context) if @context.auto_indent_mode
- @scanner.each_top_level_statement do |line, line_no|
+ @scanner.each_top_level_statement(@context) do |line, line_no|
signal_status(:IN_EVAL) do
begin
line.untaint if RUBY_VERSION < '2.7'
if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty?
IRB.set_measure_callback
end
+ # Assignment expression check should be done before @context.evaluate to handle code like `a /2#/ if false; a = 1`
+ is_assignment = assignment_expression?(line)
if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty?
result = nil
last_proc = proc{ result = @context.evaluate(line, line_no, exception: exc) }
@@ -533,7 +588,7 @@ module IRB
@context.evaluate(line, line_no, exception: exc)
end
if @context.echo?
- if assignment_expression?(line)
+ if is_assignment
if @context.echo_on_assignment?
output_value(@context.echo_on_assignment? == :truncate)
end
@@ -596,11 +651,7 @@ module IRB
if exc.backtrace
order = nil
- if '2.5.0' == RUBY_VERSION
- # Exception#full_message doesn't have keyword arguments.
- message = exc.full_message # the same of (highlight: true, order: bottom)
- order = :bottom
- elsif '2.5.1' <= RUBY_VERSION && RUBY_VERSION < '3.0.0'
+ if RUBY_VERSION < '3.0.0'
if STDOUT.tty?
message = exc.full_message(order: :bottom)
order = :bottom
@@ -831,9 +882,12 @@ module IRB
# array of parsed expressions. The first element of each expression is the
# expression's type.
verbose, $VERBOSE = $VERBOSE, nil
- result = ASSIGNMENT_NODE_TYPES.include?(Ripper.sexp(line)&.dig(1,-1,0))
+ code = "#{RubyLex.generate_local_variables_assign_code(@context.local_variables) || 'nil;'}\n#{line}"
+ # Get the last node_type of the line. drop(1) is to ignore the local_variables_assign_code part.
+ node_type = Ripper.sexp(code)&.dig(1)&.drop(1)&.dig(-1, 0)
+ ASSIGNMENT_NODE_TYPES.include?(node_type)
+ ensure
$VERBOSE = verbose
- result
end
ATTR_TTY = "\e[%sm"
@@ -923,12 +977,13 @@ class Binding
#
#
# See IRB@IRB+Usage for more information.
- def irb
+ def irb(show_code: true)
IRB.setup(source_location[0], argv: [])
workspace = IRB::WorkSpace.new(self)
- STDOUT.print(workspace.code_around_binding)
+ STDOUT.print(workspace.code_around_binding) if show_code
binding_irb = IRB::Irb.new(workspace)
binding_irb.context.irb_path = File.expand_path(source_location[0])
binding_irb.run(IRB.conf)
+ binding_irb.debug_break
end
end
diff --git a/lib/irb/cmd/backtrace.rb b/lib/irb/cmd/backtrace.rb
new file mode 100644
index 0000000000..f632894618
--- /dev/null
+++ b/lib/irb/cmd/backtrace.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Backtrace < DebugCommand
+ def self.transform_args(args)
+ args&.dump
+ end
+
+ def execute(*args)
+ super(pre_cmds: ["backtrace", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/break.rb b/lib/irb/cmd/break.rb
new file mode 100644
index 0000000000..df259a90ca
--- /dev/null
+++ b/lib/irb/cmd/break.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Break < DebugCommand
+ def self.transform_args(args)
+ args&.dump
+ end
+
+ def execute(args = nil)
+ super(pre_cmds: "break #{args}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/catch.rb b/lib/irb/cmd/catch.rb
new file mode 100644
index 0000000000..40b62c7533
--- /dev/null
+++ b/lib/irb/cmd/catch.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Catch < DebugCommand
+ def self.transform_args(args)
+ args&.dump
+ end
+
+ def execute(*args)
+ super(pre_cmds: ["catch", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/chws.rb b/lib/irb/cmd/chws.rb
index b28c090686..7c84ba0a4b 100644
--- a/lib/irb/cmd/chws.rb
+++ b/lib/irb/cmd/chws.rb
@@ -19,12 +19,18 @@ module IRB
module ExtendCommand
class CurrentWorkingWorkspace < Nop
+ category "IRB"
+ description "Show the current workspace."
+
def execute(*obj)
irb_context.main
end
end
class ChangeWorkspace < Nop
+ category "IRB"
+ description "Change the current workspace to an object."
+
def execute(*obj)
irb_context.change_workspace(*obj)
irb_context.main
diff --git a/lib/irb/cmd/continue.rb b/lib/irb/cmd/continue.rb
new file mode 100644
index 0000000000..9136177eef
--- /dev/null
+++ b/lib/irb/cmd/continue.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Continue < DebugCommand
+ def execute(*args)
+ super(do_cmds: ["continue", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/debug.rb b/lib/irb/cmd/debug.rb
new file mode 100644
index 0000000000..7d39b9fa27
--- /dev/null
+++ b/lib/irb/cmd/debug.rb
@@ -0,0 +1,136 @@
+require_relative "nop"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Debug < Nop
+ category "Debugging"
+ description "Start the debugger of debug.gem."
+
+ BINDING_IRB_FRAME_REGEXPS = [
+ '<internal:prelude>',
+ binding.method(:irb).source_location.first,
+ ].map { |file| /\A#{Regexp.escape(file)}:\d+:in `irb'\z/ }
+ IRB_DIR = File.expand_path('..', __dir__)
+
+ def execute(pre_cmds: nil, do_cmds: nil)
+ unless binding_irb?
+ puts "`debug` command is only available when IRB is started with binding.irb"
+ return
+ end
+
+ unless setup_debugger
+ puts <<~MSG
+ You need to install the debug gem before using this command.
+ If you use `bundle exec`, please add `gem "debug"` into your Gemfile.
+ MSG
+ return
+ end
+
+ options = { oneshot: true, hook_call: false }
+ if pre_cmds || do_cmds
+ options[:command] = ['irb', pre_cmds, do_cmds]
+ end
+ if DEBUGGER__::LineBreakpoint.instance_method(:initialize).parameters.include?([:key, :skip_src])
+ options[:skip_src] = true
+ end
+
+ # To make debugger commands like `next` or `continue` work without asking
+ # the user to quit IRB after that, we need to exit IRB first and then hit
+ # a TracePoint on #debug_break.
+ file, lineno = IRB::Irb.instance_method(:debug_break).source_location
+ DEBUGGER__::SESSION.add_line_breakpoint(file, lineno + 1, **options)
+ # exit current Irb#run call
+ throw :IRB_EXIT
+ end
+
+ private
+
+ def binding_irb?
+ caller.any? do |frame|
+ BINDING_IRB_FRAME_REGEXPS.any? do |regexp|
+ frame.match?(regexp)
+ end
+ end
+ end
+
+ module SkipPathHelperForIRB
+ def skip_internal_path?(path)
+ # The latter can be removed once https://github.com/ruby/debug/issues/866 is resolved
+ super || path.match?(IRB_DIR) || path.match?('<internal:prelude>')
+ end
+ end
+
+ def setup_debugger
+ unless defined?(DEBUGGER__::SESSION)
+ begin
+ require "debug/session"
+ rescue LoadError # debug.gem is not written in Gemfile
+ return false unless load_bundled_debug_gem
+ end
+ DEBUGGER__.start(nonstop: true)
+ end
+
+ unless DEBUGGER__.respond_to?(:capture_frames_without_irb)
+ DEBUGGER__.singleton_class.send(:alias_method, :capture_frames_without_irb, :capture_frames)
+
+ def DEBUGGER__.capture_frames(*args)
+ frames = capture_frames_without_irb(*args)
+ frames.reject! do |frame|
+ frame.realpath&.start_with?(IRB_DIR) || frame.path == "<internal:prelude>"
+ end
+ frames
+ end
+
+ DEBUGGER__::ThreadClient.prepend(SkipPathHelperForIRB)
+ end
+
+ true
+ end
+
+ # This is used when debug.gem is not written in Gemfile. Even if it's not
+ # installed by `bundle install`, debug.gem is installed by default because
+ # it's a bundled gem. This method tries to activate and load that.
+ def load_bundled_debug_gem
+ # Discover latest debug.gem under GEM_PATH
+ debug_gem = Gem.paths.path.flat_map { |path| Dir.glob("#{path}/gems/debug-*") }.select do |path|
+ File.basename(path).match?(/\Adebug-\d+\.\d+\.\d+(\w+)?\z/)
+ end.sort_by do |path|
+ Gem::Version.new(File.basename(path).delete_prefix('debug-'))
+ end.last
+ return false unless debug_gem
+
+ # Discover debug/debug.so under extensions for Ruby 3.2+
+ ext_name = "/debug/debug.#{RbConfig::CONFIG['DLEXT']}"
+ ext_path = Gem.paths.path.flat_map do |path|
+ Dir.glob("#{path}/extensions/**/#{File.basename(debug_gem)}#{ext_name}")
+ end.first
+
+ # Attempt to forcibly load the bundled gem
+ if ext_path
+ $LOAD_PATH << ext_path.delete_suffix(ext_name)
+ end
+ $LOAD_PATH << "#{debug_gem}/lib"
+ begin
+ require "debug/session"
+ puts "Loaded #{File.basename(debug_gem)}"
+ true
+ rescue LoadError
+ false
+ end
+ end
+ end
+
+ class DebugCommand < Debug
+ def self.category
+ "Debugging"
+ end
+
+ def self.description
+ command_name = self.name.split("::").last.downcase
+ "Start the debugger of debug.gem and run its `#{command_name}` command."
+ end
+ end
+ end
+end
diff --git a/lib/irb/cmd/delete.rb b/lib/irb/cmd/delete.rb
new file mode 100644
index 0000000000..aeb26d2572
--- /dev/null
+++ b/lib/irb/cmd/delete.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Delete < DebugCommand
+ def execute(*args)
+ super(pre_cmds: ["delete", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/edit.rb b/lib/irb/cmd/edit.rb
new file mode 100644
index 0000000000..0103891cf4
--- /dev/null
+++ b/lib/irb/cmd/edit.rb
@@ -0,0 +1,61 @@
+require 'shellwords'
+require_relative "nop"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Edit < Nop
+ category "Misc"
+ description 'Open a file with the editor command defined with `ENV["EDITOR"]`.'
+
+ class << self
+ def transform_args(args)
+ # Return a string literal as is for backward compatibility
+ if args.nil? || args.empty? || string_literal?(args)
+ args
+ else # Otherwise, consider the input as a String for convenience
+ args.strip.dump
+ end
+ end
+ end
+
+ def execute(*args)
+ path = args.first
+
+ if path.nil? && (irb_path = @irb_context.irb_path)
+ path = irb_path
+ end
+
+ if !File.exist?(path)
+ require_relative "show_source"
+
+ source =
+ begin
+ ShowSource.find_source(path, @irb_context)
+ rescue NameError
+ # if user enters a path that doesn't exist, it'll cause NameError when passed here because find_source would try to evaluate it as well
+ # in this case, we should just ignore the error
+ end
+
+ if source && File.exist?(source.file)
+ path = source.file
+ else
+ puts "Can not find file: #{path}"
+ return
+ end
+ end
+
+ if editor = ENV['EDITOR']
+ puts "command: '#{editor}'"
+ puts " path: #{path}"
+ system(*Shellwords.split(editor), path)
+ else
+ puts "Can not find editor setting: ENV['EDITOR']"
+ end
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/finish.rb b/lib/irb/cmd/finish.rb
new file mode 100644
index 0000000000..29f100feb5
--- /dev/null
+++ b/lib/irb/cmd/finish.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Finish < DebugCommand
+ def execute(*args)
+ super(do_cmds: ["finish", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/help.rb b/lib/irb/cmd/help.rb
index 0497c57457..2a135cdb14 100644
--- a/lib/irb/cmd/help.rb
+++ b/lib/irb/cmd/help.rb
@@ -16,6 +16,20 @@ module IRB
module ExtendCommand
class Help < Nop
+ class << self
+ def transform_args(args)
+ # Return a string literal as is for backward compatibility
+ if args.empty? || string_literal?(args)
+ args
+ else # Otherwise, consider the input as a String for convenience
+ args.strip.dump
+ end
+ end
+ end
+
+ category "Context"
+ description "Enter the mode to look up RI documents."
+
def execute(*names)
require 'rdoc/ri/driver'
opts = RDoc::RI::Driver.process_args([])
diff --git a/lib/irb/cmd/info.rb b/lib/irb/cmd/info.rb
index 37ecd762ff..2c0a32b34f 100644
--- a/lib/irb/cmd/info.rb
+++ b/lib/irb/cmd/info.rb
@@ -1,31 +1,18 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
-require_relative "nop"
+require_relative "debug"
module IRB
# :stopdoc:
module ExtendCommand
- class Info < Nop
- def execute
- Class.new {
- def inspect
- str = "Ruby version: #{RUBY_VERSION}\n"
- str += "IRB version: #{IRB.version}\n"
- str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
- str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
- str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
- str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
- str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
- str += "East Asian Ambiguous Width: #{Reline.ambiguous_width.inspect}\n"
- if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
- codepage = `chcp`.b.sub(/.*: (\d+)\n/, '\1')
- str += "Code page: #{codepage}\n"
- end
- str
- end
- alias_method :to_s, :inspect
- }.new
+ class Info < DebugCommand
+ def self.transform_args(args)
+ args&.dump
+ end
+
+ def execute(*args)
+ super(pre_cmds: ["info", *args].join(" "))
end
end
end
diff --git a/lib/irb/cmd/irb_info.rb b/lib/irb/cmd/irb_info.rb
new file mode 100644
index 0000000000..da11e8d40b
--- /dev/null
+++ b/lib/irb/cmd/irb_info.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: false
+
+require_relative "nop"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class IrbInfo < Nop
+ category "IRB"
+ description "Show information about IRB."
+
+ def execute
+ Class.new {
+ def inspect
+ str = "Ruby version: #{RUBY_VERSION}\n"
+ str += "IRB version: #{IRB.version}\n"
+ str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
+ str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
+ str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
+ str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
+ str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
+ str += "East Asian Ambiguous Width: #{Reline.ambiguous_width.inspect}\n"
+ if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
+ codepage = `chcp`.b.sub(/.*: (\d+)\n/, '\1')
+ str += "Code page: #{codepage}\n"
+ end
+ str
+ end
+ alias_method :to_s, :inspect
+ }.new
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/load.rb b/lib/irb/cmd/load.rb
index 2c5c01e89c..2897bbd975 100644
--- a/lib/irb/cmd/load.rb
+++ b/lib/irb/cmd/load.rb
@@ -17,18 +17,29 @@ module IRB
# :stopdoc:
module ExtendCommand
- class Load < Nop
+ class LoaderCommand < Nop
include IrbLoader
- def execute(file_name, priv = nil)
- return irb_load(file_name, priv)
+ def raise_cmd_argument_error
+ raise CommandArgumentError.new("Please specify the file name.")
end
end
- class Require < Nop
- include IrbLoader
+ class Load < LoaderCommand
+ category "IRB"
+ description "Load a Ruby file."
+
+ def execute(file_name = nil, priv = nil)
+ raise_cmd_argument_error unless file_name
+ irb_load(file_name, priv)
+ end
+ end
- def execute(file_name)
+ class Require < LoaderCommand
+ category "IRB"
+ description "Require a Ruby file."
+ def execute(file_name = nil)
+ raise_cmd_argument_error unless file_name
rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?")
return false if $".find{|f| f =~ rex}
@@ -56,13 +67,16 @@ module IRB
end
end
- class Source < Nop
- include IrbLoader
- def execute(file_name)
+ class Source < LoaderCommand
+ category "IRB"
+ description "Loads a given file in the current session."
+
+ def execute(file_name = nil)
+ raise_cmd_argument_error unless file_name
+
source_file(file_name)
end
end
end
-
# :startdoc:
end
diff --git a/lib/irb/cmd/ls.rb b/lib/irb/cmd/ls.rb
index f4a7348bd1..b65fae2bf1 100644
--- a/lib/irb/cmd/ls.rb
+++ b/lib/irb/cmd/ls.rb
@@ -9,6 +9,18 @@ module IRB
module ExtendCommand
class Ls < Nop
+ category "Context"
+ description "Show methods, constants, and variables. `-g [query]` or `-G [query]` allows you to filter out the output."
+
+ def self.transform_args(args)
+ if match = args&.match(/\A(?<args>.+\s|)(-g|-G)\s+(?<grep>[^\s]+)\s*\n\z/)
+ args = match[:args]
+ "#{args}#{',' unless args.chomp.empty?} grep: /#{match[:grep]}/"
+ else
+ args
+ end
+ end
+
def execute(*arg, grep: nil)
o = Output.new(grep: grep)
@@ -21,6 +33,7 @@ module IRB
o.dump("instance variables", obj.instance_variables)
o.dump("class variables", klass.class_variables)
o.dump("locals", locals)
+ nil
end
def dump_methods(o, klass, obj)
diff --git a/lib/irb/cmd/measure.rb b/lib/irb/cmd/measure.rb
index a97baee9f1..9122e2dac9 100644
--- a/lib/irb/cmd/measure.rb
+++ b/lib/irb/cmd/measure.rb
@@ -5,6 +5,9 @@ module IRB
module ExtendCommand
class Measure < Nop
+ category "Misc"
+ description "`measure` enables the mode to measure processing time. `measure :off` disables it."
+
def initialize(*args)
super(*args)
end
diff --git a/lib/irb/cmd/next.rb b/lib/irb/cmd/next.rb
new file mode 100644
index 0000000000..d29c82e7fc
--- /dev/null
+++ b/lib/irb/cmd/next.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Next < DebugCommand
+ def execute(*args)
+ super(do_cmds: ["next", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/nop.rb b/lib/irb/cmd/nop.rb
index 881a736722..c616c054a8 100644
--- a/lib/irb/cmd/nop.rb
+++ b/lib/irb/cmd/nop.rb
@@ -13,17 +13,41 @@ module IRB
# :stopdoc:
module ExtendCommand
+ class CommandArgumentError < StandardError; end
+
class Nop
+ class << self
+ def category(category = nil)
+ @category = category if category
+ @category
+ end
+
+ def description(description = nil)
+ @description = description if description
+ @description
+ end
+
+ private
+
+ def string_literal?(args)
+ sexp = Ripper.sexp(args)
+ sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
+ end
+ end
if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.7.0"
def self.execute(conf, *opts, **kwargs, &block)
command = new(conf)
command.execute(*opts, **kwargs, &block)
+ rescue CommandArgumentError => e
+ puts e.message
end
else
def self.execute(conf, *opts, &block)
command = new(conf)
command.execute(*opts, &block)
+ rescue CommandArgumentError => e
+ puts e.message
end
end
diff --git a/lib/irb/cmd/pushws.rb b/lib/irb/cmd/pushws.rb
index 791d8f8dbb..41d2e705f1 100644
--- a/lib/irb/cmd/pushws.rb
+++ b/lib/irb/cmd/pushws.rb
@@ -18,12 +18,18 @@ module IRB
module ExtendCommand
class Workspaces < Nop
+ category "IRB"
+ description "Show workspaces."
+
def execute(*obj)
irb_context.workspaces.collect{|ws| ws.main}
end
end
class PushWorkspace < Workspaces
+ category "IRB"
+ description "Push an object to the workspace stack."
+
def execute(*obj)
irb_context.push_workspace(*obj)
super
@@ -31,6 +37,9 @@ module IRB
end
class PopWorkspace < Workspaces
+ category "IRB"
+ description "Pop a workspace from the workspace stack."
+
def execute(*obj)
irb_context.pop_workspace(*obj)
super
diff --git a/lib/irb/cmd/show_cmds.rb b/lib/irb/cmd/show_cmds.rb
new file mode 100644
index 0000000000..acced27d48
--- /dev/null
+++ b/lib/irb/cmd/show_cmds.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require "stringio"
+require_relative "nop"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class ShowCmds < Nop
+ category "IRB"
+ description "List all available commands and their description."
+
+ def execute(*args)
+ commands_info = IRB::ExtendCommandBundle.all_commands_info
+ commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] }
+ longest_cmd_name_length = commands_info.map { |c| c[:display_name] }.max { |a, b| a.length <=> b.length }.length
+
+ output = StringIO.new
+
+ commands_grouped_by_categories.each do |category, cmds|
+ output.puts Color.colorize(category, [:BOLD])
+
+ cmds.each do |cmd|
+ output.puts " #{cmd[:display_name].to_s.ljust(longest_cmd_name_length)} #{cmd[:description]}"
+ end
+
+ output.puts
+ end
+
+ puts output.string
+
+ nil
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/show_source.rb b/lib/irb/cmd/show_source.rb
index f8a17822df..ea700be4bf 100644
--- a/lib/irb/cmd/show_source.rb
+++ b/lib/irb/cmd/show_source.rb
@@ -9,12 +9,71 @@ module IRB
module ExtendCommand
class ShowSource < Nop
+ category "Context"
+ description "Show the source code of a given method or constant."
+
+ class << self
+ def transform_args(args)
+ # Return a string literal as is for backward compatibility
+ if args.empty? || string_literal?(args)
+ args
+ else # Otherwise, consider the input as a String for convenience
+ args.strip.dump
+ end
+ end
+
+ def find_source(str, irb_context)
+ case str
+ when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
+ eval(str, irb_context.workspace.binding) # trigger autoload
+ base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
+ file, line = base.const_source_location(str) if base.respond_to?(:const_source_location) # Ruby 2.7+
+ when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
+ owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
+ method = Regexp.last_match[:method]
+ if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym)
+ file, line = owner.instance_method(method).source_location
+ end
+ when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
+ receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
+ method = Regexp.last_match[:method]
+ file, line = receiver.method(method).source_location if receiver.respond_to?(method)
+ end
+ if file && line
+ Source.new(file: file, first_line: line, last_line: find_end(file, line))
+ end
+ end
+
+ private
+
+ def find_end(file, first_line)
+ return first_line unless File.exist?(file)
+ lex = RubyLex.new
+ lines = File.read(file).lines[(first_line - 1)..-1]
+ tokens = RubyLex.ripper_lex_without_warning(lines.join)
+ prev_tokens = []
+
+ # chunk with line number
+ tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
+ code = lines[0..lnum].join
+ prev_tokens.concat chunk
+ continue = lex.process_continue(prev_tokens)
+ code_block_open = lex.check_code_block(code, prev_tokens)
+ if !continue && !code_block_open
+ return first_line + lnum
+ end
+ end
+ first_line
+ end
+ end
+
def execute(str = nil)
unless str.is_a?(String)
puts "Error: Expected a string but got #{str.inspect}"
return
end
- source = find_source(str)
+
+ source = self.class.find_source(str, @irb_context)
if source && File.exist?(source.file)
show_source(source)
else
@@ -35,48 +94,6 @@ module IRB
puts
end
- def find_source(str)
- case str
- when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
- eval(str, irb_context.workspace.binding) # trigger autoload
- base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
- file, line = base.const_source_location(str) if base.respond_to?(:const_source_location) # Ruby 2.7+
- when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
- owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
- method = Regexp.last_match[:method]
- if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym)
- file, line = owner.instance_method(method).source_location
- end
- when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
- receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
- method = Regexp.last_match[:method]
- file, line = receiver.method(method).source_location if receiver.respond_to?(method)
- end
- if file && line
- Source.new(file: file, first_line: line, last_line: find_end(file, line))
- end
- end
-
- def find_end(file, first_line)
- return first_line unless File.exist?(file)
- lex = RubyLex.new
- lines = File.read(file).lines[(first_line - 1)..-1]
- tokens = RubyLex.ripper_lex_without_warning(lines.join)
- prev_tokens = []
-
- # chunk with line number
- tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
- code = lines[0..lnum].join
- prev_tokens.concat chunk
- continue = lex.process_continue(prev_tokens)
- code_block_open = lex.check_code_block(code, prev_tokens)
- if !continue && !code_block_open
- return first_line + lnum
- end
- end
- first_line
- end
-
def bold(str)
Color.colorize(str, [:BOLD])
end
diff --git a/lib/irb/cmd/step.rb b/lib/irb/cmd/step.rb
new file mode 100644
index 0000000000..2bc74a9d79
--- /dev/null
+++ b/lib/irb/cmd/step.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Step < DebugCommand
+ def execute(*args)
+ super(do_cmds: ["step", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/subirb.rb b/lib/irb/cmd/subirb.rb
index b322aadc53..699b35fcb4 100644
--- a/lib/irb/cmd/subirb.rb
+++ b/lib/irb/cmd/subirb.rb
@@ -10,31 +10,57 @@
#
require_relative "nop"
-require_relative "../ext/multi-irb"
module IRB
# :stopdoc:
module ExtendCommand
- class IrbCommand < Nop
+ class MultiIRBCommand < Nop
+ def initialize(conf)
+ super
+ extend_irb_context
+ end
+
+ private
+
+ def extend_irb_context
+ # this extension patches IRB context like IRB.CurrentContext
+ require_relative "../ext/multi-irb"
+ end
+ end
+
+ class IrbCommand < MultiIRBCommand
+ category "IRB"
+ description "Start a child IRB."
+
def execute(*obj)
IRB.irb(nil, *obj)
end
end
- class Jobs < Nop
+ class Jobs < MultiIRBCommand
+ category "IRB"
+ description "List of current sessions."
+
def execute
IRB.JobManager
end
end
- class Foreground < Nop
- def execute(key)
+ class Foreground < MultiIRBCommand
+ category "IRB"
+ description "Switches to the session of the given number."
+
+ def execute(key = nil)
+ raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key
IRB.JobManager.switch(key)
end
end
- class Kill < Nop
+ class Kill < MultiIRBCommand
+ category "IRB"
+ description "Kills the session with the given number."
+
def execute(*keys)
IRB.JobManager.kill(*keys)
end
diff --git a/lib/irb/cmd/whereami.rb b/lib/irb/cmd/whereami.rb
index b8c7e047fa..8f56ba073d 100644
--- a/lib/irb/cmd/whereami.rb
+++ b/lib/irb/cmd/whereami.rb
@@ -7,6 +7,9 @@ module IRB
module ExtendCommand
class Whereami < Nop
+ category "Context"
+ description "Show the source code around binding.irb again."
+
def execute(*)
code = irb_context.workspace.code_around_binding
if code
diff --git a/lib/irb/color.rb b/lib/irb/color.rb
index 8307af25a9..6378e14856 100644
--- a/lib/irb/color.rb
+++ b/lib/irb/color.rb
@@ -123,15 +123,21 @@ module IRB # :nodoc:
# If `complete` is false (code is incomplete), this does not warn compile_error.
# This option is needed to avoid warning a user when the compile_error is happening
# because the input is not wrong but just incomplete.
- def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?)
+ def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?, local_variables: [])
return code unless colorable
symbol_state = SymbolState.new
colored = +''
- length = 0
- end_seen = false
+ lvars_code = RubyLex.generate_local_variables_assign_code(local_variables)
+ code_with_lvars = lvars_code ? "#{lvars_code}\n#{code}" : code
+
+ scan(code_with_lvars, allow_last_error: !complete) do |token, str, expr|
+ # handle uncolorable code
+ if token.nil?
+ colored << Reline::Unicode.escape_for_print(str)
+ next
+ end
- scan(code, allow_last_error: !complete) do |token, str, expr|
# IRB::ColorPrinter skips colorizing fragments with any invalid token
if ignore_error && ERROR_TOKENS.include?(token)
return Reline::Unicode.escape_for_print(code)
@@ -147,15 +153,12 @@ module IRB # :nodoc:
colored << line
end
end
- length += str.bytesize
- end_seen = true if token == :on___end__
end
- # give up colorizing incomplete Ripper tokens
- unless end_seen or length == code.bytesize
- return Reline::Unicode.escape_for_print(code)
+ if lvars_code
+ raise "#{lvars_code.dump} should have no \\n" if lvars_code.include?("\n")
+ colored.sub!(/\A.+\n/, '') # delete_prefix lvars_code with colors
end
-
colored
end
@@ -170,33 +173,42 @@ module IRB # :nodoc:
end
def scan(code, allow_last_error:)
- pos = [1, 0]
-
verbose, $VERBOSE = $VERBOSE, nil
RubyLex.compile_with_errors_suppressed(code) do |inner_code, line_no|
lexer = Ripper::Lexer.new(inner_code, '(ripper)', line_no)
- if lexer.respond_to?(:scan) # Ruby 2.7+
- lexer.scan.each do |elem|
- str = elem.tok
- next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message
- next if ([elem.pos[0], elem.pos[1] + str.bytesize] <=> pos) <= 0
+ byte_pos = 0
+ line_positions = [0]
+ inner_code.lines.each do |line|
+ line_positions << line_positions.last + line.bytesize
+ end
- str.each_line do |line|
- if line.end_with?("\n")
- pos[0] += 1
- pos[1] = 0
- else
- pos[1] += line.bytesize
- end
- end
+ on_scan = proc do |elem|
+ start_pos = line_positions[elem.pos[0] - 1] + elem.pos[1]
+ # yield uncolorable code
+ if byte_pos < start_pos
+ yield(nil, inner_code.byteslice(byte_pos...start_pos), nil)
+ end
+
+ if byte_pos <= start_pos
+ str = elem.tok
yield(elem.event, str, elem.state)
+ byte_pos = start_pos + str.bytesize
+ end
+ end
+
+ if lexer.respond_to?(:scan) # Ruby 2.7+
+ lexer.scan.each do |elem|
+ next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message
+ on_scan.call(elem)
end
else
- lexer.parse.each do |elem|
- yield(elem.event, elem.tok, elem.state)
+ lexer.parse.sort_by(&:pos).each do |elem|
+ on_scan.call(elem)
end
end
+ # yield uncolorable DATA section
+ yield(nil, inner_code.byteslice(byte_pos...inner_code.bytesize), nil) if byte_pos < inner_code.bytesize
end
ensure
$VERBOSE = verbose
diff --git a/lib/irb/color_printer.rb b/lib/irb/color_printer.rb
index 78f0b51520..1127bcecb4 100644
--- a/lib/irb/color_printer.rb
+++ b/lib/irb/color_printer.rb
@@ -37,6 +37,9 @@ module IRB
width ||= str.length
case str
+ when ''
+ when ',', '=>', '[', ']', '{', '}', '..', '...', /\A@\w+\z/
+ super(str, width)
when /\A#</, '=', '>'
super(Color.colorize(str, [:GREEN]), width)
else
diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb
index 9121174a50..34640e17f9 100644
--- a/lib/irb/completion.rb
+++ b/lib/irb/completion.rb
@@ -11,7 +11,29 @@ require_relative 'ruby-lex'
module IRB
module InputCompletor # :nodoc:
+ using Module.new {
+ refine ::Binding do
+ def eval_methods
+ ::Kernel.instance_method(:methods).bind(eval("self")).call
+ end
+
+ def eval_private_methods
+ ::Kernel.instance_method(:private_methods).bind(eval("self")).call
+ end
+ def eval_instance_variables
+ ::Kernel.instance_method(:instance_variables).bind(eval("self")).call
+ end
+
+ def eval_global_variables
+ ::Kernel.instance_method(:global_variables).bind(eval("self")).call
+ end
+
+ def eval_class_constants
+ ::Module.instance_method(:constants).bind(eval("self.class")).call
+ end
+ end
+ }
# Set of reserved words used by Ruby, you should not use these for
# constants or variables
@@ -42,25 +64,34 @@ module IRB
if File.respond_to?(:absolute_path?)
File.absolute_path?(p)
else
- if File.absolute_path(p) == p
- true
- else
- false
- end
+ File.absolute_path(p) == p
end
end
+ GEM_PATHS =
+ if defined?(Gem::Specification)
+ Gem::Specification.latest_specs(true).map { |s|
+ s.require_paths.map { |p|
+ if absolute_path?(p)
+ p
+ else
+ File.join(s.full_gem_path, p)
+ end
+ }
+ }.flatten
+ else
+ []
+ end.freeze
+
def self.retrieve_gem_and_system_load_path
- gem_paths = Gem::Specification.latest_specs(true).map { |s|
- s.require_paths.map { |p|
- if absolute_path?(p)
- p
- else
- File.join(s.full_gem_path, p)
- end
- }
- }.flatten if defined?(Gem::Specification)
- (gem_paths.to_a | $LOAD_PATH).sort
+ candidates = (GEM_PATHS | $LOAD_PATH)
+ candidates.map do |p|
+ if p.respond_to?(:to_path)
+ p.to_path
+ else
+ String(p) rescue nil
+ end
+ end.compact.sort
end
def self.retrieve_files_to_require_from_load_path
@@ -142,10 +173,10 @@ module IRB
receiver = $1
message = $3
- candidates = String.instance_methods.collect{|m| m.to_s}
if doc_namespace
"String.#{message}"
else
+ candidates = String.instance_methods.collect{|m| m.to_s}
select_message(receiver, message, candidates)
end
@@ -154,10 +185,10 @@ module IRB
receiver = $1
message = $2
- candidates = Regexp.instance_methods.collect{|m| m.to_s}
if doc_namespace
"Regexp.#{message}"
else
+ candidates = Regexp.instance_methods.collect{|m| m.to_s}
select_message(receiver, message, candidates)
end
@@ -166,10 +197,10 @@ module IRB
receiver = $1
message = $2
- candidates = Array.instance_methods.collect{|m| m.to_s}
if doc_namespace
"Array.#{message}"
else
+ candidates = Array.instance_methods.collect{|m| m.to_s}
select_message(receiver, message, candidates)
end
@@ -178,29 +209,33 @@ module IRB
receiver = $1
message = $2
- proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
- hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
if doc_namespace
["Proc.#{message}", "Hash.#{message}"]
else
+ proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
+ hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
select_message(receiver, message, proc_candidates | hash_candidates)
end
when /^(:[^:.]*)$/
# Symbol
- return nil if doc_namespace
- sym = $1
- candidates = Symbol.all_symbols.collect do |s|
- ":" + s.id2name.encode(Encoding.default_external)
- rescue EncodingError
- # ignore
+ if doc_namespace
+ nil
+ else
+ sym = $1
+ candidates = Symbol.all_symbols.collect do |s|
+ ":" + s.id2name.encode(Encoding.default_external)
+ rescue EncodingError
+ # ignore
+ end
+ candidates.grep(/^#{Regexp.quote(sym)}/)
end
- candidates.grep(/^#{Regexp.quote(sym)}/)
-
when /^::([A-Z][^:\.\(\)]*)$/
# Absolute Constant or class methods
receiver = $1
+
candidates = Object.constants.collect{|m| m.to_s}
+
if doc_namespace
candidates.find { |i| i == receiver }
else
@@ -211,16 +246,18 @@ module IRB
# Constant or class methods
receiver = $1
message = $2
- begin
- candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
- candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
- rescue Exception
- candidates = []
- end
+
if doc_namespace
"#{receiver}::#{message}"
else
- select_message(receiver, message, candidates, "::")
+ begin
+ candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
+ candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
+ rescue Exception
+ candidates = []
+ end
+
+ select_message(receiver, message, candidates.sort, "::")
end
when /^(:[^:.]+)(\.|::)([^.]*)$/
@@ -229,10 +266,10 @@ module IRB
sep = $2
message = $3
- candidates = Symbol.instance_methods.collect{|m| m.to_s}
if doc_namespace
"Symbol.#{message}"
else
+ candidates = Symbol.instance_methods.collect{|m| m.to_s}
select_message(receiver, message, candidates, sep)
end
@@ -244,6 +281,7 @@ module IRB
begin
instance = eval(receiver, bind)
+
if doc_namespace
"#{instance.class.name}.#{message}"
else
@@ -254,7 +292,7 @@ module IRB
if doc_namespace
nil
else
- candidates = []
+ []
end
end
@@ -276,7 +314,7 @@ module IRB
if doc_namespace
nil
else
- candidates = []
+ []
end
end
@@ -284,6 +322,7 @@ module IRB
# global var
gvar = $1
all_gvars = global_variables.collect{|m| m.to_s}
+
if doc_namespace
all_gvars.find{ |i| i == gvar }
else
@@ -296,10 +335,10 @@ module IRB
sep = $2
message = $3
- gv = eval("global_variables", bind).collect{|m| m.to_s}.push("true", "false", "nil")
- lv = eval("local_variables", bind).collect{|m| m.to_s}
- iv = eval("instance_variables", bind).collect{|m| m.to_s}
- cv = eval("self.class.constants", bind).collect{|m| m.to_s}
+ gv = bind.eval_global_variables.collect{|m| m.to_s}.push("true", "false", "nil")
+ lv = bind.local_variables.collect{|m| m.to_s}
+ iv = bind.eval_instance_variables.collect{|m| m.to_s}
+ cv = bind.eval_class_constants.collect{|m| m.to_s}
if (gv | lv | iv | cv).include?(receiver) or /^[A-Z]/ =~ receiver && /\./ !~ receiver
# foo.func and foo is var. OR
@@ -322,11 +361,13 @@ module IRB
to_ignore = ignored_modules
ObjectSpace.each_object(Module){|m|
next if (to_ignore.include?(m) rescue true)
+ next unless m.respond_to?(:instance_methods) # JRuby has modules that represent java packages. They don't include many common ruby methods
candidates.concat m.instance_methods(false).collect{|x| x.to_s}
}
candidates.sort!
candidates.uniq!
end
+
if doc_namespace
rec_class = rec.is_a?(Module) ? rec : rec.class
"#{rec_class.name}#{sep}#{candidates.find{ |i| i == message }}"
@@ -341,27 +382,28 @@ module IRB
message = $1
candidates = String.instance_methods(true).collect{|m| m.to_s}
+
if doc_namespace
"String.#{candidates.find{ |i| i == message }}"
else
- select_message(receiver, message, candidates)
+ select_message(receiver, message, candidates.sort)
end
else
if doc_namespace
- vars = eval("local_variables | instance_variables", bind).collect{|m| m.to_s}
+ vars = (bind.local_variables | bind.eval_instance_variables).collect{|m| m.to_s}
perfect_match_var = vars.find{|m| m.to_s == input}
if perfect_match_var
eval("#{perfect_match_var}.class.name", bind)
else
- candidates = eval("methods | private_methods | local_variables | instance_variables | self.class.constants", bind).collect{|m| m.to_s}
+ candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
candidates |= ReservedWords
candidates.find{ |i| i == input }
end
else
- candidates = eval("methods | private_methods | local_variables | instance_variables | self.class.constants", bind).collect{|m| m.to_s}
+ candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
candidates |= ReservedWords
- candidates.grep(/^#{Regexp.quote(input)}/)
+ candidates.grep(/^#{Regexp.quote(input)}/).sort
end
end
end
diff --git a/lib/irb/context.rb b/lib/irb/context.rb
index e6c993d423..91fbb2fcf1 100644
--- a/lib/irb/context.rb
+++ b/lib/irb/context.rb
@@ -22,7 +22,7 @@ module IRB
#
# The optional +input_method+ argument:
#
- # +nil+:: uses stdin or Reidline or Readline
+ # +nil+:: uses stdin or Reline or Readline
# +String+:: uses a File
# +other+:: uses this as InputMethod
def initialize(irb, workspace = nil, input_method = nil)
@@ -32,7 +32,7 @@ module IRB
else
@workspace = WorkSpace.new
end
- @thread = Thread.current if defined? Thread
+ @thread = Thread.current
# copy of default configuration
@ap_name = IRB.conf[:AP_NAME]
@@ -48,7 +48,15 @@ module IRB
end
if IRB.conf.has_key?(:USE_MULTILINE)
@use_multiline = IRB.conf[:USE_MULTILINE]
- elsif IRB.conf.has_key?(:USE_REIDLINE) # backward compatibility
+ elsif IRB.conf.has_key?(:USE_RELINE) # backward compatibility
+ warn <<~MSG.strip
+ USE_RELINE is deprecated, please use USE_MULTILINE instead.
+ MSG
+ @use_multiline = IRB.conf[:USE_RELINE]
+ elsif IRB.conf.has_key?(:USE_REIDLINE)
+ warn <<~MSG.strip
+ USE_REIDLINE is deprecated, please use USE_MULTILINE instead.
+ MSG
@use_multiline = IRB.conf[:USE_REIDLINE]
else
@use_multiline = nil
@@ -83,14 +91,14 @@ module IRB
when nil
if STDIN.tty? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline?
# Both of multiline mode and singleline mode aren't specified.
- @io = ReidlineInputMethod.new
+ @io = RelineInputMethod.new
else
@io = nil
end
when false
@io = nil
when true
- @io = ReidlineInputMethod.new
+ @io = RelineInputMethod.new
end
unless @io
case use_singleline?
@@ -115,6 +123,10 @@ module IRB
end
@io = StdioInputMethod.new unless @io
+ when '-'
+ @io = FileInputMethod.new($stdin)
+ @irb_name = '-'
+ @irb_path = '-'
when String
@io = FileInputMethod.new(input_method)
@irb_name = File.basename(input_method)
@@ -140,6 +152,8 @@ module IRB
if @newline_before_multiline_output.nil?
@newline_before_multiline_output = true
end
+
+ @command_aliases = IRB.conf[:COMMAND_ALIASES]
end
# The top-level workspace, see WorkSpace#main
@@ -156,7 +170,7 @@ module IRB
# The current input method.
#
# Can be either StdioInputMethod, ReadlineInputMethod,
- # ReidlineInputMethod, FileInputMethod or other specified when the
+ # RelineInputMethod, FileInputMethod or other specified when the
# context is created. See ::new for more # information on +input_method+.
attr_accessor :io
@@ -317,14 +331,17 @@ module IRB
# See IRB@Command+line+options for more command line options.
attr_accessor :back_trace_limit
+ # User-defined IRB command aliases
+ attr_accessor :command_aliases
+
# Alias for #use_multiline
alias use_multiline? use_multiline
# Alias for #use_singleline
alias use_singleline? use_singleline
# backward compatibility
- alias use_reidline use_multiline
+ alias use_reline use_multiline
# backward compatibility
- alias use_reidline? use_multiline
+ alias use_reline? use_multiline
# backward compatibility
alias use_readline use_singleline
# backward compatibility
@@ -342,7 +359,7 @@ module IRB
# Returns whether messages are displayed or not.
def verbose?
if @verbose.nil?
- if @io.kind_of?(ReidlineInputMethod)
+ if @io.kind_of?(RelineInputMethod)
false
elsif defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)
false
@@ -357,11 +374,11 @@ module IRB
end
# Whether #verbose? is +true+, and +input_method+ is either
- # StdioInputMethod or ReidlineInputMethod or ReadlineInputMethod, see #io
+ # StdioInputMethod or RelineInputMethod or ReadlineInputMethod, see #io
# for more information.
def prompting?
verbose? || (STDIN.tty? && @io.kind_of?(StdioInputMethod) ||
- @io.kind_of?(ReidlineInputMethod) ||
+ @io.kind_of?(RelineInputMethod) ||
(defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)))
end
@@ -468,6 +485,20 @@ module IRB
line = "begin ::Kernel.raise _; rescue _.class\n#{line}\n""end"
@workspace.local_variable_set(:_, exception)
end
+
+ # Transform a non-identifier alias (@, $) or keywords (next, break)
+ command, args = line.split(/\s/, 2)
+ if original = command_aliases[command.to_sym]
+ line = line.gsub(/\A#{Regexp.escape(command)}/, original.to_s)
+ command = original
+ end
+
+ # Hook command-specific transformation
+ command_class = ExtendCommandBundle.load_command(command)
+ if command_class&.respond_to?(:transform_args)
+ line = "#{command} #{command_class.transform_args(args)}"
+ end
+
set_last_value(@workspace.evaluate(self, line, irb_path, line_no))
end
@@ -509,5 +540,21 @@ module IRB
end
alias __to_s__ to_s
alias to_s inspect
+
+ def local_variables # :nodoc:
+ workspace.binding.local_variables
+ end
+
+ # Return true if it's aliased from the argument and it's not an identifier.
+ def symbol_alias?(command)
+ return nil if command.match?(/\A\w+\z/)
+ command_aliases.key?(command.to_sym)
+ end
+
+ # Return true if the command supports transforming args
+ def transform_args?(command)
+ command = command_aliases.fetch(command.to_sym, command)
+ ExtendCommandBundle.load_command(command)&.respond_to?(:transform_args)
+ end
end
end
diff --git a/lib/irb/ext/multi-irb.rb b/lib/irb/ext/multi-irb.rb
index 74de1ecde5..e57d43a569 100644
--- a/lib/irb/ext/multi-irb.rb
+++ b/lib/irb/ext/multi-irb.rb
@@ -9,7 +9,6 @@
#
#
#
-fail CantShiftToMultiIrbMode unless defined?(Thread)
module IRB
class JobManager
diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb
index 7acaebe36a..9e7620545a 100644
--- a/lib/irb/ext/save-history.rb
+++ b/lib/irb/ext/save-history.rb
@@ -70,10 +70,10 @@ module IRB
end
history_file = IRB.rc_file("_history") unless history_file
if File.exist?(history_file)
- open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
+ File.open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
f.each { |l|
l = l.chomp
- if self.class == ReidlineInputMethod and history.last&.end_with?("\\")
+ if self.class == RelineInputMethod and history.last&.end_with?("\\")
history.last.delete_suffix!("\\")
history.last << "\n" << l
else
@@ -107,13 +107,13 @@ module IRB
raise
end
- if File.exist?(history_file) && @loaded_history_mtime &&
+ if File.exist?(history_file) &&
File.mtime(history_file) != @loaded_history_mtime
- history = history[@loaded_history_lines..-1]
+ history = history[@loaded_history_lines..-1] if @loaded_history_lines
append_history = true
end
- open(history_file, "#{append_history ? 'a' : 'w'}:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
+ File.open(history_file, (append_history ? 'a' : 'w'), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f|
hist = history.map{ |l| l.split("\n").join("\\\n") }
unless append_history
begin
diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb
index 7778a0d0ce..d0829a06c4 100644
--- a/lib/irb/extend-command.rb
+++ b/lib/irb/extend-command.rb
@@ -45,14 +45,15 @@ module IRB # :nodoc:
[:quit, :irb_exit, OVERRIDE_PRIVATE_ONLY],
]
+
@EXTEND_COMMANDS = [
[
:irb_current_working_workspace, :CurrentWorkingWorkspace, "cmd/chws",
+ [:cwws, NO_OVERRIDE],
+ [:pwws, NO_OVERRIDE],
[:irb_print_working_workspace, OVERRIDE_ALL],
[:irb_cwws, OVERRIDE_ALL],
[:irb_pwws, OVERRIDE_ALL],
- [:cwws, NO_OVERRIDE],
- [:pwws, NO_OVERRIDE],
[:irb_current_working_binding, OVERRIDE_ALL],
[:irb_print_working_binding, OVERRIDE_ALL],
[:irb_cwb, OVERRIDE_ALL],
@@ -60,10 +61,10 @@ module IRB # :nodoc:
],
[
:irb_change_workspace, :ChangeWorkspace, "cmd/chws",
- [:irb_chws, OVERRIDE_ALL],
- [:irb_cws, OVERRIDE_ALL],
[:chws, NO_OVERRIDE],
[:cws, NO_OVERRIDE],
+ [:irb_chws, OVERRIDE_ALL],
+ [:irb_cws, OVERRIDE_ALL],
[:irb_change_binding, OVERRIDE_ALL],
[:irb_cb, OVERRIDE_ALL],
[:cb, NO_OVERRIDE],
@@ -77,16 +78,16 @@ module IRB # :nodoc:
],
[
:irb_push_workspace, :PushWorkspace, "cmd/pushws",
- [:irb_pushws, OVERRIDE_ALL],
[:pushws, NO_OVERRIDE],
+ [:irb_pushws, OVERRIDE_ALL],
[:irb_push_binding, OVERRIDE_ALL],
[:irb_pushb, OVERRIDE_ALL],
[:pushb, NO_OVERRIDE],
],
[
:irb_pop_workspace, :PopWorkspace, "cmd/pushws",
- [:irb_popws, OVERRIDE_ALL],
[:popws, NO_OVERRIDE],
+ [:irb_popws, OVERRIDE_ALL],
[:irb_pop_binding, OVERRIDE_ALL],
[:irb_popb, OVERRIDE_ALL],
[:popb, NO_OVERRIDE],
@@ -117,12 +118,56 @@ module IRB # :nodoc:
],
[
+ :irb_debug, :Debug, "cmd/debug",
+ [:debug, NO_OVERRIDE],
+ ],
+ [
+ :irb_edit, :Edit, "cmd/edit",
+ [:edit, NO_OVERRIDE],
+ ],
+ [
+ :irb_break, :Break, "cmd/break",
+ ],
+ [
+ :irb_catch, :Catch, "cmd/catch",
+ ],
+ [
+ :irb_next, :Next, "cmd/next"
+ ],
+ [
+ :irb_delete, :Delete, "cmd/delete",
+ [:delete, NO_OVERRIDE],
+ ],
+ [
+ :irb_step, :Step, "cmd/step",
+ [:step, NO_OVERRIDE],
+ ],
+ [
+ :irb_continue, :Continue, "cmd/continue",
+ [:continue, NO_OVERRIDE],
+ ],
+ [
+ :irb_finish, :Finish, "cmd/finish",
+ [:finish, NO_OVERRIDE],
+ ],
+ [
+ :irb_backtrace, :Backtrace, "cmd/backtrace",
+ [:backtrace, NO_OVERRIDE],
+ [:bt, NO_OVERRIDE],
+ ],
+ [
+ :irb_debug_info, :Info, "cmd/info",
+ [:info, NO_OVERRIDE],
+ ],
+
+ [
:irb_help, :Help, "cmd/help",
+ [:show_doc, NO_OVERRIDE],
[:help, NO_OVERRIDE],
],
[
- :irb_info, :Info, "cmd/info"
+ :irb_info, :IrbInfo, "cmd/irb_info"
],
[
@@ -144,24 +189,56 @@ module IRB # :nodoc:
:irb_whereami, :Whereami, "cmd/whereami",
[:whereami, NO_OVERRIDE],
],
-
+ [
+ :irb_show_cmds, :ShowCmds, "cmd/show_cmds",
+ [:show_cmds, NO_OVERRIDE],
+ ]
]
- # Installs the default irb commands:
- #
- # +irb_current_working_workspace+:: Context#main
- # +irb_change_workspace+:: Context#change_workspace
- # +irb_workspaces+:: Context#workspaces
- # +irb_push_workspace+:: Context#push_workspace
- # +irb_pop_workspace+:: Context#pop_workspace
- # +irb_load+:: #irb_load
- # +irb_require+:: #irb_require
- # +irb_source+:: IrbLoader#source_file
- # +irb+:: IRB.irb
- # +irb_jobs+:: JobManager
- # +irb_fg+:: JobManager#switch
- # +irb_kill+:: JobManager#kill
- # +irb_help+:: IRB@Command+line+options
+
+ @@commands = []
+
+ def self.all_commands_info
+ return @@commands unless @@commands.empty?
+ user_aliases = IRB.CurrentContext.command_aliases.each_with_object({}) do |(alias_name, target), result|
+ result[target] ||= []
+ result[target] << alias_name
+ end
+
+ @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases|
+ if !defined?(ExtendCommand) || !ExtendCommand.const_defined?(cmd_class, false)
+ require_relative load_file
+ end
+
+ klass = ExtendCommand.const_get(cmd_class, false)
+ aliases = aliases.map { |a| a.first }
+
+ if additional_aliases = user_aliases[cmd_name]
+ aliases += additional_aliases
+ end
+
+ display_name = aliases.shift || cmd_name
+ @@commands << { display_name: display_name, description: klass.description, category: klass.category }
+ end
+
+ @@commands
+ end
+
+ # Convert a command name to its implementation class if such command exists
+ def self.load_command(command)
+ command = command.to_sym
+ @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases|
+ next if cmd_name != command && aliases.all? { |alias_name, _| alias_name != command }
+
+ if !defined?(ExtendCommand) || !ExtendCommand.const_defined?(cmd_class, false)
+ require_relative load_file
+ end
+ return ExtendCommand.const_get(cmd_class, false)
+ end
+ nil
+ end
+
+ # Installs the default irb commands.
def self.install_extend_commands
for args in @EXTEND_COMMANDS
def_extend_command(*args)
diff --git a/lib/irb/init.rb b/lib/irb/init.rb
index d2baee2017..55453cc8f7 100644
--- a/lib/irb/init.rb
+++ b/lib/irb/init.rb
@@ -44,8 +44,8 @@ module IRB # :nodoc:
@CONF[:IRB_RC] = nil
@CONF[:USE_SINGLELINE] = false unless defined?(ReadlineInputMethod)
- @CONF[:USE_COLORIZE] = !ENV['NO_COLOR']
- @CONF[:USE_AUTOCOMPLETE] = true
+ @CONF[:USE_COLORIZE] = (nc = ENV['NO_COLOR']).nil? || nc.empty?
+ @CONF[:USE_AUTOCOMPLETE] = ENV.fetch("IRB_USE_AUTOCOMPLETE", "true") != "false"
@CONF[:INSPECT_MODE] = true
@CONF[:USE_TRACER] = false
@CONF[:USE_LOADER] = false
@@ -158,6 +158,16 @@ module IRB # :nodoc:
@CONF[:LC_MESSAGES] = Locale.new
@CONF[:AT_EXIT] = []
+
+ @CONF[:COMMAND_ALIASES] = {
+ # Symbol aliases
+ :'$' => :show_source,
+ :'@' => :whereami,
+ # Keyword aliases
+ :break => :irb_break,
+ :catch => :irb_catch,
+ :next => :irb_next,
+ }
end
def IRB.set_measure_callback(type = nil, arg = nil, &block)
@@ -255,8 +265,20 @@ module IRB # :nodoc:
when "--nosingleline", "--noreadline"
@CONF[:USE_SINGLELINE] = false
when "--multiline", "--reidline"
+ if opt == "--reidline"
+ warn <<~MSG.strip
+ --reidline is deprecated, please use --multiline instead.
+ MSG
+ end
+
@CONF[:USE_MULTILINE] = true
when "--nomultiline", "--noreidline"
+ if opt == "--noreidline"
+ warn <<~MSG.strip
+ --noreidline is deprecated, please use --nomultiline instead.
+ MSG
+ end
+
@CONF[:USE_MULTILINE] = false
when /^--extra-doc-dir(?:=(.+))?/
opt = $1 || argv.shift
@@ -289,6 +311,10 @@ module IRB # :nodoc:
@CONF[:PROMPT_MODE] = prompt_mode
when "--noprompt"
@CONF[:PROMPT_MODE] = :NULL
+ when "--script"
+ noscript = false
+ when "--noscript"
+ noscript = true
when "--inf-ruby-mode"
@CONF[:PROMPT_MODE] = :INF_RUBY
when "--sample-book-mode", "--simple-prompt"
@@ -309,16 +335,20 @@ module IRB # :nodoc:
IRB.print_usage
exit 0
when "--"
- if opt = argv.shift
+ if !noscript && (opt = argv.shift)
@CONF[:SCRIPT] = opt
$0 = opt
end
break
- when /^-/
+ when /^-./
fail UnrecognizedSwitch, opt
else
- @CONF[:SCRIPT] = opt
- $0 = opt
+ if noscript
+ argv.unshift(opt)
+ else
+ @CONF[:SCRIPT] = opt
+ $0 = opt
+ end
break
end
end
@@ -371,11 +401,9 @@ module IRB # :nodoc:
end
if xdg_config_home = ENV["XDG_CONFIG_HOME"]
irb_home = File.join(xdg_config_home, "irb")
- unless File.exist? irb_home
- require 'fileutils'
- FileUtils.mkdir_p irb_home
+ if File.directory?(irb_home)
+ yield proc{|rc| irb_home + "/irb#{rc}"}
end
- yield proc{|rc| irb_home + "/irb#{rc}"}
end
if home = ENV["HOME"]
yield proc{|rc| home+"/.irb#{rc}"}
diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb
index fd68239ee3..9480573195 100644
--- a/lib/irb/input-method.rb
+++ b/lib/irb/input-method.rb
@@ -14,7 +14,6 @@ require_relative 'magic-file'
require_relative 'completion'
require 'io/console'
require 'reline'
-require 'rdoc'
module IRB
STDIN_FILE_NAME = "(line)" # :nodoc:
@@ -138,7 +137,7 @@ module IRB
# Creates a new input method object
def initialize(file)
super
- @io = IRB::MagicFile.open(file)
+ @io = file.is_a?(IO) ? file : IRB::MagicFile.open(file)
@external_encoding = @io.external_encoding
end
# The file name of this input method, usually given during initialization.
@@ -262,7 +261,7 @@ module IRB
end
end
- class ReidlineInputMethod < InputMethod
+ class RelineInputMethod < InputMethod
include Reline
# Creates a new input method object using Reline
@@ -287,7 +286,8 @@ module IRB
if IRB.conf[:USE_COLORIZE]
proc do |output, complete: |
next unless IRB::Color.colorable?
- IRB::Color.colorize_code(output, complete: complete)
+ lvars = IRB.CurrentContext&.local_variables || []
+ IRB::Color.colorize_code(output, complete: complete, local_variables: lvars)
end
else
proc do |output|
@@ -296,8 +296,13 @@ module IRB
end
Reline.dig_perfect_match_proc = IRB::InputCompletor::PerfectMatchedProc
Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE]
+
if IRB.conf[:USE_AUTOCOMPLETE]
- Reline.add_dialog_proc(:show_doc, SHOW_DOC_DIALOG, Reline::DEFAULT_DIALOG_CONTEXT)
+ begin
+ require 'rdoc'
+ Reline.add_dialog_proc(:show_doc, SHOW_DOC_DIALOG, Reline::DEFAULT_DIALOG_CONTEXT)
+ rescue LoadError
+ end
end
end
@@ -456,7 +461,7 @@ module IRB
# For debug message
def inspect
config = Reline::Config.new
- str = "ReidlineInputMethod with Reline #{Reline::VERSION}"
+ str = "RelineInputMethod with Reline #{Reline::VERSION}"
if config.respond_to?(:inputrc_path)
inputrc_path = File.expand_path(config.inputrc_path)
else
@@ -466,4 +471,13 @@ module IRB
str
end
end
+
+ class ReidlineInputMethod < RelineInputMethod
+ def initialize
+ warn <<~MSG.strip
+ IRB::ReidlineInputMethod is deprecated, please use IRB::RelineInputMethod instead.
+ MSG
+ super
+ end
+ end
end
diff --git a/lib/irb/irb.gemspec b/lib/irb/irb.gemspec
index 26d0fb018f..c3e8a4dc58 100644
--- a/lib/irb/irb.gemspec
+++ b/lib/irb/irb.gemspec
@@ -8,8 +8,8 @@ end
Gem::Specification.new do |spec|
spec.name = "irb"
spec.version = IRB::VERSION
- spec.authors = ["Keiju ISHITSUKA"]
- spec.email = ["keiju@ruby-lang.org"]
+ spec.authors = ["aycabta", "Keiju ISHITSUKA"]
+ spec.email = ["aycabta@gmail.com", "keiju@ruby-lang.org"]
spec.summary = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).}
spec.description = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).}
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
- spec.required_ruby_version = Gem::Requirement.new(">= 2.5")
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.6")
spec.add_dependency "reline", ">= 0.3.0"
end
diff --git a/lib/irb/lc/error.rb b/lib/irb/lc/error.rb
index b8a7fe5a0e..cb5c21cdb4 100644
--- a/lib/irb/lc/error.rb
+++ b/lib/irb/lc/error.rb
@@ -48,11 +48,6 @@ module IRB
super("No such job(#{val}).")
end
end
- class CantShiftToMultiIrbMode < StandardError
- def initialize
- super("Can't shift to multi irb mode.")
- end
- end
class CantChangeBinding < StandardError
def initialize(val)
super("Can't change binding to (#{val}).")
diff --git a/lib/irb/lc/help-message b/lib/irb/lc/help-message
index 3405fa775f..5b23f4c41e 100644
--- a/lib/irb/lc/help-message
+++ b/lib/irb/lc/help-message
@@ -38,6 +38,8 @@ Usage: irb.rb [options] [programfile] [arguments]
--sample-book-mode, --simple-prompt
Set prompt mode to 'simple'.
--noprompt Don't output prompt.
+ --script Script mode (default, treat first argument as script)
+ --noscript No script mode (leave arguments in argv)
--single-irb Share self with sub-irb.
--tracer Show stack trace for each command.
--back-trace-limit n[=16]
diff --git a/lib/irb/lc/ja/error.rb b/lib/irb/lc/ja/error.rb
index d7c181c02e..5e3622cbae 100644
--- a/lib/irb/lc/ja/error.rb
+++ b/lib/irb/lc/ja/error.rb
@@ -48,11 +48,6 @@ module IRB
super("そのようなジョブ(#{val})はありません.")
end
end
- class CantShiftToMultiIrbMode < StandardError
- def initialize
- super("multi-irb modeに移れません.")
- end
- end
class CantChangeBinding < StandardError
def initialize(val)
super("バインディング(#{val})に変更できません.")
diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index 29862f5507..85b336fbe1 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -48,7 +48,7 @@ class RubyLex
end
# io functions
- def set_input(io, p = nil, context: nil, &block)
+ def set_input(io, p = nil, context:, &block)
@io = io
if @io.respond_to?(:check_termination)
@io.check_termination do |code|
@@ -65,6 +65,12 @@ class RubyLex
false
end
else
+ # Accept any single-line input for symbol aliases or commands that transform args
+ command = code.split(/\s/, 2).first
+ if context.symbol_alias?(command) || context.transform_args?(command)
+ next true
+ end
+
code.gsub!(/\s*\z/, '').concat("\n")
ltype, indent, continue, code_block_open = check_state(code, context: context)
if ltype or indent > 0 or continue or code_block_open
@@ -136,41 +142,37 @@ class RubyLex
:on_param_error
]
+ def self.generate_local_variables_assign_code(local_variables)
+ "#{local_variables.join('=')}=nil;" unless local_variables.empty?
+ end
+
def self.ripper_lex_without_warning(code, context: nil)
verbose, $VERBOSE = $VERBOSE, nil
- if context
- lvars = context&.workspace&.binding&.local_variables
- if lvars && !lvars.empty?
- code = "#{lvars.join('=')}=nil\n#{code}"
- line_no = 0
- else
- line_no = 1
- end
+ lvars_code = generate_local_variables_assign_code(context&.local_variables || [])
+ if lvars_code
+ code = "#{lvars_code}\n#{code}"
+ line_no = 0
+ else
+ line_no = 1
end
- tokens = nil
+
compile_with_errors_suppressed(code, line_no: line_no) do |inner_code, line_no|
lexer = Ripper::Lexer.new(inner_code, '-', line_no)
if lexer.respond_to?(:scan) # Ruby 2.7+
- tokens = []
- pos_to_index = {}
- lexer.scan.each do |t|
+ lexer.scan.each_with_object([]) do |t, tokens|
next if t.pos.first == 0
- if pos_to_index.has_key?(t.pos)
- index = pos_to_index[t.pos]
- found_tk = tokens[index]
- if ERROR_TOKENS.include?(found_tk.event) && !ERROR_TOKENS.include?(t.event)
- tokens[index] = t
- end
+ prev_tk = tokens.last
+ position_overlapped = prev_tk && t.pos[0] == prev_tk.pos[0] && t.pos[1] < prev_tk.pos[1] + prev_tk.tok.bytesize
+ if position_overlapped
+ tokens[-1] = t if ERROR_TOKENS.include?(prev_tk.event) && !ERROR_TOKENS.include?(t.event)
else
- pos_to_index[t.pos] = tokens.size
tokens << t
end
end
else
- tokens = lexer.parse.reject { |it| it.pos.first == 0 }
+ lexer.parse.reject { |it| it.pos.first == 0 }.sort_by(&:pos)
end
end
- tokens
ensure
$VERBOSE = verbose
end
@@ -209,12 +211,7 @@ class RubyLex
last_line = lines[line_index]&.byteslice(0, byte_pointer)
code += last_line if last_line
@tokens = self.class.ripper_lex_without_warning(code, context: context)
- corresponding_token_depth = check_corresponding_token_depth(lines, line_index)
- if corresponding_token_depth
- corresponding_token_depth
- else
- nil
- end
+ check_corresponding_token_depth(lines, line_index)
end
end
end
@@ -225,6 +222,8 @@ class RubyLex
ltype = process_literal_type(tokens)
indent = process_nesting_level(tokens)
continue = process_continue(tokens)
+ lvars_code = self.class.generate_local_variables_assign_code(context.local_variables)
+ code = "#{lvars_code}\n#{code}" if lvars_code
code_block_open = check_code_block(code, tokens)
[ltype, indent, continue, code_block_open]
end
@@ -244,13 +243,13 @@ class RubyLex
@code_block_open = false
end
- def each_top_level_statement
+ def each_top_level_statement(context)
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
- unless l = lex
+ unless l = lex(context)
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
@@ -280,18 +279,15 @@ class RubyLex
end
end
- def lex
+ def lex(context)
line = @input.call
if @io.respond_to?(:check_termination)
return line # multiline
end
code = @line + (line.nil? ? '' : line)
code.gsub!(/\s*\z/, '').concat("\n")
- @tokens = self.class.ripper_lex_without_warning(code)
- @continue = process_continue
- @code_block_open = check_code_block(code)
- @indent = process_nesting_level
- @ltype = process_literal_type
+ @tokens = self.class.ripper_lex_without_warning(code, context: context)
+ @ltype, @indent, @continue, @code_block_open = check_state(code, @tokens, context: context)
line
end
@@ -308,7 +304,7 @@ class RubyLex
return true
elsif tokens.size >= 1 and tokens[-1].event == :on_heredoc_end # "EOH\n"
return false
- elsif tokens.size >= 2 and defined?(Ripper::EXPR_BEG) and tokens[-2].state.anybits?(Ripper::EXPR_BEG | Ripper::EXPR_FNAME) and tokens[-2].tok !~ /\A\.\.\.?\z/
+ elsif tokens.size >= 2 and tokens[-2].state.anybits?(Ripper::EXPR_BEG | Ripper::EXPR_FNAME) and tokens[-2].tok !~ /\A\.\.\.?\z/
# end of literal except for regexp
# endless range at end of line is not a continue
return true
@@ -389,21 +385,20 @@ class RubyLex
$VERBOSE = verbose
end
- if defined?(Ripper::EXPR_BEG)
- last_lex_state = tokens.last.state
- if last_lex_state.allbits?(Ripper::EXPR_BEG)
- return false
- elsif last_lex_state.allbits?(Ripper::EXPR_DOT)
- return true
- elsif last_lex_state.allbits?(Ripper::EXPR_CLASS)
- return true
- elsif last_lex_state.allbits?(Ripper::EXPR_FNAME)
- return true
- elsif last_lex_state.allbits?(Ripper::EXPR_VALUE)
- return true
- elsif last_lex_state.allbits?(Ripper::EXPR_ARG)
- return false
- end
+ last_lex_state = tokens.last.state
+
+ if last_lex_state.allbits?(Ripper::EXPR_BEG)
+ return false
+ elsif last_lex_state.allbits?(Ripper::EXPR_DOT)
+ return true
+ elsif last_lex_state.allbits?(Ripper::EXPR_CLASS)
+ return true
+ elsif last_lex_state.allbits?(Ripper::EXPR_FNAME)
+ return true
+ elsif last_lex_state.allbits?(Ripper::EXPR_VALUE)
+ return true
+ elsif last_lex_state.allbits?(Ripper::EXPR_ARG)
+ return false
end
false
@@ -718,6 +713,7 @@ class RubyLex
i = 0
start_token = []
end_type = []
+ pending_heredocs = []
while i < tokens.size
t = tokens[i]
case t.event
@@ -741,18 +737,27 @@ class RubyLex
end
end
when :on_backtick
- start_token << t
- end_type << :on_tstring_end
+ if t.state.allbits?(Ripper::EXPR_BEG)
+ start_token << t
+ end_type << :on_tstring_end
+ end
when :on_qwords_beg, :on_words_beg, :on_qsymbols_beg, :on_symbols_beg
start_token << t
end_type << :on_tstring_end
when :on_heredoc_beg
- start_token << t
- end_type << :on_heredoc_end
+ pending_heredocs << t
+ end
+
+ if pending_heredocs.any? && t.tok.include?("\n")
+ pending_heredocs.reverse_each do |t|
+ start_token << t
+ end_type << :on_heredoc_end
+ end
+ pending_heredocs = []
end
i += 1
end
- start_token.last.nil? ? nil : start_token.last
+ pending_heredocs.first || start_token.last
end
def process_literal_type(tokens = @tokens)
diff --git a/lib/irb/version.rb b/lib/irb/version.rb
index 481d14ffd2..d1c0e54fdc 100644
--- a/lib/irb/version.rb
+++ b/lib/irb/version.rb
@@ -11,7 +11,7 @@
#
module IRB # :nodoc:
- VERSION = "1.4.1"
+ VERSION = "1.6.2"
@RELEASE_VERSION = VERSION
- @LAST_UPDATE_DATE = "2021-12-25"
+ @LAST_UPDATE_DATE = "2022-12-13"
end
diff --git a/lib/logger.rb b/lib/logger.rb
index cbb0da676b..7e4dacc911 100644
--- a/lib/logger.rb
+++ b/lib/logger.rb
@@ -107,7 +107,7 @@ require_relative 'logger/errors'
#
# - \Severity (one letter).
# - Timestamp.
-# - Timezone.
+# - Process id.
# - \Severity (word).
# - Program name.
# - Message.
@@ -147,7 +147,7 @@ require_relative 'logger/errors'
# when the entry is created.
#
# The logged timestamp is formatted by method
-# {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime]
+# {Time#strftime}[rdoc-ref:Time#strftime]
# using this format string:
#
# '%Y-%m-%dT%H:%M:%S.%6N'
@@ -365,7 +365,7 @@ require_relative 'logger/errors'
# You can set a different format using create-time option
# +shift_period_suffix+;
# see details and suggestions at
-# {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime].
+# {Time#strftime}[rdoc-ref:Time#strftime].
#
class Logger
_, name, rev = %w$Id$
@@ -425,7 +425,7 @@ class Logger
# Argument +datetime_format+ should be either of these:
#
# - A string suitable for use as a format for method
- # {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime].
+ # {Time#strftime}[rdoc-ref:Time#strftime].
# - +nil+: the logger uses <tt>'%Y-%m-%dT%H:%M:%S.%6N'</tt>.
#
def datetime_format=(datetime_format)
@@ -453,7 +453,7 @@ class Logger
# The proc should return a string containing the formatted entry.
#
# This custom formatter uses
- # {String#dump}[https://docs.ruby-lang.org/en/master/String.html#method-i-dump]
+ # {String#dump}[rdoc-ref:String#dump]
# to escape the message string:
#
# logger = Logger.new($stdout, progname: 'mung')
diff --git a/lib/logger/formatter.rb b/lib/logger/formatter.rb
index 34e07bc57f..c634dbf34d 100644
--- a/lib/logger/formatter.rb
+++ b/lib/logger/formatter.rb
@@ -3,7 +3,7 @@
class Logger
# Default formatter for log messages.
class Formatter
- Format = "%s, [%s #%d] %5s -- %s: %s\n"
+ Format = "%.1s, [%s #%d] %5s -- %s: %s\n"
DatetimeFormat = "%Y-%m-%dT%H:%M:%S.%6N"
attr_accessor :datetime_format
@@ -13,8 +13,7 @@ class Logger
end
def call(severity, time, progname, msg)
- Format % [severity[0, 1], format_datetime(time), Process.pid, severity, progname,
- msg2str(msg)]
+ sprintf(Format, severity, format_datetime(time), Process.pid, severity, progname, msg2str(msg))
end
private
diff --git a/lib/logger/log_device.rb b/lib/logger/log_device.rb
index 8683328a5e..84277a2656 100644
--- a/lib/logger/log_device.rb
+++ b/lib/logger/log_device.rb
@@ -79,8 +79,10 @@ class Logger
def set_dev(log)
if log.respond_to?(:write) and log.respond_to?(:close)
@dev = log
- if log.respond_to?(:path)
- @filename = log.path
+ if log.respond_to?(:path) and path = log.path
+ if File.exist?(path)
+ @filename = path
+ end
end
else
@dev = open_logfile(log)
diff --git a/lib/logger/logger.gemspec b/lib/logger/logger.gemspec
index ccd4e70db7..d12db625d9 100644
--- a/lib/logger/logger.gemspec
+++ b/lib/logger/logger.gemspec
@@ -23,5 +23,4 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "bundler", ">= 0"
spec.add_development_dependency "rake", ">= 12.3.3"
spec.add_development_dependency "test-unit"
- spec.add_development_dependency "rdoc"
end
diff --git a/lib/logger/version.rb b/lib/logger/version.rb
index ba845a9304..f85c72eed3 100644
--- a/lib/logger/version.rb
+++ b/lib/logger/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class Logger
- VERSION = "1.5.1"
+ VERSION = "1.5.3"
end
diff --git a/lib/mkmf.rb b/lib/mkmf.rb
index a6ec9bae5d..0fbc1cc2e5 100644
--- a/lib/mkmf.rb
+++ b/lib/mkmf.rb
@@ -1762,7 +1762,7 @@ SRC
hdr << "#endif\n"
hdr = hdr.join("")
log_src(hdr, "#{header} is")
- unless (IO.read(header) == hdr rescue false)
+ unless (File.read(header) == hdr rescue false)
File.open(header, "wb") do |hfile|
hfile.write(hdr)
end
@@ -1866,7 +1866,7 @@ SRC
if pkgconfig = with_config("#{pkg}-config") and find_executable0(pkgconfig)
# if and only if package specific config command is given
elsif ($PKGCONFIG ||=
- (pkgconfig = with_config("pkg-config", ("pkg-config" unless CROSS_COMPILING))) &&
+ (pkgconfig = with_config("pkg-config") {config_string("PKG_CONFIG") || "pkg-config"}) &&
find_executable0(pkgconfig) && pkgconfig) and
xsystem([*envs, $PKGCONFIG, "--exists", pkg])
# default to pkg-config command
@@ -2076,6 +2076,11 @@ sitearch = #{CONFIG['sitearch']}
ruby_version = #{RbConfig::CONFIG['ruby_version']}
ruby = #{$ruby.sub(%r[\A#{Regexp.quote(RbConfig::CONFIG['bindir'])}(?=/|\z)]) {'$(bindir)'}}
RUBY = $(ruby#{sep})
+BUILTRUBY = #{if defined?($builtruby) && $builtruby
+ $builtruby
+ else
+ File.join('$(bindir)', CONFIG["RUBY_INSTALL_NAME"] + CONFIG['EXEEXT'])
+ end}
ruby_headers = #{headers.join(' ')}
RM = #{config_string('RM', &possible_command) || '$(RUBY) -run -e rm -- -f'}
@@ -2373,11 +2378,19 @@ TIMESTAMP_DIR = #{$extout && $extmk ? '$(extout)/.timestamp' : '.'}
install_dirs.each {|d| conf << ("%-14s= %s\n" % d) if /^[[:upper:]]/ =~ d[0]}
sodir = $extout ? '$(TARGET_SO_DIR)' : '$(RUBYARCHDIR)'
n = '$(TARGET_SO_DIR)$(TARGET)'
+ cleanobjs = ["$(OBJS)"]
+ if $extmk
+ %w[bc i s].each {|ex| cleanobjs << "$(OBJS:.#{$OBJEXT}=.#{ex})"}
+ end
+ if target
+ config_string('cleanobjs') {|t| cleanobjs << t.gsub(/\$\*/, "$(TARGET)#{deffile ? '-$(arch)': ''}")}
+ end
conf << "\
TARGET_SO_DIR =#{$extout ? " $(RUBYARCHDIR)/" : ''}
TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
CLEANLIBS = #{'$(TARGET_SO) ' if target}#{config_string('cleanlibs') {|t| t.gsub(/\$\*/) {n}}}
-CLEANOBJS = *.#{$OBJEXT} #{config_string('cleanobjs') {|t| t.gsub(/\$\*/, "$(TARGET)#{deffile ? '-$(arch)': ''}")} if target} *.bak
+CLEANOBJS = #{cleanobjs.join(' ')} *.bak
+TARGET_SO_DIR_TIMESTAMP = #{timestamp_file(sodir, target_prefix)}
" #"
conf = yield(conf) if block_given?
@@ -2411,7 +2424,7 @@ static: #{$extmk && !$static ? "all" : "$(STATIC_LIB)#{$extout ? " install-rb" :
if target
f = "$(DLLIB)"
dest = "$(TARGET_SO)"
- stamp = timestamp_file(dir, target_prefix)
+ stamp = '$(TARGET_SO_DIR_TIMESTAMP)'
if $extout
mfile.puts dest
mfile.print "clean-so::\n"
@@ -2480,7 +2493,9 @@ static: #{$extmk && !$static ? "all" : "$(STATIC_LIB)#{$extout ? " install-rb" :
end
end
end
- dirs.unshift(sodir) if target and !dirs.include?(sodir)
+ if target and !dirs.include?(sodir)
+ mfile.print "$(TARGET_SO_DIR_TIMESTAMP):\n\t$(Q) $(MAKEDIRS) $(@D) #{sodir}\n\t$(Q) $(TOUCH) $@\n"
+ end
dirs.each do |d|
t = timestamp_file(d, target_prefix)
mfile.print "#{t}:\n\t$(Q) $(MAKEDIRS) $(@D) #{d}\n\t$(Q) $(TOUCH) $@\n"
@@ -2524,7 +2539,7 @@ site-install-rb: install-rb
mfile.print "$(TARGET_SO): "
mfile.print "$(DEFFILE) " if makedef
mfile.print "$(OBJS) Makefile"
- mfile.print " #{timestamp_file(sodir, target_prefix)}" if $extout
+ mfile.print " $(TARGET_SO_DIR_TIMESTAMP)" if $extout
mfile.print "\n"
mfile.print "\t$(ECHO) linking shared-object #{target_prefix.sub(/\A\/(.*)/, '\1/')}$(DLLIB)\n"
mfile.print "\t-$(Q)$(RM) $(@#{sep})\n"
@@ -2594,6 +2609,7 @@ site-install-rb: install-rb
$INCFLAGS << " -I$(hdrdir)/ruby/backward" unless $extmk
$INCFLAGS << " -I$(hdrdir) -I$(srcdir)"
$DLDFLAGS = with_config("dldflags", arg_config("DLDFLAGS", config["DLDFLAGS"])).dup
+ config_string("ADDITIONAL_DLDFLAGS") {|flags| $DLDFLAGS << " " << flags} unless $extmk
$LIBEXT = config['LIBEXT'].dup
$OBJEXT = config["OBJEXT"].dup
$EXEEXT = config["EXEEXT"].dup
diff --git a/lib/mutex_m.rb b/lib/mutex_m.rb
index abd0fc6add..17ec9924e4 100644
--- a/lib/mutex_m.rb
+++ b/lib/mutex_m.rb
@@ -40,7 +40,7 @@
#
module Mutex_m
- VERSION = "0.1.1"
+ VERSION = "0.1.2"
Ractor.make_shareable(VERSION) if defined?(Ractor)
def Mutex_m.define_aliases(cl) # :nodoc:
diff --git a/lib/net/http.rb b/lib/net/http.rb
index a583441253..387df4b8f4 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# = net/http.rb
#
@@ -32,373 +32,697 @@ module Net #:nodoc:
class HTTPHeaderSyntaxError < StandardError; end
# :startdoc:
- # == An HTTP client API for Ruby.
+ # \Class \Net::HTTP provides a rich library that implements the client
+ # in a client-server model that uses the \HTTP request-response protocol.
+ # For information about \HTTP, see:
+ #
+ # - {Hypertext Transfer Protocol}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol].
+ # - {Technical overview}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Technical_overview].
+ #
+ # == About the Examples
+ #
+ # :include: doc/net-http/examples.rdoc
+ #
+ # == Strategies
+ #
+ # - If you will make only a few GET requests,
+ # consider using {OpenURI}[rdoc-ref:OpenURI].
+ # - If you will make only a few requests of all kinds,
+ # consider using the various singleton convenience methods in this class.
+ # Each of the following methods automatically starts and finishes
+ # a {session}[rdoc-ref:Net::HTTP@Sessions] that sends a single request:
+ #
+ # # Return string response body.
+ # Net::HTTP.get(hostname, path)
+ # Net::HTTP.get(uri)
+ #
+ # # Write string response body to $stdout.
+ # Net::HTTP.get_print(hostname, path)
+ # Net::HTTP.get_print(uri)
+ #
+ # # Return response as Net::HTTPResponse object.
+ # Net::HTTP.get_response(hostname, path)
+ # Net::HTTP.get_response(uri)
+ # data = '{"title": "foo", "body": "bar", "userId": 1}'
+ # Net::HTTP.post(uri, data)
+ # params = {title: 'foo', body: 'bar', userId: 1}
+ # Net::HTTP.post_form(uri, params)
+ #
+ # - If performance is important, consider using sessions, which lower request overhead.
+ # This {session}[rdoc-ref:Net::HTTP@Sessions] has multiple requests for
+ # {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]
+ # and {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]:
+ #
+ # Net::HTTP.start(hostname) do |http|
+ # # Session started automatically before block execution.
+ # http.get(path)
+ # http.head(path)
+ # body = 'Some text'
+ # http.post(path, body) # Can also have a block.
+ # http.put(path, body)
+ # http.delete(path)
+ # http.options(path)
+ # http.trace(path)
+ # http.patch(path, body) # Can also have a block.
+ # http.copy(path)
+ # http.lock(path, body)
+ # http.mkcol(path, body)
+ # http.move(path)
+ # http.propfind(path, body)
+ # http.proppatch(path, body)
+ # http.unlock(path, body)
+ # # Session finished automatically at block exit.
+ # end
#
- # Net::HTTP provides a rich library which can be used to build HTTP
- # user-agents. For more details about HTTP see
- # [RFC2616](http://www.ietf.org/rfc/rfc2616.txt).
+ # The methods cited above are convenience methods that, via their few arguments,
+ # allow minimal control over the requests.
+ # For greater control, consider using {request objects}[rdoc-ref:Net::HTTPRequest].
#
- # Net::HTTP is designed to work closely with URI. URI::HTTP#host,
- # URI::HTTP#port and URI::HTTP#request_uri are designed to work with
- # Net::HTTP.
+ # == URIs
#
- # If you are only performing a few GET requests you should try OpenURI.
+ # On the internet, a URI
+ # ({Universal Resource Identifier}[https://en.wikipedia.org/wiki/Uniform_Resource_Identifier])
+ # is a string that identifies a particular resource.
+ # It consists of some or all of: scheme, hostname, path, query, and fragment;
+ # see {URI syntax}[https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax].
#
- # == Simple Examples
+ # A Ruby {URI::Generic}[rdoc-ref:URI::Generic] object
+ # represents an internet URI.
+ # It provides, among others, methods
+ # +scheme+, +hostname+, +path+, +query+, and +fragment+.
#
- # All examples assume you have loaded Net::HTTP with:
+ # === Schemes
#
- # require 'net/http'
+ # An internet \URI has
+ # a {scheme}[https://en.wikipedia.org/wiki/List_of_URI_schemes].
#
- # This will also require 'uri' so you don't need to require it separately.
+ # The two schemes supported in \Net::HTTP are <tt>'https'</tt> and <tt>'http'</tt>:
#
- # The Net::HTTP methods in the following section do not persist
- # connections. They are not recommended if you are performing many HTTP
- # requests.
+ # uri.scheme # => "https"
+ # URI('http://example.com').scheme # => "http"
#
- # === GET
+ # === Hostnames
#
- # Net::HTTP.get('example.com', '/index.html') # => String
+ # A hostname identifies a server (host) to which requests may be sent:
#
- # === GET by URI
+ # hostname = uri.hostname # => "jsonplaceholder.typicode.com"
+ # Net::HTTP.start(hostname) do |http|
+ # # Some HTTP stuff.
+ # end
#
- # uri = URI('http://example.com/index.html?count=10')
- # Net::HTTP.get(uri) # => String
+ # === Paths
#
- # === GET with Dynamic Parameters
+ # A host-specific path identifies a resource on the host:
#
- # uri = URI('http://example.com/index.html')
- # params = { :limit => 10, :page => 3 }
- # uri.query = URI.encode_www_form(params)
+ # _uri = uri.dup
+ # _uri.path = '/todos/1'
+ # hostname = _uri.hostname
+ # path = _uri.path
+ # Net::HTTP.get(hostname, path)
#
- # res = Net::HTTP.get_response(uri)
- # puts res.body if res.is_a?(Net::HTTPSuccess)
+ # === Queries
#
- # === POST
+ # A host-specific query adds name/value pairs to the URI:
#
- # uri = URI('http://www.example.com/search.cgi')
- # res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50')
- # puts res.body
+ # _uri = uri.dup
+ # params = {userId: 1, completed: false}
+ # _uri.query = URI.encode_www_form(params)
+ # _uri # => #<URI::HTTPS https://jsonplaceholder.typicode.com?userId=1&completed=false>
+ # Net::HTTP.get(_uri)
#
- # === POST with Multiple Values
+ # === Fragments
#
- # uri = URI('http://www.example.com/search.cgi')
- # res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50')
- # puts res.body
+ # A {URI fragment}[https://en.wikipedia.org/wiki/URI_fragment] has no effect
+ # in \Net::HTTP;
+ # the same data is returned, regardless of whether a fragment is included.
#
- # == How to use Net::HTTP
+ # == Request Headers
#
- # The following example code can be used as the basis of an HTTP user-agent
- # which can perform a variety of request types using persistent
- # connections.
+ # Request headers may be used to pass additional information to the host,
+ # similar to arguments passed in a method call;
+ # each header is a name/value pair.
#
- # uri = URI('http://example.com/some_path?query=string')
+ # Each of the \Net::HTTP methods that sends a request to the host
+ # has optional argument +headers+,
+ # where the headers are expressed as a hash of field-name/value pairs:
#
- # Net::HTTP.start(uri.host, uri.port) do |http|
- # request = Net::HTTP::Get.new uri
+ # headers = {Accept: 'application/json', Connection: 'Keep-Alive'}
+ # Net::HTTP.get(uri, headers)
#
- # response = http.request request # Net::HTTPResponse object
- # end
+ # See lists of both standard request fields and common request fields at
+ # {Request Fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields].
+ # A host may also accept other custom fields.
#
- # Net::HTTP::start immediately creates a connection to an HTTP server which
- # is kept open for the duration of the block. The connection will remain
- # open for multiple requests in the block if the server indicates it
- # supports persistent connections.
+ # == \HTTP Sessions
#
- # If you wish to re-use a connection across multiple HTTP requests without
- # automatically closing it you can use ::new and then call #start and
- # #finish manually.
+ # A _session_ is a connection between a server (host) and a client that:
#
- # The request types Net::HTTP supports are listed below in the section "HTTP
- # Request Classes".
+ # - Is begun by instance method Net::HTTP#start.
+ # - May contain any number of requests.
+ # - Is ended by instance method Net::HTTP#finish.
#
- # For all the Net::HTTP request objects and shortcut request methods you may
- # supply either a String for the request path or a URI from which Net::HTTP
- # will extract the request path.
+ # See example sessions at {Strategies}[rdoc-ref:Net::HTTP@Strategies].
#
- # === Response Data
+ # === Session Using \Net::HTTP.start
#
- # uri = URI('http://example.com/index.html')
- # res = Net::HTTP.get_response(uri)
+ # If you have many requests to make to a single host (and port),
+ # consider using singleton method Net::HTTP.start with a block;
+ # the method handles the session automatically by:
#
- # # Headers
- # res['Set-Cookie'] # => String
- # res.get_fields('set-cookie') # => Array
- # res.to_hash['set-cookie'] # => Array
- # puts "Headers: #{res.to_hash.inspect}"
+ # - Calling #start before block execution.
+ # - Executing the block.
+ # - Calling #finish after block execution.
#
- # # Status
- # puts res.code # => '200'
- # puts res.message # => 'OK'
- # puts res.class.name # => 'HTTPOK'
+ # In the block, you can use these instance methods,
+ # each of which that sends a single request:
#
- # # Body
- # puts res.body
+ # - {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]:
#
- # === Following Redirection
+ # - #get, #request_get: GET.
+ # - #head, #request_head: HEAD.
+ # - #post, #request_post: POST.
+ # - #delete: DELETE.
+ # - #options: OPTIONS.
+ # - #trace: TRACE.
+ # - #patch: PATCH.
#
- # Each Net::HTTPResponse object belongs to a class for its response code.
+ # - {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]:
#
- # For example, all 2XX responses are instances of a Net::HTTPSuccess
- # subclass, a 3XX response is an instance of a Net::HTTPRedirection
- # subclass and a 200 response is an instance of the Net::HTTPOK class. For
- # details of response classes, see the section "HTTP Response Classes"
- # below.
+ # - #copy: COPY.
+ # - #lock: LOCK.
+ # - #mkcol: MKCOL.
+ # - #move: MOVE.
+ # - #propfind: PROPFIND.
+ # - #proppatch: PROPPATCH.
+ # - #unlock: UNLOCK.
#
- # Using a case statement you can handle various types of responses properly:
+ # === Session Using \Net::HTTP.start and \Net::HTTP.finish
#
- # def fetch(uri_str, limit = 10)
- # # You should choose a better exception.
- # raise ArgumentError, 'too many HTTP redirects' if limit == 0
+ # You can manage a session manually using methods #start and #finish:
#
- # response = Net::HTTP.get_response(URI(uri_str))
+ # http = Net::HTTP.new(hostname)
+ # http.start
+ # http.get('/todos/1')
+ # http.get('/todos/2')
+ # http.delete('/posts/1')
+ # http.finish # Needed to free resources.
#
- # case response
- # when Net::HTTPSuccess then
- # response
- # when Net::HTTPRedirection then
- # location = response['location']
- # warn "redirected to #{location}"
- # fetch(location, limit - 1)
- # else
- # response.value
- # end
- # end
+ # === Single-Request Session
#
- # print fetch('http://www.ruby-lang.org')
+ # Certain convenience methods automatically handle a session by:
#
- # === POST
+ # - Creating an \HTTP object
+ # - Starting a session.
+ # - Sending a single request.
+ # - Finishing the session.
+ # - Destroying the object.
#
- # A POST can be made using the Net::HTTP::Post request class. This example
- # creates a URL encoded POST body:
+ # Such methods that send GET requests:
#
- # uri = URI('http://www.example.com/todo.cgi')
- # req = Net::HTTP::Post.new(uri)
- # req.set_form_data('from' => '2005-01-01', 'to' => '2005-03-31')
+ # - ::get: Returns the string response body.
+ # - ::get_print: Writes the string response body to $stdout.
+ # - ::get_response: Returns a Net::HTTPResponse object.
#
- # res = Net::HTTP.start(uri.hostname, uri.port) do |http|
- # http.request(req)
- # end
+ # Such methods that send POST requests:
#
- # case res
- # when Net::HTTPSuccess, Net::HTTPRedirection
- # # OK
- # else
- # res.value
- # end
+ # - ::post: Posts data to the host.
+ # - ::post_form: Posts form data to the host.
#
- # To send multipart/form-data use Net::HTTPHeader#set_form:
+ # == \HTTP Requests and Responses
#
- # req = Net::HTTP::Post.new(uri)
- # req.set_form([['upload', File.open('foo.bar')]], 'multipart/form-data')
+ # Many of the methods above are convenience methods,
+ # each of which sends a request and returns a string
+ # without directly using \Net::HTTPRequest and \Net::HTTPResponse objects.
#
- # Other requests that can contain a body such as PUT can be created in the
- # same way using the corresponding request class (Net::HTTP::Put).
+ # You can, however, directly create a request object, send the request,
+ # and retrieve the response object; see:
#
- # === Setting Headers
+ # - Net::HTTPRequest.
+ # - Net::HTTPResponse.
#
- # The following example performs a conditional GET using the
- # If-Modified-Since header. If the files has not been modified since the
- # time in the header a Not Modified response will be returned. See RFC 2616
- # section 9.3 for further details.
+ # == Following Redirection
#
- # uri = URI('http://example.com/cached_response')
- # file = File.stat 'cached_response'
+ # Each returned response is an instance of a subclass of Net::HTTPResponse.
+ # See the {response class hierarchy}[rdoc-ref:Net::HTTPResponse@Response+Subclasses].
#
- # req = Net::HTTP::Get.new(uri)
- # req['If-Modified-Since'] = file.mtime.rfc2822
+ # In particular, class Net::HTTPRedirection is the parent
+ # of all redirection classes.
+ # This allows you to craft a case statement to handle redirections properly:
#
- # res = Net::HTTP.start(uri.hostname, uri.port) {|http|
- # http.request(req)
- # }
+ # def fetch(uri, limit = 10)
+ # # You should choose a better exception.
+ # raise ArgumentError, 'Too many HTTP redirects' if limit == 0
+ #
+ # res = Net::HTTP.get_response(URI(uri))
+ # case res
+ # when Net::HTTPSuccess # Any success class.
+ # res
+ # when Net::HTTPRedirection # Any redirection class.
+ # location = res['Location']
+ # warn "Redirected to #{location}"
+ # fetch(location, limit - 1)
+ # else # Any other class.
+ # res.value
+ # end
+ # end
#
- # open 'cached_response', 'w' do |io|
- # io.write res.body
- # end if res.is_a?(Net::HTTPSuccess)
+ # fetch(uri)
#
- # === Basic Authentication
+ # == Basic Authentication
#
# Basic authentication is performed according to
- # [RFC2617](http://www.ietf.org/rfc/rfc2617.txt).
- #
- # uri = URI('http://example.com/index.html?key=value')
+ # {RFC2617}[http://www.ietf.org/rfc/rfc2617.txt]:
#
# req = Net::HTTP::Get.new(uri)
- # req.basic_auth 'user', 'pass'
- #
- # res = Net::HTTP.start(uri.hostname, uri.port) {|http|
+ # req.basic_auth('user', 'pass')
+ # res = Net::HTTP.start(hostname) do |http|
# http.request(req)
- # }
- # puts res.body
+ # end
#
- # === Streaming Response Bodies
+ # == Streaming Response Bodies
#
- # By default Net::HTTP reads an entire response into memory. If you are
+ # By default \Net::HTTP reads an entire response into memory. If you are
# handling large files or wish to implement a progress bar you can instead
# stream the body directly to an IO.
#
- # uri = URI('http://example.com/large_file')
- #
- # Net::HTTP.start(uri.host, uri.port) do |http|
- # request = Net::HTTP::Get.new uri
- #
- # http.request request do |response|
- # open 'large_file', 'w' do |io|
- # response.read_body do |chunk|
- # io.write chunk
+ # Net::HTTP.start(hostname) do |http|
+ # req = Net::HTTP::Get.new(uri)
+ # http.request(req) do |res|
+ # open('t.tmp', 'w') do |f|
+ # res.read_body do |chunk|
+ # f.write chunk
# end
# end
# end
# end
#
- # === HTTPS
- #
- # HTTPS is enabled for an HTTP connection by Net::HTTP#use_ssl=.
+ # == HTTPS
#
- # uri = URI('https://secure.example.com/some_path?query=string')
+ # HTTPS is enabled for an \HTTP connection by Net::HTTP#use_ssl=:
#
- # Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
- # request = Net::HTTP::Get.new uri
- # response = http.request request # Net::HTTPResponse object
+ # Net::HTTP.start(hostname, :use_ssl => true) do |http|
+ # req = Net::HTTP::Get.new(uri)
+ # res = http.request(req)
# end
#
- # Or if you simply want to make a GET request, you may pass in an URI
- # object that has an HTTPS URL. Net::HTTP automatically turns on TLS
- # verification if the URI object has a 'https' URI scheme.
+ # Or if you simply want to make a GET request, you may pass in a URI
+ # object that has an \HTTPS URL. \Net::HTTP automatically turns on TLS
+ # verification if the URI object has a 'https' URI scheme:
+ #
+ # uri # => #<URI::HTTPS https://jsonplaceholder.typicode.com/>
+ # Net::HTTP.get(uri)
+ #
+ # == Proxy Server
+ #
+ # An \HTTP object can have
+ # a {proxy server}[https://en.wikipedia.org/wiki/Proxy_server].
+ #
+ # You can create an \HTTP object with a proxy server
+ # using method Net::HTTP.new or method Net::HTTP.start.
+ #
+ # The proxy may be defined either by argument +p_addr+
+ # or by environment variable <tt>'http_proxy'</tt>.
+ #
+ # === Proxy Using Argument +p_addr+ as a \String
+ #
+ # When argument +p_addr+ is a string hostname,
+ # the returned +http+ has the given host as its proxy:
#
- # uri = URI('https://example.com/')
- # Net::HTTP.get(uri) # => String
+ # http = Net::HTTP.new(hostname, nil, 'proxy.example')
+ # http.proxy? # => true
+ # http.proxy_from_env? # => false
+ # http.proxy_address # => "proxy.example"
+ # # These use default values.
+ # http.proxy_port # => 80
+ # http.proxy_user # => nil
+ # http.proxy_pass # => nil
#
- # In previous versions of Ruby you would need to require 'net/https' to use
- # HTTPS. This is no longer true.
+ # The port, username, and password for the proxy may also be given:
+ #
+ # http = Net::HTTP.new(hostname, nil, 'proxy.example', 8000, 'pname', 'ppass')
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
+ # http.proxy? # => true
+ # http.proxy_from_env? # => false
+ # http.proxy_address # => "proxy.example"
+ # http.proxy_port # => 8000
+ # http.proxy_user # => "pname"
+ # http.proxy_pass # => "ppass"
+ #
+ # === Proxy Using '<tt>ENV['http_proxy']</tt>'
+ #
+ # When environment variable <tt>'http_proxy'</tt>
+ # is set to a \URI string,
+ # the returned +http+ will have the server at that URI as its proxy;
+ # note that the \URI string must have a protocol
+ # such as <tt>'http'</tt> or <tt>'https'</tt>:
+ #
+ # ENV['http_proxy'] = 'http://example.com'
+ # http = Net::HTTP.new(hostname)
+ # http.proxy? # => true
+ # http.proxy_from_env? # => true
+ # http.proxy_address # => "example.com"
+ # # These use default values.
+ # http.proxy_port # => 80
+ # http.proxy_user # => nil
+ # http.proxy_pass # => nil
+ #
+ # The \URI string may include proxy username, password, and port number:
+ #
+ # ENV['http_proxy'] = 'http://pname:ppass@example.com:8000'
+ # http = Net::HTTP.new(hostname)
+ # http.proxy? # => true
+ # http.proxy_from_env? # => true
+ # http.proxy_address # => "example.com"
+ # http.proxy_port # => 8000
+ # http.proxy_user # => "pname"
+ # http.proxy_pass # => "ppass"
+ #
+ # === Filtering Proxies
+ #
+ # With method Net::HTTP.new (but not Net::HTTP.start),
+ # you can use argument +p_no_proxy+ to filter proxies:
+ #
+ # - Reject a certain address:
+ #
+ # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example')
+ # http.proxy_address # => nil
+ #
+ # - Reject certain domains or subdomains:
+ #
+ # http = Net::HTTP.new('example.com', nil, 'my.proxy.example', 8000, 'pname', 'ppass', 'proxy.example')
+ # http.proxy_address # => nil
+ #
+ # - Reject certain addresses and port combinations:
+ #
+ # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example:1234')
+ # http.proxy_address # => "proxy.example"
+ #
+ # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example:8000')
+ # http.proxy_address # => nil
+ #
+ # - Reject a list of the types above delimited using a comma:
+ #
+ # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'my.proxy,proxy.example:8000')
+ # http.proxy_address # => nil
+ #
+ # http = Net::HTTP.new('example.com', nil, 'my.proxy', 8000, 'pname', 'ppass', 'my.proxy,proxy.example:8000')
+ # http.proxy_address # => nil
+ #
+ # == Compression and Decompression
+ #
+ # \Net::HTTP does not compress the body of a request before sending.
+ #
+ # By default, \Net::HTTP adds header <tt>'Accept-Encoding'</tt>
+ # to a new {request object}[rdoc-ref:Net::HTTPRequest]:
+ #
+ # Net::HTTP::Get.new(uri)['Accept-Encoding']
+ # # => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
+ #
+ # This requests the server to zip-encode the response body if there is one;
+ # the server is not required to do so.
+ #
+ # \Net::HTTP does not automatically decompress a response body
+ # if the response has header <tt>'Content-Range'</tt>.
+ #
+ # Otherwise decompression (or not) depends on the value of header
+ # {Content-Encoding}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-encoding-response-header]:
+ #
+ # - <tt>'deflate'</tt>, <tt>'gzip'</tt>, or <tt>'x-gzip'</tt>:
+ # decompresses the body and deletes the header.
+ # - <tt>'none'</tt> or <tt>'identity'</tt>:
+ # does not decompress the body, but deletes the header.
+ # - Any other value:
+ # leaves the body and header unchanged.
+ #
+ # == What's Here
+ #
+ # This is a categorized summary of methods and attributes.
+ #
+ # === \Net::HTTP Objects
+ #
+ # - {::new}[rdoc-ref:Net::HTTP.new]:
+ # Creates a new instance.
+ # - {#inspect}[rdoc-ref:Net::HTTP#inspect]:
+ # Returns a string representation of +self+.
+ #
+ # === Sessions
+ #
+ # - {::start}[rdoc-ref:Net::HTTP.start]:
+ # Begins a new session in a new \Net::HTTP object.
+ # - {#started?}[rdoc-ref:Net::HTTP#started?]
+ # (aliased as {#active?}[rdoc-ref:Net::HTTP#active?]):
+ # Returns whether in a session.
+ # - {#finish}[rdoc-ref:Net::HTTP#finish]:
+ # Ends an active session.
+ # - {#start}[rdoc-ref:Net::HTTP#start]:
+ # Begins a new session in an existing \Net::HTTP object (+self+).
+ #
+ # === Connections
+ #
+ # - {:continue_timeout}[rdoc-ref:Net::HTTP#continue_timeout]:
+ # Returns the continue timeout.
+ # - {#continue_timeout=}[rdoc-ref:Net::HTTP#continue_timeout=]:
+ # Sets the continue timeout seconds.
+ # - {:keep_alive_timeout}[rdoc-ref:Net::HTTP#keep_alive_timeout]:
+ # Returns the keep-alive timeout.
+ # - {:keep_alive_timeout=}[rdoc-ref:Net::HTTP#keep_alive_timeout=]:
+ # Sets the keep-alive timeout.
+ # - {:max_retries}[rdoc-ref:Net::HTTP#max_retries]:
+ # Returns the maximum retries.
+ # - {#max_retries=}[rdoc-ref:Net::HTTP#max_retries=]:
+ # Sets the maximum retries.
+ # - {:open_timeout}[rdoc-ref:Net::HTTP#open_timeout]:
+ # Returns the open timeout.
+ # - {:open_timeout=}[rdoc-ref:Net::HTTP#open_timeout=]:
+ # Sets the open timeout.
+ # - {:read_timeout}[rdoc-ref:Net::HTTP#read_timeout]:
+ # Returns the open timeout.
+ # - {:read_timeout=}[rdoc-ref:Net::HTTP#read_timeout=]:
+ # Sets the read timeout.
+ # - {:ssl_timeout}[rdoc-ref:Net::HTTP#ssl_timeout]:
+ # Returns the ssl timeout.
+ # - {:ssl_timeout=}[rdoc-ref:Net::HTTP#ssl_timeout=]:
+ # Sets the ssl timeout.
+ # - {:write_timeout}[rdoc-ref:Net::HTTP#write_timeout]:
+ # Returns the write timeout.
+ # - {write_timeout=}[rdoc-ref:Net::HTTP#write_timeout=]:
+ # Sets the write timeout.
+ #
+ # === Requests
+ #
+ # - {::get}[rdoc-ref:Net::HTTP.get]:
+ # Sends a GET request and returns the string response body.
+ # - {::get_print}[rdoc-ref:Net::HTTP.get_print]:
+ # Sends a GET request and write the string response body to $stdout.
+ # - {::get_response}[rdoc-ref:Net::HTTP.get_response]:
+ # Sends a GET request and returns a response object.
+ # - {::post_form}[rdoc-ref:Net::HTTP.post_form]:
+ # Sends a POST request with form data and returns a response object.
+ # - {::post}[rdoc-ref:Net::HTTP.post]:
+ # Sends a POST request with data and returns a response object.
+ # - {#copy}[rdoc-ref:Net::HTTP#copy]:
+ # Sends a COPY request and returns a response object.
+ # - {#delete}[rdoc-ref:Net::HTTP#delete]:
+ # Sends a DELETE request and returns a response object.
+ # - {#get}[rdoc-ref:Net::HTTP#get]:
+ # Sends a GET request and returns a response object.
+ # - {#head}[rdoc-ref:Net::HTTP#head]:
+ # Sends a HEAD request and returns a response object.
+ # - {#lock}[rdoc-ref:Net::HTTP#lock]:
+ # Sends a LOCK request and returns a response object.
+ # - {#mkcol}[rdoc-ref:Net::HTTP#mkcol]:
+ # Sends a MKCOL request and returns a response object.
+ # - {#move}[rdoc-ref:Net::HTTP#move]:
+ # Sends a MOVE request and returns a response object.
+ # - {#options}[rdoc-ref:Net::HTTP#options]:
+ # Sends a OPTIONS request and returns a response object.
+ # - {#patch}[rdoc-ref:Net::HTTP#patch]:
+ # Sends a PATCH request and returns a response object.
+ # - {#post}[rdoc-ref:Net::HTTP#post]:
+ # Sends a POST request and returns a response object.
+ # - {#propfind}[rdoc-ref:Net::HTTP#propfind]:
+ # Sends a PROPFIND request and returns a response object.
+ # - {#proppatch}[rdoc-ref:Net::HTTP#proppatch]:
+ # Sends a PROPPATCH request and returns a response object.
+ # - {#put}[rdoc-ref:Net::HTTP#put]:
+ # Sends a PUT request and returns a response object.
+ # - {#request}[rdoc-ref:Net::HTTP#request]:
+ # Sends a request and returns a response object.
+ # - {#request_get}[rdoc-ref:Net::HTTP#request_get]
+ # (aliased as {#get2}[rdoc-ref:Net::HTTP#get2]):
+ # Sends a GET request and forms a response object;
+ # if a block given, calls the block with the object,
+ # otherwise returns the object.
+ # - {#request_head}[rdoc-ref:Net::HTTP#request_head]
+ # (aliased as {#head2}[rdoc-ref:Net::HTTP#head2]):
+ # Sends a HEAD request and forms a response object;
+ # if a block given, calls the block with the object,
+ # otherwise returns the object.
+ # - {#request_post}[rdoc-ref:Net::HTTP#request_post]
+ # (aliased as {#post2}[rdoc-ref:Net::HTTP#post2]):
+ # Sends a POST request and forms a response object;
+ # if a block given, calls the block with the object,
+ # otherwise returns the object.
+ # - {#send_request}[rdoc-ref:Net::HTTP#send_request]:
+ # Sends a request and returns a response object.
+ # - {#trace}[rdoc-ref:Net::HTTP#trace]:
+ # Sends a TRACE request and returns a response object.
+ # - {#unlock}[rdoc-ref:Net::HTTP#unlock]:
+ # Sends an UNLOCK request and returns a response object.
+ #
+ # === Responses
+ #
+ # - {:close_on_empty_response}[rdoc-ref:Net::HTTP#close_on_empty_response]:
+ # Returns whether to close connection on empty response.
+ # - {:close_on_empty_response=}[rdoc-ref:Net::HTTP#close_on_empty_response=]:
+ # Sets whether to close connection on empty response.
+ # - {:ignore_eof}[rdoc-ref:Net::HTTP#ignore_eof]:
+ # Returns whether to ignore end-of-file when reading a response body
+ # with <tt>Content-Length</tt> headers.
+ # - {:ignore_eof=}[rdoc-ref:Net::HTTP#ignore_eof=]:
+ # Sets whether to ignore end-of-file when reading a response body
+ # with <tt>Content-Length</tt> headers.
+ # - {:response_body_encoding}[rdoc-ref:Net::HTTP#response_body_encoding]:
+ # Returns the encoding to use for the response body.
+ # - {#response_body_encoding=}[rdoc-ref:Net::HTTP#response_body_encoding=]:
+ # Sets the response body encoding.
#
# === Proxies
#
- # Net::HTTP will automatically create a proxy from the +http_proxy+
- # environment variable if it is present. To disable use of +http_proxy+,
- # pass +nil+ for the proxy address.
- #
- # You may also create a custom proxy:
- #
- # proxy_addr = 'your.proxy.host'
- # proxy_port = 8080
- #
- # Net::HTTP.new('example.com', nil, proxy_addr, proxy_port).start { |http|
- # # always proxy via your.proxy.addr:8080
- # }
- #
- # See Net::HTTP.new for further details and examples such as proxies that
- # require a username and password.
- #
- # === Compression
- #
- # Net::HTTP automatically adds Accept-Encoding for compression of response
- # bodies and automatically decompresses gzip and deflate responses unless a
- # Range header was sent.
- #
- # Compression can be disabled through the Accept-Encoding: identity header.
- #
- # == HTTP Request Classes
- #
- # Here is the HTTP request class hierarchy.
- #
- # * Net::HTTPRequest
- # * Net::HTTP::Get
- # * Net::HTTP::Head
- # * Net::HTTP::Post
- # * Net::HTTP::Patch
- # * Net::HTTP::Put
- # * Net::HTTP::Proppatch
- # * Net::HTTP::Lock
- # * Net::HTTP::Unlock
- # * Net::HTTP::Options
- # * Net::HTTP::Propfind
- # * Net::HTTP::Delete
- # * Net::HTTP::Move
- # * Net::HTTP::Copy
- # * Net::HTTP::Mkcol
- # * Net::HTTP::Trace
- #
- # == HTTP Response Classes
- #
- # Here is HTTP response class hierarchy. All classes are defined in Net
- # module and are subclasses of Net::HTTPResponse.
- #
- # HTTPUnknownResponse:: For unhandled HTTP extensions
- # HTTPInformation:: 1xx
- # HTTPContinue:: 100
- # HTTPSwitchProtocol:: 101
- # HTTPProcessing:: 102
- # HTTPEarlyHints:: 103
- # HTTPSuccess:: 2xx
- # HTTPOK:: 200
- # HTTPCreated:: 201
- # HTTPAccepted:: 202
- # HTTPNonAuthoritativeInformation:: 203
- # HTTPNoContent:: 204
- # HTTPResetContent:: 205
- # HTTPPartialContent:: 206
- # HTTPMultiStatus:: 207
- # HTTPAlreadyReported:: 208
- # HTTPIMUsed:: 226
- # HTTPRedirection:: 3xx
- # HTTPMultipleChoices:: 300
- # HTTPMovedPermanently:: 301
- # HTTPFound:: 302
- # HTTPSeeOther:: 303
- # HTTPNotModified:: 304
- # HTTPUseProxy:: 305
- # HTTPTemporaryRedirect:: 307
- # HTTPPermanentRedirect:: 308
- # HTTPClientError:: 4xx
- # HTTPBadRequest:: 400
- # HTTPUnauthorized:: 401
- # HTTPPaymentRequired:: 402
- # HTTPForbidden:: 403
- # HTTPNotFound:: 404
- # HTTPMethodNotAllowed:: 405
- # HTTPNotAcceptable:: 406
- # HTTPProxyAuthenticationRequired:: 407
- # HTTPRequestTimeOut:: 408
- # HTTPConflict:: 409
- # HTTPGone:: 410
- # HTTPLengthRequired:: 411
- # HTTPPreconditionFailed:: 412
- # HTTPRequestEntityTooLarge:: 413
- # HTTPRequestURITooLong:: 414
- # HTTPUnsupportedMediaType:: 415
- # HTTPRequestedRangeNotSatisfiable:: 416
- # HTTPExpectationFailed:: 417
- # HTTPMisdirectedRequest:: 421
- # HTTPUnprocessableEntity:: 422
- # HTTPLocked:: 423
- # HTTPFailedDependency:: 424
- # HTTPUpgradeRequired:: 426
- # HTTPPreconditionRequired:: 428
- # HTTPTooManyRequests:: 429
- # HTTPRequestHeaderFieldsTooLarge:: 431
- # HTTPUnavailableForLegalReasons:: 451
- # HTTPServerError:: 5xx
- # HTTPInternalServerError:: 500
- # HTTPNotImplemented:: 501
- # HTTPBadGateway:: 502
- # HTTPServiceUnavailable:: 503
- # HTTPGatewayTimeOut:: 504
- # HTTPVersionNotSupported:: 505
- # HTTPVariantAlsoNegotiates:: 506
- # HTTPInsufficientStorage:: 507
- # HTTPLoopDetected:: 508
- # HTTPNotExtended:: 510
- # HTTPNetworkAuthenticationRequired:: 511
- #
- # There is also the Net::HTTPBadResponse exception which is raised when
- # there is a protocol error.
+ # - {:proxy_address}[rdoc-ref:Net::HTTP#proxy_address]:
+ # Returns the proxy address.
+ # - {:proxy_address=}[rdoc-ref:Net::HTTP#proxy_address=]:
+ # Sets the proxy address.
+ # - {::proxy_class?}[rdoc-ref:Net::HTTP.proxy_class?]:
+ # Returns whether +self+ is a proxy class.
+ # - {#proxy?}[rdoc-ref:Net::HTTP#proxy?]:
+ # Returns whether +self+ has a proxy.
+ # - {#proxy_address}[rdoc-ref:Net::HTTP#proxy_address]
+ # (aliased as {#proxyaddr}[rdoc-ref:Net::HTTP#proxyaddr]):
+ # Returns the proxy address.
+ # - {#proxy_from_env?}[rdoc-ref:Net::HTTP#proxy_from_env?]:
+ # Returns whether the proxy is taken from an environment variable.
+ # - {:proxy_from_env=}[rdoc-ref:Net::HTTP#proxy_from_env=]:
+ # Sets whether the proxy is to be taken from an environment variable.
+ # - {:proxy_pass}[rdoc-ref:Net::HTTP#proxy_pass]:
+ # Returns the proxy password.
+ # - {:proxy_pass=}[rdoc-ref:Net::HTTP#proxy_pass=]:
+ # Sets the proxy password.
+ # - {:proxy_port}[rdoc-ref:Net::HTTP#proxy_port]:
+ # Returns the proxy port.
+ # - {:proxy_port=}[rdoc-ref:Net::HTTP#proxy_port=]:
+ # Sets the proxy port.
+ # - {#proxy_user}[rdoc-ref:Net::HTTP#proxy_user]:
+ # Returns the proxy user name.
+ # - {:proxy_user=}[rdoc-ref:Net::HTTP#proxy_user=]:
+ # Sets the proxy user.
+ #
+ # === Security
+ #
+ # - {:ca_file}[rdoc-ref:Net::HTTP#ca_file]:
+ # Returns the path to a CA certification file.
+ # - {:ca_file=}[rdoc-ref:Net::HTTP#ca_file=]:
+ # Sets the path to a CA certification file.
+ # - {:ca_path}[rdoc-ref:Net::HTTP#ca_path]:
+ # Returns the path of to CA directory containing certification files.
+ # - {:ca_path=}[rdoc-ref:Net::HTTP#ca_path=]:
+ # Sets the path of to CA directory containing certification files.
+ # - {:cert}[rdoc-ref:Net::HTTP#cert]:
+ # Returns the OpenSSL::X509::Certificate object to be used for client certification.
+ # - {:cert=}[rdoc-ref:Net::HTTP#cert=]:
+ # Sets the OpenSSL::X509::Certificate object to be used for client certification.
+ # - {:cert_store}[rdoc-ref:Net::HTTP#cert_store]:
+ # Returns the X509::Store to be used for verifying peer certificate.
+ # - {:cert_store=}[rdoc-ref:Net::HTTP#cert_store=]:
+ # Sets the X509::Store to be used for verifying peer certificate.
+ # - {:ciphers}[rdoc-ref:Net::HTTP#ciphers]:
+ # Returns the available SSL ciphers.
+ # - {:ciphers=}[rdoc-ref:Net::HTTP#ciphers=]:
+ # Sets the available SSL ciphers.
+ # - {:extra_chain_cert}[rdoc-ref:Net::HTTP#extra_chain_cert]:
+ # Returns the extra X509 certificates to be added to the certificate chain.
+ # - {:extra_chain_cert=}[rdoc-ref:Net::HTTP#extra_chain_cert=]:
+ # Sets the extra X509 certificates to be added to the certificate chain.
+ # - {:key}[rdoc-ref:Net::HTTP#key]:
+ # Returns the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
+ # - {:key=}[rdoc-ref:Net::HTTP#key=]:
+ # Sets the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
+ # - {:max_version}[rdoc-ref:Net::HTTP#max_version]:
+ # Returns the maximum SSL version.
+ # - {:max_version=}[rdoc-ref:Net::HTTP#max_version=]:
+ # Sets the maximum SSL version.
+ # - {:min_version}[rdoc-ref:Net::HTTP#min_version]:
+ # Returns the minimum SSL version.
+ # - {:min_version=}[rdoc-ref:Net::HTTP#min_version=]:
+ # Sets the minimum SSL version.
+ # - {#peer_cert}[rdoc-ref:Net::HTTP#peer_cert]:
+ # Returns the X509 certificate chain for the session's socket peer.
+ # - {:ssl_version}[rdoc-ref:Net::HTTP#ssl_version]:
+ # Returns the SSL version.
+ # - {:ssl_version=}[rdoc-ref:Net::HTTP#ssl_version=]:
+ # Sets the SSL version.
+ # - {#use_ssl=}[rdoc-ref:Net::HTTP#use_ssl=]:
+ # Sets whether a new session is to use Transport Layer Security.
+ # - {#use_ssl?}[rdoc-ref:Net::HTTP#use_ssl?]:
+ # Returns whether +self+ uses SSL.
+ # - {:verify_callback}[rdoc-ref:Net::HTTP#verify_callback]:
+ # Returns the callback for the server certification verification.
+ # - {:verify_callback=}[rdoc-ref:Net::HTTP#verify_callback=]:
+ # Sets the callback for the server certification verification.
+ # - {:verify_depth}[rdoc-ref:Net::HTTP#verify_depth]:
+ # Returns the maximum depth for the certificate chain verification.
+ # - {:verify_depth=}[rdoc-ref:Net::HTTP#verify_depth=]:
+ # Sets the maximum depth for the certificate chain verification.
+ # - {:verify_hostname}[rdoc-ref:Net::HTTP#verify_hostname]:
+ # Returns the flags for server the certification verification at the beginning of the SSL/TLS session.
+ # - {:verify_hostname=}[rdoc-ref:Net::HTTP#verify_hostname=]:
+ # Sets he flags for server the certification verification at the beginning of the SSL/TLS session.
+ # - {:verify_mode}[rdoc-ref:Net::HTTP#verify_mode]:
+ # Returns the flags for server the certification verification at the beginning of the SSL/TLS session.
+ # - {:verify_mode=}[rdoc-ref:Net::HTTP#verify_mode=]:
+ # Sets the flags for server the certification verification at the beginning of the SSL/TLS session.
+ #
+ # === Addresses and Ports
+ #
+ # - {:address}[rdoc-ref:Net::HTTP#address]:
+ # Returns the string host name or host IP.
+ # - {::default_port}[rdoc-ref:Net::HTTP.default_port]:
+ # Returns integer 80, the default port to use for HTTP requests.
+ # - {::http_default_port}[rdoc-ref:Net::HTTP.http_default_port]:
+ # Returns integer 80, the default port to use for HTTP requests.
+ # - {::https_default_port}[rdoc-ref:Net::HTTP.https_default_port]:
+ # Returns integer 443, the default port to use for HTTPS requests.
+ # - {#ipaddr}[rdoc-ref:Net::HTTP#ipaddr]:
+ # Returns the IP address for the connection.
+ # - {#ipaddr=}[rdoc-ref:Net::HTTP#ipaddr=]:
+ # Sets the IP address for the connection.
+ # - {:local_host}[rdoc-ref:Net::HTTP#local_host]:
+ # Returns the string local host used to establish the connection.
+ # - {:local_host=}[rdoc-ref:Net::HTTP#local_host=]:
+ # Sets the string local host used to establish the connection.
+ # - {:local_port}[rdoc-ref:Net::HTTP#local_port]:
+ # Returns the integer local port used to establish the connection.
+ # - {:local_port=}[rdoc-ref:Net::HTTP#local_port=]:
+ # Sets the integer local port used to establish the connection.
+ # - {:port}[rdoc-ref:Net::HTTP#port]:
+ # Returns the integer port number.
+ #
+ # === \HTTP Version
+ #
+ # - {::version_1_2?}[rdoc-ref:Net::HTTP.version_1_2?]
+ # (aliased as {::is_version_1_2?}[rdoc-ref:Net::HTTP.is_version_1_2?]
+ # and {::version_1_2}[rdoc-ref:Net::HTTP.version_1_2]):
+ # Returns true; retained for compatibility.
+ #
+ # === Debugging
+ #
+ # - {#set_debug_output}[rdoc-ref:Net::HTTP#set_debug_output]:
+ # Sets the output stream for debugging.
#
class HTTP < Protocol
# :stopdoc:
- VERSION = "0.2.2"
- Revision = %q$Revision$.split[1]
+ VERSION = "0.4.1"
HTTPVersion = '1.1'
begin
require 'zlib'
@@ -408,18 +732,17 @@ module Net #:nodoc:
end
# :startdoc:
- # Turns on net/http 1.2 (Ruby 1.8) features.
- # Defaults to ON in Ruby 1.8 or later.
+ # Returns +true+; retained for compatibility.
def HTTP.version_1_2
true
end
- # Returns true if net/http is in version 1.2 mode.
- # Defaults to true.
+ # Returns +true+; retained for compatibility.
def HTTP.version_1_2?
true
end
+ # Returns +false+; retained for compatibility.
def HTTP.version_1_1? #:nodoc:
false
end
@@ -429,25 +752,12 @@ module Net #:nodoc:
alias is_version_1_2? version_1_2? #:nodoc:
end
+ # :call-seq:
+ # Net::HTTP.get_print(hostname, path, port = 80) -> nil
+ # Net::HTTP:get_print(uri, headers = {}, port = uri.port) -> nil
#
- # short cut methods
- #
-
- #
- # Gets the body text from the target and outputs it to $stdout. The
- # target can either be specified as
- # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so:
- #
- # Net::HTTP.get_print URI('http://www.example.com/index.html')
- #
- # or:
- #
- # Net::HTTP.get_print 'www.example.com', '/index.html'
- #
- # you can also specify request headers:
- #
- # Net::HTTP.get_print URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' }
- #
+ # Like Net::HTTP.get, but writes the returned body to $stdout;
+ # returns +nil+.
def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil)
get_response(uri_or_host, path_or_headers, port) {|res|
res.read_body do |chunk|
@@ -457,40 +767,48 @@ module Net #:nodoc:
nil
end
- # Sends a GET request to the target and returns the HTTP response
- # as a string. The target can either be specified as
- # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so:
- #
- # print Net::HTTP.get(URI('http://www.example.com/index.html'))
+ # :call-seq:
+ # Net::HTTP.get(hostname, path, port = 80) -> body
+ # Net::HTTP:get(uri, headers = {}, port = uri.port) -> body
#
- # or:
+ # Sends a GET request and returns the \HTTP response body as a string.
#
- # print Net::HTTP.get('www.example.com', '/index.html')
+ # With string arguments +hostname+ and +path+:
#
- # you can also specify request headers:
+ # hostname = 'jsonplaceholder.typicode.com'
+ # path = '/todos/1'
+ # puts Net::HTTP.get(hostname, path)
#
- # Net::HTTP.get(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' })
+ # Output:
#
- def HTTP.get(uri_or_host, path_or_headers = nil, port = nil)
- get_response(uri_or_host, path_or_headers, port).body
- end
-
- # Sends a GET request to the target and returns the HTTP response
- # as a Net::HTTPResponse object. The target can either be specified as
- # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so:
+ # {
+ # "userId": 1,
+ # "id": 1,
+ # "title": "delectus aut autem",
+ # "completed": false
+ # }
#
- # res = Net::HTTP.get_response(URI('http://www.example.com/index.html'))
- # print res.body
+ # With URI object +uri+ and optional hash argument +headers+:
#
- # or:
+ # uri = URI('https://jsonplaceholder.typicode.com/todos/1')
+ # headers = {'Content-type' => 'application/json; charset=UTF-8'}
+ # Net::HTTP.get(uri, headers)
#
- # res = Net::HTTP.get_response('www.example.com', '/index.html')
- # print res.body
+ # Related:
#
- # you can also specify request headers:
+ # - Net::HTTP::Get: request class for \HTTP method +GET+.
+ # - Net::HTTP#get: convenience method for \HTTP method +GET+.
#
- # Net::HTTP.get_response(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' })
+ def HTTP.get(uri_or_host, path_or_headers = nil, port = nil)
+ get_response(uri_or_host, path_or_headers, port).body
+ end
+
+ # :call-seq:
+ # Net::HTTP.get_response(hostname, path, port = 80) -> http_response
+ # Net::HTTP:get_response(uri, headers = {}, port = uri.port) -> http_response
#
+ # Like Net::HTTP.get, but returns a Net::HTTPResponse object
+ # instead of the body string.
def HTTP.get_response(uri_or_host, path_or_headers = nil, port = nil, &block)
if path_or_headers && !path_or_headers.is_a?(Hash)
host = uri_or_host
@@ -508,16 +826,31 @@ module Net #:nodoc:
end
end
- # Posts data to the specified URI object.
+ # Posts data to a host; returns a Net::HTTPResponse object.
#
- # Example:
+ # Argument +url+ must be a URL;
+ # argument +data+ must be a string:
+ #
+ # _uri = uri.dup
+ # _uri.path = '/posts'
+ # data = '{"title": "foo", "body": "bar", "userId": 1}'
+ # headers = {'content-type': 'application/json'}
+ # res = Net::HTTP.post(_uri, data, headers) # => #<Net::HTTPCreated 201 Created readbody=true>
+ # puts res.body
+ #
+ # Output:
#
- # require 'net/http'
- # require 'uri'
+ # {
+ # "title": "foo",
+ # "body": "bar",
+ # "userId": 1,
+ # "id": 101
+ # }
#
- # Net::HTTP.post URI('http://www.example.com/api/search'),
- # { "q" => "ruby", "max" => "50" }.to_json,
- # "Content-Type" => "application/json"
+ # Related:
+ #
+ # - Net::HTTP::Post: request class for \HTTP method +POST+.
+ # - Net::HTTP#post: convenience method for \HTTP method +POST+.
#
def HTTP.post(url, data, header = nil)
start(url.hostname, url.port,
@@ -526,22 +859,25 @@ module Net #:nodoc:
}
end
- # Posts HTML form data to the specified URI object.
- # The form data must be provided as a Hash mapping from String to String.
- # Example:
+ # Posts data to a host; returns a Net::HTTPResponse object.
#
- # { "cmd" => "search", "q" => "ruby", "max" => "50" }
+ # Argument +url+ must be a URI;
+ # argument +data+ must be a hash:
#
- # This method also does Basic Authentication if and only if +url+.user exists.
- # But userinfo for authentication is deprecated (RFC3986).
- # So this feature will be removed.
- #
- # Example:
+ # _uri = uri.dup
+ # _uri.path = '/posts'
+ # data = {title: 'foo', body: 'bar', userId: 1}
+ # res = Net::HTTP.post_form(_uri, data) # => #<Net::HTTPCreated 201 Created readbody=true>
+ # puts res.body
#
- # require 'net/http'
+ # Output:
#
- # Net::HTTP.post_form URI('http://www.example.com/search.cgi'),
- # { "q" => "ruby", "max" => "50" }
+ # {
+ # "title": "foo",
+ # "body": "bar",
+ # "userId": "1",
+ # "id": 101
+ # }
#
def HTTP.post_form(url, params)
req = Post.new(url)
@@ -554,20 +890,29 @@ module Net #:nodoc:
end
#
- # HTTP session management
+ # \HTTP session management
#
- # The default port to use for HTTP requests; defaults to 80.
+ # Returns integer +80+, the default port to use for \HTTP requests:
+ #
+ # Net::HTTP.default_port # => 80
+ #
def HTTP.default_port
http_default_port()
end
- # The default port to use for HTTP requests; defaults to 80.
+ # Returns integer +80+, the default port to use for \HTTP requests:
+ #
+ # Net::HTTP.http_default_port # => 80
+ #
def HTTP.http_default_port
80
end
- # The default port to use for HTTPS requests; defaults to 443.
+ # Returns integer +443+, the default port to use for HTTPS requests:
+ #
+ # Net::HTTP.https_default_port # => 443
+ #
def HTTP.https_default_port
443
end
@@ -577,35 +922,91 @@ module Net #:nodoc:
end
# :call-seq:
- # HTTP.start(address, port, p_addr, p_port, p_user, p_pass, &block)
- # HTTP.start(address, port=nil, p_addr=:ENV, p_port=nil, p_user=nil, p_pass=nil, opt, &block)
- #
- # Creates a new Net::HTTP object, then additionally opens the TCP
- # connection and HTTP session.
- #
- # Arguments are the following:
- # _address_ :: hostname or IP address of the server
- # _port_ :: port of the server
- # _p_addr_ :: address of proxy
- # _p_port_ :: port of proxy
- # _p_user_ :: user of proxy
- # _p_pass_ :: pass of proxy
- # _opt_ :: optional hash
- #
- # _opt_ sets following values by its accessor.
- # The keys are ipaddr, ca_file, ca_path, cert, cert_store, ciphers, keep_alive_timeout,
- # close_on_empty_response, key, open_timeout, read_timeout, write_timeout, ssl_timeout,
- # ssl_version, use_ssl, verify_callback, verify_depth and verify_mode.
- # If you set :use_ssl as true, you can use https and default value of
- # verify_mode is set as OpenSSL::SSL::VERIFY_PEER.
- #
- # If the optional block is given, the newly
- # created Net::HTTP object is passed to it and closed when the
- # block finishes. In this case, the return value of this method
- # is the return value of the block. If no block is given, the
- # return value of this method is the newly created Net::HTTP object
- # itself, and the caller is responsible for closing it upon completion
- # using the finish() method.
+ # HTTP.start(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, opts) -> http
+ # HTTP.start(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, opts) {|http| ... } -> object
+ #
+ # Creates a new \Net::HTTP object, +http+, via \Net::HTTP.new:
+ #
+ # - For arguments +address+ and +port+, see Net::HTTP.new.
+ # - For proxy-defining arguments +p_addr+ through +p_pass+,
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
+ # - For argument +opts+, see below.
+ #
+ # With no block given:
+ #
+ # - Calls <tt>http.start</tt> with no block (see #start),
+ # which opens a TCP connection and \HTTP session.
+ # - Returns +http+.
+ # - The caller should call #finish to close the session:
+ #
+ # http = Net::HTTP.start(hostname)
+ # http.started? # => true
+ # http.finish
+ # http.started? # => false
+ #
+ # With a block given:
+ #
+ # - Calls <tt>http.start</tt> with the block (see #start), which:
+ #
+ # - Opens a TCP connection and \HTTP session.
+ # - Calls the block,
+ # which may make any number of requests to the host.
+ # - Closes the \HTTP session and TCP connection on block exit.
+ # - Returns the block's value +object+.
+ #
+ # - Returns +object+.
+ #
+ # Example:
+ #
+ # hostname = 'jsonplaceholder.typicode.com'
+ # Net::HTTP.start(hostname) do |http|
+ # puts http.get('/todos/1').body
+ # puts http.get('/todos/2').body
+ # end
+ #
+ # Output:
+ #
+ # {
+ # "userId": 1,
+ # "id": 1,
+ # "title": "delectus aut autem",
+ # "completed": false
+ # }
+ # {
+ # "userId": 1,
+ # "id": 2,
+ # "title": "quis ut nam facilis et officia qui",
+ # "completed": false
+ # }
+ #
+ # If the last argument given is a hash, it is the +opts+ hash,
+ # where each key is a method or accessor to be called,
+ # and its value is the value to be set.
+ #
+ # The keys may include:
+ #
+ # - #ca_file
+ # - #ca_path
+ # - #cert
+ # - #cert_store
+ # - #ciphers
+ # - #close_on_empty_response
+ # - +ipaddr+ (calls #ipaddr=)
+ # - #keep_alive_timeout
+ # - #key
+ # - #open_timeout
+ # - #read_timeout
+ # - #ssl_timeout
+ # - #ssl_version
+ # - +use_ssl+ (calls #use_ssl=)
+ # - #verify_callback
+ # - #verify_depth
+ # - #verify_mode
+ # - #write_timeout
+ #
+ # Note: If +port+ is +nil+ and <tt>opts[:use_ssl]</tt> is a truthy value,
+ # the value passed to +new+ is Net::HTTP.https_default_port, not +port+.
+ #
def HTTP.start(address, *arg, &block) # :yield: +http+
arg.pop if opt = Hash.try_convert(arg[-1])
port, p_addr, p_port, p_user, p_pass = *arg
@@ -632,25 +1033,34 @@ module Net #:nodoc:
alias newobj new # :nodoc:
end
- # Creates a new Net::HTTP object without opening a TCP connection or
- # HTTP session.
+ # Returns a new \Net::HTTP object +http+
+ # (but does not open a TCP connection or \HTTP session).
+ #
+ # With only string argument +address+ given
+ # (and <tt>ENV['http_proxy']</tt> undefined or +nil+),
+ # the returned +http+:
+ #
+ # - Has the given address.
+ # - Has the default port number, Net::HTTP.default_port (80).
+ # - Has no proxy.
#
- # The +address+ should be a DNS hostname or IP address, the +port+ is the
- # port the server operates on. If no +port+ is given the default port for
- # HTTP or HTTPS is used.
+ # Example:
+ #
+ # http = Net::HTTP.new(hostname)
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
+ # http.address # => "jsonplaceholder.typicode.com"
+ # http.port # => 80
+ # http.proxy? # => false
+ #
+ # With integer argument +port+ also given,
+ # the returned +http+ has the given port:
#
- # If none of the +p_+ arguments are given, the proxy host and port are
- # taken from the +http_proxy+ environment variable (or its uppercase
- # equivalent) if present. If the proxy requires authentication you must
- # supply it by hand. See URI::Generic#find_proxy for details of proxy
- # detection from the environment. To disable proxy detection set +p_addr+
- # to nil.
+ # http = Net::HTTP.new(hostname, 8000)
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:8000 open=false>
+ # http.port # => 8000
#
- # If you are connecting to a custom proxy, +p_addr+ specifies the DNS name
- # or IP address of the proxy host, +p_port+ the port to use to access the
- # proxy, +p_user+ and +p_pass+ the username and password if authorization
- # is required to use the proxy, and p_no_proxy hosts which do not
- # use the proxy.
+ # For proxy-defining arguments +p_addr+ through +p_no_proxy+,
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
#
def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil)
http = super address, port
@@ -664,7 +1074,7 @@ module Net #:nodoc:
elsif p_addr == :ENV then
http.proxy_from_env = true
else
- if p_addr && p_no_proxy && !URI::Generic.use_proxy?(p_addr, p_addr, p_port, p_no_proxy)
+ if p_addr && p_no_proxy && !URI::Generic.use_proxy?(address, address, port, p_no_proxy)
p_addr = nil
p_port = nil
end
@@ -677,10 +1087,10 @@ module Net #:nodoc:
http
end
- # Creates a new Net::HTTP object for the specified server address,
- # without opening the TCP connection or initializing the HTTP session.
+ # Creates a new \Net::HTTP object for the specified server address,
+ # without opening the TCP connection or initializing the \HTTP session.
# The +address+ should be a DNS hostname or IP address.
- def initialize(address, port = nil)
+ def initialize(address, port = nil) # :nodoc:
@address = address
@port = (port || HTTP.default_port)
@ipaddr = nil
@@ -717,6 +1127,11 @@ module Net #:nodoc:
end
end
+ # Returns a string representation of +self+:
+ #
+ # Net::HTTP.new(hostname).inspect
+ # # => "#<Net::HTTP jsonplaceholder.typicode.com:80 open=false>"
+ #
def inspect
"#<#{self.class} #{@address}:#{@port} open=#{started?}>"
end
@@ -724,83 +1139,184 @@ module Net #:nodoc:
# *WARNING* This method opens a serious security hole.
# Never use this method in production code.
#
- # Sets an output stream for debugging.
+ # Sets the output stream for debugging:
#
# http = Net::HTTP.new(hostname)
- # http.set_debug_output $stderr
- # http.start { .... }
+ # File.open('t.tmp', 'w') do |file|
+ # http.set_debug_output(file)
+ # http.start
+ # http.get('/nosuch/1')
+ # http.finish
+ # end
+ # puts File.read('t.tmp')
+ #
+ # Output:
+ #
+ # opening connection to jsonplaceholder.typicode.com:80...
+ # opened
+ # <- "GET /nosuch/1 HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nHost: jsonplaceholder.typicode.com\r\n\r\n"
+ # -> "HTTP/1.1 404 Not Found\r\n"
+ # -> "Date: Mon, 12 Dec 2022 21:14:11 GMT\r\n"
+ # -> "Content-Type: application/json; charset=utf-8\r\n"
+ # -> "Content-Length: 2\r\n"
+ # -> "Connection: keep-alive\r\n"
+ # -> "X-Powered-By: Express\r\n"
+ # -> "X-Ratelimit-Limit: 1000\r\n"
+ # -> "X-Ratelimit-Remaining: 999\r\n"
+ # -> "X-Ratelimit-Reset: 1670879660\r\n"
+ # -> "Vary: Origin, Accept-Encoding\r\n"
+ # -> "Access-Control-Allow-Credentials: true\r\n"
+ # -> "Cache-Control: max-age=43200\r\n"
+ # -> "Pragma: no-cache\r\n"
+ # -> "Expires: -1\r\n"
+ # -> "X-Content-Type-Options: nosniff\r\n"
+ # -> "Etag: W/\"2-vyGp6PvFo4RvsFtPoIWeCReyIC8\"\r\n"
+ # -> "Via: 1.1 vegur\r\n"
+ # -> "CF-Cache-Status: MISS\r\n"
+ # -> "Server-Timing: cf-q-config;dur=1.3000000762986e-05\r\n"
+ # -> "Report-To: {\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=yOr40jo%2BwS1KHzhTlVpl54beJ5Wx2FcG4gGV0XVrh3X9OlR5q4drUn2dkt5DGO4GDcE%2BVXT7CNgJvGs%2BZleIyMu8CLieFiDIvOviOY3EhHg94m0ZNZgrEdpKD0S85S507l1vsEwEHkoTm%2Ff19SiO\"}],\"group\":\"cf-nel\",\"max_age\":604800}\r\n"
+ # -> "NEL: {\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}\r\n"
+ # -> "Server: cloudflare\r\n"
+ # -> "CF-RAY: 778977dc484ce591-DFW\r\n"
+ # -> "alt-svc: h3=\":443\"; ma=86400, h3-29=\":443\"; ma=86400\r\n"
+ # -> "\r\n"
+ # reading 2 bytes...
+ # -> "{}"
+ # read 2 bytes
+ # Conn keep-alive
#
def set_debug_output(output)
warn 'Net::HTTP#set_debug_output called after HTTP started', uplevel: 1 if started?
@debug_output = output
end
- # The DNS host name or IP address to connect to.
+ # Returns the string host name or host IP given as argument +address+ in ::new.
attr_reader :address
- # The port number to connect to.
+ # Returns the integer port number given as argument +port+ in ::new.
attr_reader :port
- # The local host used to establish the connection.
+ # Sets or returns the string local host used to establish the connection;
+ # initially +nil+.
attr_accessor :local_host
- # The local port used to establish the connection.
+ # Sets or returns the integer local port used to establish the connection;
+ # initially +nil+.
attr_accessor :local_port
- # The encoding to use for the response body. If Encoding, uses the
- # specified encoding. If other true value, tries to detect the response
- # body encoding.
+ # Returns the encoding to use for the response body;
+ # see #response_body_encoding=.
attr_reader :response_body_encoding
- # Set the encoding to use for the response body. If given a String, find
- # the related Encoding.
+ # Sets the encoding to be used for the response body;
+ # returns the encoding.
+ #
+ # The given +value+ may be:
+ #
+ # - An Encoding object.
+ # - The name of an encoding.
+ # - An alias for an encoding name.
+ #
+ # See {Encoding}[rdoc-ref:Encoding].
+ #
+ # Examples:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.response_body_encoding = Encoding::US_ASCII # => #<Encoding:US-ASCII>
+ # http.response_body_encoding = 'US-ASCII' # => "US-ASCII"
+ # http.response_body_encoding = 'ASCII' # => "ASCII"
+ #
def response_body_encoding=(value)
value = Encoding.find(value) if value.is_a?(String)
@response_body_encoding = value
end
+ # Sets whether to determine the proxy from environment variable
+ # '<tt>ENV['http_proxy']</tt>';
+ # see {Proxy Using ENV['http_proxy']}[rdoc-ref:Net::HTTP@Proxy+Using+-27ENV-5B-27http_proxy-27-5D-27].
attr_writer :proxy_from_env
+
+ # Sets the proxy address;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
attr_writer :proxy_address
+
+ # Sets the proxy port;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
attr_writer :proxy_port
+
+ # Sets the proxy user;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
attr_writer :proxy_user
+
+ # Sets the proxy password;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
attr_writer :proxy_pass
- # The IP address to connect to/used to connect to
+ # Returns the IP address for the connection.
+ #
+ # If the session has not been started,
+ # returns the value set by #ipaddr=,
+ # or +nil+ if it has not been set:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.ipaddr # => nil
+ # http.ipaddr = '172.67.155.76'
+ # http.ipaddr # => "172.67.155.76"
+ #
+ # If the session has been started,
+ # returns the IP address from the socket:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.start
+ # http.ipaddr # => "172.67.155.76"
+ # http.finish
+ #
def ipaddr
started? ? @socket.io.peeraddr[3] : @ipaddr
end
- # Set the IP address to connect to
+ # Sets the IP address for the connection:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.ipaddr # => nil
+ # http.ipaddr = '172.67.155.76'
+ # http.ipaddr # => "172.67.155.76"
+ #
+ # The IP address may not be set if the session has been started.
def ipaddr=(addr)
raise IOError, "ipaddr value changed, but session already started" if started?
@ipaddr = addr
end
- # Number of seconds to wait for the connection to open. Any number
- # may be used, including Floats for fractional seconds. If the HTTP
- # object cannot open a connection in this many seconds, it raises a
- # Net::OpenTimeout exception. The default value is 60 seconds.
+ # Sets or returns the numeric (\Integer or \Float) number of seconds
+ # to wait for a connection to open;
+ # initially 60.
+ # If the connection is not made in the given interval,
+ # an exception is raised.
attr_accessor :open_timeout
- # Number of seconds to wait for one block to be read (via one read(2)
- # call). Any number may be used, including Floats for fractional
- # seconds. If the HTTP object cannot read data in this many seconds,
- # it raises a Net::ReadTimeout exception. The default value is 60 seconds.
+ # Returns the numeric (\Integer or \Float) number of seconds
+ # to wait for one block to be read (via one read(2) call);
+ # see #read_timeout=.
attr_reader :read_timeout
- # Number of seconds to wait for one block to be written (via one write(2)
- # call). Any number may be used, including Floats for fractional
- # seconds. If the HTTP object cannot write data in this many seconds,
- # it raises a Net::WriteTimeout exception. The default value is 60 seconds.
- # Net::WriteTimeout is not raised on Windows.
+ # Returns the numeric (\Integer or \Float) number of seconds
+ # to wait for one block to be written (via one write(2) call);
+ # see #write_timeout=.
attr_reader :write_timeout
- # Maximum number of times to retry an idempotent request in case of
- # Net::ReadTimeout, IOError, EOFError, Errno::ECONNRESET,
+ # Sets the maximum number of times to retry an idempotent request in case of
+ # \Net::ReadTimeout, IOError, EOFError, Errno::ECONNRESET,
# Errno::ECONNABORTED, Errno::EPIPE, OpenSSL::SSL::SSLError,
# Timeout::Error.
- # Should be a non-negative integer number. Zero means no retries.
- # The default value is 1.
+ # The initial value is 1.
+ #
+ # Argument +retries+ must be a non-negative numeric value:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.max_retries = 2 # => 2
+ # http.max_retries # => 2
+ #
def max_retries=(retries)
retries = retries.to_int
if retries < 0
@@ -809,59 +1325,113 @@ module Net #:nodoc:
@max_retries = retries
end
+ # Returns the maximum number of times to retry an idempotent request;
+ # see #max_retries=.
attr_reader :max_retries
- # Setter for the read_timeout attribute.
+ # Sets the read timeout, in seconds, for +self+ to integer +sec+;
+ # the initial value is 60.
+ #
+ # Argument +sec+ must be a non-negative numeric value:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.read_timeout # => 60
+ # http.get('/todos/1') # => #<Net::HTTPOK 200 OK readbody=true>
+ # http.read_timeout = 0
+ # http.get('/todos/1') # Raises Net::ReadTimeout.
+ #
def read_timeout=(sec)
@socket.read_timeout = sec if @socket
@read_timeout = sec
end
- # Setter for the write_timeout attribute.
+ # Sets the write timeout, in seconds, for +self+ to integer +sec+;
+ # the initial value is 60.
+ #
+ # Argument +sec+ must be a non-negative numeric value:
+ #
+ # _uri = uri.dup
+ # _uri.path = '/posts'
+ # body = 'bar' * 200000
+ # data = <<EOF
+ # {"title": "foo", "body": "#{body}", "userId": "1"}
+ # EOF
+ # headers = {'content-type': 'application/json'}
+ # http = Net::HTTP.new(hostname)
+ # http.write_timeout # => 60
+ # http.post(_uri.path, data, headers)
+ # # => #<Net::HTTPCreated 201 Created readbody=true>
+ # http.write_timeout = 0
+ # http.post(_uri.path, data, headers) # Raises Net::WriteTimeout.
+ #
def write_timeout=(sec)
@socket.write_timeout = sec if @socket
@write_timeout = sec
end
- # Seconds to wait for 100 Continue response. If the HTTP object does not
- # receive a response in this many seconds it sends the request body. The
- # default value is +nil+.
+ # Returns the continue timeout value;
+ # see continue_timeout=.
attr_reader :continue_timeout
- # Setter for the continue_timeout attribute.
+ # Sets the continue timeout value,
+ # which is the number of seconds to wait for an expected 100 Continue response.
+ # If the \HTTP object does not receive a response in this many seconds
+ # it sends the request body.
def continue_timeout=(sec)
@socket.continue_timeout = sec if @socket
@continue_timeout = sec
end
- # Seconds to reuse the connection of the previous request.
- # If the idle time is less than this Keep-Alive Timeout,
- # Net::HTTP reuses the TCP/IP socket used by the previous communication.
- # The default value is 2 seconds.
+ # Sets or returns the numeric (\Integer or \Float) number of seconds
+ # to keep the connection open after a request is sent;
+ # initially 2.
+ # If a new request is made during the given interval,
+ # the still-open connection is used;
+ # otherwise the connection will have been closed
+ # and a new connection is opened.
attr_accessor :keep_alive_timeout
- # Whether to ignore EOF when reading response bodies with defined
- # Content-Length headers. For backwards compatibility, the default is true.
+ # Sets or returns whether to ignore end-of-file when reading a response body
+ # with <tt>Content-Length</tt> headers;
+ # initially +true+.
attr_accessor :ignore_eof
- # Returns true if the HTTP session has been started.
+ # Returns +true+ if the \HTTP session has been started:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.started? # => false
+ # http.start
+ # http.started? # => true
+ # http.finish # => nil
+ # http.started? # => false
+ #
+ # Net::HTTP.start(hostname) do |http|
+ # http.started?
+ # end # => true
+ # http.started? # => false
+ #
def started?
@started
end
alias active? started? #:nodoc: obsolete
+ # Sets or returns whether to close the connection when the response is empty;
+ # initially +false+.
attr_accessor :close_on_empty_response
- # Returns true if SSL/TLS is being used with HTTP.
+ # Returns +true+ if +self+ uses SSL, +false+ otherwise.
+ # See Net::HTTP#use_ssl=.
def use_ssl?
@use_ssl
end
- # Turn on/off SSL.
- # This flag must be set before starting session.
- # If you change use_ssl value after session started,
- # a Net::HTTP object raises IOError.
+ # Sets whether a new session is to use
+ # {Transport Layer Security}[https://en.wikipedia.org/wiki/Transport_Layer_Security]:
+ #
+ # Raises IOError if attempting to change during a session.
+ #
+ # Raises OpenSSL::SSL::SSLError if the port is not an HTTPS port.
def use_ssl=(flag)
flag = flag ? true : false
if started? and @use_ssl != flag
@@ -886,7 +1456,7 @@ module Net #:nodoc:
:@verify_depth,
:@verify_mode,
:@verify_hostname,
- ]
+ ] # :nodoc:
SSL_ATTRIBUTES = [
:ca_file,
:ca_path,
@@ -903,64 +1473,67 @@ module Net #:nodoc:
:verify_depth,
:verify_mode,
:verify_hostname,
- ]
+ ] # :nodoc:
- # Sets path of a CA certification file in PEM format.
- #
- # The file can contain several CA certificates.
+ # Sets or returns the path to a CA certification file in PEM format.
attr_accessor :ca_file
- # Sets path of a CA certification directory containing certifications in
- # PEM format.
+ # Sets or returns the path of to CA directory
+ # containing certification files in PEM format.
attr_accessor :ca_path
- # Sets an OpenSSL::X509::Certificate object as client certificate.
- # (This method is appeared in Michal Rokos's OpenSSL extension).
+ # Sets or returns the OpenSSL::X509::Certificate object
+ # to be used for client certification.
attr_accessor :cert
- # Sets the X509::Store to verify peer certificate.
+ # Sets or returns the X509::Store to be used for verifying peer certificate.
attr_accessor :cert_store
- # Sets the available ciphers. See OpenSSL::SSL::SSLContext#ciphers=
+ # Sets or returns the available SSL ciphers.
+ # See {OpenSSL::SSL::SSLContext#ciphers=}[rdoc-ref:OpenSSL::SSL::SSLContext#ciphers-3D].
attr_accessor :ciphers
- # Sets the extra X509 certificates to be added to the certificate chain.
- # See OpenSSL::SSL::SSLContext#extra_chain_cert=
+ # Sets or returns the extra X509 certificates to be added to the certificate chain.
+ # See {OpenSSL::SSL::SSLContext#add_certificate}[rdoc-ref:OpenSSL::SSL::SSLContext#add_certificate].
attr_accessor :extra_chain_cert
- # Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
- # (This method is appeared in Michal Rokos's OpenSSL extension.)
+ # Sets or returns the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
attr_accessor :key
- # Sets the SSL timeout seconds.
+ # Sets or returns the SSL timeout seconds.
attr_accessor :ssl_timeout
- # Sets the SSL version. See OpenSSL::SSL::SSLContext#ssl_version=
+ # Sets or returns the SSL version.
+ # See {OpenSSL::SSL::SSLContext#ssl_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#ssl_version-3D].
attr_accessor :ssl_version
- # Sets the minimum SSL version. See OpenSSL::SSL::SSLContext#min_version=
+ # Sets or returns the minimum SSL version.
+ # See {OpenSSL::SSL::SSLContext#min_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#min_version-3D].
attr_accessor :min_version
- # Sets the maximum SSL version. See OpenSSL::SSL::SSLContext#max_version=
+ # Sets or returns the maximum SSL version.
+ # See {OpenSSL::SSL::SSLContext#max_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#max_version-3D].
attr_accessor :max_version
- # Sets the verify callback for the server certification verification.
+ # Sets or returns the callback for the server certification verification.
attr_accessor :verify_callback
- # Sets the maximum depth for the certificate chain verification.
+ # Sets or returns the maximum depth for the certificate chain verification.
attr_accessor :verify_depth
- # Sets the flags for server the certification verification at beginning of
- # SSL/TLS session.
- #
+ # Sets or returns the flags for server the certification verification
+ # at the beginning of the SSL/TLS session.
# OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER are acceptable.
attr_accessor :verify_mode
- # Sets to check the server certificate is valid for the hostname.
- # See OpenSSL::SSL::SSLContext#verify_hostname=
+ # Sets or returns whether to verify that the server certificate is valid
+ # for the hostname.
+ # See {OpenSSL::SSL::SSLContext#verify_hostname=}[rdoc-ref:OpenSSL::SSL::SSLContext#attribute-i-verify_mode].
attr_accessor :verify_hostname
- # Returns the X.509 certificates the server presented.
+ # Returns the X509 certificate chain (an array of strings)
+ # for the session's socket peer,
+ # or +nil+ if none.
def peer_cert
if not use_ssl? or not @socket
return nil
@@ -968,14 +1541,26 @@ module Net #:nodoc:
@socket.io.peer_cert
end
- # Opens a TCP connection and HTTP session.
+ # Starts an \HTTP session.
#
- # When this method is called with a block, it passes the Net::HTTP
- # object to the block, and closes the TCP connection and HTTP session
- # after the block has been executed.
+ # Without a block, returns +self+:
#
- # When called with a block, it returns the return value of the
- # block; otherwise, it returns self.
+ # http = Net::HTTP.new(hostname)
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
+ # http.start
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=true>
+ # http.started? # => true
+ # http.finish
+ #
+ # With a block, calls the block with +self+,
+ # finishes the session when the block exits,
+ # and returns the block's value:
+ #
+ # http.start do |http|
+ # http
+ # end
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
+ # http.started? # => false
#
def start # :yield: http
raise IOError, 'HTTP session already opened' if @started
@@ -1013,13 +1598,14 @@ module Net #:nodoc:
end
debug "opening connection to #{conn_addr}:#{conn_port}..."
- begin
- s = Socket.tcp conn_addr, conn_port, @local_host, @local_port, connect_timeout: @open_timeout
- rescue => e
- e = Net::OpenTimeout.new(e) if e.is_a?(Errno::ETIMEDOUT) #for compatibility with previous versions
- raise e, "Failed to open TCP connection to " +
- "#{conn_addr}:#{conn_port} (#{e.message})"
- end
+ s = Timeout.timeout(@open_timeout, Net::OpenTimeout) {
+ begin
+ TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
+ rescue => e
+ raise e, "Failed to open TCP connection to " +
+ "#{conn_addr}:#{conn_port} (#{e.message})"
+ end
+ }
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
debug "opened"
if use_ssl?
@@ -1028,8 +1614,8 @@ module Net #:nodoc:
write_timeout: @write_timeout,
continue_timeout: @continue_timeout,
debug_output: @debug_output)
- buf = "CONNECT #{conn_address}:#{@port} HTTP/#{HTTPVersion}\r\n"
- buf << "Host: #{@address}:#{@port}\r\n"
+ buf = +"CONNECT #{conn_address}:#{@port} HTTP/#{HTTPVersion}\r\n" \
+ "Host: #{@address}:#{@port}\r\n"
if proxy_user
credential = ["#{proxy_user}:#{proxy_pass}"].pack('m0')
buf << "Proxy-Authorization: Basic #{credential}\r\n"
@@ -1110,8 +1696,15 @@ module Net #:nodoc:
end
private :on_connect
- # Finishes the HTTP session and closes the TCP connection.
- # Raises IOError if the session has not been started.
+ # Finishes the \HTTP session:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.start
+ # http.started? # => true
+ # http.finish # => nil
+ # http.started? # => false
+ #
+ # Raises IOError if not in a session.
def finish
raise IOError, 'HTTP session not yet started' unless started?
do_finish
@@ -1138,12 +1731,12 @@ module Net #:nodoc:
@proxy_user = nil
@proxy_pass = nil
- # Creates an HTTP proxy class which behaves like Net::HTTP, but
+ # Creates an \HTTP proxy class which behaves like \Net::HTTP, but
# performs all access via the specified proxy.
#
# This class is obsolete. You may pass these same parameters directly to
- # Net::HTTP.new. See Net::HTTP.new for details of the arguments.
- def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil)
+ # \Net::HTTP.new. See Net::HTTP.new for details of the arguments.
+ def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc:
return self unless p_addr
Class.new(self) {
@@ -1165,31 +1758,37 @@ module Net #:nodoc:
end
class << HTTP
- # returns true if self is a class which was created by HTTP::Proxy.
+ # Returns true if self is a class which was created by HTTP::Proxy.
def proxy_class?
defined?(@is_proxy_class) ? @is_proxy_class : false
end
- # Address of proxy host. If Net::HTTP does not use a proxy, nil.
+ # Returns the address of the proxy host, or +nil+ if none;
+ # see Net::HTTP@Proxy+Server.
attr_reader :proxy_address
- # Port number of proxy host. If Net::HTTP does not use a proxy, nil.
+ # Returns the port number of the proxy host, or +nil+ if none;
+ # see Net::HTTP@Proxy+Server.
attr_reader :proxy_port
- # User name for accessing proxy. If Net::HTTP does not use a proxy, nil.
+ # Returns the user name for accessing the proxy, or +nil+ if none;
+ # see Net::HTTP@Proxy+Server.
attr_reader :proxy_user
- # User password for accessing proxy. If Net::HTTP does not use a proxy,
- # nil.
+ # Returns the password for accessing the proxy, or +nil+ if none;
+ # see Net::HTTP@Proxy+Server.
attr_reader :proxy_pass
end
- # True if requests for this connection will be proxied
+ # Returns +true+ if a proxy server is defined, +false+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
def proxy?
!!(@proxy_from_env ? proxy_uri : @proxy_address)
end
- # True if the proxy for this connection is determined from the environment
+ # Returns +true+ if the proxy server is defined in the environment,
+ # +false+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
def proxy_from_env?
@proxy_from_env
end
@@ -1198,12 +1797,13 @@ module Net #:nodoc:
def proxy_uri # :nodoc:
return if @proxy_uri == false
@proxy_uri ||= URI::HTTP.new(
- "http".freeze, nil, address, port, nil, nil, nil, nil, nil
+ "http", nil, address, port, nil, nil, nil, nil, nil
).find_proxy || false
@proxy_uri || nil
end
- # The address of the proxy server, if one is configured.
+ # Returns the address of the proxy server, if defined, +nil+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
def proxy_address
if @proxy_from_env then
proxy_uri&.hostname
@@ -1212,7 +1812,8 @@ module Net #:nodoc:
end
end
- # The port of the proxy server, if one is configured.
+ # Returns the port number of the proxy server, if defined, +nil+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
def proxy_port
if @proxy_from_env then
proxy_uri&.port
@@ -1221,16 +1822,10 @@ module Net #:nodoc:
end
end
- # [Bug #12921]
- if /linux|freebsd|darwin/ =~ RUBY_PLATFORM
- ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE = true
- else
- ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE = false
- end
-
- # The username of the proxy server, if one is configured.
+ # Returns the user name of the proxy server, if defined, +nil+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
def proxy_user
- if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env
+ if @proxy_from_env
user = proxy_uri&.user
unescape(user) if user
else
@@ -1238,9 +1833,10 @@ module Net #:nodoc:
end
end
- # The password of the proxy server, if one is configured.
+ # Returns the password of the proxy server, if defined, +nil+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
def proxy_pass
- if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env
+ if @proxy_from_env
pass = proxy_uri&.password
unescape(pass) if pass
else
@@ -1286,45 +1882,38 @@ module Net #:nodoc:
public
- # Retrieves data from +path+ on the connected-to host which may be an
- # absolute path String or a URI to extract the path from.
+ # :call-seq:
+ # get(path, initheader = nil) {|res| ... }
+ #
+ # Sends a GET request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Get object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # With a block given, calls the block with the response body:
#
- # +initheader+ must be a Hash like { 'Accept' => '*/*', ... },
- # and it defaults to an empty hash.
- # If +initheader+ doesn't have the key 'accept-encoding', then
- # a value of "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" is used,
- # so that gzip compression is used in preference to deflate
- # compression, which is used in preference to no compression.
- # Ruby doesn't have libraries to support the compress (Lempel-Ziv)
- # compression, so that is not supported. The intent of this is
- # to reduce bandwidth by default. If this routine sets up
- # compression, then it does the decompression also, removing
- # the header as well to prevent confusion. Otherwise
- # it leaves the body as it found it.
+ # http = Net::HTTP.new(hostname)
+ # http.get('/todos/1') do |res|
+ # p res
+ # end # => #<Net::HTTPOK 200 OK readbody=true>
#
- # This method returns a Net::HTTPResponse object.
+ # Output:
#
- # If called with a block, yields each fragment of the
- # entity body in turn as a string as it is read from
- # the socket. Note that in this case, the returned response
- # object will *not* contain a (meaningful) body.
+ # "{\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"delectus aut autem\",\n \"completed\": false\n}"
#
- # +dest+ argument is obsolete.
- # It still works but you must not use it.
+ # With no block given, simply returns the response object:
#
- # This method never raises an exception.
+ # http.get('/') # => #<Net::HTTPOK 200 OK readbody=true>
#
- # response = http.get('/index.html')
+ # Related:
#
- # # using block
- # File.open('result.txt', 'w') {|f|
- # http.get('/~foo/') do |str|
- # f.write str
- # end
- # }
+ # - Net::HTTP::Get: request class for \HTTP method GET.
+ # - Net::HTTP.get: sends GET request, returns response body.
#
def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+
res = nil
+
request(Get.new(path, initheader)) {|r|
r.read_body dest, &block
res = r
@@ -1332,198 +1921,312 @@ module Net #:nodoc:
res
end
- # Gets only the header from +path+ on the connected-to host.
- # +header+ is a Hash like { 'Accept' => '*/*', ... }.
+ # Sends a HEAD request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
#
- # This method returns a Net::HTTPResponse object.
+ # The request is based on the Net::HTTP::Head object
+ # created from string +path+ and initial headers hash +initheader+:
#
- # This method never raises an exception.
- #
- # response = nil
- # Net::HTTP.start('some.www.server', 80) {|http|
- # response = http.head('/index.html')
- # }
- # p response['content-type']
+ # res = http.head('/todos/1') # => #<Net::HTTPOK 200 OK readbody=true>
+ # res.body # => nil
+ # res.to_hash.take(3)
+ # # =>
+ # [["date", ["Wed, 15 Feb 2023 15:25:42 GMT"]],
+ # ["content-type", ["application/json; charset=utf-8"]],
+ # ["connection", ["close"]]]
#
def head(path, initheader = nil)
request(Head.new(path, initheader))
end
- # Posts +data+ (must be a String) to +path+. +header+ must be a Hash
- # like { 'Accept' => '*/*', ... }.
+ # :call-seq:
+ # post(path, data, initheader = nil) {|res| ... }
#
- # This method returns a Net::HTTPResponse object.
+ # Sends a POST request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
#
- # If called with a block, yields each fragment of the
- # entity body in turn as a string as it is read from
- # the socket. Note that in this case, the returned response
- # object will *not* contain a (meaningful) body.
+ # The request is based on the Net::HTTP::Post object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
#
- # +dest+ argument is obsolete.
- # It still works but you must not use it.
+ # With a block given, calls the block with the response body:
#
- # This method never raises exception.
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.post('/todos', data) do |res|
+ # p res
+ # end # => #<Net::HTTPCreated 201 Created readbody=true>
+ #
+ # Output:
+ #
+ # "{\n \"{\\\"userId\\\": 1, \\\"id\\\": 1, \\\"title\\\": \\\"delectus aut autem\\\", \\\"completed\\\": false}\": \"\",\n \"id\": 201\n}"
+ #
+ # With no block given, simply returns the response object:
#
- # response = http.post('/cgi-bin/search.rb', 'query=foo')
+ # http.post('/todos', data) # => #<Net::HTTPCreated 201 Created readbody=true>
#
- # # using block
- # File.open('result.txt', 'w') {|f|
- # http.post('/cgi-bin/search.rb', 'query=foo') do |str|
- # f.write str
- # end
- # }
+ # Related:
#
- # You should set Content-Type: header field for POST.
- # If no Content-Type: field given, this method uses
- # "application/x-www-form-urlencoded" by default.
+ # - Net::HTTP::Post: request class for \HTTP method POST.
+ # - Net::HTTP.post: sends POST request, returns response body.
#
def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
send_entity(path, data, initheader, dest, Post, &block)
end
- # Sends a PATCH request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # :call-seq:
+ # patch(path, data, initheader = nil) {|res| ... }
+ #
+ # Sends a PATCH request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Patch object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
+ #
+ # With a block given, calls the block with the response body:
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.patch('/todos/1', data) do |res|
+ # p res
+ # end # => #<Net::HTTPOK 200 OK readbody=true>
+ #
+ # Output:
+ #
+ # "{\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"delectus aut autem\",\n \"completed\": false,\n \"{\\\"userId\\\": 1, \\\"id\\\": 1, \\\"title\\\": \\\"delectus aut autem\\\", \\\"completed\\\": false}\": \"\"\n}"
+ #
+ # With no block given, simply returns the response object:
+ #
+ # http.patch('/todos/1', data) # => #<Net::HTTPCreated 201 Created readbody=true>
+ #
def patch(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
send_entity(path, data, initheader, dest, Patch, &block)
end
- def put(path, data, initheader = nil) #:nodoc:
+ # Sends a PUT request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Put object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.put('/todos/1', data) # => #<Net::HTTPOK 200 OK readbody=true>
+ #
+ def put(path, data, initheader = nil)
request(Put.new(path, initheader), data)
end
- # Sends a PROPPATCH request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a PROPPATCH request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Proppatch object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.proppatch('/todos/1', data)
+ #
def proppatch(path, body, initheader = nil)
request(Proppatch.new(path, initheader), body)
end
- # Sends a LOCK request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a LOCK request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Lock object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.lock('/todos/1', data)
+ #
def lock(path, body, initheader = nil)
request(Lock.new(path, initheader), body)
end
- # Sends a UNLOCK request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends an UNLOCK request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Unlock object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.unlock('/todos/1', data)
+ #
def unlock(path, body, initheader = nil)
request(Unlock.new(path, initheader), body)
end
- # Sends a OPTIONS request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends an Options request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Options object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.options('/')
+ #
def options(path, initheader = nil)
request(Options.new(path, initheader))
end
- # Sends a PROPFIND request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a PROPFIND request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Propfind object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.propfind('/todos/1', data)
+ #
def propfind(path, body = nil, initheader = {'Depth' => '0'})
request(Propfind.new(path, initheader), body)
end
- # Sends a DELETE request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a DELETE request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Delete object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.delete('/todos/1')
+ #
def delete(path, initheader = {'Depth' => 'Infinity'})
request(Delete.new(path, initheader))
end
- # Sends a MOVE request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a MOVE request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Move object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.move('/todos/1')
+ #
def move(path, initheader = nil)
request(Move.new(path, initheader))
end
- # Sends a COPY request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a COPY request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Copy object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.copy('/todos/1')
+ #
def copy(path, initheader = nil)
request(Copy.new(path, initheader))
end
- # Sends a MKCOL request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a MKCOL request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Mkcol object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http.mkcol('/todos/1', data)
+ # http = Net::HTTP.new(hostname)
+ #
def mkcol(path, body = nil, initheader = nil)
request(Mkcol.new(path, initheader), body)
end
- # Sends a TRACE request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a TRACE request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Trace object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.trace('/todos/1')
+ #
def trace(path, initheader = nil)
request(Trace.new(path, initheader))
end
- # Sends a GET request to the +path+.
- # Returns the response as a Net::HTTPResponse object.
+ # Sends a GET request to the server;
+ # forms the response into a Net::HTTPResponse object.
#
- # When called with a block, passes an HTTPResponse object to the block.
- # The body of the response will not have been read yet;
- # the block can process it using HTTPResponse#read_body,
- # if desired.
+ # The request is based on the Net::HTTP::Get object
+ # created from string +path+ and initial headers hash +initheader+.
#
- # Returns the response.
+ # With no block given, returns the response object:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.request_get('/todos') # => #<Net::HTTPOK 200 OK readbody=true>
#
- # This method never raises Net::* exceptions.
+ # With a block given, calls the block with the response object
+ # and returns the response object:
#
- # response = http.request_get('/index.html')
- # # The entity body is already read in this case.
- # p response['content-type']
- # puts response.body
+ # http.request_get('/todos') do |res|
+ # p res
+ # end # => #<Net::HTTPOK 200 OK readbody=true>
#
- # # Using a block
- # http.request_get('/index.html') {|response|
- # p response['content-type']
- # response.read_body do |str| # read body now
- # print str
- # end
- # }
+ # Output:
+ #
+ # #<Net::HTTPOK 200 OK readbody=false>
#
def request_get(path, initheader = nil, &block) # :yield: +response+
request(Get.new(path, initheader), &block)
end
- # Sends a HEAD request to the +path+ and returns the response
- # as a Net::HTTPResponse object.
- #
- # Returns the response.
+ # Sends a HEAD request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
#
- # This method never raises Net::* exceptions.
+ # The request is based on the Net::HTTP::Head object
+ # created from string +path+ and initial headers hash +initheader+.
#
- # response = http.request_head('/index.html')
- # p response['content-type']
+ # http = Net::HTTP.new(hostname)
+ # http.head('/todos/1') # => #<Net::HTTPOK 200 OK readbody=true>
#
def request_head(path, initheader = nil, &block)
request(Head.new(path, initheader), &block)
end
- # Sends a POST request to the +path+.
+ # Sends a POST request to the server;
+ # forms the response into a Net::HTTPResponse object.
#
- # Returns the response as a Net::HTTPResponse object.
+ # The request is based on the Net::HTTP::Post object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
#
- # When called with a block, the block is passed an HTTPResponse
- # object. The body of that response will not have been read yet;
- # the block can process it using HTTPResponse#read_body, if desired.
+ # With no block given, returns the response object:
#
- # Returns the response.
+ # http = Net::HTTP.new(hostname)
+ # http.post('/todos', 'xyzzy')
+ # # => #<Net::HTTPCreated 201 Created readbody=true>
+ #
+ # With a block given, calls the block with the response body
+ # and returns the response object:
#
- # This method never raises Net::* exceptions.
+ # http.post('/todos', 'xyzzy') do |res|
+ # p res
+ # end # => #<Net::HTTPCreated 201 Created readbody=true>
#
- # # example
- # response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...')
- # p response.status
- # puts response.body # body is already read in this case
+ # Output:
#
- # # using block
- # http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response|
- # p response.status
- # p response['content-type']
- # response.read_body do |str| # read body now
- # print str
- # end
- # }
+ # "{\n \"xyzzy\": \"\",\n \"id\": 201\n}"
#
def request_post(path, data, initheader = nil, &block) # :yield: +response+
request Post.new(path, initheader), data, &block
end
+ # Sends a PUT request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Put object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.put('/todos/1', 'xyzzy')
+ # # => #<Net::HTTPOK 200 OK readbody=true>
+ #
def request_put(path, data, initheader = nil, &block) #:nodoc:
request Put.new(path, initheader), data, &block
end
@@ -1533,16 +2236,25 @@ module Net #:nodoc:
alias post2 request_post #:nodoc: obsolete
alias put2 request_put #:nodoc: obsolete
-
- # Sends an HTTP request to the HTTP server.
- # Also sends a DATA string if +data+ is given.
+ # Sends an \HTTP request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
#
- # Returns a Net::HTTPResponse object.
+ # The request is based on the Net::HTTPRequest object
+ # created from string +path+, string +data+, and initial headers hash +header+.
+ # That object is an instance of the
+ # {subclass of Net::HTTPRequest}[rdoc-ref:Net::HTTPRequest@Request+Subclasses],
+ # that corresponds to the given uppercase string +name+,
+ # which must be
+ # an {HTTP request method}[https://en.wikipedia.org/wiki/HTTP#Request_methods]
+ # or a {WebDAV request method}[https://en.wikipedia.org/wiki/WebDAV#Implementation].
#
- # This method never raises Net::* exceptions.
+ # Examples:
#
- # response = http.send_request('GET', '/index.html')
- # puts response.body
+ # http = Net::HTTP.new(hostname)
+ # http.send_request('GET', '/todos/1')
+ # # => #<Net::HTTPOK 200 OK readbody=true>
+ # http.send_request('POST', '/todos', 'xyzzy')
+ # # => #<Net::HTTPCreated 201 Created readbody=true>
#
def send_request(name, path, data = nil, header = nil)
has_response_body = name != 'HEAD'
@@ -1550,20 +2262,35 @@ module Net #:nodoc:
request r, data
end
- # Sends an HTTPRequest object +req+ to the HTTP server.
+ # Sends the given request +req+ to the server;
+ # forms the response into a Net::HTTPResponse object.
+ #
+ # The given +req+ must be an instance of a
+ # {subclass of Net::HTTPRequest}[rdoc-ref:Net::HTTPRequest@Request+Subclasses].
+ # Argument +body+ should be given only if needed for the request.
+ #
+ # With no block given, returns the response object:
+ #
+ # http = Net::HTTP.new(hostname)
+ #
+ # req = Net::HTTP::Get.new('/todos/1')
+ # http.request(req)
+ # # => #<Net::HTTPOK 200 OK readbody=true>
+ #
+ # req = Net::HTTP::Post.new('/todos')
+ # http.request(req, 'xyzzy')
+ # # => #<Net::HTTPCreated 201 Created readbody=true>
#
- # If +req+ is a Net::HTTP::Post or Net::HTTP::Put request containing
- # data, the data is also sent. Providing data for a Net::HTTP::Head or
- # Net::HTTP::Get request results in an ArgumentError.
+ # With a block given, calls the block with the response and returns the response:
#
- # Returns an HTTPResponse object.
+ # req = Net::HTTP::Get.new('/todos/1')
+ # http.request(req) do |res|
+ # p res
+ # end # => #<Net::HTTPOK 200 OK readbody=true>
#
- # When called with a block, passes an HTTPResponse object to the block.
- # The body of the response will not have been read yet;
- # the block can process it using HTTPResponse#read_body,
- # if desired.
+ # Output:
#
- # This method never raises Net::* exceptions.
+ # #<Net::HTTPOK 200 OK readbody=false>
#
def request(req, body = nil, &block) # :yield: +response+
unless started?
diff --git a/lib/net/http/backward.rb b/lib/net/http/backward.rb
index 691e41e4f1..b44577edbd 100644
--- a/lib/net/http/backward.rb
+++ b/lib/net/http/backward.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# for backward compatibility
# :enddoc:
diff --git a/lib/net/http/exceptions.rb b/lib/net/http/exceptions.rb
index 9c425cae16..ceec8f7b0a 100644
--- a/lib/net/http/exceptions.rb
+++ b/lib/net/http/exceptions.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Net
# Net::HTTP exception class.
# You cannot use Net::HTTPExceptions directly; instead, you must use
diff --git a/lib/net/http/generic_request.rb b/lib/net/http/generic_request.rb
index 313de6ac92..44e329a0c8 100644
--- a/lib/net/http/generic_request.rb
+++ b/lib/net/http/generic_request.rb
@@ -1,24 +1,29 @@
-# frozen_string_literal: false
-# HTTPGenericRequest is the parent of the Net::HTTPRequest class.
-# Do not use this directly; use a subclass of Net::HTTPRequest.
+# frozen_string_literal: true
#
-# Mixes in the Net::HTTPHeader module to provide easier access to HTTP headers.
+# \HTTPGenericRequest is the parent of the Net::HTTPRequest class.
+#
+# Do not use this directly; instead, use a subclass of Net::HTTPRequest.
+#
+# == About the Examples
+#
+# :include: doc/net-http/examples.rdoc
#
class Net::HTTPGenericRequest
include Net::HTTPHeader
- def initialize(m, reqbody, resbody, uri_or_path, initheader = nil)
+ def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) # :nodoc:
@method = m
@request_has_body = reqbody
@response_has_body = resbody
if URI === uri_or_path then
raise ArgumentError, "not an HTTP URI" unless URI::HTTP === uri_or_path
- raise ArgumentError, "no host component for URI" unless uri_or_path.hostname
+ hostname = uri_or_path.hostname
+ raise ArgumentError, "no host component for URI" unless (hostname && hostname.length > 0)
@uri = uri_or_path.dup
host = @uri.hostname.dup
- host << ":".freeze << @uri.port.to_s if @uri.port != @uri.default_port
+ host << ":" << @uri.port.to_s if @uri.port != @uri.default_port
@path = uri_or_path.request_uri
raise ArgumentError, "no HTTP request path given" unless @path
else
@@ -52,15 +57,47 @@ class Net::HTTPGenericRequest
@body_data = nil
end
+ # Returns the string method name for the request:
+ #
+ # Net::HTTP::Get.new(uri).method # => "GET"
+ # Net::HTTP::Post.new(uri).method # => "POST"
+ #
attr_reader :method
+
+ # Returns the string path for the request:
+ #
+ # Net::HTTP::Get.new(uri).path # => "/"
+ # Net::HTTP::Post.new('example.com').path # => "example.com"
+ #
attr_reader :path
+
+ # Returns the URI object for the request, or +nil+ if none:
+ #
+ # Net::HTTP::Get.new(uri).uri
+ # # => #<URI::HTTPS https://jsonplaceholder.typicode.com/>
+ # Net::HTTP::Get.new('example.com').uri # => nil
+ #
attr_reader :uri
- # Automatically set to false if the user sets the Accept-Encoding header.
- # This indicates they wish to handle Content-encoding in responses
- # themselves.
+ # Returns +false+ if the request's header <tt>'Accept-Encoding'</tt>
+ # has been set manually or deleted
+ # (indicating that the user intends to handle encoding in the response),
+ # +true+ otherwise:
+ #
+ # req = Net::HTTP::Get.new(uri) # => #<Net::HTTP::Get GET>
+ # req['Accept-Encoding'] # => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
+ # req.decode_content # => true
+ # req['Accept-Encoding'] = 'foo'
+ # req.decode_content # => false
+ # req.delete('Accept-Encoding')
+ # req.decode_content # => false
+ #
attr_reader :decode_content
+ # Returns a string representation of the request:
+ #
+ # Net::HTTP::Post.new(uri).inspect # => "#<Net::HTTP::Post POST>"
+ #
def inspect
"\#<#{self.class} #{@method}>"
end
@@ -75,21 +112,45 @@ class Net::HTTPGenericRequest
super key, val
end
+ # Returns whether the request may have a body:
+ #
+ # Net::HTTP::Post.new(uri).request_body_permitted? # => true
+ # Net::HTTP::Get.new(uri).request_body_permitted? # => false
+ #
def request_body_permitted?
@request_has_body
end
+ # Returns whether the response may have a body:
+ #
+ # Net::HTTP::Post.new(uri).response_body_permitted? # => true
+ # Net::HTTP::Head.new(uri).response_body_permitted? # => false
+ #
def response_body_permitted?
@response_has_body
end
- def body_exist?
+ def body_exist? # :nodoc:
warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?", uplevel: 1 if $VERBOSE
response_body_permitted?
end
+ # Returns the string body for the request, or +nil+ if there is none:
+ #
+ # req = Net::HTTP::Post.new(uri)
+ # req.body # => nil
+ # req.body = '{"title": "foo","body": "bar","userId": 1}'
+ # req.body # => "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}"
+ #
attr_reader :body
+ # Sets the body for the request:
+ #
+ # req = Net::HTTP::Post.new(uri)
+ # req.body # => nil
+ # req.body = '{"title": "foo","body": "bar","userId": 1}'
+ # req.body # => "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}"
+ #
def body=(str)
@body = str
@body_stream = nil
@@ -97,8 +158,24 @@ class Net::HTTPGenericRequest
str
end
+ # Returns the body stream object for the request, or +nil+ if there is none:
+ #
+ # req = Net::HTTP::Post.new(uri) # => #<Net::HTTP::Post POST>
+ # req.body_stream # => nil
+ # require 'stringio'
+ # req.body_stream = StringIO.new('xyzzy') # => #<StringIO:0x0000027d1e5affa8>
+ # req.body_stream # => #<StringIO:0x0000027d1e5affa8>
+ #
attr_reader :body_stream
+ # Sets the body stream for the request:
+ #
+ # req = Net::HTTP::Post.new(uri) # => #<Net::HTTP::Post POST>
+ # req.body_stream # => nil
+ # require 'stringio'
+ # req.body_stream = StringIO.new('xyzzy') # => #<StringIO:0x0000027d1e5affa8>
+ # req.body_stream # => #<StringIO:0x0000027d1e5affa8>
+ #
def body_stream=(input)
@body = nil
@body_stream = input
@@ -135,15 +212,15 @@ class Net::HTTPGenericRequest
return unless @uri
if ssl
- scheme = 'https'.freeze
+ scheme = 'https'
klass = URI::HTTPS
else
- scheme = 'http'.freeze
+ scheme = 'http'
klass = URI::HTTP
end
if host = self['host']
- host.sub!(/:.*/m, ''.freeze)
+ host.sub!(/:.*/m, '')
elsif host = @uri.host
else
host = addr
@@ -239,7 +316,7 @@ class Net::HTTPGenericRequest
boundary ||= SecureRandom.urlsafe_base64(40)
chunked_p = chunked?
- buf = ''
+ buf = +''
params.each do |key, value, h={}|
key = quote_string(key, charset)
filename =
@@ -324,7 +401,7 @@ class Net::HTTPGenericRequest
if /[\r\n]/ =~ reqline
raise ArgumentError, "A Request-Line must not contain CR or LF"
end
- buf = ""
+ buf = +''
buf << reqline << "\r\n"
each_capitalized do |k,v|
buf << "#{k}: #{v}\r\n"
diff --git a/lib/net/http/header.rb b/lib/net/http/header.rb
index b0ec4b0625..6660c8075a 100644
--- a/lib/net/http/header.rb
+++ b/lib/net/http/header.rb
@@ -1,16 +1,188 @@
-# frozen_string_literal: false
-# The HTTPHeader module defines methods for reading and writing
-# HTTP headers.
+# frozen_string_literal: true
#
-# It is used as a mixin by other classes, to provide hash-like
-# access to HTTP header values. Unlike raw hash access, HTTPHeader
-# provides access via case-insensitive keys. It also provides
-# methods for accessing commonly-used HTTP header values in more
-# convenient formats.
+# The \HTTPHeader module provides access to \HTTP headers.
+#
+# The module is included in:
+#
+# - Net::HTTPGenericRequest (and therefore Net::HTTPRequest).
+# - Net::HTTPResponse.
+#
+# The headers are a hash-like collection of key/value pairs called _fields_.
+#
+# == Request and Response Fields
+#
+# Headers may be included in:
+#
+# - A Net::HTTPRequest object:
+# the object's headers will be sent with the request.
+# Any fields may be defined in the request;
+# see {Setters}[rdoc-ref:Net::HTTPHeader@Setters].
+# - A Net::HTTPResponse object:
+# the objects headers are usually those returned from the host.
+# Fields may be retrieved from the object;
+# see {Getters}[rdoc-ref:Net::HTTPHeader@Getters]
+# and {Iterators}[rdoc-ref:Net::HTTPHeader@Iterators].
+#
+# Exactly which fields should be sent or expected depends on the host;
+# see:
+#
+# - {Request fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields].
+# - {Response fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Response_fields].
+#
+# == About the Examples
+#
+# :include: doc/net-http/examples.rdoc
+#
+# == Fields
+#
+# A header field is a key/value pair.
+#
+# === Field Keys
+#
+# A field key may be:
+#
+# - A string: Key <tt>'Accept'</tt> is treated as if it were
+# <tt>'Accept'.downcase</tt>; i.e., <tt>'accept'</tt>.
+# - A symbol: Key <tt>:Accept</tt> is treated as if it were
+# <tt>:Accept.to_s.downcase</tt>; i.e., <tt>'accept'</tt>.
+#
+# Examples:
+#
+# req = Net::HTTP::Get.new(uri)
+# req[:accept] # => "*/*"
+# req['Accept'] # => "*/*"
+# req['ACCEPT'] # => "*/*"
+#
+# req['accept'] = 'text/html'
+# req[:accept] = 'text/html'
+# req['ACCEPT'] = 'text/html'
+#
+# === Field Values
+#
+# A field value may be returned as an array of strings or as a string:
+#
+# - These methods return field values as arrays:
+#
+# - #get_fields: Returns the array value for the given key,
+# or +nil+ if it does not exist.
+# - #to_hash: Returns a hash of all header fields:
+# each key is a field name; its value is the array value for the field.
+#
+# - These methods return field values as string;
+# the string value for a field is equivalent to
+# <tt>self[key.downcase.to_s].join(', '))</tt>:
+#
+# - #[]: Returns the string value for the given key,
+# or +nil+ if it does not exist.
+# - #fetch: Like #[], but accepts a default value
+# to be returned if the key does not exist.
+#
+# The field value may be set:
+#
+# - #[]=: Sets the value for the given key;
+# the given value may be a string, a symbol, an array, or a hash.
+# - #add_field: Adds a given value to a value for the given key
+# (not overwriting the existing value).
+# - #delete: Deletes the field for the given key.
+#
+# Example field values:
+#
+# - \String:
+#
+# req['Accept'] = 'text/html' # => "text/html"
+# req['Accept'] # => "text/html"
+# req.get_fields('Accept') # => ["text/html"]
+#
+# - \Symbol:
+#
+# req['Accept'] = :text # => :text
+# req['Accept'] # => "text"
+# req.get_fields('Accept') # => ["text"]
+#
+# - Simple array:
+#
+# req[:foo] = %w[bar baz bat]
+# req[:foo] # => "bar, baz, bat"
+# req.get_fields(:foo) # => ["bar", "baz", "bat"]
+#
+# - Simple hash:
+#
+# req[:foo] = {bar: 0, baz: 1, bat: 2}
+# req[:foo] # => "bar, 0, baz, 1, bat, 2"
+# req.get_fields(:foo) # => ["bar", "0", "baz", "1", "bat", "2"]
+#
+# - Nested:
+#
+# req[:foo] = [%w[bar baz], {bat: 0, bam: 1}]
+# req[:foo] # => "bar, baz, bat, 0, bam, 1"
+# req.get_fields(:foo) # => ["bar", "baz", "bat", "0", "bam", "1"]
+#
+# req[:foo] = {bar: %w[baz bat], bam: {bah: 0, bad: 1}}
+# req[:foo] # => "bar, baz, bat, bam, bah, 0, bad, 1"
+# req.get_fields(:foo) # => ["bar", "baz", "bat", "bam", "bah", "0", "bad", "1"]
+#
+# == Convenience Methods
+#
+# Various convenience methods retrieve values, set values, query values,
+# set form values, or iterate over fields.
+#
+# === Setters
+#
+# \Method #[]= can set any field, but does little to validate the new value;
+# some of the other setter methods provide some validation:
+#
+# - #[]=: Sets the string or array value for the given key.
+# - #add_field: Creates or adds to the array value for the given key.
+# - #basic_auth: Sets the string authorization header for <tt>'Authorization'</tt>.
+# - #content_length=: Sets the integer length for field <tt>'Content-Length</tt>.
+# - #content_type=: Sets the string value for field <tt>'Content-Type'</tt>.
+# - #proxy_basic_auth: Sets the string authorization header for <tt>'Proxy-Authorization'</tt>.
+# - #set_range: Sets the value for field <tt>'Range'</tt>.
+#
+# === Form Setters
+#
+# - #set_form: Sets an HTML form data set.
+# - #set_form_data: Sets header fields and a body from HTML form data.
+#
+# === Getters
+#
+# \Method #[] can retrieve the value of any field that exists,
+# but always as a string;
+# some of the other getter methods return something different
+# from the simple string value:
+#
+# - #[]: Returns the string field value for the given key.
+# - #content_length: Returns the integer value of field <tt>'Content-Length'</tt>.
+# - #content_range: Returns the Range value of field <tt>'Content-Range'</tt>.
+# - #content_type: Returns the string value of field <tt>'Content-Type'</tt>.
+# - #fetch: Returns the string field value for the given key.
+# - #get_fields: Returns the array field value for the given +key+.
+# - #main_type: Returns first part of the string value of field <tt>'Content-Type'</tt>.
+# - #sub_type: Returns second part of the string value of field <tt>'Content-Type'</tt>.
+# - #range: Returns an array of Range objects of field <tt>'Range'</tt>, or +nil+.
+# - #range_length: Returns the integer length of the range given in field <tt>'Content-Range'</tt>.
+# - #type_params: Returns the string parameters for <tt>'Content-Type'</tt>.
+#
+# === Queries
+#
+# - #chunked?: Returns whether field <tt>'Transfer-Encoding'</tt> is set to <tt>'chunked'</tt>.
+# - #connection_close?: Returns whether field <tt>'Connection'</tt> is set to <tt>'close'</tt>.
+# - #connection_keep_alive?: Returns whether field <tt>'Connection'</tt> is set to <tt>'keep-alive'</tt>.
+# - #key?: Returns whether a given key exists.
+#
+# === Iterators
+#
+# - #each_capitalized: Passes each field capitalized-name/value pair to the block.
+# - #each_capitalized_name: Passes each capitalized field name to the block.
+# - #each_header: Passes each field name/value pair to the block.
+# - #each_name: Passes each field name to the block.
+# - #each_value: Passes each string field value to the block.
#
module Net::HTTPHeader
+ MAX_KEY_LENGTH = 1024
+ MAX_FIELD_LENGTH = 65536
- def initialize_http_header(initheader)
+ def initialize_http_header(initheader) #:nodoc:
@header = {}
return unless initheader
initheader.each do |key, value|
@@ -19,6 +191,12 @@ module Net::HTTPHeader
warn "net/http: nil HTTP header: #{key}", uplevel: 3 if $VERBOSE
else
value = value.strip # raise error for invalid byte sequences
+ if key.to_s.bytesize > MAX_KEY_LENGTH
+ raise ArgumentError, "too long (#{key.bytesize} bytes) header: #{key[0, 30].inspect}..."
+ end
+ if value.to_s.bytesize > MAX_FIELD_LENGTH
+ raise ArgumentError, "header #{key} has too long field value: #{value.bytesize}"
+ end
if value.count("\r\n") > 0
raise ArgumentError, "header #{key} has field value #{value.inspect}, this cannot include CR/LF"
end
@@ -33,14 +211,32 @@ module Net::HTTPHeader
alias length size #:nodoc: obsolete
- # Returns the header field corresponding to the case-insensitive key.
- # For example, a key of "Content-Type" might return "text/html"
+ # Returns the string field value for the case-insensitive field +key+,
+ # or +nil+ if there is no such key;
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['Connection'] # => "keep-alive"
+ # res['Nosuch'] # => nil
+ #
+ # Note that some field values may be retrieved via convenience methods;
+ # see {Getters}[rdoc-ref:Net::HTTPHeader@Getters].
def [](key)
a = @header[key.downcase.to_s] or return nil
a.join(', ')
end
- # Sets the header field corresponding to the case-insensitive key.
+ # Sets the value for the case-insensitive +key+ to +val+,
+ # overwriting the previous value if the field exists;
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req['Accept'] # => "*/*"
+ # req['Accept'] = 'text/html'
+ # req['Accept'] # => "text/html"
+ #
+ # Note that some field values may be set via convenience methods;
+ # see {Setters}[rdoc-ref:Net::HTTPHeader@Setters].
def []=(key, val)
unless val
@header.delete key.downcase.to_s
@@ -49,20 +245,18 @@ module Net::HTTPHeader
set_field(key, val)
end
- # [Ruby 1.8.3]
- # Adds a value to a named header field, instead of replacing its value.
- # Second argument +val+ must be a String.
- # See also #[]=, #[] and #get_fields.
+ # Adds value +val+ to the value array for field +key+ if the field exists;
+ # creates the field with the given +key+ and +val+ if it does not exist.
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
#
- # request.add_field 'X-My-Header', 'a'
- # p request['X-My-Header'] #=> "a"
- # p request.get_fields('X-My-Header') #=> ["a"]
- # request.add_field 'X-My-Header', 'b'
- # p request['X-My-Header'] #=> "a, b"
- # p request.get_fields('X-My-Header') #=> ["a", "b"]
- # request.add_field 'X-My-Header', 'c'
- # p request['X-My-Header'] #=> "a, b, c"
- # p request.get_fields('X-My-Header') #=> ["a", "b", "c"]
+ # req = Net::HTTP::Get.new(uri)
+ # req.add_field('Foo', 'bar')
+ # req['Foo'] # => "bar"
+ # req.add_field('Foo', 'baz')
+ # req['Foo'] # => "bar, baz"
+ # req.add_field('Foo', %w[baz bam])
+ # req['Foo'] # => "bar, baz, baz, bam"
+ # req.get_fields('Foo') # => ["bar", "baz", "baz", "bam"]
#
def add_field(key, val)
stringified_downcased_key = key.downcase.to_s
@@ -101,16 +295,13 @@ module Net::HTTPHeader
end
end
- # [Ruby 1.8.3]
- # Returns an array of header field strings corresponding to the
- # case-insensitive +key+. This method allows you to get duplicated
- # header fields without any processing. See also #[].
+ # Returns the array field value for the given +key+,
+ # or +nil+ if there is no such field;
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
#
- # p response.get_fields('Set-Cookie')
- # #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23",
- # "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"]
- # p response['Set-Cookie']
- # #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res.get_fields('Connection') # => ["keep-alive"]
+ # res.get_fields('Nosuch') # => nil
#
def get_fields(key)
stringified_downcased_key = key.downcase.to_s
@@ -118,24 +309,58 @@ module Net::HTTPHeader
@header[stringified_downcased_key].dup
end
- # Returns the header field corresponding to the case-insensitive key.
- # Returns the default value +args+, or the result of the block, or
- # raises an IndexError if there's no header field named +key+
- # See Hash#fetch
+ # call-seq:
+ # fetch(key, default_val = nil) {|key| ... } -> object
+ # fetch(key, default_val = nil) -> value or default_val
+ #
+ # With a block, returns the string value for +key+ if it exists;
+ # otherwise returns the value of the block;
+ # ignores the +default_val+;
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ #
+ # # Field exists; block not called.
+ # res.fetch('Connection') do |value|
+ # fail 'Cannot happen'
+ # end # => "keep-alive"
+ #
+ # # Field does not exist; block called.
+ # res.fetch('Nosuch') do |value|
+ # value.downcase
+ # end # => "nosuch"
+ #
+ # With no block, returns the string value for +key+ if it exists;
+ # otherwise, returns +default_val+ if it was given;
+ # otherwise raises an exception:
+ #
+ # res.fetch('Connection', 'Foo') # => "keep-alive"
+ # res.fetch('Nosuch', 'Foo') # => "Foo"
+ # res.fetch('Nosuch') # Raises KeyError.
+ #
def fetch(key, *args, &block) #:yield: +key+
a = @header.fetch(key.downcase.to_s, *args, &block)
a.kind_of?(Array) ? a.join(', ') : a
end
- # Iterates through the header names and values, passing in the name
- # and value to the code block supplied.
+ # Calls the block with each key/value pair:
#
- # Returns an enumerator if no block is given.
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res.each_header do |key, value|
+ # p [key, value] if key.start_with?('c')
+ # end
#
- # Example:
+ # Output:
#
- # response.header.each_header {|key,value| puts "#{key} = #{value}" }
+ # ["content-type", "application/json; charset=utf-8"]
+ # ["connection", "keep-alive"]
+ # ["cache-control", "max-age=43200"]
+ # ["cf-cache-status", "HIT"]
+ # ["cf-ray", "771d17e9bc542cf5-ORD"]
#
+ # Returns an enumerator if no block is given.
+ #
+ # Net::HTTPHeader#each is an alias for Net::HTTPHeader#each_header.
def each_header #:yield: +key+, +value+
block_given? or return enum_for(__method__) { @header.size }
@header.each do |k,va|
@@ -145,10 +370,24 @@ module Net::HTTPHeader
alias each each_header
- # Iterates through the header names in the header, passing
- # each header name to the code block.
+ # Calls the block with each field key:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res.each_key do |key|
+ # p key if key.start_with?('c')
+ # end
+ #
+ # Output:
+ #
+ # "content-type"
+ # "connection"
+ # "cache-control"
+ # "cf-cache-status"
+ # "cf-ray"
#
# Returns an enumerator if no block is given.
+ #
+ # Net::HTTPHeader#each_name is an alias for Net::HTTPHeader#each_key.
def each_name(&block) #:yield: +key+
block_given? or return enum_for(__method__) { @header.size }
@header.each_key(&block)
@@ -156,12 +395,23 @@ module Net::HTTPHeader
alias each_key each_name
- # Iterates through the header names in the header, passing
- # capitalized header names to the code block.
+ # Calls the block with each capitalized field name:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res.each_capitalized_name do |key|
+ # p key if key.start_with?('C')
+ # end
+ #
+ # Output:
#
- # Note that header names are capitalized systematically;
- # capitalization may not match that used by the remote HTTP
- # server in its response.
+ # "Content-Type"
+ # "Connection"
+ # "Cache-Control"
+ # "Cf-Cache-Status"
+ # "Cf-Ray"
+ #
+ # The capitalization is system-dependent;
+ # see {Case Mapping}[rdoc-ref:case_mapping.rdoc].
#
# Returns an enumerator if no block is given.
def each_capitalized_name #:yield: +key+
@@ -171,8 +421,18 @@ module Net::HTTPHeader
end
end
- # Iterates through header values, passing each value to the
- # code block.
+ # Calls the block with each string field value:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res.each_value do |value|
+ # p value if value.start_with?('c')
+ # end
+ #
+ # Output:
+ #
+ # "chunked"
+ # "cf-q-config;dur=6.0000002122251e-06"
+ # "cloudflare"
#
# Returns an enumerator if no block is given.
def each_value #:yield: +value+
@@ -182,32 +442,45 @@ module Net::HTTPHeader
end
end
- # Removes a header field, specified by case-insensitive key.
+ # Removes the header for the given case-insensitive +key+
+ # (see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]);
+ # returns the deleted value, or +nil+ if no such field exists:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req.delete('Accept') # => ["*/*"]
+ # req.delete('Nosuch') # => nil
+ #
def delete(key)
@header.delete(key.downcase.to_s)
end
- # true if +key+ header exists.
+ # Returns +true+ if the field for the case-insensitive +key+ exists, +false+ otherwise:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req.key?('Accept') # => true
+ # req.key?('Nosuch') # => false
+ #
def key?(key)
@header.key?(key.downcase.to_s)
end
- # Returns a Hash consisting of header names and array of values.
- # e.g.
- # {"cache-control" => ["private"],
- # "content-type" => ["text/html"],
- # "date" => ["Wed, 22 Jun 2005 22:11:50 GMT"]}
+ # Returns a hash of the key/value pairs:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req.to_hash
+ # # =>
+ # {"accept-encoding"=>["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],
+ # "accept"=>["*/*"],
+ # "user-agent"=>["Ruby"],
+ # "host"=>["jsonplaceholder.typicode.com"]}
+ #
def to_hash
@header.dup
end
- # As for #each_header, except the keys are provided in capitalized form.
+ # Like #each_header, but the keys are returned in capitalized form.
#
- # Note that header names are capitalized systematically;
- # capitalization may not match that used by the remote HTTP
- # server in its response.
- #
- # Returns an enumerator if no block is given.
+ # Net::HTTPHeader#canonical_each is an alias for Net::HTTPHeader#each_capitalized.
def each_capitalized
block_given? or return enum_for(__method__) { @header.size }
@header.each do |k,v|
@@ -222,8 +495,17 @@ module Net::HTTPHeader
end
private :capitalize
- # Returns an Array of Range objects which represent the Range:
- # HTTP header field, or +nil+ if there is no such header.
+ # Returns an array of Range objects that represent
+ # the value of field <tt>'Range'</tt>,
+ # or +nil+ if there is no such field;
+ # see {Range request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#range-request-header]:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req['Range'] = 'bytes=0-99,200-299,400-499'
+ # req.range # => [0..99, 200..299, 400..499]
+ # req.delete('Range')
+ # req.range # # => nil
+ #
def range
return nil unless @header['range']
@@ -266,14 +548,31 @@ module Net::HTTPHeader
result
end
- # Sets the HTTP Range: header.
- # Accepts either a Range object as a single argument,
- # or a beginning index and a length from that index.
- # Example:
+ # call-seq:
+ # set_range(length) -> length
+ # set_range(offset, length) -> range
+ # set_range(begin..length) -> range
+ #
+ # Sets the value for field <tt>'Range'</tt>;
+ # see {Range request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#range-request-header]:
+ #
+ # With argument +length+:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req.set_range(100) # => 100
+ # req['Range'] # => "bytes=0-99"
#
- # req.range = (0..1023)
- # req.set_range 0, 1023
+ # With arguments +offset+ and +length+:
#
+ # req.set_range(100, 100) # => 100...200
+ # req['Range'] # => "bytes=100-199"
+ #
+ # With argument +range+:
+ #
+ # req.set_range(100..199) # => 100..199
+ # req['Range'] # => "bytes=100-199"
+ #
+ # Net::HTTPHeader#range= is an alias for Net::HTTPHeader#set_range.
def set_range(r, e = nil)
unless r
@header.delete 'range'
@@ -305,8 +604,15 @@ module Net::HTTPHeader
alias range= set_range
- # Returns an Integer object which represents the HTTP Content-Length:
- # header field, or +nil+ if that field was not provided.
+ # Returns the value of field <tt>'Content-Length'</tt> as an integer,
+ # or +nil+ if there is no such field;
+ # see {Content-Length request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-length-request-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/nosuch/1')
+ # res.content_length # => 2
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res.content_length # => nil
+ #
def content_length
return nil unless key?('Content-Length')
len = self['Content-Length'].slice(/\d+/) or
@@ -314,6 +620,20 @@ module Net::HTTPHeader
len.to_i
end
+ # Sets the value of field <tt>'Content-Length'</tt> to the given numeric;
+ # see {Content-Length response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-length-response-header]:
+ #
+ # _uri = uri.dup
+ # hostname = _uri.hostname # => "jsonplaceholder.typicode.com"
+ # _uri.path = '/posts' # => "/posts"
+ # req = Net::HTTP::Post.new(_uri) # => #<Net::HTTP::Post POST>
+ # req.body = '{"title": "foo","body": "bar","userId": 1}'
+ # req.content_length = req.body.size # => 42
+ # req.content_type = 'application/json'
+ # res = Net::HTTP.start(hostname) do |http|
+ # http.request(req)
+ # end # => #<Net::HTTPCreated 201 Created readbody=true>
+ #
def content_length=(len)
unless len
@header.delete 'content-length'
@@ -322,20 +642,31 @@ module Net::HTTPHeader
@header['content-length'] = [len.to_i.to_s]
end
- # Returns "true" if the "transfer-encoding" header is present and
- # set to "chunked". This is an HTTP/1.1 feature, allowing
- # the content to be sent in "chunks" without at the outset
- # stating the entire content length.
+ # Returns +true+ if field <tt>'Transfer-Encoding'</tt>
+ # exists and has value <tt>'chunked'</tt>,
+ # +false+ otherwise;
+ # see {Transfer-Encoding response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#transfer-encoding-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['Transfer-Encoding'] # => "chunked"
+ # res.chunked? # => true
+ #
def chunked?
return false unless @header['transfer-encoding']
field = self['Transfer-Encoding']
(/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
end
- # Returns a Range object which represents the value of the Content-Range:
- # header field.
- # For a partial entity body, this indicates where this fragment
- # fits inside the full entity body, as range of byte offsets.
+ # Returns a Range object representing the value of field
+ # <tt>'Content-Range'</tt>, or +nil+ if no such field exists;
+ # see {Content-Range response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-range-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['Content-Range'] # => nil
+ # res['Content-Range'] = 'bytes 0-499/1000'
+ # res['Content-Range'] # => "bytes 0-499/1000"
+ # res.content_range # => 0..499
+ #
def content_range
return nil unless @header['content-range']
m = %r<\A\s*(\w+)\s+(\d+)-(\d+)/(\d+|\*)>.match(self['Content-Range']) or
@@ -344,32 +675,66 @@ module Net::HTTPHeader
m[2].to_i .. m[3].to_i
end
- # The length of the range represented in Content-Range: header.
+ # Returns the integer representing length of the value of field
+ # <tt>'Content-Range'</tt>, or +nil+ if no such field exists;
+ # see {Content-Range response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-range-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['Content-Range'] # => nil
+ # res['Content-Range'] = 'bytes 0-499/1000'
+ # res.range_length # => 500
+ #
def range_length
r = content_range() or return nil
r.end - r.begin + 1
end
- # Returns a content type string such as "text/html".
- # This method returns nil if Content-Type: header field does not exist.
+ # Returns the {media type}[https://en.wikipedia.org/wiki/Media_type]
+ # from the value of field <tt>'Content-Type'</tt>,
+ # or +nil+ if no such field exists;
+ # see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['content-type'] # => "application/json; charset=utf-8"
+ # res.content_type # => "application/json"
+ #
def content_type
- return nil unless main_type()
- if sub_type()
- then "#{main_type()}/#{sub_type()}"
- else main_type()
+ main = main_type()
+ return nil unless main
+
+ sub = sub_type()
+ if sub
+ "#{main}/#{sub}"
+ else
+ main
end
end
- # Returns a content type string such as "text".
- # This method returns nil if Content-Type: header field does not exist.
+ # Returns the leading ('type') part of the
+ # {media type}[https://en.wikipedia.org/wiki/Media_type]
+ # from the value of field <tt>'Content-Type'</tt>,
+ # or +nil+ if no such field exists;
+ # see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['content-type'] # => "application/json; charset=utf-8"
+ # res.main_type # => "application"
+ #
def main_type
return nil unless @header['content-type']
self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
end
- # Returns a content type string such as "html".
- # This method returns nil if Content-Type: header field does not exist
- # or sub-type is not given (e.g. "Content-Type: text").
+ # Returns the trailing ('subtype') part of the
+ # {media type}[https://en.wikipedia.org/wiki/Media_type]
+ # from the value of field <tt>'Content-Type'</tt>,
+ # or +nil+ if no such field exists;
+ # see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['content-type'] # => "application/json; charset=utf-8"
+ # res.sub_type # => "json"
+ #
def sub_type
return nil unless @header['content-type']
_, sub = *self['Content-Type'].split(';').first.to_s.split('/')
@@ -377,9 +742,14 @@ module Net::HTTPHeader
sub.strip
end
- # Any parameters specified for the content type, returned as a Hash.
- # For example, a header of Content-Type: text/html; charset=EUC-JP
- # would result in type_params returning {'charset' => 'EUC-JP'}
+ # Returns the trailing ('parameters') part of the value of field <tt>'Content-Type'</tt>,
+ # or +nil+ if no such field exists;
+ # see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['content-type'] # => "application/json; charset=utf-8"
+ # res.type_params # => {"charset"=>"utf-8"}
+ #
def type_params
result = {}
list = self['Content-Type'].to_s.split(';')
@@ -391,29 +761,54 @@ module Net::HTTPHeader
result
end
- # Sets the content type in an HTTP header.
- # The +type+ should be a full HTTP content type, e.g. "text/html".
- # The +params+ are an optional Hash of parameters to add after the
- # content type, e.g. {'charset' => 'iso-8859-1'}
+ # Sets the value of field <tt>'Content-Type'</tt>;
+ # returns the new value;
+ # see {Content-Type request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-request-header]:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req.set_content_type('application/json') # => ["application/json"]
+ #
+ # Net::HTTPHeader#content_type= is an alias for Net::HTTPHeader#set_content_type.
def set_content_type(type, params = {})
@header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
end
alias content_type= set_content_type
- # Set header fields and a body from HTML form data.
- # +params+ should be an Array of Arrays or
- # a Hash containing HTML form data.
- # Optional argument +sep+ means data record separator.
+ # Sets the request body to a URL-encoded string derived from argument +params+,
+ # and sets request header field <tt>'Content-Type'</tt>
+ # to <tt>'application/x-www-form-urlencoded'</tt>.
+ #
+ # The resulting request is suitable for HTTP request +POST+ or +PUT+.
+ #
+ # Argument +params+ must be suitable for use as argument +enum+ to
+ # {URI.encode_www_form}[rdoc-ref:URI.encode_www_form].
+ #
+ # With only argument +params+ given,
+ # sets the body to a URL-encoded string with the default separator <tt>'&'</tt>:
+ #
+ # req = Net::HTTP::Post.new('example.com')
+ #
+ # req.set_form_data(q: 'ruby', lang: 'en')
+ # req.body # => "q=ruby&lang=en"
+ # req['Content-Type'] # => "application/x-www-form-urlencoded"
#
- # Values are URL encoded as necessary and the content-type is set to
- # application/x-www-form-urlencoded
+ # req.set_form_data([['q', 'ruby'], ['lang', 'en']])
+ # req.body # => "q=ruby&lang=en"
#
- # Example:
- # http.form_data = {"q" => "ruby", "lang" => "en"}
- # http.form_data = {"q" => ["ruby", "perl"], "lang" => "en"}
- # http.set_form_data({"q" => "ruby", "lang" => "en"}, ';')
+ # req.set_form_data(q: ['ruby', 'perl'], lang: 'en')
+ # req.body # => "q=ruby&q=perl&lang=en"
#
+ # req.set_form_data([['q', 'ruby'], ['q', 'perl'], ['lang', 'en']])
+ # req.body # => "q=ruby&q=perl&lang=en"
+ #
+ # With string argument +sep+ also given,
+ # uses that string as the separator:
+ #
+ # req.set_form_data({q: 'ruby', lang: 'en'}, '|')
+ # req.body # => "q=ruby|lang=en"
+ #
+ # Net::HTTPHeader#form_data= is an alias for Net::HTTPHeader#set_form_data.
def set_form_data(params, sep = '&')
query = URI.encode_www_form(params)
query.gsub!(/&/, sep) if sep != '&'
@@ -423,53 +818,108 @@ module Net::HTTPHeader
alias form_data= set_form_data
- # Set an HTML form data set.
- # +params+ :: The form data to set, which should be an enumerable.
- # See below for more details.
- # +enctype+ :: The content type to use to encode the form submission,
- # which should be application/x-www-form-urlencoded or
- # multipart/form-data.
- # +formopt+ :: An options hash, supporting the following options:
- # :boundary :: The boundary of the multipart message. If
- # not given, a random boundary will be used.
- # :charset :: The charset of the form submission. All
- # field names and values of non-file fields
- # should be encoded with this charset.
- #
- # Each item of params should respond to +each+ and yield 2-3 arguments,
- # or an array of 2-3 elements. The arguments yielded should be:
- # * The name of the field.
- # * The value of the field, it should be a String or a File or IO-like.
- # * An options hash, supporting the following options, only
- # used for file uploads:
- # :filename :: The name of the file to use.
- # :content_type :: The content type of the uploaded file.
- #
- # Each item is a file field or a normal field.
- # If +value+ is a File object or the +opt+ hash has a :filename key,
- # the item is treated as a file field.
- #
- # If Transfer-Encoding is set as chunked, this sends the request using
- # chunked encoding. Because chunked encoding is HTTP/1.1 feature,
- # you should confirm that the server supports HTTP/1.1 before using
- # chunked encoding.
- #
- # Example:
- # req.set_form([["q", "ruby"], ["lang", "en"]])
- #
- # req.set_form({"f"=>File.open('/path/to/filename')},
- # "multipart/form-data",
- # charset: "UTF-8",
- # )
- #
- # req.set_form([["f",
- # File.open('/path/to/filename.bar'),
- # {filename: "other-filename.foo"}
- # ]],
- # "multipart/form-data",
- # )
- #
- # See also RFC 2388, RFC 2616, HTML 4.01, and HTML5
+ # Stores form data to be used in a +POST+ or +PUT+ request.
+ #
+ # The form data given in +params+ consists of zero or more fields;
+ # each field is:
+ #
+ # - A scalar value.
+ # - A name/value pair.
+ # - An IO stream opened for reading.
+ #
+ # Argument +params+ should be an
+ # {Enumerable}[rdoc-ref:Enumerable@Enumerable+in+Ruby+Classes]
+ # (method <tt>params.map</tt> will be called),
+ # and is often an array or hash.
+ #
+ # First, we set up a request:
+ #
+ # _uri = uri.dup
+ # _uri.path ='/posts'
+ # req = Net::HTTP::Post.new(_uri)
+ #
+ # <b>Argument +params+ As an Array</b>
+ #
+ # When +params+ is an array,
+ # each of its elements is a subarray that defines a field;
+ # the subarray may contain:
+ #
+ # - One string:
+ #
+ # req.set_form([['foo'], ['bar'], ['baz']])
+ #
+ # - Two strings:
+ #
+ # req.set_form([%w[foo 0], %w[bar 1], %w[baz 2]])
+ #
+ # - When argument +enctype+ (see below) is given as
+ # <tt>'multipart/form-data'</tt>:
+ #
+ # - A string name and an IO stream opened for reading:
+ #
+ # require 'stringio'
+ # req.set_form([['file', StringIO.new('Ruby is cool.')]])
+ #
+ # - A string name, an IO stream opened for reading,
+ # and an options hash, which may contain these entries:
+ #
+ # - +:filename+: The name of the file to use.
+ # - +:content_type+: The content type of the uploaded file.
+ #
+ # Example:
+ #
+ # req.set_form([['file', file, {filename: "other-filename.foo"}]]
+ #
+ # The various forms may be mixed:
+ #
+ # req.set_form(['foo', %w[bar 1], ['file', file]])
+ #
+ # <b>Argument +params+ As a Hash</b>
+ #
+ # When +params+ is a hash,
+ # each of its entries is a name/value pair that defines a field:
+ #
+ # - The name is a string.
+ # - The value may be:
+ #
+ # - +nil+.
+ # - Another string.
+ # - An IO stream opened for reading
+ # (only when argument +enctype+ -- see below -- is given as
+ # <tt>'multipart/form-data'</tt>).
+ #
+ # Examples:
+ #
+ # # Nil-valued fields.
+ # req.set_form({'foo' => nil, 'bar' => nil, 'baz' => nil})
+ #
+ # # String-valued fields.
+ # req.set_form({'foo' => 0, 'bar' => 1, 'baz' => 2})
+ #
+ # # IO-valued field.
+ # require 'stringio'
+ # req.set_form({'file' => StringIO.new('Ruby is cool.')})
+ #
+ # # Mixture of fields.
+ # req.set_form({'foo' => nil, 'bar' => 1, 'file' => file})
+ #
+ # Optional argument +enctype+ specifies the value to be given
+ # to field <tt>'Content-Type'</tt>, and must be one of:
+ #
+ # - <tt>'application/x-www-form-urlencoded'</tt> (the default).
+ # - <tt>'multipart/form-data'</tt>;
+ # see {RFC 7578}[https://www.rfc-editor.org/rfc/rfc7578].
+ #
+ # Optional argument +formopt+ is a hash of options
+ # (applicable only when argument +enctype+
+ # is <tt>'multipart/form-data'</tt>)
+ # that may include the following entries:
+ #
+ # - +:boundary+: The value is the boundary string for the multipart message.
+ # If not given, the boundary is a random string.
+ # See {Boundary}[https://www.rfc-editor.org/rfc/rfc7578#section-4.1].
+ # - +:charset+: Value is the character set for the form submission.
+ # Field names and values of non-file fields should be encoded with this charset.
#
def set_form(params, enctype='application/x-www-form-urlencoded', formopt={})
@body_data = params
@@ -485,12 +935,24 @@ module Net::HTTPHeader
end
end
- # Set the Authorization: header for "Basic" authorization.
+ # Sets header <tt>'Authorization'</tt> using the given
+ # +account+ and +password+ strings:
+ #
+ # req.basic_auth('my_account', 'my_password')
+ # req['Authorization']
+ # # => "Basic bXlfYWNjb3VudDpteV9wYXNzd29yZA=="
+ #
def basic_auth(account, password)
@header['authorization'] = [basic_encode(account, password)]
end
- # Set Proxy-Authorization: header for "Basic" authorization.
+ # Sets header <tt>'Proxy-Authorization'</tt> using the given
+ # +account+ and +password+ strings:
+ #
+ # req.proxy_basic_auth('my_account', 'my_password')
+ # req['Proxy-Authorization']
+ # # => "Basic bXlfYWNjb3VudDpteV9wYXNzd29yZA=="
+ #
def proxy_basic_auth(account, password)
@header['proxy-authorization'] = [basic_encode(account, password)]
end
@@ -500,6 +962,7 @@ module Net::HTTPHeader
end
private :basic_encode
+# Returns whether the HTTP session is to be closed.
def connection_close?
token = /(?:\A|,)\s*close\s*(?:\z|,)/i
@header['connection']&.grep(token) {return true}
@@ -507,6 +970,7 @@ module Net::HTTPHeader
false
end
+# Returns whether the HTTP session is to be kept alive.
def connection_keep_alive?
token = /(?:\A|,)\s*keep-alive\s*(?:\z|,)/i
@header['connection']&.grep(token) {return true}
diff --git a/lib/net/http/net-http.gemspec b/lib/net/http/net-http.gemspec
index a7f122fc0e..0021136793 100644
--- a/lib/net/http/net-http.gemspec
+++ b/lib/net/http/net-http.gemspec
@@ -2,9 +2,14 @@
name = File.basename(__FILE__, ".gemspec")
version = ["lib", Array.new(name.count("-")+1, "..").join("/")].find do |dir|
- break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
- /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
- end rescue nil
+ file = File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")
+ begin
+ break File.foreach(file, mode: "rb") do |line|
+ /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
+ end
+ rescue SystemCallError
+ next
+ end
end
Gem::Specification.new do |spec|
@@ -25,7 +30,7 @@ Gem::Specification.new do |spec|
# 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 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
+ `git ls-files -z 2>#{IO::NULL}`.split("\x0").reject { |f| f.match(%r{\A(?:(?:test|spec|features)/|\.git)}) }
end
spec.bindir = "exe"
spec.require_paths = ["lib"]
diff --git a/lib/net/http/proxy_delta.rb b/lib/net/http/proxy_delta.rb
index a2f770ebdb..e7d30def64 100644
--- a/lib/net/http/proxy_delta.rb
+++ b/lib/net/http/proxy_delta.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Net::HTTP::ProxyDelta #:nodoc: internal use only
private
diff --git a/lib/net/http/request.rb b/lib/net/http/request.rb
index 1e86f3e4b4..4a138572e9 100644
--- a/lib/net/http/request.rb
+++ b/lib/net/http/request.rb
@@ -1,8 +1,76 @@
-# frozen_string_literal: false
-# HTTP request class.
-# This class wraps together the request header and the request path.
-# You cannot use this class directly. Instead, you should use one of its
-# subclasses: Net::HTTP::Get, Net::HTTP::Post, Net::HTTP::Head.
+# frozen_string_literal: true
+
+# This class is the base class for \Net::HTTP request classes.
+# The class should not be used directly;
+# instead you should use its subclasses, listed below.
+#
+# == Creating a Request
+#
+# An request object may be created with either a URI or a string hostname:
+#
+# require 'net/http'
+# uri = URI('https://jsonplaceholder.typicode.com/')
+# req = Net::HTTP::Get.new(uri) # => #<Net::HTTP::Get GET>
+# req = Net::HTTP::Get.new(uri.hostname) # => #<Net::HTTP::Get GET>
+#
+# And with any of the subclasses:
+#
+# req = Net::HTTP::Head.new(uri) # => #<Net::HTTP::Head HEAD>
+# req = Net::HTTP::Post.new(uri) # => #<Net::HTTP::Post POST>
+# req = Net::HTTP::Put.new(uri) # => #<Net::HTTP::Put PUT>
+# # ...
+#
+# The new instance is suitable for use as the argument to Net::HTTP#request.
+#
+# == Request Headers
+#
+# A new request object has these header fields by default:
+#
+# req.to_hash
+# # =>
+# {"accept-encoding"=>["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],
+# "accept"=>["*/*"],
+# "user-agent"=>["Ruby"],
+# "host"=>["jsonplaceholder.typicode.com"]}
+#
+# See:
+#
+# - {Request header Accept-Encoding}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Accept-Encoding]
+# and {Compression and Decompression}[rdoc-ref:Net::HTTP@Compression+and+Decompression].
+# - {Request header Accept}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#accept-request-header].
+# - {Request header User-Agent}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#user-agent-request-header].
+# - {Request header Host}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#host-request-header].
+#
+# You can add headers or override default headers:
+#
+# # res = Net::HTTP::Get.new(uri, {'foo' => '0', 'bar' => '1'})
+#
+# This class (and therefore its subclasses) also includes (indirectly)
+# module Net::HTTPHeader, which gives access to its
+# {methods for setting headers}[rdoc-ref:Net::HTTPHeader@Setters].
+#
+# == Request Subclasses
+#
+# Subclasses for HTTP requests:
+#
+# - Net::HTTP::Get
+# - Net::HTTP::Head
+# - Net::HTTP::Post
+# - Net::HTTP::Put
+# - Net::HTTP::Delete
+# - Net::HTTP::Options
+# - Net::HTTP::Trace
+# - Net::HTTP::Patch
+#
+# Subclasses for WebDAV requests:
+#
+# - Net::HTTP::Propfind
+# - Net::HTTP::Proppatch
+# - Net::HTTP::Mkcol
+# - Net::HTTP::Copy
+# - Net::HTTP::Move
+# - Net::HTTP::Lock
+# - Net::HTTP::Unlock
#
class Net::HTTPRequest < Net::HTTPGenericRequest
# Creates an HTTP request object for +path+.
@@ -18,4 +86,3 @@ class Net::HTTPRequest < Net::HTTPGenericRequest
path, initheader
end
end
-
diff --git a/lib/net/http/requests.rb b/lib/net/http/requests.rb
index d4c80a3812..5724164205 100644
--- a/lib/net/http/requests.rb
+++ b/lib/net/http/requests.rb
@@ -1,67 +1,257 @@
-# frozen_string_literal: false
-#
+# frozen_string_literal: true
+
# HTTP/1.1 methods --- RFC2616
-#
-# See Net::HTTPGenericRequest for attributes and methods.
-# See Net::HTTP for usage examples.
+# \Class for representing
+# {HTTP method GET}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#GET_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Get.new(uri) # => #<Net::HTTP::Get GET>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: optional.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
+#
+# Related:
+#
+# - Net::HTTP.get: sends +GET+ request, returns response body.
+# - Net::HTTP#get: sends +GET+ request, returns response object.
+#
class Net::HTTP::Get < Net::HTTPRequest
METHOD = 'GET'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
-# See Net::HTTP for usage examples.
+# \Class for representing
+# {HTTP method HEAD}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#HEAD_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Head.new(uri) # => #<Net::HTTP::Head HEAD>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: optional.
+# - Response body: no.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
+#
+# Related:
+#
+# - Net::HTTP#head: sends +HEAD+ request, returns response object.
+#
class Net::HTTP::Head < Net::HTTPRequest
METHOD = 'HEAD'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = false
end
-# See Net::HTTPGenericRequest for attributes and methods.
-# See Net::HTTP for usage examples.
+# \Class for representing
+# {HTTP method POST}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#POST_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# uri.path = '/posts'
+# req = Net::HTTP::Post.new(uri) # => #<Net::HTTP::Post POST>
+# req.body = '{"title": "foo","body": "bar","userId": 1}'
+# req.content_type = 'application/json'
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: yes.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: no.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
+#
+# Related:
+#
+# - Net::HTTP.post: sends +POST+ request, returns response object.
+# - Net::HTTP#post: sends +POST+ request, returns response object.
+#
class Net::HTTP::Post < Net::HTTPRequest
METHOD = 'POST'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
-# See Net::HTTP for usage examples.
+# \Class for representing
+# {HTTP method PUT}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#PUT_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# uri.path = '/posts'
+# req = Net::HTTP::Put.new(uri) # => #<Net::HTTP::Put PUT>
+# req.body = '{"title": "foo","body": "bar","userId": 1}'
+# req.content_type = 'application/json'
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: yes.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
class Net::HTTP::Put < Net::HTTPRequest
METHOD = 'PUT'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
-# See Net::HTTP for usage examples.
+# \Class for representing
+# {HTTP method DELETE}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#DELETE_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# uri.path = '/posts/1'
+# req = Net::HTTP::Delete.new(uri) # => #<Net::HTTP::Delete DELETE>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: optional.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+# Related:
+#
+# - Net::HTTP#delete: sends +DELETE+ request, returns response object.
+#
class Net::HTTP::Delete < Net::HTTPRequest
METHOD = 'DELETE'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {HTTP method OPTIONS}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#OPTIONS_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Options.new(uri) # => #<Net::HTTP::Options OPTIONS>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: optional.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+# Related:
+#
+# - Net::HTTP#options: sends +OPTIONS+ request, returns response object.
+#
class Net::HTTP::Options < Net::HTTPRequest
METHOD = 'OPTIONS'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {HTTP method TRACE}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#TRACE_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Trace.new(uri) # => #<Net::HTTP::Trace TRACE>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: no.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+# Related:
+#
+# - Net::HTTP#trace: sends +TRACE+ request, returns response object.
+#
class Net::HTTP::Trace < Net::HTTPRequest
METHOD = 'TRACE'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = true
end
+# \Class for representing
+# {HTTP method PATCH}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#PATCH_method]:
#
-# PATCH method --- RFC5789
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# uri.path = '/posts'
+# req = Net::HTTP::Patch.new(uri) # => #<Net::HTTP::Patch PATCH>
+# req.body = '{"title": "foo","body": "bar","userId": 1}'
+# req.content_type = 'application/json'
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: yes.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: no.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+# Related:
+#
+# - Net::HTTP#patch: sends +PATCH+ request, returns response object.
#
-
-# See Net::HTTPGenericRequest for attributes and methods.
class Net::HTTP::Patch < Net::HTTPRequest
METHOD = 'PATCH'
REQUEST_HAS_BODY = true
@@ -72,49 +262,161 @@ end
# WebDAV methods --- RFC2518
#
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method PROPFIND}[http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Propfind.new(uri) # => #<Net::HTTP::Propfind PROPFIND>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#propfind: sends +PROPFIND+ request, returns response object.
+#
class Net::HTTP::Propfind < Net::HTTPRequest
METHOD = 'PROPFIND'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method PROPPATCH}[http://www.webdav.org/specs/rfc4918.html#METHOD_PROPPATCH]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Proppatch.new(uri) # => #<Net::HTTP::Proppatch PROPPATCH>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#proppatch: sends +PROPPATCH+ request, returns response object.
+#
class Net::HTTP::Proppatch < Net::HTTPRequest
METHOD = 'PROPPATCH'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method MKCOL}[http://www.webdav.org/specs/rfc4918.html#METHOD_MKCOL]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Mkcol.new(uri) # => #<Net::HTTP::Mkcol MKCOL>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#mkcol: sends +MKCOL+ request, returns response object.
+#
class Net::HTTP::Mkcol < Net::HTTPRequest
METHOD = 'MKCOL'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method COPY}[http://www.webdav.org/specs/rfc4918.html#METHOD_COPY]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Copy.new(uri) # => #<Net::HTTP::Copy COPY>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#copy: sends +COPY+ request, returns response object.
+#
class Net::HTTP::Copy < Net::HTTPRequest
METHOD = 'COPY'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method MOVE}[http://www.webdav.org/specs/rfc4918.html#METHOD_MOVE]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Move.new(uri) # => #<Net::HTTP::Move MOVE>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#move: sends +MOVE+ request, returns response object.
+#
class Net::HTTP::Move < Net::HTTPRequest
METHOD = 'MOVE'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method LOCK}[http://www.webdav.org/specs/rfc4918.html#METHOD_LOCK]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Lock.new(uri) # => #<Net::HTTP::Lock LOCK>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#lock: sends +LOCK+ request, returns response object.
+#
class Net::HTTP::Lock < Net::HTTPRequest
METHOD = 'LOCK'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method UNLOCK}[http://www.webdav.org/specs/rfc4918.html#METHOD_UNLOCK]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Unlock.new(uri) # => #<Net::HTTP::Unlock UNLOCK>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#unlock: sends +UNLOCK+ request, returns response object.
+#
class Net::HTTP::Unlock < Net::HTTPRequest
METHOD = 'UNLOCK'
REQUEST_HAS_BODY = true
diff --git a/lib/net/http/response.rb b/lib/net/http/response.rb
index f8b522f1ff..40de963868 100644
--- a/lib/net/http/response.rb
+++ b/lib/net/http/response.rb
@@ -1,20 +1,136 @@
-# frozen_string_literal: false
-# HTTP response class.
+# frozen_string_literal: true
+
+# This class is the base class for \Net::HTTP response classes.
+#
+# == About the Examples
+#
+# :include: doc/net-http/examples.rdoc
+#
+# == Returned Responses
+#
+# \Method Net::HTTP.get_response returns
+# an instance of one of the subclasses of \Net::HTTPResponse:
+#
+# Net::HTTP.get_response(uri)
+# # => #<Net::HTTPOK 200 OK readbody=true>
+# Net::HTTP.get_response(hostname, '/nosuch')
+# # => #<Net::HTTPNotFound 404 Not Found readbody=true>
+#
+# As does method Net::HTTP#request:
+#
+# req = Net::HTTP::Get.new(uri)
+# Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end # => #<Net::HTTPOK 200 OK readbody=true>
+#
+# \Class \Net::HTTPResponse includes module Net::HTTPHeader,
+# which provides access to response header values via (among others):
+#
+# - \Hash-like method <tt>[]</tt>.
+# - Specific reader methods, such as +content_type+.
+#
+# Examples:
+#
+# res = Net::HTTP.get_response(uri) # => #<Net::HTTPOK 200 OK readbody=true>
+# res['Content-Type'] # => "text/html; charset=UTF-8"
+# res.content_type # => "text/html"
+#
+# == Response Subclasses
+#
+# \Class \Net::HTTPResponse has a subclass for each
+# {HTTP status code}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes].
+# You can look up the response class for a given code:
+#
+# Net::HTTPResponse::CODE_TO_OBJ['200'] # => Net::HTTPOK
+# Net::HTTPResponse::CODE_TO_OBJ['400'] # => Net::HTTPBadRequest
+# Net::HTTPResponse::CODE_TO_OBJ['404'] # => Net::HTTPNotFound
+#
+# And you can retrieve the status code for a response object:
+#
+# Net::HTTP.get_response(uri).code # => "200"
+# Net::HTTP.get_response(hostname, '/nosuch').code # => "404"
+#
+# The response subclasses (indentation shows class hierarchy):
#
-# This class wraps together the response header and the response body (the
-# entity requested).
+# - Net::HTTPUnknownResponse (for unhandled \HTTP extensions).
#
-# It mixes in the HTTPHeader module, which provides access to response
-# header values both via hash-like methods and via individual readers.
+# - Net::HTTPInformation:
#
-# Note that each possible HTTP response code defines its own
-# HTTPResponse subclass. All classes are defined under the Net module.
-# Indentation indicates inheritance. For a list of the classes see Net::HTTP.
+# - Net::HTTPContinue (100)
+# - Net::HTTPSwitchProtocol (101)
+# - Net::HTTPProcessing (102)
+# - Net::HTTPEarlyHints (103)
#
-# Correspondence <code>HTTP code => class</code> is stored in CODE_TO_OBJ
-# constant:
+# - Net::HTTPSuccess:
#
-# Net::HTTPResponse::CODE_TO_OBJ['404'] #=> Net::HTTPNotFound
+# - Net::HTTPOK (200)
+# - Net::HTTPCreated (201)
+# - Net::HTTPAccepted (202)
+# - Net::HTTPNonAuthoritativeInformation (203)
+# - Net::HTTPNoContent (204)
+# - Net::HTTPResetContent (205)
+# - Net::HTTPPartialContent (206)
+# - Net::HTTPMultiStatus (207)
+# - Net::HTTPAlreadyReported (208)
+# - Net::HTTPIMUsed (226)
+#
+# - Net::HTTPRedirection:
+#
+# - Net::HTTPMultipleChoices (300)
+# - Net::HTTPMovedPermanently (301)
+# - Net::HTTPFound (302)
+# - Net::HTTPSeeOther (303)
+# - Net::HTTPNotModified (304)
+# - Net::HTTPUseProxy (305)
+# - Net::HTTPTemporaryRedirect (307)
+# - Net::HTTPPermanentRedirect (308)
+#
+# - Net::HTTPClientError:
+#
+# - Net::HTTPBadRequest (400)
+# - Net::HTTPUnauthorized (401)
+# - Net::HTTPPaymentRequired (402)
+# - Net::HTTPForbidden (403)
+# - Net::HTTPNotFound (404)
+# - Net::HTTPMethodNotAllowed (405)
+# - Net::HTTPNotAcceptable (406)
+# - Net::HTTPProxyAuthenticationRequired (407)
+# - Net::HTTPRequestTimeOut (408)
+# - Net::HTTPConflict (409)
+# - Net::HTTPGone (410)
+# - Net::HTTPLengthRequired (411)
+# - Net::HTTPPreconditionFailed (412)
+# - Net::HTTPRequestEntityTooLarge (413)
+# - Net::HTTPRequestURITooLong (414)
+# - Net::HTTPUnsupportedMediaType (415)
+# - Net::HTTPRequestedRangeNotSatisfiable (416)
+# - Net::HTTPExpectationFailed (417)
+# - Net::HTTPMisdirectedRequest (421)
+# - Net::HTTPUnprocessableEntity (422)
+# - Net::HTTPLocked (423)
+# - Net::HTTPFailedDependency (424)
+# - Net::HTTPUpgradeRequired (426)
+# - Net::HTTPPreconditionRequired (428)
+# - Net::HTTPTooManyRequests (429)
+# - Net::HTTPRequestHeaderFieldsTooLarge (431)
+# - Net::HTTPUnavailableForLegalReasons (451)
+#
+# - Net::HTTPServerError:
+#
+# - Net::HTTPInternalServerError (500)
+# - Net::HTTPNotImplemented (501)
+# - Net::HTTPBadGateway (502)
+# - Net::HTTPServiceUnavailable (503)
+# - Net::HTTPGatewayTimeOut (504)
+# - Net::HTTPVersionNotSupported (505)
+# - Net::HTTPVariantAlsoNegotiates (506)
+# - Net::HTTPInsufficientStorage (507)
+# - Net::HTTPLoopDetected (508)
+# - Net::HTTPNotExtended (510)
+# - Net::HTTPNetworkAuthenticationRequired (511)
+#
+# There is also the Net::HTTPBadResponse exception which is raised when
+# there is a protocol error.
#
class Net::HTTPResponse
class << self
@@ -108,13 +224,32 @@ class Net::HTTPResponse
# Accept-Encoding header from the user.
attr_accessor :decode_content
- # The encoding to use for the response body. If Encoding, use that encoding.
- # If other true value, attempt to detect the appropriate encoding, and use
- # that.
+ # Returns the value set by body_encoding=, or +false+ if none;
+ # see #body_encoding=.
attr_reader :body_encoding
- # Set the encoding to use for the response body. If given a String, find
- # the related Encoding.
+ # Sets the encoding that should be used when reading the body:
+ #
+ # - If the given value is an Encoding object, that encoding will be used.
+ # - Otherwise if the value is a string, the value of
+ # {Encoding#find(value)}[rdoc-ref:Encoding.find]
+ # will be used.
+ # - Otherwise an encoding will be deduced from the body itself.
+ #
+ # Examples:
+ #
+ # http = Net::HTTP.new(hostname)
+ # req = Net::HTTP::Get.new('/')
+ #
+ # http.request(req) do |res|
+ # p res.body.encoding # => #<Encoding:ASCII-8BIT>
+ # end
+ #
+ # http.request(req) do |res|
+ # res.body_encoding = "UTF-8"
+ # p res.body.encoding # => #<Encoding:UTF-8>
+ # end
+ #
def body_encoding=(value)
value = Encoding.find(value) if value.is_a?(String)
@body_encoding = value
@@ -138,7 +273,7 @@ class Net::HTTPResponse
def error! #:nodoc:
message = @code
- message += ' ' + @message.dump if @message
+ message = "#{message} #{@message.dump}" if @message
raise error_type().new(message, self)
end
@@ -231,6 +366,7 @@ class Net::HTTPResponse
@body = nil
end
@read = true
+ return if @body.nil?
case enc = @body_encoding
when Encoding, false, nil
@@ -246,26 +382,26 @@ class Net::HTTPResponse
@body
end
- # Returns the full entity body.
+ # Returns the string response body;
+ # note that repeated calls for the unmodified body return a cached string:
#
- # Calling this method a second or subsequent time will return the
- # string already read.
+ # path = '/todos/1'
+ # Net::HTTP.start(hostname) do |http|
+ # res = http.get(path)
+ # p res.body
+ # p http.head(path).body # No body.
+ # end
#
- # http.request_get('/index.html') {|res|
- # puts res.body
- # }
+ # Output:
#
- # http.request_get('/index.html') {|res|
- # p res.body.object_id # 538149362
- # p res.body.object_id # 538149362
- # }
+ # "{\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"delectus aut autem\",\n \"completed\": false\n}"
+ # nil
#
def body
read_body()
end
- # Because it may be necessary to modify the body, Eg, decompression
- # this method facilitates that.
+ # Sets the body of the response to the given value.
def body=(value)
@body = value
end
@@ -504,7 +640,7 @@ class Net::HTTPResponse
end
def stream_check
- raise IOError, 'attempt to read body out of block' if @socket.closed?
+ raise IOError, 'attempt to read body out of block' if @socket.nil? || @socket.closed?
end
def procdest(dest, block)
@@ -513,7 +649,7 @@ class Net::HTTPResponse
if block
Net::ReadAdapter.new(block)
else
- dest || ''
+ dest || +''
end
end
diff --git a/lib/net/http/responses.rb b/lib/net/http/responses.rb
index 02a2fdaa4c..6f6fb8d055 100644
--- a/lib/net/http/responses.rb
+++ b/lib/net/http/responses.rb
@@ -3,192 +3,909 @@
# https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
module Net
- # :stopdoc:
class HTTPUnknownResponse < HTTPResponse
HAS_BODY = true
EXCEPTION_TYPE = HTTPError #
end
- class HTTPInformation < HTTPResponse # 1xx
+
+ # Parent class for informational (1xx) HTTP response classes.
+ #
+ # An informational response indicates that the request was received and understood.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.1xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#1xx_informational_response].
+ #
+ class HTTPInformation < HTTPResponse
HAS_BODY = false
EXCEPTION_TYPE = HTTPError #
end
- class HTTPSuccess < HTTPResponse # 2xx
+
+ # Parent class for success (2xx) HTTP response classes.
+ #
+ # A success response indicates the action requested by the client
+ # was received, understood, and accepted.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.2xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_success].
+ #
+ class HTTPSuccess < HTTPResponse
HAS_BODY = true
EXCEPTION_TYPE = HTTPError #
end
- class HTTPRedirection < HTTPResponse # 3xx
+
+ # Parent class for redirection (3xx) HTTP response classes.
+ #
+ # A redirection response indicates the client must take additional action
+ # to complete the request.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.3xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_redirection].
+ #
+ class HTTPRedirection < HTTPResponse
HAS_BODY = true
EXCEPTION_TYPE = HTTPRetriableError #
end
- class HTTPClientError < HTTPResponse # 4xx
+
+ # Parent class for client error (4xx) HTTP response classes.
+ #
+ # A client error response indicates that the client may have caused an error.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.4xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_client_errors].
+ #
+ class HTTPClientError < HTTPResponse
HAS_BODY = true
EXCEPTION_TYPE = HTTPClientException #
end
- class HTTPServerError < HTTPResponse # 5xx
+
+ # Parent class for server error (5xx) HTTP response classes.
+ #
+ # A server error response indicates that the server failed to fulfill a request.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.5xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_server_errors].
+ #
+ class HTTPServerError < HTTPResponse
HAS_BODY = true
EXCEPTION_TYPE = HTTPFatalError #
end
- class HTTPContinue < HTTPInformation # 100
+ # Response class for +Continue+ responses (status code 100).
+ #
+ # A +Continue+ response indicates that the server has received the request headers.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/100].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-100-continue].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#100].
+ #
+ class HTTPContinue < HTTPInformation
HAS_BODY = false
end
- class HTTPSwitchProtocol < HTTPInformation # 101
+
+ # Response class for <tt>Switching Protocol</tt> responses (status code 101).
+ #
+ # The <tt>Switching Protocol<tt> response indicates that the server has received
+ # a request to switch protocols, and has agreed to do so.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/101].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-101-switching-protocols].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#101].
+ #
+ class HTTPSwitchProtocol < HTTPInformation
HAS_BODY = false
end
- class HTTPProcessing < HTTPInformation # 102
+
+ # Response class for +Processing+ responses (status code 102).
+ #
+ # The +Processing+ response indicates that the server has received
+ # and is processing the request, but no response is available yet.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 2518}[https://www.rfc-editor.org/rfc/rfc2518#section-10.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#102].
+ #
+ class HTTPProcessing < HTTPInformation
HAS_BODY = false
end
- class HTTPEarlyHints < HTTPInformation # 103 - RFC 8297
+
+ # Response class for <tt>Early Hints</tt> responses (status code 103).
+ #
+ # The <tt>Early Hints</tt> indicates that the server has received
+ # and is processing the request, and contains certain headers;
+ # the final response is not available yet.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103].
+ # - {RFC 8297}[https://www.rfc-editor.org/rfc/rfc8297.html#section-2].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#103].
+ #
+ class HTTPEarlyHints < HTTPInformation
HAS_BODY = false
end
- class HTTPOK < HTTPSuccess # 200
+ # Response class for +OK+ responses (status code 200).
+ #
+ # The +OK+ response indicates that the server has received
+ # a request and has responded successfully.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#200].
+ #
+ class HTTPOK < HTTPSuccess
HAS_BODY = true
end
- class HTTPCreated < HTTPSuccess # 201
+
+ # Response class for +Created+ responses (status code 201).
+ #
+ # The +Created+ response indicates that the server has received
+ # and has fulfilled a request to create a new resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-201-created].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#201].
+ #
+ class HTTPCreated < HTTPSuccess
HAS_BODY = true
end
- class HTTPAccepted < HTTPSuccess # 202
+
+ # Response class for +Accepted+ responses (status code 202).
+ #
+ # The +Accepted+ response indicates that the server has received
+ # and is processing a request, but the processing has not yet been completed.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/202].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-202-accepted].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#202].
+ #
+ class HTTPAccepted < HTTPSuccess
HAS_BODY = true
end
- class HTTPNonAuthoritativeInformation < HTTPSuccess # 203
+
+ # Response class for <tt>Non-Authoritative Information</tt> responses (status code 203).
+ #
+ # The <tt>Non-Authoritative Information</tt> response indicates that the server
+ # is a transforming proxy (such as a Web accelerator)
+ # that received a 200 OK response from its origin,
+ # and is returning a modified version of the origin's response.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/203].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-203-non-authoritative-infor].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#203].
+ #
+ class HTTPNonAuthoritativeInformation < HTTPSuccess
HAS_BODY = true
end
- class HTTPNoContent < HTTPSuccess # 204
+
+ # Response class for <tt>No Content</tt> responses (status code 204).
+ #
+ # The <tt>No Content</tt> response indicates that the server
+ # successfully processed the request, and is not returning any content.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-204-no-content].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#204].
+ #
+ class HTTPNoContent < HTTPSuccess
HAS_BODY = false
end
- class HTTPResetContent < HTTPSuccess # 205
+
+ # Response class for <tt>Reset Content</tt> responses (status code 205).
+ #
+ # The <tt>Reset Content</tt> response indicates that the server
+ # successfully processed the request,
+ # asks that the client reset its document view, and is not returning any content.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/205].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-205-reset-content].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#205].
+ #
+ class HTTPResetContent < HTTPSuccess
HAS_BODY = false
end
- class HTTPPartialContent < HTTPSuccess # 206
+
+ # Response class for <tt>Partial Content</tt> responses (status code 206).
+ #
+ # The <tt>Partial Content</tt> response indicates that the server is delivering
+ # only part of the resource (byte serving)
+ # due to a Range header in the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-206-partial-content].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#206].
+ #
+ class HTTPPartialContent < HTTPSuccess
HAS_BODY = true
end
- class HTTPMultiStatus < HTTPSuccess # 207 - RFC 4918
+
+ # Response class for <tt>Multi-Status (WebDAV)</tt> responses (status code 207).
+ #
+ # The <tt>Multi-Status (WebDAV)</tt> response indicates that the server
+ # has received the request,
+ # and that the message body can contain a number of separate response codes.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 4818}[https://www.rfc-editor.org/rfc/rfc4918#section-11.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#207].
+ #
+ class HTTPMultiStatus < HTTPSuccess
HAS_BODY = true
end
- class HTTPAlreadyReported < HTTPSuccess # 208 - RFC 5842
+
+ # Response class for <tt>Already Reported (WebDAV)</tt> responses (status code 208).
+ #
+ # The <tt>Already Reported (WebDAV)</tt> response indicates that the server
+ # has received the request,
+ # and that the members of a DAV binding have already been enumerated
+ # in a preceding part of the (multi-status) response,
+ # and are not being included again.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 5842}[https://www.rfc-editor.org/rfc/rfc5842.html#section-7.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#208].
+ #
+ class HTTPAlreadyReported < HTTPSuccess
HAS_BODY = true
end
- class HTTPIMUsed < HTTPSuccess # 226 - RFC 3229
+
+ # Response class for <tt>IM Used</tt> responses (status code 226).
+ #
+ # The <tt>IM Used</tt> response indicates that the server has fulfilled a request
+ # for the resource, and the response is a representation of the result
+ # of one or more instance-manipulations applied to the current instance.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 3229}[https://www.rfc-editor.org/rfc/rfc3229.html#section-10.4.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#226].
+ #
+ class HTTPIMUsed < HTTPSuccess
HAS_BODY = true
end
- class HTTPMultipleChoices < HTTPRedirection # 300
+ # Response class for <tt>Multiple Choices</tt> responses (status code 300).
+ #
+ # The <tt>Multiple Choices</tt> response indicates that the server
+ # offers multiple options for the resource from which the client may choose.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/300].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-300-multiple-choices].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#300].
+ #
+ class HTTPMultipleChoices < HTTPRedirection
HAS_BODY = true
end
HTTPMultipleChoice = HTTPMultipleChoices
- class HTTPMovedPermanently < HTTPRedirection # 301
+
+ # Response class for <tt>Moved Permanently</tt> responses (status code 301).
+ #
+ # The <tt>Moved Permanently</tt> response indicates that links or records
+ # returning this response should be updated to use the given URL.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-301-moved-permanently].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#301].
+ #
+ class HTTPMovedPermanently < HTTPRedirection
HAS_BODY = true
end
- class HTTPFound < HTTPRedirection # 302
+
+ # Response class for <tt>Found</tt> responses (status code 302).
+ #
+ # The <tt>Found</tt> response indicates that the client
+ # should look at (browse to) another URL.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-302-found].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#302].
+ #
+ class HTTPFound < HTTPRedirection
HAS_BODY = true
end
HTTPMovedTemporarily = HTTPFound
- class HTTPSeeOther < HTTPRedirection # 303
+
+ # Response class for <tt>See Other</tt> responses (status code 303).
+ #
+ # The response to the request can be found under another URI using the GET method.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-303-see-other].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#303].
+ #
+ class HTTPSeeOther < HTTPRedirection
HAS_BODY = true
end
- class HTTPNotModified < HTTPRedirection # 304
+
+ # Response class for <tt>Not Modified</tt> responses (status code 304).
+ #
+ # Indicates that the resource has not been modified since the version
+ # specified by the request headers.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-304-not-modified].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#304].
+ #
+ class HTTPNotModified < HTTPRedirection
HAS_BODY = false
end
- class HTTPUseProxy < HTTPRedirection # 305
+
+ # Response class for <tt>Use Proxy</tt> responses (status code 305).
+ #
+ # The requested resource is available only through a proxy,
+ # whose address is provided in the response.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-305-use-proxy].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#305].
+ #
+ class HTTPUseProxy < HTTPRedirection
HAS_BODY = false
end
- # 306 Switch Proxy - no longer unused
- class HTTPTemporaryRedirect < HTTPRedirection # 307
+
+ # Response class for <tt>Temporary Redirect</tt> responses (status code 307).
+ #
+ # The request should be repeated with another URI;
+ # however, future requests should still use the original URI.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-307-temporary-redirect].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#307].
+ #
+ class HTTPTemporaryRedirect < HTTPRedirection
HAS_BODY = true
end
- class HTTPPermanentRedirect < HTTPRedirection # 308
+
+ # Response class for <tt>Permanent Redirect</tt> responses (status code 308).
+ #
+ # This and all future requests should be directed to the given URI.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-308-permanent-redirect].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#308].
+ #
+ class HTTPPermanentRedirect < HTTPRedirection
HAS_BODY = true
end
- class HTTPBadRequest < HTTPClientError # 400
+ # Response class for <tt>Bad Request</tt> responses (status code 400).
+ #
+ # The server cannot or will not process the request due to an apparent client error.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#400].
+ #
+ class HTTPBadRequest < HTTPClientError
HAS_BODY = true
end
- class HTTPUnauthorized < HTTPClientError # 401
+
+ # Response class for <tt>Unauthorized</tt> responses (status code 401).
+ #
+ # Authentication is required, but either was not provided or failed.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#401].
+ #
+ class HTTPUnauthorized < HTTPClientError
HAS_BODY = true
end
- class HTTPPaymentRequired < HTTPClientError # 402
+
+ # Response class for <tt>Payment Required</tt> responses (status code 402).
+ #
+ # Reserved for future use.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-402-payment-required].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#402].
+ #
+ class HTTPPaymentRequired < HTTPClientError
HAS_BODY = true
end
- class HTTPForbidden < HTTPClientError # 403
+
+ # Response class for <tt>Forbidden</tt> responses (status code 403).
+ #
+ # The request contained valid data and was understood by the server,
+ # but the server is refusing action.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#403].
+ #
+ class HTTPForbidden < HTTPClientError
HAS_BODY = true
end
- class HTTPNotFound < HTTPClientError # 404
+
+ # Response class for <tt>Not Found</tt> responses (status code 404).
+ #
+ # The requested resource could not be found but may be available in the future.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#404].
+ #
+ class HTTPNotFound < HTTPClientError
HAS_BODY = true
end
- class HTTPMethodNotAllowed < HTTPClientError # 405
+
+ # Response class for <tt>Method Not Allowed</tt> responses (status code 405).
+ #
+ # The request method is not supported for the requested resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-405-method-not-allowed].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#405].
+ #
+ class HTTPMethodNotAllowed < HTTPClientError
HAS_BODY = true
end
- class HTTPNotAcceptable < HTTPClientError # 406
+
+ # Response class for <tt>Not Acceptable</tt> responses (status code 406).
+ #
+ # The requested resource is capable of generating only content
+ # that not acceptable according to the Accept headers sent in the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#406].
+ #
+ class HTTPNotAcceptable < HTTPClientError
HAS_BODY = true
end
- class HTTPProxyAuthenticationRequired < HTTPClientError # 407
+
+ # Response class for <tt>Proxy Authentication Required</tt> responses (status code 407).
+ #
+ # The client must first authenticate itself with the proxy.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/407].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-407-proxy-authentication-re].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#407].
+ #
+ class HTTPProxyAuthenticationRequired < HTTPClientError
HAS_BODY = true
end
- class HTTPRequestTimeout < HTTPClientError # 408
+
+ # Response class for <tt>Request Timeout</tt> responses (status code 408).
+ #
+ # The server timed out waiting for the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-408-request-timeout].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#408].
+ #
+ class HTTPRequestTimeout < HTTPClientError
HAS_BODY = true
end
HTTPRequestTimeOut = HTTPRequestTimeout
- class HTTPConflict < HTTPClientError # 409
+
+ # Response class for <tt>Conflict</tt> responses (status code 409).
+ #
+ # The request could not be processed because of conflict in the current state of the resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#409].
+ #
+ class HTTPConflict < HTTPClientError
HAS_BODY = true
end
- class HTTPGone < HTTPClientError # 410
+
+ # Response class for <tt>Gone</tt> responses (status code 410).
+ #
+ # The resource requested was previously in use but is no longer available
+ # and will not be available again.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/410].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-410-gone].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#410].
+ #
+ class HTTPGone < HTTPClientError
HAS_BODY = true
end
- class HTTPLengthRequired < HTTPClientError # 411
+
+ # Response class for <tt>Length Required</tt> responses (status code 411).
+ #
+ # The request did not specify the length of its content,
+ # which is required by the requested resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/411].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-411-length-required].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#411].
+ #
+ class HTTPLengthRequired < HTTPClientError
HAS_BODY = true
end
- class HTTPPreconditionFailed < HTTPClientError # 412
+
+ # Response class for <tt>Precondition Failed</tt> responses (status code 412).
+ #
+ # The server does not meet one of the preconditions
+ # specified in the request headers.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-412-precondition-failed].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#412].
+ #
+ class HTTPPreconditionFailed < HTTPClientError
HAS_BODY = true
end
- class HTTPPayloadTooLarge < HTTPClientError # 413
+
+ # Response class for <tt>Payload Too Large</tt> responses (status code 413).
+ #
+ # The request is larger than the server is willing or able to process.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-413-content-too-large].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#413].
+ #
+ class HTTPPayloadTooLarge < HTTPClientError
HAS_BODY = true
end
HTTPRequestEntityTooLarge = HTTPPayloadTooLarge
- class HTTPURITooLong < HTTPClientError # 414
+
+ # Response class for <tt>URI Too Long</tt> responses (status code 414).
+ #
+ # The URI provided was too long for the server to process.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/414].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-414-uri-too-long].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#414].
+ #
+ class HTTPURITooLong < HTTPClientError
HAS_BODY = true
end
HTTPRequestURITooLong = HTTPURITooLong
HTTPRequestURITooLarge = HTTPRequestURITooLong
- class HTTPUnsupportedMediaType < HTTPClientError # 415
+
+ # Response class for <tt>Unsupported Media Type</tt> responses (status code 415).
+ #
+ # The request entity has a media type which the server or resource does not support.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/415].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-415-unsupported-media-type].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#415].
+ #
+ class HTTPUnsupportedMediaType < HTTPClientError
HAS_BODY = true
end
- class HTTPRangeNotSatisfiable < HTTPClientError # 416
+
+ # Response class for <tt>Range Not Satisfiable</tt> responses (status code 416).
+ #
+ # The request entity has a media type which the server or resource does not support.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-416-range-not-satisfiable].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#416].
+ #
+ class HTTPRangeNotSatisfiable < HTTPClientError
HAS_BODY = true
end
HTTPRequestedRangeNotSatisfiable = HTTPRangeNotSatisfiable
- class HTTPExpectationFailed < HTTPClientError # 417
+
+ # Response class for <tt>Expectation Failed</tt> responses (status code 417).
+ #
+ # The server cannot meet the requirements of the Expect request-header field.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/417].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-417-expectation-failed].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#417].
+ #
+ class HTTPExpectationFailed < HTTPClientError
HAS_BODY = true
end
+
# 418 I'm a teapot - RFC 2324; a joke RFC
+ # See https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#418.
+
# 420 Enhance Your Calm - Twitter
- class HTTPMisdirectedRequest < HTTPClientError # 421 - RFC 7540
+
+ # Response class for <tt>Misdirected Request</tt> responses (status code 421).
+ #
+ # The request was directed at a server that is not able to produce a response.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-421-misdirected-request].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#421].
+ #
+ class HTTPMisdirectedRequest < HTTPClientError
HAS_BODY = true
end
- class HTTPUnprocessableEntity < HTTPClientError # 422 - RFC 4918
+
+ # Response class for <tt>Unprocessable Entity</tt> responses (status code 422).
+ #
+ # The request was well-formed but had semantic errors.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-422-unprocessable-content].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#422].
+ #
+ class HTTPUnprocessableEntity < HTTPClientError
HAS_BODY = true
end
- class HTTPLocked < HTTPClientError # 423 - RFC 4918
+
+ # Response class for <tt>Locked (WebDAV)</tt> responses (status code 423).
+ #
+ # The requested resource is locked.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 4918}[https://www.rfc-editor.org/rfc/rfc4918#section-11.3].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#423].
+ #
+ class HTTPLocked < HTTPClientError
HAS_BODY = true
end
- class HTTPFailedDependency < HTTPClientError # 424 - RFC 4918
+
+ # Response class for <tt>Failed Dependency (WebDAV)</tt> responses (status code 424).
+ #
+ # The request failed because it depended on another request and that request failed.
+ # See {424 Failed Dependency (WebDAV)}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#424].
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 4918}[https://www.rfc-editor.org/rfc/rfc4918#section-11.4].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#424].
+ #
+ class HTTPFailedDependency < HTTPClientError
HAS_BODY = true
end
- # 425 Unordered Collection - existed only in draft
- class HTTPUpgradeRequired < HTTPClientError # 426 - RFC 2817
+
+ # 425 Too Early
+ # https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#425.
+
+ # Response class for <tt>Upgrade Required</tt> responses (status code 426).
+ #
+ # The client should switch to the protocol given in the Upgrade header field.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/426].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-426-upgrade-required].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#426].
+ #
+ class HTTPUpgradeRequired < HTTPClientError
HAS_BODY = true
end
- class HTTPPreconditionRequired < HTTPClientError # 428 - RFC 6585
+
+ # Response class for <tt>Precondition Required</tt> responses (status code 428).
+ #
+ # The origin server requires the request to be conditional.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/428].
+ # - {RFC 6585}[https://www.rfc-editor.org/rfc/rfc6585#section-3].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#428].
+ #
+ class HTTPPreconditionRequired < HTTPClientError
HAS_BODY = true
end
- class HTTPTooManyRequests < HTTPClientError # 429 - RFC 6585
+
+ # Response class for <tt>Too Many Requests</tt> responses (status code 429).
+ #
+ # The user has sent too many requests in a given amount of time.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429].
+ # - {RFC 6585}[https://www.rfc-editor.org/rfc/rfc6585#section-4].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#429].
+ #
+ class HTTPTooManyRequests < HTTPClientError
HAS_BODY = true
end
- class HTTPRequestHeaderFieldsTooLarge < HTTPClientError # 431 - RFC 6585
+
+ # Response class for <tt>Request Header Fields Too Large</tt> responses (status code 431).
+ #
+ # An individual header field is too large,
+ # or all the header fields collectively, are too large.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431].
+ # - {RFC 6585}[https://www.rfc-editor.org/rfc/rfc6585#section-5].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#431].
+ #
+ class HTTPRequestHeaderFieldsTooLarge < HTTPClientError
HAS_BODY = true
end
- class HTTPUnavailableForLegalReasons < HTTPClientError # 451 - RFC 7725
+
+ # Response class for <tt>Unavailable For Legal Reasons</tt> responses (status code 451).
+ #
+ # A server operator has received a legal demand to deny access to a resource or to a set of resources
+ # that includes the requested resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/451].
+ # - {RFC 7725}[https://www.rfc-editor.org/rfc/rfc7725.html#section-3].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#451].
+ #
+ class HTTPUnavailableForLegalReasons < HTTPClientError
HAS_BODY = true
end
# 444 No Response - Nginx
@@ -196,43 +913,188 @@ module Net
# 450 Blocked by Windows Parental Controls - Microsoft
# 499 Client Closed Request - Nginx
- class HTTPInternalServerError < HTTPServerError # 500
+ # Response class for <tt>Internal Server Error</tt> responses (status code 500).
+ #
+ # An unexpected condition was encountered and no more specific message is suitable.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-500-internal-server-error].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#500].
+ #
+ class HTTPInternalServerError < HTTPServerError
HAS_BODY = true
end
- class HTTPNotImplemented < HTTPServerError # 501
+
+ # Response class for <tt>Not Implemented</tt> responses (status code 501).
+ #
+ # The server either does not recognize the request method,
+ # or it lacks the ability to fulfil the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-501-not-implemented].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#501].
+ #
+ class HTTPNotImplemented < HTTPServerError
HAS_BODY = true
end
- class HTTPBadGateway < HTTPServerError # 502
+
+ # Response class for <tt>Bad Gateway</tt> responses (status code 502).
+ #
+ # The server was acting as a gateway or proxy
+ # and received an invalid response from the upstream server.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-502-bad-gateway].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#502].
+ #
+ class HTTPBadGateway < HTTPServerError
HAS_BODY = true
end
- class HTTPServiceUnavailable < HTTPServerError # 503
+
+ # Response class for <tt>Service Unavailable</tt> responses (status code 503).
+ #
+ # The server cannot handle the request
+ # (because it is overloaded or down for maintenance).
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-503-service-unavailable].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#503].
+ #
+ class HTTPServiceUnavailable < HTTPServerError
HAS_BODY = true
end
- class HTTPGatewayTimeout < HTTPServerError # 504
+
+ # Response class for <tt>Gateway Timeout</tt> responses (status code 504).
+ #
+ # The server was acting as a gateway or proxy
+ # and did not receive a timely response from the upstream server.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-504-gateway-timeout].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#504].
+ #
+ class HTTPGatewayTimeout < HTTPServerError
HAS_BODY = true
end
HTTPGatewayTimeOut = HTTPGatewayTimeout
- class HTTPVersionNotSupported < HTTPServerError # 505
+
+ # Response class for <tt>HTTP Version Not Supported</tt> responses (status code 505).
+ #
+ # The server does not support the HTTP version used in the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/505].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-505-http-version-not-suppor].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#505].
+ #
+ class HTTPVersionNotSupported < HTTPServerError
HAS_BODY = true
end
- class HTTPVariantAlsoNegotiates < HTTPServerError # 506
+
+ # Response class for <tt>Variant Also Negotiates</tt> responses (status code 506).
+ #
+ # Transparent content negotiation for the request results in a circular reference.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/506].
+ # - {RFC 2295}[https://www.rfc-editor.org/rfc/rfc2295#section-8.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#506].
+ #
+ class HTTPVariantAlsoNegotiates < HTTPServerError
HAS_BODY = true
end
- class HTTPInsufficientStorage < HTTPServerError # 507 - RFC 4918
+
+ # Response class for <tt>Insufficient Storage (WebDAV)</tt> responses (status code 507).
+ #
+ # The server is unable to store the representation needed to complete the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/507].
+ # - {RFC 4918}[https://www.rfc-editor.org/rfc/rfc4918#section-11.5].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#507].
+ #
+ class HTTPInsufficientStorage < HTTPServerError
HAS_BODY = true
end
- class HTTPLoopDetected < HTTPServerError # 508 - RFC 5842
+
+ # Response class for <tt>Loop Detected (WebDAV)</tt> responses (status code 508).
+ #
+ # The server detected an infinite loop while processing the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/508].
+ # - {RFC 5942}[https://www.rfc-editor.org/rfc/rfc5842.html#section-7.2].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#508].
+ #
+ class HTTPLoopDetected < HTTPServerError
HAS_BODY = true
end
# 509 Bandwidth Limit Exceeded - Apache bw/limited extension
- class HTTPNotExtended < HTTPServerError # 510 - RFC 2774
+
+ # Response class for <tt>Not Extended</tt> responses (status code 510).
+ #
+ # Further extensions to the request are required for the server to fulfill it.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/510].
+ # - {RFC 2774}[https://www.rfc-editor.org/rfc/rfc2774.html#section-7].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#510].
+ #
+ class HTTPNotExtended < HTTPServerError
HAS_BODY = true
end
- class HTTPNetworkAuthenticationRequired < HTTPServerError # 511 - RFC 6585
+
+ # Response class for <tt>Network Authentication Required</tt> responses (status code 511).
+ #
+ # The client needs to authenticate to gain network access.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/511].
+ # - {RFC 6585}[https://www.rfc-editor.org/rfc/rfc6585#section-6].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#511].
+ #
+ class HTTPNetworkAuthenticationRequired < HTTPServerError
HAS_BODY = true
end
- # :startdoc:
end
class Net::HTTPResponse
diff --git a/lib/net/http/status.rb b/lib/net/http/status.rb
index 8db3f7d9e3..e70b47d9fb 100644
--- a/lib/net/http/status.rb
+++ b/lib/net/http/status.rb
@@ -4,7 +4,7 @@ require_relative '../http'
if $0 == __FILE__
require 'open-uri'
- IO.foreach(__FILE__) do |line|
+ File.foreach(__FILE__) do |line|
puts line
break if line.start_with?('end')
end
@@ -16,7 +16,7 @@ if $0 == __FILE__
next if ['(Unused)', 'Unassigned', 'Description'].include?(mes)
puts " #{code} => '#{mes}',"
end
- puts "}"
+ puts "} # :nodoc:"
end
Net::HTTP::STATUS_CODES = {
@@ -55,15 +55,16 @@ Net::HTTP::STATUS_CODES = {
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
- 413 => 'Payload Too Large',
+ 413 => 'Content Too Large',
414 => 'URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Range Not Satisfiable',
417 => 'Expectation Failed',
421 => 'Misdirected Request',
- 422 => 'Unprocessable Entity',
+ 422 => 'Unprocessable Content',
423 => 'Locked',
424 => 'Failed Dependency',
+ 425 => 'Too Early',
426 => 'Upgrade Required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
@@ -78,6 +79,6 @@ Net::HTTP::STATUS_CODES = {
506 => 'Variant Also Negotiates',
507 => 'Insufficient Storage',
508 => 'Loop Detected',
- 510 => 'Not Extended',
+ 510 => 'Not Extended (OBSOLETED)',
511 => 'Network Authentication Required',
-}
+} # :nodoc:
diff --git a/lib/net/https.rb b/lib/net/https.rb
index d46721c82a..0f23e1fb13 100644
--- a/lib/net/https.rb
+++ b/lib/net/https.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
=begin
= net/https -- SSL/TLS enhancement for Net::HTTP.
diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb
index 822bc00574..ea0752a971 100644
--- a/lib/net/protocol.rb
+++ b/lib/net/protocol.rb
@@ -26,7 +26,7 @@ require 'io/wait'
module Net # :nodoc:
class Protocol #:nodoc: internal use only
- VERSION = "0.1.3"
+ VERSION = "0.2.1"
private
def Protocol.protocol_param(name, val)
@@ -120,6 +120,8 @@ module Net # :nodoc:
@continue_timeout = continue_timeout
@debug_output = debug_output
@rbuf = ''.b
+ @rbuf_empty = true
+ @rbuf_offset = 0
end
attr_reader :io
@@ -154,14 +156,15 @@ module Net # :nodoc:
LOG "reading #{len} bytes..."
read_bytes = 0
begin
- while read_bytes + @rbuf.size < len
- s = rbuf_consume(@rbuf.size)
- read_bytes += s.size
- dest << s
+ while read_bytes + rbuf_size < len
+ if s = rbuf_consume_all
+ read_bytes += s.bytesize
+ dest << s
+ end
rbuf_fill
end
s = rbuf_consume(len - read_bytes)
- read_bytes += s.size
+ read_bytes += s.bytesize
dest << s
rescue EOFError
raise unless ignore_eof
@@ -175,9 +178,10 @@ module Net # :nodoc:
read_bytes = 0
begin
while true
- s = rbuf_consume(@rbuf.size)
- read_bytes += s.size
- dest << s
+ if s = rbuf_consume_all
+ read_bytes += s.bytesize
+ dest << s
+ end
rbuf_fill
end
rescue EOFError
@@ -188,14 +192,16 @@ module Net # :nodoc:
end
def readuntil(terminator, ignore_eof = false)
+ offset = @rbuf_offset
begin
- until idx = @rbuf.index(terminator)
+ until idx = @rbuf.index(terminator, offset)
+ offset = @rbuf.bytesize
rbuf_fill
end
- return rbuf_consume(idx + terminator.size)
+ return rbuf_consume(idx + terminator.bytesize - @rbuf_offset)
rescue EOFError
raise unless ignore_eof
- return rbuf_consume(@rbuf.size)
+ return rbuf_consume
end
end
@@ -208,12 +214,16 @@ module Net # :nodoc:
BUFSIZE = 1024 * 16
def rbuf_fill
- tmp = @rbuf.empty? ? @rbuf : nil
+ tmp = @rbuf_empty ? @rbuf : nil
case rv = @io.read_nonblock(BUFSIZE, tmp, exception: false)
when String
- return if rv.equal?(tmp)
- @rbuf << rv
- rv.clear
+ @rbuf_empty = false
+ if rv.equal?(tmp)
+ @rbuf_offset = 0
+ else
+ @rbuf << rv
+ rv.clear
+ end
return
when :wait_readable
(io = @io.to_io).wait_readable(@read_timeout) or raise Net::ReadTimeout.new(io)
@@ -228,13 +238,40 @@ module Net # :nodoc:
end while true
end
- def rbuf_consume(len)
- if len == @rbuf.size
+ def rbuf_flush
+ if @rbuf_empty
+ @rbuf.clear
+ @rbuf_offset = 0
+ end
+ nil
+ end
+
+ def rbuf_size
+ @rbuf.bytesize - @rbuf_offset
+ end
+
+ def rbuf_consume_all
+ rbuf_consume if rbuf_size > 0
+ end
+
+ def rbuf_consume(len = nil)
+ if @rbuf_offset == 0 && (len.nil? || len == @rbuf.bytesize)
s = @rbuf
@rbuf = ''.b
+ @rbuf_offset = 0
+ @rbuf_empty = true
+ elsif len.nil?
+ s = @rbuf.byteslice(@rbuf_offset..-1)
+ @rbuf = ''.b
+ @rbuf_offset = 0
+ @rbuf_empty = true
else
- s = @rbuf.slice!(0, len)
+ s = @rbuf.byteslice(@rbuf_offset, len)
+ @rbuf_offset += len
+ @rbuf_empty = @rbuf_offset == @rbuf.bytesize
+ rbuf_flush
end
+
@debug_output << %Q[-> #{s.dump}\n] if @debug_output
s
end
diff --git a/lib/open-uri.gemspec b/lib/open-uri.gemspec
index 12f10ef316..cad63e4d80 100644
--- a/lib/open-uri.gemspec
+++ b/lib/open-uri.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "open-uri"
- spec.version = "0.2.0"
+ spec.version = "0.3.0"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
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)/}) }
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A((bin|test|spec|features)/|\.git|[Rr]ake|Gemfile)|\.gemspec\z}) }
end
spec.executables = []
spec.require_paths = ["lib"]
diff --git a/lib/open-uri.rb b/lib/open-uri.rb
index cb9c3aa505..93e8cfcdb7 100644
--- a/lib/open-uri.rb
+++ b/lib/open-uri.rb
@@ -99,6 +99,8 @@ module OpenURI
:open_timeout => true,
:ssl_ca_cert => nil,
:ssl_verify_mode => nil,
+ :ssl_min_version => nil,
+ :ssl_max_version => nil,
:ftp_active_mode => false,
:redirect => true,
:encoding => nil,
@@ -298,6 +300,8 @@ module OpenURI
require 'net/https'
http.use_ssl = true
http.verify_mode = options[:ssl_verify_mode] || OpenSSL::SSL::VERIFY_PEER
+ http.min_version = options[:ssl_min_version]
+ http.max_version = options[:ssl_max_version]
store = OpenSSL::X509::Store.new
if options[:ssl_ca_cert]
Array(options[:ssl_ca_cert]).each do |cert|
@@ -353,7 +357,8 @@ module OpenURI
when Net::HTTPMovedPermanently, # 301
Net::HTTPFound, # 302
Net::HTTPSeeOther, # 303
- Net::HTTPTemporaryRedirect # 307
+ Net::HTTPTemporaryRedirect, # 307
+ Net::HTTPPermanentRedirect # 308
begin
loc_uri = URI.parse(resp['location'])
rescue URI::InvalidURIError
@@ -410,6 +415,13 @@ module OpenURI
end
end
+ # :stopdoc:
+ RE_LWS = /[\r\n\t ]+/n
+ RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n
+ RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])*"}n
+ RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n
+ # :startdoc:
+
# Mixin for holding meta-information.
module Meta
def Meta.init(obj, src=nil) # :nodoc:
@@ -487,13 +499,6 @@ module OpenURI
end
end
- # :stopdoc:
- RE_LWS = /[\r\n\t ]+/n
- RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n
- RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])*"}n
- RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n
- # :startdoc:
-
def content_type_parse # :nodoc:
vs = @metas['content-type']
# The last (?:;#{RE_LWS}?)? matches extra ";" which violates RFC2045.
@@ -698,6 +703,20 @@ module OpenURI
#
# :ssl_verify_mode is used to specify openssl verify mode.
#
+ # [:ssl_min_version]
+ # Synopsis:
+ # :ssl_min_version=>:TLS1_2
+ #
+ # :ssl_min_version option specifies the minimum allowed SSL/TLS protocol
+ # version. See also OpenSSL::SSL::SSLContext#min_version=.
+ #
+ # [:ssl_max_version]
+ # Synopsis:
+ # :ssl_max_version=>:TLS1_2
+ #
+ # :ssl_max_version option specifies the maximum allowed SSL/TLS protocol
+ # version. See also OpenSSL::SSL::SSLContext#max_version=.
+ #
# [:ftp_active_mode]
# Synopsis:
# :ftp_active_mode=>bool
diff --git a/lib/open3/version.rb b/lib/open3/version.rb
index 5a8e84b4ae..b6b6ee2c9c 100644
--- a/lib/open3/version.rb
+++ b/lib/open3/version.rb
@@ -1,3 +1,3 @@
module Open3
- VERSION = "0.1.1"
+ VERSION = "0.1.2"
end
diff --git a/lib/optparse.rb b/lib/optparse.rb
index ec37bde3bd..53a4387bd8 100644
--- a/lib/optparse.rb
+++ b/lib/optparse.rb
@@ -425,7 +425,7 @@
# If you have any questions, file a ticket at http://bugs.ruby-lang.org.
#
class OptionParser
- OptionParser::Version = "0.2.0"
+ OptionParser::Version = "0.3.1"
# :stopdoc:
NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
@@ -765,15 +765,15 @@ class OptionParser
end
#
- # Switch that takes an argument, which does not begin with '-'.
+ # Switch that takes an argument, which does not begin with '-' or is '-'.
#
class PlacedArgument < self
#
- # Returns nil if argument is not present or begins with '-'.
+ # Returns nil if argument is not present or begins with '-' and is not '-'.
#
def parse(arg, argv, &error)
- if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
+ if !(val = arg) and (argv.empty? or /\A-./ =~ (val = argv[0]))
return nil, block, nil
end
opt = (val = parse_arg(val, &error))[1]
@@ -1148,6 +1148,7 @@ XXX
@summary_indent = indent
@default_argv = ARGV
@require_exact = false
+ @raise_unknown = true
add_officious
yield self if block_given?
end
@@ -1225,6 +1226,9 @@ XXX
# abbreviated long option as short option).
attr_accessor :require_exact
+ # Whether to raise at unknown option.
+ attr_accessor :raise_unknown
+
#
# Heading banner preceding summary.
#
@@ -1502,7 +1506,7 @@ XXX
style = notwice(default_style.guess(arg = o), style, 'style')
default_pattern, conv = search(:atype, Object) unless default_pattern
else
- desc.push(o)
+ desc.push(o) if o && !o.empty?
end
end
@@ -1639,9 +1643,11 @@ XXX
begin
sw, = complete(:long, opt, true)
if require_exact && !sw.long.include?(arg)
+ throw :terminate, arg unless raise_unknown
raise InvalidOption, arg
end
rescue ParseError
+ throw :terminate, arg unless raise_unknown
raise $!.set_option(arg, true)
end
begin
@@ -1673,6 +1679,7 @@ XXX
end
end
rescue ParseError
+ throw :terminate, arg unless raise_unknown
raise $!.set_option(arg, true)
end
begin
@@ -1903,10 +1910,13 @@ XXX
# directory ~/.options, then the basename with '.options' suffix
# under XDG and Haiku standard places.
#
- def load(filename = nil)
+ # The optional +into+ keyword argument works exactly like that accepted in
+ # method #parse.
+ #
+ def load(filename = nil, into: nil)
unless filename
basename = File.basename($0, '.*')
- return true if load(File.expand_path(basename, '~/.options')) rescue nil
+ return true if load(File.expand_path(basename, '~/.options'), into: into) rescue nil
basename << ".options"
return [
# XDG
@@ -1918,11 +1928,11 @@ XXX
'~/config/settings',
].any? {|dir|
next if !dir or dir.empty?
- load(File.expand_path(basename, dir)) rescue nil
+ load(File.expand_path(basename, dir), into: into) rescue nil
}
end
begin
- parse(*IO.readlines(filename).each {|s| s.chomp!})
+ parse(*File.readlines(filename, chomp: true), into: into)
true
rescue Errno::ENOENT, Errno::ENOTDIR
false
@@ -2074,10 +2084,23 @@ XXX
f |= Regexp::IGNORECASE if /i/ =~ o
f |= Regexp::MULTILINE if /m/ =~ o
f |= Regexp::EXTENDED if /x/ =~ o
- k = o.delete("imx")
- k = nil if k.empty?
+ case o = o.delete("imx")
+ when ""
+ when "u"
+ s = s.encode(Encoding::UTF_8)
+ when "e"
+ s = s.encode(Encoding::EUC_JP)
+ when "s"
+ s = s.encode(Encoding::SJIS)
+ when "n"
+ f |= Regexp::NOENCODING
+ else
+ raise OptionParser::InvalidArgument, "unknown regexp option - #{o}"
+ end
+ else
+ s ||= all
end
- Regexp.new(s || all, f, k)
+ Regexp.new(s, f)
end
#
diff --git a/lib/pp.gemspec b/lib/pp.gemspec
index d4b0be83df..3f08f400c4 100644
--- a/lib/pp.gemspec
+++ b/lib/pp.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "pp"
- spec.version = "0.3.0"
+ spec.version = "0.4.0"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/pp.rb b/lib/pp.rb
index f43356a3df..81551aa116 100644
--- a/lib/pp.rb
+++ b/lib/pp.rb
@@ -416,6 +416,26 @@ class Struct # :nodoc:
end
end
+class Data # :nodoc:
+ def pretty_print(q) # :nodoc:
+ q.group(1, sprintf("#<data %s", PP.mcall(self, Kernel, :class).name), '>') {
+ q.seplist(PP.mcall(self, Data, :members), lambda { q.text "," }) {|member|
+ q.breakable
+ q.text member.to_s
+ q.text '='
+ q.group(1) {
+ q.breakable ''
+ q.pp public_send(member)
+ }
+ }
+ }
+ end
+
+ def pretty_print_cycle(q) # :nodoc:
+ q.text sprintf("#<data %s:...>", PP.mcall(self, Kernel, :class).name)
+ end
+end if "3.2" <= RUBY_VERSION
+
class Range # :nodoc:
def pretty_print(q) # :nodoc:
q.pp self.begin
diff --git a/lib/pstore.rb b/lib/pstore.rb
index 8d7137aa39..72deaa1017 100644
--- a/lib/pstore.rb
+++ b/lib/pstore.rb
@@ -71,7 +71,7 @@ require "digest"
# when the store is created (see PStore.new).
# The objects are stored and retrieved using
# module Marshal, which means that certain objects cannot be added to the store;
-# see {Marshal::dump}[https://docs.ruby-lang.org/en/master/Marshal.html#method-c-dump].
+# see {Marshal::dump}[rdoc-ref:Marshal.dump].
#
# == Entries
#
@@ -79,11 +79,11 @@ require "digest"
# Each entry has a key and a value, just as in a hash:
#
# - Key: as in a hash, the key can be (almost) any object;
-# see {Hash Keys}[https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Hash+Keys].
+# see {Hash Keys}[rdoc-ref:Hash@Hash+Keys].
# You may find it convenient to keep it simple by using only
# symbols or strings as keys.
# - Value: the value may be any object that can be marshalled by \Marshal
-# (see {Marshal::dump}[https://docs.ruby-lang.org/en/master/Marshal.html#method-c-dump])
+# (see {Marshal::dump}[rdoc-ref:Marshal.dump])
# and in fact may be a collection
# (e.g., an array, a hash, a set, a range, etc).
# That collection may in turn contain nested objects,
@@ -194,7 +194,7 @@ require "digest"
# end
#
# And recall that you can use
-# {dig methods}[https://docs.ruby-lang.org/en/master/dig_methods_rdoc.html]
+# {dig methods}[rdoc-ref:dig_methods.rdoc]
# in a returned hierarchy of objects.
#
# == Working with the Store
@@ -326,7 +326,7 @@ require "digest"
# end
#
class PStore
- VERSION = "0.1.1"
+ VERSION = "0.1.2"
RDWR_ACCESS = {mode: IO::RDWR | IO::CREAT | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
RD_ACCESS = {mode: IO::RDONLY | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
diff --git a/lib/racc/info.rb b/lib/racc/info.rb
index bb1f100adc..37bff7edba 100644
--- a/lib/racc/info.rb
+++ b/lib/racc/info.rb
@@ -11,7 +11,7 @@
#++
module Racc
- VERSION = '1.6.0'
+ VERSION = '1.6.2'
Version = VERSION
Copyright = 'Copyright (c) 1999-2006 Minero Aoki'
end
diff --git a/lib/racc/parser-text.rb b/lib/racc/parser-text.rb
index 4188fa853d..0579f4ce9b 100644
--- a/lib/racc/parser-text.rb
+++ b/lib/racc/parser-text.rb
@@ -22,7 +22,7 @@ module Racc
class ParseError < StandardError; end
end
unless defined?(::ParseError)
- ParseError = Racc::ParseError
+ ParseError = Racc::ParseError # :nodoc:
end
# Racc is a LALR(1) parser generator.
diff --git a/lib/racc/racc.gemspec b/lib/racc/racc.gemspec
index 7ee706f63d..1095c8f47e 100644
--- a/lib/racc/racc.gemspec
+++ b/lib/racc/racc.gemspec
@@ -20,7 +20,7 @@ Racc is a LALR(1) parser generator.
DESC
s.authors = ["Minero Aoki", "Aaron Patterson"]
s.email = [nil, "aaron@tenderlovemaking.com"]
- s.homepage = "https://i.loveruby.net/en/projects/racc/"
+ s.homepage = "https://github.com/ruby/racc"
s.licenses = ["Ruby", "BSD-2-Clause"]
s.executables = ["racc"]
s.files = [
diff --git a/lib/racc/statetransitiontable.rb b/lib/racc/statetransitiontable.rb
index cae411c98b..d75fa1657a 100644
--- a/lib/racc/statetransitiontable.rb
+++ b/lib/racc/statetransitiontable.rb
@@ -216,7 +216,7 @@ module Racc
end
i = ii
end
- Regexp.compile(map, nil, 'n')
+ Regexp.compile(map, Regexp::NOENCODING)
end
def set_table(entries, dummy, tbl, chk, ptr)
diff --git a/lib/random/formatter.rb b/lib/random/formatter.rb
index 744853a4b7..4dea61c16c 100644
--- a/lib/random/formatter.rb
+++ b/lib/random/formatter.rb
@@ -1,9 +1,14 @@
# -*- coding: us-ascii -*-
# frozen_string_literal: true
-# == Random number formatter.
+# == \Random number formatter.
#
-# Formats generated random numbers in many manners.
+# Formats generated random numbers in many manners. When <tt>'random/formatter'</tt>
+# is required, several methods are added to empty core module <tt>Random::Formatter</tt>,
+# making them available as Random's instance and module methods.
+#
+# Standard library SecureRandom is also extended with the module, and the methods
+# described below are available as a module methods in it.
#
# === Examples
#
@@ -11,34 +16,45 @@
#
# require 'random/formatter'
#
+# prng = Random.new
# prng.hex(10) #=> "52750b30ffbc7de3b362"
# prng.hex(10) #=> "92b15d6c8dc4beb5f559"
# prng.hex(13) #=> "39b290146bea6ce975c37cfc23"
+# # or just
+# Random.hex #=> "1aed0c631e41be7f77365415541052ee"
#
# Generate random base64 strings:
#
# prng.base64(10) #=> "EcmTPZwWRAozdA=="
# prng.base64(10) #=> "KO1nIU+p9DKxGg=="
# prng.base64(12) #=> "7kJSM/MzBJI+75j8"
+# Random.base64(4) #=> "bsQ3fQ=="
#
# Generate random binary strings:
#
# prng.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
# prng.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
+# Random.random_bytes(6) #=> "\xA1\xE6Lr\xC43"
#
# Generate alphanumeric strings:
#
# prng.alphanumeric(10) #=> "S8baxMJnPl"
# prng.alphanumeric(10) #=> "aOxAg8BAJe"
+# Random.alphanumeric #=> "TmP9OsJHJLtaZYhP"
#
# Generate UUIDs:
#
# prng.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
# prng.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
+# Random.uuid #=> "f14e0271-de96-45cc-8911-8910292a42cd"
+#
+# All methods are available in the standard library SecureRandom, too:
+#
+# SecureRandom.hex #=> "05b45376a30c67238eb93b16499e50cf"
module Random::Formatter
- # Random::Formatter#random_bytes generates a random binary string.
+ # Generate a random binary string.
#
# The argument _n_ specifies the length of the result string.
#
@@ -49,14 +65,16 @@ module Random::Formatter
#
# require 'random/formatter'
#
- # prng.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
+ # Random.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
+ # # or
+ # prng = Random.new
# prng.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"
def random_bytes(n=nil)
n = n ? n.to_int : 16
gen_random(n)
end
- # Random::Formatter#hex generates a random hexadecimal string.
+ # Generate a random hexadecimal string.
#
# The argument _n_ specifies the length, in bytes, of the random number to be generated.
# The length of the resulting hexadecimal string is twice of _n_.
@@ -68,13 +86,15 @@ module Random::Formatter
#
# require 'random/formatter'
#
- # prng.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
+ # Random.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
+ # # or
+ # prng = Random.new
# prng.hex #=> "91dc3bfb4de5b11d029d376634589b61"
def hex(n=nil)
random_bytes(n).unpack1("H*")
end
- # Random::Formatter#base64 generates a random base64 string.
+ # Generate a random base64 string.
#
# The argument _n_ specifies the length, in bytes, of the random number
# to be generated. The length of the result string is about 4/3 of _n_.
@@ -86,7 +106,9 @@ module Random::Formatter
#
# require 'random/formatter'
#
- # prng.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
+ # Random.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
+ # # or
+ # prng = Random.new
# prng.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="
#
# See RFC 3548 for the definition of base64.
@@ -94,7 +116,7 @@ module Random::Formatter
[random_bytes(n)].pack("m0")
end
- # Random::Formatter#urlsafe_base64 generates a random URL-safe base64 string.
+ # Generate a random URL-safe base64 string.
#
# The argument _n_ specifies the length, in bytes, of the random number
# to be generated. The length of the result string is about 4/3 of _n_.
@@ -112,7 +134,9 @@ module Random::Formatter
#
# require 'random/formatter'
#
- # prng.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
+ # Random.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
+ # # or
+ # prng = Random.new
# prng.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
#
# prng.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="
@@ -126,12 +150,14 @@ module Random::Formatter
s
end
- # Random::Formatter#uuid generates a random v4 UUID (Universally Unique IDentifier).
+ # Generate a random v4 UUID (Universally Unique IDentifier).
#
# require 'random/formatter'
#
- # prng.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
- # prng.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
+ # Random.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
+ # Random.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
+ # # or
+ # prng = Random.new
# prng.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"
#
# The version 4 UUID is purely random (except the version).
@@ -139,7 +165,7 @@ module Random::Formatter
#
# The result contains 122 random bits (15.25 random bytes).
#
- # See RFC 4122 for details of UUID.
+ # See RFC4122[https://datatracker.ietf.org/doc/html/rfc4122] for details of UUID.
#
def uuid
ary = random_bytes(16).unpack("NnnnnN")
@@ -152,7 +178,7 @@ module Random::Formatter
self.bytes(n)
end
- # Random::Formatter#choose generates a string that randomly draws from a
+ # Generate a string that randomly draws from a
# source array of characters.
#
# The argument _source_ specifies the array of characters from which
@@ -196,7 +222,7 @@ module Random::Formatter
end
ALPHANUMERIC = [*'A'..'Z', *'a'..'z', *'0'..'9']
- # Random::Formatter#alphanumeric generates a random alphanumeric string.
+ # Generate a random alphanumeric string.
#
# The argument _n_ specifies the length, in characters, of the alphanumeric
# string to be generated.
@@ -208,7 +234,9 @@ module Random::Formatter
#
# require 'random/formatter'
#
- # prng.alphanumeric #=> "2BuBuLf3WfSKyQbR"
+ # Random.alphanumeric #=> "2BuBuLf3WfSKyQbR"
+ # # or
+ # prng = Random.new
# prng.alphanumeric(10) #=> "i6K93NdqiH"
def alphanumeric(n=nil)
n = 16 if n.nil?
diff --git a/lib/rdoc/generator/markup.rb b/lib/rdoc/generator/markup.rb
index 41e132450d..b54265717c 100644
--- a/lib/rdoc/generator/markup.rb
+++ b/lib/rdoc/generator/markup.rb
@@ -109,7 +109,7 @@ class RDoc::MethodAttr
lines.shift if src =~ /\A.*#\ *File/i # remove '# File' comment
lines.each do |line|
if line =~ /^ *(?=\S)/
- n = $&.length
+ n = $~.end(0)
indent = n if n < indent
break if n == 0
end
diff --git a/lib/rdoc/generator/template/darkfish/_head.rhtml b/lib/rdoc/generator/template/darkfish/_head.rhtml
index 4f331245c3..d5aed3e9ef 100644
--- a/lib/rdoc/generator/template/darkfish/_head.rhtml
+++ b/lib/rdoc/generator/template/darkfish/_head.rhtml
@@ -3,18 +3,18 @@
<title><%= h @title %></title>
<script type="text/javascript">
- var rdoc_rel_prefix = "<%= asset_rel_prefix %>/";
- var index_rel_prefix = "<%= rel_prefix %>/";
+ var rdoc_rel_prefix = "<%= h asset_rel_prefix %>/";
+ var index_rel_prefix = "<%= h rel_prefix %>/";
</script>
-<script src="<%= asset_rel_prefix %>/js/navigation.js" defer></script>
-<script src="<%= asset_rel_prefix %>/js/search.js" defer></script>
-<script src="<%= asset_rel_prefix %>/js/search_index.js" defer></script>
-<script src="<%= asset_rel_prefix %>/js/searcher.js" defer></script>
-<script src="<%= asset_rel_prefix %>/js/darkfish.js" defer></script>
+<script src="<%= h asset_rel_prefix %>/js/navigation.js" defer></script>
+<script src="<%= h asset_rel_prefix %>/js/search.js" defer></script>
+<script src="<%= h asset_rel_prefix %>/js/search_index.js" defer></script>
+<script src="<%= h asset_rel_prefix %>/js/searcher.js" defer></script>
+<script src="<%= h asset_rel_prefix %>/js/darkfish.js" defer></script>
-<link href="<%= asset_rel_prefix %>/css/fonts.css" rel="stylesheet">
-<link href="<%= asset_rel_prefix %>/css/rdoc.css" rel="stylesheet">
+<link href="<%= h asset_rel_prefix %>/css/fonts.css" rel="stylesheet">
+<link href="<%= h asset_rel_prefix %>/css/rdoc.css" rel="stylesheet">
<%- @options.template_stylesheets.each do |stylesheet| -%>
-<link href="<%= asset_rel_prefix %>/<%= File.basename stylesheet %>" rel="stylesheet">
+<link href="<%= h asset_rel_prefix %>/<%= File.basename stylesheet %>" rel="stylesheet">
<%- end -%>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml
index 0ed683ca14..3f68f0c0dc 100644
--- a/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml
@@ -12,18 +12,18 @@
<%- end.each do |n, files| -%>
<%- f = files.shift -%>
<%- if files.empty? -%>
- <li><a href="<%= rel_prefix %>/<%= f.path %>"><%= h f.page_name %></a>
+ <li><a href="<%= rel_prefix %>/<%= h f.path %>"><%= h f.page_name %></a>
<%- next -%>
<%- end -%>
<li><details<% if dir == n %> open<% end %>><summary><%
if n == f.page_name
- %><a href="<%= rel_prefix %>/<%= f.path %>"><%= h n %></a><%
+ %><a href="<%= rel_prefix %>/<%= h f.path %>"><%= h n %></a><%
else
%><%= h n %><% files.unshift(f)
end %></summary>
<ul class="link-list">
<%- files.each do |f| -%>
- <li><a href="<%= rel_prefix %>/<%= f.path %>"><%= h f.page_name %></a>
+ <li><a href="<%= rel_prefix %>/<%= h f.path %>"><%= h f.page_name %></a>
<%- end -%>
</ul></details>
<%- end -%>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml
index bf70819f64..b1e047b5f7 100644
--- a/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml
@@ -3,16 +3,37 @@
else
current.comment
end
- table = current.parse(comment).table_of_contents
+ table = current.parse(comment).table_of_contents.dup
if table.length > 1 then %>
<div class="nav-section">
<h3>Table of Contents</h3>
+ <%- display_link = proc do |heading| -%>
+ <a href="#<%= heading.label current %>"><%= heading.plain_html %></a>
+ <%- end -%>
+
+ <%- list_siblings = proc do -%>
+ <%- level = table.first&.level -%>
+ <%- while table.first && table.first.level >= level -%>
+ <%- heading = table.shift -%>
+ <%- if table.first.nil? || table.first.level <= heading.level -%>
+ <li><% display_link.call heading -%>
+ <%- else -%>
+ <li>
+ <details open>
+ <summary><%- display_link.call heading -%></summary>
+ <ul class="link-list" role="directory">
+ <% list_siblings.call %>
+ </ul>
+ </details>
+ </li>
+ <%- end -%>
+ <%- end -%>
+ <%- end -%>
+
<ul class="link-list" role="directory">
-<%- table.each do |heading| -%>
- <li><a href="#<%= heading.label current %>"><%= heading.plain_html %></a>
-<%- end -%>
+ <% list_siblings.call %>
</ul>
</div>
<%- end -%>
diff --git a/lib/rdoc/generator/template/darkfish/class.rhtml b/lib/rdoc/generator/template/darkfish/class.rhtml
index 5d7b6a1b80..97d175dddc 100644
--- a/lib/rdoc/generator/template/darkfish/class.rhtml
+++ b/lib/rdoc/generator/template/darkfish/class.rhtml
@@ -98,28 +98,30 @@
<%- methods.each do |method| -%>
<div id="<%= method.aref %>" class="method-detail <%= method.is_alias_for ? "method-alias" : '' %>">
- <%- if (call_seq = method.call_seq) then -%>
- <%- call_seq.strip.split("\n").each_with_index do |call_seq, i| -%>
- <div class="method-heading">
- <span class="method-callseq">
- <%= h(call_seq.strip.
- gsub( /^\w+\./m, '')).
- gsub(/(.*)[-=]&gt;/, '\1&rarr;') %>
- </span>
- <%- if i == 0 and method.token_stream then -%>
- <span class="method-click-advice">click to toggle source</span>
- <%- end -%>
- </div>
- <%- end -%>
- <%- else -%>
- <div class="method-heading">
- <span class="method-name"><%= h method.name %></span><span
- class="method-args"><%= h method.param_seq %></span>
- <%- if method.token_stream then -%>
- <span class="method-click-advice">click to toggle source</span>
+ <div class="method-header">
+ <%- if (call_seq = method.call_seq) then -%>
+ <%- call_seq.strip.split("\n").each_with_index do |call_seq, i| -%>
+ <div class="method-heading">
+ <span class="method-callseq">
+ <%= h(call_seq.strip.
+ gsub( /^\w+\./m, '')).
+ gsub(/(.*)[-=]&gt;/, '\1&rarr;') %>
+ </span>
+ <%- if i == 0 and method.token_stream then -%>
+ <span class="method-click-advice">click to toggle source</span>
+ <%- end -%>
+ </div>
+ <%- end -%>
+ <%- else -%>
+ <div class="method-heading">
+ <span class="method-name"><%= h method.name %></span><span
+ class="method-args"><%= h method.param_seq %></span>
+ <%- if method.token_stream then -%>
+ <span class="method-click-advice">click to toggle source</span>
+ <%- end -%>
+ </div>
<%- end -%>
</div>
- <%- end -%>
<div class="method-description">
<%- if method.comment then -%>
diff --git a/lib/rdoc/generator/template/darkfish/index.rhtml b/lib/rdoc/generator/template/darkfish/index.rhtml
index 13fa3dcc7f..423e225b68 100644
--- a/lib/rdoc/generator/template/darkfish/index.rhtml
+++ b/lib/rdoc/generator/template/darkfish/index.rhtml
@@ -17,6 +17,6 @@
main_page = @files.find { |f| f.full_name == @options.main_page } then %>
<%= main_page.description %>
<%- else -%>
-<p>This is the API documentation for <%= @title %>.
+<p>This is the API documentation for <%= h @title %>.
<%- end -%>
</main>
diff --git a/lib/rdoc/generator/template/darkfish/js/darkfish.js b/lib/rdoc/generator/template/darkfish/js/darkfish.js
index 111bbf8eb9..d0c9467751 100644
--- a/lib/rdoc/generator/template/darkfish/js/darkfish.js
+++ b/lib/rdoc/generator/template/darkfish/js/darkfish.js
@@ -54,7 +54,7 @@ function hookSearch() {
var html = '';
// TODO add relative path to <script> per-page
- html += '<p class="search-match"><a href="' + index_rel_prefix + result.path + '">' + this.hlt(result.title);
+ html += '<p class="search-match"><a href="' + index_rel_prefix + this.escapeHTML(result.path) + '">' + this.hlt(result.title);
if (result.params)
html += '<span class="params">' + result.params + '</span>';
html += '</a>';
diff --git a/lib/rdoc/generator/template/darkfish/js/search.js b/lib/rdoc/generator/template/darkfish/js/search.js
index b558ca5b4f..58e52afecf 100644
--- a/lib/rdoc/generator/template/darkfish/js/search.js
+++ b/lib/rdoc/generator/template/darkfish/js/search.js
@@ -101,7 +101,7 @@ Search.prototype = Object.assign({}, Navigation, new function() {
}
this.escapeHTML = function(html) {
- return html.replace(/[&<>]/g, function(c) {
+ return html.replace(/[&<>"`']/g, function(c) {
return '&#' + c.charCodeAt(0) + ';';
});
}
diff --git a/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml b/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
index 303d7016cc..941ff9d630 100644
--- a/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
+++ b/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
@@ -8,14 +8,14 @@
<ul>
<%- simple_files.sort.each do |file| -%>
<li class="file">
- <a href="<%= file.path %>"><%= h file.page_name %></a>
+ <a href="<%= h file.path %>"><%= h file.page_name %></a>
<%
# HACK table_of_contents should not exist on Document
table = file.parse(file.comment).table_of_contents
unless table.empty? then %>
<ul>
<%- table.each do |heading| -%>
- <li><a href="<%= file.path %>#<%= heading.aref %>"><%= heading.plain_html %></a>
+ <li><a href="<%= h file.path %>#<%= heading.aref %>"><%= heading.plain_html %></a>
<%- end -%>
</ul>
<%- end -%>
diff --git a/lib/rdoc/markdown.rb b/lib/rdoc/markdown.rb
index 25a7217d3c..a0709b6352 100644
--- a/lib/rdoc/markdown.rb
+++ b/lib/rdoc/markdown.rb
@@ -209,45 +209,75 @@ class RDoc::Markdown
attr_accessor :result, :pos
def current_column(target=pos)
- if c = string.rindex("\n", target-1)
- return target - c - 1
+ if string[target] == "\n" && (c = string.rindex("\n", target-1) || -1)
+ return target - c
+ elsif c = string.rindex("\n", target)
+ return target - c
end
target + 1
end
+ def position_line_offsets
+ unless @position_line_offsets
+ @position_line_offsets = []
+ total = 0
+ string.each_line do |line|
+ total += line.size
+ @position_line_offsets << total
+ end
+ end
+ @position_line_offsets
+ end
+
if [].respond_to? :bsearch_index
def current_line(target=pos)
- unless @line_offsets
- @line_offsets = []
- total = 0
- string.each_line do |line|
- total += line.size
- @line_offsets << total
- end
+ if line = position_line_offsets.bsearch_index {|x| x > target }
+ return line + 1
end
-
- @line_offsets.bsearch_index {|x| x >= target } + 1 || -1
+ raise "Target position #{target} is outside of string"
end
else
def current_line(target=pos)
- cur_offset = 0
- cur_line = 0
-
- string.each_line do |line|
- cur_line += 1
- cur_offset += line.size
- return cur_line if cur_offset >= target
+ if line = position_line_offsets.index {|x| x > target }
+ return line + 1
end
- -1
+ raise "Target position #{target} is outside of string"
end
end
+ def current_character(target=pos)
+ if target < 0 || target >= string.size
+ raise "Target position #{target} is outside of string"
+ end
+ string[target, 1]
+ end
+
+ KpegPosInfo = Struct.new(:pos, :lno, :col, :line, :char)
+
+ def current_pos_info(target=pos)
+ l = current_line target
+ c = current_column target
+ ln = get_line(l-1)
+ chr = string[target,1]
+ KpegPosInfo.new(target, l, c, ln, chr)
+ end
+
def lines
- lines = []
- string.each_line { |l| lines << l }
- lines
+ string.lines
+ end
+
+ def get_line(no)
+ loff = position_line_offsets
+ if no < 0
+ raise "Line No is out of range: #{no} < 0"
+ elsif no >= loff.size
+ raise "Line No is out of range: #{no} >= #{loff.size}"
+ end
+ lend = loff[no]-1
+ lstart = no > 0 ? loff[no-1] : 0
+ string[lstart..lend]
end
@@ -261,6 +291,7 @@ class RDoc::Markdown
@string = string
@string_size = string ? string.size : 0
@pos = pos
+ @position_line_offsets = nil
end
def show_pos
@@ -285,30 +316,22 @@ class RDoc::Markdown
end
def failure_caret
- l = current_line @failing_rule_offset
- c = current_column @failing_rule_offset
-
- line = lines[l-1]
- "#{line}\n#{' ' * (c - 1)}^"
+ p = current_pos_info @failing_rule_offset
+ "#{p.line.chomp}\n#{' ' * (p.col - 1)}^"
end
def failure_character
- l = current_line @failing_rule_offset
- c = current_column @failing_rule_offset
- lines[l-1][c-1, 1]
+ current_character @failing_rule_offset
end
def failure_oneline
- l = current_line @failing_rule_offset
- c = current_column @failing_rule_offset
-
- char = lines[l-1][c-1, 1]
+ p = current_pos_info @failing_rule_offset
if @failed_rule.kind_of? Symbol
info = self.class::Rules[@failed_rule]
- "@#{l}:#{c} failed rule '#{info.name}', got '#{char}'"
+ "@#{p.lno}:#{p.col} failed rule '#{info.name}', got '#{p.char}'"
else
- "@#{l}:#{c} failed rule '#{@failed_rule}', got '#{char}'"
+ "@#{p.lno}:#{p.col} failed rule '#{@failed_rule}', got '#{p.char}'"
end
end
@@ -321,10 +344,9 @@ class RDoc::Markdown
def show_error(io=STDOUT)
error_pos = @failing_rule_offset
- line_no = current_line(error_pos)
- col_no = current_column(error_pos)
+ p = current_pos_info(error_pos)
- io.puts "On line #{line_no}, column #{col_no}:"
+ io.puts "On line #{p.lno}, column #{p.col}:"
if @failed_rule.kind_of? Symbol
info = self.class::Rules[@failed_rule]
@@ -333,10 +355,9 @@ class RDoc::Markdown
io.puts "Failed to match rule '#{@failed_rule}'"
end
- io.puts "Got: #{string[error_pos,1].inspect}"
- line = lines[line_no-1]
- io.puts "=> #{line}"
- io.print(" " * (col_no + 3))
+ io.puts "Got: #{p.char.inspect}"
+ io.puts "=> #{p.line}"
+ io.print(" " * (p.col + 2))
io.puts "^"
end
@@ -445,6 +466,7 @@ class RDoc::Markdown
end
def apply_with_args(rule, *args)
+ @result = nil
memo_key = [rule, args]
if m = @memoizations[memo_key][@pos]
@pos = m.pos
@@ -478,6 +500,7 @@ class RDoc::Markdown
end
def apply(rule)
+ @result = nil
if m = @memoizations[rule][@pos]
@pos = m.pos
if !m.set
@@ -811,7 +834,7 @@ class RDoc::Markdown
@note_order.each_with_index do |ref, index|
label = index + 1
- note = @footnotes[ref]
+ note = @footnotes[ref] or raise ParseError, "footnote [^#{ref}] not found"
link = "{^#{label}}[rdoc-label:footmark-#{label}:foottext-#{label}] "
note.parts.unshift link
@@ -15533,7 +15556,7 @@ class RDoc::Markdown
return _tmp
end
- # RawNoteBlock = @StartList:a (!@BlankLine OptionallyIndentedLine:l { a << l })+ < @BlankLine* > { a << text } { a }
+ # RawNoteBlock = @StartList:a (!@BlankLine !RawNoteReference OptionallyIndentedLine:l { a << l })+ < @BlankLine* > { a << text } { a }
def _RawNoteBlock
_save = self.pos
@@ -15556,6 +15579,14 @@ class RDoc::Markdown
self.pos = _save2
break
end
+ _save4 = self.pos
+ _tmp = apply(:_RawNoteReference)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save2
+ break
+ end
_tmp = apply(:_OptionallyIndentedLine)
l = @result
unless _tmp
@@ -15573,26 +15604,34 @@ class RDoc::Markdown
if _tmp
while true
- _save4 = self.pos
+ _save5 = self.pos
while true # sequence
- _save5 = self.pos
+ _save6 = self.pos
_tmp = _BlankLine()
_tmp = _tmp ? nil : true
- self.pos = _save5
+ self.pos = _save6
unless _tmp
- self.pos = _save4
+ self.pos = _save5
+ break
+ end
+ _save7 = self.pos
+ _tmp = apply(:_RawNoteReference)
+ _tmp = _tmp ? nil : true
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save5
break
end
_tmp = apply(:_OptionallyIndentedLine)
l = @result
unless _tmp
- self.pos = _save4
+ self.pos = _save5
break
end
@result = begin; a << l ; end
_tmp = true
unless _tmp
- self.pos = _save4
+ self.pos = _save5
end
break
end # end sequence
@@ -15894,7 +15933,7 @@ class RDoc::Markdown
return _tmp
end
- # Table = &{ github? } TableRow:header TableLine:line TableRow+:body { table = RDoc::Markup::Table.new(header, line, body) }
+ # Table = &{ github? } TableHead:header TableLine:line TableRow+:body { table = RDoc::Markup::Table.new(header, line, body) }
def _Table
_save = self.pos
@@ -15906,7 +15945,7 @@ class RDoc::Markdown
self.pos = _save
break
end
- _tmp = apply(:_TableRow)
+ _tmp = apply(:_TableHead)
header = @result
unless _tmp
self.pos = _save
@@ -15950,18 +15989,18 @@ class RDoc::Markdown
return _tmp
end
- # TableRow = TableItem+:row "|" @Newline { row }
- def _TableRow
+ # TableHead = TableItem2+:items "|"? @Newline { items }
+ def _TableHead
_save = self.pos
while true # sequence
_save1 = self.pos
_ary = []
- _tmp = apply(:_TableItem)
+ _tmp = apply(:_TableItem2)
if _tmp
_ary << @result
while true
- _tmp = apply(:_TableItem)
+ _tmp = apply(:_TableItem2)
_ary << @result if _tmp
break unless _tmp
end
@@ -15970,13 +16009,18 @@ class RDoc::Markdown
else
self.pos = _save1
end
- row = @result
+ items = @result
unless _tmp
self.pos = _save
break
end
+ _save2 = self.pos
_tmp = match_string("|")
unless _tmp
+ _tmp = true
+ self.pos = _save2
+ end
+ unless _tmp
self.pos = _save
break
end
@@ -15985,7 +16029,7 @@ class RDoc::Markdown
self.pos = _save
break
end
- @result = begin; row ; end
+ @result = begin; items ; end
_tmp = true
unless _tmp
self.pos = _save
@@ -15993,90 +16037,92 @@ class RDoc::Markdown
break
end # end sequence
- set_failed_rule :_TableRow unless _tmp
+ set_failed_rule :_TableHead unless _tmp
return _tmp
end
- # TableItem = "|" < (!"|" !@Newline .)+ > { text.strip }
- def _TableItem
+ # TableRow = ((TableItem:item1 TableItem2*:items { [item1, *items] }):row | TableItem2+:row) "|"? @Newline { row }
+ def _TableRow
_save = self.pos
while true # sequence
- _tmp = match_string("|")
- unless _tmp
- self.pos = _save
- break
- end
- _text_start = self.pos
+
_save1 = self.pos
+ while true # choice
- _save2 = self.pos
- while true # sequence
- _save3 = self.pos
- _tmp = match_string("|")
- _tmp = _tmp ? nil : true
- self.pos = _save3
- unless _tmp
- self.pos = _save2
+ _save2 = self.pos
+ while true # sequence
+ _tmp = apply(:_TableItem)
+ item1 = @result
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _ary = []
+ while true
+ _tmp = apply(:_TableItem2)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ items = @result
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ @result = begin; [item1, *items] ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save2
+ end
break
- end
+ end # end sequence
+
+ row = @result
+ break if _tmp
+ self.pos = _save1
_save4 = self.pos
- _tmp = _Newline()
- _tmp = _tmp ? nil : true
- self.pos = _save4
- unless _tmp
- self.pos = _save2
- break
- end
- _tmp = get_byte
- unless _tmp
- self.pos = _save2
+ _ary = []
+ _tmp = apply(:_TableItem2)
+ if _tmp
+ _ary << @result
+ while true
+ _tmp = apply(:_TableItem2)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save4
end
+ row = @result
+ break if _tmp
+ self.pos = _save1
break
- end # end sequence
-
- if _tmp
- while true
-
- _save5 = self.pos
- while true # sequence
- _save6 = self.pos
- _tmp = match_string("|")
- _tmp = _tmp ? nil : true
- self.pos = _save6
- unless _tmp
- self.pos = _save5
- break
- end
- _save7 = self.pos
- _tmp = _Newline()
- _tmp = _tmp ? nil : true
- self.pos = _save7
- unless _tmp
- self.pos = _save5
- break
- end
- _tmp = get_byte
- unless _tmp
- self.pos = _save5
- end
- break
- end # end sequence
+ end # end choice
- break unless _tmp
- end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save5 = self.pos
+ _tmp = match_string("|")
+ unless _tmp
_tmp = true
- else
- self.pos = _save1
+ self.pos = _save5
end
- if _tmp
- text = get_text(_text_start)
+ unless _tmp
+ self.pos = _save
+ break
end
+ _tmp = _Newline()
unless _tmp
self.pos = _save
break
end
- @result = begin; text.strip ; end
+ @result = begin; row ; end
_tmp = true
unless _tmp
self.pos = _save
@@ -16084,46 +16130,46 @@ class RDoc::Markdown
break
end # end sequence
- set_failed_rule :_TableItem unless _tmp
+ set_failed_rule :_TableRow unless _tmp
return _tmp
end
- # TableLine = TableColumn+:line "|" @Newline { line }
- def _TableLine
+ # TableItem2 = "|" TableItem
+ def _TableItem2
_save = self.pos
while true # sequence
- _save1 = self.pos
- _ary = []
- _tmp = apply(:_TableColumn)
- if _tmp
- _ary << @result
- while true
- _tmp = apply(:_TableColumn)
- _ary << @result if _tmp
- break unless _tmp
- end
- _tmp = true
- @result = _ary
- else
- self.pos = _save1
- end
- line = @result
+ _tmp = match_string("|")
unless _tmp
self.pos = _save
break
end
- _tmp = match_string("|")
+ _tmp = apply(:_TableItem)
unless _tmp
self.pos = _save
- break
end
- _tmp = _Newline()
+ break
+ end # end sequence
+
+ set_failed_rule :_TableItem2 unless _tmp
+ return _tmp
+ end
+
+ # TableItem = < /(?:\\.|[^|\n])+/ > { text.strip.gsub(/\\(.)/, '\1') }
+ def _TableItem
+
+ _save = self.pos
+ while true # sequence
+ _text_start = self.pos
+ _tmp = scan(/\G(?-mix:(?:\\.|[^|\n])+)/)
+ if _tmp
+ text = get_text(_text_start)
+ end
unless _tmp
self.pos = _save
break
end
- @result = begin; line ; end
+ @result = begin; text.strip.gsub(/\\(.)/, '\1') ; end
_tmp = true
unless _tmp
self.pos = _save
@@ -16131,80 +16177,136 @@ class RDoc::Markdown
break
end # end sequence
- set_failed_rule :_TableLine unless _tmp
+ set_failed_rule :_TableItem unless _tmp
return _tmp
end
- # TableColumn = "|" < ("-"+ ":"? | ":" "-"*) > { text.start_with?(":") ? :left : text.end_with?(":") ? :right : nil }
- def _TableColumn
+ # TableLine = ((TableAlign:align1 TableAlign2*:aligns {[align1, *aligns] }):line | TableAlign2+:line) "|"? @Newline { line }
+ def _TableLine
_save = self.pos
while true # sequence
- _tmp = match_string("|")
- unless _tmp
- self.pos = _save
- break
- end
- _text_start = self.pos
_save1 = self.pos
while true # choice
_save2 = self.pos
while true # sequence
- _save3 = self.pos
- _tmp = match_string("-")
- if _tmp
- while true
- _tmp = match_string("-")
- break unless _tmp
- end
- _tmp = true
- else
- self.pos = _save3
- end
+ _tmp = apply(:_TableAlign)
+ align1 = @result
unless _tmp
self.pos = _save2
break
end
- _save4 = self.pos
- _tmp = match_string(":")
+ _ary = []
+ while true
+ _tmp = apply(:_TableAlign2)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ aligns = @result
unless _tmp
- _tmp = true
- self.pos = _save4
+ self.pos = _save2
+ break
end
+ @result = begin; [align1, *aligns] ; end
+ _tmp = true
unless _tmp
self.pos = _save2
end
break
end # end sequence
+ line = @result
break if _tmp
self.pos = _save1
-
- _save5 = self.pos
- while true # sequence
- _tmp = match_string(":")
- unless _tmp
- self.pos = _save5
- break
- end
+ _save4 = self.pos
+ _ary = []
+ _tmp = apply(:_TableAlign2)
+ if _tmp
+ _ary << @result
while true
- _tmp = match_string("-")
+ _tmp = apply(:_TableAlign2)
+ _ary << @result if _tmp
break unless _tmp
end
_tmp = true
- unless _tmp
- self.pos = _save5
- end
- break
- end # end sequence
-
+ @result = _ary
+ else
+ self.pos = _save4
+ end
+ line = @result
break if _tmp
self.pos = _save1
break
end # end choice
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save5 = self.pos
+ _tmp = match_string("|")
+ unless _tmp
+ _tmp = true
+ self.pos = _save5
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; line ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_TableLine unless _tmp
+ return _tmp
+ end
+
+ # TableAlign2 = "|" @Sp TableAlign
+ def _TableAlign2
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("|")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_TableAlign)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_TableAlign2 unless _tmp
+ return _tmp
+ end
+
+ # TableAlign = < /:?-+:?/ > @Sp { text.start_with?(":") ? (text.end_with?(":") ? :center : :left) : (text.end_with?(":") ? :right : nil) }
+ def _TableAlign
+
+ _save = self.pos
+ while true # sequence
+ _text_start = self.pos
+ _tmp = scan(/\G(?-mix::?-+:?)/)
if _tmp
text = get_text(_text_start)
end
@@ -16212,8 +16314,15 @@ class RDoc::Markdown
self.pos = _save
break
end
- @result = begin; text.start_with?(":") ? :left :
- text.end_with?(":") ? :right : nil
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin;
+ text.start_with?(":") ?
+ (text.end_with?(":") ? :center : :left) :
+ (text.end_with?(":") ? :right : nil)
; end
_tmp = true
unless _tmp
@@ -16222,7 +16331,7 @@ class RDoc::Markdown
break
end # end sequence
- set_failed_rule :_TableColumn unless _tmp
+ set_failed_rule :_TableAlign unless _tmp
return _tmp
end
@@ -16656,13 +16765,16 @@ class RDoc::Markdown
Rules[:_Note] = rule_info("Note", "&{ notes? } @NonindentSpace RawNoteReference:ref \":\" @Sp @StartList:a RawNoteBlock:i { a.concat i } (&Indent RawNoteBlock:i { a.concat i })* { @footnotes[ref] = paragraph a nil }")
Rules[:_InlineNote] = rule_info("InlineNote", "&{ notes? } \"^[\" @StartList:a (!\"]\" Inline:l { a << l })+ \"]\" { ref = [:inline, @note_order.length] @footnotes[ref] = paragraph a note_for ref }")
Rules[:_Notes] = rule_info("Notes", "(Note | SkipBlock)*")
- Rules[:_RawNoteBlock] = rule_info("RawNoteBlock", "@StartList:a (!@BlankLine OptionallyIndentedLine:l { a << l })+ < @BlankLine* > { a << text } { a }")
+ Rules[:_RawNoteBlock] = rule_info("RawNoteBlock", "@StartList:a (!@BlankLine !RawNoteReference OptionallyIndentedLine:l { a << l })+ < @BlankLine* > { a << text } { a }")
Rules[:_CodeFence] = rule_info("CodeFence", "&{ github? } Ticks3 (@Sp StrChunk:format)? Spnl < ((!\"`\" Nonspacechar)+ | !Ticks3 /`+/ | Spacechar | @Newline)+ > Ticks3 @Sp @Newline* { verbatim = RDoc::Markup::Verbatim.new text verbatim.format = format.intern if format.instance_of?(String) verbatim }")
- Rules[:_Table] = rule_info("Table", "&{ github? } TableRow:header TableLine:line TableRow+:body { table = RDoc::Markup::Table.new(header, line, body) }")
- Rules[:_TableRow] = rule_info("TableRow", "TableItem+:row \"|\" @Newline { row }")
- Rules[:_TableItem] = rule_info("TableItem", "\"|\" < (!\"|\" !@Newline .)+ > { text.strip }")
- Rules[:_TableLine] = rule_info("TableLine", "TableColumn+:line \"|\" @Newline { line }")
- Rules[:_TableColumn] = rule_info("TableColumn", "\"|\" < (\"-\"+ \":\"? | \":\" \"-\"*) > { text.start_with?(\":\") ? :left : text.end_with?(\":\") ? :right : nil }")
+ Rules[:_Table] = rule_info("Table", "&{ github? } TableHead:header TableLine:line TableRow+:body { table = RDoc::Markup::Table.new(header, line, body) }")
+ Rules[:_TableHead] = rule_info("TableHead", "TableItem2+:items \"|\"? @Newline { items }")
+ Rules[:_TableRow] = rule_info("TableRow", "((TableItem:item1 TableItem2*:items { [item1, *items] }):row | TableItem2+:row) \"|\"? @Newline { row }")
+ Rules[:_TableItem2] = rule_info("TableItem2", "\"|\" TableItem")
+ Rules[:_TableItem] = rule_info("TableItem", "< /(?:\\\\.|[^|\\n])+/ > { text.strip.gsub(/\\\\(.)/, '\\1') }")
+ Rules[:_TableLine] = rule_info("TableLine", "((TableAlign:align1 TableAlign2*:aligns {[align1, *aligns] }):line | TableAlign2+:line) \"|\"? @Newline { line }")
+ Rules[:_TableAlign2] = rule_info("TableAlign2", "\"|\" @Sp TableAlign")
+ Rules[:_TableAlign] = rule_info("TableAlign", "< /:?-+:?/ > @Sp { text.start_with?(\":\") ? (text.end_with?(\":\") ? :center : :left) : (text.end_with?(\":\") ? :right : nil) }")
Rules[:_DefinitionList] = rule_info("DefinitionList", "&{ definition_lists? } DefinitionListItem+:list { RDoc::Markup::List.new :NOTE, *list.flatten }")
Rules[:_DefinitionListItem] = rule_info("DefinitionListItem", "DefinitionListLabel+:label DefinitionListDefinition+:defns { list_items = [] list_items << RDoc::Markup::ListItem.new(label, defns.shift) list_items.concat defns.map { |defn| RDoc::Markup::ListItem.new nil, defn } unless list_items.empty? list_items }")
Rules[:_DefinitionListLabel] = rule_info("DefinitionListLabel", "StrChunk:label @Sp @Newline { label }")
diff --git a/lib/rdoc/markdown/literals.rb b/lib/rdoc/markdown/literals.rb
index 4c36672de7..37659b7ae0 100644
--- a/lib/rdoc/markdown/literals.rb
+++ b/lib/rdoc/markdown/literals.rb
@@ -39,45 +39,75 @@ class RDoc::Markdown::Literals
attr_accessor :result, :pos
def current_column(target=pos)
- if c = string.rindex("\n", target-1)
- return target - c - 1
+ if string[target] == "\n" && (c = string.rindex("\n", target-1) || -1)
+ return target - c
+ elsif c = string.rindex("\n", target)
+ return target - c
end
target + 1
end
+ def position_line_offsets
+ unless @position_line_offsets
+ @position_line_offsets = []
+ total = 0
+ string.each_line do |line|
+ total += line.size
+ @position_line_offsets << total
+ end
+ end
+ @position_line_offsets
+ end
+
if [].respond_to? :bsearch_index
def current_line(target=pos)
- unless @line_offsets
- @line_offsets = []
- total = 0
- string.each_line do |line|
- total += line.size
- @line_offsets << total
- end
+ if line = position_line_offsets.bsearch_index {|x| x > target }
+ return line + 1
end
-
- @line_offsets.bsearch_index {|x| x >= target } + 1 || -1
+ raise "Target position #{target} is outside of string"
end
else
def current_line(target=pos)
- cur_offset = 0
- cur_line = 0
-
- string.each_line do |line|
- cur_line += 1
- cur_offset += line.size
- return cur_line if cur_offset >= target
+ if line = position_line_offsets.index {|x| x > target }
+ return line + 1
end
- -1
+ raise "Target position #{target} is outside of string"
+ end
+ end
+
+ def current_character(target=pos)
+ if target < 0 || target >= string.size
+ raise "Target position #{target} is outside of string"
end
+ string[target, 1]
+ end
+
+ KpegPosInfo = Struct.new(:pos, :lno, :col, :line, :char)
+
+ def current_pos_info(target=pos)
+ l = current_line target
+ c = current_column target
+ ln = get_line(l-1)
+ chr = string[target,1]
+ KpegPosInfo.new(target, l, c, ln, chr)
end
def lines
- lines = []
- string.each_line { |l| lines << l }
- lines
+ string.lines
+ end
+
+ def get_line(no)
+ loff = position_line_offsets
+ if no < 0
+ raise "Line No is out of range: #{no} < 0"
+ elsif no >= loff.size
+ raise "Line No is out of range: #{no} >= #{loff.size}"
+ end
+ lend = loff[no]-1
+ lstart = no > 0 ? loff[no-1] : 0
+ string[lstart..lend]
end
@@ -91,6 +121,7 @@ class RDoc::Markdown::Literals
@string = string
@string_size = string ? string.size : 0
@pos = pos
+ @position_line_offsets = nil
end
def show_pos
@@ -115,30 +146,22 @@ class RDoc::Markdown::Literals
end
def failure_caret
- l = current_line @failing_rule_offset
- c = current_column @failing_rule_offset
-
- line = lines[l-1]
- "#{line}\n#{' ' * (c - 1)}^"
+ p = current_pos_info @failing_rule_offset
+ "#{p.line.chomp}\n#{' ' * (p.col - 1)}^"
end
def failure_character
- l = current_line @failing_rule_offset
- c = current_column @failing_rule_offset
- lines[l-1][c-1, 1]
+ current_character @failing_rule_offset
end
def failure_oneline
- l = current_line @failing_rule_offset
- c = current_column @failing_rule_offset
-
- char = lines[l-1][c-1, 1]
+ p = current_pos_info @failing_rule_offset
if @failed_rule.kind_of? Symbol
info = self.class::Rules[@failed_rule]
- "@#{l}:#{c} failed rule '#{info.name}', got '#{char}'"
+ "@#{p.lno}:#{p.col} failed rule '#{info.name}', got '#{p.char}'"
else
- "@#{l}:#{c} failed rule '#{@failed_rule}', got '#{char}'"
+ "@#{p.lno}:#{p.col} failed rule '#{@failed_rule}', got '#{p.char}'"
end
end
@@ -151,10 +174,9 @@ class RDoc::Markdown::Literals
def show_error(io=STDOUT)
error_pos = @failing_rule_offset
- line_no = current_line(error_pos)
- col_no = current_column(error_pos)
+ p = current_pos_info(error_pos)
- io.puts "On line #{line_no}, column #{col_no}:"
+ io.puts "On line #{p.lno}, column #{p.col}:"
if @failed_rule.kind_of? Symbol
info = self.class::Rules[@failed_rule]
@@ -163,10 +185,9 @@ class RDoc::Markdown::Literals
io.puts "Failed to match rule '#{@failed_rule}'"
end
- io.puts "Got: #{string[error_pos,1].inspect}"
- line = lines[line_no-1]
- io.puts "=> #{line}"
- io.print(" " * (col_no + 3))
+ io.puts "Got: #{p.char.inspect}"
+ io.puts "=> #{p.line}"
+ io.print(" " * (p.col + 2))
io.puts "^"
end
@@ -275,6 +296,7 @@ class RDoc::Markdown::Literals
end
def apply_with_args(rule, *args)
+ @result = nil
memo_key = [rule, args]
if m = @memoizations[memo_key][@pos]
@pos = m.pos
@@ -308,6 +330,7 @@ class RDoc::Markdown::Literals
end
def apply(rule)
+ @result = nil
if m = @memoizations[rule][@pos]
@pos = m.pos
if !m.set
diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb
index f7aa02fd9f..6e93030965 100644
--- a/lib/rdoc/markup.rb
+++ b/lib/rdoc/markup.rb
@@ -97,638 +97,7 @@
#
# = \RDoc Markup Reference
#
-# == Block Markup
-#
-# === Paragraphs and Verbatim
-#
-# The markup engine looks for a document's natural left margin. This is
-# used as the initial margin for the document.
-#
-# Consecutive lines starting at this margin are considered to be a
-# paragraph. Empty lines separate paragraphs.
-#
-# Any line that starts to the right of the current margin is treated
-# as verbatim text. This is useful for code listings:
-#
-# 3.times { puts "Ruby" }
-#
-# In verbatim text, two or more blank lines are collapsed into one,
-# and trailing blank lines are removed:
-#
-# This is the first line
-#
-#
-# This is the second non-blank line,
-# after 2 blank lines in the source markup.
-#
-#
-# There were two trailing blank lines right above this paragraph, that
-# have been removed. In addition, the verbatim text has been shifted
-# left, so the amount of indentation of verbatim text is unimportant.
-#
-# For HTML output RDoc makes a small effort to determine if a verbatim section
-# contains Ruby source code. If so, the verbatim block will be marked up as
-# HTML. Triggers include "def", "class", "module", "require", the "hash
-# rocket"# (=>) or a block call with a parameter.
-#
-# === Headers
-#
-# A line starting with an equal sign (=) is treated as a
-# heading. Level one headings have one equals sign, level two headings
-# have two, and so on until level six, which is the maximum
-# (seven hyphens or more result in a level six heading).
-#
-# For example, the above header was obtained with:
-#
-# === Headers
-#
-# In HTML output headers have an id matching their name. The above example's
-# HTML is:
-#
-# <h3 id="label-Headers">Headers</h3>
-#
-# If a heading is inside a method body the id will be prefixed with the
-# method's id. If the above header where in the documentation for a method
-# such as:
-#
-# ##
-# # This method does fun things
-# #
-# # = Example
-# #
-# # Example of fun things goes here ...
-#
-# def do_fun_things
-# end
-#
-# The header's id would be:
-#
-# <h1 id="method-i-do_fun_things-label-Example">Example</h1>
-#
-# The label can be linked-to using <tt>SomeClass@Headers</tt>. See
-# {Links}[rdoc-ref:RDoc::Markup@Links] for further details.
-#
-# === Rules
-#
-# A line starting with three or more hyphens (at the current indent)
-# generates a horizontal rule.
-#
-# ---
-#
-# produces:
-#
-# ---
-#
-# === Simple Lists
-#
-# If a paragraph starts with a "*", "-", "<digit>." or "<letter>.",
-# then it is taken to be the start of a list. The margin is increased to be
-# the first non-space following the list start flag. Subsequent lines
-# should be indented to this new margin until the list ends. For example:
-#
-# * this is a list with three paragraphs in
-# the first item. This is the first paragraph.
-#
-# And this is the second paragraph.
-#
-# 1. This is an indented, numbered list.
-# 2. This is the second item in that list
-#
-# This is the third conventional paragraph in the
-# first list item.
-#
-# * This is the second item in the original list
-#
-# produces:
-#
-# * this is a list with three paragraphs in
-# the first item. This is the first paragraph.
-#
-# And this is the second paragraph.
-#
-# 1. This is an indented, numbered list.
-# 2. This is the second item in that list
-#
-# This is the third conventional paragraph in the
-# first list item.
-#
-# * This is the second item in the original list
-#
-# === Labeled Lists
-#
-# You can also construct labeled lists, sometimes called description
-# or definition lists. Do this by putting the label in square brackets
-# and indenting the list body:
-#
-# [cat] a small furry mammal
-# that seems to sleep a lot
-#
-# [ant] a little insect that is known
-# to enjoy picnics
-#
-# produces:
-#
-# [cat] a small furry mammal
-# that seems to sleep a lot
-#
-# [ant] a little insect that is known
-# to enjoy picnics
-#
-# If you want the list bodies to line up to the left of the labels,
-# use two colons:
-#
-# cat:: a small furry mammal
-# that seems to sleep a lot
-#
-# ant:: a little insect that is known
-# to enjoy picnics
-#
-# produces:
-#
-# cat:: a small furry mammal
-# that seems to sleep a lot
-#
-# ant:: a little insect that is known
-# to enjoy picnics
-#
-# Notice that blank lines right after the label are ignored in labeled lists:
-#
-# [one]
-#
-# definition 1
-#
-# [two]
-#
-# definition 2
-#
-# produces the same output as
-#
-# [one] definition 1
-# [two] definition 2
-#
-#
-# === Lists and Verbatim
-#
-# If you want to introduce a verbatim section right after a list, it has to be
-# less indented than the list item bodies, but more indented than the list
-# label, letter, digit or bullet. For instance:
-#
-# * point 1
-#
-# * point 2, first paragraph
-#
-# point 2, second paragraph
-# verbatim text inside point 2
-# point 2, third paragraph
-# verbatim text outside of the list (the list is therefore closed)
-# regular paragraph after the list
-#
-# produces:
-#
-# * point 1
-#
-# * point 2, first paragraph
-#
-# point 2, second paragraph
-# verbatim text inside point 2
-# point 2, third paragraph
-# verbatim text outside of the list (the list is therefore closed)
-# regular paragraph after the list
-#
-# == Text Markup
-#
-# === Bold, Italic, Typewriter Text
-#
-# You can use markup within text (except verbatim) to change the
-# appearance of parts of that text. Out of the box, RDoc::Markup
-# supports word-based and general markup.
-#
-# Word-based markup uses flag characters around individual words:
-#
-# <tt>\*_word_\*</tt>:: displays _word_ in a *bold* font
-# <tt>\__word_\_</tt>:: displays _word_ in an _emphasized_ font
-# <tt>\+_word_\+</tt>:: displays _word_ in a +code+ font
-#
-# General markup affects text between a start delimiter and an end
-# delimiter. Not surprisingly, these delimiters look like HTML markup.
-#
-# <tt>\<b>_text_</b></tt>:: displays _text_ in a *bold* font
-# <tt>\<em>_text_</em></tt>:: displays _text_ in an _emphasized_ font
-# (alternate tag: <tt>\<i></tt>)
-# <tt>\<tt>_text_\</tt></tt>:: displays _text_ in a +code+ font
-# (alternate tag: <tt>\<code></tt>)
-#
-# Unlike conventional Wiki markup, general markup can cross line
-# boundaries. You can turn off the interpretation of markup by
-# preceding the first character with a backslash (see <i>Escaping
-# Text Markup</i>, below).
-#
-# === Links
-#
-# Links to starting with +http:+, +https:+, +mailto:+, +ftp:+ or +www.+
-# are recognized. An HTTP url that references an external image is converted
-# into an inline image element.
-#
-# Classes and methods will be automatically linked to their definition. For
-# example, <tt>RDoc::Markup</tt> will link to this documentation. By default
-# methods will only be automatically linked if they contain an <tt>_</tt> (all
-# methods can be automatically linked through the <tt>--hyperlink-all</tt>
-# command line option).
-#
-# Single-word methods can be linked by using the <tt>#</tt> character for
-# instance methods or <tt>::</tt> for class methods. For example,
-# <tt>#convert</tt> links to #convert. A class or method may be combined like
-# <tt>RDoc::Markup#convert</tt>.
-#
-# A heading inside the documentation can be linked by following the class
-# or method by an <tt>@</tt> then the heading name.
-# <tt>RDoc::Markup@Links</tt> will link to this section like this:
-# RDoc::Markup@Links. Spaces in headings with multiple words must be escaped
-# with <tt>+</tt> like <tt>RDoc::Markup@Escaping+Text+Markup</tt>.
-# Punctuation and other special characters must be escaped like CGI.escape.
-#
-# The <tt>@</tt> can also be used to link to sections. If a section and a
-# heading share the same name the section is preferred for the link.
-#
-# Links can also be of the form <tt>label[url]</tt>, in which case +label+ is
-# used in the displayed text, and +url+ is used as the target. If +label+
-# contains multiple words, put it in braces: <tt>{multi word label}[url]</tt>.
-# The +url+ may be an +http:+-type link or a cross-reference to a class,
-# module or method with a label.
-#
-# Links with the <code>rdoc-image:</code> scheme will create an image tag for
-# HTML output. Only fully-qualified URLs are supported.
-#
-# Links with the <tt>rdoc-ref:</tt> scheme will link to the referenced class,
-# module, method, file, etc. If the referenced item is does not exist
-# no link will be generated and <tt>rdoc-ref:</tt> will be removed from the
-# resulting text.
-#
-# Links starting with <tt>rdoc-label:label_name</tt> will link to the
-# +label_name+. You can create a label for the current link (for
-# bidirectional links) by supplying a name for the current link like
-# <tt>rdoc-label:label-other:label-mine</tt>.
-#
-# Links starting with +link:+ refer to local files whose path is relative to
-# the <tt>--op</tt> directory. Use <tt>rdoc-ref:</tt> instead of
-# <tt>link:</tt> to link to files generated by RDoc as the link target may
-# be different across RDoc generators.
-#
-# Example links:
-#
-# https://github.com/ruby/rdoc
-# mailto:user@example.com
-# {RDoc Documentation}[http://rdoc.rubyforge.org]
-# {RDoc Markup}[rdoc-ref:RDoc::Markup]
-#
-# === Escaping Text Markup
-#
-# Text markup can be escaped with a backslash, as in \<tt>, which was obtained
-# with <tt>\\<tt></tt>. Except in verbatim sections and between \<tt> tags,
-# to produce a backslash you have to double it unless it is followed by a
-# space, tab or newline. Otherwise, the HTML formatter will discard it, as it
-# is used to escape potential links:
-#
-# * The \ must be doubled if not followed by white space: \\.
-# * But not in \<tt> tags: in a Regexp, <tt>\S</tt> matches non-space.
-# * This is a link to {ruby-lang}[https://www.ruby-lang.org].
-# * This is not a link, however: \{ruby-lang.org}[https://www.ruby-lang.org].
-# * This will not be linked to \RDoc::RDoc#document
-#
-# generates:
-#
-# * The \ must be doubled if not followed by white space: \\.
-# * But not in \<tt> tags: in a Regexp, <tt>\S</tt> matches non-space.
-# * This is a link to {ruby-lang}[https://www.ruby-lang.org]
-# * This is not a link, however: \{ruby-lang.org}[https://www.ruby-lang.org]
-# * This will not be linked to \RDoc::RDoc#document
-#
-# Inside \<tt> tags, more precisely, leading backslashes are removed only if
-# followed by a markup character (<tt><*_+</tt>), a backslash, or a known link
-# reference (a known class or method). So in the example above, the backslash
-# of <tt>\S</tt> would be removed if there was a class or module named +S+ in
-# the current context.
-#
-# This behavior is inherited from RDoc version 1, and has been kept for
-# compatibility with existing RDoc documentation.
-#
-# === Conversion of characters
-#
-# HTML will convert two/three dashes to an em-dash. Other common characters are
-# converted as well:
-#
-# em-dash:: -- or ---
-# ellipsis:: ...
-#
-# single quotes:: 'text' or `text'
-# double quotes:: "text" or ``text''
-#
-# copyright:: (c)
-# registered trademark:: (r)
-#
-# produces:
-#
-# em-dash:: -- or ---
-# ellipsis:: ...
-#
-# single quotes:: 'text' or `text'
-# double quotes:: "text" or ``text''
-#
-# copyright:: (c)
-# registered trademark:: (r)
-#
-#
-# == Documenting Source Code
-#
-# Comment blocks can be written fairly naturally, either using <tt>#</tt> on
-# successive lines of the comment, or by including the comment in
-# a <tt>=begin</tt>/<tt>=end</tt> block. If you use the latter form,
-# the <tt>=begin</tt> line _must_ be flagged with an +rdoc+ tag:
-#
-# =begin rdoc
-# Documentation to be processed by RDoc.
-#
-# ...
-# =end
-#
-# RDoc stops processing comments if it finds a comment line starting
-# with <tt>--</tt> right after the <tt>#</tt> character (otherwise,
-# it will be treated as a rule if it has three dashes or more).
-# This can be used to separate external from internal comments,
-# or to stop a comment being associated with a method, class, or module.
-# Commenting can be turned back on with a line that starts with <tt>++</tt>.
-#
-# ##
-# # Extract the age and calculate the date-of-birth.
-# #--
-# # FIXME: fails if the birthday falls on February 29th
-# #++
-# # The DOB is returned as a Time object.
-#
-# def get_dob(person)
-# # ...
-# end
-#
-# Names of classes, files, and any method names containing an underscore or
-# preceded by a hash character are automatically linked from comment text to
-# their description. This linking works inside the current class or module,
-# and with ancestor methods (in included modules or in the superclass).
-#
-# Method parameter lists are extracted and displayed with the method
-# description. If a method calls +yield+, then the parameters passed to yield
-# will also be displayed:
-#
-# def fred
-# ...
-# yield line, address
-#
-# This will get documented as:
-#
-# fred() { |line, address| ... }
-#
-# You can override this using a comment containing ':yields: ...' immediately
-# after the method definition
-#
-# def fred # :yields: index, position
-# # ...
-#
-# yield line, address
-#
-# which will get documented as
-#
-# fred() { |index, position| ... }
-#
-# +:yields:+ is an example of a documentation directive. These appear
-# immediately after the start of the document element they are modifying.
-#
-# RDoc automatically cross-references words with underscores or camel-case.
-# To suppress cross-references, prefix the word with a \ character. To
-# include special characters like "<tt>\n</tt>", you'll need to use
-# two \ characters in normal text, but only one in \<tt> text:
-#
-# "\\n" or "<tt>\n</tt>"
-#
-# produces:
-#
-# "\\n" or "<tt>\n</tt>"
-#
-# == Directives
-#
-# Directives are keywords surrounded by ":" characters.
-#
-# === Controlling what is documented
-#
-# [+:nodoc:+ / <tt>:nodoc: all</tt>]
-# This directive prevents documentation for the element from
-# being generated. For classes and modules, methods, aliases,
-# constants, and attributes directly within the affected class or
-# module also will be omitted. By default, though, modules and
-# classes within that class or module _will_ be documented. This is
-# turned off by adding the +all+ modifier.
-#
-# module MyModule # :nodoc:
-# class Input
-# end
-# end
-#
-# module OtherModule # :nodoc: all
-# class Output
-# end
-# end
-#
-# In the above code, only class <tt>MyModule::Input</tt> will be documented.
-#
-# The +:nodoc:+ directive, like +:enddoc:+, +:stopdoc:+ and +:startdoc:+
-# presented below, is local to the current file: if you do not want to
-# document a module that appears in several files, specify +:nodoc:+ on each
-# appearance, at least once per file.
-#
-# [+:stopdoc:+ / +:startdoc:+]
-# Stop and start adding new documentation elements to the current container.
-# For example, if a class has a number of constants that you don't want to
-# document, put a +:stopdoc:+ before the first, and a +:startdoc:+ after the
-# last. If you don't specify a +:startdoc:+ by the end of the container,
-# disables documentation for the rest of the current file.
-#
-# [+:doc:+]
-# Forces a method or attribute to be documented even if it wouldn't be
-# otherwise. Useful if, for example, you want to include documentation of a
-# particular private method.
-#
-# [+:enddoc:+]
-# Document nothing further at the current level: directives +:startdoc:+ and
-# +:doc:+ that appear after this will not be honored for the current container
-# (file, class or module), in the current file.
-#
-# [+:notnew:+ / +:not_new:+ / +:not-new:+ ]
-# Only applicable to the +initialize+ instance method. Normally RDoc
-# assumes that the documentation and parameters for +initialize+ are
-# actually for the +new+ method, and so fakes out a +new+ for the class.
-# The +:notnew:+ directive stops this. Remember that +initialize+ is private,
-# so you won't see the documentation unless you use the +-a+ command line
-# option.
-#
-# === Method arguments
-#
-# [+:arg:+ or +:args:+ _parameters_]
-# Overrides the default argument handling with exactly these parameters.
-#
-# ##
-# # :args: a, b
-#
-# def some_method(*a)
-# end
-#
-# [+:yield:+ or +:yields:+ _parameters_]
-# Overrides the default yield discovery with these parameters.
-#
-# ##
-# # :yields: key, value
-#
-# def each_thing &block
-# @things.each(&block)
-# end
-#
-# [+:call-seq:+]
-# Lines up to the next blank line or lines with a common prefix in the
-# comment are treated as the method's calling sequence, overriding the
-# default parsing of method parameters and yield arguments.
-#
-# Multiple lines may be used.
-#
-# # :call-seq:
-# # ARGF.readlines(sep=$/) -> array
-# # ARGF.readlines(limit) -> array
-# # ARGF.readlines(sep, limit) -> array
-# #
-# # ARGF.to_a(sep=$/) -> array
-# # ARGF.to_a(limit) -> array
-# # ARGF.to_a(sep, limit) -> array
-# #
-# # The remaining lines are documentation ...
-#
-# === Sections
-#
-# Sections allow you to group methods in a class into sensible containers. If
-# you use the sections 'Public', 'Internal' and 'Deprecated' (the three
-# allowed method statuses from TomDoc) the sections will be displayed in that
-# order placing the most useful methods at the top. Otherwise, sections will
-# be displayed in alphabetical order.
-#
-# [+:category:+ _section_]
-# Adds this item to the named +section+ overriding the current section. Use
-# this to group methods by section in RDoc output while maintaining a
-# sensible ordering (like alphabetical).
-#
-# # :category: Utility Methods
-# #
-# # CGI escapes +text+
-#
-# def convert_string text
-# CGI.escapeHTML text
-# end
-#
-# An empty category will place the item in the default category:
-#
-# # :category:
-# #
-# # This method is in the default category
-#
-# def some_method
-# # ...
-# end
-#
-# Unlike the :section: directive, :category: is not sticky. The category
-# only applies to the item immediately following the comment.
-#
-# Use the :section: directive to provide introductory text for a section of
-# documentation.
-#
-# [+:section:+ _title_]
-# Provides section introductory text in RDoc output. The title following
-# +:section:+ is used as the section name and the remainder of the comment
-# containing the section is used as introductory text. A section's comment
-# block must be separated from following comment blocks. Use an empty title
-# to switch to the default section.
-#
-# The :section: directive is sticky, so subsequent methods, aliases,
-# attributes, and classes will be contained in this section until the
-# section is changed. The :category: directive will override the :section:
-# directive.
-#
-# A :section: comment block may have one or more lines before the :section:
-# directive. These will be removed, and any identical lines at the end of
-# the block are also removed. This allows you to add visual cues to the
-# section.
-#
-# Example:
-#
-# # ----------------------------------------
-# # :section: My Section
-# # This is the section that I wrote.
-# # See it glisten in the noon-day sun.
-# # ----------------------------------------
-#
-# ##
-# # Comment for some_method
-#
-# def some_method
-# # ...
-# end
-#
-# === Other directives
-#
-# [+:markup:+ _type_]
-# Overrides the default markup type for this comment with the specified
-# markup type. For Ruby files, if the first comment contains this directive
-# it is applied automatically to all comments in the file.
-#
-# Unless you are converting between markup formats you should use a
-# <code>.rdoc_options</code> file to specify the default documentation
-# format for your entire project. See RDoc::Options@Saved+Options for
-# instructions.
-#
-# At the top of a file the +:markup:+ directive applies to the entire file:
-#
-# # coding: UTF-8
-# # :markup: TomDoc
-#
-# # TomDoc comment here ...
-#
-# class MyClass
-# # ...
-#
-# For just one comment:
-#
-# # ...
-# end
-#
-# # :markup: RDoc
-# #
-# # This is a comment in RDoc markup format ...
-#
-# def some_method
-# # ...
-#
-# See Markup@CONTRIBUTING for instructions on adding a new markup format.
-#
-# [+:include:+ _filename_]
-# Include the contents of the named file at this point. This directive
-# must appear alone on one line, possibly preceded by spaces. In this
-# position, it can be escaped with a \ in front of the first colon.
-#
-# The file will be searched for in the directories listed by the +--include+
-# option, or in the current directory by default. The contents of the file
-# will be shifted to have the same indentation as the ':' at the start of
-# the +:include:+ directive.
-#
-# [+:title:+ _text_]
-# Sets the title for the document. Equivalent to the <tt>--title</tt>
-# command line parameter. (The command line parameter overrides any :title:
-# directive in the source).
-#
-# [+:main:+ _name_]
-# Equivalent to the <tt>--main</tt> command line parameter.
+# See RDoc::MarkupReference.
#
#--
# Original Author:: Dave Thomas, dave@pragmaticprogrammer.com
diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb
index 6ef5af8856..601e6bc189 100644
--- a/lib/rdoc/markup/attribute_manager.rb
+++ b/lib/rdoc/markup/attribute_manager.rb
@@ -2,6 +2,17 @@
##
# Manages changes of attributes in a block of text
+unless MatchData.method_defined?(:match_length)
+ using Module.new {
+ refine(MatchData) {
+ def match_length(nth)
+ b, e = offset(nth)
+ e - b if b
+ end
+ }
+ }
+end
+
class RDoc::Markup::AttributeManager
##
@@ -153,16 +164,17 @@ class RDoc::Markup::AttributeManager
tags = "[#{tags.join("")}](?!#{PROTECT_ATTR})"
all_tags = "[#{@matching_word_pairs.keys.join("")}](?!#{PROTECT_ATTR})"
- re = /(^|\W|#{all_tags})(#{tags})(\2*[#\\]?[\w:#{PROTECT_ATTR}.\/\[\]-]+?\S?)\2(?!\2)(#{all_tags}|\W|$)/
+ re = /(?:^|\W|#{all_tags})\K(#{tags})(\1*[#\\]?[\w:#{PROTECT_ATTR}.\/\[\]-]+?\S?)\1(?!\1)(?=#{all_tags}|\W|$)/
1 while str.gsub!(re) { |orig|
- attr = @matching_word_pairs[$2]
- attr_updated = attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
- if attr_updated
- $1 + NULL * $2.length + $3 + NULL * $2.length + $4
+ a, w = (m = $~).values_at(1, 2)
+ attr = @matching_word_pairs[a]
+ if attrs.set_attrs(m.begin(2), w.length, attr)
+ a = NULL * a.length
else
- $1 + NON_PRINTING_START + $2 + NON_PRINTING_END + $3 + NON_PRINTING_START + $2 + NON_PRINTING_END + $4
+ a = NON_PRINTING_START + a + NON_PRINTING_END
end
+ a + w + a
}
str.delete!(NON_PRINTING_START + NON_PRINTING_END)
end
@@ -173,9 +185,10 @@ class RDoc::Markup::AttributeManager
@word_pair_map.each do |regexp, attr|
next unless exclusive == exclusive?(attr)
1 while str.gsub!(regexp) { |orig|
- updated = attrs.set_attrs($`.length + $1.length, $2.length, attr)
+ w = (m = ($~))[2]
+ updated = attrs.set_attrs(m.begin(2), w.length, attr)
if updated
- NULL * $1.length + $2 + NULL * $3.length
+ NULL * m.match_length(1) + w + NULL * m.match_length(3)
else
orig
end
@@ -194,9 +207,9 @@ class RDoc::Markup::AttributeManager
1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) { |orig|
attr = @html_tags[$1.downcase]
- html_length = $1.length + 2
+ html_length = $~.match_length(1) + 2 # "<>".length
seq = NULL * html_length
- attrs.set_attrs($`.length + html_length, $2.length, attr)
+ attrs.set_attrs($~.begin(2), $~.match_length(2), attr)
seq + $2 + seq + NULL
}
end
diff --git a/lib/rdoc/markup/parser.rb b/lib/rdoc/markup/parser.rb
index 1b54a519d1..0029df7e65 100644
--- a/lib/rdoc/markup/parser.rb
+++ b/lib/rdoc/markup/parser.rb
@@ -287,6 +287,12 @@ class RDoc::Markup::Parser
line << ' ' * indent
when :BREAK, :TEXT then
line << data
+ when :BLOCKQUOTE then
+ line << '>>>'
+ peek_type, _, peek_column = peek_token
+ if peek_type != :NEWLINE and peek_column
+ line << ' ' * (peek_column - column - 3)
+ end
else # *LIST_TOKENS
list_marker = case type
when :BULLET then data
@@ -372,11 +378,8 @@ class RDoc::Markup::Parser
unget
parse_text parent, indent
when :BLOCKQUOTE then
- type, _, column = get
- if type == :NEWLINE
- type, _, column = get
- end
- unget if type
+ nil while (type, = get; type) and type != :NEWLINE
+ _, _, column, = peek_token
bq = RDoc::Markup::BlockQuote.new
p :blockquote_start => [data, column] if @debug
parse bq, column
@@ -544,7 +547,10 @@ class RDoc::Markup::Parser
[:NOTE, @s[1], *pos]
# >>> followed by end of line => :BLOCKQUOTE
when @s.scan(/>>> *(\w+)?$/) then
- [:BLOCKQUOTE, @s[1], *pos]
+ if word = @s[1]
+ @s.unscan(word)
+ end
+ [:BLOCKQUOTE, word, *pos]
# anything else: :TEXT
else
@s.scan(/(.*?)( )?\r?$/)
diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb
index 2bfabc8942..bf323074de 100644
--- a/lib/rdoc/markup/to_html.rb
+++ b/lib/rdoc/markup/to_html.rb
@@ -84,7 +84,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
def handle_RDOCLINK url # :nodoc:
case url
when /^rdoc-ref:/
- $'
+ CGI.escapeHTML($')
when /^rdoc-label:/
text = $'
@@ -95,13 +95,11 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
else text
end
- gen_url url, text
+ gen_url CGI.escapeHTML(url), CGI.escapeHTML(text)
when /^rdoc-image:/
- "<img src=\"#{$'}\">"
- else
- url =~ /\Ardoc-[a-z]+:/
-
- $'
+ %[<img src=\"#{CGI.escapeHTML($')}\">]
+ when /\Ardoc-[a-z]+:/
+ CGI.escapeHTML($')
end
end
@@ -125,7 +123,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
# Reference to a local file relative to the output directory.
def handle_regexp_HYPERLINK(target)
- url = target.text
+ url = CGI.escapeHTML(target.text)
gen_url url, url
end
@@ -154,9 +152,13 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
text =~ /^\{(.*)\}\[(.*?)\]$/ or text =~ /^(\S+)\[(.*?)\]$/
label = $1
- url = $2
+ url = CGI.escapeHTML($2)
- label = handle_RDOCLINK label if /^rdoc-image:/ =~ label
+ if /^rdoc-image:/ =~ label
+ label = handle_RDOCLINK(label)
+ else
+ label = CGI.escapeHTML(label)
+ end
gen_url url, label
end
@@ -324,7 +326,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
header.zip(aligns) do |text, align|
@res << '<th'
@res << ' align="' << align << '"' if align
- @res << '>' << CGI.escapeHTML(text) << "</th>\n"
+ @res << '>' << to_html(text) << "</th>\n"
end
@res << "</tr>\n</thead>\n<tbody>\n"
body.each do |row|
@@ -332,7 +334,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
row.zip(aligns) do |text, align|
@res << '<td'
@res << ' align="' << align << '"' if align
- @res << '>' << CGI.escapeHTML(text) << "</td>\n"
+ @res << '>' << to_html(text) << "</td>\n"
end
@res << "</tr>\n"
end
diff --git a/lib/rdoc/markup/to_rdoc.rb b/lib/rdoc/markup/to_rdoc.rb
index 3cdf4fd08b..2a9b05625c 100644
--- a/lib/rdoc/markup/to_rdoc.rb
+++ b/lib/rdoc/markup/to_rdoc.rb
@@ -330,31 +330,14 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
text_len = 20 if text_len < 20
- re = /^(.{0,#{text_len}})[ \n]/
next_prefix = ' ' * @indent
prefix = @prefix || next_prefix
@prefix = nil
- @res << prefix
-
- while text.length > text_len
- if text =~ re then
- @res << $1
- text.slice!(0, $&.length)
- else
- @res << text.slice!(0, text_len)
- end
-
- @res << "\n" << next_prefix
- end
-
- if text.empty? then
- @res.pop
- @res.pop
- else
- @res << text
- @res << "\n"
+ text.scan(/\G(?:([^ \n]{#{text_len}})(?=[^ \n])|(.{1,#{text_len}})(?:[ \n]|\z))/) do
+ @res << prefix << ($1 || $2) << "\n"
+ prefix = next_prefix
end
end
diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb
index 55994c9dcc..eed0f6b39b 100644
--- a/lib/rdoc/options.rb
+++ b/lib/rdoc/options.rb
@@ -339,6 +339,10 @@ class RDoc::Options
attr_reader :visibility
+ ##
+ # Indicates if files of test suites should be skipped
+ attr_accessor :skip_tests
+
def initialize loaded_options = nil # :nodoc:
init_ivars
override loaded_options if loaded_options
@@ -386,6 +390,7 @@ class RDoc::Options
@write_options = false
@encoding = Encoding::UTF_8
@charset = @encoding.name
+ @skip_tests = true
end
def init_with map # :nodoc:
@@ -778,6 +783,13 @@ Usage: #{opt.program_name} [options] [names...]
opt.separator nil
+ opt.on("--no-skipping-tests", nil,
+ "Don't skip generating documentation for test and spec files") do |value|
+ @skip_tests = false
+ end
+
+ opt.separator nil
+
opt.on("--extension=NEW=OLD", "-E",
"Treat files ending with .new as if they",
"ended with .old. Using '-E cgi=rb' will",
diff --git a/lib/rdoc/parser.rb b/lib/rdoc/parser.rb
index 7006265b63..3bb6f5d1f2 100644
--- a/lib/rdoc/parser.rb
+++ b/lib/rdoc/parser.rb
@@ -266,6 +266,23 @@ class RDoc::Parser
autoload :RubyTools, "#{__dir__}/parser/ruby_tools"
autoload :Text, "#{__dir__}/parser/text"
+ ##
+ # Normalizes tabs in +body+
+
+ def handle_tab_width(body)
+ if /\t/ =~ body
+ tab_width = @options.tab_width
+ body.split(/\n/).map do |line|
+ 1 while line.gsub!(/\t+/) do
+ b, e = $~.offset(0)
+ ' ' * (tab_width * (e-b) - b % tab_width)
+ end
+ line
+ end.join "\n"
+ else
+ body
+ end
+ end
end
# simple must come first in order to show up last in the parsers list
diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb
index a03e4663e4..5695bf1acb 100644
--- a/lib/rdoc/parser/c.rb
+++ b/lib/rdoc/parser/c.rb
@@ -1058,23 +1058,6 @@ class RDoc::Parser::C < RDoc::Parser
end
##
- # Normalizes tabs in +body+
-
- def handle_tab_width(body)
- if /\t/ =~ body
- tab_width = @options.tab_width
- body.split(/\n/).map do |line|
- 1 while line.gsub!(/\t+/) do
- ' ' * (tab_width * $&.length - $`.length % tab_width)
- end && $~
- line
- end.join "\n"
- else
- body
- end
- end
-
- ##
# Loads the variable map with the given +name+ from the RDoc::Store, if
# present.
diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb
index 3c5f79632c..b74ead65ab 100644
--- a/lib/rdoc/parser/ruby.rb
+++ b/lib/rdoc/parser/ruby.rb
@@ -164,15 +164,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
def initialize(top_level, file_name, content, options, stats)
super
- if /\t/ =~ content then
- tab_width = @options.tab_width
- content = content.split(/\n/).map do |line|
- 1 while line.gsub!(/\t+/) {
- ' ' * (tab_width*$&.length - $`.length % tab_width)
- } && $~
- line
- end.join("\n")
- end
+ content = handle_tab_width(content)
@size = 0
@token_listeners = nil
diff --git a/lib/rdoc/rd/block_parser.rb b/lib/rdoc/rd/block_parser.rb
index eb7d46925b..6f70622c0b 100644
--- a/lib/rdoc/rd/block_parser.rb
+++ b/lib/rdoc/rd/block_parser.rb
@@ -18,8 +18,6 @@ class BlockParser < Racc::Parser
# :stopdoc:
-TMPFILE = ["rdtmp", $$, 0]
-
MARK_TO_LEVEL = {
'=' => 1,
'==' => 2,
@@ -129,15 +127,19 @@ def next_token # :nodoc:
# non-RD part begin
when /^=begin\s+(\w+)/
part = $1
+=begin # not imported to RDoc
if @in_part # if in non-RD part
@part_content.push(line)
else
@in_part = part if @tree.filter[part] # if filter exists
# p "BEGIN_PART: #{@in_part}" # DEBUG
end
+=end
+ @in_part = part
# non-RD part end
- when /^=end/
+ when /^=end(?:$|[\s\0\C-d\C-z])/
if @in_part # if in non-RD part
+=begin # not imported to RDoc
# p "END_PART: #{@in_part}" # DEBUG
# make Part-in object
part = RDoc::RD::Part.new(@part_content.join(""), @tree, "r")
@@ -148,20 +150,22 @@ def next_token # :nodoc:
if @tree.filter[@in_part].mode == :rd # if output is RD formatted
subtree = parse_subtree(part_out.to_a)
else # if output is target formatted
- basename = TMPFILE.join('.')
- TMPFILE[-1] += 1
- tmpfile = open(@tree.tmp_dir + "/" + basename + ".#{@in_part}", "w")
- tmpfile.print(part_out)
- tmpfile.close
+ basename = Tempfile.create(["rdtmp", ".#{@in_part}"], @tree.tmp_dir) do |tmpfile|
+ tmpfile.print(part_out)
+ File.basename(tmpfile.path)
+ end
subtree = parse_subtree(["=begin\n", "<<< #{basename}\n", "=end\n"])
end
@in_part = nil
return [:SUBTREE, subtree]
+=end
end
else
+=begin # not imported to RDoc
if @in_part # if in non-RD part
@part_content.push(line)
end
+=end
end
end
diff --git a/lib/rdoc/rdoc.gemspec b/lib/rdoc/rdoc.gemspec
index 547a67b04b..3c96f7deb1 100644
--- a/lib/rdoc/rdoc.gemspec
+++ b/lib/rdoc/rdoc.gemspec
@@ -38,16 +38,12 @@ RDoc includes the +rdoc+ and +ri+ tools for generating and displaying documentat
"CVE-2013-0256.rdoc",
"ExampleMarkdown.md",
"ExampleRDoc.rdoc",
- "Gemfile",
"History.rdoc",
"LEGAL.rdoc",
"LICENSE.rdoc",
"README.rdoc",
"RI.rdoc",
- "Rakefile",
"TODO.rdoc",
- "bin/console",
- "bin/setup",
"exe/rdoc",
"exe/ri",
"lib/rdoc.rb",
@@ -223,7 +219,6 @@ RDoc includes the +rdoc+ and +ri+ tools for generating and displaying documentat
"lib/rdoc/top_level.rb",
"lib/rdoc/version.rb",
"man/ri.1",
- "rdoc.gemspec",
]
# files from .gitignore
s.files << "lib/rdoc/rd/block_parser.rb" << "lib/rdoc/rd/inline_parser.rb" << "lib/rdoc/markdown.rb" << "lib/rdoc/markdown/literals.rb"
diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb
index 400f9b5bc3..2d8a9dea8c 100644
--- a/lib/rdoc/rdoc.rb
+++ b/lib/rdoc/rdoc.rb
@@ -36,6 +36,17 @@ class RDoc::RDoc
GENERATORS = {}
##
+ # List of directory names always skipped
+
+ UNCONDITIONALLY_SKIPPED_DIRECTORIES = %w[CVS .svn .git].freeze
+
+ ##
+ # List of directory names skipped if test suites should be skipped
+
+ TEST_SUITE_DIRECTORY_NAMES = %w[spec test].freeze
+
+
+ ##
# Generator instance used for creating output
attr_accessor :generator
@@ -280,7 +291,10 @@ option)
file_list[rel_file_name] = mtime
end
when "directory" then
- next if rel_file_name == "CVS" || rel_file_name == ".svn"
+ next if UNCONDITIONALLY_SKIPPED_DIRECTORIES.include?(rel_file_name)
+
+ basename = File.basename(rel_file_name)
+ next if options.skip_tests && TEST_SUITE_DIRECTORY_NAMES.include?(basename)
created_rid = File.join rel_file_name, "created.rid"
next if File.file? created_rid
diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb
index dd66d532ca..819cff8aa3 100644
--- a/lib/rdoc/ri/driver.rb
+++ b/lib/rdoc/ri/driver.rb
@@ -1,12 +1,6 @@
# frozen_string_literal: true
-require 'abbrev'
require 'optparse'
-begin
- require 'readline'
-rescue LoadError
-end
-
require_relative '../../rdoc'
require_relative 'formatter' # For RubyGems backwards compatibility
@@ -1080,6 +1074,10 @@ or the PAGER environment variable.
def interactive
puts "\nEnter the method name you want to look up."
+ begin
+ require 'readline'
+ rescue LoadError
+ end
if defined? Readline then
Readline.completion_proc = method :complete
puts "You can use tab to autocomplete."
diff --git a/lib/rdoc/store.rb b/lib/rdoc/store.rb
index 9fc540d317..c793e49ed8 100644
--- a/lib/rdoc/store.rb
+++ b/lib/rdoc/store.rb
@@ -556,9 +556,7 @@ class RDoc::Store
def load_cache
#orig_enc = @encoding
- File.open cache_path, 'rb' do |io|
- @cache = Marshal.load io
- end
+ @cache = marshal_load(cache_path)
load_enc = @cache[:encoding]
@@ -615,9 +613,7 @@ class RDoc::Store
def load_class_data klass_name
file = class_file klass_name
- File.open file, 'rb' do |io|
- Marshal.load io
- end
+ marshal_load(file)
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, klass_name)
error.set_backtrace e.backtrace
@@ -630,14 +626,10 @@ class RDoc::Store
def load_method klass_name, method_name
file = method_file klass_name, method_name
- File.open file, 'rb' do |io|
- obj = Marshal.load io
- obj.store = self
- obj.parent =
- find_class_or_module(klass_name) || load_class(klass_name) unless
- obj.parent
- obj
- end
+ obj = marshal_load(file)
+ obj.store = self
+ obj.parent ||= find_class_or_module(klass_name) || load_class(klass_name)
+ obj
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, klass_name + method_name)
error.set_backtrace e.backtrace
@@ -650,11 +642,9 @@ class RDoc::Store
def load_page page_name
file = page_file page_name
- File.open file, 'rb' do |io|
- obj = Marshal.load io
- obj.store = self
- obj
- end
+ obj = marshal_load(file)
+ obj.store = self
+ obj
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, page_name)
error.set_backtrace e.backtrace
@@ -976,4 +966,21 @@ class RDoc::Store
@unique_modules
end
+ private
+ def marshal_load(file)
+ File.open(file, 'rb') {|io| Marshal.load(io, MarshalFilter)}
+ end
+
+ MarshalFilter = proc do |obj|
+ case obj
+ when true, false, nil, Array, Class, Encoding, Hash, Integer, String, Symbol, RDoc::Text
+ else
+ unless obj.class.name.start_with?("RDoc::")
+ raise TypeError, "not permitted class: #{obj.class.name}"
+ end
+ end
+ obj
+ end
+ private_constant :MarshalFilter
+
end
diff --git a/lib/rdoc/version.rb b/lib/rdoc/version.rb
index 2c52a6b87b..31c1aa0276 100644
--- a/lib/rdoc/version.rb
+++ b/lib/rdoc/version.rb
@@ -5,6 +5,6 @@ module RDoc
##
# RDoc version you are using
- VERSION = '6.4.0'
+ VERSION = '6.5.1.1'
end
diff --git a/lib/reline.rb b/lib/reline.rb
index 9de04a95b3..7800a281ce 100644
--- a/lib/reline.rb
+++ b/lib/reline.rb
@@ -46,21 +46,6 @@ module Reline
keyword_init: true
)
- DIALOG_COLOR_APIS = [
- :dialog_default_bg_color,
- :dialog_default_bg_color_sequence,
- :dialog_default_bg_color=,
- :dialog_default_fg_color,
- :dialog_default_fg_color_sequence,
- :dialog_default_fg_color=,
- :dialog_pointer_bg_color,
- :dialog_pointer_bg_color_sequence,
- :dialog_pointer_bg_color=,
- :dialog_pointer_fg_color,
- :dialog_pointer_fg_color_sequence,
- :dialog_pointer_fg_color=
- ]
-
class Core
ATTR_READER_NAMES = %i(
completion_append_character
@@ -87,8 +72,7 @@ module Reline
extend Forwardable
def_delegators :config,
:autocompletion,
- :autocompletion=,
- *DIALOG_COLOR_APIS
+ :autocompletion=
def initialize
self.output = STDOUT
@@ -272,10 +256,10 @@ module Reline
contents: result,
scrollbar: true,
height: 15,
- bg_color: config.dialog_default_bg_color_sequence,
- pointer_bg_color: config.dialog_pointer_bg_color_sequence,
- fg_color: config.dialog_default_fg_color_sequence,
- pointer_fg_color: config.dialog_pointer_fg_color_sequence
+ bg_color: 46,
+ pointer_bg_color: 45,
+ fg_color: 37,
+ pointer_fg_color: 37
)
}
Reline::DEFAULT_DIALOG_CONTEXT = Array.new
@@ -561,7 +545,6 @@ module Reline
def_single_delegators :core, :add_dialog_proc
def_single_delegators :core, :dialog_proc
def_single_delegators :core, :autocompletion, :autocompletion=
- def_single_delegators :core, *DIALOG_COLOR_APIS
def_single_delegators :core, :readmultiline
def_instance_delegators self, :readmultiline
@@ -584,10 +567,6 @@ module Reline
core.filename_quote_characters = ""
core.special_prefixes = ""
core.add_dialog_proc(:autocomplete, Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE, Reline::DEFAULT_DIALOG_CONTEXT)
- core.dialog_default_bg_color = :cyan
- core.dialog_default_fg_color = :white
- core.dialog_pointer_bg_color = :magenta
- core.dialog_pointer_fg_color = :white
}
end
@@ -601,24 +580,21 @@ module Reline
end
require 'reline/general_io'
-if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
- require 'reline/windows'
- if Reline::Windows.msys_tty?
- Reline::IOGate = if ENV['TERM'] == 'dumb'
- Reline::GeneralIO
- else
- require 'reline/ansi'
- Reline::ANSI
- end
+io = Reline::GeneralIO
+unless ENV['TERM'] == 'dumb'
+ case RbConfig::CONFIG['host_os']
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
+ require 'reline/windows'
+ tty = (io = Reline::Windows).msys_tty?
else
- Reline::IOGate = Reline::Windows
+ tty = $stdout.tty?
end
+end
+Reline::IOGate = if tty
+ require 'reline/ansi'
+ Reline::ANSI
else
- Reline::IOGate = if $stdout.isatty
- require 'reline/ansi'
- Reline::ANSI
- else
- Reline::GeneralIO
- end
+ io
end
+
Reline::HISTORY = Reline::History.new(Reline.core.config)
diff --git a/lib/reline/ansi.rb b/lib/reline/ansi.rb
index ab147a6185..c40085e50d 100644
--- a/lib/reline/ansi.rb
+++ b/lib/reline/ansi.rb
@@ -150,7 +150,7 @@ class Reline::ANSI
until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte }
Reline.core.line_editor.resize
end
- (c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
+ (c == 0x16 && @@input.raw(min: 0, time: 0, &:getbyte)) || c
rescue Errno::EIO
# Maybe the I/O has been closed.
nil
diff --git a/lib/reline/config.rb b/lib/reline/config.rb
index cef7735bdb..5ba269258f 100644
--- a/lib/reline/config.rb
+++ b/lib/reline/config.rb
@@ -46,10 +46,6 @@ class Reline::Config
end
attr_accessor :autocompletion
- attr_reader :dialog_default_bg_color_sequence,
- :dialog_default_fg_color_sequence,
- :dialog_pointer_bg_color_sequence,
- :dialog_pointer_fg_color_sequence
def initialize
@additional_key_bindings = {} # from inputrc
@@ -75,10 +71,6 @@ class Reline::Config
@test_mode = false
@autocompletion = false
@convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
- @dialog_default_bg_color_sequence = nil
- @dialog_pointer_bg_color_sequence = nil
- @dialog_default_fg_color_sequence = nil
- @dialog_pointer_fg_color_sequence = nil
end
def reset
@@ -104,65 +96,6 @@ class Reline::Config
(val.respond_to?(:any?) ? val : [val]).any?(@editing_mode_label)
end
- def dialog_default_bg_color=(color)
- @dialog_default_bg_color_sequence = dialog_color_to_code(:bg, color)
- end
-
- def dialog_default_fg_color=(color)
- @dialog_default_fg_color_sequence = dialog_color_to_code(:fg, color)
- end
-
- def dialog_pointer_bg_color=(color)
- @dialog_pointer_bg_color_sequence = dialog_color_to_code(:bg, color)
- end
-
- def dialog_pointer_fg_color=(color)
- @dialog_pointer_fg_color_sequence = dialog_color_to_code(:fg, color)
- end
-
- def dialog_default_bg_color
- dialog_code_to_color(:bg, @dialog_default_bg_color_sequence)
- end
-
- def dialog_default_fg_color
- dialog_code_to_color(:fg, @dialog_default_fg_color_sequence)
- end
-
- def dialog_pointer_bg_color
- dialog_code_to_color(:bg, @dialog_pointer_bg_color_sequence)
- end
-
- def dialog_pointer_fg_color
- dialog_code_to_color(:fg, @dialog_pointer_fg_color_sequence)
- end
-
- COLORS = [
- :black,
- :red,
- :green,
- :yellow,
- :blue,
- :magenta,
- :cyan,
- :white
- ].freeze
-
- private def dialog_color_to_code(type, color)
- base = type == :bg ? 40 : 30
- c = COLORS.index(color.to_sym)
-
- if c
- base + c
- else
- raise ArgumentError.new("Unknown color: #{color}.\nAvailable colors: #{COLORS.join(", ")}")
- end
- end
-
- private def dialog_code_to_color(type, code)
- base = type == :bg ? 40 : 30
- COLORS[code - base]
- end
-
def keymap
@key_actors[@keymap_label]
end
@@ -395,14 +328,6 @@ class Reline::Config
@vi_ins_mode_string = retrieve_string(value)
when 'emacs-mode-string'
@emacs_mode_string = retrieve_string(value)
- when 'dialog-default-bg-color'
- self.dialog_default_bg_color = value
- when 'dialog-default-fg-color'
- self.dialog_default_fg_color = value
- when 'dialog-pointer-bg-color'
- self.dialog_pointer_bg_color = value
- when 'dialog-pointer-fg-color'
- self.dialog_pointer_fg_color = value
when *VARIABLE_NAMES then
variable_name = :"@#{name.tr(?-, ?_)}"
instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
diff --git a/lib/reline/general_io.rb b/lib/reline/general_io.rb
index 3fafad5c6e..92c76cbba1 100644
--- a/lib/reline/general_io.rb
+++ b/lib/reline/general_io.rb
@@ -57,6 +57,12 @@ class Reline::GeneralIO
Reline::CursorPos.new(1, 1)
end
+ def self.hide_cursor
+ end
+
+ def self.show_cursor
+ end
+
def self.move_cursor_column(val)
end
diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb
index 8d0719ef7c..8153aaba05 100644
--- a/lib/reline/line_editor.rb
+++ b/lib/reline/line_editor.rb
@@ -655,7 +655,10 @@ class Reline::LineEditor
end
private def padding_space_with_escape_sequences(str, width)
- str + (' ' * (width - calculate_width(str, true)))
+ padding_width = width - calculate_width(str, true)
+ # padding_width should be only positive value. But macOS and Alacritty returns negative value.
+ padding_width = 0 if padding_width < 0
+ str + (' ' * padding_width)
end
private def render_each_dialog(dialog, cursor_column)
@@ -758,7 +761,6 @@ class Reline::LineEditor
@output.write @full_block
elsif dialog.scrollbar_pos <= (i * 2) and (i * 2) < (dialog.scrollbar_pos + bar_height)
@output.write @upper_half_block
- str += ''
elsif dialog.scrollbar_pos <= (i * 2 + 1) and (i * 2) < (dialog.scrollbar_pos + bar_height)
@output.write @lower_half_block
else
@@ -1428,7 +1430,7 @@ class Reline::LineEditor
if @waiting_operator_proc
if VI_MOTIONS.include?(method_symbol)
old_cursor, old_byte_pointer = @cursor, @byte_pointer
- @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg > 1
+ @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg&.> 1
block.(true)
unless @waiting_proc
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
diff --git a/lib/reline/version.rb b/lib/reline/version.rb
index 1bb1c02f3d..67a3d694bd 100644
--- a/lib/reline/version.rb
+++ b/lib/reline/version.rb
@@ -1,3 +1,3 @@
module Reline
- VERSION = '0.3.1'
+ VERSION = '0.3.2'
end
diff --git a/lib/resolv-replace.gemspec b/lib/resolv-replace.gemspec
index 6bc07dbe10..48f7108a8e 100644
--- a/lib/resolv-replace.gemspec
+++ b/lib/resolv-replace.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "resolv-replace"
- spec.version = "0.1.0"
+ spec.version = "0.1.1"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/resolv.gemspec b/lib/resolv.gemspec
index c6a0609b51..f221010ab6 100644
--- a/lib/resolv.gemspec
+++ b/lib/resolv.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "resolv"
- spec.version = "0.2.1"
+ spec.version = "0.2.3"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/resolv.rb b/lib/resolv.rb
index 61c9c7d5cf..eaea69bfd5 100644
--- a/lib/resolv.rb
+++ b/lib/resolv.rb
@@ -1624,6 +1624,7 @@ class Resolv
prev_index = @index
save_index = nil
d = []
+ size = -1
while true
raise DecodeError.new("limit exceeded") if @limit <= @index
case @data.getbyte(@index)
@@ -1644,7 +1645,10 @@ class Resolv
end
@index = idx
else
- d << self.get_label
+ l = self.get_label
+ d << l
+ size += 1 + l.string.bytesize
+ raise DecodeError.new("name label data exceed 255 octets") if size > 255
end
end
end
diff --git a/lib/ruby_vm/mjit/c_pointer.rb b/lib/ruby_vm/mjit/c_pointer.rb
new file mode 100644
index 0000000000..a92c2140ae
--- /dev/null
+++ b/lib/ruby_vm/mjit/c_pointer.rb
@@ -0,0 +1,329 @@
+module RubyVM::MJIT # :nodoc: all
+ # Every class under this namespace is a pointer. Even if the type is
+ # immediate, it shouldn't be dereferenced until `*` is called.
+ module CPointer
+ # Note: We'd like to avoid alphabetic method names to avoid a conflict
+ # with member methods. to_i and to_s are considered an exception.
+ class Struct
+ # @param name [String]
+ # @param sizeof [Integer]
+ # @param members [Hash{ Symbol => [RubyVM::MJIT::CType::*, Integer, TrueClass] }]
+ def initialize(addr, sizeof, members)
+ @addr = addr
+ @sizeof = sizeof
+ @members = members
+ end
+
+ # Get a raw address
+ def to_i
+ @addr
+ end
+
+ # Serialized address for generated code
+ def to_s
+ "0x#{@addr.to_s(16)}"
+ end
+
+ # Pointer diff
+ def -(struct)
+ raise ArgumentError if self.class != struct.class
+ (@addr - struct.to_i) / @sizeof
+ end
+
+ # Primitive API that does no automatic dereference
+ # TODO: remove this?
+ # @param member [Symbol]
+ def [](member)
+ type, offset = @members.fetch(member)
+ type.new(@addr + offset / 8)
+ end
+
+ private
+
+ # @param member [Symbol]
+ # @param value [Object]
+ def []=(member, value)
+ type, offset = @members.fetch(member)
+ type[@addr + offset / 8] = value
+ end
+
+ # @param sizeof [Integer]
+ # @param members [Hash{ Symbol => [Integer, RubyVM::MJIT::CType::*] }]
+ def self.define(sizeof, members)
+ Class.new(self) do
+ # Return the size of this type
+ define_singleton_method(:sizeof) { sizeof }
+
+ define_method(:initialize) do |addr = nil|
+ if addr.nil? # TODO: get rid of this feature later
+ addr = Fiddle.malloc(sizeof)
+ end
+ super(addr, sizeof, members)
+ end
+
+ members.each do |member, (type, offset, to_ruby)|
+ # Intelligent API that does automatic dereference
+ define_method(member) do
+ value = self[member]
+ if value.respond_to?(:*)
+ value = value.*
+ end
+ if to_ruby
+ value = C.to_ruby(value)
+ end
+ value
+ end
+
+ define_method("#{member}=") do |value|
+ self[member] = value
+ end
+ end
+ end
+ end
+ end
+
+ # Note: We'd like to avoid alphabetic method names to avoid a conflict
+ # with member methods. to_i is considered an exception.
+ class Union
+ # @param _name [String] To be used when it starts defining a union pointer class
+ # @param sizeof [Integer]
+ # @param members [Hash{ Symbol => RubyVM::MJIT::CType::* }]
+ def initialize(addr, sizeof, members)
+ @addr = addr
+ @sizeof = sizeof
+ @members = members
+ end
+
+ # Get a raw address
+ def to_i
+ @addr
+ end
+
+ # Move addr to access this pointer like an array
+ def +(index)
+ raise ArgumentError unless index.is_a?(Integer)
+ self.class.new(@addr + index * @sizeof)
+ end
+
+ # Pointer diff
+ def -(union)
+ raise ArgumentError if self.class != union.class
+ (@addr - union.instance_variable_get(:@addr)) / @sizeof
+ end
+
+ # @param sizeof [Integer]
+ # @param members [Hash{ Symbol => RubyVM::MJIT::CType::* }]
+ def self.define(sizeof, members)
+ Class.new(self) do
+ # Return the size of this type
+ define_singleton_method(:sizeof) { sizeof }
+
+ define_method(:initialize) do |addr|
+ super(addr, sizeof, members)
+ end
+
+ members.each do |member, type|
+ # Intelligent API that does automatic dereference
+ define_method(member) do
+ value = type.new(@addr)
+ if value.respond_to?(:*)
+ value = value.*
+ end
+ value
+ end
+ end
+ end
+ end
+ end
+
+ class Immediate
+ # @param addr [Integer]
+ # @param size [Integer]
+ # @param pack [String]
+ def initialize(addr, size, pack)
+ @addr = addr
+ @size = size
+ @pack = pack
+ end
+
+ # Get a raw address
+ def to_i
+ @addr
+ end
+
+ # Move addr to addess this pointer like an array
+ def +(index)
+ Immediate.new(@addr + index * @size, @size, @pack)
+ end
+
+ # Dereference
+ def *
+ self[0]
+ end
+
+ # Array access
+ def [](index)
+ return nil if @addr == 0
+ Fiddle::Pointer.new(@addr + index * @size)[0, @size].unpack1(@pack)
+ end
+
+ # Array set
+ def []=(index, value)
+ Fiddle::Pointer.new(@addr + index * @size)[0, @size] = [value].pack(@pack)
+ end
+
+ # Serialized address for generated code. Used for embedding things like body->iseq_encoded.
+ def to_s
+ "0x#{Integer(@addr).to_s(16)}"
+ end
+
+ # @param fiddle_type [Integer] Fiddle::TYPE_*
+ def self.define(fiddle_type)
+ size = Fiddle::PackInfo::SIZE_MAP.fetch(fiddle_type)
+ pack = Fiddle::PackInfo::PACK_MAP.fetch(fiddle_type)
+
+ Class.new(self) do
+ define_method(:initialize) do |addr|
+ super(addr, size, pack)
+ end
+
+ define_singleton_method(:size) do
+ size
+ end
+
+ # Type-level []=: Used by struct fields
+ define_singleton_method(:[]=) do |addr, value|
+ Fiddle::Pointer.new(addr)[0, size] = [value].pack(pack)
+ end
+ end
+ end
+ end
+
+ # -Fiddle::TYPE_CHAR Immediate with special handling of true/false
+ class Bool < Immediate.define(-Fiddle::TYPE_CHAR)
+ # Dereference
+ def *
+ return nil if @addr == 0
+ super != 0
+ end
+
+ def self.[]=(addr, value)
+ super(addr, value ? 1 : 0)
+ end
+ end
+
+ class Pointer
+ attr_reader :type
+
+ # @param addr [Integer]
+ # @param type [Class] RubyVM::MJIT::CType::*
+ def initialize(addr, type)
+ @addr = addr
+ @type = type
+ end
+
+ # Move addr to addess this pointer like an array
+ def +(index)
+ raise ArgumentError unless index.is_a?(Integer)
+ Pointer.new(@addr + index * Fiddle::SIZEOF_VOIDP, @type)
+ end
+
+ # Dereference
+ def *
+ return nil if dest_addr == 0
+ @type.new(dest_addr)
+ end
+
+ # Array access
+ def [](index)
+ (self + index).*
+ end
+
+ # Array set
+ # @param index [Integer]
+ # @param value [Integer, RubyVM::MJIT::CPointer::Struct] an address itself or an object that return an address with to_i
+ def []=(index, value)
+ Fiddle::Pointer.new(@addr + index * Fiddle::SIZEOF_VOIDP)[0, Fiddle::SIZEOF_VOIDP] =
+ [value.to_i].pack(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP])
+ end
+
+ private
+
+ def dest_addr
+ Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_VOIDP].unpack1(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP])
+ end
+
+ def self.define(block)
+ Class.new(self) do
+ define_method(:initialize) do |addr|
+ super(addr, block.call)
+ end
+
+ # Type-level []=: Used by struct fields
+ # @param addr [Integer]
+ # @param value [Integer, RubyVM::MJIT::CPointer::Struct] an address itself, or an object that return an address with to_i
+ define_singleton_method(:[]=) do |addr, value|
+ value = value.to_i
+ Fiddle::Pointer.new(addr)[0, Fiddle::SIZEOF_VOIDP] = [value].pack(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP])
+ end
+ end
+ end
+ end
+
+ class BitField
+ # @param addr [Integer]
+ # @param width [Integer]
+ # @param offset [Integer]
+ def initialize(addr, width, offset)
+ @addr = addr
+ @width = width
+ @offset = offset
+ end
+
+ # Dereference
+ def *
+ byte = Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_CHAR].unpack1('c')
+ if @width == 1
+ bit = (1 & (byte >> @offset))
+ bit == 1
+ elsif @width <= 8 && @offset == 0
+ bitmask = @width.times.sum { |i| 1 << i }
+ byte & bitmask
+ else
+ raise NotImplementedError.new("not-implemented bit field access: width=#{@width} offset=#{@offset}")
+ end
+ end
+
+ # @param width [Integer]
+ # @param offset [Integer]
+ def self.define(width, offset)
+ Class.new(self) do
+ define_method(:initialize) do |addr|
+ super(addr, width, offset)
+ end
+ end
+ end
+ end
+
+ # Give a name to a dynamic CPointer class to see it on inspect
+ def self.with_class_name(prefix, name, cache: false, &block)
+ return block.call if name.empty?
+
+ # Use a cached result only if cache: true
+ class_name = "#{prefix}_#{name}"
+ klass =
+ if cache && self.const_defined?(class_name)
+ self.const_get(class_name)
+ else
+ block.call
+ end
+
+ # Give it a name unless it's already defined
+ unless self.const_defined?(class_name)
+ self.const_set(class_name, klass)
+ end
+
+ klass
+ end
+ end
+end
diff --git a/lib/ruby_vm/mjit/c_type.rb b/lib/ruby_vm/mjit/c_type.rb
new file mode 100644
index 0000000000..9c965ad2fb
--- /dev/null
+++ b/lib/ruby_vm/mjit/c_type.rb
@@ -0,0 +1,91 @@
+require 'fiddle'
+require 'fiddle/pack'
+require_relative 'c_pointer'
+
+module RubyVM::MJIT # :nodoc: all
+ module CType
+ module Struct
+ # @param name [String]
+ # @param members [Hash{ Symbol => [Integer, RubyVM::MJIT::CType::*] }]
+ def self.new(name, sizeof, **members)
+ name = members.keys.join('_') if name.empty?
+ CPointer.with_class_name('Struct', name) do
+ CPointer::Struct.define(sizeof, members)
+ end
+ end
+ end
+
+ module Union
+ # @param name [String]
+ # @param members [Hash{ Symbol => RubyVM::MJIT::CType::* }]
+ def self.new(name, sizeof, **members)
+ name = members.keys.join('_') if name.empty?
+ CPointer.with_class_name('Union', name) do
+ CPointer::Union.define(sizeof, members)
+ end
+ end
+ end
+
+ module Immediate
+ # @param fiddle_type [Integer]
+ def self.new(fiddle_type)
+ name = Fiddle.constants.find do |const|
+ const.start_with?('TYPE_') && Fiddle.const_get(const) == fiddle_type.abs
+ end&.to_s
+ name.delete_prefix!('TYPE_')
+ if fiddle_type.negative?
+ name.prepend('U')
+ end
+ CPointer.with_class_name('Immediate', name, cache: true) do
+ CPointer::Immediate.define(fiddle_type)
+ end
+ end
+
+ # @param type [String]
+ def self.parse(ctype)
+ new(Fiddle::Importer.parse_ctype(ctype))
+ end
+
+ def self.find(size, signed)
+ fiddle_type = TYPE_MAP.fetch(size)
+ fiddle_type = -fiddle_type unless signed
+ new(fiddle_type)
+ end
+
+ TYPE_MAP = Fiddle::PackInfo::SIZE_MAP.map { |type, size| [size, type.abs] }.to_h
+ private_constant :TYPE_MAP
+ end
+
+ module Bool
+ def self.new
+ CPointer::Bool
+ end
+ end
+
+ class Pointer
+ # This takes a block to avoid "stack level too deep" on a cyclic reference
+ # @param block [Proc]
+ def self.new(&block)
+ CPointer.with_class_name('Pointer', block.object_id.to_s) do
+ CPointer::Pointer.define(block)
+ end
+ end
+ end
+
+ module BitField
+ # @param width [Integer]
+ # @param offset [Integer]
+ def self.new(width, offset)
+ CPointer.with_class_name('BitField', "#{offset}_#{width}") do
+ CPointer::BitField.define(width, offset)
+ end
+ end
+ end
+
+ # Types that are referenced but not part of code generation targets
+ Stub = ::Struct.new(:name)
+
+ # Types that it failed to figure out from the header
+ Unknown = Module.new
+ end
+end
diff --git a/lib/ruby_vm/mjit/compiler.rb b/lib/ruby_vm/mjit/compiler.rb
new file mode 100644
index 0000000000..81022cd0a8
--- /dev/null
+++ b/lib/ruby_vm/mjit/compiler.rb
@@ -0,0 +1,952 @@
+# Available variables and macros in JIT-ed function:
+# ec: the first argument of _mjitXXX
+# reg_cfp: the second argument of _mjitXXX
+# GET_CFP(): refers to `reg_cfp`
+# GET_EP(): refers to `reg_cfp->ep`
+# GET_SP(): refers to `reg_cfp->sp`, or `(stack + stack_size)` if local_stack_p
+# GET_SELF(): refers to `cfp_self`
+# GET_LEP(): refers to `VM_EP_LEP(reg_cfp->ep)`
+# EXEC_EC_CFP(): refers to `val = vm_exec(ec, true)` with frame setup
+# CALL_METHOD(): using `GET_CFP()` and `EXEC_EC_CFP()`
+# TOPN(): refers to `reg_cfp->sp`, or `*(stack + (stack_size - num - 1))` if local_stack_p
+# STACK_ADDR_FROM_TOP(): refers to `reg_cfp->sp`, or `stack + (stack_size - num)` if local_stack_p
+# DISPATCH_ORIGINAL_INSN(): expanded in _mjit_compile_insn.erb
+# THROW_EXCEPTION(): specially defined for JIT
+# RESTORE_REGS(): specially defined for `leave`
+class RubyVM::MJIT::Compiler # :nodoc: all
+ C = RubyVM::MJIT.const_get(:C, false)
+ INSNS = RubyVM::MJIT.const_get(:INSNS, false)
+ UNSUPPORTED_INSNS = [
+ :defineclass, # low priority
+ ]
+
+ def initialize = freeze
+
+ # @param iseq [RubyVM::MJIT::CPointer::Struct]
+ # @param funcname [String]
+ # @param id [Integer]
+ # @return [String,NilClass]
+ def compile(iseq, funcname, id)
+ status = C.compile_status.new # not freed for now
+ status.compiled_iseq = iseq.body
+ status.compiled_id = id
+ init_compile_status(status, iseq.body, true) # not freed for now
+ if iseq.body.ci_size > 0 && status.cc_entries_index == -1
+ return nil
+ end
+
+ src = +''
+ if !status.compile_info.disable_send_cache && !status.compile_info.disable_inlining
+ unless precompile_inlinable_iseqs(src, iseq, status)
+ return nil
+ end
+ end
+
+ src << "VALUE\n#{funcname}(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)\n{\n"
+ success = compile_body(src, iseq, status)
+ src << "\n} // end of #{funcname}\n"
+
+ return success ? src : nil
+ rescue Exception => e # should we use rb_rescue in C instead?
+ if C.mjit_opts.warnings || C.mjit_opts.verbose > 0
+ $stderr.puts "MJIT error: #{e.full_message}"
+ end
+ return nil
+ end
+
+ private
+
+ def compile_body(src, iseq, status)
+ status.success = true
+ status.local_stack_p = !iseq.body.catch_except_p
+
+ if status.local_stack_p
+ src << " VALUE stack[#{iseq.body.stack_max}];\n"
+ else
+ src << " VALUE *stack = reg_cfp->sp;\n"
+ end
+
+ unless status.inlined_iseqs.nil? # i.e. compile root
+ src << " static const rb_iseq_t *original_iseq = (const rb_iseq_t *)#{iseq};\n"
+ end
+ src << " static const VALUE *const original_body_iseq = (VALUE *)#{iseq.body.iseq_encoded};\n"
+
+ src << " VALUE cfp_self = reg_cfp->self;\n" # cache self across the method
+ src << "#undef GET_SELF\n"
+ src << "#define GET_SELF() cfp_self\n"
+
+ # Generate merged ivar guards first if needed
+ if !status.compile_info.disable_ivar_cache && using_ivar?(iseq.body)
+ src << " if (UNLIKELY(!RB_TYPE_P(GET_SELF(), T_OBJECT))) {"
+ src << " goto ivar_cancel;\n"
+ src << " }\n"
+ end
+
+ # Simulate `opt_pc` in setup_parameters_complex. Other PCs which may be passed by catch tables
+ # are not considered since vm_exec doesn't call jit_exec for catch tables.
+ if iseq.body.param.flags.has_opt
+ src << "\n"
+ src << " switch (reg_cfp->pc - ISEQ_BODY(reg_cfp->iseq)->iseq_encoded) {\n"
+ (0..iseq.body.param.opt_num).each do |i|
+ pc_offset = iseq.body.param.opt_table[i]
+ src << " case #{pc_offset}:\n"
+ src << " goto label_#{pc_offset};\n"
+ end
+ src << " }\n"
+ end
+
+ compile_insns(0, 0, status, iseq.body, src)
+ compile_cancel_handler(src, iseq.body, status)
+ src << "#undef GET_SELF\n"
+ return status.success
+ end
+
+ # Compile one conditional branch. If it has branchXXX insn, this should be
+ # called multiple times for each branch.
+ def compile_insns(stack_size, pos, status, body, src)
+ branch = C.compile_branch.new # not freed for now
+ branch.stack_size = stack_size
+ branch.finish_p = false
+
+ while pos < body.iseq_size && !already_compiled?(status, pos) && !branch.finish_p
+ insn = INSNS.fetch(C.rb_vm_insn_decode(body.iseq_encoded[pos]))
+ status.stack_size_for_pos[pos] = branch.stack_size
+
+ src << "\nlabel_#{pos}: /* #{insn.name} */\n"
+ pos = compile_insn(insn, pos, status, body.iseq_encoded + (pos+1), body, branch, src)
+ if status.success && branch.stack_size > body.stack_max
+ if mjit_opts.warnings || mjit_opts.verbose > 0
+ $stderr.puts "MJIT warning: JIT stack size (#{branch.stack_size}) exceeded its max size (#{body.stack_max})"
+ end
+ status.success = false
+ end
+ break unless status.success
+ end
+ end
+
+ # Main function of JIT compilation, vm_exec_core counterpart for JIT. Compile one insn to `f`, may modify
+ # b->stack_size and return next position.
+ #
+ # When you add a new instruction to insns.def, it would be nice to have JIT compilation support here but
+ # it's optional. This JIT compiler just ignores ISeq which includes unknown instruction, and ISeq which
+ # does not have it can be compiled as usual.
+ def compile_insn(insn, pos, status, operands, body, b, src)
+ sp_inc = C.mjit_call_attribute_sp_inc(insn.bin, operands)
+ next_pos = pos + insn.len
+
+ result = compile_insn_entry(insn, b.stack_size, sp_inc, status.local_stack_p, pos, next_pos, insn.len,
+ status.inlined_iseqs.nil?, status, operands, body)
+ if result.nil?
+ if C.mjit_opts.warnings || C.mjit_opts.verbose > 0
+ $stderr.puts "MJIT warning: Skipped to compile unsupported instruction: #{insn.name}"
+ end
+ status.success = false
+ else
+ result_src, next_pos, finish_p, compile_insns_p = result
+
+ src << result_src
+ b.stack_size += sp_inc
+
+ if finish_p
+ b.finish_p = true
+ end
+ if compile_insns_p
+ if already_compiled?(status, pos + insn.len)
+ src << "goto label_#{pos + insn.len};\n"
+ else
+ compile_insns(b.stack_size, pos + insn.len, status, body, src)
+ end
+ end
+ end
+
+ # If next_pos is already compiled and this branch is not finished yet,
+ # next instruction won't be compiled in C code next and will need `goto`.
+ if !b.finish_p && next_pos < body.iseq_size && already_compiled?(status, next_pos)
+ src << "goto label_#{next_pos};\n"
+
+ # Verify stack size assumption is the same among multiple branches
+ if status.stack_size_for_pos[next_pos] != b.stack_size
+ if mjit_opts.warnings || mjit_opts.verbose > 0
+ $stderr.puts "MJIT warning: JIT stack assumption is not the same between branches (#{status.stack_size_for_pos[next_pos]} != #{b.stack_size})\n"
+ end
+ status.success = false
+ end
+ end
+
+ return next_pos
+ end
+
+ def compile_insn_entry(insn, stack_size, sp_inc, local_stack_p, pos, next_pos, insn_len, inlined_iseq_p, status, operands, body)
+ finish_p = false
+ compile_insns = false
+
+ # TODO: define this outside this method, or at least cache it
+ opt_send_without_block = INSNS.values.find { |i| i.name == :opt_send_without_block }
+ if opt_send_without_block.nil?
+ raise 'opt_send_without_block not found'
+ end
+ send_compatible_opt_insns = INSNS.values.select do |insn|
+ insn.name.start_with?('opt_') && opt_send_without_block.opes == insn.opes &&
+ insn.expr.lines.any? { |l| l.match(/\A\s+CALL_SIMPLE_METHOD\(\);\s+\z/) }
+ end.map(&:name)
+
+ case insn.name
+ when *UNSUPPORTED_INSNS
+ return nil
+ when :opt_send_without_block, :send
+ if src = compile_send(insn, stack_size, sp_inc, local_stack_p, pos, next_pos, status, operands, body)
+ return src, next_pos, finish_p, compile_insns
+ end
+ when *send_compatible_opt_insns
+ if C.has_cache_for_send(captured_cc_entries(status)[call_data_index(C.CALL_DATA.new(operands[0]), body)], insn.bin) &&
+ src = compile_send(opt_send_without_block, stack_size, sp_inc, local_stack_p, pos, next_pos, status, operands, body)
+ return src, next_pos, finish_p, compile_insns
+ end
+ when :getinstancevariable, :setinstancevariable
+ if src = compile_ivar(insn.name, stack_size, pos, status, operands, body)
+ return src, next_pos, finish_p, compile_insns
+ end
+ when :opt_getconstant_path
+ if src = compile_getconstant_path(stack_size, pos, insn_len, operands, status)
+ return src, next_pos, finish_p, compile_insns
+ end
+ when :invokebuiltin, :opt_invokebuiltin_delegate, :opt_invokebuiltin_delegate_leave
+ if src = compile_invokebuiltin(insn, stack_size, sp_inc, body, operands)
+ if insn.name == :opt_invokebuiltin_delegate_leave
+ src << compile_leave(stack_size, pos, inlined_iseq_p)
+ finish_p = true
+ end
+ return src, next_pos, finish_p, compile_insns
+ end
+ when :leave
+ if stack_size != 1
+ raise "Unexpected JIT stack_size on leave: #{stack_size}"
+ end
+ src = compile_leave(stack_size, pos, inlined_iseq_p)
+ finish_p = true
+ return src, next_pos, finish_p, compile_insns
+ end
+
+ return compile_insn_default(insn, stack_size, sp_inc, local_stack_p, pos, next_pos, insn_len, inlined_iseq_p, operands)
+ end
+
+ # Optimized case of send / opt_send_without_block instructions.
+ def compile_send(insn, stack_size, sp_inc, local_stack_p, pos, next_pos, status, operands, body)
+ # compiler: Use captured cc to avoid race condition
+ cd = C.CALL_DATA.new(operands[0])
+ cd_index = call_data_index(cd, body)
+ captured_cc = captured_cc_entries(status)[cd_index]
+
+ # compiler: Inline send insn where some supported fastpath is used.
+ ci = cd.ci
+ kw_splat = (C.vm_ci_flag(ci) & C.VM_CALL_KW_SPLAT) > 0
+ if !status.compile_info.disable_send_cache && has_valid_method_type?(captured_cc) && (
+ # `CC_SET_FASTPATH(cd->cc, vm_call_cfunc_with_frame, ...)` in `vm_call_cfunc`
+ (vm_cc_cme(captured_cc).def.type == C.VM_METHOD_TYPE_CFUNC && !C.rb_splat_or_kwargs_p(ci) && !kw_splat) ||
+ # `CC_SET_FASTPATH(cc, vm_call_iseq_setup_func(...), vm_call_iseq_optimizable_p(...))` in `vm_callee_setup_arg`,
+ # and support only non-VM_CALL_TAILCALL path inside it
+ (vm_cc_cme(captured_cc).def.type == C.VM_METHOD_TYPE_ISEQ &&
+ C.fastpath_applied_iseq_p(ci, captured_cc, iseq = def_iseq_ptr(vm_cc_cme(captured_cc).def)) &&
+ (C.vm_ci_flag(ci) & C.VM_CALL_TAILCALL) == 0)
+ )
+ src = +"{\n"
+
+ # JIT: Invalidate call cache if it requires vm_search_method. This allows to inline some of following things.
+ src << " const struct rb_callcache *cc = (struct rb_callcache *)#{captured_cc};\n"
+ src << " const rb_callable_method_entry_t *cc_cme = (rb_callable_method_entry_t *)#{vm_cc_cme(captured_cc)};\n"
+ src << " const VALUE recv = stack[#{stack_size + sp_inc - 1}];\n"
+ # If opt_class_of is true, use RBASIC_CLASS instead of CLASS_OF to reduce code size
+ opt_class_of = !maybe_special_const?(captured_cc.klass)
+ src << " if (UNLIKELY(#{opt_class_of ? 'RB_SPECIAL_CONST_P(recv)' : 'false'} || !vm_cc_valid_p(cc, cc_cme, #{opt_class_of ? 'RBASIC_CLASS' : 'CLASS_OF'}(recv)))) {\n"
+ src << " reg_cfp->pc = original_body_iseq + #{pos};\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ src << " goto send_cancel;\n"
+ src << " }\n"
+
+ # JIT: move sp and pc if necessary
+ pc_moved_p = compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos)
+
+ # JIT: If ISeq is inlinable, call the inlined method without pushing a frame.
+ if iseq && status.inlined_iseqs && iseq.body.to_i == status.inlined_iseqs[pos]&.to_i
+ src << " {\n"
+ src << " VALUE orig_self = reg_cfp->self;\n"
+ src << " reg_cfp->self = stack[#{stack_size + sp_inc - 1}];\n"
+ src << " stack[#{stack_size + sp_inc - 1}] = _mjit#{status.compiled_id}_inlined_#{pos}(ec, reg_cfp, orig_self, original_iseq);\n"
+ src << " reg_cfp->self = orig_self;\n"
+ src << " }\n"
+ else
+ # JIT: Forked `vm_sendish` (except method_explorer = vm_search_method_wrap) to inline various things
+ src << " {\n"
+ src << " VALUE val;\n"
+ src << " struct rb_calling_info calling;\n"
+ if insn.name == :send
+ src << " calling.block_handler = vm_caller_setup_arg_block(ec, reg_cfp, (const struct rb_callinfo *)#{ci}, (rb_iseq_t *)0x#{operands[1].to_s(16)}, FALSE);\n"
+ else
+ src << " calling.block_handler = VM_BLOCK_HANDLER_NONE;\n"
+ end
+ src << " calling.kw_splat = #{kw_splat ? 1 : 0};\n"
+ src << " calling.recv = stack[#{stack_size + sp_inc - 1}];\n"
+ src << " calling.argc = #{C.vm_ci_argc(ci)};\n"
+
+ if vm_cc_cme(captured_cc).def.type == C.VM_METHOD_TYPE_CFUNC
+ # TODO: optimize this more
+ src << " calling.ci = (const struct rb_callinfo *)#{ci};\n" # creating local cd here because operand's cd->cc may not be the same as inlined cc.
+ src << " calling.cc = cc;"
+ src << " val = vm_call_cfunc_with_frame(ec, reg_cfp, &calling);\n"
+ else # :iseq
+ # fastpath_applied_iseq_p checks rb_simple_iseq_p, which ensures has_opt == FALSE
+ src << " vm_call_iseq_setup_normal(ec, reg_cfp, &calling, cc_cme, 0, #{iseq.body.param.size}, #{iseq.body.local_table_size});\n"
+ if iseq.body.catch_except_p
+ src << " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n"
+ src << " val = vm_exec(ec, true);\n"
+ else
+ src << " if ((val = jit_exec(ec)) == Qundef) {\n"
+ src << " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n" # This is vm_call0_body's code after vm_call_iseq_setup
+ src << " val = vm_exec(ec, false);\n"
+ src << " }\n"
+ end
+ end
+ src << " stack[#{stack_size + sp_inc - 1}] = val;\n"
+ src << " }\n"
+
+ # JIT: We should evaluate ISeq modified for TracePoint if it's enabled. Note: This is slow.
+ src << " if (UNLIKELY(!mjit_call_p)) {\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size + sp_inc};\n"
+ if !pc_moved_p
+ src << " reg_cfp->pc = original_body_iseq + #{next_pos};\n"
+ end
+ src << " goto cancel;\n"
+ src << " }\n"
+ end
+
+ src << "}\n"
+ return src
+ else
+ return nil
+ end
+ end
+
+ def compile_ivar(insn_name, stack_size, pos, status, operands, body)
+ iv_cache = C.iseq_inline_storage_entry.new(operands[1]).iv_cache
+ dest_shape_id = iv_cache.value >> C.SHAPE_FLAG_SHIFT
+ source_shape_id = parent_shape_id(dest_shape_id)
+ attr_index = iv_cache.value & ((1 << C.SHAPE_FLAG_SHIFT) - 1)
+
+ src = +''
+ if !status.compile_info.disable_ivar_cache && source_shape_id != C.INVALID_SHAPE_ID
+ # JIT: optimize away motion of sp and pc. This path does not call rb_warning() and so it's always leaf and not `handles_sp`.
+ # compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos)
+
+ # JIT: prepare vm_getivar/vm_setivar arguments and variables
+ src << "{\n"
+ src << " VALUE obj = GET_SELF();\n" # T_OBJECT guaranteed by compile_body
+ # JIT: cache hit path of vm_getivar/vm_setivar, or cancel JIT (recompile it with exivar)
+ if insn_name == :setinstancevariable
+ src << " const uint32_t index = #{attr_index - 1};\n"
+ src << " const shape_id_t dest_shape_id = (shape_id_t)#{dest_shape_id};\n"
+ src << " if (dest_shape_id == ROBJECT_SHAPE_ID(obj)) {\n"
+ src << " VALUE *ptr = ROBJECT_IVPTR(obj);\n"
+ src << " RB_OBJ_WRITE(obj, &ptr[index], stack[#{stack_size - 1}]);\n"
+ src << " }\n"
+ else
+ src << " const shape_id_t source_shape_id = (shape_id_t)#{dest_shape_id};\n"
+ if attr_index == 0 # cache hit, but uninitialized iv
+ src << " /* Uninitialized instance variable */\n"
+ src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj)) {\n"
+ src << " stack[#{stack_size}] = Qnil;\n"
+ src << " }\n"
+ else
+ src << " const uint32_t index = #{attr_index - 1};\n"
+ src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj)) {\n"
+ src << " stack[#{stack_size}] = ROBJECT_IVPTR(obj)[index];\n"
+ src << " }\n"
+ end
+ end
+ src << " else {\n"
+ src << " reg_cfp->pc = original_body_iseq + #{pos};\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ src << " goto ivar_cancel;\n"
+ src << " }\n"
+ src << "}\n"
+ return src
+ elsif insn_name == :getinstancevariable && !status.compile_info.disable_exivar_cache && source_shape_id != C.INVALID_SHAPE_ID
+ # JIT: optimize away motion of sp and pc. This path does not call rb_warning() and so it's always leaf and not `handles_sp`.
+ # compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos)
+
+ # JIT: prepare vm_getivar's arguments and variables
+ src << "{\n"
+ src << " VALUE obj = GET_SELF();\n"
+ src << " const shape_id_t source_shape_id = (shape_id_t)#{dest_shape_id};\n"
+ src << " const uint32_t index = #{attr_index - 1};\n"
+ # JIT: cache hit path of vm_getivar, or cancel JIT (recompile it without any ivar optimization)
+ src << " struct gen_ivtbl *ivtbl;\n"
+ src << " if (LIKELY(FL_TEST_RAW(GET_SELF(), FL_EXIVAR) && source_shape_id == rb_shape_get_shape_id(obj) && rb_ivar_generic_ivtbl_lookup(obj, &ivtbl))) {\n"
+ src << " stack[#{stack_size}] = ivtbl->ivptr[index];\n"
+ src << " }\n"
+ src << " else {\n"
+ src << " reg_cfp->pc = original_body_iseq + #{pos};\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ src << " goto exivar_cancel;\n"
+ src << " }\n"
+ src << "}\n"
+ return src
+ else
+ return nil
+ end
+ end
+
+ def compile_invokebuiltin(insn, stack_size, sp_inc, body, operands)
+ bf = C.RB_BUILTIN.new(operands[0])
+ if bf.compiler > 0
+ index = (insn.name == :invokebuiltin ? -1 : operands[1])
+ src = +"{\n"
+ src << " VALUE val;\n"
+ C.builtin_compiler(src, bf, index, stack_size, body.builtin_inline_p)
+ src << " stack[#{stack_size + sp_inc - 1}] = val;\n"
+ src << "}\n"
+ return src
+ else
+ return nil
+ end
+ end
+
+ def compile_leave(stack_size, pos, inlined_iseq_p)
+ src = +''
+ # Skip vm_pop_frame for inlined call
+ unless inlined_iseq_p
+ # Cancel on interrupts to make leave insn leaf
+ src << " if (UNLIKELY(RUBY_VM_INTERRUPTED_ANY(ec))) {\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ src << " reg_cfp->pc = original_body_iseq + #{pos};\n"
+ src << " rb_threadptr_execute_interrupts(rb_ec_thread_ptr(ec), 0);\n"
+ src << " }\n"
+ src << " ec->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(reg_cfp);\n" # vm_pop_frame
+ end
+ src << " return stack[0];\n"
+ end
+
+ def compile_getconstant_path(stack_size, pos, insn_len, operands, status)
+ ice = C.IC.new(operands[0]).entry
+ if !status.compile_info.disable_const_cache && ice
+ # JIT: Inline everything in IC, and cancel the slow path
+ src = +" if (vm_inlined_ic_hit_p(#{ice.flags}, #{ice.value}, (const rb_cref_t *)#{to_addr(ice.ic_cref)}, reg_cfp->ep)) {\n"
+ src << " stack[#{stack_size}] = #{ice.value};\n"
+ src << " }\n"
+ src << " else {\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ src << " reg_cfp->pc = original_body_iseq + #{pos};\n"
+ src << " goto const_cancel;\n"
+ src << " }\n"
+ return src
+ else
+ return nil
+ end
+ end
+
+ def compile_insn_default(insn, stack_size, sp_inc, local_stack_p, pos, next_pos, insn_len, inlined_iseq_p, operands)
+ src = +''
+ finish_p = false
+ compile_insns = false
+
+ # JIT: Declare stack_size to be used in some macro of _mjit_compile_insn_body.erb
+ src << "{\n"
+ if local_stack_p
+ src << " MAYBE_UNUSED(unsigned int) stack_size = #{stack_size};\n"
+ end
+
+ # JIT: Declare variables for operands, popped values and return values
+ insn.declarations.each do |decl|
+ src << " #{decl};\n"
+ end
+
+ # JIT: Set const expressions for `RubyVM::OperandsUnifications` insn
+ insn.preamble.each do |amble|
+ src << "#{amble.sub(/const \S+\s+/, '')}\n"
+ end
+
+ # JIT: Initialize operands
+ insn.opes.each_with_index do |ope, i|
+ src << " #{ope.fetch(:name)} = (#{ope.fetch(:type)})#{operands[i]};\n"
+ # TODO: resurrect comment_id
+ end
+
+ # JIT: Initialize popped values
+ insn.pops.reverse_each.with_index.reverse_each do |pop, i|
+ src << " #{pop.fetch(:name)} = stack[#{stack_size - (i + 1)}];\n"
+ end
+
+ # JIT: move sp and pc if necessary
+ pc_moved_p = compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos)
+
+ # JIT: Print insn body in insns.def
+ next_pos = compile_insn_body(src, insn, pos, next_pos, insn_len, local_stack_p, stack_size, sp_inc, operands)
+
+ # JIT: Set return values
+ insn.rets.reverse_each.with_index do |ret, i|
+ # TOPN(n) = ...
+ src << " stack[#{stack_size + sp_inc - (i + 1)}] = #{ret.fetch(:name)};\n"
+ end
+
+ # JIT: We should evaluate ISeq modified for TracePoint if it's enabled. Note: This is slow.
+ # leaf insn may not cancel JIT. leaf_without_check_ints is covered in RUBY_VM_CHECK_INTS of _mjit_compile_insn_body.erb.
+ unless insn.always_leaf? || insn.leaf_without_check_ints?
+ src << " if (UNLIKELY(!mjit_call_p)) {\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size + sp_inc};\n"
+ if !pc_moved_p
+ src << " reg_cfp->pc = original_body_iseq + #{next_pos};\n"
+ end
+ src << " goto cancel;\n"
+ src << " }\n"
+ end
+
+ src << "}\n"
+
+ # compiler: If insn has conditional JUMP, the code should go to the branch not targeted by JUMP next.
+ if insn.expr.match?(/if\s+\([^{}]+\)\s+\{[^{}]+JUMP\([^)]+\);[^{}]+\}/)
+ compile_insns = true
+ end
+
+ # compiler: If insn returns (leave) or does longjmp (throw), the branch should no longer be compiled. TODO: create attr for it?
+ if insn.expr.match?(/\sTHROW_EXCEPTION\([^)]+\);/) || insn.expr.match?(/\bvm_pop_frame\(/)
+ finish_p = true
+ end
+
+ return src, next_pos, finish_p, compile_insns
+ end
+
+ def compile_insn_body(src, insn, pos, next_pos, insn_len, local_stack_p, stack_size, sp_inc, operands)
+ # Print a body of insn, but with macro expansion.
+ expand_simple_macros(insn.expr).each_line do |line|
+ # Expand dynamic macro here
+ # TODO: support combination of following macros in the same line
+ case line
+ when /\A\s+RUBY_VM_CHECK_INTS\(ec\);\s+\z/
+ if insn.leaf_without_check_ints? # lazily move PC and optionalize mjit_call_p here
+ src << " if (UNLIKELY(RUBY_VM_INTERRUPTED_ANY(ec))) {\n"
+ src << " reg_cfp->pc = original_body_iseq + #{next_pos};\n" # ADD_PC(INSN_ATTR(width));
+ src << " rb_threadptr_execute_interrupts(rb_ec_thread_ptr(ec), 0);\n"
+ src << " if (UNLIKELY(!mjit_call_p)) {\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ src << " goto cancel;\n"
+ src << " }\n"
+ src << " }\n"
+ else
+ src << to_cstr(line)
+ end
+ when /\A\s+JUMP\((?<dest>[^)]+)\);\s+\z/
+ dest = Regexp.last_match[:dest]
+ if insn.name == :opt_case_dispatch # special case... TODO: use another macro to avoid checking name
+ hash_offsets = C.rb_hash_values(operands[0]).uniq
+ else_offset = cast_offset(operands[1])
+ base_pos = pos + insn_len
+
+ src << " switch (#{dest}) {\n"
+ hash_offsets.each do |offset|
+ src << " case #{offset}:\n"
+ src << " goto label_#{base_pos + offset};\n"
+ end
+ src << " case #{else_offset}:\n"
+ src << " goto label_#{base_pos + else_offset};\n"
+ src << " }\n"
+ else
+ # Before we `goto` next insn, we need to set return values, especially for getinlinecache
+ insn.rets.reverse_each.with_index do |ret, i|
+ # TOPN(n) = ...
+ src << " stack[#{stack_size + sp_inc - (i + 1)}] = #{ret.fetch(:name)};\n"
+ end
+
+ next_pos = pos + insn_len + cast_offset(operands[0]) # workaround: assuming dest == operands[0]. TODO: avoid relying on it
+ src << " goto label_#{next_pos};\n"
+ end
+ when /\A\s+CALL_SIMPLE_METHOD\(\);\s+\z/
+ # For `opt_xxx`'s fallbacks.
+ if local_stack_p
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ end
+ src << " reg_cfp->pc = original_body_iseq + #{pos};\n"
+ src << " goto cancel;\n"
+ when /\A(?<prefix>.+\b)INSN_LABEL\((?<name>[^)]+)\)(?<suffix>.+)\z/m
+ prefix, name, suffix = Regexp.last_match[:prefix], Regexp.last_match[:name], Regexp.last_match[:suffix]
+ src << "#{prefix}INSN_LABEL(#{name}_#{pos})#{suffix}"
+ else
+ if insn.handles_sp?
+ # If insn.handles_sp? is true, cfp->sp might be changed inside insns (like vm_caller_setup_arg_block)
+ # and thus we need to use cfp->sp, even when local_stack_p is TRUE. When insn.handles_sp? is true,
+ # cfp->sp should be available too because _mjit_compile_pc_and_sp.erb sets it.
+ src << to_cstr(line)
+ else
+ # If local_stack_p is TRUE and insn.handles_sp? is false, stack values are only available in local variables
+ # for stack. So we need to replace those macros if local_stack_p is TRUE here.
+ case line
+ when /\bGET_SP\(\)/
+ # reg_cfp->sp
+ src << to_cstr(line.sub(/\bGET_SP\(\)/, local_stack_p ? '(stack + stack_size)' : 'GET_SP()'))
+ when /\bSTACK_ADDR_FROM_TOP\((?<num>[^)]+)\)/
+ # #define STACK_ADDR_FROM_TOP(n) (GET_SP()-(n))
+ num = Regexp.last_match[:num]
+ src << to_cstr(line.sub(/\bSTACK_ADDR_FROM_TOP\(([^)]+)\)/, local_stack_p ? "(stack + (stack_size - (#{num})))" : "STACK_ADDR_FROM_TOP(#{num})"))
+ when /\bTOPN\((?<num>[^)]+)\)/
+ # #define TOPN(n) (*(GET_SP()-(n)-1))
+ num = Regexp.last_match[:num]
+ src << to_cstr(line.sub(/\bTOPN\(([^)]+)\)/, local_stack_p ? "*(stack + (stack_size - (#{num}) - 1))" : "TOPN(#{num})"))
+ else
+ src << to_cstr(line)
+ end
+ end
+ end
+ end
+ return next_pos
+ end
+
+ def compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos)
+ # JIT: When an insn is leaf, we don't need to Move pc for a catch table on catch_except_p, #caller_locations,
+ # and rb_profile_frames. For check_ints, we lazily move PC when we have interruptions.
+ pc_moved_p = false
+ unless insn.always_leaf? || insn.leaf_without_check_ints?
+ src << " reg_cfp->pc = original_body_iseq + #{next_pos};\n" # ADD_PC(INSN_ATTR(width));
+ pc_moved_p = true
+ end
+
+ # JIT: move sp to use or preserve stack variables
+ if local_stack_p
+ # sp motion is optimized away for `handles_sp? #=> false` case.
+ # Thus sp should be set properly before `goto cancel`.
+ if insn.handles_sp?
+ # JIT-only behavior (pushing JIT's local variables to VM's stack):
+ push_size = -sp_inc + insn.rets.size - insn.pops.size
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{push_size};\n"
+ push_size.times do |i|
+ src << " *(reg_cfp->sp + #{i - push_size}) = stack[#{stack_size - push_size + i}];\n"
+ end
+ end
+ else
+ if insn.handles_sp?
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size - insn.pops.size};\n" # POPN(INSN_ATTR(popn));
+ else
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ end
+ end
+ return pc_moved_p
+ end
+
+ # Print the block to cancel inlined method call. It's supporting only `opt_send_without_block` for now.
+ def compile_inlined_cancel_handler(src, body, inline_context)
+ src << "\ncancel:\n"
+ src << " rb_mjit_recompile_inlining(original_iseq);\n"
+
+ # Swap pc/sp set on cancel with original pc/sp.
+ src << " const VALUE *current_pc = reg_cfp->pc;\n"
+ src << " VALUE *current_sp = reg_cfp->sp;\n"
+ src << " reg_cfp->pc = orig_pc;\n"
+ src << " reg_cfp->sp = orig_sp;\n\n"
+
+ # Lazily push the current call frame.
+ src << " struct rb_calling_info calling;\n"
+ src << " calling.block_handler = VM_BLOCK_HANDLER_NONE;\n" # assumes `opt_send_without_block`
+ src << " calling.argc = #{inline_context.orig_argc};\n"
+ src << " calling.recv = reg_cfp->self;\n"
+ src << " reg_cfp->self = orig_self;\n"
+ # fastpath_applied_iseq_p checks rb_simple_iseq_p, which ensures has_opt == FALSE
+ src << " vm_call_iseq_setup_normal(ec, reg_cfp, &calling, (const rb_callable_method_entry_t *)#{inline_context.me}, 0, #{inline_context.param_size}, #{inline_context.local_size});\n\n"
+
+ # Start usual cancel from here.
+ src << " reg_cfp = ec->cfp;\n" # work on the new frame
+ src << " reg_cfp->pc = current_pc;\n"
+ src << " reg_cfp->sp = current_sp;\n"
+ (0...body.stack_max).each do |i| # should be always `status->local_stack_p`
+ src << " *(vm_base_ptr(reg_cfp) + #{i}) = stack[#{i}];\n"
+ end
+ # We're not just returning Qundef here so that caller's normal cancel handler can
+ # push back `stack` to `cfp->sp`.
+ src << " return vm_exec(ec, false);\n"
+ end
+
+ # Print the block to cancel JIT execution.
+ def compile_cancel_handler(src, body, status)
+ if status.inlined_iseqs.nil? # the current ISeq is being inlined
+ compile_inlined_cancel_handler(src, body, status.inline_context)
+ return
+ end
+
+ src << "\nsend_cancel:\n"
+ src << " rb_mjit_recompile_send(original_iseq);\n"
+ src << " goto cancel;\n"
+
+ src << "\nivar_cancel:\n"
+ src << " rb_mjit_recompile_ivar(original_iseq);\n"
+ src << " goto cancel;\n"
+
+ src << "\nexivar_cancel:\n"
+ src << " rb_mjit_recompile_exivar(original_iseq);\n"
+ src << " goto cancel;\n"
+
+ src << "\nconst_cancel:\n"
+ src << " rb_mjit_recompile_const(original_iseq);\n"
+ src << " goto cancel;\n"
+
+ src << "\ncancel:\n"
+ if status.local_stack_p
+ (0...body.stack_max).each do |i|
+ src << " *(vm_base_ptr(reg_cfp) + #{i}) = stack[#{i}];\n"
+ end
+ end
+ src << " return Qundef;\n"
+ end
+
+ def precompile_inlinable_child_iseq(src, child_iseq, status, ci, cc, pos)
+ child_status = C.compile_status.new # not freed for now
+ child_status.compiled_iseq = status.compiled_iseq
+ child_status.compiled_id = status.compiled_id
+ init_compile_status(child_status, child_iseq.body, false) # not freed for now
+ child_status.inline_context.orig_argc = C.vm_ci_argc(ci)
+ child_status.inline_context.me = vm_cc_cme(cc).to_i
+ child_status.inline_context.param_size = child_iseq.body.param.size
+ child_status.inline_context.local_size = child_iseq.body.local_table_size
+ if child_iseq.body.ci_size > 0 && child_status.cc_entries_index == -1
+ return false
+ end
+
+ src << "ALWAYS_INLINE(static VALUE _mjit#{status.compiled_id}_inlined_#{pos}(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE orig_self, const rb_iseq_t *original_iseq));\n"
+ src << "static inline VALUE\n_mjit#{status.compiled_id}_inlined_#{pos}(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE orig_self, const rb_iseq_t *original_iseq)\n{\n"
+ src << " const VALUE *orig_pc = reg_cfp->pc;\n"
+ src << " VALUE *orig_sp = reg_cfp->sp;\n"
+
+ success = compile_body(src, child_iseq, child_status)
+
+ src << "\n} /* end of _mjit#{status.compiled_id}_inlined_#{pos} */\n\n"
+
+ return success;
+ end
+
+ def precompile_inlinable_iseqs(src, iseq, status)
+ body = iseq.body
+ pos = 0
+ while pos < body.iseq_size
+ insn = INSNS.fetch(C.rb_vm_insn_decode(body.iseq_encoded[pos]))
+ if insn.name == :opt_send_without_block || insn.name == :opt_size # `compile_inlined_cancel_handler` supports only `opt_send_without_block`
+ cd = C.CALL_DATA.new(body.iseq_encoded[pos + 1])
+ ci = cd.ci
+ cc = captured_cc_entries(status)[call_data_index(cd, body)] # use copy to avoid race condition
+
+ if (child_iseq = rb_mjit_inlinable_iseq(ci, cc)) != nil
+ status.inlined_iseqs[pos] = child_iseq.body
+
+ if C.mjit_opts.verbose >= 1 # print beforehand because ISeq may be GCed during copy job.
+ child_location = child_iseq.body.location
+ $stderr.puts "JIT inline: #{child_location.label}@#{C.rb_iseq_path(child_iseq)}:#{C.rb_iseq_first_lineno(child_iseq)} " \
+ "=> #{iseq.body.location.label}@#{C.rb_iseq_path(iseq)}:#{C.rb_iseq_first_lineno(iseq)}"
+ end
+ if !precompile_inlinable_child_iseq(src, child_iseq, status, ci, cc, pos)
+ return false
+ end
+ end
+ end
+ pos += insn.len
+ end
+ return true
+ end
+
+ def init_compile_status(status, body, compile_root_p)
+ status.stack_size_for_pos = Fiddle.malloc(Fiddle::SIZEOF_INT * body.iseq_size)
+ body.iseq_size.times do |i|
+ status.stack_size_for_pos[i] = C.NOT_COMPILED_STACK_SIZE
+ end
+ if compile_root_p
+ status.inlined_iseqs = Fiddle.malloc(Fiddle::SIZEOF_VOIDP * body.iseq_size)
+ body.iseq_size.times do |i|
+ status.inlined_iseqs[i] = nil
+ end
+ end
+ if body.ci_size > 0
+ status.cc_entries_index = C.mjit_capture_cc_entries(status.compiled_iseq, body)
+ else
+ status.cc_entries_index = -1
+ end
+ if compile_root_p
+ status.compile_info = rb_mjit_iseq_compile_info(body)
+ else
+ status.compile_info = Fiddle.malloc(C.rb_mjit_compile_info.sizeof)
+ status.compile_info.disable_ivar_cache = false
+ status.compile_info.disable_exivar_cache = false
+ status.compile_info.disable_send_cache = false
+ status.compile_info.disable_inlining = false
+ status.compile_info.disable_const_cache = false
+ end
+ end
+
+ def using_ivar?(body)
+ pos = 0
+ while pos < body.iseq_size
+ insn = INSNS.fetch(C.rb_vm_insn_decode(body.iseq_encoded[pos]))
+ case insn.name
+ when :getinstancevariable, :setinstancevariable
+ return true
+ end
+ pos += insn.len
+ end
+ return false
+ end
+
+ # Expand simple macro that doesn't require dynamic C code.
+ def expand_simple_macros(arg_expr)
+ arg_expr.dup.tap do |expr|
+ # For `leave`. We can't proceed next ISeq in the same JIT function.
+ expr.gsub!(/^(?<indent>\s*)RESTORE_REGS\(\);\n/) do
+ indent = Regexp.last_match[:indent]
+ <<-end.gsub(/^ {12}/, '')
+ #if OPT_CALL_THREADED_CODE
+ #{indent}rb_ec_thread_ptr(ec)->retval = val;
+ #{indent}return 0;
+ #else
+ #{indent}return val;
+ #endif
+ end
+ end
+ expr.gsub!(/^(?<indent>\s*)NEXT_INSN\(\);\n/) do
+ indent = Regexp.last_match[:indent]
+ <<-end.gsub(/^ {12}/, '')
+ #{indent}UNREACHABLE_RETURN(Qundef);
+ end
+ end
+ end
+ end
+
+ def to_cstr(expr)
+ expr.gsub(/^(?!#)/, ' ') # indent everything but preprocessor lines
+ end
+
+ # Interpret unsigned long as signed long (VALUE -> OFFSET)
+ def cast_offset(offset)
+ if offset >= 1 << 8 * Fiddle::SIZEOF_VOIDP - 1 # negative
+ offset -= 1 << 8 * Fiddle::SIZEOF_VOIDP
+ end
+ offset
+ end
+
+ def captured_cc_entries(status)
+ status.compiled_iseq.mjit_unit.cc_entries + status.cc_entries_index
+ end
+
+ def call_data_index(cd, body)
+ cd - body.call_data
+ end
+
+ def vm_cc_cme(cc)
+ # TODO: add VM_ASSERT like actual vm_cc_cme
+ cc.cme_
+ end
+
+ def def_iseq_ptr(method_def)
+ C.rb_iseq_check(method_def.body.iseq.iseqptr)
+ end
+
+ def rb_mjit_iseq_compile_info(body)
+ body.mjit_unit.compile_info
+ end
+
+ def ISEQ_IS_SIZE(body)
+ body.ic_size + body.ivc_size + body.ise_size + body.icvarc_size
+ end
+
+ # Return true if an object of the class may be a special const (immediate).
+ # It's "maybe" because Integer and Float are not guaranteed to be an immediate.
+ # If this returns false, rb_class_of could be optimzied to RBASIC_CLASS.
+ def maybe_special_const?(klass)
+ [
+ C.rb_cFalseClass,
+ C.rb_cNilClass,
+ C.rb_cTrueClass,
+ C.rb_cInteger,
+ C.rb_cSymbol,
+ C.rb_cFloat,
+ ].include?(klass)
+ end
+
+ def has_valid_method_type?(cc)
+ vm_cc_cme(cc) != nil
+ end
+
+ def already_compiled?(status, pos)
+ status.stack_size_for_pos[pos] != C.NOT_COMPILED_STACK_SIZE
+ end
+
+ # Return an iseq pointer if cc has inlinable iseq.
+ def rb_mjit_inlinable_iseq(ci, cc)
+ if has_valid_method_type?(cc) &&
+ C.vm_ci_flag(ci) & C.VM_CALL_TAILCALL == 0 && # inlining only non-tailcall path
+ vm_cc_cme(cc).def.type == C.VM_METHOD_TYPE_ISEQ &&
+ C.fastpath_applied_iseq_p(ci, cc, iseq = def_iseq_ptr(vm_cc_cme(cc).def)) &&
+ inlinable_iseq_p(iseq.body) # CC_SET_FASTPATH in vm_callee_setup_arg
+ return iseq
+ end
+ return nil
+ end
+
+ # Return true if the ISeq can be inlined without pushing a new control frame.
+ def inlinable_iseq_p(body)
+ # 1) If catch_except_p, caller frame should be preserved when callee catches an exception.
+ # Then we need to wrap `vm_exec()` but then we can't inline the call inside it.
+ #
+ # 2) If `body->catch_except_p` is false and `handles_sp?` of an insn is false,
+ # sp is not moved as we assume `status->local_stack_p = !body->catch_except_p`.
+ #
+ # 3) If `body->catch_except_p` is false and `always_leaf?` of an insn is true,
+ # pc is not moved.
+ if body.catch_except_p
+ return false
+ end
+
+ pos = 0
+ while pos < body.iseq_size
+ insn = INSNS.fetch(C.rb_vm_insn_decode(body.iseq_encoded[pos]))
+ # All insns in the ISeq except `leave` (to be overridden in the inlined code)
+ # should meet following strong assumptions:
+ # * Do not require `cfp->sp` motion
+ # * Do not move `cfp->pc`
+ # * Do not read any `cfp->pc`
+ if insn.name == :invokebuiltin || insn.name == :opt_invokebuiltin_delegate || insn.name == :opt_invokebuiltin_delegate_leave
+ # builtin insn's inlinability is handled by `Primitive.attr! 'inline'` per iseq
+ if !body.builtin_inline_p
+ return false;
+ end
+ elsif insn.name != :leave && C.insn_may_depend_on_sp_or_pc(insn.bin, body.iseq_encoded + (pos + 1))
+ return false
+ end
+ # At this moment, `cfp->ep` in an inlined method is not working.
+ case insn.name
+ when :getlocal,
+ :getlocal_WC_0,
+ :getlocal_WC_1,
+ :setlocal,
+ :setlocal_WC_0,
+ :setlocal_WC_1,
+ :getblockparam,
+ :getblockparamproxy,
+ :setblockparam
+ return false
+ end
+ pos += insn.len
+ end
+ return true
+ end
+
+ # CPointer::Struct could be nil on field reference, and this is a helper to
+ # handle that case while using CPointer::Struct#to_s in most cases.
+ # @param struct [RubyVM::MJIT::CPointer::Struct]
+ def to_addr(struct)
+ struct&.to_s || 'NULL'
+ end
+
+ def parent_shape_id(shape_id)
+ return shape_id if shape_id == C.INVALID_SHAPE_ID
+
+ parent_id = C.rb_shape_get_shape_by_id(shape_id).parent_id
+ parent = C.rb_shape_get_shape_by_id(parent_id)
+
+ if parent.type == C.SHAPE_CAPACITY_CHANGE
+ parent.parent_id
+ else
+ parent_id
+ end
+ end
+end
diff --git a/lib/ruby_vm/mjit/hooks.rb b/lib/ruby_vm/mjit/hooks.rb
new file mode 100644
index 0000000000..3fb1004111
--- /dev/null
+++ b/lib/ruby_vm/mjit/hooks.rb
@@ -0,0 +1,32 @@
+module RubyVM::MJIT::Hooks # :nodoc: all
+ C = RubyVM::MJIT.const_get(:C, false)
+
+ def self.on_bop_redefined(_redefined_flag, _bop)
+ C.mjit_cancel_all("BOP is redefined")
+ end
+
+ def self.on_cme_invalidate(_cme)
+ # to be used later
+ end
+
+ def self.on_ractor_spawn
+ C.mjit_cancel_all("Ractor is spawned")
+ end
+
+ def self.on_constant_state_changed(_id)
+ # to be used later
+ end
+
+ def self.on_constant_ic_update(_iseq, _ic, _insn_idx)
+ # to be used later
+ end
+
+ def self.on_tracing_invalidate_all(new_iseq_events)
+ # Stop calling all JIT-ed code. We can't rewrite existing JIT-ed code to trace_ insns for now.
+ # :class events are triggered only in ISEQ_TYPE_CLASS, but mjit_target_iseq_p ignores such iseqs.
+ # Thus we don't need to cancel JIT-ed code for :class events.
+ if new_iseq_events != C.RUBY_EVENT_CLASS
+ C.mjit_cancel_all("TracePoint is enabled")
+ end
+ end
+end
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 13c23cda03..af86646a82 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -1,22 +1,23 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require 'rbconfig'
+require "rbconfig"
module Gem
- VERSION = "3.4.0.dev".freeze
+ VERSION = "3.4.19"
end
# Must be first since it unloads the prelude from 1.9.2
-require_relative 'rubygems/compatibility'
+require_relative "rubygems/compatibility"
-require_relative 'rubygems/defaults'
-require_relative 'rubygems/deprecate'
-require_relative 'rubygems/errors'
+require_relative "rubygems/defaults"
+require_relative "rubygems/deprecate"
+require_relative "rubygems/errors"
##
# RubyGems is the Ruby standard for publishing and managing third party
@@ -117,11 +118,7 @@ module Gem
# Taint support is deprecated in Ruby 2.7.
# This allows switching ".untaint" to ".tap(&Gem::UNTAINT)",
# to avoid deprecation warnings in Ruby 2.7.
- UNTAINT = RUBY_VERSION < '2.7' ? :untaint.to_sym : proc {}
-
- # When https://bugs.ruby-lang.org/issues/17259 is available, there is no need to override Kernel#warn
- KERNEL_WARN_IGNORES_INTERNAL_ENTRIES = RUBY_ENGINE == "truffleruby" ||
- (RUBY_ENGINE == "ruby" && RUBY_VERSION >= '3.0')
+ UNTAINT = RUBY_VERSION < "2.7" ? :untaint.to_sym : proc {}
##
# An Array of Regexps that match windows Ruby platforms.
@@ -185,6 +182,8 @@ module Gem
@default_source_date_epoch = nil
+ @discover_gems_on_require = true
+
##
# Try to activate a gem containing +path+. Returns true if
# activation succeeded or wasn't needed because it was already
@@ -293,14 +292,14 @@ module Gem
# The mode needed to read a file as straight binary.
def self.binary_mode
- 'rb'
+ "rb"
end
##
# The path where gem executables are to be installed.
def self.bindir(install_dir=Gem.dir)
- return File.join install_dir, 'bin' unless
+ return File.join install_dir, "bin" unless
install_dir.to_s == Gem.default_dir.to_s
Gem.default_bindir
end
@@ -309,7 +308,7 @@ module Gem
# The path were rubygems plugins are to be installed.
def self.plugindir(install_dir=Gem.dir)
- File.join install_dir, 'plugins'
+ File.join install_dir, "plugins"
end
##
@@ -353,7 +352,7 @@ module Gem
# A Zlib::Deflate.deflate wrapper
def self.deflate(data)
- require 'zlib'
+ require "zlib"
Zlib::Deflate.deflate data
end
@@ -375,7 +374,7 @@ module Gem
target = {}
env.each_pair do |k,v|
case k
- when 'GEM_HOME', 'GEM_PATH', 'GEM_SPEC_CACHE'
+ when "GEM_HOME", "GEM_PATH", "GEM_SPEC_CACHE"
case v
when nil, String
target[k] = v
@@ -450,7 +449,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
subdir = File.join dir, name
next if File.exist? subdir
- require 'fileutils'
+ require "fileutils"
begin
FileUtils.mkdir_p subdir, **options
@@ -466,7 +465,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# distinction as extensions cannot be shared between the two.
def self.extension_api_version # :nodoc:
- if 'no' == RbConfig::CONFIG['ENABLE_SHARED']
+ if "no" == RbConfig::CONFIG["ENABLE_SHARED"]
"#{ruby_api_version}-static"
else
ruby_api_version
@@ -576,7 +575,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
return i if path.instance_variable_defined?(:@gem_prelude_index)
end
- index = $LOAD_PATH.index RbConfig::CONFIG['sitelibdir']
+ index = $LOAD_PATH.index RbConfig::CONFIG["sitelibdir"]
index || 0
end
@@ -607,10 +606,10 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
def self.load_yaml
return if @yaml_loaded
- require 'psych'
- require_relative 'rubygems/psych_tree'
+ require "psych"
+ require_relative "rubygems/psych_tree"
- require_relative 'rubygems/safe_yaml'
+ require_relative "rubygems/safe_yaml"
@yaml_loaded = true
end
@@ -741,9 +740,9 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
def self.prefix
prefix = File.dirname RUBYGEMS_DIR
- if prefix != File.expand_path(RbConfig::CONFIG['sitelibdir']) and
- prefix != File.expand_path(RbConfig::CONFIG['libdir']) and
- 'lib' == File.basename(RUBYGEMS_DIR)
+ if prefix != File.expand_path(RbConfig::CONFIG["sitelibdir"]) &&
+ prefix != File.expand_path(RbConfig::CONFIG["libdir"]) &&
+ "lib" == File.basename(RUBYGEMS_DIR)
prefix
end
end
@@ -759,11 +758,11 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Safely read a file in binary mode on all platforms.
def self.read_binary(path)
- open_file(path, 'rb+') do |io|
+ open_file(path, "rb+") do |io|
io.read
end
rescue Errno::EACCES, Errno::EROFS
- open_file(path, 'rb') do |io|
+ open_file(path, "rb") do |io|
io.read
end
end
@@ -771,9 +770,13 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# Safely write a file in binary mode on all platforms.
def self.write_binary(path, data)
- open_file(path, 'wb') do |io|
+ open_file(path, "wb") do |io|
io.write data
end
+ rescue Errno::ENOSPC
+ # If we ran out of space but the file exists, it's *guaranteed* to be corrupted.
+ File.delete(path) if File.exist?(path)
+ raise
end
##
@@ -816,13 +819,13 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Returns a String containing the API compatibility version of Ruby
def self.ruby_api_version
- @ruby_api_version ||= RbConfig::CONFIG['ruby_version'].dup
+ @ruby_api_version ||= RbConfig::CONFIG["ruby_version"].dup
end
def self.env_requirement(gem_name)
@env_requirements_by_name ||= {}
@env_requirements_by_name[gem_name] ||= begin
- req = ENV["GEM_REQUIREMENT_#{gem_name.upcase}"] || '>= 0'.freeze
+ req = ENV["GEM_REQUIREMENT_#{gem_name.upcase}"] || ">= 0"
Gem::Requirement.create(req)
end
end
@@ -845,16 +848,15 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Returns the latest release version of RubyGems.
def self.latest_rubygems_version
- latest_version_for('rubygems-update') or
- raise "Can't find 'rubygems-update' in any repo. Check `gem source list`."
+ latest_version_for("rubygems-update") ||
+ raise("Can't find 'rubygems-update' in any repo. Check `gem source list`.")
end
##
# Returns the version of the latest release-version of gem +name+
def self.latest_version_for(name)
- spec = latest_spec_for name
- spec and spec.version
+ latest_spec_for(name)&.version
end
##
@@ -940,11 +942,11 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Suffixes for require-able paths.
def self.suffixes
- @suffixes ||= ['',
- '.rb',
+ @suffixes ||= ["",
+ ".rb",
*%w[DLEXT DLEXT2].map do |key|
val = RbConfig::CONFIG[key]
- next unless val and not val.empty?
+ next unless val && !val.empty?
".#{val}"
end,
].compact.uniq
@@ -970,7 +972,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Lazily loads DefaultUserInteraction and returns the default UI.
def self.ui
- require_relative 'rubygems/user_interaction'
+ require_relative "rubygems/user_interaction"
Gem::DefaultUserInteraction.ui
end
@@ -992,7 +994,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
def self.win_platform?
if @@win_platform.nil?
- ruby_platform = RbConfig::CONFIG['host_os']
+ ruby_platform = RbConfig::CONFIG["host_os"]
@@win_platform = !!WIN_PATTERNS.find {|r| ruby_platform =~ r }
end
@@ -1010,7 +1012,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Is this platform Solaris?
def self.solaris_platform?
- RUBY_PLATFORM =~ /solaris/
+ RUBY_PLATFORM.include?("solaris")
end
##
@@ -1071,7 +1073,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
def self.use_gemdeps(path = nil)
raise_exception = path
- path ||= ENV['RUBYGEMS_GEMDEPS']
+ path ||= ENV["RUBYGEMS_GEMDEPS"]
return unless path
path = path.dup
@@ -1096,7 +1098,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
end
ENV["BUNDLE_GEMFILE"] ||= File.expand_path(path)
- require_relative 'rubygems/user_interaction'
+ require_relative "rubygems/user_interaction"
require "bundler"
begin
Gem::DefaultUserInteraction.use_ui(ui) do
@@ -1163,9 +1165,17 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# RubyGems distributors (like operating system package managers) can
# disable RubyGems update by setting this to error message printed to
# end-users on gem update --system instead of actual update.
+
attr_accessor :disable_system_update_message
##
+ # Whether RubyGems should enhance builtin `require` to automatically
+ # check whether the path required is present in installed gems, and
+ # automatically activate them and add them to `$LOAD_PATH`.
+
+ attr_accessor :discover_gems_on_require
+
+ ##
# Hash of loaded Gem::Specification keyed by name
attr_reader :loaded_specs
@@ -1291,36 +1301,35 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# Location of Marshal quick gemspecs on remote repositories
- MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/".freeze
-
- autoload :BundlerVersionFinder, File.expand_path('rubygems/bundler_version_finder', __dir__)
- autoload :ConfigFile, File.expand_path('rubygems/config_file', __dir__)
- autoload :Dependency, File.expand_path('rubygems/dependency', __dir__)
- autoload :DependencyList, File.expand_path('rubygems/dependency_list', __dir__)
- autoload :Installer, File.expand_path('rubygems/installer', __dir__)
- autoload :Licenses, File.expand_path('rubygems/util/licenses', __dir__)
- autoload :NameTuple, File.expand_path('rubygems/name_tuple', __dir__)
- autoload :PathSupport, File.expand_path('rubygems/path_support', __dir__)
- autoload :RequestSet, File.expand_path('rubygems/request_set', __dir__)
- autoload :Requirement, File.expand_path('rubygems/requirement', __dir__)
- autoload :Resolver, File.expand_path('rubygems/resolver', __dir__)
- autoload :Source, File.expand_path('rubygems/source', __dir__)
- autoload :SourceList, File.expand_path('rubygems/source_list', __dir__)
- autoload :SpecFetcher, File.expand_path('rubygems/spec_fetcher', __dir__)
- autoload :SpecificationPolicy, File.expand_path('rubygems/specification_policy', __dir__)
- autoload :Util, File.expand_path('rubygems/util', __dir__)
- autoload :Version, File.expand_path('rubygems/version', __dir__)
+ MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/"
+
+ autoload :ConfigFile, File.expand_path("rubygems/config_file", __dir__)
+ autoload :Dependency, File.expand_path("rubygems/dependency", __dir__)
+ autoload :DependencyList, File.expand_path("rubygems/dependency_list", __dir__)
+ autoload :Installer, File.expand_path("rubygems/installer", __dir__)
+ autoload :Licenses, File.expand_path("rubygems/util/licenses", __dir__)
+ autoload :NameTuple, File.expand_path("rubygems/name_tuple", __dir__)
+ autoload :PathSupport, File.expand_path("rubygems/path_support", __dir__)
+ autoload :RequestSet, File.expand_path("rubygems/request_set", __dir__)
+ autoload :Requirement, File.expand_path("rubygems/requirement", __dir__)
+ autoload :Resolver, File.expand_path("rubygems/resolver", __dir__)
+ autoload :Source, File.expand_path("rubygems/source", __dir__)
+ autoload :SourceList, File.expand_path("rubygems/source_list", __dir__)
+ autoload :SpecFetcher, File.expand_path("rubygems/spec_fetcher", __dir__)
+ autoload :SpecificationPolicy, File.expand_path("rubygems/specification_policy", __dir__)
+ autoload :Util, File.expand_path("rubygems/util", __dir__)
+ autoload :Version, File.expand_path("rubygems/version", __dir__)
end
-require_relative 'rubygems/exceptions'
-require_relative 'rubygems/specification'
+require_relative "rubygems/exceptions"
+require_relative "rubygems/specification"
# REFACTOR: This should be pulled out into some kind of hacks file.
begin
##
# Defaults the operating system (or packager) wants to provide for RubyGems.
- require 'rubygems/defaults/operating_system'
+ require "rubygems/defaults/operating_system"
rescue LoadError
# Ignored
rescue StandardError => e
@@ -1345,6 +1354,17 @@ end
# Loads the default specs.
Gem::Specification.load_defaults
-require_relative 'rubygems/core_ext/kernel_gem'
-require_relative 'rubygems/core_ext/kernel_require'
-require_relative 'rubygems/core_ext/kernel_warn'
+require_relative "rubygems/core_ext/kernel_gem"
+
+path = File.join(__dir__, "rubygems/core_ext/kernel_require.rb")
+# When https://bugs.ruby-lang.org/issues/17259 is available, there is no need to override Kernel#warn
+if RUBY_ENGINE == "truffleruby" ||
+ (RUBY_ENGINE == "ruby" && RUBY_VERSION >= "3.0")
+ file = "<internal:#{path}>"
+else
+ require_relative "rubygems/core_ext/kernel_warn"
+ file = path
+end
+eval File.read(path), nil, file
+
+require ENV["BUNDLER_SETUP"] if ENV["BUNDLER_SETUP"] && !defined?(Bundler)
diff --git a/lib/rubygems/available_set.rb b/lib/rubygems/available_set.rb
index 499483d9e9..2ab3e137da 100644
--- a/lib/rubygems/available_set.rb
+++ b/lib/rubygems/available_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
class Gem::AvailableSet
include Enumerable
@@ -26,7 +27,7 @@ class Gem::AvailableSet
s = o.set
when Array
s = o.map do |sp,so|
- if !sp.kind_of?(Gem::Specification) or !so.kind_of?(Gem::Source)
+ if !sp.kind_of?(Gem::Specification) || !so.kind_of?(Gem::Source)
raise TypeError, "Array must be in [[spec, source], ...] form"
end
@@ -149,8 +150,8 @@ class Gem::AvailableSet
@set.reject! do |t|
# already locally installed
Gem::Specification.any? do |installed_spec|
- dep.name == installed_spec.name and
- dep.requirement.satisfied_by? installed_spec.version
+ dep.name == installed_spec.name &&
+ dep.requirement.satisfied_by?(installed_spec.version)
end
end
diff --git a/lib/rubygems/basic_specification.rb b/lib/rubygems/basic_specification.rb
index b3b63b51aa..5d198114e0 100644
--- a/lib/rubygems/basic_specification.rb
+++ b/lib/rubygems/basic_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# BasicSpecification is an abstract class which implements some common code
# used by both Specification and StubSpecification.
@@ -47,7 +48,7 @@ class Gem::BasicSpecification
# directory.
def gem_build_complete_path # :nodoc:
- File.join extension_dir, 'gem.build_complete'
+ File.join extension_dir, "gem.build_complete"
end
##
@@ -77,7 +78,7 @@ class Gem::BasicSpecification
if Gem::Platform::RUBY == platform || Gem::Platform.local === platform
warn "Ignoring #{full_name} because its extensions are not built. " +
- "Try: gem pristine #{name} --version #{version}"
+ "Try: gem pristine #{name} --version #{version}"
end
return false
@@ -103,7 +104,7 @@ class Gem::BasicSpecification
def extensions_dir
Gem.default_ext_dir_for(base_dir) ||
- File.join(base_dir, 'extensions', Gem::Platform.local.to_s,
+ File.join(base_dir, "extensions", Gem::Platform.local.to_s,
Gem.extension_api_version)
end
@@ -131,7 +132,7 @@ class Gem::BasicSpecification
# default Ruby platform.
def full_name
- if platform == Gem::Platform::RUBY or platform.nil?
+ if platform == Gem::Platform::RUBY || platform.nil?
"#{name}-#{version}".dup.tap(&Gem::UNTAINT)
else
"#{name}-#{version}-#{platform}".dup.tap(&Gem::UNTAINT)
diff --git a/lib/rubygems/bundler_version_finder.rb b/lib/rubygems/bundler_version_finder.rb
index f6fad0bd83..5b34227d3a 100644
--- a/lib/rubygems/bundler_version_finder.rb
+++ b/lib/rubygems/bundler_version_finder.rb
@@ -21,7 +21,7 @@ module Gem::BundlerVersionFinder
end
def self.bundle_update_bundler_version
- return unless File.basename($0) == "bundle".freeze
+ return unless File.basename($0) == "bundle"
return unless "update".start_with?(ARGV.first || " ")
bundler_version = nil
update_index = nil
@@ -47,7 +47,7 @@ module Gem::BundlerVersionFinder
def self.lockfile_contents
gemfile = ENV["BUNDLE_GEMFILE"]
- gemfile = nil if gemfile && gemfile.empty?
+ gemfile = nil if gemfile&.empty?
unless gemfile
begin
diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb
index f74cb419f9..07cf4e0e81 100644
--- a/lib/rubygems/command.rb
+++ b/lib/rubygems/command.rb
@@ -1,13 +1,14 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative 'optparse'
-require_relative 'requirement'
-require_relative 'user_interaction'
+require_relative "optparse"
+require_relative "requirement"
+require_relative "user_interaction"
##
# Base class for all Gem commands. When creating a new gem command, define
@@ -76,7 +77,7 @@ class Gem::Command
when Array
@extra_args = value
when String
- @extra_args = value.split(' ')
+ @extra_args = value.split(" ")
end
end
@@ -159,11 +160,11 @@ class Gem::Command
gem = "'#{gem_name}' (#{version})"
msg = String.new "Could not find a valid gem #{gem}"
- if errors and !errors.empty?
+ if errors && !errors.empty?
msg << ", here is why:\n"
errors.each {|x| msg << " #{x.wordy}\n" }
else
- if required_by and gem != required_by
+ if required_by && gem != required_by
msg << " (required by #{required_by}) in any repository"
else
msg << " in any repository"
@@ -186,7 +187,7 @@ class Gem::Command
def get_all_gem_names
args = options[:args]
- if args.nil? or args.empty?
+ if args.nil? || args.empty?
raise Gem::CommandLineError,
"Please specify at least one gem name (e.g. gem build GEMNAME)"
end
@@ -201,11 +202,15 @@ class Gem::Command
# respectively.
def get_all_gem_names_and_versions
get_all_gem_names.map do |name|
- if /\A(.*):(#{Gem::Requirement::PATTERN_RAW})\z/ =~ name
- [$1, $2]
- else
- [name]
- end
+ extract_gem_name_and_version(name)
+ end
+ end
+
+ def extract_gem_name_and_version(name) # :nodoc:
+ if /\A(.*):(#{Gem::Requirement::PATTERN_RAW})\z/ =~ name
+ [$1, $2]
+ else
+ [name]
end
end
@@ -216,7 +221,7 @@ class Gem::Command
def get_one_gem_name
args = options[:args]
- if args.nil? or args.empty?
+ if args.nil? || args.empty?
raise Gem::CommandLineError,
"Please specify a gem name on the command line (e.g. gem build GEMNAME)"
end
@@ -554,9 +559,9 @@ class Gem::Command
end
def configure_options(header, option_list)
- return if option_list.nil? or option_list.empty?
+ return if option_list.nil? || option_list.empty?
- header = header.to_s.empty? ? '' : "#{header} "
+ header = header.to_s.empty? ? "" : "#{header} "
@parser.separator " #{header}Options:"
option_list.each do |args, handler|
@@ -565,7 +570,7 @@ class Gem::Command
end
end
- @parser.separator ''
+ @parser.separator ""
end
##
@@ -578,22 +583,22 @@ class Gem::Command
# ----------------------------------------------------------------
# Add the options common to all commands.
- add_common_option('-h', '--help',
- 'Get help on this command') do |value, options|
+ add_common_option("-h", "--help",
+ "Get help on this command") do |value, options|
options[:help] = true
end
- add_common_option('-V', '--[no-]verbose',
- 'Set the verbose level of output') do |value, options|
+ add_common_option("-V", "--[no-]verbose",
+ "Set the verbose level of output") do |value, options|
# Set us to "really verbose" so the progress meter works
- if Gem.configuration.verbose and value
+ if Gem.configuration.verbose && value
Gem.configuration.verbose = 1
else
Gem.configuration.verbose = value
end
end
- add_common_option('-q', '--quiet', 'Silence command progress meter') do |value, options|
+ add_common_option("-q", "--quiet", "Silence command progress meter") do |value, options|
Gem.configuration.verbose = false
end
@@ -606,31 +611,35 @@ class Gem::Command
# commands. Both options are actually handled before the other
# options get parsed.
- add_common_option('--config-file FILE',
- 'Use this config file instead of default') do
+ add_common_option("--config-file FILE",
+ "Use this config file instead of default") do
end
- add_common_option('--backtrace',
- 'Show stack backtrace on errors') do
+ add_common_option("--backtrace",
+ "Show stack backtrace on errors") do
end
- add_common_option('--debug',
- 'Turn on Ruby debugging') do
+ add_common_option("--debug",
+ "Turn on Ruby debugging") do
end
- add_common_option('--norc',
- 'Avoid loading any .gemrc file') do
+ add_common_option("--norc",
+ "Avoid loading any .gemrc file") do
end
# :stopdoc:
- HELP = <<-HELP.freeze
+ HELP = <<-HELP
RubyGems is a package manager for Ruby.
Usage:
gem -h/--help
gem -v/--version
- gem command [arguments...] [options...]
+ gem [global options...] command [arguments...] [options...]
+
+ Global options:
+ -C PATH run as if gem was started in <PATH>
+ instead of the current working directory
Examples:
gem install rake
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index e421f89884..b3913d465a 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -1,13 +1,14 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative 'command'
-require_relative 'user_interaction'
-require_relative 'text'
+require_relative "command"
+require_relative "user_interaction"
+require_relative "text"
##
# The command manager registers and installs all the individual sub-commands
@@ -43,6 +44,7 @@ class Gem::CommandManager
:contents,
:dependency,
:environment,
+ :exec,
:fetch,
:generate_index,
:help,
@@ -73,16 +75,16 @@ class Gem::CommandManager
].freeze
ALIAS_COMMANDS = {
- 'i' => 'install',
- 'login' => 'signin',
- 'logout' => 'signout',
+ "i" => "install",
+ "login" => "signin",
+ "logout" => "signout",
}.freeze
##
# Return the authoritative instance of the command manager.
def self.instance
- @command_manager ||= new
+ @instance ||= new
end
##
@@ -97,14 +99,14 @@ class Gem::CommandManager
# Reset the authoritative instance of the command manager.
def self.reset
- @command_manager = nil
+ @instance = nil
end
##
# Register all the subcommands supported by the gem command.
def initialize
- require 'timeout'
+ require "timeout"
@commands = {}
BUILTIN_COMMANDS.each do |name|
@@ -169,20 +171,26 @@ class Gem::CommandManager
end
case args.first
- when '-h', '--help' then
+ when "-h", "--help" then
say Gem::Command::HELP
terminate_interaction 0
- when '-v', '--version' then
+ when "-v", "--version" then
say Gem::VERSION
terminate_interaction 0
+ when "-C" then
+ args.shift
+ start_point = args.shift
+ if Dir.exist?(start_point)
+ Dir.chdir(start_point) { invoke_command(args, build_args) }
+ else
+ alert_error clean_text("#{start_point} isn't a directory.")
+ terminate_interaction 1
+ end
when /^-/ then
alert_error clean_text("Invalid option: #{args.first}. See 'gem --help'.")
terminate_interaction 1
else
- cmd_name = args.shift.downcase
- cmd = find_command cmd_name
- cmd.deprecation_warning if cmd.deprecated?
- cmd.invoke_with_build_args args, build_args
+ invoke_command(args, build_args)
end
end
@@ -237,4 +245,11 @@ class Gem::CommandManager
ui.backtrace e
end
end
+
+ def invoke_command(args, build_args)
+ cmd_name = args.shift.downcase
+ cmd = find_command cmd_name
+ cmd.deprecation_warning if cmd.deprecated?
+ cmd.invoke_with_build_args args, build_args
+ end
end
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb
index 6d1a057dfa..68e020d94a 100644
--- a/lib/rubygems/commands/build_command.rb
+++ b/lib/rubygems/commands/build_command.rb
@@ -1,31 +1,35 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../package'
-require_relative '../version_option'
+
+require_relative "../command"
+require_relative "../package"
+require_relative "../version_option"
class Gem::Commands::BuildCommand < Gem::Command
include Gem::VersionOption
def initialize
- super 'build', 'Build a gem from a gemspec'
+ super "build", "Build a gem from a gemspec"
add_platform_option
- add_option '--force', 'skip validation of the spec' do |value, options|
+ add_option "--force", "skip validation of the spec" do |value, options|
options[:force] = true
end
- add_option '--strict', 'consider warnings as errors when validating the spec' do |value, options|
+ add_option "--strict", "consider warnings as errors when validating the spec" do |value, options|
options[:strict] = true
end
- add_option '-o', '--output FILE', 'output gem with the given filename' do |value, options|
+ add_option "-o", "--output FILE", "output gem with the given filename" do |value, options|
options[:output] = value
end
- add_option '-C PATH', 'Run as if gem build was started in <PATH> instead of the current working directory.' do |value, options|
+ add_option "-C PATH", "Run as if gem build was started in <PATH> instead of the current working directory." do |value, options|
options[:build_path] = value
end
+ deprecate_option "-C",
+ version: "4.0",
+ extra_msg: "-C is a global flag now. Use `gem -C PATH build GEMSPEC_FILE [options]` instead"
end
def arguments # :nodoc:
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
index b59564d575..344f31fba9 100644
--- a/lib/rubygems/commands/cert_command.rb
+++ b/lib/rubygems/commands/cert_command.rb
@@ -1,69 +1,70 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../security'
+
+require_relative "../command"
+require_relative "../security"
class Gem::Commands::CertCommand < Gem::Command
def initialize
- super 'cert', 'Manage RubyGems certificates and signing settings',
+ super "cert", "Manage RubyGems certificates and signing settings",
:add => [], :remove => [], :list => [], :build => [], :sign => []
- add_option('-a', '--add CERT',
- 'Add a trusted certificate.') do |cert_file, options|
+ add_option("-a", "--add CERT",
+ "Add a trusted certificate.") do |cert_file, options|
options[:add] << open_cert(cert_file)
end
- add_option('-l', '--list [FILTER]',
- 'List trusted certificates where the',
- 'subject contains FILTER') do |filter, options|
- filter ||= ''
+ add_option("-l", "--list [FILTER]",
+ "List trusted certificates where the",
+ "subject contains FILTER") do |filter, options|
+ filter ||= ""
options[:list] << filter
end
- add_option('-r', '--remove FILTER',
- 'Remove trusted certificates where the',
- 'subject contains FILTER') do |filter, options|
+ add_option("-r", "--remove FILTER",
+ "Remove trusted certificates where the",
+ "subject contains FILTER") do |filter, options|
options[:remove] << filter
end
- add_option('-b', '--build EMAIL_ADDR',
- 'Build private key and self-signed',
- 'certificate for EMAIL_ADDR') do |email_address, options|
+ add_option("-b", "--build EMAIL_ADDR",
+ "Build private key and self-signed",
+ "certificate for EMAIL_ADDR") do |email_address, options|
options[:build] << email_address
end
- add_option('-C', '--certificate CERT',
- 'Signing certificate for --sign') do |cert_file, options|
+ add_option("-C", "--certificate CERT",
+ "Signing certificate for --sign") do |cert_file, options|
options[:issuer_cert] = open_cert(cert_file)
options[:issuer_cert_file] = cert_file
end
- add_option('-K', '--private-key KEY',
- 'Key for --sign or --build') do |key_file, options|
+ add_option("-K", "--private-key KEY",
+ "Key for --sign or --build") do |key_file, options|
options[:key] = open_private_key(key_file)
end
- add_option('-A', '--key-algorithm ALGORITHM',
- 'Select which key algorithm to use for --build') do |algorithm, options|
+ add_option("-A", "--key-algorithm ALGORITHM",
+ "Select which key algorithm to use for --build") do |algorithm, options|
options[:key_algorithm] = algorithm
end
- add_option('-s', '--sign CERT',
- 'Signs CERT with the key from -K',
- 'and the certificate from -C') do |cert_file, options|
+ add_option("-s", "--sign CERT",
+ "Signs CERT with the key from -K",
+ "and the certificate from -C") do |cert_file, options|
raise Gem::OptionParser::InvalidArgument, "#{cert_file}: does not exist" unless
File.file? cert_file
options[:sign] << cert_file
end
- add_option('-d', '--days NUMBER_OF_DAYS',
- 'Days before the certificate expires') do |days, options|
+ add_option("-d", "--days NUMBER_OF_DAYS",
+ "Days before the certificate expires") do |days, options|
options[:expiration_length_days] = days.to_i
end
- add_option('-R', '--re-sign',
- 'Re-signs the certificate from -C with the key from -K') do |resign, options|
+ add_option("-R", "--re-sign",
+ "Re-signs the certificate from -C with the key from -K") do |resign, options|
options[:resign] = resign
end
end
@@ -93,7 +94,7 @@ class Gem::Commands::CertCommand < Gem::Command
def open_private_key(key_file)
check_openssl
- passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
+ passphrase = ENV["GEM_PRIVATE_KEY_PASSPHRASE"]
key = OpenSSL::PKey.read File.read(key_file), passphrase
raise Gem::OptionParser::InvalidArgument,
"#{key_file}: private key not found" unless key.private?
@@ -152,7 +153,7 @@ class Gem::Commands::CertCommand < Gem::Command
def build_cert(email, key) # :nodoc:
expiration_length_days = options[:expiration_length_days] ||
- Gem.configuration.cert_expiration_length_days
+ Gem.configuration.cert_expiration_length_days
cert = Gem::Security.create_cert_email(
email,
@@ -166,10 +167,10 @@ class Gem::Commands::CertCommand < Gem::Command
def build_key # :nodoc:
return options[:key] if options[:key]
- passphrase = ask_for_password 'Passphrase for your Private Key:'
+ passphrase = ask_for_password "Passphrase for your Private Key:"
say "\n"
- passphrase_confirmation = ask_for_password 'Please repeat the passphrase for your Private Key:'
+ passphrase_confirmation = ask_for_password "Please repeat the passphrase for your Private Key:"
say "\n"
raise Gem::CommandLineError,
@@ -260,7 +261,7 @@ For further reading on signing gems see `ri Gem::Security`.
def load_default_key
key_file = File.join Gem.default_key_path
key = File.read key_file
- passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
+ passphrase = ENV["GEM_PRIVATE_KEY_PASSPHRASE"]
options[:key] = OpenSSL::PKey.read key, passphrase
rescue Errno::ENOENT
diff --git a/lib/rubygems/commands/check_command.rb b/lib/rubygems/commands/check_command.rb
index 3b6b97ae3b..b300b4f87a 100644
--- a/lib/rubygems/commands/check_command.rb
+++ b/lib/rubygems/commands/check_command.rb
@@ -1,44 +1,45 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../version_option'
-require_relative '../validator'
-require_relative '../doctor'
+
+require_relative "../command"
+require_relative "../version_option"
+require_relative "../validator"
+require_relative "../doctor"
class Gem::Commands::CheckCommand < Gem::Command
include Gem::VersionOption
def initialize
- super 'check', 'Check a gem repository for added or missing files',
+ super "check", "Check a gem repository for added or missing files",
:alien => true, :doctor => false, :dry_run => false, :gems => true
- add_option('-a', '--[no-]alien',
+ add_option("-a", "--[no-]alien",
'Report "unmanaged" or rogue files in the',
- 'gem repository') do |value, options|
+ "gem repository") do |value, options|
options[:alien] = value
end
- add_option('--[no-]doctor',
- 'Clean up uninstalled gems and broken',
- 'specifications') do |value, options|
+ add_option("--[no-]doctor",
+ "Clean up uninstalled gems and broken",
+ "specifications") do |value, options|
options[:doctor] = value
end
- add_option('--[no-]dry-run',
- 'Do not remove files, only report what',
- 'would be removed') do |value, options|
+ add_option("--[no-]dry-run",
+ "Do not remove files, only report what",
+ "would be removed") do |value, options|
options[:dry_run] = value
end
- add_option('--[no-]gems',
- 'Check installed gems for problems') do |value, options|
+ add_option("--[no-]gems",
+ "Check installed gems for problems") do |value, options|
options[:gems] = value
end
- add_version_option 'check'
+ add_version_option "check"
end
def check_gems
- say 'Checking gems...'
+ say "Checking gems..."
say
gems = get_all_gem_names rescue []
@@ -57,7 +58,7 @@ class Gem::Commands::CheckCommand < Gem::Command
end
def doctor
- say 'Checking for files from uninstalled gems...'
+ say "Checking for files from uninstalled gems..."
say
Gem.path.each do |gem_repo|
@@ -72,11 +73,11 @@ class Gem::Commands::CheckCommand < Gem::Command
end
def arguments # :nodoc:
- 'GEMNAME name of gem to check'
+ "GEMNAME name of gem to check"
end
def defaults_str # :nodoc:
- '--gems --alien'
+ "--gems --alien"
end
def description # :nodoc:
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
index dc181e4de0..ee5e1252b6 100644
--- a/lib/rubygems/commands/cleanup_command.rb
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -1,35 +1,36 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../dependency_list'
-require_relative '../uninstaller'
+
+require_relative "../command"
+require_relative "../dependency_list"
+require_relative "../uninstaller"
class Gem::Commands::CleanupCommand < Gem::Command
def initialize
- super 'cleanup',
- 'Clean up old versions of installed gems',
+ super "cleanup",
+ "Clean up old versions of installed gems",
:force => false, :install_dir => Gem.dir,
:check_dev => true
- add_option('-n', '-d', '--dry-run',
- 'Do not uninstall gems') do |value, options|
+ add_option("-n", "-d", "--dry-run",
+ "Do not uninstall gems") do |value, options|
options[:dryrun] = true
end
- add_option(:Deprecated, '--dryrun',
- 'Do not uninstall gems') do |value, options|
+ add_option(:Deprecated, "--dryrun",
+ "Do not uninstall gems") do |value, options|
options[:dryrun] = true
end
- deprecate_option('--dryrun', extra_msg: 'Use --dry-run instead')
+ deprecate_option("--dryrun", extra_msg: "Use --dry-run instead")
- add_option('-D', '--[no-]check-development',
- 'Check development dependencies while uninstalling',
- '(default: true)') do |value, options|
+ add_option("-D", "--[no-]check-development",
+ "Check development dependencies while uninstalling",
+ "(default: true)") do |value, options|
options[:check_dev] = value
end
- add_option('--[no-]user-install',
- 'Cleanup in user\'s home directory instead',
- 'of GEM_HOME.') do |value, options|
+ add_option("--[no-]user-install",
+ "Cleanup in user's home directory instead",
+ "of GEM_HOME.") do |value, options|
options[:user_install] = value
end
@@ -149,7 +150,7 @@ If no gems are named all gems in GEM_HOME are cleaned.
@primary_gems = {}
Gem::Specification.each do |spec|
- if @primary_gems[spec.name].nil? or
+ if @primary_gems[spec.name].nil? ||
@primary_gems[spec.name].version < spec.version
@primary_gems[spec.name] = spec
end
diff --git a/lib/rubygems/commands/contents_command.rb b/lib/rubygems/commands/contents_command.rb
index 716022c458..f378441ca4 100644
--- a/lib/rubygems/commands/contents_command.rb
+++ b/lib/rubygems/commands/contents_command.rb
@@ -1,39 +1,40 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../version_option'
+
+require_relative "../command"
+require_relative "../version_option"
class Gem::Commands::ContentsCommand < Gem::Command
include Gem::VersionOption
def initialize
- super 'contents', 'Display the contents of the installed gems',
+ super "contents", "Display the contents of the installed gems",
:specdirs => [], :lib_only => false, :prefix => true,
:show_install_dir => false
add_version_option
- add_option('--all',
+ add_option("--all",
"Contents for all gems") do |all, options|
options[:all] = all
end
- add_option('-s', '--spec-dir a,b,c', Array,
+ add_option("-s", "--spec-dir a,b,c", Array,
"Search for gems under specific paths") do |spec_dirs, options|
options[:specdirs] = spec_dirs
end
- add_option('-l', '--[no-]lib-only',
+ add_option("-l", "--[no-]lib-only",
"Only return files in the Gem's lib_dirs") do |lib_only, options|
options[:lib_only] = lib_only
end
- add_option('--[no-]prefix',
+ add_option("--[no-]prefix",
"Don't include installed path prefix") do |prefix, options|
options[:prefix] = prefix
end
- add_option('--[no-]show-install-dir',
- 'Show only the gem install dir') do |show, options|
+ add_option("--[no-]show-install-dir",
+ "Show only the gem install dir") do |show, options|
options[:show_install_dir] = show
end
@@ -77,7 +78,7 @@ prefix or only the files that are requireable.
gem_contents name
end
- terminate_interaction 1 unless found or names.length > 1
+ terminate_interaction 1 unless found || names.length > 1
end
end
@@ -105,11 +106,11 @@ prefix or only the files that are requireable.
case file
when /\A#{spec.bindir}\//
# $' is POSTMATCH
- [RbConfig::CONFIG['bindir'], $']
+ [RbConfig::CONFIG["bindir"], $']
when /\.so\z/
- [RbConfig::CONFIG['archdir'], file]
+ [RbConfig::CONFIG["archdir"], file]
else
- [RbConfig::CONFIG['rubylibdir'], file]
+ [RbConfig::CONFIG["rubylibdir"], file]
end
end
end
diff --git a/lib/rubygems/commands/dependency_command.rb b/lib/rubygems/commands/dependency_command.rb
index d2fed022fe..cef98e30d3 100644
--- a/lib/rubygems/commands/dependency_command.rb
+++ b/lib/rubygems/commands/dependency_command.rb
@@ -1,28 +1,29 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../local_remote_options'
-require_relative '../version_option'
+
+require_relative "../command"
+require_relative "../local_remote_options"
+require_relative "../version_option"
class Gem::Commands::DependencyCommand < Gem::Command
include Gem::LocalRemoteOptions
include Gem::VersionOption
def initialize
- super 'dependency',
- 'Show the dependencies of an installed gem',
+ super "dependency",
+ "Show the dependencies of an installed gem",
:version => Gem::Requirement.default, :domain => :local
add_version_option
add_platform_option
add_prerelease_option
- add_option('-R', '--[no-]reverse-dependencies',
- 'Include reverse dependencies in the output') do
+ add_option("-R", "--[no-]reverse-dependencies",
+ "Include reverse dependencies in the output") do
|value, options|
options[:reverse_dependencies] = value
end
- add_option('-p', '--pipe',
+ add_option("-p", "--pipe",
"Pipe Format (name --version ver)") do |value, options|
options[:pipe_format] = value
end
@@ -77,7 +78,7 @@ use with other commands.
name_matches = name_pattern ? name_pattern =~ spec.name : true
version_matches = requirement.satisfied_by?(spec.version)
- name_matches and version_matches
+ name_matches && version_matches
}.map(&:to_spec)
end
@@ -133,8 +134,8 @@ use with other commands.
end
def ensure_local_only_reverse_dependencies # :nodoc:
- if options[:reverse_dependencies] and remote? and not local?
- alert_error 'Only reverse dependencies for local gems are supported.'
+ if options[:reverse_dependencies] && remote? && !local?
+ alert_error "Only reverse dependencies for local gems are supported."
terminate_interaction 1
end
end
@@ -142,7 +143,7 @@ use with other commands.
def ensure_specs(specs) # :nodoc:
return unless specs.empty?
- patterns = options[:args].join ','
+ patterns = options[:args].join ","
say "No gems found matching #{patterns} (#{options[:version]})" if
Gem.configuration.verbose
@@ -151,10 +152,10 @@ use with other commands.
def print_dependencies(spec, level = 0) # :nodoc:
response = String.new
- response << ' ' * level + "Gem #{spec.full_name}\n"
+ response << " " * level + "Gem #{spec.full_name}\n"
unless spec.dependencies.empty?
spec.dependencies.sort_by {|dep| dep.name }.each do |dep|
- response << ' ' * level + " #{dep}\n"
+ response << " " * level + " #{dep}\n"
end
end
response
@@ -182,7 +183,7 @@ use with other commands.
sp.dependencies.each do |dep|
dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep
- if spec.name == dep.name and
+ if spec.name == dep.name &&
dep.requirement.satisfied_by?(spec.version)
result << [sp.full_name, dep]
end
@@ -197,7 +198,7 @@ use with other commands.
def name_pattern(args)
return if args.empty?
- if args.length == 1 and args.first =~ /\A(.*)(i)?\z/m
+ if args.length == 1 && args.first =~ /\A(.*)(i)?\z/m
flags = $2 ? Regexp::IGNORECASE : nil
Regexp.new $1, flags
else
diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb
index b6b3d3812c..28dbf93c50 100644
--- a/lib/rubygems/commands/environment_command.rb
+++ b/lib/rubygems/commands/environment_command.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
-require_relative '../command'
+
+require_relative "../command"
class Gem::Commands::EnvironmentCommand < Gem::Command
def initialize
- super 'environment', 'Display information about the RubyGems environment'
+ super "environment", "Display information about the RubyGems environment"
end
def arguments # :nodoc:
@@ -16,7 +17,7 @@ class Gem::Commands::EnvironmentCommand < Gem::Command
platform display the supported gem platforms
<omitted> display everything
EOF
- return args.gsub(/^\s+/, '')
+ return args.gsub(/^\s+/, "")
end
def description # :nodoc:
@@ -141,7 +142,7 @@ lib/rubygems/defaults/operating_system.rb
out << " - GEM CONFIGURATION:\n"
Gem.configuration.each do |name, value|
- value = value.gsub(/./, '*') if name == 'gemcutter_key'
+ value = value.gsub(/./, "*") if name == "gemcutter_key"
out << " - #{name.inspect} => #{value.inspect}\n"
end
@@ -152,7 +153,7 @@ lib/rubygems/defaults/operating_system.rb
out << " - SHELL PATH:\n"
- shell_path = ENV['PATH'].split(File::PATH_SEPARATOR)
+ shell_path = ENV["PATH"].split(File::PATH_SEPARATOR)
add_path out, shell_path
out
diff --git a/lib/rubygems/commands/exec_command.rb b/lib/rubygems/commands/exec_command.rb
new file mode 100644
index 0000000000..383c8c5b37
--- /dev/null
+++ b/lib/rubygems/commands/exec_command.rb
@@ -0,0 +1,249 @@
+# frozen_string_literal: true
+
+require_relative "../command"
+require_relative "../dependency_installer"
+require_relative "../gem_runner"
+require_relative "../package"
+require_relative "../version_option"
+
+class Gem::Commands::ExecCommand < Gem::Command
+ include Gem::VersionOption
+
+ def initialize
+ super "exec", "Run a command from a gem", {
+ version: Gem::Requirement.default,
+ }
+
+ add_version_option
+ add_prerelease_option "to be installed"
+
+ add_option "-g", "--gem GEM", "run the executable from the given gem" do |value, options|
+ options[:gem_name] = value
+ end
+
+ add_option(:"Install/Update", "--conservative",
+ "Prefer the most recent installed version, ",
+ "rather than the latest version overall") do |value, options|
+ options[:conservative] = true
+ end
+ end
+
+ def arguments # :nodoc:
+ "COMMAND the executable command to run"
+ end
+
+ def defaults_str # :nodoc:
+ "--version '#{Gem::Requirement.default}'"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The exec command handles installing (if necessary) and running an executable
+from a gem, regardless of whether that gem is currently installed.
+
+The exec command can be thought of as a shortcut to running `gem install` and
+then the executable from the installed gem.
+
+For example, `gem exec rails new .` will run `rails new .` in the current
+directory, without having to manually run `gem install rails`.
+Additionally, the exec command ensures the most recent version of the gem
+is used (unless run with `--conservative`), and that the gem is not installed
+to the same gem path as user-installed gems.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [options --] COMMAND [args]"
+ end
+
+ def execute
+ gem_paths = { "GEM_HOME" => Gem.paths.home, "GEM_PATH" => Gem.paths.path.join(File::PATH_SEPARATOR), "GEM_SPEC_CACHE" => Gem.paths.spec_cache_dir }.compact
+
+ check_executable
+
+ print_command
+ if options[:gem_name] == "gem" && options[:executable] == "gem"
+ set_gem_exec_install_paths
+ Gem::GemRunner.new.run options[:args]
+ return
+ elsif options[:conservative]
+ install_if_needed
+ else
+ install
+ activate!
+ end
+
+ load!
+ ensure
+ ENV.update(gem_paths) if gem_paths
+ Gem.clear_paths
+ end
+
+ private
+
+ def handle_options(args)
+ args = add_extra_args(args)
+ check_deprecated_options(args)
+ @options = Marshal.load Marshal.dump @defaults # deep copy
+ parser.order!(args) do |v|
+ # put the non-option back at the front of the list of arguments
+ args.unshift(v)
+
+ # stop parsing once we hit the first non-option,
+ # so you can call `gem exec rails --version` and it prints the rails
+ # version rather than rubygem's
+ break
+ end
+ @options[:args] = args
+
+ options[:executable], gem_version = extract_gem_name_and_version(options[:args].shift)
+ options[:gem_name] ||= options[:executable]
+
+ if gem_version
+ if options[:version].none?
+ options[:version] = Gem::Requirement.new(gem_version)
+ else
+ options[:version].concat [gem_version]
+ end
+ end
+
+ if options[:prerelease] && !options[:version].prerelease?
+ if options[:version].none?
+ options[:version] = Gem::Requirement.default_prerelease
+ else
+ options[:version].concat [Gem::Requirement.default_prerelease]
+ end
+ end
+ end
+
+ def check_executable
+ if options[:executable].nil?
+ raise Gem::CommandLineError,
+ "Please specify an executable to run (e.g. #{program_name} COMMAND)"
+ end
+ end
+
+ def print_command
+ verbose "running #{program_name} with:\n"
+ opts = options.reject {|_, v| v.nil? || Array(v).empty? }
+ max_length = opts.map {|k, _| k.size }.max
+ opts.each do |k, v|
+ next if v.nil?
+ verbose "\t#{k.to_s.rjust(max_length)}: #{v}"
+ end
+ verbose ""
+ end
+
+ def install_if_needed
+ activate!
+ rescue Gem::MissingSpecError
+ verbose "#{Gem::Dependency.new(options[:gem_name], options[:version])} not available locally, installing from remote"
+ install
+ activate!
+ end
+
+ def set_gem_exec_install_paths
+ home = File.join(Gem.dir, "gem_exec")
+
+ ENV["GEM_PATH"] = ([home] + Gem.path).join(File::PATH_SEPARATOR)
+ ENV["GEM_HOME"] = home
+ Gem.clear_paths
+ end
+
+ def install
+ set_gem_exec_install_paths
+
+ gem_name = options[:gem_name]
+ gem_version = options[:version]
+
+ install_options = options.merge(
+ minimal_deps: false,
+ wrappers: true
+ )
+
+ suppress_always_install do
+ dep_installer = Gem::DependencyInstaller.new install_options
+
+ request_set = dep_installer.resolve_dependencies gem_name, gem_version
+
+ verbose "Gems to install:"
+ request_set.sorted_requests.each do |activation_request|
+ verbose "\t#{activation_request.full_name}"
+ end
+
+ request_set.install install_options
+ end
+
+ Gem::Specification.reset
+ rescue Gem::InstallError => e
+ alert_error "Error installing #{gem_name}:\n\t#{e.message}"
+ terminate_interaction 1
+ rescue Gem::GemNotFoundException => e
+ show_lookup_failure e.name, e.version, e.errors, false
+
+ terminate_interaction 2
+ rescue Gem::UnsatisfiableDependencyError => e
+ show_lookup_failure e.name, e.version, e.errors, false,
+ "'#{gem_name}' (#{gem_version})"
+
+ terminate_interaction 2
+ end
+
+ def activate!
+ gem(options[:gem_name], options[:version])
+ Gem.finish_resolve
+
+ verbose "activated #{options[:gem_name]} (#{Gem.loaded_specs[options[:gem_name]].version})"
+ end
+
+ def load!
+ argv = ARGV.clone
+ ARGV.replace options[:args]
+
+ exe = executable = options[:executable]
+
+ contains_executable = Gem.loaded_specs.values.select do |spec|
+ spec.executables.include?(executable)
+ end
+
+ if contains_executable.any? {|s| s.name == executable }
+ contains_executable.select! {|s| s.name == executable }
+ end
+
+ if contains_executable.empty?
+ if (spec = Gem.loaded_specs[executable]) && (exe = spec.executable)
+ contains_executable << spec
+ else
+ alert_error "Failed to load executable `#{executable}`," \
+ " are you sure the gem `#{options[:gem_name]}` contains it?"
+ terminate_interaction 1
+ end
+ end
+
+ if contains_executable.size > 1
+ alert_error "Ambiguous which gem `#{executable}` should come from: " \
+ "the options are #{contains_executable.map(&:name)}, " \
+ "specify one via `-g`"
+ terminate_interaction 1
+ end
+
+ load Gem.activate_bin_path(contains_executable.first.name, exe, ">= 0.a")
+ ensure
+ ARGV.replace argv
+ end
+
+ def suppress_always_install
+ name = :always_install
+ cls = ::Gem::Resolver::InstallerSet
+ method = cls.instance_method(name)
+ cls.remove_method(name)
+ cls.define_method(name) { [] }
+
+ begin
+ yield
+ ensure
+ cls.remove_method(name)
+ cls.define_method(name, method)
+ end
+ end
+end
diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb
index c8ecb0d48c..950d4fe30e 100644
--- a/lib/rubygems/commands/fetch_command.rb
+++ b/lib/rubygems/commands/fetch_command.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../local_remote_options'
-require_relative '../version_option'
+
+require_relative "../command"
+require_relative "../local_remote_options"
+require_relative "../version_option"
class Gem::Commands::FetchCommand < Gem::Command
include Gem::LocalRemoteOptions
@@ -10,10 +11,10 @@ class Gem::Commands::FetchCommand < Gem::Command
def initialize
defaults = {
:suggest_alternate => true,
- :version => Gem::Requirement.default,
+ :version => Gem::Requirement.default,
}
- super 'fetch', 'Download a gem and place it in the current directory', defaults
+ super "fetch", "Download a gem and place it in the current directory", defaults
add_bulk_threshold_option
add_proxy_option
@@ -24,13 +25,13 @@ class Gem::Commands::FetchCommand < Gem::Command
add_platform_option
add_prerelease_option
- add_option '--[no-]suggestions', 'Suggest alternates when gems are not found' do |value, options|
+ add_option "--[no-]suggestions", "Suggest alternates when gems are not found" do |value, options|
options[:suggest_alternate] = value
end
end
def arguments # :nodoc:
- 'GEMNAME name of gem to download'
+ "GEMNAME name of gem to download"
end
def defaults_str # :nodoc:
@@ -52,8 +53,8 @@ then repackaging it.
end
def check_version # :nodoc:
- if options[:version] != Gem::Requirement.default and
- get_all_gem_names.size > 1
+ if options[:version] != Gem::Requirement.default &&
+ get_all_gem_names.size > 1
alert_error "Can't use --version with multiple gems. You can specify multiple gems with" \
" version requirements using `gem fetch 'my_gem:1.0.0' 'my_other_gem:~>2.0.0'`"
terminate_interaction 1
diff --git a/lib/rubygems/commands/generate_index_command.rb b/lib/rubygems/commands/generate_index_command.rb
index 87200dab91..ce580cfaf9 100644
--- a/lib/rubygems/commands/generate_index_command.rb
+++ b/lib/rubygems/commands/generate_index_command.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../indexer'
+
+require_relative "../command"
+require_relative "../indexer"
##
# Generates a index files for use as a gem server.
@@ -9,27 +10,27 @@ require_relative '../indexer'
class Gem::Commands::GenerateIndexCommand < Gem::Command
def initialize
- super 'generate_index',
- 'Generates the index files for a gem server directory',
- :directory => '.', :build_modern => true
+ super "generate_index",
+ "Generates the index files for a gem server directory",
+ :directory => ".", :build_modern => true
- add_option '-d', '--directory=DIRNAME',
- 'repository base dir containing gems subdir' do |dir, options|
+ add_option "-d", "--directory=DIRNAME",
+ "repository base dir containing gems subdir" do |dir, options|
options[:directory] = File.expand_path dir
end
- add_option '--[no-]modern',
- 'Generate indexes for RubyGems',
- '(always true)' do |value, options|
+ add_option "--[no-]modern",
+ "Generate indexes for RubyGems",
+ "(always true)" do |value, options|
options[:build_modern] = value
end
- deprecate_option('--modern', version: '4.0', extra_msg: 'Modern indexes (specs, latest_specs, and prerelease_specs) are always generated, so this option is not needed.')
- deprecate_option('--no-modern', version: '4.0', extra_msg: 'The `--no-modern` option is currently ignored. Modern indexes (specs, latest_specs, and prerelease_specs) are always generated.')
+ deprecate_option("--modern", version: "4.0", extra_msg: "Modern indexes (specs, latest_specs, and prerelease_specs) are always generated, so this option is not needed.")
+ deprecate_option("--no-modern", version: "4.0", extra_msg: "The `--no-modern` option is currently ignored. Modern indexes (specs, latest_specs, and prerelease_specs) are always generated.")
- add_option '--update',
- 'Update modern indexes with gems added',
- 'since the last update' do |value, options|
+ add_option "--update",
+ "Update modern indexes with gems added",
+ "since the last update" do |value, options|
options[:update] = value
end
end
@@ -68,8 +69,8 @@ Marshal::MINOR_VERSION constants. It is used to ensure compatibility.
# This is always true because it's the only way now.
options[:build_modern] = true
- if not File.exist?(options[:directory]) or
- not File.directory?(options[:directory])
+ if !File.exist?(options[:directory]) ||
+ !File.directory?(options[:directory])
alert_error "unknown directory name #{options[:directory]}."
terminate_interaction 1
else
diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb
index 7f3383c9f3..20b99fa366 100644
--- a/lib/rubygems/commands/help_command.rb
+++ b/lib/rubygems/commands/help_command.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
-require_relative '../command'
+
+require_relative "../command"
class Gem::Commands::HelpCommand < Gem::Command
# :stopdoc:
- EXAMPLES = <<-EOF.freeze
+ EXAMPLES = <<-EOF
Some examples of 'gem' usage.
* Install 'rake', either from local directory or remote server:
@@ -52,7 +53,7 @@ Some examples of 'gem' usage.
gem update --system
EOF
- GEM_DEPENDENCIES = <<-EOF.freeze
+ GEM_DEPENDENCIES = <<-EOF
A gem dependencies file allows installation of a consistent set of gems across
multiple environments. The RubyGems implementation is designed to be
compatible with Bundler's Gemfile format. You can see additional
@@ -229,7 +230,7 @@ default. This may be overridden with the :development_group option:
EOF
- PLATFORMS = <<-'EOF'.freeze
+ PLATFORMS = <<-'EOF'
RubyGems platforms are composed of three parts, a CPU, an OS, and a
version. These values are taken from values in rbconfig.rb. You can view
your current platform by running `gem environment`.
@@ -280,7 +281,7 @@ platform.
# :startdoc:
def initialize
- super 'help', "Provide help on the 'gem' command"
+ super "help", "Provide help on the 'gem' command"
@command_manager = Gem::CommandManager.instance
end
@@ -326,7 +327,7 @@ platform.
desc_width = @command_manager.command_names.map {|n| n.size }.max + 4
summary_width = 80 - margin_width - desc_width
- wrap_indent = ' ' * (margin_width + desc_width)
+ wrap_indent = " " * (margin_width + desc_width)
format = "#{' ' * margin_width}%-#{desc_width}s%s"
@command_manager.command_names.each do |cmd_name|
diff --git a/lib/rubygems/commands/info_command.rb b/lib/rubygems/commands/info_command.rb
index 3f2dd4ae0b..ced7751ff5 100644
--- a/lib/rubygems/commands/info_command.rb
+++ b/lib/rubygems/commands/info_command.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../query_utils'
+require_relative "../command"
+require_relative "../query_utils"
class Gem::Commands::InfoCommand < Gem::Command
include Gem::QueryUtils
@@ -13,7 +13,7 @@ class Gem::Commands::InfoCommand < Gem::Command
add_query_options
- remove_option('-d')
+ remove_option("-d")
defaults[:details] = true
defaults[:exact] = true
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index 87563accb0..b0bc2908b4 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -1,10 +1,12 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../install_update_options'
-require_relative '../dependency_installer'
-require_relative '../local_remote_options'
-require_relative '../validator'
-require_relative '../version_option'
+
+require_relative "../command"
+require_relative "../install_update_options"
+require_relative "../dependency_installer"
+require_relative "../local_remote_options"
+require_relative "../validator"
+require_relative "../version_option"
+require_relative "../update_suggestion"
##
# Gem installer command line tool
@@ -17,19 +19,20 @@ class Gem::Commands::InstallCommand < Gem::Command
include Gem::VersionOption
include Gem::LocalRemoteOptions
include Gem::InstallUpdateOptions
+ include Gem::UpdateSuggestion
def initialize
defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
:format_executable => false,
- :lock => true,
+ :lock => true,
:suggest_alternate => true,
- :version => Gem::Requirement.default,
- :without_groups => [],
+ :version => Gem::Requirement.default,
+ :without_groups => [],
})
defaults.merge!(install_update_options)
- super 'install', 'Install a gem into the local repository', defaults
+ super "install", "Install a gem into the local repository", defaults
add_install_update_options
add_local_remote_options
@@ -46,8 +49,8 @@ class Gem::Commands::InstallCommand < Gem::Command
def defaults_str # :nodoc:
"--both --version '#{Gem::Requirement.default}' --no-force\n" +
- "--install-dir #{Gem.dir} --lock\n" +
- install_update_defaults_str
+ "--install-dir #{Gem.dir} --lock\n" +
+ install_update_defaults_str
end
def description # :nodoc:
@@ -134,15 +137,15 @@ You can use `i` command instead of `install`.
end
def check_install_dir # :nodoc:
- if options[:install_dir] and options[:user_install]
+ if options[:install_dir] && options[:user_install]
alert_error "Use --install-dir or --user-install but not both"
terminate_interaction 1
end
end
def check_version # :nodoc:
- if options[:version] != Gem::Requirement.default and
- get_all_gem_names.size > 1
+ if options[:version] != Gem::Requirement.default &&
+ get_all_gem_names.size > 1
alert_error "Can't use --version with multiple gems. You can specify multiple gems with" \
" version requirements using `gem install 'my_gem:1.0.0' 'my_other_gem:~>2.0.0'`"
terminate_interaction 1
@@ -157,7 +160,7 @@ You can use `i` command instead of `install`.
@installed_specs = []
- ENV.delete 'GEM_PATH' if options[:install_dir].nil?
+ ENV.delete "GEM_PATH" if options[:install_dir].nil?
check_install_dir
check_version
@@ -168,11 +171,13 @@ You can use `i` command instead of `install`.
show_installed
+ say update_suggestion if eglible_for_update?
+
terminate_interaction exit_code
end
def install_from_gemdeps # :nodoc:
- require_relative '../request_set'
+ require_relative "../request_set"
rs = Gem::RequestSet.new
specs = rs.install_from_gemdeps options do |req, inst|
@@ -191,8 +196,8 @@ You can use `i` command instead of `install`.
end
def install_gem(name, version) # :nodoc:
- return if options[:conservative] and
- not Gem::Dependency.new(name, version).matching_specs.empty?
+ return if options[:conservative] &&
+ !Gem::Dependency.new(name, version).matching_specs.empty?
req = Gem::Requirement.create(version)
@@ -247,11 +252,11 @@ You can use `i` command instead of `install`.
def load_hooks # :nodoc:
if options[:install_as_default]
- require_relative '../install_default_message'
+ require_relative "../install_default_message"
else
- require_relative '../install_message'
+ require_relative "../install_message"
end
- require_relative '../rdoc'
+ require_relative "../rdoc"
end
def show_install_errors(errors) # :nodoc:
@@ -270,7 +275,7 @@ You can use `i` command instead of `install`.
def show_installed # :nodoc:
return if @installed_specs.empty?
- gems = @installed_specs.length == 1 ? 'gem' : 'gems'
+ gems = @installed_specs.length == 1 ? "gem" : "gems"
say "#{@installed_specs.length} #{gems} installed"
end
end
diff --git a/lib/rubygems/commands/list_command.rb b/lib/rubygems/commands/list_command.rb
index 010d968f9c..522c771f90 100644
--- a/lib/rubygems/commands/list_command.rb
+++ b/lib/rubygems/commands/list_command.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../query_utils'
+
+require_relative "../command"
+require_relative "../query_utils"
##
# Searches for gems starting with the supplied argument.
@@ -9,7 +10,7 @@ class Gem::Commands::ListCommand < Gem::Command
include Gem::QueryUtils
def initialize
- super 'list', 'Display local gems whose name matches REGEXP',
+ super "list", "Display local gems whose name matches REGEXP",
:domain => :local, :details => false, :versions => true,
:installed => nil, :version => Gem::Requirement.default
diff --git a/lib/rubygems/commands/lock_command.rb b/lib/rubygems/commands/lock_command.rb
index cb6229a2cb..3a9512fe3f 100644
--- a/lib/rubygems/commands/lock_command.rb
+++ b/lib/rubygems/commands/lock_command.rb
@@ -1,13 +1,14 @@
# frozen_string_literal: true
-require_relative '../command'
+
+require_relative "../command"
class Gem::Commands::LockCommand < Gem::Command
def initialize
- super 'lock', 'Generate a lockdown list of gems',
+ super "lock", "Generate a lockdown list of gems",
:strict => false
- add_option '-s', '--[no-]strict',
- 'fail if unable to satisfy a dependency' do |strict, options|
+ add_option "-s", "--[no-]strict",
+ "fail if unable to satisfy a dependency" do |strict, options|
options[:strict] = strict
end
end
diff --git a/lib/rubygems/commands/mirror_command.rb b/lib/rubygems/commands/mirror_command.rb
index 7daa47e2f0..b91a8db12d 100644
--- a/lib/rubygems/commands/mirror_command.rb
+++ b/lib/rubygems/commands/mirror_command.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
-require_relative '../command'
+
+require_relative "../command"
unless defined? Gem::Commands::MirrorCommand
class Gem::Commands::MirrorCommand < Gem::Command
def initialize
- super('mirror', 'Mirror all gem files (requires rubygems-mirror)')
+ super("mirror", "Mirror all gem files (requires rubygems-mirror)")
begin
- Gem::Specification.find_by_name('rubygems-mirror').activate
+ Gem::Specification.find_by_name("rubygems-mirror").activate
rescue Gem::LoadError
# no-op
end
diff --git a/lib/rubygems/commands/open_command.rb b/lib/rubygems/commands/open_command.rb
index 1e616fd68f..5a13074a1d 100644
--- a/lib/rubygems/commands/open_command.rb
+++ b/lib/rubygems/commands/open_command.rb
@@ -1,18 +1,19 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../version_option'
+
+require_relative "../command"
+require_relative "../version_option"
class Gem::Commands::OpenCommand < Gem::Command
include Gem::VersionOption
def initialize
- super 'open', 'Open gem sources in editor'
+ super "open", "Open gem sources in editor"
- add_option('-e', '--editor COMMAND', String,
+ add_option("-e", "--editor COMMAND", String,
"Prepends COMMAND to gem path. Could be used to specify editor.") do |command, options|
options[:editor] = command || get_env_editor
end
- add_option('-v', '--version VERSION', String,
+ add_option("-v", "--version VERSION", String,
"Opens specific gem version") do |version|
options[:version] = version
end
@@ -40,10 +41,10 @@ class Gem::Commands::OpenCommand < Gem::Command
end
def get_env_editor
- ENV['GEM_EDITOR'] ||
- ENV['VISUAL'] ||
- ENV['EDITOR'] ||
- 'vi'
+ ENV["GEM_EDITOR"] ||
+ ENV["VISUAL"] ||
+ ENV["EDITOR"] ||
+ "vi"
end
def execute
diff --git a/lib/rubygems/commands/outdated_command.rb b/lib/rubygems/commands/outdated_command.rb
index 162d338320..08a9221a26 100644
--- a/lib/rubygems/commands/outdated_command.rb
+++ b/lib/rubygems/commands/outdated_command.rb
@@ -1,15 +1,16 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../local_remote_options'
-require_relative '../spec_fetcher'
-require_relative '../version_option'
+
+require_relative "../command"
+require_relative "../local_remote_options"
+require_relative "../spec_fetcher"
+require_relative "../version_option"
class Gem::Commands::OutdatedCommand < Gem::Command
include Gem::LocalRemoteOptions
include Gem::VersionOption
def initialize
- super 'outdated', 'Display all gems that need updates'
+ super "outdated", "Display all gems that need updates"
add_local_remote_options
add_platform_option
diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb
index 42b0d79135..b51c9cf888 100644
--- a/lib/rubygems/commands/owner_command.rb
+++ b/lib/rubygems/commands/owner_command.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../local_remote_options'
-require_relative '../gemcutter_utilities'
-require_relative '../text'
+
+require_relative "../command"
+require_relative "../local_remote_options"
+require_relative "../gemcutter_utilities"
+require_relative "../text"
class Gem::Commands::OwnerCommand < Gem::Command
include Gem::Text
@@ -15,7 +16,7 @@ The owner command lets you add and remove owners of a gem on a push
server (the default is https://rubygems.org). Multiple owners can be
added or removed at the same time, if the flag is given multiple times.
-The supported user identifiers are dependant on the push server.
+The supported user identifiers are dependent on the push server.
For rubygems.org, both e-mail and handle are supported, even though the
user identifier field is called "email".
@@ -34,23 +35,23 @@ permission to.
end
def initialize
- super 'owner', 'Manage gem owners of a gem on the push server'
+ super "owner", "Manage gem owners of a gem on the push server"
add_proxy_option
add_key_option
add_otp_option
defaults.merge! :add => [], :remove => []
- add_option '-a', '--add NEW_OWNER', 'Add an owner by user identifier' do |value, options|
+ add_option "-a", "--add NEW_OWNER", "Add an owner by user identifier" do |value, options|
options[:add] << value
end
- add_option '-r', '--remove OLD_OWNER', 'Remove an owner by user identifier' do |value, options|
+ add_option "-r", "--remove OLD_OWNER", "Remove an owner by user identifier" do |value, options|
options[:remove] << value
end
- add_option '-h', '--host HOST',
- 'Use another gemcutter-compatible host',
- ' (e.g. https://rubygems.org)' do |value, options|
+ add_option "-h", "--host HOST",
+ "Use another gemcutter-compatible host",
+ " (e.g. https://rubygems.org)" do |value, options|
options[:host] = value
end
end
@@ -98,8 +99,10 @@ permission to.
action = method == :delete ? "Removing" : "Adding"
with_response response, "#{action} #{owner}"
- rescue
- # ignore
+ rescue Gem::WebauthnVerificationError => e
+ raise e
+ rescue StandardError
+ # ignore early exits to allow for completing the iteration of all owners
end
end
end
@@ -108,7 +111,7 @@ permission to.
def send_owner_request(method, name, owner)
rubygems_api_request method, "api/v1/gems/#{name}/owners", scope: get_owner_scope(method: method) do |request|
- request.set_form_data 'email' => owner
+ request.set_form_data "email" => owner
request.add_field "Authorization", api_key
end
end
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index 030c1bffce..64608a033f 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -1,67 +1,73 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../package'
-require_relative '../installer'
-require_relative '../version_option'
+
+require_relative "../command"
+require_relative "../package"
+require_relative "../installer"
+require_relative "../version_option"
class Gem::Commands::PristineCommand < Gem::Command
include Gem::VersionOption
def initialize
- super 'pristine',
- 'Restores installed gems to pristine condition from files located in the gem cache',
+ super "pristine",
+ "Restores installed gems to pristine condition from files located in the gem cache",
:version => Gem::Requirement.default,
:extensions => true,
:extensions_set => false,
:all => false
- add_option('--all',
- 'Restore all installed gems to pristine',
- 'condition') do |value, options|
+ add_option("--all",
+ "Restore all installed gems to pristine",
+ "condition") do |value, options|
options[:all] = value
end
- add_option('--skip=gem_name',
- 'used on --all, skip if name == gem_name') do |value, options|
+ add_option("--skip=gem_name",
+ "used on --all, skip if name == gem_name") do |value, options|
options[:skip] ||= []
options[:skip] << value
end
- add_option('--[no-]extensions',
- 'Restore gems with extensions',
- 'in addition to regular gems') do |value, options|
+ add_option("--[no-]extensions",
+ "Restore gems with extensions",
+ "in addition to regular gems") do |value, options|
options[:extensions_set] = true
options[:extensions] = value
end
- add_option('--only-executables',
- 'Only restore executables') do |value, options|
+ add_option("--only-missing-extensions",
+ "Only restore gems with missing extensions") do |value, options|
+ options[:only_missing_extensions] = value
+ end
+
+ add_option("--only-executables",
+ "Only restore executables") do |value, options|
options[:only_executables] = value
end
- add_option('--only-plugins',
- 'Only restore plugins') do |value, options|
+ add_option("--only-plugins",
+ "Only restore plugins") do |value, options|
options[:only_plugins] = value
end
- add_option('-E', '--[no-]env-shebang',
- 'Rewrite executables with a shebang',
- 'of /usr/bin/env') do |value, options|
+ add_option("-E", "--[no-]env-shebang",
+ "Rewrite executables with a shebang",
+ "of /usr/bin/env") do |value, options|
options[:env_shebang] = value
end
- add_option('-i', '--install-dir DIR',
- 'Gem repository to get binstubs and plugins installed') do |value, options|
+ add_option("-i", "--install-dir DIR",
+ "Gem repository to get binstubs and plugins installed") do |value, options|
options[:install_dir] = File.expand_path(value)
end
- add_option('-n', '--bindir DIR',
- 'Directory where executables are',
- 'located') do |value, options|
+ add_option("-n", "--bindir DIR",
+ "Directory where executables are",
+ "located") do |value, options|
options[:bin_dir] = File.expand_path(value)
end
- add_version_option('restore to', 'pristine condition')
+ add_version_option("restore to", "pristine condition")
end
def arguments # :nodoc:
@@ -69,7 +75,7 @@ class Gem::Commands::PristineCommand < Gem::Command
end
def defaults_str # :nodoc:
- '--extensions'
+ "--extensions"
end
def description # :nodoc:
@@ -102,10 +108,14 @@ extensions will be restored.
# `--extensions` must be explicitly given to pristine only gems
# with extensions.
- elsif options[:extensions_set] and
- options[:extensions] and options[:args].empty?
+ elsif options[:extensions_set] &&
+ options[:extensions] && options[:args].empty?
+ Gem::Specification.select do |spec|
+ spec.extensions && !spec.extensions.empty?
+ end
+ elsif options[:only_missing_extensions]
Gem::Specification.select do |spec|
- spec.extensions and not spec.extensions.empty?
+ spec.missing_extensions?
end
else
get_all_gem_names.sort.map do |gem_name|
@@ -135,15 +145,15 @@ extensions will be restored.
end
end
- unless spec.extensions.empty? or options[:extensions] or options[:only_executables] or options[:only_plugins]
+ unless spec.extensions.empty? || options[:extensions] || options[:only_executables] || options[:only_plugins]
say "Skipped #{spec.full_name}, it needs to compile an extension"
next
end
gem = spec.cache_file
- unless File.exist? gem or options[:only_executables] or options[:only_plugins]
- require_relative '../remote_fetcher'
+ unless File.exist?(gem) || options[:only_executables] || options[:only_plugins]
+ require_relative "../remote_fetcher"
say "Cached gem for #{spec.full_name} not found, attempting to fetch..."
@@ -163,8 +173,8 @@ extensions will be restored.
if options.include? :env_shebang
options[:env_shebang]
else
- install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS['install']
- install_defaults.to_s['--env-shebang']
+ install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS["install"]
+ install_defaults.to_s["--env-shebang"]
end
bin_dir = options[:bin_dir] if options[:bin_dir]
diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb
index 4d0d5a9f4b..79ca3d59b0 100644
--- a/lib/rubygems/commands/push_command.rb
+++ b/lib/rubygems/commands/push_command.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../local_remote_options'
-require_relative '../gemcutter_utilities'
-require_relative '../package'
+
+require_relative "../command"
+require_relative "../local_remote_options"
+require_relative "../gemcutter_utilities"
+require_relative "../package"
class Gem::Commands::PushCommand < Gem::Command
include Gem::LocalRemoteOptions
@@ -29,7 +30,7 @@ The push command will use ~/.gem/credentials to authenticate to a server, but yo
end
def initialize
- super 'push', 'Push a gem up to the gem server', :host => self.host
+ super "push", "Push a gem up to the gem server", :host => self.host
@user_defined_host = false
@@ -37,9 +38,9 @@ The push command will use ~/.gem/credentials to authenticate to a server, but yo
add_key_option
add_otp_option
- add_option('--host HOST',
- 'Push to another gemcutter-compatible host',
- ' (e.g. https://rubygems.org)') do |value, options|
+ add_option("--host HOST",
+ "Push to another gemcutter-compatible host",
+ " (e.g. https://rubygems.org)") do |value, options|
options[:host] = value
@user_defined_host = true
end
diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb
index 442c4b19bb..4fa201272e 100644
--- a/lib/rubygems/commands/query_command.rb
+++ b/lib/rubygems/commands/query_command.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../query_utils'
-require_relative '../deprecate'
+
+require_relative "../command"
+require_relative "../query_utils"
+require_relative "../deprecate"
class Gem::Commands::QueryCommand < Gem::Command
extend Gem::Deprecate
@@ -17,15 +18,15 @@ class Gem::Commands::QueryCommand < Gem::Command
alert_warning message unless Gem::Deprecate.skip
end
- def initialize(name = 'query',
- summary = 'Query gem information in local or remote repositories')
+ def initialize(name = "query",
+ summary = "Query gem information in local or remote repositories")
super name, summary,
:domain => :local, :details => false, :versions => true,
:installed => nil, :version => Gem::Requirement.default
- add_option('-n', '--name-matches REGEXP',
- 'Name of gem(s) to query on matches the',
- 'provided REGEXP') do |value, options|
+ add_option("-n", "--name-matches REGEXP",
+ "Name of gem(s) to query on matches the",
+ "provided REGEXP") do |value, options|
options[:name] = /#{value}/i
end
diff --git a/lib/rubygems/commands/rdoc_command.rb b/lib/rubygems/commands/rdoc_command.rb
index 7c5d6212f5..e318a52914 100644
--- a/lib/rubygems/commands/rdoc_command.rb
+++ b/lib/rubygems/commands/rdoc_command.rb
@@ -1,35 +1,36 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../version_option'
-require_relative '../rdoc'
-require 'fileutils'
+
+require_relative "../command"
+require_relative "../version_option"
+require_relative "../rdoc"
+require "fileutils"
class Gem::Commands::RdocCommand < Gem::Command
include Gem::VersionOption
def initialize
- super 'rdoc', 'Generates RDoc for pre-installed gems',
+ super "rdoc", "Generates RDoc for pre-installed gems",
:version => Gem::Requirement.default,
:include_rdoc => false, :include_ri => true, :overwrite => false
- add_option('--all',
- 'Generate RDoc/RI documentation for all',
- 'installed gems') do |value, options|
+ add_option("--all",
+ "Generate RDoc/RI documentation for all",
+ "installed gems") do |value, options|
options[:all] = value
end
- add_option('--[no-]rdoc',
- 'Generate RDoc HTML') do |value, options|
+ add_option("--[no-]rdoc",
+ "Generate RDoc HTML") do |value, options|
options[:include_rdoc] = value
end
- add_option('--[no-]ri',
- 'Generate RI data') do |value, options|
+ add_option("--[no-]ri",
+ "Generate RI data") do |value, options|
options[:include_ri] = value
end
- add_option('--[no-]overwrite',
- 'Overwrite installed documents') do |value, options|
+ add_option("--[no-]overwrite",
+ "Overwrite installed documents") do |value, options|
options[:overwrite] = value
end
@@ -69,7 +70,7 @@ Use --overwrite to force rebuilding of documentation.
end
if specs.empty?
- alert_error 'No matching gems found'
+ alert_error "No matching gems found"
terminate_interaction 1
end
@@ -79,15 +80,16 @@ Use --overwrite to force rebuilding of documentation.
doc.force = options[:overwrite]
if options[:overwrite]
- FileUtils.rm_rf File.join(spec.doc_dir, 'ri')
- FileUtils.rm_rf File.join(spec.doc_dir, 'rdoc')
+ FileUtils.rm_rf File.join(spec.doc_dir, "ri")
+ FileUtils.rm_rf File.join(spec.doc_dir, "rdoc")
end
begin
doc.generate
rescue Errno::ENOENT => e
- e.message =~ / - /
- alert_error "Unable to document #{spec.full_name}, #{$'} is missing, skipping"
+ match = / - /.match(e.message)
+ alert_error "Unable to document #{spec.full_name}, " \
+ " #{match.post_match} is missing, skipping"
terminate_interaction 1 if specs.length == 1
end
end
diff --git a/lib/rubygems/commands/search_command.rb b/lib/rubygems/commands/search_command.rb
index 75d99986f9..c7469e1fa8 100644
--- a/lib/rubygems/commands/search_command.rb
+++ b/lib/rubygems/commands/search_command.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../query_utils'
+
+require_relative "../command"
+require_relative "../query_utils"
class Gem::Commands::SearchCommand < Gem::Command
include Gem::QueryUtils
def initialize
- super 'search', 'Display remote gems whose name matches REGEXP',
+ super "search", "Display remote gems whose name matches REGEXP",
:domain => :remote, :details => false, :versions => true,
:installed => nil, :version => Gem::Requirement.default
diff --git a/lib/rubygems/commands/server_command.rb b/lib/rubygems/commands/server_command.rb
index f8cad3b5db..f1dde4aa02 100644
--- a/lib/rubygems/commands/server_command.rb
+++ b/lib/rubygems/commands/server_command.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
-require_relative '../command'
+
+require_relative "../command"
unless defined? Gem::Commands::ServerCommand
class Gem::Commands::ServerCommand < Gem::Command
def initialize
- super('server', 'Starts up a web server that hosts the RDoc (requires rubygems-server)')
+ super("server", "Starts up a web server that hosts the RDoc (requires rubygems-server)")
begin
- Gem::Specification.find_by_name('rubygems-server').activate
+ Gem::Specification.find_by_name("rubygems-server").activate
rescue Gem::LoadError
# no-op
end
diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb
index db2fefa65e..df1732b49e 100644
--- a/lib/rubygems/commands/setup_command.rb
+++ b/lib/rubygems/commands/setup_command.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-require_relative '../command'
+
+require_relative "../command"
##
# Installs RubyGems itself. This command is ordinarily only available from a
@@ -12,47 +13,47 @@ class Gem::Commands::SetupCommand < Gem::Command
ENV_PATHS = %w[/usr/bin/env /bin/env].freeze
def initialize
- super 'setup', 'Install RubyGems',
+ super "setup", "Install RubyGems",
:format_executable => false, :document => %w[ri],
:force => true,
- :site_or_vendor => 'sitelibdir',
- :destdir => '', :prefix => '', :previous_version => '',
+ :site_or_vendor => "sitelibdir",
+ :destdir => "", :prefix => "", :previous_version => "",
:regenerate_binstubs => true,
:regenerate_plugins => true
- add_option '--previous-version=VERSION',
- 'Previous version of RubyGems',
- 'Used for changelog processing' do |version, options|
+ add_option "--previous-version=VERSION",
+ "Previous version of RubyGems",
+ "Used for changelog processing" do |version, options|
options[:previous_version] = version
end
- add_option '--prefix=PREFIX',
- 'Prefix path for installing RubyGems',
- 'Will not affect gem repository location' do |prefix, options|
+ add_option "--prefix=PREFIX",
+ "Prefix path for installing RubyGems",
+ "Will not affect gem repository location" do |prefix, options|
options[:prefix] = File.expand_path prefix
end
- add_option '--destdir=DESTDIR',
- 'Root directory to install RubyGems into',
- 'Mainly used for packaging RubyGems' do |destdir, options|
+ add_option "--destdir=DESTDIR",
+ "Root directory to install RubyGems into",
+ "Mainly used for packaging RubyGems" do |destdir, options|
options[:destdir] = File.expand_path destdir
end
- add_option '--[no-]vendor',
- 'Install into vendorlibdir not sitelibdir' do |vendor, options|
- options[:site_or_vendor] = vendor ? 'vendorlibdir' : 'sitelibdir'
+ add_option "--[no-]vendor",
+ "Install into vendorlibdir not sitelibdir" do |vendor, options|
+ options[:site_or_vendor] = vendor ? "vendorlibdir" : "sitelibdir"
end
- add_option '--[no-]format-executable',
- 'Makes `gem` match ruby',
- 'If Ruby is ruby18, gem will be gem18' do |value, options|
+ add_option "--[no-]format-executable",
+ "Makes `gem` match ruby",
+ "If Ruby is ruby18, gem will be gem18" do |value, options|
options[:format_executable] = value
end
- add_option '--[no-]document [TYPES]', Array,
- 'Generate documentation for RubyGems',
- 'List the documentation types you wish to',
- 'generate. For example: rdoc,ri' do |value, options|
+ add_option "--[no-]document [TYPES]", Array,
+ "Generate documentation for RubyGems",
+ "List the documentation types you wish to",
+ "generate. For example: rdoc,ri" do |value, options|
options[:document] = case value
when nil then %w[rdoc ri]
when false then []
@@ -60,46 +61,46 @@ class Gem::Commands::SetupCommand < Gem::Command
end
end
- add_option '--[no-]rdoc',
- 'Generate RDoc documentation for RubyGems' do |value, options|
+ add_option "--[no-]rdoc",
+ "Generate RDoc documentation for RubyGems" do |value, options|
if value
- options[:document] << 'rdoc'
+ options[:document] << "rdoc"
else
- options[:document].delete 'rdoc'
+ options[:document].delete "rdoc"
end
options[:document].uniq!
end
- add_option '--[no-]ri',
- 'Generate RI documentation for RubyGems' do |value, options|
+ add_option "--[no-]ri",
+ "Generate RI documentation for RubyGems" do |value, options|
if value
- options[:document] << 'ri'
+ options[:document] << "ri"
else
- options[:document].delete 'ri'
+ options[:document].delete "ri"
end
options[:document].uniq!
end
- add_option '--[no-]regenerate-binstubs',
- 'Regenerate gem binstubs' do |value, options|
+ add_option "--[no-]regenerate-binstubs",
+ "Regenerate gem binstubs" do |value, options|
options[:regenerate_binstubs] = value
end
- add_option '--[no-]regenerate-plugins',
- 'Regenerate gem plugins' do |value, options|
+ add_option "--[no-]regenerate-plugins",
+ "Regenerate gem plugins" do |value, options|
options[:regenerate_plugins] = value
end
- add_option '-f', '--[no-]force',
- 'Forcefully overwrite binstubs' do |value, options|
+ add_option "-f", "--[no-]force",
+ "Forcefully overwrite binstubs" do |value, options|
options[:force] = value
end
- add_option('-E', '--[no-]env-shebang',
- 'Rewrite executables with a shebang',
- 'of /usr/bin/env') do |value, options|
+ add_option("-E", "--[no-]env-shebang",
+ "Rewrite executables with a shebang",
+ "of /usr/bin/env") do |value, options|
options[:env_shebang] = value
end
@@ -107,7 +108,7 @@ class Gem::Commands::SetupCommand < Gem::Command
end
def check_ruby_version
- required_version = Gem::Requirement.new '>= 2.3.0'
+ required_version = Gem::Requirement.new ">= 2.6.0"
unless required_version.satisfied_by? Gem.ruby_version
alert_error "Expected Ruby version #{required_version}, is #{Gem.ruby_version}"
@@ -149,7 +150,7 @@ By default, this RubyGems will install gem as:
check_ruby_version
- require 'fileutils'
+ require "fileutils"
if Gem.configuration.really_verbose
extend FileUtils::Verbose
else
@@ -194,7 +195,7 @@ By default, this RubyGems will install gem as:
end
if options[:previous_version].empty?
- options[:previous_version] = Gem::VERSION.sub(/[0-9]+$/, '0')
+ options[:previous_version] = Gem::VERSION.sub(/[0-9]+$/, "0")
end
options[:previous_version] = Gem::Version.new(options[:previous_version])
@@ -216,7 +217,7 @@ By default, this RubyGems will install gem as:
end
if documentation_success
- if options[:document].include? 'rdoc'
+ if options[:document].include? "rdoc"
say "Rdoc documentation was installed. You may now invoke:"
say " gem server"
say "and then peruse beautifully formatted documentation for your gems"
@@ -227,7 +228,7 @@ By default, this RubyGems will install gem as:
say
end
- if options[:document].include? 'ri'
+ if options[:document].include? "ri"
say "Ruby Interactive (ri) documentation was installed. ri is kind of like man "
say "pages for Ruby libraries. You may access it like this:"
say " ri Classname"
@@ -244,14 +245,14 @@ By default, this RubyGems will install gem as:
def install_executables(bin_dir)
prog_mode = options[:prog_mode] || 0755
- executables = { 'gem' => 'bin' }
+ executables = { "gem" => "exe" }
executables.each do |tool, path|
say "Installing #{tool} executable" if @verbose
Dir.chdir path do
bin_file = "gem"
- require 'tmpdir'
+ require "tmpdir"
dest_file = target_bin_path(bin_dir, bin_file)
bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}"
@@ -260,7 +261,7 @@ By default, this RubyGems will install gem as:
bin = File.readlines bin_file
bin[0] = shebang
- File.open bin_tmp_file, 'w' do |fp|
+ File.open bin_tmp_file, "w" do |fp|
fp.puts bin.join
end
@@ -275,7 +276,7 @@ By default, this RubyGems will install gem as:
begin
bin_cmd_file = File.join Dir.tmpdir, "#{bin_file}.bat"
- File.open bin_cmd_file, 'w' do |file|
+ File.open bin_cmd_file, "w" do |file|
file.puts <<-TEXT
@ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT
@@ -296,7 +297,7 @@ By default, this RubyGems will install gem as:
def shebang
if options[:env_shebang]
- ruby_name = RbConfig::CONFIG['ruby_install_name']
+ ruby_name = RbConfig::CONFIG["ruby_install_name"]
@env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path }
"#!#{@env_path} #{ruby_name}\n"
else
@@ -305,8 +306,8 @@ By default, this RubyGems will install gem as:
end
def install_lib(lib_dir)
- libs = { 'RubyGems' => 'lib' }
- libs['Bundler'] = 'bundler/lib'
+ libs = { "RubyGems" => "lib" }
+ libs["Bundler"] = "bundler/lib"
libs.each do |tool, path|
say "Installing #{tool}" if @verbose
@@ -319,7 +320,7 @@ By default, this RubyGems will install gem as:
end
def install_rdoc
- gem_doc_dir = File.join Gem.dir, 'doc'
+ gem_doc_dir = File.join Gem.dir, "doc"
rubygems_name = "rubygems-#{Gem::VERSION}"
rubygems_doc_dir = File.join gem_doc_dir, rubygems_name
@@ -329,23 +330,23 @@ By default, this RubyGems will install gem as:
# ignore
end
- if File.writable? gem_doc_dir and
- (not File.exist? rubygems_doc_dir or
- File.writable? rubygems_doc_dir)
+ if File.writable?(gem_doc_dir) &&
+ (!File.exist?(rubygems_doc_dir) ||
+ File.writable?(rubygems_doc_dir))
say "Removing old RubyGems RDoc and ri" if @verbose
- Dir[File.join(Gem.dir, 'doc', 'rubygems-[0-9]*')].each do |dir|
+ Dir[File.join(Gem.dir, "doc", "rubygems-[0-9]*")].each do |dir|
rm_rf dir
end
- require_relative '../rdoc'
+ require_relative "../rdoc"
- fake_spec = Gem::Specification.new 'rubygems', Gem::VERSION
+ fake_spec = Gem::Specification.new "rubygems", Gem::VERSION
def fake_spec.full_gem_path
- File.expand_path '../../..', __dir__
+ File.expand_path "../../..", __dir__
end
- generate_ri = options[:document].include? 'ri'
- generate_rdoc = options[:document].include? 'rdoc'
+ generate_ri = options[:document].include? "ri"
+ generate_rdoc = options[:document].include? "rdoc"
rdoc = Gem::RDoc.new fake_spec, generate_rdoc, generate_ri
rdoc.generate
@@ -397,7 +398,7 @@ By default, this RubyGems will install gem as:
cp File.join("bundler", bundler_spec.bindir, e), File.join(bundler_bin_dir, e)
end
- require_relative '../installer'
+ require_relative "../installer"
Dir.chdir("bundler") do
built_gem = Gem::Package.build(bundler_spec)
@@ -439,10 +440,10 @@ By default, this RubyGems will install gem as:
prefix = options[:prefix]
if prefix.empty?
- man_dir = RbConfig::CONFIG['mandir']
+ man_dir = RbConfig::CONFIG["mandir"]
return unless man_dir
else
- man_dir = File.join prefix, 'man'
+ man_dir = File.join prefix, "man"
end
prepend_destdir_if_present(man_dir)
@@ -454,10 +455,10 @@ By default, this RubyGems will install gem as:
if prefix.empty?
lib_dir = RbConfig::CONFIG[site_or_vendor]
- bin_dir = RbConfig::CONFIG['bindir']
+ bin_dir = RbConfig::CONFIG["bindir"]
else
- lib_dir = File.join prefix, 'lib'
- bin_dir = File.join prefix, 'bin'
+ lib_dir = File.join prefix, "lib"
+ bin_dir = File.join prefix, "bin"
end
[prepend_destdir_if_present(lib_dir), prepend_destdir_if_present(bin_dir)]
@@ -465,19 +466,19 @@ By default, this RubyGems will install gem as:
def files_in(dir)
Dir.chdir dir do
- Dir.glob(File.join('**', '*'), File::FNM_DOTMATCH).
+ Dir.glob(File.join("**", "*"), File::FNM_DOTMATCH).
select {|f| !File.directory?(f) }
end
end
def remove_old_bin_files(bin_dir)
old_bin_files = {
- 'gem_mirror' => 'gem mirror',
- 'gem_server' => 'gem server',
- 'gemlock' => 'gem lock',
- 'gemri' => 'ri',
- 'gemwhich' => 'gem which',
- 'index_gem_repository.rb' => 'gem generate_index',
+ "gem_mirror" => "gem mirror",
+ "gem_server" => "gem server",
+ "gemlock" => "gem lock",
+ "gemri" => "ri",
+ "gemwhich" => "gem which",
+ "index_gem_repository.rb" => "gem generate_index",
}
old_bin_files.each do |old_bin_file, new_name|
@@ -486,7 +487,7 @@ By default, this RubyGems will install gem as:
deprecation_message = "`#{old_bin_file}` has been deprecated. Use `#{new_name}` instead."
- File.open old_bin_path, 'w' do |fp|
+ File.open old_bin_path, "w" do |fp|
fp.write <<-EOF
#!#{Gem.ruby}
@@ -496,15 +497,15 @@ abort "#{deprecation_message}"
next unless Gem.win_platform?
- File.open "#{old_bin_path}.bat", 'w' do |fp|
+ File.open "#{old_bin_path}.bat", "w" do |fp|
fp.puts %(@ECHO.#{deprecation_message})
end
end
end
def remove_old_lib_files(lib_dir)
- lib_dirs = { File.join(lib_dir, 'rubygems') => 'lib/rubygems' }
- lib_dirs[File.join(lib_dir, 'bundler')] = 'bundler/lib/bundler'
+ lib_dirs = { File.join(lib_dir, "rubygems") => "lib/rubygems" }
+ lib_dirs[File.join(lib_dir, "bundler")] = "bundler/lib/bundler"
lib_dirs.each do |old_lib_dir, new_lib_dir|
lib_files = files_in(new_lib_dir)
@@ -512,11 +513,11 @@ abort "#{deprecation_message}"
to_remove = old_lib_files - lib_files
- gauntlet_rubygems = File.join(lib_dir, 'gauntlet_rubygems.rb')
+ gauntlet_rubygems = File.join(lib_dir, "gauntlet_rubygems.rb")
to_remove << gauntlet_rubygems if File.exist? gauntlet_rubygems
to_remove.delete_if do |file|
- file.start_with? 'defaults'
+ file.start_with? "defaults"
end
remove_file_list(to_remove, old_lib_dir)
@@ -542,7 +543,7 @@ abort "#{deprecation_message}"
end
def show_release_notes
- release_notes = File.join Dir.pwd, 'CHANGELOG.md'
+ release_notes = File.join Dir.pwd, "CHANGELOG.md"
release_notes =
if File.exist? release_notes
@@ -559,7 +560,7 @@ abort "#{deprecation_message}"
history_string = ""
- until versions.length == 0 or
+ until versions.length == 0 ||
versions.shift <= options[:previous_version] do
history_string += version_lines.shift + text.shift
end
@@ -573,10 +574,10 @@ abort "#{deprecation_message}"
end
def uninstall_old_gemcutter
- require_relative '../uninstaller'
+ require_relative "../uninstaller"
- ui = Gem::Uninstaller.new('gemcutter', :all => true, :ignore => true,
- :version => '< 0.4')
+ ui = Gem::Uninstaller.new("gemcutter", :all => true, :ignore => true,
+ :version => "< 0.4")
ui.uninstall
rescue Gem::InstallError
end
@@ -625,7 +626,7 @@ abort "#{deprecation_message}"
destdir = options[:destdir]
return path if destdir.empty?
- File.join(options[:destdir], path.gsub(/^[a-zA-Z]:/, ''))
+ File.join(options[:destdir], path.gsub(/^[a-zA-Z]:/, ""))
end
def install_file_list(files, dest_dir)
diff --git a/lib/rubygems/commands/signin_command.rb b/lib/rubygems/commands/signin_command.rb
index 23bb2f937f..0f77908c5b 100644
--- a/lib/rubygems/commands/signin_command.rb
+++ b/lib/rubygems/commands/signin_command.rb
@@ -1,15 +1,16 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../gemcutter_utilities'
+
+require_relative "../command"
+require_relative "../gemcutter_utilities"
class Gem::Commands::SigninCommand < Gem::Command
include Gem::GemcutterUtilities
def initialize
- super 'signin', 'Sign in to any gemcutter-compatible host. '\
- 'It defaults to https://rubygems.org'
+ super "signin", "Sign in to any gemcutter-compatible host. "\
+ "It defaults to https://rubygems.org"
- add_option('--host HOST', 'Push to another gemcutter-compatible host') do |value, options|
+ add_option("--host HOST", "Push to another gemcutter-compatible host") do |value, options|
options[:host] = value
end
@@ -17,10 +18,10 @@ class Gem::Commands::SigninCommand < Gem::Command
end
def description # :nodoc:
- 'The signin command executes host sign in for a push server (the default is'\
- ' https://rubygems.org). The host can be provided with the host flag or can'\
- ' be inferred from the provided gem. Host resolution matches the resolution'\
- ' strategy for the push command.'
+ "The signin command executes host sign in for a push server (the default is"\
+ " https://rubygems.org). The host can be provided with the host flag or can"\
+ " be inferred from the provided gem. Host resolution matches the resolution"\
+ " strategy for the push command."
end
def usage # :nodoc:
diff --git a/lib/rubygems/commands/signout_command.rb b/lib/rubygems/commands/signout_command.rb
index c9485e0c1b..bdd01e4393 100644
--- a/lib/rubygems/commands/signout_command.rb
+++ b/lib/rubygems/commands/signout_command.rb
@@ -1,14 +1,15 @@
# frozen_string_literal: true
-require_relative '../command'
+
+require_relative "../command"
class Gem::Commands::SignoutCommand < Gem::Command
def initialize
- super 'signout', 'Sign out from all the current sessions.'
+ super "signout", "Sign out from all the current sessions."
end
def description # :nodoc:
- 'The `signout` command is used to sign out from all current sessions,'\
- ' allowing you to sign in using a different set of credentials.'
+ "The `signout` command is used to sign out from all current sessions,"\
+ " allowing you to sign in using a different set of credentials."
end
def usage # :nodoc:
@@ -19,13 +20,13 @@ class Gem::Commands::SignoutCommand < Gem::Command
credentials_path = Gem.configuration.credentials_path
if !File.exist?(credentials_path)
- alert_error 'You are not currently signed in.'
+ alert_error "You are not currently signed in."
elsif !File.writable?(credentials_path)
alert_error "File '#{Gem.configuration.credentials_path}' is read-only."\
- ' Please make sure it is writable.'
+ " Please make sure it is writable."
else
Gem.configuration.unset_api_key!
- say 'You have successfully signed out from all sessions.'
+ say "You have successfully signed out from all sessions."
end
end
end
diff --git a/lib/rubygems/commands/sources_command.rb b/lib/rubygems/commands/sources_command.rb
index 884d9445c9..85cc68ac09 100644
--- a/lib/rubygems/commands/sources_command.rb
+++ b/lib/rubygems/commands/sources_command.rb
@@ -1,40 +1,41 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../remote_fetcher'
-require_relative '../spec_fetcher'
-require_relative '../local_remote_options'
+
+require_relative "../command"
+require_relative "../remote_fetcher"
+require_relative "../spec_fetcher"
+require_relative "../local_remote_options"
class Gem::Commands::SourcesCommand < Gem::Command
include Gem::LocalRemoteOptions
def initialize
- require 'fileutils'
+ require "fileutils"
- super 'sources',
- 'Manage the sources and cache file RubyGems uses to search for gems'
+ super "sources",
+ "Manage the sources and cache file RubyGems uses to search for gems"
- add_option '-a', '--add SOURCE_URI', 'Add source' do |value, options|
+ add_option "-a", "--add SOURCE_URI", "Add source" do |value, options|
options[:add] = value
end
- add_option '-l', '--list', 'List sources' do |value, options|
+ add_option "-l", "--list", "List sources" do |value, options|
options[:list] = value
end
- add_option '-r', '--remove SOURCE_URI', 'Remove source' do |value, options|
+ add_option "-r", "--remove SOURCE_URI", "Remove source" do |value, options|
options[:remove] = value
end
- add_option '-c', '--clear-all',
- 'Remove all sources (clear the cache)' do |value, options|
+ add_option "-c", "--clear-all",
+ "Remove all sources (clear the cache)" do |value, options|
options[:clear_all] = value
end
- add_option '-u', '--update', 'Update source cache' do |value, options|
+ add_option "-u", "--update", "Update source cache" do |value, options|
options[:update] = value
end
- add_option '-f', '--[no-]force', "Do not show any confirmation prompts and behave as if 'yes' was always answered" do |value, options|
+ add_option "-f", "--[no-]force", "Do not show any confirmation prompts and behave as if 'yes' was always answered" do |value, options|
options[:force] = value
end
@@ -82,8 +83,8 @@ Do you want to add this source?
def check_rubygems_https(source_uri) # :nodoc:
uri = URI source_uri
- if uri.scheme and uri.scheme.downcase == 'http' and
- uri.host.downcase == 'rubygems.org'
+ if uri.scheme && uri.scheme.downcase == "http" &&
+ uri.host.downcase == "rubygems.org"
question = <<-QUESTION.chomp
https://rubygems.org is recommended for security over #{uri}
@@ -112,7 +113,7 @@ Do you want to add this insecure source?
end
def defaults_str # :nodoc:
- '--list'
+ "--list"
end
def description # :nodoc:
@@ -215,9 +216,9 @@ To remove a source use the --remove argument:
def remove_cache_file(desc, path) # :nodoc:
FileUtils.rm_rf path
- if not File.exist?(path)
+ if !File.exist?(path)
say "*** Removed #{desc} source cache ***"
- elsif not File.writable?(path)
+ elsif !File.writable?(path)
say "*** Unable to remove #{desc} source cache (write protected) ***"
else
say "*** Unable to remove #{desc} source cache ***"
diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb
index 6fba3a36ec..ed158506e0 100644
--- a/lib/rubygems/commands/specification_command.rb
+++ b/lib/rubygems/commands/specification_command.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../local_remote_options'
-require_relative '../version_option'
-require_relative '../package'
+
+require_relative "../command"
+require_relative "../local_remote_options"
+require_relative "../version_option"
+require_relative "../package"
class Gem::Commands::SpecificationCommand < Gem::Command
include Gem::LocalRemoteOptions
@@ -11,28 +12,28 @@ class Gem::Commands::SpecificationCommand < Gem::Command
def initialize
Gem.load_yaml
- super 'specification', 'Display gem specification (in yaml)',
+ super "specification", "Display gem specification (in yaml)",
:domain => :local, :version => Gem::Requirement.default,
:format => :yaml
- add_version_option('examine')
+ add_version_option("examine")
add_platform_option
add_prerelease_option
- add_option('--all', 'Output specifications for all versions of',
- 'the gem') do |value, options|
+ add_option("--all", "Output specifications for all versions of",
+ "the gem") do |value, options|
options[:all] = true
end
- add_option('--ruby', 'Output ruby format') do |value, options|
+ add_option("--ruby", "Output ruby format") do |value, options|
options[:format] = :ruby
end
- add_option('--yaml', 'Output YAML format') do |value, options|
+ add_option("--yaml", "Output YAML format") do |value, options|
options[:format] = :yaml
end
- add_option('--marshal', 'Output Marshal format') do |value, options|
+ add_option("--marshal", "Output Marshal format") do |value, options|
options[:format] = :marshal
end
@@ -88,7 +89,7 @@ Specific fields in the specification can be extracted in YAML format:
raise Gem::CommandLineError, "Unsupported version type: '#{v}'"
end
- if !req.none? and options[:all]
+ if !req.none? && options[:all]
alert_error "Specify --all or -v, not both"
terminate_interaction 1
end
@@ -102,7 +103,7 @@ Specific fields in the specification can be extracted in YAML format:
field = get_one_optional_argument
raise Gem::CommandLineError, "--ruby and FIELD are mutually exclusive" if
- field and options[:format] == :ruby
+ field && options[:format] == :ruby
if local?
if File.exist? gem
diff --git a/lib/rubygems/commands/stale_command.rb b/lib/rubygems/commands/stale_command.rb
index 62a97966f1..a94d77c193 100644
--- a/lib/rubygems/commands/stale_command.rb
+++ b/lib/rubygems/commands/stale_command.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
-require_relative '../command'
+
+require_relative "../command"
class Gem::Commands::StaleCommand < Gem::Command
def initialize
- super('stale', 'List gems along with access times')
+ super("stale", "List gems along with access times")
end
def description # :nodoc:
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
index 467c8bf7ed..feba1c5b7c 100644
--- a/lib/rubygems/commands/uninstall_command.rb
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../version_option'
-require_relative '../uninstaller'
-require 'fileutils'
+
+require_relative "../command"
+require_relative "../version_option"
+require_relative "../uninstaller"
+require "fileutils"
##
# Gem uninstaller command line tool
@@ -13,78 +14,78 @@ class Gem::Commands::UninstallCommand < Gem::Command
include Gem::VersionOption
def initialize
- super 'uninstall', 'Uninstall gems from the local repository',
+ super "uninstall", "Uninstall gems from the local repository",
:version => Gem::Requirement.default, :user_install => true,
:check_dev => false, :vendor => false
- add_option('-a', '--[no-]all',
- 'Uninstall all matching versions'
+ add_option("-a", "--[no-]all",
+ "Uninstall all matching versions"
) do |value, options|
options[:all] = value
end
- add_option('-I', '--[no-]ignore-dependencies',
- 'Ignore dependency requirements while',
- 'uninstalling') do |value, options|
+ add_option("-I", "--[no-]ignore-dependencies",
+ "Ignore dependency requirements while",
+ "uninstalling") do |value, options|
options[:ignore] = value
end
- add_option('-D', '--[no-]check-development',
- 'Check development dependencies while uninstalling',
- '(default: false)') do |value, options|
+ add_option("-D", "--[no-]check-development",
+ "Check development dependencies while uninstalling",
+ "(default: false)") do |value, options|
options[:check_dev] = value
end
- add_option('-x', '--[no-]executables',
- 'Uninstall applicable executables without',
- 'confirmation') do |value, options|
+ add_option("-x", "--[no-]executables",
+ "Uninstall applicable executables without",
+ "confirmation") do |value, options|
options[:executables] = value
end
- add_option('-i', '--install-dir DIR',
- 'Directory to uninstall gem from') do |value, options|
+ add_option("-i", "--install-dir DIR",
+ "Directory to uninstall gem from") do |value, options|
options[:install_dir] = File.expand_path(value)
end
- add_option('-n', '--bindir DIR',
- 'Directory to remove executables from') do |value, options|
+ add_option("-n", "--bindir DIR",
+ "Directory to remove executables from") do |value, options|
options[:bin_dir] = File.expand_path(value)
end
- add_option('--[no-]user-install',
- 'Uninstall from user\'s home directory',
- 'in addition to GEM_HOME.') do |value, options|
+ add_option("--[no-]user-install",
+ "Uninstall from user's home directory",
+ "in addition to GEM_HOME.") do |value, options|
options[:user_install] = value
end
- add_option('--[no-]format-executable',
- 'Assume executable names match Ruby\'s prefix and suffix.') do |value, options|
+ add_option("--[no-]format-executable",
+ "Assume executable names match Ruby's prefix and suffix.") do |value, options|
options[:format_executable] = value
end
- add_option('--[no-]force',
- 'Uninstall all versions of the named gems',
- 'ignoring dependencies') do |value, options|
+ add_option("--[no-]force",
+ "Uninstall all versions of the named gems",
+ "ignoring dependencies") do |value, options|
options[:force] = value
end
- add_option('--[no-]abort-on-dependent',
- 'Prevent uninstalling gems that are',
- 'depended on by other gems.') do |value, options|
+ add_option("--[no-]abort-on-dependent",
+ "Prevent uninstalling gems that are",
+ "depended on by other gems.") do |value, options|
options[:abort_on_dependent] = value
end
add_version_option
add_platform_option
- add_option('--vendor',
- 'Uninstall gem from the vendor directory.',
- 'Only for use by gem repackagers.') do |value, options|
+ add_option("--vendor",
+ "Uninstall gem from the vendor directory.",
+ "Only for use by gem repackagers.") do |value, options|
unless Gem.vendor_dir
- raise Gem::OptionParser::InvalidOption.new 'your platform is not supported'
+ raise Gem::OptionParser::InvalidOption.new "your platform is not supported"
end
- alert_warning 'Use your OS package manager to uninstall vendor gems'
+ alert_warning "Use your OS package manager to uninstall vendor gems"
options[:vendor] = true
options[:install_dir] = Gem.vendor_dir
end
@@ -96,7 +97,7 @@ class Gem::Commands::UninstallCommand < Gem::Command
def defaults_str # :nodoc:
"--version '#{Gem::Requirement.default}' --no-force " +
- "--user-install"
+ "--user-install"
end
def description # :nodoc:
@@ -114,8 +115,8 @@ that is a dependency of an existing gem. You can use the
end
def check_version # :nodoc:
- if options[:version] != Gem::Requirement.default and
- get_all_gem_names.size > 1
+ if options[:version] != Gem::Requirement.default &&
+ get_all_gem_names.size > 1
alert_error "Can't use --version with multiple gems. You can specify multiple gems with" \
" version requirements using `gem uninstall 'my_gem:1.0.0' 'my_other_gem:~>2.0.0'`"
terminate_interaction 1
@@ -125,7 +126,10 @@ that is a dependency of an existing gem. You can use the
def execute
check_version
- if options[:all] and not options[:args].empty?
+ # Consider only gem specifications installed at `--install-dir`
+ Gem::Specification.dirs = options[:install_dir] if options[:install_dir]
+
+ if options[:all] && !options[:args].empty?
uninstall_specific
elsif options[:all]
uninstall_all
diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb
index 3f1708375f..1bff53429b 100644
--- a/lib/rubygems/commands/unpack_command.rb
+++ b/lib/rubygems/commands/unpack_command.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../version_option'
-require_relative '../security_option'
-require_relative '../remote_fetcher'
-require_relative '../package'
+
+require_relative "../command"
+require_relative "../version_option"
+require_relative "../security_option"
+require_relative "../remote_fetcher"
+require_relative "../package"
# forward-declare
@@ -17,18 +18,18 @@ class Gem::Commands::UnpackCommand < Gem::Command
include Gem::SecurityOption
def initialize
- require 'fileutils'
+ require "fileutils"
- super 'unpack', 'Unpack an installed gem to the current directory',
+ super "unpack", "Unpack an installed gem to the current directory",
:version => Gem::Requirement.default,
- :target => Dir.pwd
+ :target => Dir.pwd
- add_option('--target=DIR',
- 'target directory for unpacking') do |value, options|
+ add_option("--target=DIR",
+ "target directory for unpacking") do |value, options|
options[:target] = value
end
- add_option('--spec', 'unpack the gem specification') do |value, options|
+ add_option("--spec", "unpack the gem specification") do |value, options|
options[:spec] = true
end
@@ -103,11 +104,11 @@ command help for an example.
end
end
- File.open destination, 'w' do |io|
+ File.open destination, "w" do |io|
io.write metadata
end
else
- basename = File.basename path, '.gem'
+ basename = File.basename path, ".gem"
target_dir = File.expand_path basename, options[:target]
package = Gem::Package.new path, security_policy
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index 04ce0ea935..6f084d1c38 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -1,13 +1,14 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../command_manager'
-require_relative '../dependency_installer'
-require_relative '../install_update_options'
-require_relative '../local_remote_options'
-require_relative '../spec_fetcher'
-require_relative '../version_option'
-require_relative '../install_message' # must come before rdoc for messaging
-require_relative '../rdoc'
+
+require_relative "../command"
+require_relative "../command_manager"
+require_relative "../dependency_installer"
+require_relative "../install_update_options"
+require_relative "../local_remote_options"
+require_relative "../spec_fetcher"
+require_relative "../version_option"
+require_relative "../install_message" # must come before rdoc for messaging
+require_relative "../rdoc"
class Gem::Commands::UpdateCommand < Gem::Command
include Gem::InstallUpdateOptions
@@ -25,7 +26,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
options.merge!(install_update_options)
- super 'update', 'Update installed gems to the latest version', options
+ super "update", "Update installed gems to the latest version", options
add_install_update_options
@@ -35,8 +36,8 @@ class Gem::Commands::UpdateCommand < Gem::Command
value
end
- add_option('--system [VERSION]', Gem::Version,
- 'Update the RubyGems system software') do |value, options|
+ add_option("--system [VERSION]", Gem::Version,
+ "Update the RubyGems system software") do |value, options|
value = true unless value
options[:system] = value
@@ -56,7 +57,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
def defaults_str # :nodoc:
"--no-force --install-dir #{Gem.dir}\n" +
- install_update_defaults_str
+ install_update_defaults_str
end
def description # :nodoc:
@@ -155,7 +156,7 @@ command to remove old versions.
Gem::Specification.dirs = Gem.user_dir if options[:user_install]
Gem::Specification.each do |spec|
- if hig[spec.name].nil? or hig[spec.name].version < spec.version
+ if hig[spec.name].nil? || hig[spec.name].version < spec.version
hig[spec.name] = spec
end
end
@@ -176,13 +177,13 @@ command to remove old versions.
args = update_rubygems_arguments
version = spec.version
- update_dir = File.join spec.base_dir, 'gems', "rubygems-update-#{version}"
+ update_dir = File.join spec.base_dir, "gems", "rubygems-update-#{version}"
Dir.chdir update_dir do
say "Installing RubyGems #{version}" unless options[:silent]
installed = preparing_gem_layout_for(version) do
- system Gem.ruby, '--disable-gems', 'setup.rb', *args
+ system Gem.ruby, "--disable-gems", "setup.rb", *args
end
say "RubyGems system software updated" if installed unless options[:silent]
@@ -224,7 +225,7 @@ command to remove old versions.
requirement = Gem::Requirement.new ">= #{Gem::VERSION}"
rubygems_update = Gem::Specification.new
- rubygems_update.name = 'rubygems-update'
+ rubygems_update.name = "rubygems-update"
rubygems_update.version = version
highest_remote_tup = highest_remote_name_tuple(rubygems_update)
@@ -278,8 +279,8 @@ command to remove old versions.
check_oldest_rubygems version
- installed_gems = Gem::Specification.find_all_by_name 'rubygems-update', requirement
- installed_gems = update_gem('rubygems-update', version) if installed_gems.empty? || installed_gems.first.version != version
+ installed_gems = Gem::Specification.find_all_by_name "rubygems-update", requirement
+ installed_gems = update_gem("rubygems-update", version) if installed_gems.empty? || installed_gems.first.version != version
return if installed_gems.empty?
install_rubygems installed_gems.first
@@ -287,13 +288,13 @@ command to remove old versions.
def update_rubygems_arguments # :nodoc:
args = []
- args << '--silent' if options[:silent]
- args << '--prefix' << Gem.prefix if Gem.prefix
- args << '--no-document' unless options[:document].include?('rdoc') || options[:document].include?('ri')
- args << '--no-format-executable' if options[:no_format_executable]
- args << '--previous-version' << Gem::VERSION if
- options[:system] == true or
- Gem::Version.new(options[:system]) >= Gem::Version.new(2)
+ args << "--silent" if options[:silent]
+ args << "--prefix" << Gem.prefix if Gem.prefix
+ args << "--no-document" unless options[:document].include?("rdoc") || options[:document].include?("ri")
+ args << "--no-format-executable" if options[:no_format_executable]
+ args << "--previous-version" << Gem::VERSION if
+ options[:system] == true ||
+ Gem::Version.new(options[:system]) >= Gem::Version.new(2)
args
end
@@ -301,7 +302,7 @@ command to remove old versions.
result = []
highest_installed_gems.each do |l_name, l_spec|
- next if not gem_names.empty? and
+ next if !gem_names.empty? &&
gem_names.none? {|name| name == l_spec.name }
highest_remote_tup = highest_remote_name_tuple l_spec
@@ -329,14 +330,8 @@ command to remove old versions.
Gem::Version.new("3.2.3")
elsif Gem.ruby_version > Gem::Version.new("2.7.a")
Gem::Version.new("3.1.2")
- elsif Gem.ruby_version > Gem::Version.new("2.6.a")
- Gem::Version.new("3.0.1")
- elsif Gem.ruby_version > Gem::Version.new("2.5.a")
- Gem::Version.new("2.7.3")
- elsif Gem.ruby_version > Gem::Version.new("2.4.a")
- Gem::Version.new("2.6.8")
else
- Gem::Version.new("2.5.2")
+ Gem::Version.new("3.0.1")
end
end
end
diff --git a/lib/rubygems/commands/which_command.rb b/lib/rubygems/commands/which_command.rb
index 44e87a2b98..ec464d9672 100644
--- a/lib/rubygems/commands/which_command.rb
+++ b/lib/rubygems/commands/which_command.rb
@@ -1,17 +1,18 @@
# frozen_string_literal: true
-require_relative '../command'
+
+require_relative "../command"
class Gem::Commands::WhichCommand < Gem::Command
def initialize
- super 'which', 'Find the location of a library file you can require',
+ super "which", "Find the location of a library file you can require",
:search_gems_first => false, :show_all => false
- add_option '-a', '--[no-]all', 'show all matching files' do |show_all, options|
+ add_option "-a", "--[no-]all", "show all matching files" do |show_all, options|
options[:show_all] = show_all
end
- add_option '-g', '--[no-]gems-first',
- 'search gems before non-gems' do |gems_first, options|
+ add_option "-g", "--[no-]gems-first",
+ "search gems before non-gems" do |gems_first, options|
options[:search_gems_first] = gems_first
end
end
@@ -39,7 +40,7 @@ requiring to see why it does not behave as you expect.
found = true
options[:args].each do |arg|
- arg = arg.sub(/#{Regexp.union(*Gem.suffixes)}$/, '')
+ arg = arg.sub(/#{Regexp.union(*Gem.suffixes)}$/, "")
dirs = $LOAD_PATH
spec = Gem::Specification.find_by_path arg
@@ -71,7 +72,7 @@ requiring to see why it does not behave as you expect.
dirs.each do |dir|
Gem.suffixes.each do |ext|
full_path = File.join dir, "#{package_name}#{ext}"
- if File.exist? full_path and not File.directory? full_path
+ if File.exist?(full_path) && !File.directory?(full_path)
result << full_path
return result unless options[:show_all]
end
diff --git a/lib/rubygems/commands/yank_command.rb b/lib/rubygems/commands/yank_command.rb
index cad78aec5f..d344a020c3 100644
--- a/lib/rubygems/commands/yank_command.rb
+++ b/lib/rubygems/commands/yank_command.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
-require_relative '../command'
-require_relative '../local_remote_options'
-require_relative '../version_option'
-require_relative '../gemcutter_utilities'
+
+require_relative "../command"
+require_relative "../local_remote_options"
+require_relative "../version_option"
+require_relative "../gemcutter_utilities"
class Gem::Commands::YankCommand < Gem::Command
include Gem::LocalRemoteOptions
@@ -28,15 +29,15 @@ data you will need to change them immediately and yank your gem.
end
def initialize
- super 'yank', 'Remove a pushed gem from the index'
+ super "yank", "Remove a pushed gem from the index"
add_version_option("remove")
add_platform_option("remove")
add_otp_option
- add_option('--host HOST',
- 'Yank from another gemcutter-compatible host',
- ' (e.g. https://rubygems.org)') do |value, options|
+ add_option("--host HOST",
+ "Yank from another gemcutter-compatible host",
+ " (e.g. https://rubygems.org)") do |value, options|
options[:host] = value
end
@@ -76,10 +77,10 @@ data you will need to change them immediately and yank your gem.
request.add_field("Authorization", api_key)
data = {
- 'gem_name' => name,
- 'version' => version,
+ "gem_name" => name,
+ "version" => version,
}
- data['platform'] = platform if platform
+ data["platform"] = platform if platform
request.set_form_data data
end
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index 5dd2bfe88d..68c653269e 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative 'user_interaction'
-require 'rbconfig'
+require_relative "user_interaction"
+require "rbconfig"
##
# Gem::ConfigFile RubyGems options and gem command options from gemrc.
@@ -39,7 +40,7 @@ require 'rbconfig'
class Gem::ConfigFile
include Gem::UserInteraction
- DEFAULT_BACKTRACE = false
+ DEFAULT_BACKTRACE = true
DEFAULT_BULK_THRESHOLD = 1000
DEFAULT_VERBOSITY = true
DEFAULT_UPDATE_SOURCES = true
@@ -71,7 +72,7 @@ class Gem::ConfigFile
# :startdoc:
- SYSTEM_WIDE_CONFIG_FILE = File.join SYSTEM_CONFIG_PATH, 'gemrc'
+ SYSTEM_WIDE_CONFIG_FILE = File.join SYSTEM_CONFIG_PATH, "gemrc"
##
# List of arguments supplied to the config file object.
@@ -182,20 +183,20 @@ class Gem::ConfigFile
@update_sources = DEFAULT_UPDATE_SOURCES
@concurrent_downloads = DEFAULT_CONCURRENT_DOWNLOADS
@cert_expiration_length_days = DEFAULT_CERT_EXPIRATION_LENGTH_DAYS
- @ipv4_fallback_enabled = ENV['IPV4_FALLBACK_ENABLED'] == 'true' || DEFAULT_IPV4_FALLBACK_ENABLED
+ @ipv4_fallback_enabled = ENV["IPV4_FALLBACK_ENABLED"] == "true" || DEFAULT_IPV4_FALLBACK_ENABLED
operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS)
platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
system_config = load_file SYSTEM_WIDE_CONFIG_FILE
user_config = load_file config_file_name.dup.tap(&Gem::UNTAINT)
- environment_config = (ENV['GEMRC'] || '')
+ environment_config = (ENV["GEMRC"] || "")
.split(File::PATH_SEPARATOR).inject({}) do |result, file|
result.merge load_file file
end
@hash = operating_system_config.merge platform_config
- unless args.index '--norc'
+ unless args.index "--norc"
@hash = @hash.merge system_config
@hash = @hash.merge user_config
@hash = @hash.merge environment_config
@@ -269,7 +270,7 @@ if you believe they were disclosed to a third party.
# Location of RubyGems.org credentials
def credentials_path
- credentials = File.join Gem.user_home, '.gem', 'credentials'
+ credentials = File.join Gem.user_home, ".gem", "credentials"
if File.exist? credentials
credentials
else
@@ -320,13 +321,13 @@ if you believe they were disclosed to a third party.
config = load_file(credentials_path).merge(host => api_key)
dirname = File.dirname credentials_path
- require 'fileutils'
+ require "fileutils"
FileUtils.mkdir_p(dirname)
Gem.load_yaml
permissions = 0600 & (~File.umask)
- File.open(credentials_path, 'w', permissions) do |f|
+ File.open(credentials_path, "w", permissions) do |f|
f.write config.to_yaml
end
@@ -368,7 +369,21 @@ if you believe they were disclosed to a third party.
# True if the backtrace option has been specified, or debug is on.
def backtrace
- @backtrace or $DEBUG
+ @backtrace || $DEBUG
+ end
+
+ # Check state file is writable. Creates empty file if not present to ensure we can write to it.
+ def state_file_writable?
+ if File.exist?(state_file_name)
+ File.writable?(state_file_name)
+ else
+ require "fileutils"
+ FileUtils.mkdir_p File.dirname(state_file_name)
+ File.open(state_file_name, "w") {}
+ true
+ end
+ rescue Errno::EACCES
+ false
end
# The name of the configuration file.
@@ -376,6 +391,25 @@ if you believe they were disclosed to a third party.
@config_file_name || Gem.config_file
end
+ # The name of the state file.
+ def state_file_name
+ Gem.state_file
+ end
+
+ # Reads time of last update check from state file
+ def last_update_check
+ if File.readable?(state_file_name)
+ File.read(state_file_name).to_i
+ else
+ 0
+ end
+ end
+
+ # Writes time of last update check to state file
+ def last_update_check=(timestamp)
+ File.write(state_file_name, timestamp.to_s) if state_file_writable?
+ end
+
# Delegates to @hash
def each(&block)
hash = @hash.dup
@@ -389,7 +423,7 @@ if you believe they were disclosed to a third party.
yield :backtrace, @backtrace
yield :bulk_threshold, @bulk_threshold
- yield 'config_file_name', @config_file_name if @config_file_name
+ yield "config_file_name", @config_file_name if @config_file_name
hash.each(&block)
end
@@ -405,7 +439,7 @@ if you believe they were disclosed to a third party.
when /^--debug$/ then
$DEBUG = true
- warn 'NOTE: Debugging mode prints all exceptions even when rescued'
+ warn "NOTE: Debugging mode prints all exceptions even when rescued"
else
@args << arg
end
@@ -444,7 +478,7 @@ if you believe they were disclosed to a third party.
@hash[:ssl_client_cert] if @hash.key? :ssl_client_cert
keys = yaml_hash.keys.map {|key| key.to_s }
- keys << 'debug'
+ keys << "debug"
re = Regexp.union(*keys)
@hash.each do |key, value|
@@ -458,10 +492,10 @@ if you believe they were disclosed to a third party.
# Writes out this config file, replacing its source.
def write
- require 'fileutils'
+ require "fileutils"
FileUtils.mkdir_p File.dirname(config_file_name)
- File.open config_file_name, 'w' do |io|
+ File.open config_file_name, "w" do |io|
io.write to_yaml
end
end
@@ -477,11 +511,11 @@ if you believe they were disclosed to a third party.
end
def ==(other) # :nodoc:
- self.class === other and
- @backtrace == other.backtrace and
- @bulk_threshold == other.bulk_threshold and
- @verbose == other.verbose and
- @update_sources == other.update_sources and
+ self.class === other &&
+ @backtrace == other.backtrace &&
+ @bulk_threshold == other.bulk_threshold &&
+ @verbose == other.verbose &&
+ @update_sources == other.update_sources &&
@hash == other.hash
end
diff --git a/lib/rubygems/core_ext/kernel_gem.rb b/lib/rubygems/core_ext/kernel_gem.rb
index e722225739..b2f97b9ed9 100644
--- a/lib/rubygems/core_ext/kernel_gem.rb
+++ b/lib/rubygems/core_ext/kernel_gem.rb
@@ -1,9 +1,4 @@
# frozen_string_literal: true
-##
-# RubyGems adds the #gem method to allow activation of specific gem versions
-# and overrides the #require method on Kernel to make gems appear as if they
-# live on the <code>$LOAD_PATH</code>. See the documentation of these methods
-# for further detail.
module Kernel
@@ -39,7 +34,7 @@ module Kernel
# GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb
def gem(gem_name, *requirements) # :doc:
- skip_list = (ENV['GEM_SKIP'] || "").split(/:/)
+ skip_list = (ENV["GEM_SKIP"] || "").split(/:/)
raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name
if gem_name.kind_of? Gem::Dependency
diff --git a/lib/rubygems/core_ext/kernel_require.rb b/lib/rubygems/core_ext/kernel_require.rb
index 23badd75d2..0869c8746f 100644
--- a/lib/rubygems/core_ext/kernel_require.rb
+++ b/lib/rubygems/core_ext/kernel_require.rb
@@ -1,11 +1,12 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require 'monitor'
+require "monitor"
module Kernel
@@ -13,12 +14,12 @@ module Kernel
# Make sure we have a reference to Ruby's original Kernel#require
unless defined?(gem_original_require)
+ # :stopdoc:
alias gem_original_require require
private :gem_original_require
+ # :startdoc:
end
- file = Gem::KERNEL_WARN_IGNORES_INTERNAL_ENTRIES ? "<internal:#{__FILE__}>" : __FILE__
- module_eval <<'RUBY', file, __LINE__ + 1 # rubocop:disable Style/EvalWithLocation
##
# When RubyGems is required, Kernel#require is replaced with our own which
# is capable of loading gems on demand.
@@ -33,142 +34,135 @@ module Kernel
# The normal <tt>require</tt> functionality of returning false if
# that file has already been loaded is preserved.
- def require(path)
- if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?)
- monitor_owned = RUBYGEMS_ACTIVATION_MONITOR.mon_owned?
- end
- RUBYGEMS_ACTIVATION_MONITOR.enter
-
- path = path.to_path if path.respond_to? :to_path
-
- if spec = Gem.find_unresolved_default_spec(path)
- # Ensure -I beats a default gem
- resolved_path = begin
- rp = nil
- load_path_check_index = Gem.load_path_insert_index - Gem.activated_gem_paths
- Gem.suffixes.each do |s|
- $LOAD_PATH[0...load_path_check_index].each do |lp|
- safe_lp = lp.dup.tap(&Gem::UNTAINT)
- begin
- if File.symlink? safe_lp # for backward compatibility
- next
+ def require(path) # :doc:
+ return gem_original_require(path) unless Gem.discover_gems_on_require
+
+ begin
+ RUBYGEMS_ACTIVATION_MONITOR.enter
+
+ path = path.to_path if path.respond_to? :to_path
+
+ if spec = Gem.find_unresolved_default_spec(path)
+ # Ensure -I beats a default gem
+ resolved_path = begin
+ rp = nil
+ load_path_check_index = Gem.load_path_insert_index - Gem.activated_gem_paths
+ Gem.suffixes.each do |s|
+ $LOAD_PATH[0...load_path_check_index].each do |lp|
+ safe_lp = lp.dup.tap(&Gem::UNTAINT)
+ begin
+ if File.symlink? safe_lp # for backward compatibility
+ next
+ end
+ rescue SecurityError
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ raise
end
- rescue SecurityError
- RUBYGEMS_ACTIVATION_MONITOR.exit
- raise
- end
- full_path = File.expand_path(File.join(safe_lp, "#{path}#{s}"))
- if File.file?(full_path)
- rp = full_path
- break
+ full_path = File.expand_path(File.join(safe_lp, "#{path}#{s}"))
+ if File.file?(full_path)
+ rp = full_path
+ break
+ end
end
+ break if rp
end
- break if rp
+ rp
end
- rp
- end
-
- begin
- Kernel.send(:gem, spec.name, Gem::Requirement.default_prerelease)
- rescue Exception
- RUBYGEMS_ACTIVATION_MONITOR.exit
- raise
- end unless resolved_path
- end
-
- # If there are no unresolved deps, then we can use just try
- # normal require handle loading a gem from the rescue below.
-
- if Gem::Specification.unresolved_deps.empty?
- RUBYGEMS_ACTIVATION_MONITOR.exit
- return gem_original_require(path)
- end
-
- # If +path+ is for a gem that has already been loaded, don't
- # bother trying to find it in an unresolved gem, just go straight
- # to normal require.
- #--
- # TODO request access to the C implementation of this to speed up RubyGems
-
- if Gem::Specification.find_active_stub_by_path(path)
- RUBYGEMS_ACTIVATION_MONITOR.exit
- return gem_original_require(path)
- end
- # Attempt to find +path+ in any unresolved gems...
-
- found_specs = Gem::Specification.find_in_unresolved path
-
- # If there are no directly unresolved gems, then try and find +path+
- # in any gems that are available via the currently unresolved gems.
- # For example, given:
- #
- # a => b => c => d
- #
- # If a and b are currently active with c being unresolved and d.rb is
- # requested, then find_in_unresolved_tree will find d.rb in d because
- # it's a dependency of c.
- #
- if found_specs.empty?
- found_specs = Gem::Specification.find_in_unresolved_tree path
-
- found_specs.each do |found_spec|
- found_spec.activate
+ begin
+ Kernel.send(:gem, spec.name, Gem::Requirement.default_prerelease)
+ rescue Exception
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ raise
+ end unless resolved_path
end
- # We found +path+ directly in an unresolved gem. Now we figure out, of
- # the possible found specs, which one we should activate.
- else
-
- # Check that all the found specs are just different
- # versions of the same gem
- names = found_specs.map(&:name).uniq
+ # If there are no unresolved deps, then we can use just try
+ # normal require handle loading a gem from the rescue below.
- if names.size > 1
+ if Gem::Specification.unresolved_deps.empty?
RUBYGEMS_ACTIVATION_MONITOR.exit
- raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ', '}"
+ return gem_original_require(path)
end
- # Ok, now find a gem that has no conflicts, starting
- # at the highest version.
- valid = found_specs.find {|s| !s.has_conflicts? }
+ # If +path+ is for a gem that has already been loaded, don't
+ # bother trying to find it in an unresolved gem, just go straight
+ # to normal require.
+ #--
+ # TODO request access to the C implementation of this to speed up RubyGems
- unless valid
- le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
- le.name = names.first
+ if Gem::Specification.find_active_stub_by_path(path)
RUBYGEMS_ACTIVATION_MONITOR.exit
- raise le
+ return gem_original_require(path)
end
- valid.activate
- end
+ # Attempt to find +path+ in any unresolved gems...
+
+ found_specs = Gem::Specification.find_in_unresolved path
+
+ # If there are no directly unresolved gems, then try and find +path+
+ # in any gems that are available via the currently unresolved gems.
+ # For example, given:
+ #
+ # a => b => c => d
+ #
+ # If a and b are currently active with c being unresolved and d.rb is
+ # requested, then find_in_unresolved_tree will find d.rb in d because
+ # it's a dependency of c.
+ #
+ if found_specs.empty?
+ found_specs = Gem::Specification.find_in_unresolved_tree path
+
+ found_specs.each do |found_spec|
+ found_spec.activate
+ end
- RUBYGEMS_ACTIVATION_MONITOR.exit
- return gem_original_require(path)
- rescue LoadError => load_error
- RUBYGEMS_ACTIVATION_MONITOR.enter
+ # We found +path+ directly in an unresolved gem. Now we figure out, of
+ # the possible found specs, which one we should activate.
+ else
- begin
- if load_error.path == path and Gem.try_activate(path)
- require_again = true
+ # Check that all the found specs are just different
+ # versions of the same gem
+ names = found_specs.map(&:name).uniq
+
+ if names.size > 1
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ', '}"
+ end
+
+ # Ok, now find a gem that has no conflicts, starting
+ # at the highest version.
+ valid = found_specs.find {|s| !s.has_conflicts? }
+
+ unless valid
+ le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
+ le.name = names.first
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ raise le
+ end
+
+ valid.activate
end
- ensure
- RUBYGEMS_ACTIVATION_MONITOR.exit
- end
- return gem_original_require(path) if require_again
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ return gem_original_require(path)
+ rescue LoadError => load_error
+ if load_error.path == path
+ RUBYGEMS_ACTIVATION_MONITOR.enter
+
+ begin
+ require_again = Gem.try_activate(path)
+ ensure
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ end
- raise load_error
- ensure
- if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?)
- if monitor_owned != (ow = RUBYGEMS_ACTIVATION_MONITOR.mon_owned?)
- STDERR.puts [$$, Thread.current, $!, $!.backtrace].inspect if $!
- raise "CRITICAL: RUBYGEMS_ACTIVATION_MONITOR.owned?: before #{monitor_owned} -> after #{ow}"
+ return gem_original_require(path) if require_again
end
+
+ raise load_error
end
end
-RUBY
private :require
diff --git a/lib/rubygems/core_ext/kernel_warn.rb b/lib/rubygems/core_ext/kernel_warn.rb
index 3373cfdd3b..1f4c77f04b 100644
--- a/lib/rubygems/core_ext/kernel_warn.rb
+++ b/lib/rubygems/core_ext/kernel_warn.rb
@@ -1,54 +1,50 @@
# frozen_string_literal: true
-# `uplevel` keyword argument of Kernel#warn is available since ruby 2.5.
-if RUBY_VERSION >= "2.5" && !Gem::KERNEL_WARN_IGNORES_INTERNAL_ENTRIES
+module Kernel
+ rubygems_path = "#{__dir__}/" # Frames to be skipped start with this path.
- module Kernel
- rubygems_path = "#{__dir__}/" # Frames to be skipped start with this path.
+ original_warn = instance_method(:warn)
- original_warn = instance_method(:warn)
+ remove_method :warn
+ class << self
remove_method :warn
+ end
- class << self
- remove_method :warn
+ module_function define_method(:warn) {|*messages, **kw|
+ unless uplevel = kw[:uplevel]
+ if Gem.java_platform? && RUBY_VERSION < "3.1"
+ return original_warn.bind(self).call(*messages)
+ else
+ return original_warn.bind(self).call(*messages, **kw)
+ end
end
- module_function define_method(:warn) {|*messages, **kw|
- unless uplevel = kw[:uplevel]
- if Gem.java_platform?
- return original_warn.bind(self).call(*messages)
- else
- return original_warn.bind(self).call(*messages, **kw)
+ # Ensure `uplevel` fits a `long`
+ uplevel, = [uplevel].pack("l!").unpack("l!")
+
+ if uplevel >= 0
+ start = 0
+ while uplevel >= 0
+ loc, = caller_locations(start, 1)
+ unless loc
+ # No more backtrace
+ start += uplevel
+ break
end
- end
- # Ensure `uplevel` fits a `long`
- uplevel, = [uplevel].pack("l!").unpack("l!")
-
- if uplevel >= 0
- start = 0
- while uplevel >= 0
- loc, = caller_locations(start, 1)
- unless loc
- # No more backtrace
- start += uplevel
- break
- end
+ start += 1
- start += 1
-
- if path = loc.path
- unless path.start_with?(rubygems_path) or path.start_with?('<internal:')
- # Non-rubygems frames
- uplevel -= 1
- end
+ if path = loc.path
+ unless path.start_with?(rubygems_path) || path.start_with?("<internal:")
+ # Non-rubygems frames
+ uplevel -= 1
end
end
- kw[:uplevel] = start
end
+ kw[:uplevel] = start
+ end
- original_warn.bind(self).call(*messages, **kw)
- }
- end
+ original_warn.bind(self).call(*messages, **kw)
+ }
end
diff --git a/lib/rubygems/core_ext/tcpsocket_init.rb b/lib/rubygems/core_ext/tcpsocket_init.rb
index 2a79b63bd6..451ffa50b2 100644
--- a/lib/rubygems/core_ext/tcpsocket_init.rb
+++ b/lib/rubygems/core_ext/tcpsocket_init.rb
@@ -1,4 +1,6 @@
-require 'socket'
+# frozen_string_literal: true
+
+require "socket"
module CoreExtensions
module TCPSocketExt
diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb
index 39b69ddb1c..2b1baa333d 100644
--- a/lib/rubygems/defaults.rb
+++ b/lib/rubygems/defaults.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
+
module Gem
- DEFAULT_HOST = "https://rubygems.org".freeze
+ DEFAULT_HOST = "https://rubygems.org"
@post_install_hooks ||= []
@done_installing_hooks ||= []
@@ -20,10 +21,10 @@ module Gem
# specified in the environment
def self.default_spec_cache_dir
- default_spec_cache_dir = File.join Gem.user_home, '.gem', 'specs'
+ default_spec_cache_dir = File.join Gem.user_home, ".gem", "specs"
unless File.exist?(default_spec_cache_dir)
- default_spec_cache_dir = File.join Gem.data_home, 'gem', 'specs'
+ default_spec_cache_dir = File.join Gem.data_home, "gem", "specs"
end
default_spec_cache_dir
@@ -34,7 +35,7 @@ module Gem
# specified in the environment
def self.default_dir
- @default_dir ||= File.join(RbConfig::CONFIG['rubylibprefix'], 'gems', RbConfig::CONFIG['ruby_version'])
+ @default_dir ||= File.join(RbConfig::CONFIG["rubylibprefix"], "gems", RbConfig::CONFIG["ruby_version"])
end
##
@@ -81,7 +82,7 @@ module Gem
Dir.home.dup
rescue
if Gem.win_platform?
- File.expand_path File.join(ENV['HOMEDRIVE'] || ENV['SystemDrive'], '/')
+ File.expand_path File.join(ENV["HOMEDRIVE"] || ENV["SystemDrive"], "/")
else
File.expand_path "/"
end
@@ -103,7 +104,7 @@ module Gem
gem_dir = File.join(Gem.user_home, ".gem")
gem_dir = File.join(Gem.data_home, "gem") unless File.exist?(gem_dir)
parts = [gem_dir, ruby_engine]
- parts << RbConfig::CONFIG['ruby_version'] unless RbConfig::CONFIG['ruby_version'].empty?
+ parts << RbConfig::CONFIG["ruby_version"] unless RbConfig::CONFIG["ruby_version"].empty?
File.join parts
end
@@ -111,14 +112,14 @@ module Gem
# The path to standard location of the user's configuration directory.
def self.config_home
- @config_home ||= (ENV["XDG_CONFIG_HOME"] || File.join(Gem.user_home, '.config'))
+ @config_home ||= (ENV["XDG_CONFIG_HOME"] || File.join(Gem.user_home, ".config"))
end
##
# Finds the user's config file
def self.find_config_file
- gemrc = File.join Gem.user_home, '.gemrc'
+ gemrc = File.join Gem.user_home, ".gemrc"
if File.exist? gemrc
gemrc
else
@@ -134,17 +135,31 @@ module Gem
end
##
+ # The path to standard location of the user's state file.
+
+ def self.state_file
+ @state_file ||= File.join(Gem.state_home, "gem", "last_update_check").tap(&Gem::UNTAINT)
+ end
+
+ ##
# The path to standard location of the user's cache directory.
def self.cache_home
- @cache_home ||= (ENV["XDG_CACHE_HOME"] || File.join(Gem.user_home, '.cache'))
+ @cache_home ||= (ENV["XDG_CACHE_HOME"] || File.join(Gem.user_home, ".cache"))
end
##
# The path to standard location of the user's data directory.
def self.data_home
- @data_home ||= (ENV["XDG_DATA_HOME"] || File.join(Gem.user_home, '.local', 'share'))
+ @data_home ||= (ENV["XDG_DATA_HOME"] || File.join(Gem.user_home, ".local", "share"))
+ end
+
+ ##
+ # The path to standard location of the user's state directory.
+
+ def self.state_home
+ @state_home ||= (ENV["XDG_STATE_HOME"] || File.join(Gem.user_home, ".local", "state"))
end
##
@@ -161,7 +176,7 @@ module Gem
path = []
path << user_dir if user_home && File.exist?(user_home)
path << default_dir
- path << vendor_dir if vendor_dir and File.directory? vendor_dir
+ path << vendor_dir if vendor_dir && File.directory?(vendor_dir)
path
end
@@ -169,9 +184,9 @@ module Gem
# Deduce Ruby's --program-prefix and --program-suffix from its install name
def self.default_exec_format
- exec_format = RbConfig::CONFIG['ruby_install_name'].sub('ruby', '%s') rescue '%s'
+ exec_format = RbConfig::CONFIG["ruby_install_name"].sub("ruby", "%s") rescue "%s"
- unless exec_format =~ /%s/
+ unless exec_format.include?("%s")
raise Gem::Exception,
"[BUG] invalid exec_format #{exec_format.inspect}, no %s"
end
@@ -183,7 +198,7 @@ module Gem
# The default directory for binaries
def self.default_bindir
- RbConfig::CONFIG['bindir']
+ RbConfig::CONFIG["bindir"]
end
def self.ruby_engine
@@ -227,14 +242,14 @@ module Gem
# Directory where vendor gems are installed.
def self.vendor_dir # :nodoc:
- if vendor_dir = ENV['GEM_VENDOR']
+ if vendor_dir = ENV["GEM_VENDOR"]
return vendor_dir.dup
end
- return nil unless RbConfig::CONFIG.key? 'vendordir'
+ return nil unless RbConfig::CONFIG.key? "vendordir"
- File.join RbConfig::CONFIG['vendordir'], 'gems',
- RbConfig::CONFIG['ruby_version']
+ File.join RbConfig::CONFIG["vendordir"], "gems",
+ RbConfig::CONFIG["ruby_version"]
end
##
diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb
index 3640362364..d9e0c07b0f 100644
--- a/lib/rubygems/dependency.rb
+++ b/lib/rubygems/dependency.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The Dependency class holds a Gem name and a Gem::Requirement.
@@ -97,14 +98,14 @@ class Gem::Dependency
end
def pretty_print(q) # :nodoc:
- q.group 1, 'Gem::Dependency.new(', ')' do
+ q.group 1, "Gem::Dependency.new(", ")" do
q.pp name
- q.text ','
+ q.text ","
q.breakable
q.pp requirement
- q.text ','
+ q.text ","
q.breakable
q.pp type
@@ -115,7 +116,7 @@ class Gem::Dependency
# What does this dependency require?
def requirement
- return @requirement if defined?(@requirement) and @requirement
+ return @requirement if defined?(@requirement) && @requirement
# @version_requirements and @version_requirement are legacy ivar
# names, and supported here because older gems need to keep
@@ -197,7 +198,7 @@ class Gem::Dependency
reqs = other.requirement.requirements
return false unless reqs.length == 1
- return false unless reqs.first.first == '='
+ return false unless reqs.first.first == "="
version = reqs.first.last
@@ -230,10 +231,10 @@ class Gem::Dependency
version = Gem::Version.new version
- return true if requirement.none? and not version.prerelease?
- return false if version.prerelease? and
- not allow_prerelease and
- not prerelease?
+ return true if requirement.none? && !version.prerelease?
+ return false if version.prerelease? &&
+ !allow_prerelease &&
+ !prerelease?
requirement.satisfied_by? version
end
@@ -277,7 +278,10 @@ class Gem::Dependency
requirement.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version)
end.map(&:to_spec)
- Gem::BundlerVersionFinder.prioritize!(matches) if prioritizes_bundler?
+ if prioritizes_bundler?
+ require_relative "bundler_version_finder"
+ Gem::BundlerVersionFinder.prioritize!(matches)
+ end
if platform_only
matches.reject! do |spec|
@@ -296,7 +300,7 @@ class Gem::Dependency
end
def prioritizes_bundler?
- name == "bundler".freeze && !specific?
+ name == "bundler" && !specific?
end
def to_specs
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index 913bba32eb..a8d73e521b 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
-require_relative '../rubygems'
-require_relative 'dependency_list'
-require_relative 'package'
-require_relative 'installer'
-require_relative 'spec_fetcher'
-require_relative 'user_interaction'
-require_relative 'available_set'
-require_relative 'deprecate'
+
+require_relative "../rubygems"
+require_relative "dependency_list"
+require_relative "package"
+require_relative "installer"
+require_relative "spec_fetcher"
+require_relative "user_interaction"
+require_relative "available_set"
+require_relative "deprecate"
##
# Installs a gem along with all its dependencies from local and remote gems.
@@ -16,16 +17,16 @@ class Gem::DependencyInstaller
extend Gem::Deprecate
DEFAULT_OPTIONS = { # :nodoc:
- :env_shebang => false,
- :document => %w[ri],
- :domain => :both, # HACK dup
- :force => false,
- :format_executable => false, # HACK dup
+ :env_shebang => false,
+ :document => %w[ri],
+ :domain => :both, # HACK dup
+ :force => false,
+ :format_executable => false, # HACK dup
:ignore_dependencies => false,
- :prerelease => false,
- :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low?
- :wrappers => true,
- :build_args => nil,
+ :prerelease => false,
+ :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low?
+ :wrappers => true,
+ :build_args => nil,
:build_docs_in_background => false,
:install_as_default => false,
}.freeze
@@ -109,7 +110,7 @@ class Gem::DependencyInstaller
# gems should be considered.
def consider_local?
- @domain == :both or @domain == :local
+ @domain == :both || @domain == :local
end
##
@@ -117,7 +118,7 @@ class Gem::DependencyInstaller
# gems should be considered.
def consider_remote?
- @domain == :both or @domain == :remote
+ @domain == :both || @domain == :remote
end
##
@@ -197,7 +198,7 @@ class Gem::DependencyInstaller
def in_background(what) # :nodoc:
fork_happened = false
- if @build_docs_in_background and Process.respond_to?(:fork)
+ if @build_docs_in_background && Process.respond_to?(:fork)
begin
Process.fork do
yield
@@ -230,22 +231,22 @@ class Gem::DependencyInstaller
@installed_gems = []
options = {
- :bin_dir => @bin_dir,
- :build_args => @build_args,
- :document => @document,
- :env_shebang => @env_shebang,
- :force => @force,
- :format_executable => @format_executable,
+ :bin_dir => @bin_dir,
+ :build_args => @build_args,
+ :document => @document,
+ :env_shebang => @env_shebang,
+ :force => @force,
+ :format_executable => @format_executable,
:ignore_dependencies => @ignore_dependencies,
- :prerelease => @prerelease,
- :security_policy => @security_policy,
- :user_install => @user_install,
- :wrappers => @wrappers,
- :build_root => @build_root,
- :install_as_default => @install_as_default,
- :dir_mode => @dir_mode,
- :data_mode => @data_mode,
- :prog_mode => @prog_mode,
+ :prerelease => @prerelease,
+ :security_policy => @security_policy,
+ :user_install => @user_install,
+ :wrappers => @wrappers,
+ :build_root => @build_root,
+ :install_as_default => @install_as_default,
+ :dir_mode => @dir_mode,
+ :data_mode => @data_mode,
+ :prog_mode => @prog_mode,
}
options[:install_dir] = @install_dir if @only_install_dir
@@ -268,7 +269,7 @@ class Gem::DependencyInstaller
end
def install_development_deps # :nodoc:
- if @development and @dev_shallow
+ if @development && @dev_shallow
:shallow
elsif @development
:all
@@ -289,7 +290,7 @@ class Gem::DependencyInstaller
installer_set.force = @force
if consider_local?
- if dep_or_name =~ /\.gem$/ and File.file? dep_or_name
+ if dep_or_name =~ /\.gem$/ && File.file?(dep_or_name)
src = Gem::Source::SpecificFile.new dep_or_name
installer_set.add_local dep_or_name, src.spec, src
version = src.spec.version if version == Gem::Requirement.default
diff --git a/lib/rubygems/dependency_list.rb b/lib/rubygems/dependency_list.rb
index 10e08fc703..5820298bb9 100644
--- a/lib/rubygems/dependency_list.rb
+++ b/lib/rubygems/dependency_list.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative 'tsort'
-require_relative 'deprecate'
+require_relative "tsort"
+require_relative "deprecate"
##
# Gem::DependencyList is used for installing and uninstalling gems in the
@@ -119,11 +120,11 @@ class Gem::DependencyList
each do |spec|
spec.runtime_dependencies.each do |dep|
inst = Gem::Specification.any? do |installed_spec|
- dep.name == installed_spec.name and
- dep.requirement.satisfied_by? installed_spec.version
+ dep.name == installed_spec.name &&
+ dep.requirement.satisfied_by?(installed_spec.version)
end
- unless inst or @specs.find {|s| s.satisfies_requirement? dep }
+ unless inst || @specs.find {|s| s.satisfies_requirement? dep }
unsatisfied[spec.name] << dep
return unsatisfied if quick
end
@@ -175,7 +176,7 @@ class Gem::DependencyList
def remove_specs_unsatisfied_by(dependencies)
specs.reject! do |spec|
dep = dependencies[spec.name]
- dep and not dep.requirement.satisfied_by? spec.version
+ dep && !dep.requirement.satisfied_by?(spec.version)
end
end
diff --git a/lib/rubygems/deprecate.rb b/lib/rubygems/deprecate.rb
index 5fe0afb6b0..9fff306949 100644
--- a/lib/rubygems/deprecate.rb
+++ b/lib/rubygems/deprecate.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Provides 3 methods for declaring when something is going away.
#
@@ -143,7 +144,7 @@ module Gem::Deprecate
end
# Deprecation method to deprecate Rubygems commands
- def rubygems_deprecate_command
+ def rubygems_deprecate_command(version = Gem::Deprecate.next_rubygems_major_version)
class_eval do
define_method "deprecated?" do
true
@@ -151,7 +152,7 @@ module Gem::Deprecate
define_method "deprecation_warning" do
msg = [ "#{self.command} command is deprecated",
- ". It will be removed in Rubygems #{Gem::Deprecate.next_rubygems_major_version}.\n",
+ ". It will be removed in Rubygems #{version}.\n",
]
alert_warning "#{msg.join}" unless Gem::Deprecate.skip
diff --git a/lib/rubygems/doctor.rb b/lib/rubygems/doctor.rb
index d14c64a166..1d3889af77 100644
--- a/lib/rubygems/doctor.rb
+++ b/lib/rubygems/doctor.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
-require_relative '../rubygems'
-require_relative 'user_interaction'
+
+require_relative "../rubygems"
+require_relative "user_interaction"
##
# Cleans up after a partially-failed uninstall or for an invalid
@@ -19,18 +20,18 @@ class Gem::Doctor
# subdirectory.
REPOSITORY_EXTENSION_MAP = [ # :nodoc:
- ['specifications', '.gemspec'],
- ['build_info', '.info'],
- ['cache', '.gem'],
- ['doc', ''],
- ['extensions', ''],
- ['gems', ''],
- ['plugins', ''],
+ ["specifications", ".gemspec"],
+ ["build_info", ".info"],
+ ["cache", ".gem"],
+ ["doc", ""],
+ ["extensions", ""],
+ ["gems", ""],
+ ["plugins", ""],
].freeze
missing =
Gem::REPOSITORY_SUBDIRECTORIES.sort -
- REPOSITORY_EXTENSION_MAP.map {|(k,_)| k }.sort
+ REPOSITORY_EXTENSION_MAP.map {|(k,_)| k }.sort
raise "Update REPOSITORY_EXTENSION_MAP, missing: #{missing.join ', '}" unless
missing.empty?
@@ -59,7 +60,7 @@ class Gem::Doctor
# Are we doctoring a gem repository?
def gem_repository?
- not installed_specs.empty?
+ !installed_specs.empty?
end
##
@@ -74,8 +75,8 @@ class Gem::Doctor
Gem.use_paths @gem_repository.to_s
unless gem_repository?
- say 'This directory does not appear to be a RubyGems repository, ' +
- 'skipping'
+ say "This directory does not appear to be a RubyGems repository, " +
+ "skipping"
say
return
end
@@ -111,16 +112,16 @@ class Gem::Doctor
basename = File.basename(child, extension)
next if installed_specs.include? basename
next if /^rubygems-\d/ =~ basename
- next if 'specifications' == sub_directory and 'default' == basename
- next if 'plugins' == sub_directory and Gem.plugin_suffix_regexp =~ basename
+ next if "specifications" == sub_directory && "default" == basename
+ next if "plugins" == sub_directory && Gem.plugin_suffix_regexp =~ (basename)
- type = File.directory?(child) ? 'directory' : 'file'
+ type = File.directory?(child) ? "directory" : "file"
action = if @dry_run
- 'Extra'
+ "Extra"
else
FileUtils.rm_r(child)
- 'Removed'
+ "Removed"
end
say "#{action} #{type} #{sub_directory}/#{File.basename(child)}"
diff --git a/lib/rubygems/errors.rb b/lib/rubygems/errors.rb
index e6e222033d..b36fabffc2 100644
--- a/lib/rubygems/errors.rb
+++ b/lib/rubygems/errors.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# This file contains all the various exceptions and other errors that are used
# inside of RubyGems.
@@ -136,8 +137,8 @@ module Gem
"Found %s (%s), but was for platform%s %s" %
[@name,
@version,
- @platforms.size == 1 ? '' : 's',
- @platforms.join(' ,')]
+ @platforms.size == 1 ? "" : "s",
+ @platforms.join(" ,")]
end
end
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index 1806869098..b92396f0ef 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require_relative 'deprecate'
-require_relative 'unknown_command_spell_checker'
+require_relative "deprecate"
+require_relative "unknown_command_spell_checker"
##
# Base exception class for RubyGems. All exception raised by RubyGems are a
@@ -27,7 +27,7 @@ class Gem::UnknownCommandError < Gem::Exception
if DidYouMean.respond_to?(:correct_error)
DidYouMean.correct_error(Gem::UnknownCommandError, Gem::UnknownCommandSpellChecker)
else
- DidYouMean::SPELL_CHECKERS['Gem::UnknownCommandError'] =
+ DidYouMean::SPELL_CHECKERS["Gem::UnknownCommandError"] =
Gem::UnknownCommandSpellChecker
prepend DidYouMean::Correctable
@@ -154,7 +154,7 @@ class Gem::ImpossibleDependenciesError < Gem::Exception
def build_message # :nodoc:
requester = @request.requester
- requester = requester ? requester.spec.full_name : 'The user'
+ requester = requester ? requester.spec.full_name : "The user"
dependency = @request.dependency
message = "#{requester} requires #{dependency} but it conflicted:\n".dup
@@ -214,6 +214,16 @@ class Gem::RubyVersionMismatch < Gem::Exception; end
class Gem::VerificationError < Gem::Exception; end
##
+# Raised by Gem::WebauthnListener when an error occurs during security
+# device verification.
+
+class Gem::WebauthnVerificationError < Gem::Exception
+ def initialize(message)
+ super "Security device verification failed: #{message}"
+ end
+end
+
+##
# Raised to indicate that a system exit should occur with the specified
# exit_code
@@ -221,14 +231,12 @@ class Gem::SystemExitException < SystemExit
##
# The exit code for the process
- attr_accessor :exit_code
+ alias exit_code status
##
# Creates a new SystemExitException with the given +exit_code+
def initialize(exit_code)
- @exit_code = exit_code
-
super exit_code, "Exiting RubyGems with exit_code #{exit_code}"
end
end
@@ -254,7 +262,7 @@ class Gem::UnsatisfiableDependencyError < Gem::DependencyError
# Gem::Resolver::DependencyRequest +dep+
def initialize(dep, platform_mismatch=nil)
- if platform_mismatch and !platform_mismatch.empty?
+ if platform_mismatch && !platform_mismatch.empty?
plats = platform_mismatch.map {|x| x.platform.to_s }.sort.uniq
super "Unable to resolve dependency: No match for '#{dep}' on this platform. Found: #{plats.join(', ')}"
else
diff --git a/lib/rubygems/ext.rb b/lib/rubygems/ext.rb
index 59fd830437..b5ca126a08 100644
--- a/lib/rubygems/ext.rb
+++ b/lib/rubygems/ext.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -10,10 +11,10 @@
module Gem::Ext; end
-require_relative 'ext/build_error'
-require_relative 'ext/builder'
-require_relative 'ext/configure_builder'
-require_relative 'ext/ext_conf_builder'
-require_relative 'ext/rake_builder'
-require_relative 'ext/cmake_builder'
-require_relative 'ext/cargo_builder'
+require_relative "ext/build_error"
+require_relative "ext/builder"
+require_relative "ext/configure_builder"
+require_relative "ext/ext_conf_builder"
+require_relative "ext/rake_builder"
+require_relative "ext/cmake_builder"
+require_relative "ext/cargo_builder"
diff --git a/lib/rubygems/ext/build_error.rb b/lib/rubygems/ext/build_error.rb
index 8ef57ed91a..0329c1eec3 100644
--- a/lib/rubygems/ext/build_error.rb
+++ b/lib/rubygems/ext/build_error.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
+
##
# Raised when there is an error while building extensions.
-require_relative '../exceptions'
+require_relative "../exceptions"
class Gem::Ext::BuildError < Gem::InstallError
end
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index 99dd2c162c..4960ddd7b1 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -1,11 +1,13 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative '../user_interaction'
+require_relative "../user_interaction"
+require_relative "../shellwords"
class Gem::Ext::Builder
include Gem::UserInteraction
@@ -17,30 +19,30 @@ class Gem::Ext::Builder
$1.downcase
end
- def self.make(dest_path, results, make_dir = Dir.pwd, sitedir = nil)
- unless File.exist? File.join(make_dir, 'Makefile')
- raise Gem::InstallError, 'Makefile not found'
+ def self.make(dest_path, results, make_dir = Dir.pwd, sitedir = nil, targets = ["clean", "", "install"])
+ unless File.exist? File.join(make_dir, "Makefile")
+ raise Gem::InstallError, "Makefile not found"
end
# try to find make program from Ruby configure arguments first
- RbConfig::CONFIG['configure_args'] =~ /with-make-prog\=(\w+)/
- make_program_name = ENV['MAKE'] || ENV['make'] || $1
+ RbConfig::CONFIG["configure_args"] =~ /with-make-prog\=(\w+)/
+ make_program_name = ENV["MAKE"] || ENV["make"] || $1
unless make_program_name
- make_program_name = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
+ make_program_name = (RUBY_PLATFORM.include?("mswin")) ? "nmake" : "make"
end
make_program = Shellwords.split(make_program_name)
# The installation of the bundled gems is failed when DESTDIR is empty in mswin platform.
- destdir = (/\bnmake/i !~ make_program_name || ENV['DESTDIR'] && ENV['DESTDIR'] != "") ? 'DESTDIR=%s' % ENV['DESTDIR'] : ''
+ destdir = (/\bnmake/i !~ make_program_name || ENV["DESTDIR"] && ENV["DESTDIR"] != "") ? "DESTDIR=%s" % ENV["DESTDIR"] : ""
env = [destdir]
if sitedir
- env << 'sitearchdir=%s' % sitedir
- env << 'sitelibdir=%s' % sitedir
+ env << "sitearchdir=%s" % sitedir
+ env << "sitelibdir=%s" % sitedir
end
- ['clean', '', 'install'].each do |target|
+ targets.each do |target|
# Pass DESTDIR via command line to override what's in MAKEFLAGS
cmd = [
*make_program,
@@ -50,27 +52,42 @@ class Gem::Ext::Builder
begin
run(cmd, results, "make #{target}".rstrip, make_dir)
rescue Gem::InstallError
- raise unless target == 'clean' # ignore clean failure
+ raise unless target == "clean" # ignore clean failure
end
end
end
+ def self.ruby
+ # Gem.ruby is quoted if it contains whitespace
+ cmd = Shellwords.split(Gem.ruby)
+
+ # This load_path is only needed when running rubygems test without a proper installation.
+ # Prepending it in a normal installation will cause problem with order of $LOAD_PATH.
+ # Therefore only add load_path if it is not present in the default $LOAD_PATH.
+ load_path = File.expand_path("../..", __dir__)
+ case load_path
+ when RbConfig::CONFIG["sitelibdir"], RbConfig::CONFIG["vendorlibdir"], RbConfig::CONFIG["rubylibdir"]
+ cmd
+ else
+ cmd << "-I#{load_path}"
+ end
+ end
+
def self.run(command, results, command_name = nil, dir = Dir.pwd, env = {})
verbose = Gem.configuration.really_verbose
begin
- rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], nil
+ rubygems_gemdeps, ENV["RUBYGEMS_GEMDEPS"] = ENV["RUBYGEMS_GEMDEPS"], nil
if verbose
puts("current directory: #{dir}")
p(command)
end
results << "current directory: #{dir}"
- require "shellwords"
- results << command.shelljoin
+ results << Shellwords.join(command)
require "open3"
# Set $SOURCE_DATE_EPOCH for the subprocess.
- build_env = { 'SOURCE_DATE_EPOCH' => Gem.source_date_epoch_string }.merge(env)
+ build_env = { "SOURCE_DATE_EPOCH" => Gem.source_date_epoch_string }.merge(env)
output, status = begin
Open3.capture2e(build_env, *command, :chdir => dir)
rescue => error
@@ -82,7 +99,7 @@ class Gem::Ext::Builder
results << output
end
ensure
- ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ ENV["RUBYGEMS_GEMDEPS"] = rubygems_gemdeps
end
unless status.success?
@@ -131,8 +148,7 @@ class Gem::Ext::Builder
when /CMakeLists.txt/ then
Gem::Ext::CmakeBuilder
when /Cargo.toml/ then
- # We use the spec name here to ensure we invoke the correct init function later
- Gem::Ext::CargoBuilder.new(@spec)
+ Gem::Ext::CargoBuilder.new
else
build_error("No builder for extension '#{extension}'")
end
@@ -212,11 +228,11 @@ EOF
# Writes +output+ to gem_make.out in the extension install directory.
def write_gem_make_out(output) # :nodoc:
- destination = File.join @spec.extension_dir, 'gem_make.out'
+ destination = File.join @spec.extension_dir, "gem_make.out"
FileUtils.mkdir_p @spec.extension_dir
- File.open destination, 'wb' do |io|
+ File.open destination, "wb" do |io|
io.puts output
end
diff --git a/lib/rubygems/ext/cargo_builder.rb b/lib/rubygems/ext/cargo_builder.rb
index 232c0a313d..ce3b296f79 100644
--- a/lib/rubygems/ext/cargo_builder.rb
+++ b/lib/rubygems/ext/cargo_builder.rb
@@ -1,80 +1,132 @@
# frozen_string_literal: true
+require_relative "../shellwords"
+
# This class is used by rubygems to build Rust extensions. It is a thin-wrapper
# over the `cargo rustc` command which takes care of building Rust code in a way
# that Ruby can use.
class Gem::Ext::CargoBuilder < Gem::Ext::Builder
attr_accessor :spec, :runner, :profile
- def initialize(spec)
+ def initialize
require_relative "../command"
require_relative "cargo_builder/link_flag_converter"
- @spec = spec
@runner = self.class.method(:run)
@profile = :release
end
- def build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
+ def build(extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
+ require "tempfile"
require "fileutils"
- require "shellwords"
- build_crate(dest_path, results, args, cargo_dir)
- validate_cargo_build!(dest_path)
- rename_cdylib_for_ruby_compatibility(dest_path)
- finalize_directory(dest_path, lib_dir, cargo_dir)
- results
- end
+ # Where's the Cargo.toml of the crate we're building
+ cargo_toml = File.join(cargo_dir, "Cargo.toml")
+ # What's the crate's name
+ crate_name = cargo_crate_name(cargo_dir, cargo_toml, results)
+
+ begin
+ # Create a tmp dir to do the build in
+ tmp_dest = Dir.mktmpdir(".gem.", cargo_dir)
+
+ # Run the build
+ cmd = cargo_command(cargo_toml, tmp_dest, args, crate_name)
+ runner.call(cmd, results, "cargo", cargo_dir, build_env)
+
+ # Where do we expect Cargo to write the compiled library
+ dylib_path = cargo_dylib_path(tmp_dest, crate_name)
+
+ # Helpful error if we didn't find the compiled library
+ raise DylibNotFoundError, tmp_dest unless File.exist?(dylib_path)
- def build_crate(dest_path, results, args, cargo_dir)
- env = build_env
- cmd = cargo_command(cargo_dir, dest_path, args)
- runner.call cmd, results, 'cargo', cargo_dir, env
+ # Cargo and Ruby differ on how the library should be named, rename from
+ # what Cargo outputs to what Ruby expects
+ dlext_name = "#{crate_name}.#{makefile_config("DLEXT")}"
+ dlext_path = File.join(File.dirname(dylib_path), dlext_name)
+ FileUtils.cp(dylib_path, dlext_path)
+
+ nesting = extension_nesting(extension)
+
+ # TODO: remove in RubyGems 4
+ if Gem.install_extension_in_lib && lib_dir
+ nested_lib_dir = File.join(lib_dir, nesting)
+ FileUtils.mkdir_p nested_lib_dir
+ FileUtils.cp_r dlext_path, nested_lib_dir, remove_destination: true
+ end
+
+ # move to final destination
+ nested_dest_path = File.join(dest_path, nesting)
+ FileUtils.mkdir_p nested_dest_path
+ FileUtils.cp_r dlext_path, nested_dest_path, remove_destination: true
+ ensure
+ # clean up intermediary build artifacts
+ FileUtils.rm_rf tmp_dest if tmp_dest
+ end
results
end
def build_env
build_env = rb_config_env
- build_env["RUBY_STATIC"] = "true" if ruby_static? && ENV.key?('RUBY_STATIC')
+ build_env["RUBY_STATIC"] = "true" if ruby_static? && ENV.key?("RUBY_STATIC")
+ cfg = "--cfg=rb_sys_gem --cfg=rubygems --cfg=rubygems_#{Gem::VERSION.tr(".", "_")}"
+ build_env["RUSTFLAGS"] = [ENV["RUSTFLAGS"], cfg].compact.join(" ")
build_env
end
- def cargo_command(cargo_dir, dest_path, args = [])
- manifest = File.join(cargo_dir, "Cargo.toml")
- cargo = ENV.fetch("CARGO", "cargo")
-
+ def cargo_command(cargo_toml, dest_path, args = [], crate_name = nil)
cmd = []
cmd += [cargo, "rustc"]
- cmd += ["--target", ENV['CARGO_BUILD_TARGET']] if ENV['CARGO_BUILD_TARGET']
+ cmd += ["--crate-type", "cdylib"]
+ cmd += ["--target", ENV["CARGO_BUILD_TARGET"]] if ENV["CARGO_BUILD_TARGET"]
cmd += ["--target-dir", dest_path]
- cmd += ["--manifest-path", manifest]
+ cmd += ["--manifest-path", cargo_toml]
cmd += ["--lib"]
cmd += ["--profile", profile.to_s]
- cmd += ["--locked"] if profile == :release
+ cmd += ["--locked"]
cmd += Gem::Command.build_args
cmd += args
cmd += ["--"]
- cmd += [*cargo_rustc_args(dest_path)]
+ cmd += [*cargo_rustc_args(dest_path, crate_name)]
cmd
end
private
+ def cargo
+ ENV.fetch("CARGO", "cargo")
+ end
+
+ # returns the directory nesting of the extension, ignoring the first part, so
+ # "ext/foo/bar/Cargo.toml" becomes "foo/bar"
+ def extension_nesting(extension)
+ parts = extension.to_s.split(Regexp.union([File::SEPARATOR, File::ALT_SEPARATOR].compact))
+
+ parts = parts.each_with_object([]) do |segment, final|
+ next if segment == "."
+ if segment == ".."
+ raise Gem::InstallError, "extension outside of gem root" if final.empty?
+ next final.pop
+ end
+ final << segment
+ end
+
+ File.join(parts[1...-1])
+ end
+
def rb_config_env
result = {}
RbConfig::CONFIG.each {|k, v| result["RBCONFIG_#{k}"] = v }
result
end
- def cargo_rustc_args(dest_dir)
+ def cargo_rustc_args(dest_dir, crate_name)
[
*linker_args,
*mkmf_libpath,
- *rustc_dynamic_linker_flags(dest_dir),
+ *rustc_dynamic_linker_flags(dest_dir, crate_name),
*rustc_lib_flags(dest_dir),
*platform_specific_rustc_args(dest_dir),
- *debug_flags,
]
end
@@ -92,6 +144,9 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
# run on one that isn't the missing libraries will cause the extension
# to fail on start.
flags += ["-C", "link-arg=-static-libgcc"]
+ elsif darwin_target?
+ # Ventura does not always have this flag enabled
+ flags += ["-C", "link-arg=-Wl,-undefined,dynamic_lookup"]
end
flags
@@ -100,14 +155,23 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
# We want to use the same linker that Ruby uses, so that the linker flags from
# mkmf work properly.
def linker_args
- # Have to handle CC="cl /nologo" on mswin
cc_flag = Shellwords.split(makefile_config("CC"))
linker = cc_flag.shift
link_args = cc_flag.flat_map {|a| ["-C", "link-arg=#{a}"] }
+ return mswin_link_args if linker == "cl"
+
["-C", "linker=#{linker}", *link_args]
end
+ def mswin_link_args
+ args = []
+ args += ["-l", makefile_config("LIBRUBYARG_SHARED").chomp(".lib")]
+ args += split_flags("LIBS").flat_map {|lib| ["-l", lib.chomp(".lib")] }
+ args += split_flags("LOCAL_LIBS").flat_map {|lib| ["-l", lib.chomp(".lib")] }
+ args
+ end
+
def libruby_args(dest_dir)
libs = makefile_config(ruby_static? ? "LIBRUBYARG_STATIC" : "LIBRUBYARG_SHARED")
raw_libs = Shellwords.split(libs)
@@ -120,42 +184,70 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
makefile_config("ENABLE_SHARED") == "no"
end
- # Ruby expects the dylib to follow a file name convention for loading
- def rename_cdylib_for_ruby_compatibility(dest_path)
- new_path = final_extension_path(dest_path)
- FileUtils.cp(cargo_dylib_path(dest_path), new_path)
- new_path
+ def cargo_dylib_path(dest_path, crate_name)
+ prefix = so_ext == "dll" ? "" : "lib"
+ path_parts = [dest_path]
+ path_parts << ENV["CARGO_BUILD_TARGET"] if ENV["CARGO_BUILD_TARGET"]
+ path_parts += ["release", "#{prefix}#{crate_name}.#{so_ext}"]
+ File.join(*path_parts)
end
- def validate_cargo_build!(dir)
- dylib_path = cargo_dylib_path(dir)
+ def cargo_crate_name(cargo_dir, manifest_path, results)
+ require "open3"
+ Gem.load_yaml
- raise DylibNotFoundError, dir unless File.exist?(dylib_path)
+ output, status =
+ begin
+ Open3.capture2e(cargo, "metadata", "--no-deps", "--format-version", "1", :chdir => cargo_dir)
+ rescue => error
+ raise Gem::InstallError, "cargo metadata failed #{error.message}"
+ end
- dylib_path
- end
+ unless status.success?
+ if Gem.configuration.really_verbose
+ puts output
+ else
+ results << output
+ end
- def final_extension_path(dest_path)
- dylib_path = cargo_dylib_path(dest_path)
- dlext_name = "#{spec.name}.#{makefile_config("DLEXT")}"
- dylib_path.gsub(File.basename(dylib_path), dlext_name)
- end
+ exit_reason =
+ if status.exited?
+ ", exit code #{status.exitstatus}"
+ elsif status.signaled?
+ ", uncaught signal #{status.termsig}"
+ end
- def cargo_dylib_path(dest_path)
- prefix = so_ext == "dll" ? "" : "lib"
- path_parts = [dest_path]
- path_parts << ENV['CARGO_BUILD_TARGET'] if ENV['CARGO_BUILD_TARGET']
- path_parts += [profile_target_directory, "#{prefix}#{cargo_crate_name}.#{so_ext}"]
- File.join(*path_parts)
+ raise Gem::InstallError, "cargo metadata failed#{exit_reason}"
+ end
+
+ # cargo metadata output is specified as json, but with the
+ # --format-version 1 option the output is compatible with YAML, so we can
+ # avoid the json dependency
+ metadata = Gem::SafeYAML.safe_load(output)
+ package = metadata["packages"].find {|pkg| normalize_path(pkg["manifest_path"]) == manifest_path }
+ unless package
+ found = metadata["packages"].map {|md| "#{md["name"]} at #{md["manifest_path"]}" }
+ raise Gem::InstallError, <<-EOF
+failed to determine cargo package name
+
+looking for: #{manifest_path}
+
+found:
+#{found.join("\n")}
+EOF
+ end
+ package["name"].tr("-", "_")
end
- def cargo_crate_name
- spec.metadata.fetch('cargo_crate_name', spec.name).tr('-', '_')
+ def normalize_path(path)
+ return path unless File::ALT_SEPARATOR
+
+ path.tr(File::ALT_SEPARATOR, File::SEPARATOR)
end
- def rustc_dynamic_linker_flags(dest_dir)
+ def rustc_dynamic_linker_flags(dest_dir, crate_name)
split_flags("DLDFLAGS")
- .map {|arg| maybe_resolve_ldflag_variable(arg, dest_dir) }
+ .map {|arg| maybe_resolve_ldflag_variable(arg, dest_dir, crate_name) }
.compact
.flat_map {|arg| ldflag_to_link_modifier(arg) }
end
@@ -189,8 +281,8 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
!!Gem::WIN_PATTERNS.find {|r| target_platform =~ r }
end
- # Interpolate substition vars in the arg (i.e. $(DEFFILE))
- def maybe_resolve_ldflag_variable(input_arg, dest_dir)
+ # Interpolate substitution vars in the arg (i.e. $(DEFFILE))
+ def maybe_resolve_ldflag_variable(input_arg, dest_dir, crate_name)
var_matches = input_arg.match(/\$\((\w+)\)/)
return input_arg unless var_matches
@@ -203,19 +295,19 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
# On windows, it is assumed that mkmf has setup an exports file for the
# extension, so we have to to create one ourselves.
when "DEFFILE"
- write_deffile(dest_dir)
+ write_deffile(dest_dir, crate_name)
else
RbConfig::CONFIG[var_name]
end
end
- def write_deffile(dest_dir)
- deffile_path = File.join(dest_dir, "#{spec.name}-#{RbConfig::CONFIG["arch"]}.def")
+ def write_deffile(dest_dir, crate_name)
+ deffile_path = File.join(dest_dir, "#{crate_name}-#{RbConfig::CONFIG["arch"]}.def")
export_prefix = makefile_config("EXPORT_PREFIX") || ""
File.open(deffile_path, "w") do |f|
f.puts "EXPORTS"
- f.puts "#{export_prefix.strip}Init_#{spec.name}"
+ f.puts "#{export_prefix.strip}Init_#{crate_name}"
end
deffile_path
@@ -250,59 +342,6 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
RbConfig.expand(val.dup)
end
- # Good balance between binary size and debugability
- def debug_flags
- return [] if profile == :dev
-
- ["-C", "debuginfo=1"]
- end
-
- # Copied from ExtConfBuilder
- def finalize_directory(dest_path, lib_dir, extension_dir)
- require "fileutils"
- require "tempfile"
-
- ext_path = final_extension_path(dest_path)
-
- begin
- tmp_dest = Dir.mktmpdir(".gem.", extension_dir)
-
- # Some versions of `mktmpdir` return absolute paths, which will break make
- # if the paths contain spaces.
- #
- # As such, we convert to a relative path.
- tmp_dest_relative = get_relative_path(tmp_dest.clone, extension_dir)
-
- full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
-
- # TODO: remove in RubyGems 4
- if Gem.install_extension_in_lib && lib_dir
- FileUtils.mkdir_p lib_dir
- FileUtils.cp_r ext_path, lib_dir, remove_destination: true
- end
-
- FileUtils::Entry_.new(full_tmp_dest).traverse do |ent|
- destent = ent.class.new(dest_path, ent.rel)
- destent.exist? || FileUtils.mv(ent.path, destent.path)
- end
- ensure
- FileUtils.rm_rf tmp_dest if tmp_dest
- end
- end
-
- def get_relative_path(path, base)
- path[0..base.length - 1] = "." if path.start_with?(base)
- path
- end
-
- def profile_target_directory
- case profile
- when :release then 'release'
- when :dev then 'debug'
- else raise "unknown target directory for profile: #{profile}"
- end
- end
-
# Error raised when no cdylib artifact was created
class DylibNotFoundError < StandardError
def initialize(dir)
diff --git a/lib/rubygems/ext/cargo_builder/link_flag_converter.rb b/lib/rubygems/ext/cargo_builder/link_flag_converter.rb
index 111bb05492..e4d196cb10 100644
--- a/lib/rubygems/ext/cargo_builder/link_flag_converter.rb
+++ b/lib/rubygems/ext/cargo_builder/link_flag_converter.rb
@@ -3,20 +3,24 @@
class Gem::Ext::CargoBuilder < Gem::Ext::Builder
# Converts Ruby link flags into something cargo understands
class LinkFlagConverter
+ FILTERED_PATTERNS = [
+ /compress-debug-sections/, # Not supported by all linkers, and not required for Rust
+ ].freeze
+
def self.convert(arg)
+ return [] if FILTERED_PATTERNS.any? {|p| p.match?(arg) }
+
case arg.chomp
when /^-L\s*(.+)$/
["-L", "native=#{$1}"]
when /^--library=(\w+\S+)$/, /^-l\s*(\w+\S+)$/
["-l", $1]
- when /^-l\s*:lib(\S+).a$/
- ["-l", "static=#{$1}"]
- when /^-l\s*:lib(\S+).(so|dylib|dll)$/
- ["-l", "dylib=#{$1}"]
+ when /^-l\s*([^:\s])+/ # -lfoo, but not -l:libfoo.a
+ ["-l", $1]
when /^-F\s*(.*)$/
["-l", "framework=#{$1}"]
else
- ["-C", "link_arg=#{arg}"]
+ ["-C", "link-args=#{arg}"]
end
end
end
diff --git a/lib/rubygems/ext/cmake_builder.rb b/lib/rubygems/ext/cmake_builder.rb
index e47cabef84..b162664784 100644
--- a/lib/rubygems/ext/cmake_builder.rb
+++ b/lib/rubygems/ext/cmake_builder.rb
@@ -2,8 +2,8 @@
class Gem::Ext::CmakeBuilder < Gem::Ext::Builder
def self.build(extension, dest_path, results, args=[], lib_dir=nil, cmake_dir=Dir.pwd)
- unless File.exist?(File.join(cmake_dir, 'Makefile'))
- require_relative '../command'
+ unless File.exist?(File.join(cmake_dir, "Makefile"))
+ require_relative "../command"
cmd = ["cmake", ".", "-DCMAKE_INSTALL_PREFIX=#{dest_path}", *Gem::Command.build_args]
run cmd, results, class_name, cmake_dir
diff --git a/lib/rubygems/ext/configure_builder.rb b/lib/rubygems/ext/configure_builder.rb
index eb2f9fce61..6b8590ba2e 100644
--- a/lib/rubygems/ext/configure_builder.rb
+++ b/lib/rubygems/ext/configure_builder.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -7,7 +8,7 @@
class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder
def self.build(extension, dest_path, results, args=[], lib_dir=nil, configure_dir=Dir.pwd)
- unless File.exist?(File.join(configure_dir, 'Makefile'))
+ unless File.exist?(File.join(configure_dir, "Makefile"))
cmd = ["sh", "./configure", "--prefix=#{dest_path}", *args]
run cmd, results, class_name, configure_dir
diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb
index 2f0183fe2f..9c22329fe3 100644
--- a/lib/rubygems/ext/ext_conf_builder.rb
+++ b/lib/rubygems/ext/ext_conf_builder.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -7,8 +8,8 @@
class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd)
- require 'fileutils'
- require 'tempfile'
+ require "fileutils"
+ require "tempfile"
tmp_dest = Dir.mktmpdir(".gem.", extension_dir)
@@ -21,17 +22,16 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
destdir = ENV["DESTDIR"]
begin
- require "shellwords"
- cmd = Gem.ruby.shellsplit << "-I" << File.expand_path('../..', __dir__) << File.basename(extension)
+ cmd = ruby << File.basename(extension)
cmd.push(*args)
run(cmd, results, class_name, extension_dir) do |s, r|
- mkmf_log = File.join(extension_dir, 'mkmf.log')
+ mkmf_log = File.join(extension_dir, "mkmf.log")
if File.exist? mkmf_log
unless s.success?
r << "To see why this extension failed to compile, please check" \
" the mkmf.log which can be found here:\n"
- r << " " + File.join(dest_path, 'mkmf.log') + "\n"
+ r << " " + File.join(dest_path, "mkmf.log") + "\n"
end
FileUtils.mv mkmf_log, dest_path
end
@@ -44,7 +44,7 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
# TODO remove in RubyGems 4
- if Gem.install_extension_in_lib and lib_dir
+ if Gem.install_extension_in_lib && lib_dir
FileUtils.mkdir_p lib_dir
entries = Dir.entries(full_tmp_dest) - %w[. ..]
entries = entries.map {|entry| File.join full_tmp_dest, entry }
@@ -53,8 +53,10 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
FileUtils::Entry_.new(full_tmp_dest).traverse do |ent|
destent = ent.class.new(dest_path, ent.rel)
- destent.exist? or FileUtils.mv(ent.path, destent.path)
+ destent.exist? || FileUtils.mv(ent.path, destent.path)
end
+
+ make dest_path, results, extension_dir, tmp_dest_relative, ["clean"]
ensure
ENV["DESTDIR"] = destdir
end
@@ -67,7 +69,7 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
private
def self.get_relative_path(path, base)
- path[0..base.length - 1] = '.' if path.start_with?(base)
+ path[0..base.length - 1] = "." if path.start_with?(base)
path
end
end
diff --git a/lib/rubygems/ext/rake_builder.rb b/lib/rubygems/ext/rake_builder.rb
index fed98e741c..8f39a63e11 100644
--- a/lib/rubygems/ext/rake_builder.rb
+++ b/lib/rubygems/ext/rake_builder.rb
@@ -1,4 +1,7 @@
# frozen_string_literal: true
+
+require_relative "../shellwords"
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -11,16 +14,15 @@ class Gem::Ext::RakeBuilder < Gem::Ext::Builder
run([Gem.ruby, File.basename(extension), *args], results, class_name, extension_dir)
end
- rake = ENV['rake']
+ rake = ENV["rake"]
if rake
- require "shellwords"
- rake = rake.shellsplit
+ rake = Shellwords.split(rake)
else
begin
- rake = [Gem.ruby, "-I#{File.expand_path("../..", __dir__)}", "-rrubygems", Gem.bin_path('rake', 'rake')]
+ rake = ruby << "-rrubygems" << Gem.bin_path("rake", "rake")
rescue Gem::Exception
- rake = [Gem.default_exec_format % 'rake']
+ rake = [Gem.default_exec_format % "rake"]
end
end
diff --git a/lib/rubygems/gem_runner.rb b/lib/rubygems/gem_runner.rb
index b3f925773b..d238a5863b 100644
--- a/lib/rubygems/gem_runner.rb
+++ b/lib/rubygems/gem_runner.rb
@@ -1,13 +1,14 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative '../rubygems'
-require_relative 'command_manager'
-require_relative 'deprecate'
+require_relative "../rubygems"
+require_relative "command_manager"
+require_relative "deprecate"
##
# Run an instance of the gem program.
@@ -41,7 +42,7 @@ class Gem::GemRunner
config_args = Gem.configuration[command_name]
config_args = case config_args
when String
- config_args.split ' '
+ config_args.split " "
else
Array(config_args)
end
@@ -56,7 +57,7 @@ class Gem::GemRunner
# other arguments in the list.
def extract_build_args(args) # :nodoc:
- return [] unless offset = args.index('--')
+ return [] unless offset = args.index("--")
build_args = args.slice!(offset...args.length)
diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb
index af0d957bc8..92739617f6 100644
--- a/lib/rubygems/gemcutter_utilities.rb
+++ b/lib/rubygems/gemcutter_utilities.rb
@@ -1,6 +1,9 @@
# frozen_string_literal: true
-require_relative 'remote_fetcher'
-require_relative 'text'
+
+require_relative "remote_fetcher"
+require_relative "text"
+require_relative "gemcutter_utilities/webauthn_listener"
+require_relative "gemcutter_utilities/webauthn_poller"
##
# Utility methods for using the RubyGems API.
@@ -19,8 +22,8 @@ module Gem::GemcutterUtilities
# Add the --key option
def add_key_option
- add_option('-k', '--key KEYNAME', Symbol,
- 'Use the given API key',
+ add_option("-k", "--key KEYNAME", Symbol,
+ "Use the given API key",
"from #{Gem.configuration.credentials_path}") do |value,options|
options[:key] = value
end
@@ -30,9 +33,9 @@ module Gem::GemcutterUtilities
# Add the --otp option
def add_otp_option
- add_option('--otp CODE',
- 'Digit code for multifactor authentication',
- 'You can also use the environment variable GEM_HOST_OTP_CODE') do |value, options|
+ add_option("--otp CODE",
+ "Digit code for multifactor authentication",
+ "You can also use the environment variable GEM_HOST_OTP_CODE") do |value, options|
options[:otp] = value
end
end
@@ -69,9 +72,8 @@ module Gem::GemcutterUtilities
@host ||=
begin
- env_rubygems_host = ENV['RUBYGEMS_HOST']
- env_rubygems_host = nil if
- env_rubygems_host and env_rubygems_host.empty?
+ env_rubygems_host = ENV["RUBYGEMS_HOST"]
+ env_rubygems_host = nil if env_rubygems_host&.empty?
env_rubygems_host || configured_host
end
@@ -82,8 +84,8 @@ module Gem::GemcutterUtilities
#
# If +allowed_push_host+ metadata is present, then it will only allow that host.
- def rubygems_api_request(method, path, host = nil, allowed_push_host = nil, scope: nil, &block)
- require 'net/http'
+ def rubygems_api_request(method, path, host = nil, allowed_push_host = nil, scope: nil, credentials: {}, &block)
+ require "net/http"
self.host = host if host
unless self.host
@@ -105,7 +107,7 @@ module Gem::GemcutterUtilities
response = request_with_otp(method, uri, &block)
if mfa_unauthorized?(response)
- ask_otp
+ fetch_otp(credentials)
response = request_with_otp(method, uri, &block)
end
@@ -118,7 +120,7 @@ module Gem::GemcutterUtilities
end
def mfa_unauthorized?(response)
- response.kind_of?(Net::HTTPUnauthorized) && response.body.start_with?('You have enabled multifactor authentication')
+ response.kind_of?(Net::HTTPUnauthorized) && response.body.start_with?("You have enabled multifactor authentication")
end
def update_scope(scope)
@@ -163,11 +165,16 @@ module Gem::GemcutterUtilities
key_name = get_key_name(scope)
scope_params = get_scope_params(scope)
- mfa_params = get_mfa_params(email, password)
+ profile = get_user_profile(email, password)
+ mfa_params = get_mfa_params(profile)
all_params = scope_params.merge(mfa_params)
+ warning = profile["warning"]
+ credentials = { email: email, password: password }
+
+ say "#{warning}\n" if warning
response = rubygems_api_request(:post, "api/v1/api_key",
- sign_in_host, scope: scope) do |request|
+ sign_in_host, credentials: credentials, scope: scope) do |request|
request.basic_auth email, password
request["OTP"] = otp if otp
request.body = URI.encode_www_form({ name: key_name }.merge(all_params))
@@ -197,7 +204,8 @@ module Gem::GemcutterUtilities
# block was given or shows the response body to the user.
#
# If the response was not successful, shows an error to the user including
- # the +error_prefix+ and the response body.
+ # the +error_prefix+ and the response body. If the response was a permanent redirect,
+ # shows an error to the user including the redirect location.
def with_response(response, error_prefix = nil)
case response
@@ -207,6 +215,12 @@ module Gem::GemcutterUtilities
else
say clean_text(response.body)
end
+ when Net::HTTPPermanentRedirect, Net::HTTPRedirection then
+ message = "The request has redirected permanently to #{response['location']}. Please check your defined push host URL."
+ message = "#{error_prefix}: #{message}" if error_prefix
+
+ say clean_text(message)
+ terminate_interaction(ERROR_CODE)
else
message = response.body
message = "#{error_prefix}: #{message}" if error_prefix
@@ -239,14 +253,57 @@ module Gem::GemcutterUtilities
end
end
- def ask_otp
- say 'You have enabled multi-factor authentication. Please enter OTP code.'
- options[:otp] = ask 'Code: '
+ def fetch_otp(credentials)
+ options[:otp] = if webauthn_url = webauthn_verification_url(credentials)
+ server = TCPServer.new 0
+ port = server.addr[1].to_s
+
+ url_with_port = "#{webauthn_url}?port=#{port}"
+ say "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option."
+
+ threads = [WebauthnListener.listener_thread(host, server), WebauthnPoller.poll_thread(options, host, webauthn_url, credentials)]
+ otp_thread = wait_for_otp_thread(*threads)
+
+ threads.each(&:join)
+
+ if error = otp_thread[:error]
+ alert_error error.message
+ terminate_interaction(1)
+ end
+
+ say "You are verified with a security device. You may close the browser window."
+ otp_thread[:otp]
+ else
+ say "You have enabled multi-factor authentication. Please enter OTP code."
+ ask "Code: "
+ end
+ end
+
+ def wait_for_otp_thread(*threads)
+ loop do
+ threads.each do |otp_thread|
+ return otp_thread unless otp_thread.alive?
+ end
+ sleep 0.1
+ end
+ ensure
+ threads.each(&:exit)
+ end
+
+ def webauthn_verification_url(credentials)
+ response = rubygems_api_request(:post, "api/v1/webauthn_verification") do |request|
+ if credentials.empty?
+ request.add_field "Authorization", api_key
+ else
+ request.basic_auth credentials[:email], credentials[:password]
+ end
+ end
+ response.is_a?(Net::HTTPSuccess) ? response.body : nil
end
def pretty_host(host)
if default_host?
- 'RubyGems.org'
+ "RubyGems.org"
else
host
end
@@ -273,27 +330,26 @@ module Gem::GemcutterUtilities
self.host == Gem::DEFAULT_HOST
end
- def get_mfa_params(email, password)
+ def get_user_profile(email, password)
return {} unless default_host?
- mfa_level = get_user_mfa_level(email, password)
- params = {}
- if mfa_level == "ui_only" || mfa_level == "ui_and_gem_signin"
- selected = ask_yes_no("Would you like to enable MFA for this key? (strongly recommended)")
- params["mfa"] = true if selected
- end
- params
- end
-
- def get_user_mfa_level(email, password)
response = rubygems_api_request(:get, "api/v1/profile/me.yaml") do |request|
request.basic_auth email, password
end
with_response response do |resp|
- body = Gem::SafeYAML.load clean_text(resp.body)
- body["mfa"]
+ Gem::SafeYAML.load clean_text(resp.body)
+ end
+ end
+
+ def get_mfa_params(profile)
+ mfa_level = profile["mfa"]
+ params = {}
+ if mfa_level == "ui_only" || mfa_level == "ui_and_gem_signin"
+ selected = ask_yes_no("Would you like to enable MFA for this key? (strongly recommended)")
+ params["mfa"] = true if selected
end
+ params
end
def get_key_name(scope)
diff --git a/lib/rubygems/gemcutter_utilities/webauthn_listener.rb b/lib/rubygems/gemcutter_utilities/webauthn_listener.rb
new file mode 100644
index 0000000000..bea9d9e397
--- /dev/null
+++ b/lib/rubygems/gemcutter_utilities/webauthn_listener.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+require_relative "webauthn_listener/response"
+
+##
+# The WebauthnListener class retrieves an OTP after a user successfully WebAuthns with the Gem host.
+# An instance opens a socket using the TCPServer instance given and listens for a request from the Gem host.
+# The request should be a GET request to the root path and contains the OTP code in the form
+# of a query parameter `code`. The listener will return the code which will be used as the OTP for
+# API requests.
+#
+# Types of responses sent by the listener after receiving a request:
+# - 200 OK: OTP code was successfully retrieved
+# - 204 No Content: If the request was an OPTIONS request
+# - 400 Bad Request: If the request did not contain a query parameter `code`
+# - 404 Not Found: The request was not to the root path
+# - 405 Method Not Allowed: OTP code was not retrieved because the request was not a GET/OPTIONS request
+#
+# Example usage:
+#
+# thread = Gem::WebauthnListener.listener_thread("https://rubygems.example", server)
+# thread.join
+# otp = thread[:otp]
+# error = thread[:error]
+#
+
+module Gem::GemcutterUtilities
+ class WebauthnListener
+ attr_reader :host
+
+ def initialize(host)
+ @host = host
+ end
+
+ def self.listener_thread(host, server)
+ Thread.new do
+ thread = Thread.current
+ thread.abort_on_exception = true
+ thread.report_on_exception = false
+ thread[:otp] = new(host).wait_for_otp_code(server)
+ rescue Gem::WebauthnVerificationError => e
+ thread[:error] = e
+ ensure
+ server.close
+ end
+ end
+
+ def wait_for_otp_code(server)
+ loop do
+ socket = server.accept
+ request_line = socket.gets
+
+ method, req_uri, _protocol = request_line.split(" ")
+ req_uri = URI.parse(req_uri)
+
+ responder = SocketResponder.new(socket)
+
+ unless root_path?(req_uri)
+ responder.send(NotFoundResponse.for(host))
+ raise Gem::WebauthnVerificationError, "Page at #{req_uri.path} not found."
+ end
+
+ case method.upcase
+ when "OPTIONS"
+ responder.send(NoContentResponse.for(host))
+ next # will be GET
+ when "GET"
+ if otp = parse_otp_from_uri(req_uri)
+ responder.send(OkResponse.for(host))
+ return otp
+ end
+ responder.send(BadRequestResponse.for(host))
+ raise Gem::WebauthnVerificationError, "Did not receive OTP from #{host}."
+ else
+ responder.send(MethodNotAllowedResponse.for(host))
+ raise Gem::WebauthnVerificationError, "Invalid HTTP method #{method.upcase} received."
+ end
+ end
+ end
+
+ private
+
+ def root_path?(uri)
+ uri.path == "/"
+ end
+
+ def parse_otp_from_uri(uri)
+ require "cgi"
+
+ return if uri.query.nil?
+ CGI.parse(uri.query).dig("code", 0)
+ end
+
+ class SocketResponder
+ def initialize(socket)
+ @socket = socket
+ end
+
+ def send(response)
+ @socket.print response.to_s
+ @socket.close
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb b/lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb
new file mode 100644
index 0000000000..7709a8fef3
--- /dev/null
+++ b/lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb
@@ -0,0 +1,163 @@
+# frozen_string_literal: true
+
+##
+# The WebauthnListener Response class is used by the WebauthnListener to create
+# responses to be sent to the Gem host. It creates a Net::HTTPResponse instance
+# when initialized and can be converted to the appropriate format to be sent by a socket using `to_s`.
+# Net::HTTPResponse instances cannot be directly sent over a socket.
+#
+# Types of response classes:
+# - OkResponse
+# - NoContentResponse
+# - BadRequestResponse
+# - NotFoundResponse
+# - MethodNotAllowedResponse
+#
+# Example usage:
+#
+# server = TCPServer.new(0)
+# socket = server.accept
+#
+# response = OkResponse.for("https://rubygems.example")
+# socket.print response.to_s
+# socket.close
+#
+
+module Gem::GemcutterUtilities
+ class WebauthnListener
+ class Response
+ attr_reader :http_response
+
+ def self.for(host)
+ new(host)
+ end
+
+ def initialize(host)
+ @host = host
+
+ build_http_response
+ end
+
+ def to_s
+ status_line = "HTTP/#{@http_response.http_version} #{@http_response.code} #{@http_response.message}\r\n"
+ headers = @http_response.to_hash.map {|header, value| "#{header}: #{value.join(", ")}\r\n" }.join + "\r\n"
+ body = @http_response.body ? "#{@http_response.body}\n" : ""
+
+ status_line + headers + body
+ end
+
+ private
+
+ # Must be implemented in subclasses
+ def code
+ raise NotImplementedError
+ end
+
+ def reason_phrase
+ raise NotImplementedError
+ end
+
+ def body; end
+
+ def build_http_response
+ response_class = Net::HTTPResponse::CODE_TO_OBJ[code.to_s]
+ @http_response = response_class.new("1.1", code, reason_phrase)
+ @http_response.instance_variable_set(:@read, true)
+
+ add_connection_header
+ add_access_control_headers
+ add_body
+ end
+
+ def add_connection_header
+ @http_response["connection"] = "close"
+ end
+
+ def add_access_control_headers
+ @http_response["access-control-allow-origin"] = @host
+ @http_response["access-control-allow-methods"] = "POST"
+ @http_response["access-control-allow-headers"] = %w[Content-Type Authorization x-csrf-token]
+ end
+
+ def add_body
+ return unless body
+ @http_response["content-type"] = "text/plain; charset=utf-8"
+ @http_response["content-length"] = body.bytesize
+ @http_response.instance_variable_set(:@body, body)
+ end
+ end
+
+ class OkResponse < Response
+ private
+
+ def code
+ 200
+ end
+
+ def reason_phrase
+ "OK"
+ end
+
+ def body
+ "success"
+ end
+ end
+
+ class NoContentResponse < Response
+ private
+
+ def code
+ 204
+ end
+
+ def reason_phrase
+ "No Content"
+ end
+ end
+
+ class BadRequestResponse < Response
+ private
+
+ def code
+ 400
+ end
+
+ def reason_phrase
+ "Bad Request"
+ end
+
+ def body
+ "missing code parameter"
+ end
+ end
+
+ class NotFoundResponse < Response
+ private
+
+ def code
+ 404
+ end
+
+ def reason_phrase
+ "Not Found"
+ end
+ end
+
+ class MethodNotAllowedResponse < Response
+ private
+
+ def code
+ 405
+ end
+
+ def reason_phrase
+ "Method Not Allowed"
+ end
+
+ def add_access_control_headers
+ super
+ @http_response["allow"] = %w[GET OPTIONS]
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/gemcutter_utilities/webauthn_poller.rb b/lib/rubygems/gemcutter_utilities/webauthn_poller.rb
new file mode 100644
index 0000000000..e7068605a4
--- /dev/null
+++ b/lib/rubygems/gemcutter_utilities/webauthn_poller.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+##
+# The WebauthnPoller class retrieves an OTP after a user successfully WebAuthns. An instance
+# polls the Gem host for the OTP code. The polling request (api/v1/webauthn_verification/<webauthn_token>/status.json)
+# is sent to the Gem host every 5 seconds and will timeout after 5 minutes. If the status field in the json response
+# is "success", the code field will contain the OTP code.
+#
+# Example usage:
+#
+# thread = Gem::WebauthnPoller.poll_thread(
+# {},
+# "RubyGems.org",
+# "https://rubygems.org/api/v1/webauthn_verification/odow34b93t6aPCdY",
+# { email: "email@example.com", password: "password" }
+# )
+# thread.join
+# otp = thread[:otp]
+# error = thread[:error]
+#
+
+module Gem::GemcutterUtilities
+ class WebauthnPoller
+ include Gem::GemcutterUtilities
+ TIMEOUT_IN_SECONDS = 300
+
+ attr_reader :options, :host
+
+ def initialize(options, host)
+ @options = options
+ @host = host
+ end
+
+ def self.poll_thread(options, host, webauthn_url, credentials)
+ Thread.new do
+ thread = Thread.current
+ thread.abort_on_exception = true
+ thread.report_on_exception = false
+ thread[:otp] = new(options, host).poll_for_otp(webauthn_url, credentials)
+ rescue Gem::WebauthnVerificationError, Timeout::Error => e
+ thread[:error] = e
+ end
+ end
+
+ def poll_for_otp(webauthn_url, credentials)
+ Timeout.timeout(TIMEOUT_IN_SECONDS) do
+ loop do
+ response = webauthn_verification_poll_response(webauthn_url, credentials)
+ raise Gem::WebauthnVerificationError, response.message unless response.is_a?(Net::HTTPSuccess)
+
+ require "json"
+ parsed_response = JSON.parse(response.body)
+ case parsed_response["status"]
+ when "pending"
+ sleep 5
+ when "success"
+ return parsed_response["code"]
+ else
+ raise Gem::WebauthnVerificationError, parsed_response.fetch("message", "Invalid response from server")
+ end
+ end
+ end
+ end
+
+ private
+
+ def webauthn_verification_poll_response(webauthn_url, credentials)
+ webauthn_token = %r{(?<=\/)[^\/]+(?=$)}.match(webauthn_url)[0]
+ rubygems_api_request(:get, "api/v1/webauthn_verification/#{webauthn_token}/status.json") do |request|
+ if credentials.empty?
+ request.add_field "Authorization", api_key
+ else
+ request.basic_auth credentials[:email], credentials[:password]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb
index a5a86f4111..0935fe8486 100644
--- a/lib/rubygems/indexer.rb
+++ b/lib/rubygems/indexer.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
-require_relative '../rubygems'
-require_relative 'package'
-require 'tmpdir'
+
+require_relative "../rubygems"
+require_relative "package"
+require "tmpdir"
##
# Top level class for building the gem repository index.
@@ -43,28 +44,28 @@ class Gem::Indexer
# Create an indexer that will index the gems in +directory+.
def initialize(directory, options = {})
- require 'fileutils'
- require 'tmpdir'
- require 'zlib'
+ require "fileutils"
+ require "tmpdir"
+ require "zlib"
options = { :build_modern => true }.merge options
@build_modern = options[:build_modern]
@dest_directory = directory
- @directory = Dir.mktmpdir 'gem_generate_index'
+ @directory = Dir.mktmpdir "gem_generate_index"
marshal_name = "Marshal.#{Gem.marshal_version}"
- @master_index = File.join @directory, 'yaml'
+ @master_index = File.join @directory, "yaml"
@marshal_index = File.join @directory, marshal_name
- @quick_dir = File.join @directory, 'quick'
+ @quick_dir = File.join @directory, "quick"
@quick_marshal_dir = File.join @quick_dir, marshal_name
@quick_marshal_dir_base = File.join "quick", marshal_name # FIX: UGH
- @quick_index = File.join @quick_dir, 'index'
- @latest_index = File.join @quick_dir, 'latest_index'
+ @quick_index = File.join @quick_dir, "index"
+ @latest_index = File.join @quick_dir, "latest_index"
@specs_index = File.join @directory, "specs.#{Gem.marshal_version}"
@latest_specs_index =
@@ -104,7 +105,7 @@ class Gem::Indexer
files = []
- Gem.time 'Generated Marshal quick index gemspecs' do
+ Gem.time "Generated Marshal quick index gemspecs" do
specs.each do |spec|
next if spec.default_gem?
spec_file_name = "#{spec.original_name}.gemspec.rz"
@@ -112,7 +113,7 @@ class Gem::Indexer
marshal_zipped = Gem.deflate Marshal.dump(spec)
- File.open marshal_name, 'wb' do |io|
+ File.open marshal_name, "wb" do |io|
io.write marshal_zipped
end
@@ -136,7 +137,7 @@ class Gem::Indexer
say "Generating #{name} index"
Gem.time "Generated #{name} index" do
- File.open(file, 'wb') do |io|
+ File.open(file, "wb") do |io|
specs = index.map do |*spec|
# We have to splat here because latest_specs is an array, while the
# others are hashes.
@@ -149,7 +150,7 @@ class Gem::Indexer
next
end
- platform = Gem::Platform::RUBY if platform.nil? or platform.empty?
+ platform = Gem::Platform::RUBY if platform.nil? || platform.empty?
[spec.name, spec.version, platform]
end
@@ -169,10 +170,10 @@ class Gem::Indexer
latest_specs =
Gem::Specification._latest_specs specs
- build_modern_index(released.sort, @specs_index, 'specs')
- build_modern_index(latest_specs.sort, @latest_specs_index, 'latest specs')
+ build_modern_index(released.sort, @specs_index, "specs")
+ build_modern_index(latest_specs.sort, @latest_specs_index, "latest specs")
build_modern_index(prerelease.sort, @prerelease_specs_index,
- 'prerelease specs')
+ "prerelease specs")
@files += [@specs_index,
"#{@specs_index}.gz",
@@ -217,7 +218,7 @@ class Gem::Indexer
def compress_indices
say "Compressing indices"
- Gem.time 'Compressed indices' do
+ Gem.time "Compressed indices" do
if @build_modern
gzip @specs_index
gzip @latest_specs_index
@@ -252,7 +253,7 @@ class Gem::Indexer
zipped = Gem.deflate data
- File.open "#{filename}.#{extension}", 'wb' do |io|
+ File.open "#{filename}.#{extension}", "wb" do |io|
io.write zipped
end
end
@@ -296,7 +297,7 @@ class Gem::Indexer
files = @files
files.delete @quick_marshal_dir if files.include? @quick_dir
- if files.include? @quick_marshal_dir and not files.include? @quick_dir
+ if files.include?(@quick_marshal_dir) && !files.include?(@quick_dir)
files.delete @quick_marshal_dir
dst_name = File.join(@dest_directory, @quick_marshal_dir_base)
@@ -308,7 +309,7 @@ class Gem::Indexer
end
files = files.map do |path|
- path.sub(/^#{Regexp.escape @directory}\/?/, '') # HACK?
+ path.sub(/^#{Regexp.escape @directory}\/?/, "") # HACK?
end
files.each do |file|
@@ -358,7 +359,7 @@ class Gem::Indexer
end
if updated_gems.empty?
- say 'No new gems'
+ say "No new gems"
terminate_interaction 0
end
@@ -367,7 +368,7 @@ class Gem::Indexer
files = build_marshal_gemspecs specs
- Gem.time 'Updated indexes' do
+ Gem.time "Updated indexes" do
update_specs_index released, @dest_specs_index, @specs_index
update_specs_index released, @dest_latest_specs_index, @latest_specs_index
update_specs_index(prerelease,
@@ -389,7 +390,7 @@ class Gem::Indexer
files << "#{@prerelease_specs_index}.gz"
files = files.map do |path|
- path.sub(/^#{Regexp.escape @directory}\/?/, '') # HACK?
+ path.sub(/^#{Regexp.escape @directory}\/?/, "") # HACK?
end
files.each do |file|
@@ -397,7 +398,7 @@ class Gem::Indexer
dst_name = File.join @dest_directory, file # REFACTOR: duped above
FileUtils.mv src_name, dst_name, :verbose => verbose,
- :force => true
+ :force => true
File.utime newest_mtime, newest_mtime, dst_name
end
@@ -414,13 +415,13 @@ class Gem::Indexer
index.each do |spec|
platform = spec.original_platform
- platform = Gem::Platform::RUBY if platform.nil? or platform.empty?
+ platform = Gem::Platform::RUBY if platform.nil? || platform.empty?
specs_index << [spec.name, spec.version, platform]
end
specs_index = compact_specs specs_index.uniq.sort
- File.open dest, 'wb' do |io|
+ File.open dest, "wb" do |io|
Marshal.dump specs_index, io
end
end
diff --git a/lib/rubygems/install_default_message.rb b/lib/rubygems/install_default_message.rb
index 052ef528e1..0640eaaf08 100644
--- a/lib/rubygems/install_default_message.rb
+++ b/lib/rubygems/install_default_message.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
-require_relative '../rubygems'
-require_relative 'user_interaction'
+
+require_relative "../rubygems"
+require_relative "user_interaction"
##
# A post-install hook that displays "Successfully installed
diff --git a/lib/rubygems/install_message.rb b/lib/rubygems/install_message.rb
index 861ead3770..a24e26b918 100644
--- a/lib/rubygems/install_message.rb
+++ b/lib/rubygems/install_message.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
-require_relative '../rubygems'
-require_relative 'user_interaction'
+
+require_relative "../rubygems"
+require_relative "user_interaction"
##
# A default post-install hook that displays "Successfully installed
diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb
index 454104435d..e125b3b9b3 100644
--- a/lib/rubygems/install_update_options.rb
+++ b/lib/rubygems/install_update_options.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative '../rubygems'
-require_relative 'security_option'
+require_relative "../rubygems"
+require_relative "security_option"
##
# Mixin methods for install and update options for Gem::Commands
@@ -18,22 +19,22 @@ module Gem::InstallUpdateOptions
# Add the install/update options to the option parser.
def add_install_update_options
- add_option(:"Install/Update", '-i', '--install-dir DIR',
- 'Gem repository directory to get installed',
- 'gems') do |value, options|
+ add_option(:"Install/Update", "-i", "--install-dir DIR",
+ "Gem repository directory to get installed",
+ "gems") do |value, options|
options[:install_dir] = File.expand_path(value)
end
- add_option(:"Install/Update", '-n', '--bindir DIR',
- 'Directory where executables will be',
- 'placed when the gem is installed') do |value, options|
+ add_option(:"Install/Update", "-n", "--bindir DIR",
+ "Directory where executables will be",
+ "placed when the gem is installed") do |value, options|
options[:bin_dir] = File.expand_path(value)
end
- add_option(:"Install/Update", '--document [TYPES]', Array,
- 'Generate documentation for installed gems',
- 'List the documentation types you wish to',
- 'generate. For example: rdoc,ri') do |value, options|
+ add_option(:"Install/Update", "--document [TYPES]", Array,
+ "Generate documentation for installed gems",
+ "List the documentation types you wish to",
+ "generate. For example: rdoc,ri") do |value, options|
options[:document] = case value
when nil then %w[ri]
when false then []
@@ -41,63 +42,63 @@ module Gem::InstallUpdateOptions
end
end
- add_option(:"Install/Update", '--build-root DIR',
- 'Temporary installation root. Useful for building',
- 'packages. Do not use this when installing remote gems.') do |value, options|
+ add_option(:"Install/Update", "--build-root DIR",
+ "Temporary installation root. Useful for building",
+ "packages. Do not use this when installing remote gems.") do |value, options|
options[:build_root] = File.expand_path(value)
end
- add_option(:"Install/Update", '--vendor',
- 'Install gem into the vendor directory.',
- 'Only for use by gem repackagers.') do |value, options|
+ add_option(:"Install/Update", "--vendor",
+ "Install gem into the vendor directory.",
+ "Only for use by gem repackagers.") do |value, options|
unless Gem.vendor_dir
- raise Gem::OptionParser::InvalidOption.new 'your platform is not supported'
+ raise Gem::OptionParser::InvalidOption.new "your platform is not supported"
end
options[:vendor] = true
options[:install_dir] = Gem.vendor_dir
end
- add_option(:"Install/Update", '-N', '--no-document',
- 'Disable documentation generation') do |value, options|
+ add_option(:"Install/Update", "-N", "--no-document",
+ "Disable documentation generation") do |value, options|
options[:document] = []
end
- add_option(:"Install/Update", '-E', '--[no-]env-shebang',
+ add_option(:"Install/Update", "-E", "--[no-]env-shebang",
"Rewrite the shebang line on installed",
"scripts to use /usr/bin/env") do |value, options|
options[:env_shebang] = value
end
- add_option(:"Install/Update", '-f', '--[no-]force',
- 'Force gem to install, bypassing dependency',
- 'checks') do |value, options|
+ add_option(:"Install/Update", "-f", "--[no-]force",
+ "Force gem to install, bypassing dependency",
+ "checks") do |value, options|
options[:force] = value
end
- add_option(:"Install/Update", '-w', '--[no-]wrappers',
- 'Use bin wrappers for executables',
- 'Not available on dosish platforms') do |value, options|
+ add_option(:"Install/Update", "-w", "--[no-]wrappers",
+ "Use bin wrappers for executables",
+ "Not available on dosish platforms") do |value, options|
options[:wrappers] = value
end
add_security_option
- add_option(:"Install/Update", '--ignore-dependencies',
- 'Do not install any required dependent gems') do |value, options|
+ add_option(:"Install/Update", "--ignore-dependencies",
+ "Do not install any required dependent gems") do |value, options|
options[:ignore_dependencies] = value
end
- add_option(:"Install/Update", '--[no-]format-executable',
- 'Make installed executable names match Ruby.',
- 'If Ruby is ruby18, foo_exec will be',
- 'foo_exec18') do |value, options|
+ add_option(:"Install/Update", "--[no-]format-executable",
+ "Make installed executable names match Ruby.",
+ "If Ruby is ruby18, foo_exec will be",
+ "foo_exec18") do |value, options|
options[:format_executable] = value
end
- add_option(:"Install/Update", '--[no-]user-install',
- 'Install in user\'s home directory instead',
- 'of GEM_HOME.') do |value, options|
+ add_option(:"Install/Update", "--[no-]user-install",
+ "Install in user's home directory instead",
+ "of GEM_HOME.") do |value, options|
options[:user_install] = value
end
@@ -133,9 +134,9 @@ module Gem::InstallUpdateOptions
options[:post_install_message] = value
end
- add_option(:"Install/Update", '-g', '--file [FILE]',
- 'Read from a gem dependencies API file and',
- 'install the listed gems') do |v,o|
+ add_option(:"Install/Update", "-g", "--file [FILE]",
+ "Read from a gem dependencies API file and",
+ "install the listed gems") do |v,o|
v = Gem::GEM_DEP_FILES.find do |file|
File.exist? file
end unless v
@@ -150,32 +151,32 @@ module Gem::InstallUpdateOptions
options[:gemdeps] = v
end
- add_option(:"Install/Update", '--without GROUPS', Array,
- 'Omit the named groups (comma separated)',
- 'when installing from a gem dependencies',
- 'file') do |v,o|
+ add_option(:"Install/Update", "--without GROUPS", Array,
+ "Omit the named groups (comma separated)",
+ "when installing from a gem dependencies",
+ "file") do |v,o|
options[:without_groups].concat v.map {|without| without.intern }
end
- add_option(:"Install/Update", '--default',
- 'Add the gem\'s full specification to',
- 'specifications/default and extract only its bin') do |v,o|
+ add_option(:"Install/Update", "--default",
+ "Add the gem's full specification to",
+ "specifications/default and extract only its bin") do |v,o|
options[:install_as_default] = v
end
- add_option(:"Install/Update", '--explain',
- 'Rather than install the gems, indicate which would',
- 'be installed') do |v,o|
+ add_option(:"Install/Update", "--explain",
+ "Rather than install the gems, indicate which would",
+ "be installed") do |v,o|
options[:explain] = v
end
- add_option(:"Install/Update", '--[no-]lock',
- 'Create a lock file (when used with -g/--file)') do |v,o|
+ add_option(:"Install/Update", "--[no-]lock",
+ "Create a lock file (when used with -g/--file)") do |v,o|
options[:lock] = v
end
- add_option(:"Install/Update", '--[no-]suggestions',
- 'Suggest alternates when gems are not found') do |v,o|
+ add_option(:"Install/Update", "--[no-]suggestions",
+ "Suggest alternates when gems are not found") do |v,o|
options[:suggest_alternate] = v
end
end
@@ -193,7 +194,7 @@ module Gem::InstallUpdateOptions
# Default description for the gem install and update commands.
def install_update_defaults_str
- '--document=ri'
+ "--document=ri"
end
end
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index 7484145467..1b152aaf1f 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -1,16 +1,17 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative 'installer_uninstaller_utils'
-require_relative 'exceptions'
-require_relative 'deprecate'
-require_relative 'package'
-require_relative 'ext'
-require_relative 'user_interaction'
+require_relative "installer_uninstaller_utils"
+require_relative "exceptions"
+require_relative "deprecate"
+require_relative "package"
+require_relative "ext"
+require_relative "user_interaction"
##
# The installer installs the files contained in the .gem into the Gem.home.
@@ -125,14 +126,14 @@ class Gem::Installer
@spec = spec
end
- def extract_files(destination_dir, pattern = '*')
+ def extract_files(destination_dir, pattern = "*")
FileUtils.mkdir_p destination_dir
spec.files.each do |file|
file = File.join destination_dir, file
next if File.exist? file
FileUtils.mkdir_p File.dirname(file)
- File.open file, 'w' do |fp|
+ File.open file, "w" do |fp|
fp.puts "# #{file}"
end
end
@@ -177,7 +178,7 @@ class Gem::Installer
# :post_install_message:: Print gem post install message if true
def initialize(package, options={})
- require 'fileutils'
+ require "fileutils"
@options = options
@package = package
@@ -219,7 +220,7 @@ class Gem::Installer
ruby_executable = false
existing = nil
- File.open generated_bin, 'rb' do |io|
+ File.open generated_bin, "rb" do |io|
line = io.gets
shebang = /^#!.*ruby/
@@ -234,8 +235,8 @@ class Gem::Installer
io.gets # blankline
# TODO detect a specially formatted comment instead of trying
- # to run a regexp against Ruby code.
- next unless io.gets =~ /This file was generated by RubyGems/
+ # to find a string inside Ruby code.
+ next unless io.gets.to_s.include?("This file was generated by RubyGems")
ruby_executable = true
existing = io.read.slice(%r{
@@ -251,12 +252,12 @@ class Gem::Installer
return if spec.name == existing
# somebody has written to RubyGems' directory, overwrite, too bad
- return if Gem.default_bindir != @bin_dir and not ruby_executable
+ return if Gem.default_bindir != @bin_dir && !ruby_executable
question = "#{spec.name}'s executable \"#{filename}\" conflicts with ".dup
if ruby_executable
- question << (existing || 'an unknown executable')
+ question << (existing || "an unknown executable")
return if ask_yes_no "#{question}\nOverwrite the executable?", false
@@ -342,6 +343,8 @@ class Gem::Installer
Gem::Specification.add_spec(spec)
+ load_plugin
+
run_post_install_hooks
spec
@@ -388,7 +391,7 @@ class Gem::Installer
# we'll be installing into.
def installed_specs
- @specs ||= begin
+ @installed_specs ||= begin
specs = []
Gem::Util.glob_files_in_dir("*.gemspec", File.join(gem_home, "specifications")).each do |path|
@@ -418,10 +421,10 @@ class Gem::Installer
# True if the gems in the system satisfy +dependency+.
def installation_satisfies_dependency?(dependency)
- return true if @options[:development] and dependency.type == :development
+ return true if @options[:development] && dependency.type == :development
return true if installed_specs.detect {|s| dependency.matches_spec? s }
return false if @only_install_dir
- not dependency.matching_specs.empty?
+ !dependency.matching_specs.empty?
end
##
@@ -474,7 +477,7 @@ class Gem::Installer
if Gem.win_platform?
script_name = formatted_program_filename(filename) + ".bat"
script_path = File.join bindir, File.basename(script_name)
- File.open script_path, 'w' do |file|
+ File.open script_path, "w" do |file|
file.puts windows_stub_script(bindir, filename)
end
@@ -483,28 +486,20 @@ class Gem::Installer
end
def generate_bin # :nodoc:
- return if spec.executables.nil? or spec.executables.empty?
+ return if spec.executables.nil? || spec.executables.empty?
ensure_writable_dir @bin_dir
spec.executables.each do |filename|
filename.tap(&Gem::UNTAINT)
bin_path = File.join gem_dir, spec.bindir, filename
-
- unless File.exist? bin_path
- if File.symlink? bin_path
- alert_warning "`#{bin_path}` is dangling symlink pointing to `#{File.readlink bin_path}`"
- else
- alert_warning "`#{bin_path}` does not exist, maybe `gem pristine #{spec.name}` will fix it?"
- end
- next
- end
+ next unless File.exist? bin_path
mode = File.stat(bin_path).mode
dir_mode = options[:prog_mode] || (mode | 0111)
unless dir_mode == mode
- require 'fileutils'
+ require "fileutils"
FileUtils.chmod dir_mode, bin_path
end
@@ -541,10 +536,10 @@ class Gem::Installer
def generate_bin_script(filename, bindir)
bin_script_path = File.join bindir, formatted_program_filename(filename)
- require 'fileutils'
+ require "fileutils"
FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
- File.open bin_script_path, 'wb', 0755 do |file|
+ File.open bin_script_path, "wb", 0755 do |file|
file.print app_script_text(filename)
file.chmod(options[:prog_mode] || 0755)
end
@@ -565,7 +560,7 @@ class Gem::Installer
if File.exist? dst
if File.symlink? dst
link = File.readlink(dst).split File::SEPARATOR
- cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ''))
+ cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ""))
return if spec.version < cur_version
end
File.unlink dst
@@ -657,9 +652,9 @@ class Gem::Installer
def process_options # :nodoc:
@options = {
- :bin_dir => nil,
- :env_shebang => false,
- :force => false,
+ :bin_dir => nil,
+ :env_shebang => false,
+ :force => false,
:only_install_dir => false,
:post_install_message => true,
}.merge options
@@ -684,9 +679,9 @@ class Gem::Installer
@build_args = options[:build_args]
unless @build_root.nil?
- @bin_dir = File.join(@build_root, @bin_dir.gsub(/^[a-zA-Z]:/, ''))
- @gem_home = File.join(@build_root, @gem_home.gsub(/^[a-zA-Z]:/, ''))
- @plugins_dir = File.join(@build_root, @plugins_dir.gsub(/^[a-zA-Z]:/, ''))
+ @bin_dir = File.join(@build_root, @bin_dir.gsub(/^[a-zA-Z]:/, ""))
+ @gem_home = File.join(@build_root, @gem_home.gsub(/^[a-zA-Z]:/, ""))
+ @plugins_dir = File.join(@build_root, @plugins_dir.gsub(/^[a-zA-Z]:/, ""))
alert_warning "You build with buildroot.\n Build root: #{@build_root}\n Bin dir: #{@bin_dir}\n Gem home: #{@gem_home}\n Plugins dir: #{@plugins_dir}"
end
end
@@ -697,7 +692,7 @@ class Gem::Installer
user_bin_dir = @bin_dir || Gem.bindir(gem_home)
user_bin_dir = user_bin_dir.tr(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
- path = ENV['PATH']
+ path = ENV["PATH"]
path = path.tr(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
if Gem.win_platform?
@@ -708,7 +703,7 @@ class Gem::Installer
path = path.split(File::PATH_SEPARATOR)
unless path.include? user_bin_dir
- unless !Gem.win_platform? && (path.include? user_bin_dir.sub(ENV['HOME'], '~'))
+ unless !Gem.win_platform? && (path.include? user_bin_dir.sub(ENV["HOME"], "~"))
alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run."
self.class.path_warning = true
end
@@ -788,7 +783,7 @@ TEXT
end
def gemdeps_load(name)
- return '' if name == "bundler"
+ return "" if name == "bundler"
<<-TEXT
@@ -824,7 +819,7 @@ TEXT
TEXT
elsif bindir.downcase.start_with? rb_topdir.downcase
# stub within ruby folder, but not standard bin. Portable
- require 'pathname'
+ require "pathname"
from = Pathname.new bindir
to = Pathname.new "#{rb_topdir}/bin"
rel = to.relative_path_from from
@@ -935,14 +930,14 @@ TEXT
def write_build_info_file
return if build_args.empty?
- build_info_dir = File.join gem_home, 'build_info'
+ build_info_dir = File.join gem_home, "build_info"
dir_mode = options[:dir_mode]
FileUtils.mkdir_p build_info_dir, :mode => dir_mode && 0755
build_info_file = File.join build_info_dir, "#{spec.full_name}.info"
- File.open build_info_file, 'w' do |io|
+ File.open build_info_file, "w" do |io|
build_args.each do |arg|
io.puts arg
end
@@ -955,7 +950,7 @@ TEXT
# Writes the .gem file to the cache directory
def write_cache_file
- cache_file = File.join gem_home, 'cache', spec.file_name
+ cache_file = File.join gem_home, "cache", spec.file_name
@package.copy_to cache_file
end
@@ -987,7 +982,7 @@ TEXT
end
def load_relative_enabled?
- rb_config["LIBRUBY_RELATIVE"] == 'yes'
+ rb_config["LIBRUBY_RELATIVE"] == "yes"
end
def bash_prolog_script
@@ -1010,4 +1005,17 @@ TEXT
""
end
end
+
+ def load_plugin
+ specs = Gem::Specification.find_all_by_name(spec.name)
+ # If old version already exists, this plugin isn't loaded
+ # immediately. It's for avoiding a case that multiple versions
+ # are loaded at the same time.
+ return unless specs.size == 1
+
+ plugin_files = spec.plugins.map do |plugin|
+ File.join(@plugins_dir, "#{spec.name}_plugin#{File.extname(plugin)}")
+ end
+ Gem.load_plugin_files(plugin_files)
+ end
end
diff --git a/lib/rubygems/installer_uninstaller_utils.rb b/lib/rubygems/installer_uninstaller_utils.rb
index 2c8b7c635e..d97b4e29b1 100644
--- a/lib/rubygems/installer_uninstaller_utils.rb
+++ b/lib/rubygems/installer_uninstaller_utils.rb
@@ -9,12 +9,12 @@ module Gem::InstallerUninstallerUtils
plugins = spec.plugins
return if plugins.empty?
- require 'pathname'
+ require "pathname"
spec.plugins.each do |plugin|
plugin_script_path = File.join plugins_dir, "#{spec.name}_plugin#{File.extname(plugin)}"
- File.open plugin_script_path, 'wb' do |file|
+ File.open plugin_script_path, "wb" do |file|
file.puts "require_relative '#{Pathname.new(plugin).relative_path_from(Pathname.new(plugins_dir))}'"
end
diff --git a/lib/rubygems/local_remote_options.rb b/lib/rubygems/local_remote_options.rb
index 6b5e08bf1c..3d970ef225 100644
--- a/lib/rubygems/local_remote_options.rb
+++ b/lib/rubygems/local_remote_options.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require 'uri'
-require_relative '../rubygems'
+require "uri"
+require_relative "../rubygems"
##
# Mixin methods for local and remote Gem::Command options.
@@ -38,18 +39,18 @@ module Gem::LocalRemoteOptions
# Add local/remote options to the command line parser.
def add_local_remote_options
- add_option(:"Local/Remote", '-l', '--local',
- 'Restrict operations to the LOCAL domain') do |value, options|
+ add_option(:"Local/Remote", "-l", "--local",
+ "Restrict operations to the LOCAL domain") do |value, options|
options[:domain] = :local
end
- add_option(:"Local/Remote", '-r', '--remote',
- 'Restrict operations to the REMOTE domain') do |value, options|
+ add_option(:"Local/Remote", "-r", "--remote",
+ "Restrict operations to the REMOTE domain") do |value, options|
options[:domain] = :remote
end
- add_option(:"Local/Remote", '-b', '--both',
- 'Allow LOCAL and REMOTE operations') do |value, options|
+ add_option(:"Local/Remote", "-b", "--both",
+ "Allow LOCAL and REMOTE operations") do |value, options|
options[:domain] = :both
end
@@ -64,7 +65,7 @@ module Gem::LocalRemoteOptions
# Add the --bulk-threshold option
def add_bulk_threshold_option
- add_option(:"Local/Remote", '-B', '--bulk-threshold COUNT',
+ add_option(:"Local/Remote", "-B", "--bulk-threshold COUNT",
"Threshold for switching to bulk",
"synchronization (default #{Gem.configuration.bulk_threshold})") do
|value, options|
@@ -76,8 +77,8 @@ module Gem::LocalRemoteOptions
# Add the --clear-sources option
def add_clear_sources_option
- add_option(:"Local/Remote", '--clear-sources',
- 'Clear the gem sources') do |value, options|
+ add_option(:"Local/Remote", "--clear-sources",
+ "Clear the gem sources") do |value, options|
Gem.sources = nil
options[:sources_cleared] = true
end
@@ -89,8 +90,8 @@ module Gem::LocalRemoteOptions
def add_proxy_option
accept_uri_http
- add_option(:"Local/Remote", '-p', '--[no-]http-proxy [URL]', URI::HTTP,
- 'Use HTTP proxy for remote operations') do |value, options|
+ add_option(:"Local/Remote", "-p", "--[no-]http-proxy [URL]", URI::HTTP,
+ "Use HTTP proxy for remote operations") do |value, options|
options[:http_proxy] = (value == false) ? :no_proxy : value
Gem.configuration[:http_proxy] = options[:http_proxy]
end
@@ -102,9 +103,9 @@ module Gem::LocalRemoteOptions
def add_source_option
accept_uri_http
- add_option(:"Local/Remote", '-s', '--source URL', URI::HTTP,
- 'Append URL to list of remote gem sources') do |source, options|
- source << '/' if source !~ /\/\z/
+ add_option(:"Local/Remote", "-s", "--source URL", URI::HTTP,
+ "Append URL to list of remote gem sources") do |source, options|
+ source << "/" if source !~ /\/\z/
if options.delete :sources_cleared
Gem.sources = [source]
@@ -118,8 +119,8 @@ module Gem::LocalRemoteOptions
# Add the --update-sources option
def add_update_sources_option
- add_option(:Deprecated, '-u', '--[no-]update-sources',
- 'Update local source cache') do |value, options|
+ add_option(:Deprecated, "-u", "--[no-]update-sources",
+ "Update local source cache") do |value, options|
Gem.configuration.update_sources = value
end
end
diff --git a/lib/rubygems/mock_gem_ui.rb b/lib/rubygems/mock_gem_ui.rb
index 914ecb9a71..2dfdc6d523 100644
--- a/lib/rubygems/mock_gem_ui.rb
+++ b/lib/rubygems/mock_gem_ui.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-require_relative 'user_interaction'
+
+require_relative "user_interaction"
##
# This Gem::StreamUI subclass records input and output to StringIO for
@@ -40,7 +41,7 @@ class Gem::MockGemUi < Gem::StreamUI
end
def initialize(input = "")
- require 'stringio'
+ require "stringio"
ins = StringIO.new input
outs = StringIO.new
errs = StringIO.new
diff --git a/lib/rubygems/name_tuple.rb b/lib/rubygems/name_tuple.rb
index c732d7f968..c26d0765e6 100644
--- a/lib/rubygems/name_tuple.rb
+++ b/lib/rubygems/name_tuple.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
#
# Represents a gem of name +name+ at +version+ of +platform+. These
@@ -10,7 +11,7 @@ class Gem::NameTuple
@version = version
unless platform.kind_of? Gem::Platform
- platform = "ruby" if !platform or platform.empty?
+ platform = "ruby" if !platform || platform.empty?
end
@platform = platform
@@ -48,7 +49,7 @@ class Gem::NameTuple
def full_name
case @platform
- when nil, 'ruby', ''
+ when nil, "ruby", ""
"#{@name}-#{@version}"
else
"#{@name}-#{@version}-#{@platform}"
@@ -102,8 +103,8 @@ class Gem::NameTuple
def ==(other)
case other
when self.class
- @name == other.name and
- @version == other.version and
+ @name == other.name &&
+ @version == other.version &&
@platform == other.platform
when Array
to_a == other
diff --git a/lib/rubygems/optparse.rb b/lib/rubygems/optparse.rb
index 65be9f6b74..6ed718423c 100644
--- a/lib/rubygems/optparse.rb
+++ b/lib/rubygems/optparse.rb
@@ -1,3 +1,3 @@
# frozen_string_literal: true
-require_relative 'optparse/lib/optparse'
+require_relative "optparse/lib/optparse"
diff --git a/lib/rubygems/optparse/lib/optparse.rb b/lib/rubygems/optparse/lib/optparse.rb
index 98865612ba..1e50bda769 100644
--- a/lib/rubygems/optparse/lib/optparse.rb
+++ b/lib/rubygems/optparse/lib/optparse.rb
@@ -425,7 +425,7 @@
# If you have any questions, file a ticket at http://bugs.ruby-lang.org.
#
class Gem::OptionParser
- Gem::OptionParser::Version = "0.2.0"
+ Gem::OptionParser::Version = "0.3.0"
# :stopdoc:
NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
@@ -765,15 +765,15 @@ class Gem::OptionParser
end
#
- # Switch that takes an argument, which does not begin with '-'.
+ # Switch that takes an argument, which does not begin with '-' or is '-'.
#
class PlacedArgument < self
#
- # Returns nil if argument is not present or begins with '-'.
+ # Returns nil if argument is not present or begins with '-' and is not '-'.
#
def parse(arg, argv, &error)
- if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
+ if !(val = arg) and (argv.empty? or /\A-./ =~ (val = argv[0]))
return nil, block, nil
end
opt = (val = parse_arg(val, &error))[1]
@@ -1148,6 +1148,7 @@ XXX
@summary_indent = indent
@default_argv = ARGV
@require_exact = false
+ @raise_unknown = true
add_officious
yield self if block_given?
end
@@ -1225,6 +1226,9 @@ XXX
# abbreviated long option as short option).
attr_accessor :require_exact
+ # Whether to raise at unknown option.
+ attr_accessor :raise_unknown
+
#
# Heading banner preceding summary.
#
@@ -1502,7 +1506,7 @@ XXX
style = notwice(default_style.guess(arg = o), style, 'style')
default_pattern, conv = search(:atype, Object) unless default_pattern
else
- desc.push(o)
+ desc.push(o) if o && !o.empty?
end
end
@@ -1639,9 +1643,11 @@ XXX
begin
sw, = complete(:long, opt, true)
if require_exact && !sw.long.include?(arg)
+ throw :terminate, arg unless raise_unknown
raise InvalidOption, arg
end
rescue ParseError
+ throw :terminate, arg unless raise_unknown
raise $!.set_option(arg, true)
end
begin
@@ -1673,6 +1679,7 @@ XXX
end
end
rescue ParseError
+ throw :terminate, arg unless raise_unknown
raise $!.set_option(arg, true)
end
begin
@@ -1862,12 +1869,7 @@ XXX
end
all_candidates.select! {|cand| cand.is_a?(String) }
checker = DidYouMean::SpellChecker.new(dictionary: all_candidates)
- suggestions = all_candidates & checker.correct(opt)
- if DidYouMean.respond_to?(:formatter)
- DidYouMean.formatter.message_for(suggestions)
- else
- "\nDid you mean? #{suggestions.join("\n ")}"
- end
+ DidYouMean.formatter.message_for(all_candidates & checker.correct(opt))
end
def candidate(word)
@@ -1908,10 +1910,13 @@ XXX
# directory ~/.options, then the basename with '.options' suffix
# under XDG and Haiku standard places.
#
- def load(filename = nil)
+ # The optional +into+ keyword argument works exactly like that accepted in
+ # method #parse.
+ #
+ def load(filename = nil, into: nil)
unless filename
basename = File.basename($0, '.*')
- return true if load(File.expand_path(basename, '~/.options')) rescue nil
+ return true if load(File.expand_path(basename, '~/.options'), into: into) rescue nil
basename << ".options"
return [
# XDG
@@ -1923,11 +1928,11 @@ XXX
'~/config/settings',
].any? {|dir|
next if !dir or dir.empty?
- load(File.expand_path(basename, dir)) rescue nil
+ load(File.expand_path(basename, dir), into: into) rescue nil
}
end
begin
- parse(*IO.readlines(filename).each {|s| s.chomp!})
+ parse(*File.readlines(filename, chomp: true), into: into)
true
rescue Errno::ENOENT, Errno::ENOTDIR
false
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index 2dd8e8c28e..f28d521bdf 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
+
#--
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
#++
require_relative "../rubygems"
-require_relative 'security'
-require_relative 'user_interaction'
+require_relative "security"
+require_relative "user_interaction"
##
# Example using a Gem::Package
@@ -68,14 +69,14 @@ class Gem::Package
class PathError < Error
def initialize(destination, destination_dir)
super "installing into parent path %s of %s is not allowed" %
- [destination, destination_dir]
+ [destination, destination_dir]
end
end
class SymlinkError < Error
def initialize(name, destination, destination_dir)
super "installing symlink '%s' pointing to parent path %s of %s is not allowed" %
- [name, destination, destination_dir]
+ [name, destination, destination_dir]
end
end
@@ -158,7 +159,7 @@ class Gem::Package
return super unless gem.present?
return super unless gem.start
- return super unless gem.start.include? 'MD5SUM ='
+ return super unless gem.start.include? "MD5SUM ="
Gem::Package::Old.new gem
end
@@ -178,9 +179,9 @@ class Gem::Package
tar = Gem::Package::TarReader.new io
tar.each_entry do |entry|
case entry.full_name
- when 'metadata' then
+ when "metadata" then
metadata = entry.read
- when 'metadata.gz' then
+ when "metadata.gz" then
metadata = Gem::Util.gunzip entry.read
end
end
@@ -193,7 +194,7 @@ class Gem::Package
# Creates a new package that will read or write to the file +gem+.
def initialize(gem, security_policy) # :notnew:
- require 'zlib'
+ require "zlib"
@gem = gem
@@ -229,7 +230,7 @@ class Gem::Package
end
end
- tar.add_file_signed 'checksums.yaml.gz', 0444, @signer do |io|
+ tar.add_file_signed "checksums.yaml.gz", 0444, @signer do |io|
gzip_to io do |gz_io|
Psych.dump checksums_by_algorithm, gz_io
end
@@ -241,7 +242,7 @@ class Gem::Package
# and adds this file to the +tar+.
def add_contents(tar) # :nodoc:
- digests = tar.add_file_signed 'data.tar.gz', 0444, @signer do |io|
+ digests = tar.add_file_signed "data.tar.gz", 0444, @signer do |io|
gzip_to io do |gz_io|
Gem::Package::TarWriter.new gz_io do |data_tar|
add_files data_tar
@@ -249,7 +250,7 @@ class Gem::Package
end
end
- @checksums['data.tar.gz'] = digests
+ @checksums["data.tar.gz"] = digests
end
##
@@ -266,7 +267,7 @@ class Gem::Package
next unless stat.file?
tar.add_file_simple file, stat.mode, stat.size do |dst_io|
- File.open file, 'rb' do |src_io|
+ File.open file, "rb" do |src_io|
dst_io.write src_io.read 16384 until src_io.eof?
end
end
@@ -277,13 +278,13 @@ class Gem::Package
# Adds the package's Gem::Specification to the +tar+ file
def add_metadata(tar) # :nodoc:
- digests = tar.add_file_signed 'metadata.gz', 0444, @signer do |io|
+ digests = tar.add_file_signed "metadata.gz", 0444, @signer do |io|
gzip_to io do |gz_io|
gz_io.write @spec.to_yaml
end
end
- @checksums['metadata.gz'] = digests
+ @checksums["metadata.gz"] = digests
end
##
@@ -335,7 +336,7 @@ EOM
gem_tar = Gem::Package::TarReader.new io
gem_tar.each do |entry|
- next unless entry.full_name == 'data.tar.gz'
+ next unless entry.full_name == "data.tar.gz"
open_tar_gz entry do |pkg_tar|
pkg_tar.each do |contents_entry|
@@ -387,7 +388,7 @@ EOM
reader = Gem::Package::TarReader.new io
reader.each do |entry|
- next unless entry.full_name == 'data.tar.gz'
+ next unless entry.full_name == "data.tar.gz"
extract_tar_gz entry, destination_dir, pattern
@@ -409,18 +410,23 @@ EOM
def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc:
directories = []
+ symlinks = []
+
open_tar_gz io do |tar|
tar.each do |entry|
- next unless File.fnmatch pattern, entry.full_name, File::FNM_DOTMATCH
+ full_name = entry.full_name
+ next unless File.fnmatch pattern, full_name, File::FNM_DOTMATCH
- destination = install_location entry.full_name, destination_dir
+ destination = install_location full_name, destination_dir
if entry.symlink?
link_target = entry.header.linkname
real_destination = link_target.start_with?("/") ? link_target : File.expand_path(link_target, File.dirname(destination))
- raise Gem::Package::SymlinkError.new(entry.full_name, real_destination, destination_dir) unless
- normalize_path(real_destination).start_with? normalize_path(destination_dir + '/')
+ raise Gem::Package::SymlinkError.new(full_name, real_destination, destination_dir) unless
+ normalize_path(real_destination).start_with? normalize_path(destination_dir + "/")
+
+ symlinks << [full_name, link_target, destination, real_destination]
end
FileUtils.rm_rf destination
@@ -439,24 +445,35 @@ EOM
directories << mkdir
end
- File.open destination, 'wb' do |out|
- out.write entry.read
+ if entry.file?
+ File.open(destination, "wb") {|out| out.write entry.read }
FileUtils.chmod file_mode(entry.header.mode), destination
- end if entry.file?
-
- File.symlink(entry.header.linkname, destination) if entry.symlink?
+ end
verbose destination
end
end
+ symlinks.each do |name, target, destination, real_destination|
+ if File.exist?(real_destination)
+ File.symlink(target, destination)
+ else
+ alert_warning "#{@spec.full_name} ships with a dangling symlink named #{name} pointing to missing #{target} file. Ignoring"
+ end
+ end
+
if dir_mode
File.chmod(dir_mode, *directories)
end
end
def file_mode(mode) # :nodoc:
- ((mode & 0111).zero? ? data_mode : prog_mode) || mode
+ ((mode & 0111).zero? ? data_mode : prog_mode) ||
+ # If we're not using one of the default modes, then we're going to fall
+ # back to the mode from the tarball. In this case we need to mask it down
+ # to fit into 2^16 bits (the maximum value for a mode in CRuby since it
+ # gets put into an unsigned short).
+ (mode & ((1 << 16) - 1))
end
##
@@ -481,13 +498,13 @@ EOM
def install_location(filename, destination_dir) # :nodoc:
raise Gem::Package::PathError.new(filename, destination_dir) if
- filename.start_with? '/'
+ filename.start_with? "/"
destination_dir = File.realpath(destination_dir)
destination = File.expand_path(filename, destination_dir)
raise Gem::Package::PathError.new(destination, destination_dir) unless
- normalize_path(destination).start_with? normalize_path(destination_dir + '/')
+ normalize_path(destination).start_with? normalize_path(destination_dir + "/")
destination.tap(&Gem::UNTAINT)
destination
@@ -506,9 +523,9 @@ EOM
def load_spec(entry) # :nodoc:
case entry.full_name
- when 'metadata' then
+ when "metadata" then
@spec = Gem::Specification.from_yaml entry.read
- when 'metadata.gz' then
+ when "metadata.gz" then
Zlib::GzipReader.wrap(entry, external_encoding: Encoding::UTF_8) do |gzio|
@spec = Gem::Specification.from_yaml gzio.read
end
@@ -532,7 +549,7 @@ EOM
def read_checksums(gem)
Gem.load_yaml
- @checksums = gem.seek 'checksums.yaml.gz' do |entry|
+ @checksums = gem.seek "checksums.yaml.gz" do |entry|
Zlib::GzipReader.wrap entry do |gz_io|
Gem::SafeYAML.safe_load gz_io.read
end
@@ -544,7 +561,7 @@ EOM
# certificate and key are not present only checksum generation is set up.
def setup_signer(signer_options: {})
- passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
+ passphrase = ENV["GEM_PRIVATE_KEY_PASSPHRASE"]
if @spec.signing_key
@signer =
Gem::Security::Signer.new(
@@ -600,8 +617,7 @@ EOM
verify_checksums @digests, @checksums
- @security_policy.verify_signatures @spec, @digests, @signatures if
- @security_policy
+ @security_policy&.verify_signatures @spec, @digests, @signatures
true
rescue Gem::Security::Exception
@@ -651,7 +667,7 @@ EOM
case file_name
when "metadata", "metadata.gz" then
load_spec entry
- when 'data.tar.gz' then
+ when "data.tar.gz" then
verify_gz entry
end
rescue
@@ -668,15 +684,15 @@ EOM
end
unless @spec
- raise Gem::Package::FormatError.new 'package metadata is missing', @gem
+ raise Gem::Package::FormatError.new "package metadata is missing", @gem
end
- unless @files.include? 'data.tar.gz'
+ unless @files.include? "data.tar.gz"
raise Gem::Package::FormatError.new \
- 'package content (data.tar.gz) is missing', @gem
+ "package content (data.tar.gz) is missing", @gem
end
- if duplicates = @files.group_by {|f| f }.select {|k,v| v.size > 1 }.map(&:first) and duplicates.any?
+ if (duplicates = @files.group_by {|f| f }.select {|k,v| v.size > 1 }.map(&:first)) && duplicates.any?
raise Gem::Security::Exception, "duplicate files in the package: (#{duplicates.map(&:inspect).join(', ')})"
end
end
@@ -693,12 +709,12 @@ EOM
end
end
-require_relative 'package/digest_io'
-require_relative 'package/source'
-require_relative 'package/file_source'
-require_relative 'package/io_source'
-require_relative 'package/old'
-require_relative 'package/tar_header'
-require_relative 'package/tar_reader'
-require_relative 'package/tar_reader/entry'
-require_relative 'package/tar_writer'
+require_relative "package/digest_io"
+require_relative "package/source"
+require_relative "package/file_source"
+require_relative "package/io_source"
+require_relative "package/old"
+require_relative "package/tar_header"
+require_relative "package/tar_reader"
+require_relative "package/tar_reader/entry"
+require_relative "package/tar_writer"
diff --git a/lib/rubygems/package/digest_io.rb b/lib/rubygems/package/digest_io.rb
index 4736f76d93..1572cb9f2c 100644
--- a/lib/rubygems/package/digest_io.rb
+++ b/lib/rubygems/package/digest_io.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# IO wrapper that creates digests of contents written to the IO it wraps.
diff --git a/lib/rubygems/package/file_source.rb b/lib/rubygems/package/file_source.rb
index 114a950c77..d9717e0f2a 100644
--- a/lib/rubygems/package/file_source.rb
+++ b/lib/rubygems/package/file_source.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The primary source of gems is a file on disk, including all usages
# internal to rubygems.
@@ -22,10 +23,10 @@ class Gem::Package::FileSource < Gem::Package::Source # :nodoc: all
end
def with_write_io(&block)
- File.open path, 'wb', &block
+ File.open path, "wb", &block
end
def with_read_io(&block)
- File.open path, 'rb', &block
+ File.open path, "rb", &block
end
end
diff --git a/lib/rubygems/package/io_source.rb b/lib/rubygems/package/io_source.rb
index 03d7714524..227835dfce 100644
--- a/lib/rubygems/package/io_source.rb
+++ b/lib/rubygems/package/io_source.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Supports reading and writing gems from/to a generic IO object. This is
# useful for other applications built on top of rubygems, such as
diff --git a/lib/rubygems/package/old.rb b/lib/rubygems/package/old.rb
index 301d403411..bf0ed61b0a 100644
--- a/lib/rubygems/package/old.rb
+++ b/lib/rubygems/package/old.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -19,8 +20,8 @@ class Gem::Package::Old < Gem::Package
# cannot be written.
def initialize(gem, security_policy)
- require 'fileutils'
- require 'zlib'
+ require "fileutils"
+ require "zlib"
Gem.load_yaml
@contents = nil
@@ -41,7 +42,7 @@ class Gem::Package::Old < Gem::Package
read_until_dashes io # spec
header = file_list io
- @contents = header.map {|file| file['path'] }
+ @contents = header.map {|file| file["path"] }
end
end
@@ -59,7 +60,7 @@ class Gem::Package::Old < Gem::Package
raise Gem::Exception, errstr unless header
header.each do |entry|
- full_name = entry['path']
+ full_name = entry["path"]
destination = install_location full_name, destination_dir
@@ -73,13 +74,13 @@ class Gem::Package::Old < Gem::Package
file_data = Zlib::Inflate.inflate file_data
raise Gem::Package::FormatError, "#{full_name} in #{@gem} is corrupt" if
- file_data.length != entry['size'].to_i
+ file_data.length != entry["size"].to_i
FileUtils.rm_rf destination
FileUtils.mkdir_p File.dirname(destination), :mode => dir_mode && 0755
- File.open destination, 'wb', file_mode(entry['mode']) do |out|
+ File.open destination, "wb", file_mode(entry["mode"]) do |out|
out.write file_data
end
@@ -119,7 +120,7 @@ class Gem::Package::Old < Gem::Package
loop do
line = io.gets
- return if line.chomp == '__END__'
+ return if line.chomp == "__END__"
break unless line
end
@@ -160,7 +161,7 @@ class Gem::Package::Old < Gem::Package
return true unless @security_policy
raise Gem::Security::Exception,
- 'old format gems do not contain signatures and cannot be verified' if
+ "old format gems do not contain signatures and cannot be verified" if
@security_policy.verify_data
true
diff --git a/lib/rubygems/package/source.rb b/lib/rubygems/package/source.rb
index 69701e55e9..8c44f8c305 100644
--- a/lib/rubygems/package/source.rb
+++ b/lib/rubygems/package/source.rb
@@ -1,3 +1,4 @@
# frozen_string_literal: true
+
class Gem::Package::Source # :nodoc:
end
diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb
index ce9b49e3eb..99bd755f90 100644
--- a/lib/rubygems/package/tar_header.rb
+++ b/lib/rubygems/package/tar_header.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
@@ -53,42 +54,42 @@ class Gem::Package::TarHeader
##
# Pack format for a tar header
- PACK_FORMAT = 'a100' + # name
- 'a8' + # mode
- 'a8' + # uid
- 'a8' + # gid
- 'a12' + # size
- 'a12' + # mtime
- 'a7a' + # chksum
- 'a' + # typeflag
- 'a100' + # linkname
- 'a6' + # magic
- 'a2' + # version
- 'a32' + # uname
- 'a32' + # gname
- 'a8' + # devmajor
- 'a8' + # devminor
- 'a155' # prefix
+ PACK_FORMAT = "a100" + # name
+ "a8" + # mode
+ "a8" + # uid
+ "a8" + # gid
+ "a12" + # size
+ "a12" + # mtime
+ "a7a" + # chksum
+ "a" + # typeflag
+ "a100" + # linkname
+ "a6" + # magic
+ "a2" + # version
+ "a32" + # uname
+ "a32" + # gname
+ "a8" + # devmajor
+ "a8" + # devminor
+ "a155" # prefix
##
# Unpack format for a tar header
- UNPACK_FORMAT = 'A100' + # name
- 'A8' + # mode
- 'A8' + # uid
- 'A8' + # gid
- 'A12' + # size
- 'A12' + # mtime
- 'A8' + # checksum
- 'A' + # typeflag
- 'A100' + # linkname
- 'A6' + # magic
- 'A2' + # version
- 'A32' + # uname
- 'A32' + # gname
- 'A8' + # devmajor
- 'A8' + # devminor
- 'A155' # prefix
+ UNPACK_FORMAT = "A100" + # name
+ "A8" + # mode
+ "A8" + # uid
+ "A8" + # gid
+ "A12" + # size
+ "A12" + # mtime
+ "A8" + # checksum
+ "A" + # typeflag
+ "A100" + # linkname
+ "A6" + # magic
+ "A2" + # version
+ "A32" + # uname
+ "A32" + # gname
+ "A8" + # devmajor
+ "A8" + # devminor
+ "A155" # prefix
attr_reader(*FIELDS)
@@ -103,22 +104,22 @@ class Gem::Package::TarHeader
fields = header.unpack UNPACK_FORMAT
- new :name => fields.shift,
- :mode => strict_oct(fields.shift),
- :uid => oct_or_256based(fields.shift),
- :gid => oct_or_256based(fields.shift),
- :size => strict_oct(fields.shift),
- :mtime => strict_oct(fields.shift),
+ new :name => fields.shift,
+ :mode => strict_oct(fields.shift),
+ :uid => oct_or_256based(fields.shift),
+ :gid => oct_or_256based(fields.shift),
+ :size => strict_oct(fields.shift),
+ :mtime => strict_oct(fields.shift),
:checksum => strict_oct(fields.shift),
:typeflag => fields.shift,
:linkname => fields.shift,
- :magic => fields.shift,
- :version => strict_oct(fields.shift),
- :uname => fields.shift,
- :gname => fields.shift,
+ :magic => fields.shift,
+ :version => strict_oct(fields.shift),
+ :uname => fields.shift,
+ :gname => fields.shift,
:devmajor => strict_oct(fields.shift),
:devminor => strict_oct(fields.shift),
- :prefix => fields.shift,
+ :prefix => fields.shift,
:empty => empty
end
@@ -134,7 +135,7 @@ class Gem::Package::TarHeader
# \ff flags a negative 256-based number
# In case we have a match, parse it as a signed binary value
# in big-endian order, except that the high-order bit is ignored.
- return str.unpack('N2').last if str =~ /\A[\x80\xff]/n
+ return str.unpack("N2").last if str =~ /\A[\x80\xff]/n
strict_oct(str)
end
@@ -173,23 +174,23 @@ class Gem::Package::TarHeader
end
def ==(other) # :nodoc:
- self.class === other and
- @checksum == other.checksum and
- @devmajor == other.devmajor and
- @devminor == other.devminor and
- @gid == other.gid and
- @gname == other.gname and
- @linkname == other.linkname and
- @magic == other.magic and
- @mode == other.mode and
- @mtime == other.mtime and
- @name == other.name and
- @prefix == other.prefix and
- @size == other.size and
- @typeflag == other.typeflag and
- @uid == other.uid and
- @uname == other.uname and
- @version == other.version
+ self.class === other &&
+ @checksum == other.checksum &&
+ @devmajor == other.devmajor &&
+ @devminor == other.devminor &&
+ @gid == other.gid &&
+ @gname == other.gname &&
+ @linkname == other.linkname &&
+ @magic == other.magic &&
+ @mode == other.mode &&
+ @mtime == other.mtime &&
+ @name == other.name &&
+ @prefix == other.prefix &&
+ @size == other.size &&
+ @typeflag == other.typeflag &&
+ @uid == other.uid &&
+ @uname == other.uname &&
+ @version == other.version
end
def to_s # :nodoc:
@@ -208,7 +209,7 @@ class Gem::Package::TarHeader
private
def calculate_checksum(header)
- header.unpack("C*").inject {|a, b| a + b }
+ header.sum(0)
end
def header(checksum = @checksum)
diff --git a/lib/rubygems/package/tar_reader.rb b/lib/rubygems/package/tar_reader.rb
index 41121f3bfb..a8dd39c572 100644
--- a/lib/rubygems/package/tar_reader.rb
+++ b/lib/rubygems/package/tar_reader.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
@@ -53,39 +54,11 @@ class Gem::Package::TarReader
def each
return enum_for __method__ unless block_given?
- use_seek = @io.respond_to?(:seek)
-
until @io.eof? do
header = Gem::Package::TarHeader.from @io
return if header.empty?
-
entry = Gem::Package::TarReader::Entry.new header, @io
- size = entry.header.size
-
yield entry
-
- skip = (512 - (size % 512)) % 512
- pending = size - entry.bytes_read
-
- if use_seek
- begin
- # avoid reading if the @io supports seeking
- @io.seek pending, IO::SEEK_CUR
- pending = 0
- rescue Errno::EINVAL
- end
- end
-
- # if seeking isn't supported or failed
- while pending > 0 do
- bytes_read = @io.read([pending, 4096].min).size
- raise UnexpectedEOF if @io.eof?
- pending -= bytes_read
- end
-
- @io.read skip # discard trailing zeros
-
- # make sure nobody can use #read, #getc or #rewind anymore
entry.close
end
end
@@ -121,4 +94,4 @@ class Gem::Package::TarReader
end
end
-require_relative 'tar_reader/entry'
+require_relative "tar_reader/entry"
diff --git a/lib/rubygems/package/tar_reader/entry.rb b/lib/rubygems/package/tar_reader/entry.rb
index 5865599d3a..e770079f41 100644
--- a/lib/rubygems/package/tar_reader/entry.rb
+++ b/lib/rubygems/package/tar_reader/entry.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#++
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
@@ -9,6 +10,20 @@
class Gem::Package::TarReader::Entry
##
+ # Creates a new tar entry for +header+ that will be read from +io+
+ # If a block is given, the entry is yielded and then closed.
+
+ def self.open(header, io, &block)
+ entry = new header, io
+ return entry unless block_given?
+ begin
+ yield entry
+ ensure
+ entry.close
+ end
+ end
+
+ ##
# Header for this tar entry
attr_reader :header
@@ -21,6 +36,7 @@ class Gem::Package::TarReader::Entry
@header = header
@io = io
@orig_pos = @io.pos
+ @end_pos = @orig_pos + @header.size
@read = 0
end
@@ -39,7 +55,14 @@ class Gem::Package::TarReader::Entry
# Closes the tar entry
def close
+ return if closed?
+ # Seek to the end of the entry if it wasn't fully read
+ seek(0, IO::SEEK_END)
+ # discard trailing zeros
+ skip = (512 - (@header.size % 512)) % 512
+ @io.read(skip)
@closed = true
+ nil
end
##
@@ -68,9 +91,9 @@ class Gem::Package::TarReader::Entry
@header.name
end
rescue ArgumentError => e
- raise unless e.message == 'string contains null byte'
+ raise unless e.message == "string contains null byte"
raise Gem::Package::TarInvalidError,
- 'tar is corrupt, name contains null byte'
+ "tar is corrupt, name contains null byte"
end
##
@@ -117,6 +140,14 @@ class Gem::Package::TarReader::Entry
bytes_read
end
+ ##
+ # Seek to the position in the tar entry
+
+ def pos=(new_pos)
+ seek(new_pos, IO::SEEK_SET)
+ new_pos
+ end
+
def size
@header.size
end
@@ -130,9 +161,10 @@ class Gem::Package::TarReader::Entry
def read(len = nil)
check_closed
- return nil if @read >= @header.size
-
len ||= @header.size - @read
+
+ return nil if len > 0 && @read >= @header.size
+
max_read = [len, @header.size - @read].min
ret = @io.read max_read
@@ -144,9 +176,10 @@ class Gem::Package::TarReader::Entry
def readpartial(maxlen = nil, outbuf = "".b)
check_closed
- raise EOFError if @read >= @header.size
-
maxlen ||= @header.size - @read
+
+ raise EOFError if maxlen > 0 && @read >= @header.size
+
max_read = [maxlen, @header.size - @read].min
@io.readpartial(max_read, outbuf)
@@ -156,12 +189,61 @@ class Gem::Package::TarReader::Entry
end
##
+ # Seeks to +offset+ bytes into the tar file entry
+ # +whence+ can be IO::SEEK_SET, IO::SEEK_CUR, or IO::SEEK_END
+
+ def seek(offset, whence = IO::SEEK_SET)
+ check_closed
+
+ new_pos =
+ case whence
+ when IO::SEEK_SET then @orig_pos + offset
+ when IO::SEEK_CUR then @io.pos + offset
+ when IO::SEEK_END then @end_pos + offset
+ else
+ raise ArgumentError, "invalid whence"
+ end
+
+ if new_pos < @orig_pos
+ new_pos = @orig_pos
+ elsif new_pos > @end_pos
+ new_pos = @end_pos
+ end
+
+ pending = new_pos - @io.pos
+
+ if @io.respond_to?(:seek)
+ begin
+ # avoid reading if the @io supports seeking
+ @io.seek new_pos, IO::SEEK_SET
+ pending = 0
+ rescue Errno::EINVAL
+ end
+ end
+
+ # if seeking isn't supported or failed
+ # negative seek requires that we rewind and read
+ if pending < 0
+ @io.rewind
+ pending = new_pos
+ end
+
+ while pending > 0 do
+ size_read = @io.read([pending, 4096].min).size
+ raise UnexpectedEOF if @io.eof?
+ pending -= size_read
+ end
+
+ @read = @io.pos - @orig_pos
+
+ 0
+ end
+
+ ##
# Rewinds to the beginning of the tar file entry
def rewind
check_closed
-
- @io.pos = @orig_pos
- @read = 0
+ seek(0, IO::SEEK_SET)
end
end
diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb
index 877cc167c9..32a13e9162 100644
--- a/lib/rubygems/package/tar_writer.rb
+++ b/lib/rubygems/package/tar_writer.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
@@ -166,7 +167,7 @@ class Gem::Package::TarWriter
def add_file_signed(name, mode, signer)
digest_algorithms = [
signer.digest_algorithm,
- Gem::Security.create_digest('SHA512'),
+ Gem::Security.create_digest("SHA512"),
].compact.uniq
digests = add_file_digest name, mode, digest_algorithms do |io|
@@ -304,17 +305,17 @@ class Gem::Package::TarWriter
raise Gem::Package::TooLongFileName.new("File \"#{name}\" has a too long path (should be 256 or less)")
end
- prefix = ''
+ prefix = ""
if name.bytesize > 100
- parts = name.split('/', -1) # parts are never empty here
+ parts = name.split("/", -1) # parts are never empty here
name = parts.pop # initially empty for names with a trailing slash ("foo/.../bar/")
- prefix = parts.join('/') # if empty, then it's impossible to split (parts is empty too)
+ prefix = parts.join("/") # if empty, then it's impossible to split (parts is empty too)
while !parts.empty? && (prefix.bytesize > 155 || name.empty?)
- name = parts.pop + '/' + name
- prefix = parts.join('/')
+ name = parts.pop + "/" + name
+ prefix = parts.join("/")
end
- if name.bytesize > 100 or prefix.empty?
+ if name.bytesize > 100 || prefix.empty?
raise Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long name (should be 100 or less)")
end
diff --git a/lib/rubygems/package_task.rb b/lib/rubygems/package_task.rb
index bb48616b0e..a67d8cb916 100644
--- a/lib/rubygems/package_task.rb
+++ b/lib/rubygems/package_task.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
# Copyright (c) 2003, 2004 Jim Weirich, 2009 Eric Hodel
#
# Permission is hereby granted, free of charge, to any person obtaining
@@ -20,9 +21,9 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-require_relative '../rubygems'
-require_relative 'package'
-require 'rake/packagetask'
+require_relative "../rubygems"
+require_relative "package"
+require "rake/packagetask"
##
# Create a package based upon a Gem::Specification. Gem packages, as well as
@@ -113,7 +114,7 @@ class Gem::PackageTask < Rake::PackageTask
Gem::Package.build gem_spec
verbose trace do
- mv gem_file, '..'
+ mv gem_file, ".."
end
end
end
diff --git a/lib/rubygems/path_support.rb b/lib/rubygems/path_support.rb
index d601e653c9..d9df543ad9 100644
--- a/lib/rubygems/path_support.rb
+++ b/lib/rubygems/path_support.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
#
# Gem::PathSupport facilitates the GEM_HOME and GEM_PATH environment settings
diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb
index 8fcabf164d..b721629b78 100644
--- a/lib/rubygems/platform.rb
+++ b/lib/rubygems/platform.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "deprecate"
##
@@ -12,7 +13,7 @@ class Gem::Platform
attr_accessor :cpu, :os, :version
def self.local
- arch = RbConfig::CONFIG['arch']
+ arch = RbConfig::CONFIG["arch"]
arch = "#{arch}_60" if arch =~ /mswin(?:32|64)$/
@local ||= new(arch)
end
@@ -22,10 +23,11 @@ class Gem::Platform
end
def self.match_platforms?(platform, platforms)
+ platform = Gem::Platform.new(platform) unless platform.is_a?(Gem::Platform)
platforms.any? do |local_platform|
- platform.nil? or
- local_platform == platform or
- (local_platform != Gem::Platform::RUBY and local_platform =~ platform)
+ platform.nil? ||
+ local_platform == platform ||
+ (local_platform != Gem::Platform::RUBY && platform =~ local_platform)
end
end
private_class_method :match_platforms?
@@ -56,7 +58,7 @@ class Gem::Platform
case arch
when Gem::Platform::CURRENT then
Gem::Platform.local
- when Gem::Platform::RUBY, nil, '' then
+ when Gem::Platform::RUBY, nil, "" then
Gem::Platform::RUBY
else
super
@@ -68,9 +70,9 @@ class Gem::Platform
when Array then
@cpu, @os, @version = arch
when String then
- arch = arch.split '-'
+ arch = arch.split "-"
- if arch.length > 2 and arch.last !~ /\d/ # reassemble x86-linux-gnu
+ if arch.length > 2 && arch.last !~ /\d+(\.\d+)?$/ # reassemble x86-linux-{libc}
extra = arch.pop
arch.last << "-#{extra}"
end
@@ -78,11 +80,11 @@ class Gem::Platform
cpu = arch.shift
@cpu = case cpu
- when /i\d86/ then 'x86'
+ when /i\d86/ then "x86"
else cpu
end
- if arch.length == 2 and arch.last =~ /^\d+(\.\d+)?$/ # for command-line
+ if arch.length == 2 && arch.last =~ /^\d+(\.\d+)?$/ # for command-line
@os, @version = arch
return
end
@@ -91,31 +93,29 @@ class Gem::Platform
@cpu, os = nil, cpu if os.nil? # legacy jruby
@os, @version = case os
- when /aix(\d+)?/ then [ 'aix', $1 ]
- when /cygwin/ then [ 'cygwin', nil ]
- when /darwin(\d+)?/ then [ 'darwin', $1 ]
- when /^macruby$/ then [ 'macruby', nil ]
- when /freebsd(\d+)?/ then [ 'freebsd', $1 ]
- when /hpux(\d+)?/ then [ 'hpux', $1 ]
- when /^java$/, /^jruby$/ then [ 'java', nil ]
- when /^java([\d.]*)/ then [ 'java', $1 ]
- when /^dalvik(\d+)?$/ then [ 'dalvik', $1 ]
- when /^dotnet$/ then [ 'dotnet', nil ]
- when /^dotnet([\d.]*)/ then [ 'dotnet', $1 ]
- when /linux-?((?!gnu)\w+)?/ then [ 'linux', $1 ]
- when /mingw32/ then [ 'mingw32', nil ]
- when /mingw-?(\w+)?/ then [ 'mingw', $1 ]
+ when /aix(\d+)?/ then [ "aix", $1 ]
+ when /cygwin/ then [ "cygwin", nil ]
+ when /darwin(\d+)?/ then [ "darwin", $1 ]
+ when /^macruby$/ then [ "macruby", nil ]
+ when /freebsd(\d+)?/ then [ "freebsd", $1 ]
+ when /^java$/, /^jruby$/ then [ "java", nil ]
+ when /^java([\d.]*)/ then [ "java", $1 ]
+ when /^dalvik(\d+)?$/ then [ "dalvik", $1 ]
+ when /^dotnet$/ then [ "dotnet", nil ]
+ when /^dotnet([\d.]*)/ then [ "dotnet", $1 ]
+ when /linux-?(\w+)?/ then [ "linux", $1 ]
+ when /mingw32/ then [ "mingw32", nil ]
+ when /mingw-?(\w+)?/ then [ "mingw", $1 ]
when /(mswin\d+)(\_(\d+))?/ then
os, version = $1, $3
- @cpu = 'x86' if @cpu.nil? and os =~ /32$/
+ @cpu = "x86" if @cpu.nil? && os =~ /32$/
[os, version]
- when /netbsdelf/ then [ 'netbsdelf', nil ]
- when /openbsd(\d+\.\d+)?/ then [ 'openbsd', $1 ]
- when /bitrig(\d+\.\d+)?/ then [ 'bitrig', $1 ]
- when /solaris(\d+\.\d+)?/ then [ 'solaris', $1 ]
+ when /netbsdelf/ then [ "netbsdelf", nil ]
+ when /openbsd(\d+\.\d+)?/ then [ "openbsd", $1 ]
+ when /solaris(\d+\.\d+)?/ then [ "solaris", $1 ]
# test
when /^(\w+_platform)(\d+)?/ then [ $1, $2 ]
- else [ 'unknown', nil ]
+ else [ "unknown", nil ]
end
when Gem::Platform then
@cpu = arch.cpu
@@ -131,7 +131,7 @@ class Gem::Platform
end
def to_s
- to_a.compact.join '-'
+ to_a.compact.join "-"
end
##
@@ -139,7 +139,7 @@ class Gem::Platform
# the same CPU, OS and version.
def ==(other)
- self.class === other and to_a == other.to_a
+ self.class === other && to_a == other.to_a
end
alias :eql? :==
@@ -151,27 +151,53 @@ class Gem::Platform
##
# Does +other+ match this platform? Two platforms match if they have the
# same CPU, or either has a CPU of 'universal', they have the same OS, and
- # they have the same version, or either has no version.
+ # they have the same version, or either one has no version
#
# Additionally, the platform will match if the local CPU is 'arm' and the
# other CPU starts with "arm" (for generic ARM family support).
+ #
+ # Of note, this method is not commutative. Indeed the OS 'linux' has a
+ # special case: the version is the libc name, yet while "no version" stands
+ # as a wildcard for a binary gem platform (as for other OSes), for the
+ # runtime platform "no version" stands for 'gnu'. To be able to distinguish
+ # these, the method receiver is the gem platform, while the argument is
+ # the runtime platform.
+ #
+ #--
+ # NOTE: Until it can be removed, changes to this method must also be reflected in `bundler/lib/bundler/rubygems_ext.rb`
def ===(other)
return nil unless Gem::Platform === other
# universal-mingw32 matches x64-mingw-ucrt
- return true if (@cpu == 'universal' or other.cpu == 'universal') and
- @os.start_with?('mingw') and other.os.start_with?('mingw')
+ return true if (@cpu == "universal" || other.cpu == "universal") &&
+ @os.start_with?("mingw") && other.os.start_with?("mingw")
# cpu
- ([nil,'universal'].include?(@cpu) or [nil, 'universal'].include?(other.cpu) or @cpu == other.cpu or
- (@cpu == 'arm' and other.cpu.start_with?("arm"))) and
+ ([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu ||
+ (@cpu == "arm" && other.cpu.start_with?("arm"))) &&
+
+ # os
+ @os == other.os &&
+
+ # version
+ (
+ (@os != "linux" && (@version.nil? || other.version.nil?)) ||
+ (@os == "linux" && (normalized_linux_version == other.normalized_linux_version || ["musl#{@version}", "musleabi#{@version}", "musleabihf#{@version}"].include?(other.version))) ||
+ @version == other.version
+ )
+ end
+
+ #--
+ # NOTE: Until it can be removed, changes to this method must also be reflected in `bundler/lib/bundler/rubygems_ext.rb`
+
+ def normalized_linux_version
+ return nil unless @version
- # os
- @os == other.os and
+ without_gnu_nor_abi_modifiers = @version.sub(/\Agnu/, "").sub(/eabi(hf)?\Z/, "")
+ return nil if without_gnu_nor_abi_modifiers.empty?
- # version
- (@version.nil? or other.version.nil? or @version == other.version)
+ without_gnu_nor_abi_modifiers
end
##
@@ -184,17 +210,17 @@ class Gem::Platform
when String then
# This data is from http://gems.rubyforge.org/gems/yaml on 19 Aug 2007
other = case other
- when /^i686-darwin(\d)/ then ['x86', 'darwin', $1 ]
- when /^i\d86-linux/ then ['x86', 'linux', nil ]
- when 'java', 'jruby' then [nil, 'java', nil ]
- when /^dalvik(\d+)?$/ then [nil, 'dalvik', $1 ]
- when /dotnet(\-(\d+\.\d+))?/ then ['universal','dotnet', $2 ]
- when /mswin32(\_(\d+))?/ then ['x86', 'mswin32', $2 ]
- when /mswin64(\_(\d+))?/ then ['x64', 'mswin64', $2 ]
- when 'powerpc-darwin' then ['powerpc', 'darwin', nil ]
- when /powerpc-darwin(\d)/ then ['powerpc', 'darwin', $1 ]
- when /sparc-solaris2.8/ then ['sparc', 'solaris', '2.8' ]
- when /universal-darwin(\d)/ then ['universal', 'darwin', $1 ]
+ when /^i686-darwin(\d)/ then ["x86", "darwin", $1 ]
+ when /^i\d86-linux/ then ["x86", "linux", nil ]
+ when "java", "jruby" then [nil, "java", nil ]
+ when /^dalvik(\d+)?$/ then [nil, "dalvik", $1 ]
+ when /dotnet(\-(\d+\.\d+))?/ then ["universal","dotnet", $2 ]
+ when /mswin32(\_(\d+))?/ then ["x86", "mswin32", $2 ]
+ when /mswin64(\_(\d+))?/ then ["x64", "mswin64", $2 ]
+ when "powerpc-darwin" then ["powerpc", "darwin", nil ]
+ when /powerpc-darwin(\d)/ then ["powerpc", "darwin", $1 ]
+ when /sparc-solaris2.8/ then ["sparc", "solaris", "2.8" ]
+ when /universal-darwin(\d)/ then ["universal", "darwin", $1 ]
else other
end
@@ -210,11 +236,11 @@ class Gem::Platform
# A pure-Ruby gem that may use Gem::Specification#extensions to build
# binary files.
- RUBY = 'ruby'.freeze
+ RUBY = "ruby"
##
# A platform-specific gem that is built for the packaging Ruby's platform.
# This will be replaced with Gem::Platform::local.
- CURRENT = 'current'.freeze
+ CURRENT = "current"
end
diff --git a/lib/rubygems/psych_tree.rb b/lib/rubygems/psych_tree.rb
index 6f399a289e..2d478c94d9 100644
--- a/lib/rubygems/psych_tree.rb
+++ b/lib/rubygems/psych_tree.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem
if defined? ::Psych::Visitors
class NoAliasYAMLTree < Psych::Visitors::YAMLTree
@@ -7,7 +8,7 @@ module Gem
end unless respond_to? :create
def visit_String(str)
- return super unless str == '=' # or whatever you want
+ return super unless str == "=" # or whatever you want
quote = Psych::Nodes::Scalar::SINGLE_QUOTED
@emitter.scalar str, nil, nil, false, true, quote
diff --git a/lib/rubygems/query_utils.rb b/lib/rubygems/query_utils.rb
index e0c71c43cb..c72955f83b 100644
--- a/lib/rubygems/query_utils.rb
+++ b/lib/rubygems/query_utils.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
-require_relative 'local_remote_options'
-require_relative 'spec_fetcher'
-require_relative 'version_option'
-require_relative 'text'
+require_relative "local_remote_options"
+require_relative "spec_fetcher"
+require_relative "version_option"
+require_relative "text"
module Gem::QueryUtils
@@ -12,41 +12,41 @@ module Gem::QueryUtils
include Gem::VersionOption
def add_query_options
- add_option('-i', '--[no-]installed',
- 'Check for installed gem') do |value, options|
+ add_option("-i", "--[no-]installed",
+ "Check for installed gem") do |value, options|
options[:installed] = value
end
- add_option('-I', 'Equivalent to --no-installed') do |value, options|
+ add_option("-I", "Equivalent to --no-installed") do |value, options|
options[:installed] = false
end
add_version_option command, "for use with --installed"
- add_option('-d', '--[no-]details',
- 'Display detailed information of gem(s)') do |value, options|
+ add_option("-d", "--[no-]details",
+ "Display detailed information of gem(s)") do |value, options|
options[:details] = value
end
- add_option('--[no-]versions',
- 'Display only gem names') do |value, options|
+ add_option("--[no-]versions",
+ "Display only gem names") do |value, options|
options[:versions] = value
options[:details] = false unless value
end
- add_option('-a', '--all',
- 'Display all gem versions') do |value, options|
+ add_option("-a", "--all",
+ "Display all gem versions") do |value, options|
options[:all] = value
end
- add_option('-e', '--exact',
- 'Name of gem(s) to query on matches the',
- 'provided STRING') do |value, options|
+ add_option("-e", "--exact",
+ "Name of gem(s) to query on matches the",
+ "provided STRING") do |value, options|
options[:exact] = value
end
- add_option('--[no-]prerelease',
- 'Display prerelease versions') do |value, options|
+ add_option("--[no-]prerelease",
+ "Display prerelease versions") do |value, options|
options[:prerelease] = value
end
@@ -112,7 +112,7 @@ module Gem::QueryUtils
end
def display_header(type)
- if (ui.outs.tty? and Gem.configuration.verbose) or both?
+ if (ui.outs.tty? && Gem.configuration.verbose) || both?
say
say "*** #{type} GEMS ***"
say
@@ -132,7 +132,7 @@ module Gem::QueryUtils
name_matches = name ? s.name =~ name : true
version_matches = show_prereleases? || !s.version.prerelease?
- name_matches and version_matches
+ name_matches && version_matches
end
spec_tuples = specs.map do |spec|
@@ -151,7 +151,7 @@ module Gem::QueryUtils
fetcher.detect(specs_type) { true }
else
fetcher.detect(specs_type) do |name_tuple|
- name === name_tuple.name
+ name === name_tuple.name && options[:version].satisfied_by?(name_tuple.version)
end
end
@@ -159,7 +159,7 @@ module Gem::QueryUtils
end
def specs_type
- if options[:all]
+ if options[:all] || options[:version].specific?
if options[:prerelease]
:complete
else
@@ -176,7 +176,7 @@ module Gem::QueryUtils
# Check if gem +name+ version +version+ is installed.
def installed?(name, req = Gem::Requirement.default)
- Gem::Specification.any? {|s| s.name =~ name and req =~ s.version }
+ Gem::Specification.any? {|s| s.name =~ name && req =~ s.version }
end
def output_query_results(spec_tuples)
@@ -242,7 +242,7 @@ module Gem::QueryUtils
return unless options[:versions]
list =
- if platforms.empty? or options[:details]
+ if platforms.empty? || options[:details]
name_tuples.map {|n| n.version }.uniq
else
platforms.sort.reverse.map do |version, pls|
@@ -257,7 +257,7 @@ module Gem::QueryUtils
if pls != [Gem::Platform::RUBY]
platform_list = [pls.delete(Gem::Platform::RUBY), *pls.sort].compact
- out = platform_list.unshift(out).join(' ')
+ out = platform_list.unshift(out).join(" ")
end
out
@@ -284,21 +284,21 @@ module Gem::QueryUtils
def spec_authors(entry, spec)
authors = "Author#{spec.authors.length > 1 ? 's' : ''}: ".dup
- authors << spec.authors.join(', ')
+ authors << spec.authors.join(", ")
entry << format_text(authors, 68, 4)
end
def spec_homepage(entry, spec)
- return if spec.homepage.nil? or spec.homepage.empty?
+ return if spec.homepage.nil? || spec.homepage.empty?
entry << "\n" << format_text("Homepage: #{spec.homepage}", 68, 4)
end
def spec_license(entry, spec)
- return if spec.license.nil? or spec.license.empty?
+ return if spec.license.nil? || spec.license.empty?
licenses = "License#{spec.licenses.length > 1 ? 's' : ''}: ".dup
- licenses << spec.licenses.join(', ')
+ licenses << spec.licenses.join(", ")
entry << "\n" << format_text(licenses, 68, 4)
end
@@ -306,15 +306,15 @@ module Gem::QueryUtils
return unless spec.loaded_from
if specs.length == 1
- default = spec.default_gem? ? ' (default)' : nil
+ default = spec.default_gem? ? " (default)" : nil
entry << "\n" << " Installed at#{default}: #{spec.base_dir}"
else
- label = 'Installed at'
+ label = "Installed at"
specs.each do |s|
version = s.version.to_s
- version << ', default' if s.default_gem?
+ version << ", default" if s.default_gem?
entry << "\n" << " #{label} (#{version}): #{s.base_dir}"
- label = ' ' * label.length
+ label = " " * label.length
end
end
end
@@ -327,7 +327,7 @@ module Gem::QueryUtils
return unless non_ruby
if platforms.length == 1
- title = platforms.values.length == 1 ? 'Platform' : 'Platforms'
+ title = platforms.values.length == 1 ? "Platform" : "Platforms"
entry << " #{title}: #{platforms.values.sort.join(', ')}\n"
else
entry << " Platforms:\n"
@@ -336,7 +336,7 @@ module Gem::QueryUtils
sorted_platforms.each do |version, pls|
label = " #{version}: "
- data = format_text pls.sort.join(', '), 68, label.length
+ data = format_text pls.sort.join(", "), 68, label.length
data[0, label.length] = label
entry << data << "\n"
end
diff --git a/lib/rubygems/rdoc.rb b/lib/rubygems/rdoc.rb
index ac5e8f0822..907dcd9431 100644
--- a/lib/rubygems/rdoc.rb
+++ b/lib/rubygems/rdoc.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
-require_relative '../rubygems'
+
+require_relative "../rubygems"
begin
- require 'rdoc/rubygems_hook'
+ require "rdoc/rubygems_hook"
module Gem
RDoc = ::RDoc::RubygemsHook
end
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index b8f9f90cee..1c8a441d0c 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -1,11 +1,12 @@
# frozen_string_literal: true
-require_relative '../rubygems'
-require_relative 'request'
-require_relative 'request/connection_pools'
-require_relative 's3_uri_signer'
-require_relative 'uri_formatter'
-require_relative 'uri'
-require_relative 'user_interaction'
+
+require_relative "../rubygems"
+require_relative "request"
+require_relative "request/connection_pools"
+require_relative "s3_uri_signer"
+require_relative "uri_formatter"
+require_relative "uri"
+require_relative "user_interaction"
##
# RemoteFetcher handles the details of fetching gems and gem information from
@@ -72,10 +73,10 @@ class Gem::RemoteFetcher
# fetching the gem.
def initialize(proxy=nil, dns=nil, headers={})
- require_relative 'core_ext/tcpsocket_init' if Gem.configuration.ipv4_fallback_enabled
- require 'net/http'
- require 'stringio'
- require 'uri'
+ require_relative "core_ext/tcpsocket_init" if Gem.configuration.ipv4_fallback_enabled
+ require "net/http"
+ require "stringio"
+ require "uri"
Socket.do_not_reverse_lookup = true
@@ -114,7 +115,7 @@ class Gem::RemoteFetcher
cache_dir =
if Dir.pwd == install_dir # see fetch_command
install_dir
- elsif File.writable?(install_cache_dir) || (File.writable?(install_dir) && (not File.exist?(install_cache_dir)))
+ elsif File.writable?(install_cache_dir) || (File.writable?(install_dir) && (!File.exist?(install_cache_dir)))
install_cache_dir
else
File.join Gem.user_dir, "cache"
@@ -136,7 +137,7 @@ class Gem::RemoteFetcher
# REFACTOR: split this up and dispatch on scheme (eg download_http)
# REFACTOR: be sure to clean up fake fetcher when you do this... cleaner
case scheme
- when 'http', 'https', 's3' then
+ when "http", "https", "s3" then
unless File.exist? local_gem_path
begin
verbose "Downloading gem #{gem_file_name}"
@@ -156,12 +157,12 @@ class Gem::RemoteFetcher
self.cache_update_path remote_gem_path, local_gem_path
end
end
- when 'file' then
+ when "file" then
begin
path = source_uri.path
- path = File.dirname(path) if File.extname(path) == '.gem'
+ path = File.dirname(path) if File.extname(path) == ".gem"
- remote_gem_path = Gem::Util.correct_for_windows_path(File.join(path, 'gems', gem_file_name))
+ remote_gem_path = Gem::Util.correct_for_windows_path(File.join(path, "gems", gem_file_name))
FileUtils.cp(remote_gem_path, local_gem_path)
rescue Errno::EACCES
@@ -171,7 +172,7 @@ class Gem::RemoteFetcher
verbose "Using local gem #{local_gem_path}"
when nil then # TODO test for local overriding cache
source_path = if Gem.win_platform? && source_uri.scheme &&
- !source_uri.path.include?(':')
+ !source_uri.path.include?(":")
"#{source_uri.scheme}:#{source_uri.path}"
else
source_uri.path
@@ -216,9 +217,9 @@ class Gem::RemoteFetcher
head ? response : response.body
when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
Net::HTTPTemporaryRedirect then
- raise FetchError.new('too many redirects', uri) if depth > 10
+ raise FetchError.new("too many redirects", uri) if depth > 10
- unless location = response['Location']
+ unless location = response["Location"]
raise FetchError.new("redirecting but no redirect location was given", uri)
end
location = Gem::Uri.new location
@@ -247,7 +248,7 @@ class Gem::RemoteFetcher
data = send "fetch_#{uri.scheme}", uri, mtime, head
- if data and !head and uri.to_s.end_with?(".gz")
+ if data && !head && uri.to_s.end_with?(".gz")
begin
data = Gem::Util.gunzip data
rescue Zlib::GzipFile::Error
@@ -288,7 +289,7 @@ class Gem::RemoteFetcher
return Gem.read_binary(path)
end
- if update and path
+ if update && path
Gem.write_binary(path, data)
end
@@ -312,7 +313,7 @@ class Gem::RemoteFetcher
end
def https?(uri)
- uri.scheme.downcase == 'https'
+ uri.scheme.downcase == "https"
end
def close_all
diff --git a/lib/rubygems/request.rb b/lib/rubygems/request.rb
index 6d6c2d8de8..8ea39d9358 100644
--- a/lib/rubygems/request.rb
+++ b/lib/rubygems/request.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
-require 'net/http'
-require_relative 'user_interaction'
+
+require "net/http"
+require_relative "user_interaction"
class Gem::Request
extend Gem::UserInteraction
@@ -44,7 +45,7 @@ class Gem::Request
end
def self.configure_connection_for_https(connection, cert_files)
- raise Gem::Exception.new('OpenSSL is not available. Install OpenSSL and rebuild Ruby (preferred) or use non-HTTPS sources') unless Gem::HAVE_OPENSSL
+ raise Gem::Exception.new("OpenSSL is not available. Install OpenSSL and rebuild Ruby (preferred) or use non-HTTPS sources") unless Gem::HAVE_OPENSSL
connection.use_ssl = true
connection.verify_mode =
@@ -96,10 +97,10 @@ class Gem::Request
return unless cert
case error_number
when OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED then
- require 'time'
+ require "time"
"Certificate #{cert.subject} expired at #{cert.not_after.iso8601}"
when OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID then
- require 'time'
+ require "time"
"Certificate #{cert.subject} not valid until #{cert.not_before.iso8601}"
when OpenSSL::X509::V_ERR_CERT_REJECTED then
"Certificate #{cert.subject} is rejected"
@@ -140,13 +141,13 @@ class Gem::Request
Gem::UriFormatter.new(@uri.password).unescape
end
- request.add_field 'User-Agent', @user_agent
- request.add_field 'Connection', 'keep-alive'
- request.add_field 'Keep-Alive', '30'
+ request.add_field "User-Agent", @user_agent
+ request.add_field "Connection", "keep-alive"
+ request.add_field "Keep-Alive", "30"
if @last_modified
- require 'time'
- request.add_field 'If-Modified-Since', @last_modified.httpdate
+ require "time"
+ request.add_field "If-Modified-Since", @last_modified.httpdate
end
yield request if block_given?
@@ -158,7 +159,7 @@ class Gem::Request
# Returns a proxy URI for the given +scheme+ if one is set in the
# environment variables.
- def self.get_proxy_from_env(scheme = 'http')
+ def self.get_proxy_from_env(scheme = "http")
_scheme = scheme.downcase
_SCHEME = scheme.upcase
env_proxy = ENV["#{_scheme}_proxy"] || ENV["#{_SCHEME}_PROXY"]
@@ -166,14 +167,14 @@ class Gem::Request
no_env_proxy = env_proxy.nil? || env_proxy.empty?
if no_env_proxy
- return (_scheme == 'https' || _scheme == 'http') ?
- :no_proxy : get_proxy_from_env('http')
+ return (_scheme == "https" || _scheme == "http") ?
+ :no_proxy : get_proxy_from_env("http")
end
require "uri"
uri = URI(Gem::UriFormatter.new(env_proxy).normalize)
- if uri and uri.user.nil? and uri.password.nil?
+ if uri && uri.user.nil? && uri.password.nil?
user = ENV["#{_scheme}_proxy_user"] || ENV["#{_SCHEME}_PROXY_USER"]
password = ENV["#{_scheme}_proxy_pass"] || ENV["#{_SCHEME}_PROXY_PASS"]
@@ -229,14 +230,14 @@ class Gem::Request
reset connection
- raise Gem::RemoteFetcher::FetchError.new('too many bad responses', @uri) if bad_response
+ raise Gem::RemoteFetcher::FetchError.new("too many bad responses", @uri) if bad_response
bad_response = true
retry
rescue Net::HTTPFatalError
verbose "fatal error"
- raise Gem::RemoteFetcher::FetchError.new('fatal error', @uri)
+ raise Gem::RemoteFetcher::FetchError.new("fatal error", @uri)
# HACK work around EOFError bug in Net::HTTP
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
# to install gems.
@@ -246,7 +247,7 @@ class Gem::Request
requests = @requests[connection.object_id]
verbose "connection reset after #{requests} requests, retrying"
- raise Gem::RemoteFetcher::FetchError.new('too many connection resets', @uri) if retried
+ raise Gem::RemoteFetcher::FetchError.new("too many connection resets", @uri) if retried
reset connection
@@ -273,7 +274,7 @@ class Gem::Request
ua = "RubyGems/#{Gem::VERSION} #{Gem::Platform.local}".dup
ruby_version = RUBY_VERSION
- ruby_version += 'dev' if RUBY_PATCHLEVEL == -1
+ ruby_version += "dev" if RUBY_PATCHLEVEL == -1
ua << " Ruby/#{ruby_version} (#{RUBY_RELEASE_DATE}"
if RUBY_PATCHLEVEL >= 0
@@ -283,12 +284,12 @@ class Gem::Request
end
ua << ")"
- ua << " #{RUBY_ENGINE}" if RUBY_ENGINE != 'ruby'
+ ua << " #{RUBY_ENGINE}" if RUBY_ENGINE != "ruby"
ua
end
end
-require_relative 'request/http_pool'
-require_relative 'request/https_pool'
-require_relative 'request/connection_pools'
+require_relative "request/http_pool"
+require_relative "request/https_pool"
+require_relative "request/connection_pools"
diff --git a/lib/rubygems/request/connection_pools.rb b/lib/rubygems/request/connection_pools.rb
index a4c2929b38..44280489fb 100644
--- a/lib/rubygems/request/connection_pools.rb
+++ b/lib/rubygems/request/connection_pools.rb
@@ -37,15 +37,15 @@ class Gem::Request::ConnectionPools # :nodoc:
# Returns list of no_proxy entries (if any) from the environment
def get_no_proxy_from_env
- env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
+ env_no_proxy = ENV["no_proxy"] || ENV["NO_PROXY"]
- return [] if env_no_proxy.nil? or env_no_proxy.empty?
+ return [] if env_no_proxy.nil? || env_no_proxy.empty?
env_no_proxy.split(/\s*,\s*/)
end
def https?(uri)
- uri.scheme.downcase == 'https'
+ uri.scheme.downcase == "https"
end
def no_proxy?(host, env_no_proxy)
@@ -78,7 +78,7 @@ class Gem::Request::ConnectionPools # :nodoc:
no_proxy = get_no_proxy_from_env
- if proxy_uri and not no_proxy?(hostname, no_proxy)
+ if proxy_uri && !no_proxy?(hostname, no_proxy)
proxy_hostname = proxy_uri.respond_to?(:hostname) ? proxy_uri.hostname : proxy_uri.host
net_http_args + [
proxy_hostname,
diff --git a/lib/rubygems/request/http_pool.rb b/lib/rubygems/request/http_pool.rb
index f028516db8..52543de41f 100644
--- a/lib/rubygems/request/http_pool.rb
+++ b/lib/rubygems/request/http_pool.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A connection "pool" that only manages one connection for now. Provides
# thread safe `checkout` and `checkin` methods. The pool consists of one
@@ -26,7 +27,7 @@ class Gem::Request::HTTPPool # :nodoc:
def close_all
until @queue.empty?
- if connection = @queue.pop(true) and connection.started?
+ if (connection = @queue.pop(true)) && connection.started?
connection.finish
end
end
diff --git a/lib/rubygems/request/https_pool.rb b/lib/rubygems/request/https_pool.rb
index 50f42d9e0d..cb1d4b59b6 100644
--- a/lib/rubygems/request/https_pool.rb
+++ b/lib/rubygems/request/https_pool.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
class Gem::Request::HTTPSPool < Gem::Request::HTTPPool # :nodoc:
private
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index 01b01599a8..b6e0995726 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-require_relative 'tsort'
+
+require_relative "tsort"
##
# A RequestSet groups a request to activate a set of dependencies.
@@ -107,7 +108,7 @@ class Gem::RequestSet
@requests = []
@sets = []
@soft_missing = false
- @sorted = nil
+ @sorted_requests = nil
@specs = nil
@vendor_set = nil
@source_set = nil
@@ -254,7 +255,7 @@ class Gem::RequestSet
end
def install_into(dir, force = true, options = {})
- gem_home, ENV['GEM_HOME'] = ENV['GEM_HOME'], dir
+ gem_home, ENV["GEM_HOME"] = ENV["GEM_HOME"], dir
existing = force ? [] : specs_in(dir)
existing.delete_if {|s| @always_install.include? s }
@@ -287,7 +288,7 @@ class Gem::RequestSet
installed
ensure
- ENV['GEM_HOME'] = gem_home
+ ENV["GEM_HOME"] = gem_home
end
##
@@ -337,32 +338,32 @@ class Gem::RequestSet
end
def pretty_print(q) # :nodoc:
- q.group 2, '[RequestSet:', ']' do
+ q.group 2, "[RequestSet:", "]" do
q.breakable
if @remote
- q.text 'remote'
+ q.text "remote"
q.breakable
end
if @prerelease
- q.text 'prerelease'
+ q.text "prerelease"
q.breakable
end
if @development_shallow
- q.text 'shallow development'
+ q.text "shallow development"
q.breakable
elsif @development
- q.text 'development'
+ q.text "development"
q.breakable
end
if @soft_missing
- q.text 'soft missing'
+ q.text "soft missing"
end
- q.group 2, '[dependencies:', ']' do
+ q.group 2, "[dependencies:", "]" do
q.breakable
@dependencies.map do |dep|
q.text dep.to_s
@@ -371,7 +372,7 @@ class Gem::RequestSet
end
q.breakable
- q.text 'sets:'
+ q.text "sets:"
q.breakable
q.pp @sets.map {|set| set.class }
@@ -424,7 +425,7 @@ class Gem::RequestSet
end
def sorted_requests
- @sorted ||= strongly_connected_components.flatten
+ @sorted_requests ||= strongly_connected_components.flatten
end
def specs
@@ -443,14 +444,14 @@ class Gem::RequestSet
def tsort_each_child(node) # :nodoc:
node.spec.dependencies.each do |dep|
- next if dep.type == :development and not @development
+ next if dep.type == :development && !@development
match = @requests.find do |r|
- dep.match? r.spec.name, r.spec.version, @prerelease
+ dep.match? r.spec.name, r.spec.version, r.spec.is_a?(Gem::Resolver::InstalledSpecification) || @prerelease
end
unless match
- next if dep.type == :development and @development_shallow
+ next if dep.type == :development && @development_shallow
next if @soft_missing
raise Gem::DependencyError,
"Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})"
@@ -461,6 +462,6 @@ class Gem::RequestSet
end
end
-require_relative 'request_set/gem_dependency_api'
-require_relative 'request_set/lockfile'
-require_relative 'request_set/lockfile/tokenizer'
+require_relative "request_set/gem_dependency_api"
+require_relative "request_set/lockfile"
+require_relative "request_set/lockfile/tokenizer"
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb
index 7188b07346..2fd0da340a 100644
--- a/lib/rubygems/request_set/gem_dependency_api.rb
+++ b/lib/rubygems/request_set/gem_dependency_api.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A semi-compatible DSL for the Bundler Gemfile and Isolate gem dependencies
# files.
@@ -32,134 +33,134 @@
class Gem::RequestSet::GemDependencyAPI
ENGINE_MAP = { # :nodoc:
- :jruby => %w[jruby],
- :jruby_18 => %w[jruby],
- :jruby_19 => %w[jruby],
- :maglev => %w[maglev],
- :mri => %w[ruby],
- :mri_18 => %w[ruby],
- :mri_19 => %w[ruby],
- :mri_20 => %w[ruby],
- :mri_21 => %w[ruby],
- :rbx => %w[rbx],
- :truffleruby => %w[truffleruby],
- :ruby => %w[ruby rbx maglev truffleruby],
- :ruby_18 => %w[ruby rbx maglev truffleruby],
- :ruby_19 => %w[ruby rbx maglev truffleruby],
- :ruby_20 => %w[ruby rbx maglev truffleruby],
- :ruby_21 => %w[ruby rbx maglev truffleruby],
+ :jruby => %w[jruby],
+ :jruby_18 => %w[jruby],
+ :jruby_19 => %w[jruby],
+ :maglev => %w[maglev],
+ :mri => %w[ruby],
+ :mri_18 => %w[ruby],
+ :mri_19 => %w[ruby],
+ :mri_20 => %w[ruby],
+ :mri_21 => %w[ruby],
+ :rbx => %w[rbx],
+ :truffleruby => %w[truffleruby],
+ :ruby => %w[ruby rbx maglev truffleruby],
+ :ruby_18 => %w[ruby rbx maglev truffleruby],
+ :ruby_19 => %w[ruby rbx maglev truffleruby],
+ :ruby_20 => %w[ruby rbx maglev truffleruby],
+ :ruby_21 => %w[ruby rbx maglev truffleruby],
}.freeze
- mswin = Gem::Platform.new 'x86-mswin32'
- mswin64 = Gem::Platform.new 'x64-mswin64'
- x86_mingw = Gem::Platform.new 'x86-mingw32'
- x64_mingw = Gem::Platform.new 'x64-mingw32'
+ mswin = Gem::Platform.new "x86-mswin32"
+ mswin64 = Gem::Platform.new "x64-mswin64"
+ x86_mingw = Gem::Platform.new "x86-mingw32"
+ x64_mingw = Gem::Platform.new "x64-mingw32"
PLATFORM_MAP = { # :nodoc:
- :jruby => Gem::Platform::RUBY,
- :jruby_18 => Gem::Platform::RUBY,
- :jruby_19 => Gem::Platform::RUBY,
- :maglev => Gem::Platform::RUBY,
- :mingw => x86_mingw,
- :mingw_18 => x86_mingw,
- :mingw_19 => x86_mingw,
- :mingw_20 => x86_mingw,
- :mingw_21 => x86_mingw,
- :mri => Gem::Platform::RUBY,
- :mri_18 => Gem::Platform::RUBY,
- :mri_19 => Gem::Platform::RUBY,
- :mri_20 => Gem::Platform::RUBY,
- :mri_21 => Gem::Platform::RUBY,
- :mswin => mswin,
- :mswin_18 => mswin,
- :mswin_19 => mswin,
- :mswin_20 => mswin,
- :mswin_21 => mswin,
- :mswin64 => mswin64,
- :mswin64_19 => mswin64,
- :mswin64_20 => mswin64,
- :mswin64_21 => mswin64,
- :rbx => Gem::Platform::RUBY,
- :ruby => Gem::Platform::RUBY,
- :ruby_18 => Gem::Platform::RUBY,
- :ruby_19 => Gem::Platform::RUBY,
- :ruby_20 => Gem::Platform::RUBY,
- :ruby_21 => Gem::Platform::RUBY,
- :truffleruby => Gem::Platform::RUBY,
- :x64_mingw => x64_mingw,
+ :jruby => Gem::Platform::RUBY,
+ :jruby_18 => Gem::Platform::RUBY,
+ :jruby_19 => Gem::Platform::RUBY,
+ :maglev => Gem::Platform::RUBY,
+ :mingw => x86_mingw,
+ :mingw_18 => x86_mingw,
+ :mingw_19 => x86_mingw,
+ :mingw_20 => x86_mingw,
+ :mingw_21 => x86_mingw,
+ :mri => Gem::Platform::RUBY,
+ :mri_18 => Gem::Platform::RUBY,
+ :mri_19 => Gem::Platform::RUBY,
+ :mri_20 => Gem::Platform::RUBY,
+ :mri_21 => Gem::Platform::RUBY,
+ :mswin => mswin,
+ :mswin_18 => mswin,
+ :mswin_19 => mswin,
+ :mswin_20 => mswin,
+ :mswin_21 => mswin,
+ :mswin64 => mswin64,
+ :mswin64_19 => mswin64,
+ :mswin64_20 => mswin64,
+ :mswin64_21 => mswin64,
+ :rbx => Gem::Platform::RUBY,
+ :ruby => Gem::Platform::RUBY,
+ :ruby_18 => Gem::Platform::RUBY,
+ :ruby_19 => Gem::Platform::RUBY,
+ :ruby_20 => Gem::Platform::RUBY,
+ :ruby_21 => Gem::Platform::RUBY,
+ :truffleruby => Gem::Platform::RUBY,
+ :x64_mingw => x64_mingw,
:x64_mingw_20 => x64_mingw,
:x64_mingw_21 => x64_mingw,
}.freeze
- gt_eq_0 = Gem::Requirement.new '>= 0'
- tilde_gt_1_8_0 = Gem::Requirement.new '~> 1.8.0'
- tilde_gt_1_9_0 = Gem::Requirement.new '~> 1.9.0'
- tilde_gt_2_0_0 = Gem::Requirement.new '~> 2.0.0'
- tilde_gt_2_1_0 = Gem::Requirement.new '~> 2.1.0'
+ gt_eq_0 = Gem::Requirement.new ">= 0"
+ tilde_gt_1_8_0 = Gem::Requirement.new "~> 1.8.0"
+ tilde_gt_1_9_0 = Gem::Requirement.new "~> 1.9.0"
+ tilde_gt_2_0_0 = Gem::Requirement.new "~> 2.0.0"
+ tilde_gt_2_1_0 = Gem::Requirement.new "~> 2.1.0"
VERSION_MAP = { # :nodoc:
- :jruby => gt_eq_0,
- :jruby_18 => tilde_gt_1_8_0,
- :jruby_19 => tilde_gt_1_9_0,
- :maglev => gt_eq_0,
- :mingw => gt_eq_0,
- :mingw_18 => tilde_gt_1_8_0,
- :mingw_19 => tilde_gt_1_9_0,
- :mingw_20 => tilde_gt_2_0_0,
- :mingw_21 => tilde_gt_2_1_0,
- :mri => gt_eq_0,
- :mri_18 => tilde_gt_1_8_0,
- :mri_19 => tilde_gt_1_9_0,
- :mri_20 => tilde_gt_2_0_0,
- :mri_21 => tilde_gt_2_1_0,
- :mswin => gt_eq_0,
- :mswin_18 => tilde_gt_1_8_0,
- :mswin_19 => tilde_gt_1_9_0,
- :mswin_20 => tilde_gt_2_0_0,
- :mswin_21 => tilde_gt_2_1_0,
- :mswin64 => gt_eq_0,
- :mswin64_19 => tilde_gt_1_9_0,
- :mswin64_20 => tilde_gt_2_0_0,
- :mswin64_21 => tilde_gt_2_1_0,
- :rbx => gt_eq_0,
- :ruby => gt_eq_0,
- :ruby_18 => tilde_gt_1_8_0,
- :ruby_19 => tilde_gt_1_9_0,
- :ruby_20 => tilde_gt_2_0_0,
- :ruby_21 => tilde_gt_2_1_0,
- :truffleruby => gt_eq_0,
- :x64_mingw => gt_eq_0,
+ :jruby => gt_eq_0,
+ :jruby_18 => tilde_gt_1_8_0,
+ :jruby_19 => tilde_gt_1_9_0,
+ :maglev => gt_eq_0,
+ :mingw => gt_eq_0,
+ :mingw_18 => tilde_gt_1_8_0,
+ :mingw_19 => tilde_gt_1_9_0,
+ :mingw_20 => tilde_gt_2_0_0,
+ :mingw_21 => tilde_gt_2_1_0,
+ :mri => gt_eq_0,
+ :mri_18 => tilde_gt_1_8_0,
+ :mri_19 => tilde_gt_1_9_0,
+ :mri_20 => tilde_gt_2_0_0,
+ :mri_21 => tilde_gt_2_1_0,
+ :mswin => gt_eq_0,
+ :mswin_18 => tilde_gt_1_8_0,
+ :mswin_19 => tilde_gt_1_9_0,
+ :mswin_20 => tilde_gt_2_0_0,
+ :mswin_21 => tilde_gt_2_1_0,
+ :mswin64 => gt_eq_0,
+ :mswin64_19 => tilde_gt_1_9_0,
+ :mswin64_20 => tilde_gt_2_0_0,
+ :mswin64_21 => tilde_gt_2_1_0,
+ :rbx => gt_eq_0,
+ :ruby => gt_eq_0,
+ :ruby_18 => tilde_gt_1_8_0,
+ :ruby_19 => tilde_gt_1_9_0,
+ :ruby_20 => tilde_gt_2_0_0,
+ :ruby_21 => tilde_gt_2_1_0,
+ :truffleruby => gt_eq_0,
+ :x64_mingw => gt_eq_0,
:x64_mingw_20 => tilde_gt_2_0_0,
:x64_mingw_21 => tilde_gt_2_1_0,
}.freeze
WINDOWS = { # :nodoc:
- :mingw => :only,
- :mingw_18 => :only,
- :mingw_19 => :only,
- :mingw_20 => :only,
- :mingw_21 => :only,
- :mri => :never,
- :mri_18 => :never,
- :mri_19 => :never,
- :mri_20 => :never,
- :mri_21 => :never,
- :mswin => :only,
- :mswin_18 => :only,
- :mswin_19 => :only,
- :mswin_20 => :only,
- :mswin_21 => :only,
- :mswin64 => :only,
- :mswin64_19 => :only,
- :mswin64_20 => :only,
- :mswin64_21 => :only,
- :rbx => :never,
- :ruby => :never,
- :ruby_18 => :never,
- :ruby_19 => :never,
- :ruby_20 => :never,
- :ruby_21 => :never,
- :x64_mingw => :only,
+ :mingw => :only,
+ :mingw_18 => :only,
+ :mingw_19 => :only,
+ :mingw_20 => :only,
+ :mingw_21 => :only,
+ :mri => :never,
+ :mri_18 => :never,
+ :mri_19 => :never,
+ :mri_20 => :never,
+ :mri_21 => :never,
+ :mswin => :only,
+ :mswin_18 => :only,
+ :mswin_19 => :only,
+ :mswin_20 => :only,
+ :mswin_21 => :only,
+ :mswin64 => :only,
+ :mswin64_19 => :only,
+ :mswin64_20 => :only,
+ :mswin64_21 => :only,
+ :rbx => :never,
+ :ruby => :never,
+ :ruby_18 => :never,
+ :ruby_19 => :never,
+ :ruby_20 => :never,
+ :ruby_21 => :never,
+ :x64_mingw => :only,
:x64_mingw_20 => :only,
:x64_mingw_21 => :only,
}.freeze
@@ -214,7 +215,7 @@ class Gem::RequestSet::GemDependencyAPI
git_source :github do |repo_name|
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include? "/"
- "git://github.com/#{repo_name}.git"
+ "https://github.com/#{repo_name}.git"
end
git_source :bitbucket do |repo_name|
@@ -371,7 +372,7 @@ class Gem::RequestSet::GemDependencyAPI
duplicate = @dependencies.include? name
@dependencies[name] =
- if requirements.empty? and not source_set
+ if requirements.empty? && !source_set
Gem::Requirement.default
elsif source_set
Gem::Requirement.source_set
@@ -435,7 +436,6 @@ Gem dependencies file #{@path} requires #{name} more than once.
reference ||= ref
reference ||= branch
reference ||= tag
- reference ||= 'master'
if ref && branch
warn <<-WARNING
@@ -637,8 +637,8 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# :development. Only one group may be specified.
def gemspec(options = {})
- name = options.delete(:name) || '{,*}'
- path = options.delete(:path) || '.'
+ name = options.delete(:name) || "{,*}"
+ path = options.delete(:path) || "."
development_group = options.delete(:development_group) || :development
spec = find_gemspec name, path
@@ -697,11 +697,11 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
def pin_gem_source(name, type = :default, source = nil)
source_description =
case type
- when :default then '(default)'
+ when :default then "(default)"
when :path then "path: #{source}"
when :git then "git: #{source}"
when :source then "source: #{source}"
- else '(unknown)'
+ else "(unknown)"
end
raise ArgumentError,
@@ -788,8 +788,8 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
engine_version = options[:engine_version]
raise ArgumentError,
- 'You must specify engine_version along with the Ruby engine' if
- engine and not engine_version
+ "You must specify engine_version along with the Ruby engine" if
+ engine && !engine_version
return true if @installing
@@ -800,7 +800,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
raise Gem::RubyVersionMismatch, message
end
- if engine and engine != Gem.ruby_engine
+ if engine && engine != Gem.ruby_engine
message = "Your Ruby engine is #{Gem.ruby_engine}, " +
"but your #{gem_deps_file} requires #{engine}"
diff --git a/lib/rubygems/request_set/lockfile.rb b/lib/rubygems/request_set/lockfile.rb
index 684d3fc7fe..9523fa7786 100644
--- a/lib/rubygems/request_set/lockfile.rb
+++ b/lib/rubygems/request_set/lockfile.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Parses a gem.deps.rb.lock file and constructs a LockSet containing the
# dependencies found inside. If the lock file is missing no LockSet is
@@ -76,7 +77,7 @@ class Gem::RequestSet::Lockfile
@gem_deps_file = File.expand_path(gem_deps_file)
@gem_deps_dir = File.dirname(@gem_deps_file)
- if RUBY_VERSION < '2.7'
+ if RUBY_VERSION < "2.7"
@gem_deps_file.untaint unless gem_deps_file.tainted?
end
@@ -106,7 +107,7 @@ class Gem::RequestSet::Lockfile
out << " specs:"
requests.sort_by {|request| request.name }.each do |request|
- next if request.spec.name == 'bundler'
+ next if request.spec.name == "bundler"
platform = "-#{request.spec.platform}" unless
Gem::Platform::RUBY == request.spec.platform
@@ -156,7 +157,7 @@ class Gem::RequestSet::Lockfile
if dest.index(base) == 0
offset = dest[base.size + 1..-1]
- return '.' unless offset
+ return "." unless offset
offset
else
@@ -224,7 +225,7 @@ class Gem::RequestSet::Lockfile
def write
content = to_s
- File.open "#{@gem_deps_file}.lock", 'w' do |io|
+ File.open "#{@gem_deps_file}.lock", "w" do |io|
io.write content
end
end
@@ -236,4 +237,4 @@ class Gem::RequestSet::Lockfile
end
end
-require_relative 'lockfile/tokenizer'
+require_relative "lockfile/tokenizer"
diff --git a/lib/rubygems/request_set/lockfile/parser.rb b/lib/rubygems/request_set/lockfile/parser.rb
index 8c12b435af..1daec1fabd 100644
--- a/lib/rubygems/request_set/lockfile/parser.rb
+++ b/lib/rubygems/request_set/lockfile/parser.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
class Gem::RequestSet::Lockfile::Parser
###
# Parses lockfiles
@@ -19,18 +20,18 @@ class Gem::RequestSet::Lockfile::Parser
@tokens.skip :newline
case token.value
- when 'DEPENDENCIES' then
+ when "DEPENDENCIES" then
parse_DEPENDENCIES
- when 'GIT' then
+ when "GIT" then
parse_GIT
- when 'GEM' then
+ when "GEM" then
parse_GEM
- when 'PATH' then
+ when "PATH" then
parse_PATH
- when 'PLATFORMS' then
+ when "PLATFORMS" then
parse_PLATFORMS
else
- token = get until @tokens.empty? or peek.first == :section
+ token = get until @tokens.empty? || peek.first == :section
end
else
raise "BUG: unhandled token #{token.type} (#{token.value.inspect}) at line #{token.line} column #{token.column}"
@@ -44,7 +45,7 @@ class Gem::RequestSet::Lockfile::Parser
def get(expected_types = nil, expected_value = nil) # :nodoc:
token = @tokens.shift
- if expected_types and not Array(expected_types).include? token.type
+ if expected_types && !Array(expected_types).include?(token.type)
unget token
message = "unexpected token [#{token.type.inspect}, #{token.value.inspect}], " +
@@ -53,7 +54,7 @@ class Gem::RequestSet::Lockfile::Parser
raise Gem::RequestSet::Lockfile::ParseError.new message, token.column, token.line, @filename
end
- if expected_value and expected_value != token.value
+ if expected_value && expected_value != token.value
unget token
message = "unexpected token [#{token.type.inspect}, #{token.value.inspect}], " +
@@ -67,7 +68,7 @@ class Gem::RequestSet::Lockfile::Parser
end
def parse_DEPENDENCIES # :nodoc:
- while not @tokens.empty? and :text == peek.type do
+ while !@tokens.empty? && :text == peek.type do
token = get :text
requirements = []
@@ -110,8 +111,8 @@ class Gem::RequestSet::Lockfile::Parser
def parse_GEM # :nodoc:
sources = []
- while [:entry, 'remote'] == peek.first(2) do
- get :entry, 'remote'
+ while [:entry, "remote"] == peek.first(2) do
+ get :entry, "remote"
data = get(:text).value
skip :newline
@@ -120,14 +121,14 @@ class Gem::RequestSet::Lockfile::Parser
sources << Gem::Source.new(Gem::DEFAULT_HOST) if sources.empty?
- get :entry, 'specs'
+ get :entry, "specs"
skip :newline
set = Gem::Resolver::LockSet.new sources
last_specs = nil
- while not @tokens.empty? and :text == peek.type do
+ while !@tokens.empty? && :text == peek.type do
token = get :text
name = token.value
column = token.column
@@ -144,8 +145,8 @@ class Gem::RequestSet::Lockfile::Parser
type = token.type
data = token.value
- if type == :text and column == 4
- version, platform = data.split '-', 2
+ if type == :text && column == 4
+ version, platform = data.split "-", 2
platform =
platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
@@ -171,26 +172,26 @@ class Gem::RequestSet::Lockfile::Parser
end
def parse_GIT # :nodoc:
- get :entry, 'remote'
+ get :entry, "remote"
repository = get(:text).value
skip :newline
- get :entry, 'revision'
+ get :entry, "revision"
revision = get(:text).value
skip :newline
type = peek.type
value = peek.value
- if type == :entry and %w[branch ref tag].include? value
+ if type == :entry && %w[branch ref tag].include?(value)
get
get :text
skip :newline
end
- get :entry, 'specs'
+ get :entry, "specs"
skip :newline
@@ -199,7 +200,7 @@ class Gem::RequestSet::Lockfile::Parser
last_spec = nil
- while not @tokens.empty? and :text == peek.type do
+ while !@tokens.empty? && :text == peek.type do
token = get :text
name = token.value
column = token.column
@@ -214,7 +215,7 @@ class Gem::RequestSet::Lockfile::Parser
type = token.type
data = token.value
- if type == :text and column == 4
+ if type == :text && column == 4
last_spec = set.add_git_spec name, data, repository, revision, true
else
dependency = parse_dependency name, data
@@ -234,19 +235,19 @@ class Gem::RequestSet::Lockfile::Parser
end
def parse_PATH # :nodoc:
- get :entry, 'remote'
+ get :entry, "remote"
directory = get(:text).value
skip :newline
- get :entry, 'specs'
+ get :entry, "specs"
skip :newline
set = Gem::Resolver::VendorSet.new
last_spec = nil
- while not @tokens.empty? and :text == peek.first do
+ while !@tokens.empty? && :text == peek.first do
token = get :text
name = token.value
column = token.column
@@ -261,7 +262,7 @@ class Gem::RequestSet::Lockfile::Parser
type = token.type
data = token.value
- if type == :text and column == 4
+ if type == :text && column == 4
last_spec = set.add_vendor_gem name, directory
else
dependency = parse_dependency name, data
@@ -281,7 +282,7 @@ class Gem::RequestSet::Lockfile::Parser
end
def parse_PLATFORMS # :nodoc:
- while not @tokens.empty? and :text == peek.first do
+ while !@tokens.empty? && :text == peek.first do
name = get(:text).value
@platforms << name
@@ -331,7 +332,7 @@ class Gem::RequestSet::Lockfile::Parser
set.find_all(requirement)
end.compact.first
- specification && specification.version
+ specification&.version
end
##
diff --git a/lib/rubygems/request_set/lockfile/tokenizer.rb b/lib/rubygems/request_set/lockfile/tokenizer.rb
index cb8030c143..e91a11ca93 100644
--- a/lib/rubygems/request_set/lockfile/tokenizer.rb
+++ b/lib/rubygems/request_set/lockfile/tokenizer.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
-require_relative 'parser'
+
+#) frozen_string_literal: true
+require_relative "parser"
class Gem::RequestSet::Lockfile::Tokenizer
Token = Struct.new :type, :value, :column, :line
@@ -26,7 +28,7 @@ class Gem::RequestSet::Lockfile::Tokenizer
end
def skip(type)
- @tokens.shift while not @tokens.empty? and peek.type == type
+ @tokens.shift while !@tokens.empty? && peek.type == type
end
##
@@ -57,7 +59,7 @@ class Gem::RequestSet::Lockfile::Tokenizer
private
def tokenize(input)
- require 'strscan'
+ require "strscan"
s = StringScanner.new input
until s.eos? do
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index 9edd6aa7d3..eed12c4914 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "version"
##
@@ -10,19 +11,19 @@ require_relative "version"
class Gem::Requirement
OPS = { #:nodoc:
- "=" => lambda {|v, r| v == r },
- "!=" => lambda {|v, r| v != r },
- ">" => lambda {|v, r| v > r },
- "<" => lambda {|v, r| v < r },
- ">=" => lambda {|v, r| v >= r },
- "<=" => lambda {|v, r| v <= r },
- "~>" => lambda {|v, r| v >= r && v.release < r.bump },
+ "=" => lambda {|v, r| v == r },
+ "!=" => lambda {|v, r| v != r },
+ ">" => lambda {|v, r| v > r },
+ "<" => lambda {|v, r| v < r },
+ ">=" => lambda {|v, r| v >= r },
+ "<=" => lambda {|v, r| v <= r },
+ "~>" => lambda {|v, r| v >= r && v.release < r.bump },
}.freeze
SOURCE_SET_REQUIREMENT = Struct.new(:for_lockfile).new "!" # :nodoc:
quoted = OPS.keys.map {|k| Regexp.quote k }.join "|"
- PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*".freeze # :nodoc:
+ PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*" # :nodoc:
##
# A regular expression that matches a requirement
@@ -61,7 +62,7 @@ class Gem::Requirement
input
when Gem::Version, Array then
new input
- when '!' then
+ when "!" then
source_set
else
if input.respond_to? :to_str
@@ -73,11 +74,11 @@ class Gem::Requirement
end
def self.default
- new '>= 0'
+ new ">= 0"
end
def self.default_prerelease
- new '>= 0.a'
+ new ">= 0.a"
end
###
@@ -218,7 +219,7 @@ class Gem::Requirement
end
def encode_with(coder) # :nodoc:
- coder.add 'requirements', @requirements
+ coder.add "requirements", @requirements
end
##
@@ -230,7 +231,7 @@ class Gem::Requirement
end
def pretty_print(q) # :nodoc:
- q.group 1, 'Gem::Requirement.new(', ')' do
+ q.group 1, "Gem::Requirement.new(", ")" do
q.pp as_list
end
end
@@ -253,7 +254,7 @@ class Gem::Requirement
def specific?
return true if @requirements.length > 1 # GIGO, > 1, > 2 is silly
- not %w[> >=].include? @requirements.first.first # grab the operator
+ !%w[> >=].include? @requirements.first.first # grab the operator
end
def to_s # :nodoc:
diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb
index 51a11fed47..a912729b37 100644
--- a/lib/rubygems/resolver.rb
+++ b/lib/rubygems/resolver.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
-require_relative 'dependency'
-require_relative 'exceptions'
-require_relative 'util/list'
+
+require_relative "dependency"
+require_relative "exceptions"
+require_relative "util/list"
##
# Given a set of Gem::Dependency objects as +needed+ and a way to query the
@@ -10,14 +11,14 @@ require_relative 'util/list'
# all the requirements.
class Gem::Resolver
- require_relative 'resolver/molinillo'
+ require_relative "resolver/molinillo"
##
# If the DEBUG_RESOLVER environment variable is set then debugging mode is
# enabled for the resolver. This will display information about the state
# of the resolver while a set of dependencies is being resolved.
- DEBUG_RESOLVER = !ENV['DEBUG_RESOLVER'].nil?
+ DEBUG_RESOLVER = !ENV["DEBUG_RESOLVER"].nil?
##
# Set to true if all development dependencies should be considered.
@@ -74,7 +75,7 @@ class Gem::Resolver
case sets.length
when 0 then
- raise ArgumentError, 'one set in the composition must be non-nil'
+ raise ArgumentError, "one set in the composition must be non-nil"
when 1 then
sets.first
else
@@ -124,7 +125,7 @@ class Gem::Resolver
data = yield
$stderr.printf "%10s (%d entries)\n", stage.to_s.upcase, data.size
unless data.empty?
- require 'pp'
+ require "pp"
PP.pp data, $stderr
end
end
@@ -153,10 +154,10 @@ class Gem::Resolver
s.fetch_development_dependencies if @development
s.dependencies.reverse_each do |d|
- next if d.type == :development and not @development
- next if d.type == :development and @development_shallow and
+ next if d.type == :development && !@development
+ next if d.type == :development && @development_shallow &&
act.development?
- next if d.type == :development and @development_shallow and
+ next if d.type == :development && @development_shallow &&
act.parent
reqs << Gem::Resolver::DependencyRequest.new(d, act)
@@ -173,7 +174,7 @@ class Gem::Resolver
include Molinillo::UI
def output
- @output ||= debug? ? $stdout : File.open(IO::NULL, 'w')
+ @output ||= debug? ? $stdout : File.open(IO::NULL, "w")
end
def debug?
@@ -192,7 +193,7 @@ class Gem::Resolver
conflict = e.conflicts.values.first
raise Gem::DependencyResolutionError, Conflict.new(conflict.requirement_trees.first.first, conflict.existing, conflict.requirement)
ensure
- @output.close if defined?(@output) and !debug?
+ @output.close if defined?(@output) && !debug?
end
##
@@ -246,7 +247,7 @@ class Gem::Resolver
sources.each do |source|
groups[source].
- sort_by {|spec| [spec.version, Gem::Platform.local =~ spec.platform ? 1 : 0] }.
+ sort_by {|spec| [spec.version, spec.platform =~ Gem::Platform.local ? 1 : 0] }.
map {|spec| ActivationRequest.new spec, dependency }.
each {|activation_request| activation_requests << activation_request }
end
@@ -318,30 +319,30 @@ class Gem::Resolver
private :amount_constrained
end
-require_relative 'resolver/activation_request'
-require_relative 'resolver/conflict'
-require_relative 'resolver/dependency_request'
-require_relative 'resolver/requirement_list'
-require_relative 'resolver/stats'
-
-require_relative 'resolver/set'
-require_relative 'resolver/api_set'
-require_relative 'resolver/composed_set'
-require_relative 'resolver/best_set'
-require_relative 'resolver/current_set'
-require_relative 'resolver/git_set'
-require_relative 'resolver/index_set'
-require_relative 'resolver/installer_set'
-require_relative 'resolver/lock_set'
-require_relative 'resolver/vendor_set'
-require_relative 'resolver/source_set'
-
-require_relative 'resolver/specification'
-require_relative 'resolver/spec_specification'
-require_relative 'resolver/api_specification'
-require_relative 'resolver/git_specification'
-require_relative 'resolver/index_specification'
-require_relative 'resolver/installed_specification'
-require_relative 'resolver/local_specification'
-require_relative 'resolver/lock_specification'
-require_relative 'resolver/vendor_specification'
+require_relative "resolver/activation_request"
+require_relative "resolver/conflict"
+require_relative "resolver/dependency_request"
+require_relative "resolver/requirement_list"
+require_relative "resolver/stats"
+
+require_relative "resolver/set"
+require_relative "resolver/api_set"
+require_relative "resolver/composed_set"
+require_relative "resolver/best_set"
+require_relative "resolver/current_set"
+require_relative "resolver/git_set"
+require_relative "resolver/index_set"
+require_relative "resolver/installer_set"
+require_relative "resolver/lock_set"
+require_relative "resolver/vendor_set"
+require_relative "resolver/source_set"
+
+require_relative "resolver/specification"
+require_relative "resolver/spec_specification"
+require_relative "resolver/api_specification"
+require_relative "resolver/git_specification"
+require_relative "resolver/index_specification"
+require_relative "resolver/installed_specification"
+require_relative "resolver/local_specification"
+require_relative "resolver/lock_specification"
+require_relative "resolver/vendor_specification"
diff --git a/lib/rubygems/resolver/activation_request.rb b/lib/rubygems/resolver/activation_request.rb
index ae35681db9..d59859c102 100644
--- a/lib/rubygems/resolver/activation_request.rb
+++ b/lib/rubygems/resolver/activation_request.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Specifies a Specification object that should be activated. Also contains a
# dependency that was used to introduce this activation.
@@ -93,7 +94,7 @@ class Gem::Resolver::ActivationRequest
end
def inspect # :nodoc:
- '#<%s for %p from %s>' % [
+ "#<%s for %p from %s>" % [
self.class, @spec, @request
]
end
@@ -130,12 +131,12 @@ class Gem::Resolver::ActivationRequest
end
def pretty_print(q) # :nodoc:
- q.group 2, '[Activation request', ']' do
+ q.group 2, "[Activation request", "]" do
q.breakable
q.pp @spec
q.breakable
- q.text ' for '
+ q.text " for "
q.pp @request
end
end
diff --git a/lib/rubygems/resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb
index 21c9b8920c..9b57b03192 100644
--- a/lib/rubygems/resolver/api_set.rb
+++ b/lib/rubygems/resolver/api_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The global rubygems pool, available via the rubygems.org API.
# Returns instances of APISpecification.
@@ -26,13 +27,13 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
# API URL +dep_uri+ which is described at
# https://guides.rubygems.org/rubygems-org-api
- def initialize(dep_uri = 'https://index.rubygems.org/info/')
+ def initialize(dep_uri = "https://index.rubygems.org/info/")
super()
dep_uri = URI dep_uri unless URI === dep_uri
@dep_uri = dep_uri
- @uri = dep_uri + '..'
+ @uri = dep_uri + ".."
@data = Hash.new {|h,k| h[k] = [] }
@source = Gem::Source.new @uri
@@ -83,12 +84,12 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
end
def pretty_print(q) # :nodoc:
- q.group 2, '[APISet', ']' do
+ q.group 2, "[APISet", "]" do
q.breakable
q.text "URI: #{@dep_uri}"
q.breakable
- q.text 'gem names:'
+ q.text "gem names:"
q.pp @data.keys
end
end
diff --git a/lib/rubygems/resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb
index b5aa0b71d4..f26f82757e 100644
--- a/lib/rubygems/resolver/api_specification.rb
+++ b/lib/rubygems/resolver/api_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Represents a specification retrieved via the rubygems.org API.
#
@@ -40,10 +41,10 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
end
def ==(other) # :nodoc:
- self.class === other and
- @set == other.set and
- @name == other.name and
- @version == other.version and
+ self.class === other &&
+ @set == other.set &&
+ @name == other.name &&
+ @version == other.version &&
@platform == other.platform
end
@@ -62,7 +63,7 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
end
def pretty_print(q) # :nodoc:
- q.group 2, '[APISpecification', ']' do
+ q.group 2, "[APISpecification", "]" do
q.breakable
q.text "name: #{name}"
@@ -73,7 +74,7 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
q.text "platform: #{platform}"
q.breakable
- q.text 'dependencies:'
+ q.text "dependencies:"
q.breakable
q.pp @dependencies
diff --git a/lib/rubygems/resolver/best_set.rb b/lib/rubygems/resolver/best_set.rb
index 300ea8015c..d75fa7c00a 100644
--- a/lib/rubygems/resolver/best_set.rb
+++ b/lib/rubygems/resolver/best_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The BestSet chooses the best available method to query a remote index.
#
@@ -25,7 +26,7 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
end
def find_all(req) # :nodoc:
- pick_sets if @remote and @sets.empty?
+ pick_sets if @remote && @sets.empty?
super
rescue Gem::RemoteFetcher::FetchError => e
@@ -35,15 +36,15 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
end
def prefetch(reqs) # :nodoc:
- pick_sets if @remote and @sets.empty?
+ pick_sets if @remote && @sets.empty?
super
end
def pretty_print(q) # :nodoc:
- q.group 2, '[BestSet', ']' do
+ q.group 2, "[BestSet", "]" do
q.breakable
- q.text 'sets:'
+ q.text "sets:"
q.breakable
q.pp @sets
@@ -63,7 +64,7 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
uri = uri + "."
raise error unless api_set = @sets.find do |set|
- Gem::Resolver::APISet === set and set.dep_uri == uri
+ Gem::Resolver::APISet === set && set.dep_uri == uri
end
index_set = Gem::Resolver::IndexSet.new api_set.source
diff --git a/lib/rubygems/resolver/composed_set.rb b/lib/rubygems/resolver/composed_set.rb
index 226da1e1e0..0991f0713e 100644
--- a/lib/rubygems/resolver/composed_set.rb
+++ b/lib/rubygems/resolver/composed_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A ComposedSet allows multiple sets to be queried like a single set.
#
diff --git a/lib/rubygems/resolver/conflict.rb b/lib/rubygems/resolver/conflict.rb
index 4c4588d7e8..dca41da51f 100644
--- a/lib/rubygems/resolver/conflict.rb
+++ b/lib/rubygems/resolver/conflict.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Used internally to indicate that a dependency conflicted
# with a spec that would be activated.
@@ -27,9 +28,9 @@ class Gem::Resolver::Conflict
end
def ==(other) # :nodoc:
- self.class === other and
- @dependency == other.dependency and
- @activated == other.activated and
+ self.class === other &&
+ @dependency == other.dependency &&
+ @activated == other.activated &&
@failed_dep == other.failed_dep
end
@@ -65,7 +66,7 @@ class Gem::Resolver::Conflict
matching = matching % [
dependency,
- alternates.join(', '),
+ alternates.join(", "),
]
end
@@ -97,21 +98,21 @@ class Gem::Resolver::Conflict
end
def pretty_print(q) # :nodoc:
- q.group 2, '[Dependency conflict: ', ']' do
+ q.group 2, "[Dependency conflict: ", "]" do
q.breakable
- q.text 'activated '
+ q.text "activated "
q.pp @activated
q.breakable
- q.text ' dependency '
+ q.text " dependency "
q.pp @dependency
q.breakable
if @dependency == @failed_dep
- q.text ' failed'
+ q.text " failed"
else
- q.text ' failed dependency '
+ q.text " failed dependency "
q.pp @failed_dep
end
end
@@ -139,7 +140,7 @@ class Gem::Resolver::Conflict
end
end
- path = ['user request (gem command or Gemfile)'] if path.empty?
+ path = ["user request (gem command or Gemfile)"] if path.empty?
path
end
diff --git a/lib/rubygems/resolver/current_set.rb b/lib/rubygems/resolver/current_set.rb
index c3aa3a2c37..370e445089 100644
--- a/lib/rubygems/resolver/current_set.rb
+++ b/lib/rubygems/resolver/current_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A set which represents the installed gems. Respects
# all the normal settings that control where to look
diff --git a/lib/rubygems/resolver/dependency_request.rb b/lib/rubygems/resolver/dependency_request.rb
index 356aadb3b2..60b338277f 100644
--- a/lib/rubygems/resolver/dependency_request.rb
+++ b/lib/rubygems/resolver/dependency_request.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Used Internally. Wraps a Dependency object to also track which spec
# contained the Dependency.
@@ -95,12 +96,12 @@ class Gem::Resolver::DependencyRequest
end
def pretty_print(q) # :nodoc:
- q.group 2, '[Dependency request ', ']' do
+ q.group 2, "[Dependency request ", "]" do
q.breakable
q.text @dependency.to_s
q.breakable
- q.text ' requested by '
+ q.text " requested by "
q.pp @requester
end
end
diff --git a/lib/rubygems/resolver/git_set.rb b/lib/rubygems/resolver/git_set.rb
index eac51f15ad..89342ff80d 100644
--- a/lib/rubygems/resolver/git_set.rb
+++ b/lib/rubygems/resolver/git_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A GitSet represents gems that are sourced from git repositories.
#
@@ -35,7 +36,7 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set
def initialize # :nodoc:
super()
- @git = ENV['git'] || 'git'
+ @git = ENV["git"] || "git"
@need_submodules = {}
@repositories = {}
@root_dir = Gem.dir
@@ -104,7 +105,7 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set
end
def pretty_print(q) # :nodoc:
- q.group 2, '[GitSet', ']' do
+ q.group 2, "[GitSet", "]" do
next if @repositories.empty?
q.breakable
diff --git a/lib/rubygems/resolver/git_specification.rb b/lib/rubygems/resolver/git_specification.rb
index ee47080ab4..e587c17d2a 100644
--- a/lib/rubygems/resolver/git_specification.rb
+++ b/lib/rubygems/resolver/git_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A GitSpecification represents a gem that is sourced from a git repository
# and is being loaded through a gem dependencies file through the +git:+
@@ -6,9 +7,9 @@
class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
def ==(other) # :nodoc:
- self.class === other and
- @set == other.set and
- @spec == other.spec and
+ self.class === other &&
+ @set == other.set &&
+ @spec == other.spec &&
@source == other.source
end
@@ -21,7 +22,7 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
# the executables.
def install(options = {})
- require_relative '../installer'
+ require_relative "../installer"
installer = Gem::Installer.for_spec spec, options
@@ -35,7 +36,7 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
end
def pretty_print(q) # :nodoc:
- q.group 2, '[GitSpecification', ']' do
+ q.group 2, "[GitSpecification", "]" do
q.breakable
q.text "name: #{name}"
@@ -43,7 +44,7 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
q.text "version: #{version}"
q.breakable
- q.text 'dependencies:'
+ q.text "dependencies:"
q.breakable
q.pp dependencies
diff --git a/lib/rubygems/resolver/index_set.rb b/lib/rubygems/resolver/index_set.rb
index 9390e34255..5e8632c7d8 100644
--- a/lib/rubygems/resolver/index_set.rb
+++ b/lib/rubygems/resolver/index_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The global rubygems pool represented via the traditional
# source index.
@@ -53,14 +54,14 @@ class Gem::Resolver::IndexSet < Gem::Resolver::Set
end
def pretty_print(q) # :nodoc:
- q.group 2, '[IndexSet', ']' do
+ q.group 2, "[IndexSet", "]" do
q.breakable
- q.text 'sources:'
+ q.text "sources:"
q.breakable
q.pp @f.sources
q.breakable
- q.text 'specs:'
+ q.text "specs:"
q.breakable
diff --git a/lib/rubygems/resolver/index_specification.rb b/lib/rubygems/resolver/index_specification.rb
index 9ea76f40ba..6fac8c1487 100644
--- a/lib/rubygems/resolver/index_specification.rb
+++ b/lib/rubygems/resolver/index_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Represents a possible Specification object returned from IndexSet. Used to
# delay needed to download full Specification objects when only the +name+
@@ -21,7 +22,8 @@ class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification
@name = name
@version = version
@source = source
- @platform = platform.to_s
+ @platform = Gem::Platform.new(platform.to_s)
+ @original_platform = platform.to_s
@spec = nil
end
@@ -66,11 +68,11 @@ class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification
end
def inspect # :nodoc:
- '#<%s %s source %s>' % [self.class, full_name, @source]
+ "#<%s %s source %s>" % [self.class, full_name, @source]
end
def pretty_print(q) # :nodoc:
- q.group 2, '[Index specification', ']' do
+ q.group 2, "[Index specification", "]" do
q.breakable
q.text full_name
@@ -80,7 +82,7 @@ class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification
end
q.breakable
- q.text 'source '
+ q.text "source "
q.pp @source
end
end
@@ -91,7 +93,7 @@ class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification
def spec # :nodoc:
@spec ||=
begin
- tuple = Gem::NameTuple.new @name, @version, @platform
+ tuple = Gem::NameTuple.new @name, @version, @original_platform
@source.fetch_spec tuple
end
diff --git a/lib/rubygems/resolver/installed_specification.rb b/lib/rubygems/resolver/installed_specification.rb
index 167ba1439e..b80f882c77 100644
--- a/lib/rubygems/resolver/installed_specification.rb
+++ b/lib/rubygems/resolver/installed_specification.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
+
##
# An InstalledSpecification represents a gem that is already installed
# locally.
class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
def ==(other) # :nodoc:
- self.class === other and
- @set == other.set and
+ self.class === other &&
+ @set == other.set &&
@spec == other.spec
end
@@ -30,7 +31,7 @@ class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
end
def pretty_print(q) # :nodoc:
- q.group 2, '[InstalledSpecification', ']' do
+ q.group 2, "[InstalledSpecification", "]" do
q.breakable
q.text "name: #{name}"
@@ -41,7 +42,7 @@ class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
q.text "platform: #{platform}"
q.breakable
- q.text 'dependencies:'
+ q.text "dependencies:"
q.breakable
q.pp spec.dependencies
end
diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb
index 45252ed241..521d241fd5 100644
--- a/lib/rubygems/resolver/installer_set.rb
+++ b/lib/rubygems/resolver/installer_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A set of gems for installation sourced from remote sources and local .gem
# files
@@ -61,13 +62,12 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
found = find_all request
found.delete_if do |s|
- s.version.prerelease? and not s.local?
+ s.version.prerelease? && !s.local?
end unless dependency.prerelease?
found = found.select do |s|
- Gem::Source::SpecificFile === s.source or
- Gem::Platform::RUBY == s.platform or
- Gem::Platform.local === s.platform
+ Gem::Source::SpecificFile === s.source ||
+ Gem::Platform.match_spec?(s)
end
found = found.sort_by do |s|
@@ -111,14 +111,14 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
# Should local gems should be considered?
def consider_local? # :nodoc:
- @domain == :both or @domain == :local
+ @domain == :both || @domain == :local
end
##
# Should remote gems should be considered?
def consider_remote? # :nodoc:
- @domain == :both or @domain == :remote
+ @domain == :both || @domain == :remote
end
##
@@ -137,8 +137,8 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
dep = req.dependency
- return res if @ignore_dependencies and
- @always_install.none? {|spec| dep.match? spec }
+ return res if @ignore_dependencies &&
+ @always_install.none? {|spec| dep.match? spec }
name = dep.name
@@ -148,6 +148,8 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
res << Gem::Resolver::InstalledSpecification.new(self, gemspec)
end unless @ignore_installed
+ matching_local = []
+
if consider_local?
matching_local = @local.values.select do |spec, _|
req.match? spec
@@ -168,11 +170,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
end
end
- res.delete_if do |spec|
- spec.version.prerelease? and not dep.prerelease?
- end
-
- res.concat @remote_set.find_all req if consider_remote?
+ res.concat @remote_set.find_all req if consider_remote? && matching_local.empty?
res
end
@@ -190,7 +188,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
def inspect # :nodoc:
always_install = @always_install.map {|s| s.full_name }
- '#<%s domain: %s specs: %p always install: %p>' % [
+ "#<%s domain: %s specs: %p always install: %p>" % [
self.class, @domain, @specs.keys, always_install
]
end
@@ -219,16 +217,16 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
end
def pretty_print(q) # :nodoc:
- q.group 2, '[InstallerSet', ']' do
+ q.group 2, "[InstallerSet", "]" do
q.breakable
q.text "domain: #{@domain}"
q.breakable
- q.text 'specs: '
+ q.text "specs: "
q.pp @specs.keys
q.breakable
- q.text 'always install: '
+ q.text "always install: "
q.pp @always_install
end
end
diff --git a/lib/rubygems/resolver/local_specification.rb b/lib/rubygems/resolver/local_specification.rb
index 9c69c4ab74..50bf276345 100644
--- a/lib/rubygems/resolver/local_specification.rb
+++ b/lib/rubygems/resolver/local_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A LocalSpecification comes from a .gem file on the local filesystem.
@@ -17,7 +18,7 @@ class Gem::Resolver::LocalSpecification < Gem::Resolver::SpecSpecification
end
def pretty_print(q) # :nodoc:
- q.group 2, '[LocalSpecification', ']' do
+ q.group 2, "[LocalSpecification", "]" do
q.breakable
q.text "name: #{name}"
@@ -28,7 +29,7 @@ class Gem::Resolver::LocalSpecification < Gem::Resolver::SpecSpecification
q.text "platform: #{platform}"
q.breakable
- q.text 'dependencies:'
+ q.text "dependencies:"
q.breakable
q.pp dependencies
diff --git a/lib/rubygems/resolver/lock_set.rb b/lib/rubygems/resolver/lock_set.rb
index eabf217aba..8eec041bdc 100644
--- a/lib/rubygems/resolver/lock_set.rb
+++ b/lib/rubygems/resolver/lock_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A set of gems from a gem dependencies lockfile.
@@ -54,7 +55,7 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
dep = Gem::Dependency.new name, version
found = @specs.find do |spec|
- dep.matches_spec? spec and spec.platform == platform
+ dep.matches_spec?(spec) && spec.platform == platform
end
tuple = Gem::NameTuple.new found.name, found.version, found.platform
@@ -63,15 +64,15 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
end
def pretty_print(q) # :nodoc:
- q.group 2, '[LockSet', ']' do
+ q.group 2, "[LockSet", "]" do
q.breakable
- q.text 'source:'
+ q.text "source:"
q.breakable
q.pp @source
q.breakable
- q.text 'specs:'
+ q.text "specs:"
q.breakable
q.pp @specs.map {|spec| spec.full_name }
diff --git a/lib/rubygems/resolver/lock_specification.rb b/lib/rubygems/resolver/lock_specification.rb
index cdb8e4e425..06f912dd85 100644
--- a/lib/rubygems/resolver/lock_specification.rb
+++ b/lib/rubygems/resolver/lock_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The LockSpecification comes from a lockfile (Gem::RequestSet::Lockfile).
#
@@ -29,7 +30,7 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
def install(options = {})
destination = options[:install_dir] || Gem.dir
- if File.exist? File.join(destination, 'specifications', spec.spec_name)
+ if File.exist? File.join(destination, "specifications", spec.spec_name)
yield nil
return
end
@@ -45,7 +46,7 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
end
def pretty_print(q) # :nodoc:
- q.group 2, '[LockSpecification', ']' do
+ q.group 2, "[LockSpecification", "]" do
q.breakable
q.text "name: #{@name}"
@@ -59,7 +60,7 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
unless @dependencies.empty?
q.breakable
- q.text 'dependencies:'
+ q.text "dependencies:"
q.breakable
q.pp @dependencies
end
@@ -71,7 +72,7 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
def spec
@spec ||= Gem::Specification.find do |spec|
- spec.name == @name and spec.version == @version
+ spec.name == @name && spec.version == @version
end
@spec ||= Gem::Specification.new do |s|
diff --git a/lib/rubygems/resolver/molinillo.rb b/lib/rubygems/resolver/molinillo.rb
index 12ca740e5a..d703505410 100644
--- a/lib/rubygems/resolver/molinillo.rb
+++ b/lib/rubygems/resolver/molinillo.rb
@@ -1,2 +1,3 @@
# frozen_string_literal: true
-require_relative 'molinillo/lib/molinillo'
+
+require_relative "molinillo/lib/molinillo"
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
index 95f8416b96..731a9e3e90 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
@@ -32,7 +32,7 @@ module Gem::Resolver::Molinillo
# all belong to the same graph.
# @return [Array<Vertex>] The sorted vertices.
def self.tsort(vertices)
- TSort.tsort(
+ Gem::TSort.tsort(
lambda { |b| vertices.each(&b) },
lambda { |v, &b| (v.successors & vertices).each(&b) }
)
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb
index ada03a901c..4289902828 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb
@@ -107,36 +107,42 @@ module Gem::Resolver::Molinillo
end
end
- conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
- o << "\n" << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
- if conflict.locked_requirement
- o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
- o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
- o << %(\n)
- end
- o << %( In #{name_for_explicit_dependency_source}:\n)
- trees = reduce_trees.call(conflict.requirement_trees)
-
- o << trees.map do |tree|
- t = ''.dup
- depth = 2
- tree.each do |req|
- t << ' ' * depth << printable_requirement.call(req)
- unless tree.last == req
- if spec = conflict.activated_by_name[name_for(req)]
- t << %( was resolved to #{version_for_spec.call(spec)}, which)
+ full_message_for_conflict = opts.delete(:full_message_for_conflict) do
+ proc do |name, conflict|
+ o = "\n".dup << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
+ if conflict.locked_requirement
+ o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
+ o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
+ o << %(\n)
+ end
+ o << %( In #{name_for_explicit_dependency_source}:\n)
+ trees = reduce_trees.call(conflict.requirement_trees)
+
+ o << trees.map do |tree|
+ t = ''.dup
+ depth = 2
+ tree.each do |req|
+ t << ' ' * depth << printable_requirement.call(req)
+ unless tree.last == req
+ if spec = conflict.activated_by_name[name_for(req)]
+ t << %( was resolved to #{version_for_spec.call(spec)}, which)
+ end
+ t << %( depends on)
end
- t << %( depends on)
+ t << %(\n)
+ depth += 1
end
- t << %(\n)
- depth += 1
- end
- t
- end.join("\n")
+ t
+ end.join("\n")
- additional_message_for_conflict.call(o, name, conflict)
+ additional_message_for_conflict.call(o, name, conflict)
- o
+ o
+ end
+ end
+
+ conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
+ o << full_message_for_conflict.call(name, conflict)
end.strip
end
end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
index 6b5ada7ade..86c249c404 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
@@ -2,5 +2,5 @@
module Gem::Resolver::Molinillo
# The version of Gem::Resolver::Molinillo.
- VERSION = '0.7.0'.freeze
+ VERSION = '0.8.0'.freeze
end
diff --git a/lib/rubygems/resolver/requirement_list.rb b/lib/rubygems/resolver/requirement_list.rb
index 5b51493c9a..6f86f0f412 100644
--- a/lib/rubygems/resolver/requirement_list.rb
+++ b/lib/rubygems/resolver/requirement_list.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The RequirementList is used to hold the requirements being considered
# while resolving a set of gems.
diff --git a/lib/rubygems/resolver/set.rb b/lib/rubygems/resolver/set.rb
index 5d8dd51eaa..243fee5fd5 100644
--- a/lib/rubygems/resolver/set.rb
+++ b/lib/rubygems/resolver/set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Resolver sets are used to look up specifications (and their
# dependencies) used in resolution. This set is abstract.
diff --git a/lib/rubygems/resolver/source_set.rb b/lib/rubygems/resolver/source_set.rb
index bf8c23184e..296cf41078 100644
--- a/lib/rubygems/resolver/source_set.rb
+++ b/lib/rubygems/resolver/source_set.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# The SourceSet chooses the best available method to query a remote index.
#
diff --git a/lib/rubygems/resolver/spec_specification.rb b/lib/rubygems/resolver/spec_specification.rb
index 7b665fe876..79a34d8063 100644
--- a/lib/rubygems/resolver/spec_specification.rb
+++ b/lib/rubygems/resolver/spec_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The Resolver::SpecSpecification contains common functionality for
# Resolver specifications that are backed by a Gem::Specification.
diff --git a/lib/rubygems/resolver/specification.rb b/lib/rubygems/resolver/specification.rb
index dfcb7eb057..d2098ef0e2 100644
--- a/lib/rubygems/resolver/specification.rb
+++ b/lib/rubygems/resolver/specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A Resolver::Specification contains a subset of the information
# contained in a Gem::Specification. Only the information necessary for
@@ -93,7 +94,7 @@ class Gem::Resolver::Specification
# specification.
def install(options = {})
- require_relative '../installer'
+ require_relative "../installer"
gem = download options
diff --git a/lib/rubygems/resolver/stats.rb b/lib/rubygems/resolver/stats.rb
index 64b458f504..9920976b2a 100644
--- a/lib/rubygems/resolver/stats.rb
+++ b/lib/rubygems/resolver/stats.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
class Gem::Resolver::Stats
def initialize
@max_depth = 0
@@ -32,7 +33,7 @@ class Gem::Resolver::Stats
@iterations += 1
end
- PATTERN = "%20s: %d\n".freeze
+ PATTERN = "%20s: %d\n"
def display
$stdout.puts "=== Resolver Statistics ==="
diff --git a/lib/rubygems/resolver/vendor_set.rb b/lib/rubygems/resolver/vendor_set.rb
index 48c640d8c9..293a1e3331 100644
--- a/lib/rubygems/resolver/vendor_set.rb
+++ b/lib/rubygems/resolver/vendor_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A VendorSet represents gems that have been unpacked into a specific
# directory that contains a gemspec.
@@ -69,7 +70,7 @@ class Gem::Resolver::VendorSet < Gem::Resolver::Set
end
def pretty_print(q) # :nodoc:
- q.group 2, '[VendorSet', ']' do
+ q.group 2, "[VendorSet", "]" do
next if @directories.empty?
q.breakable
diff --git a/lib/rubygems/resolver/vendor_specification.rb b/lib/rubygems/resolver/vendor_specification.rb
index 8dfe5940f2..ac78f54558 100644
--- a/lib/rubygems/resolver/vendor_specification.rb
+++ b/lib/rubygems/resolver/vendor_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A VendorSpecification represents a gem that has been unpacked into a project
# and is being loaded through a gem dependencies file through the +path:+
@@ -6,9 +7,9 @@
class Gem::Resolver::VendorSpecification < Gem::Resolver::SpecSpecification
def ==(other) # :nodoc:
- self.class === other and
- @set == other.set and
- @spec == other.spec and
+ self.class === other &&
+ @set == other.set &&
+ @spec == other.spec &&
@source == other.source
end
diff --git a/lib/rubygems/s3_uri_signer.rb b/lib/rubygems/s3_uri_signer.rb
index 4d1deee997..5161e43ebb 100644
--- a/lib/rubygems/s3_uri_signer.rb
+++ b/lib/rubygems/s3_uri_signer.rb
@@ -1,4 +1,6 @@
-require_relative 'openssl'
+# frozen_string_literal: true
+
+require_relative "openssl"
##
# S3URISigner implements AWS SigV4 for S3 Source to avoid a dependency on the aws-sdk-* gems
@@ -138,14 +140,14 @@ class Gem::S3URISigner
end
def ec2_metadata_credentials_json
- require 'net/http'
- require_relative 'request'
- require_relative 'request/connection_pools'
- require 'json'
+ require "net/http"
+ require_relative "request"
+ require_relative "request/connection_pools"
+ require "json"
iam_info = ec2_metadata_request(EC2_IAM_INFO)
# Expected format: arn:aws:iam::<id>:instance-profile/<role_name>
- role_name = iam_info['InstanceProfileArn'].split('/').last
+ role_name = iam_info["InstanceProfileArn"].split("/").last
ec2_metadata_request(EC2_IAM_SECURITY_CREDENTIALS + role_name)
end
@@ -170,6 +172,6 @@ class Gem::S3URISigner
end
BASE64_URI_TRANSLATE = { "+" => "%2B", "/" => "%2F", "=" => "%3D", "\n" => "" }.freeze
- EC2_IAM_INFO = "http://169.254.169.254/latest/meta-data/iam/info".freeze
- EC2_IAM_SECURITY_CREDENTIALS = "http://169.254.169.254/latest/meta-data/iam/security-credentials/".freeze
+ EC2_IAM_INFO = "http://169.254.169.254/latest/meta-data/iam/info"
+ EC2_IAM_SECURITY_CREDENTIALS = "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
end
diff --git a/lib/rubygems/safe_yaml.rb b/lib/rubygems/safe_yaml.rb
index 81f99ee26e..74ad25ca15 100644
--- a/lib/rubygems/safe_yaml.rb
+++ b/lib/rubygems/safe_yaml.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gem
###
@@ -26,7 +28,7 @@ module Gem
if ::Psych.respond_to? :safe_load
def self.safe_load(input)
- if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1')
+ if Gem::Version.new(Psych::VERSION) >= Gem::Version.new("3.1.0.pre1")
::Psych.safe_load(input, permitted_classes: PERMITTED_CLASSES, permitted_symbols: PERMITTED_SYMBOLS, aliases: true)
else
::Psych.safe_load(input, PERMITTED_CLASSES, PERMITTED_SYMBOLS, true)
@@ -34,7 +36,7 @@ module Gem
end
def self.load(input)
- if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1')
+ if Gem::Version.new(Psych::VERSION) >= Gem::Version.new("3.1.0.pre1")
::Psych.safe_load(input, permitted_classes: [::Symbol])
else
::Psych.safe_load(input, [::Symbol])
diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb
index fc23c1c481..73ee1e4bb9 100644
--- a/lib/rubygems/security.rb
+++ b/lib/rubygems/security.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative 'exceptions'
-require_relative 'openssl'
+require_relative "exceptions"
+require_relative "openssl"
##
# = Signing gems
@@ -334,7 +335,7 @@ module Gem::Security
##
# Used internally to select the signing digest from all computed digests
- DIGEST_NAME = 'SHA256' # :nodoc:
+ DIGEST_NAME = "SHA256" # :nodoc:
##
# Length of keys created by RSA and DSA keys
@@ -344,18 +345,18 @@ module Gem::Security
##
# Default algorithm to use when building a key pair
- DEFAULT_KEY_ALGORITHM = 'RSA'
+ DEFAULT_KEY_ALGORITHM = "RSA"
##
# Named curve used for Elliptic Curve
- EC_NAME = 'secp384r1'
+ EC_NAME = "secp384r1"
##
# Cipher used to encrypt the key pair used to sign gems.
# Must be in the list returned by OpenSSL::Cipher.ciphers
- KEY_CIPHER = OpenSSL::Cipher.new('AES-256-CBC') if defined?(OpenSSL::Cipher)
+ KEY_CIPHER = OpenSSL::Cipher.new("AES-256-CBC") if defined?(OpenSSL::Cipher)
##
# One day in seconds
@@ -376,10 +377,10 @@ module Gem::Security
# * The certificate contains a subject key identifier
EXTENSIONS = {
- 'basicConstraints' => 'CA:FALSE',
- 'keyUsage' =>
- 'keyEncipherment,dataEncipherment,digitalSignature',
- 'subjectKeyIdentifier' => 'hash',
+ "basicConstraints" => "CA:FALSE",
+ "keyUsage" =>
+ "keyEncipherment,dataEncipherment,digitalSignature",
+ "subjectKeyIdentifier" => "hash",
}.freeze
def self.alt_name_or_x509_entry(certificate, x509_entry)
@@ -434,13 +435,6 @@ module Gem::Security
end
##
- # In Ruby 2.3 EC doesn't implement the private_key? but not the private? method
-
- if defined?(OpenSSL::PKey::EC) && Gem::Version.new(String.new(RUBY_VERSION)) < Gem::Version.new("2.4.0")
- OpenSSL::PKey::EC.send(:alias_method, :private?, :private_key?)
- end
-
- ##
# Creates a self-signed certificate with an issuer and subject from +email+,
# a subject alternative name of +email+ and the given +extensions+ for the
# +key+.
@@ -473,7 +467,7 @@ module Gem::Security
OpenSSL::Digest.new(algorithm)
end
else
- require 'digest'
+ require "digest"
def self.create_digest(algorithm = DIGEST_NAME)
Digest.const_get(algorithm).new
@@ -487,18 +481,12 @@ module Gem::Security
def self.create_key(algorithm)
if defined?(OpenSSL::PKey)
case algorithm.downcase
- when 'dsa'
+ when "dsa"
OpenSSL::PKey::DSA.new(RSA_DSA_KEY_LENGTH)
- when 'rsa'
+ when "rsa"
OpenSSL::PKey::RSA.new(RSA_DSA_KEY_LENGTH)
- when 'ec'
- if RUBY_VERSION >= "2.4.0"
- OpenSSL::PKey::EC.generate(EC_NAME)
- else
- domain_key = OpenSSL::PKey::EC.new(EC_NAME)
- domain_key.generate_key
- domain_key
- end
+ when "ec"
+ OpenSSL::PKey::EC.generate(EC_NAME)
else
raise Gem::Security::Exception,
"#{algorithm} algorithm not found. RSA, DSA, and EC algorithms are supported."
@@ -510,11 +498,11 @@ module Gem::Security
# Turns +email_address+ into an OpenSSL::X509::Name
def self.email_to_name(email_address)
- email_address = email_address.gsub(/[^\w@.-]+/i, '_')
+ email_address = email_address.gsub(/[^\w@.-]+/i, "_")
- cn, dcs = email_address.split '@'
+ cn, dcs = email_address.split "@"
- dcs = dcs.split '.'
+ dcs = dcs.split "."
OpenSSL::X509::Name.new([
["CN", cn],
@@ -571,17 +559,17 @@ module Gem::Security
signee_key = certificate.public_key
alt_name = certificate.extensions.find do |extension|
- extension.oid == 'subjectAltName'
+ extension.oid == "subjectAltName"
end
- extensions = extensions.merge 'subjectAltName' => alt_name.value if
+ extensions = extensions.merge "subjectAltName" => alt_name.value if
alt_name
issuer_alt_name = signing_cert.extensions.find do |extension|
- extension.oid == 'subjectAltName'
+ extension.oid == "subjectAltName"
end
- extensions = extensions.merge 'issuerAltName' => issuer_alt_name.value if
+ extensions = extensions.merge "issuerAltName" => issuer_alt_name.value if
issuer_alt_name
signed = create_cert signee_subject, signee_key, age, extensions, serial
@@ -597,7 +585,7 @@ module Gem::Security
def self.trust_dir
return @trust_dir if @trust_dir
- dir = File.join Gem.user_home, '.gem', 'trust'
+ dir = File.join Gem.user_home, ".gem", "trust"
@trust_dir ||= Gem::Security::TrustDir.new dir
end
@@ -617,8 +605,8 @@ module Gem::Security
def self.write(pemmable, path, permissions = 0600, passphrase = nil, cipher = KEY_CIPHER)
path = File.expand_path path
- File.open path, 'wb', permissions do |io|
- if passphrase and cipher
+ File.open path, "wb", permissions do |io|
+ if passphrase && cipher
io.write pemmable.to_pem cipher, passphrase
else
io.write pemmable.to_pem
@@ -633,9 +621,9 @@ module Gem::Security
end
if Gem::HAVE_OPENSSL
- require_relative 'security/policy'
- require_relative 'security/policies'
- require_relative 'security/trust_dir'
+ require_relative "security/policy"
+ require_relative "security/policies"
+ require_relative "security/trust_dir"
end
-require_relative 'security/signer'
+require_relative "security/signer"
diff --git a/lib/rubygems/security/policies.rb b/lib/rubygems/security/policies.rb
index 8f6ad99316..bdfe9ed43f 100644
--- a/lib/rubygems/security/policies.rb
+++ b/lib/rubygems/security/policies.rb
@@ -1,17 +1,18 @@
# frozen_string_literal: true
+
module Gem::Security
##
# No security policy: all package signature checks are disabled.
NoSecurity = Policy.new(
- 'No Security',
- :verify_data => false,
- :verify_signer => false,
- :verify_chain => false,
- :verify_root => false,
- :only_trusted => false,
- :only_signed => false
+ "No Security",
+ :verify_data => false,
+ :verify_signer => false,
+ :verify_chain => false,
+ :verify_root => false,
+ :only_trusted => false,
+ :only_signed => false
)
##
@@ -23,13 +24,13 @@ module Gem::Security
# easily spoofed, and is not recommended.
AlmostNoSecurity = Policy.new(
- 'Almost No Security',
- :verify_data => true,
- :verify_signer => false,
- :verify_chain => false,
- :verify_root => false,
- :only_trusted => false,
- :only_signed => false
+ "Almost No Security",
+ :verify_data => true,
+ :verify_signer => false,
+ :verify_chain => false,
+ :verify_root => false,
+ :only_trusted => false,
+ :only_signed => false
)
##
@@ -40,13 +41,13 @@ module Gem::Security
# is not recommended.
LowSecurity = Policy.new(
- 'Low Security',
- :verify_data => true,
- :verify_signer => true,
- :verify_chain => false,
- :verify_root => false,
- :only_trusted => false,
- :only_signed => false
+ "Low Security",
+ :verify_data => true,
+ :verify_signer => true,
+ :verify_chain => false,
+ :verify_root => false,
+ :only_trusted => false,
+ :only_signed => false
)
##
@@ -59,13 +60,13 @@ module Gem::Security
# gem off as unsigned.
MediumSecurity = Policy.new(
- 'Medium Security',
- :verify_data => true,
- :verify_signer => true,
- :verify_chain => true,
- :verify_root => true,
- :only_trusted => true,
- :only_signed => false
+ "Medium Security",
+ :verify_data => true,
+ :verify_signer => true,
+ :verify_chain => true,
+ :verify_root => true,
+ :only_trusted => true,
+ :only_signed => false
)
##
@@ -78,37 +79,37 @@ module Gem::Security
# a reasonable guarantee that the contents of the gem have not been altered.
HighSecurity = Policy.new(
- 'High Security',
- :verify_data => true,
- :verify_signer => true,
- :verify_chain => true,
- :verify_root => true,
- :only_trusted => true,
- :only_signed => true
+ "High Security",
+ :verify_data => true,
+ :verify_signer => true,
+ :verify_chain => true,
+ :verify_root => true,
+ :only_trusted => true,
+ :only_signed => true
)
##
# Policy used to verify a certificate and key when signing a gem
SigningPolicy = Policy.new(
- 'Signing Policy',
- :verify_data => false,
- :verify_signer => true,
- :verify_chain => true,
- :verify_root => true,
- :only_trusted => false,
- :only_signed => false
+ "Signing Policy",
+ :verify_data => false,
+ :verify_signer => true,
+ :verify_chain => true,
+ :verify_root => true,
+ :only_trusted => false,
+ :only_signed => false
)
##
# Hash of configured security policies
Policies = {
- 'NoSecurity' => NoSecurity,
- 'AlmostNoSecurity' => AlmostNoSecurity,
- 'LowSecurity' => LowSecurity,
- 'MediumSecurity' => MediumSecurity,
- 'HighSecurity' => HighSecurity,
+ "NoSecurity" => NoSecurity,
+ "AlmostNoSecurity" => AlmostNoSecurity,
+ "LowSecurity" => LowSecurity,
+ "MediumSecurity" => MediumSecurity,
+ "HighSecurity" => HighSecurity,
# SigningPolicy is not intended for use by `gem -P` so do not list it
}.freeze
diff --git a/lib/rubygems/security/policy.rb b/lib/rubygems/security/policy.rb
index 06eae073f4..9b9eac46bf 100644
--- a/lib/rubygems/security/policy.rb
+++ b/lib/rubygems/security/policy.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-require_relative '../user_interaction'
+
+require_relative "../user_interaction"
##
# A Gem::Security::Policy object encapsulates the settings for verifying
@@ -53,8 +54,8 @@ class Gem::Security::Policy
# and is valid for the given +time+.
def check_chain(chain, time)
- raise Gem::Security::Exception, 'missing signing chain' unless chain
- raise Gem::Security::Exception, 'empty signing chain' if chain.empty?
+ raise Gem::Security::Exception, "missing signing chain" unless chain
+ raise Gem::Security::Exception, "empty signing chain" if chain.empty?
begin
chain.each_cons 2 do |issuer, cert|
@@ -83,21 +84,21 @@ class Gem::Security::Policy
# If the +issuer+ is +nil+ no verification is performed.
def check_cert(signer, issuer, time)
- raise Gem::Security::Exception, 'missing signing certificate' unless
+ raise Gem::Security::Exception, "missing signing certificate" unless
signer
message = "certificate #{signer.subject}"
- if not_before = signer.not_before and not_before > time
+ if (not_before = signer.not_before) && not_before > time
raise Gem::Security::Exception,
"#{message} not valid before #{not_before}"
end
- if not_after = signer.not_after and not_after < time
+ if (not_after = signer.not_after) && not_after < time
raise Gem::Security::Exception, "#{message} not valid after #{not_after}"
end
- if issuer and not signer.verify issuer.public_key
+ if issuer && !signer.verify(issuer.public_key)
raise Gem::Security::Exception,
"#{message} was not issued by #{issuer.subject}"
end
@@ -109,10 +110,10 @@ class Gem::Security::Policy
# Ensures the public key of +key+ matches the public key in +signer+
def check_key(signer, key)
- unless signer and key
+ unless signer && key
return true unless @only_signed
- raise Gem::Security::Exception, 'missing key or signature'
+ raise Gem::Security::Exception, "missing key or signature"
end
raise Gem::Security::Exception,
@@ -127,11 +128,11 @@ class Gem::Security::Policy
# +time+.
def check_root(chain, time)
- raise Gem::Security::Exception, 'missing signing chain' unless chain
+ raise Gem::Security::Exception, "missing signing chain" unless chain
root = chain.first
- raise Gem::Security::Exception, 'missing root certificate' unless root
+ raise Gem::Security::Exception, "missing root certificate" unless root
raise Gem::Security::Exception,
"root certificate #{root.subject} is not self-signed " +
@@ -146,11 +147,11 @@ class Gem::Security::Policy
# the digests of the two certificates match according to +digester+
def check_trust(chain, digester, trust_dir)
- raise Gem::Security::Exception, 'missing signing chain' unless chain
+ raise Gem::Security::Exception, "missing signing chain" unless chain
root = chain.first
- raise Gem::Security::Exception, 'missing root certificate' unless root
+ raise Gem::Security::Exception, "missing root certificate" unless root
path = Gem::Security.trust_dir.cert_path root
@@ -182,7 +183,7 @@ class Gem::Security::Policy
def subject(certificate) # :nodoc:
certificate.extensions.each do |extension|
- next unless extension.oid == 'subjectAltName'
+ next unless extension.oid == "subjectAltName"
return extension.value
end
@@ -206,7 +207,7 @@ class Gem::Security::Policy
# If +key+ is given it is used to validate the signing certificate.
def verify(chain, key = nil, digests = {}, signatures = {},
- full_name = '(unknown)')
+ full_name = "(unknown)")
if signatures.empty?
if @only_signed
raise Gem::Security::Exception,
@@ -230,8 +231,8 @@ class Gem::Security::Policy
end
if @verify_data
- raise Gem::Security::Exception, 'no digests provided (probable bug)' if
- signer_digests.nil? or signer_digests.empty?
+ raise Gem::Security::Exception, "no digests provided (probable bug)" if
+ signer_digests.nil? || signer_digests.empty?
else
signer_digests = {}
end
@@ -248,7 +249,7 @@ class Gem::Security::Policy
if @only_trusted
check_trust chain, digester, trust_dir
- elsif signatures.empty? and digests.empty?
+ elsif signatures.empty? && digests.empty?
# trust is irrelevant if there's no signatures to verify
else
alert_warning "#{subject signer} is not trusted for #{full_name}"
diff --git a/lib/rubygems/security/signer.rb b/lib/rubygems/security/signer.rb
index 968cf88973..d76af03eb8 100644
--- a/lib/rubygems/security/signer.rb
+++ b/lib/rubygems/security/signer.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Basic OpenSSL-based package signing class.
@@ -42,7 +43,7 @@ class Gem::Security::Signer
def self.re_sign_cert(expired_cert, expired_cert_path, private_key)
return unless expired_cert.not_after < Time.now
- expiry = expired_cert.not_after.strftime('%Y%m%d%H%M%S')
+ expiry = expired_cert.not_after.strftime("%Y%m%d%H%M%S")
expired_cert_file = "#{File.basename(expired_cert_path)}.expired.#{expiry}"
new_expired_cert_path = File.join(Gem.user_home, ".gem", expired_cert_file)
@@ -105,7 +106,7 @@ class Gem::Security::Signer
# this value is preferred, otherwise the subject is used.
def extract_name(cert) # :nodoc:
- subject_alt_name = cert.extensions.find {|e| 'subjectAltName' == e.oid }
+ subject_alt_name = cert.extensions.find {|e| "subjectAltName" == e.oid }
if subject_alt_name
/\Aemail:/ =~ subject_alt_name.value # rubocop:disable Performance/StartWith
@@ -139,9 +140,9 @@ class Gem::Security::Signer
def sign(data)
return unless @key
- raise Gem::Security::Exception, 'no certs provided' if @cert_chain.empty?
+ raise Gem::Security::Exception, "no certs provided" if @cert_chain.empty?
- if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now
+ if @cert_chain.length == 1 && @cert_chain.last.not_after < Time.now
alert("Your certificate has expired, trying to re-sign it...")
re_sign_key(
@@ -182,7 +183,7 @@ class Gem::Security::Signer
return unless disk_key
if disk_key.to_pem == @key.to_pem && disk_cert == old_cert.to_pem
- expiry = old_cert.not_after.strftime('%Y%m%d%H%M%S')
+ expiry = old_cert.not_after.strftime("%Y%m%d%H%M%S")
old_cert_file = "gem-public_cert.pem.expired.#{expiry}"
old_cert_path = File.join(Gem.user_home, ".gem", old_cert_file)
diff --git a/lib/rubygems/security/trust_dir.rb b/lib/rubygems/security/trust_dir.rb
index 456947274c..4de33068f9 100644
--- a/lib/rubygems/security/trust_dir.rb
+++ b/lib/rubygems/security/trust_dir.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The TrustDir manages the trusted certificates for gem signature
# verification.
@@ -8,7 +9,7 @@ class Gem::Security::TrustDir
# Default permissions for the trust directory and its contents
DEFAULT_PERMISSIONS = {
- :trust_dir => 0700,
+ :trust_dir => 0700,
:trusted_cert => 0600,
}.freeze
@@ -41,7 +42,7 @@ class Gem::Security::TrustDir
def each_certificate
return enum_for __method__ unless block_given?
- glob = File.join @dir, '*.pem'
+ glob = File.join @dir, "*.pem"
Dir[glob].each do |certificate_file|
begin
@@ -92,7 +93,7 @@ class Gem::Security::TrustDir
destination = cert_path certificate
- File.open destination, 'wb', 0600 do |io|
+ File.open destination, "wb", 0600 do |io|
io.write certificate.to_pem
io.chmod(@permissions[:trusted_cert])
end
@@ -104,7 +105,7 @@ class Gem::Security::TrustDir
# permissions.
def verify
- require 'fileutils'
+ require "fileutils"
if File.exist? @dir
raise Gem::Security::Exception,
"trust directory #{@dir} is not a directory" unless
diff --git a/lib/rubygems/security_option.rb b/lib/rubygems/security_option.rb
index a4c570ded5..f288ebd047 100644
--- a/lib/rubygems/security_option.rb
+++ b/lib/rubygems/security_option.rb
@@ -1,11 +1,12 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative '../rubygems'
+require_relative "../rubygems"
# forward-declare
@@ -20,9 +21,9 @@ end
module Gem::SecurityOption
def add_security_option
Gem::OptionParser.accept Gem::Security::Policy do |value|
- require_relative 'security'
+ require_relative "security"
- raise Gem::OptionParser::InvalidArgument, 'OpenSSL not installed' unless
+ raise Gem::OptionParser::InvalidArgument, "OpenSSL not installed" unless
defined?(Gem::Security::HighSecurity)
policy = Gem::Security::Policies[value]
@@ -33,9 +34,9 @@ module Gem::SecurityOption
policy
end
- add_option(:"Install/Update", '-P', '--trust-policy POLICY',
+ add_option(:"Install/Update", "-P", "--trust-policy POLICY",
Gem::Security::Policy,
- 'Specify gem trust policy') do |value, options|
+ "Specify gem trust policy") do |value, options|
options[:security_policy] = value
end
end
diff --git a/lib/rubygems/shellwords.rb b/lib/rubygems/shellwords.rb
new file mode 100644
index 0000000000..741dccb363
--- /dev/null
+++ b/lib/rubygems/shellwords.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+autoload :Shellwords, "shellwords"
diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb
index 5f49a0d216..aa0cbc1641 100644
--- a/lib/rubygems/source.rb
+++ b/lib/rubygems/source.rb
@@ -12,9 +12,9 @@ class Gem::Source
include Gem::Text
FILES = { # :nodoc:
- :released => 'specs',
- :latest => 'latest_specs',
- :prerelease => 'prerelease_specs',
+ :released => "specs",
+ :latest => "latest_specs",
+ :prerelease => "prerelease_specs",
}.freeze
##
@@ -62,7 +62,7 @@ class Gem::Source
end
def ==(other) # :nodoc:
- self.class === other and @uri == other.uri
+ self.class === other && @uri == other.uri
end
alias_method :eql?, :== # :nodoc:
@@ -71,7 +71,7 @@ class Gem::Source
# Returns a Set that can fetch specifications from this source.
def dependency_resolver_set # :nodoc:
- return Gem::Resolver::IndexSet.new self if 'file' == uri.scheme
+ return Gem::Resolver::IndexSet.new self if "file" == uri.scheme
fetch_uri = if uri.host == "rubygems.org"
index_uri = uri.dup
@@ -141,7 +141,7 @@ class Gem::Source
return spec if spec
end
- source_uri.path << '.rz'
+ source_uri.path << ".rz"
spec = fetcher.fetch_path source_uri
spec = Gem::Util.inflate spec
@@ -150,7 +150,7 @@ class Gem::Source
require "fileutils"
FileUtils.mkdir_p cache_dir
- File.open local_spec, 'wb' do |io|
+ File.open local_spec, "wb" do |io|
io.write spec
end
end
@@ -209,13 +209,13 @@ class Gem::Source
end
def pretty_print(q) # :nodoc:
- q.group 2, '[Remote:', ']' do
+ q.group 2, "[Remote:", "]" do
q.breakable
q.text @uri.to_s
if api = uri
q.breakable
- q.text 'API URI: '
+ q.text "API URI: "
q.text api.to_s
end
end
@@ -229,13 +229,13 @@ class Gem::Source
private
def enforce_trailing_slash(uri)
- uri.merge(uri.path.gsub(/\/+$/, '') + '/')
+ uri.merge(uri.path.gsub(/\/+$/, "") + "/")
end
end
-require_relative 'source/git'
-require_relative 'source/installed'
-require_relative 'source/specific_file'
-require_relative 'source/local'
-require_relative 'source/lock'
-require_relative 'source/vendor'
+require_relative "source/git"
+require_relative "source/installed"
+require_relative "source/specific_file"
+require_relative "source/local"
+require_relative "source/lock"
+require_relative "source/vendor"
diff --git a/lib/rubygems/source/git.rb b/lib/rubygems/source/git.rb
index 1d964eb59a..7ac685f978 100644
--- a/lib/rubygems/source/git.rb
+++ b/lib/rubygems/source/git.rb
@@ -53,12 +53,12 @@ class Gem::Source::Git < Gem::Source
@uri = Gem::Uri.parse(repository)
@name = name
@repository = repository
- @reference = reference
+ @reference = reference || "HEAD"
@need_submodules = submodules
@remote = true
@root_dir = Gem.dir
- @git = ENV['git'] || 'git'
+ @git = ENV["git"] || "git"
end
def <=>(other)
@@ -76,10 +76,10 @@ class Gem::Source::Git < Gem::Source
end
def ==(other) # :nodoc:
- super and
- @name == other.name and
- @repository == other.repository and
- @reference == other.reference and
+ super &&
+ @name == other.name &&
+ @repository == other.repository &&
+ @reference == other.reference &&
@need_submodules == other.need_submodules
end
@@ -92,18 +92,18 @@ class Gem::Source::Git < Gem::Source
return false unless File.exist? repo_cache_dir
unless File.exist? install_dir
- system @git, 'clone', '--quiet', '--no-checkout',
+ system @git, "clone", "--quiet", "--no-checkout",
repo_cache_dir, install_dir
end
Dir.chdir install_dir do
- system @git, 'fetch', '--quiet', '--force', '--tags', install_dir
+ system @git, "fetch", "--quiet", "--force", "--tags", install_dir
- success = system @git, 'reset', '--quiet', '--hard', rev_parse
+ success = system @git, "reset", "--quiet", "--hard", rev_parse
if @need_submodules
require "open3"
- _, status = Open3.capture2e(@git, 'submodule', 'update', '--quiet', '--init', '--recursive')
+ _, status = Open3.capture2e(@git, "submodule", "update", "--quiet", "--init", "--recursive")
success &&= status.success?
end
@@ -120,11 +120,11 @@ class Gem::Source::Git < Gem::Source
if File.exist? repo_cache_dir
Dir.chdir repo_cache_dir do
- system @git, 'fetch', '--quiet', '--force', '--tags',
- @repository, 'refs/heads/*:refs/heads/*'
+ system @git, "fetch", "--quiet", "--force", "--tags",
+ @repository, "refs/heads/*:refs/heads/*"
end
else
- system @git, 'clone', '--quiet', '--bare', '--no-hardlinks',
+ system @git, "clone", "--quiet", "--bare", "--no-hardlinks",
@repository, repo_cache_dir
end
end
@@ -133,7 +133,7 @@ class Gem::Source::Git < Gem::Source
# Directory where git gems get unpacked and so-forth.
def base_dir # :nodoc:
- File.join @root_dir, 'bundler'
+ File.join @root_dir, "bundler"
end
##
@@ -155,11 +155,11 @@ class Gem::Source::Git < Gem::Source
def install_dir # :nodoc:
return unless File.exist? repo_cache_dir
- File.join base_dir, 'gems', "#{@name}-#{dir_shortref}"
+ File.join base_dir, "gems", "#{@name}-#{dir_shortref}"
end
def pretty_print(q) # :nodoc:
- q.group 2, '[Git: ', ']' do
+ q.group 2, "[Git: ", "]" do
q.breakable
q.text @repository
@@ -172,7 +172,7 @@ class Gem::Source::Git < Gem::Source
# The directory where the git gem's repository will be cached.
def repo_cache_dir # :nodoc:
- File.join @root_dir, 'cache', 'bundler', 'git', "#{@name}-#{uri_hash}"
+ File.join @root_dir, "cache", "bundler", "git", "#{@name}-#{uri_hash}"
end
##
@@ -182,7 +182,7 @@ class Gem::Source::Git < Gem::Source
hash = nil
Dir.chdir repo_cache_dir do
- hash = Gem::Util.popen(@git, 'rev-parse', @reference).strip
+ hash = Gem::Util.popen(@git, "rev-parse", @reference).strip
end
raise Gem::Exception,
@@ -201,7 +201,7 @@ class Gem::Source::Git < Gem::Source
return [] unless install_dir
Dir.chdir install_dir do
- Dir['{,*,*/*}.gemspec'].map do |spec_file|
+ Dir["{,*,*/*}.gemspec"].map do |spec_file|
directory = File.dirname spec_file
file = File.basename spec_file
@@ -211,7 +211,7 @@ class Gem::Source::Git < Gem::Source
spec.base_dir = base_dir
spec.extension_dir =
- File.join base_dir, 'extensions', Gem::Platform.local.to_s,
+ File.join base_dir, "extensions", Gem::Platform.local.to_s,
Gem.extension_api_version, "#{name}-#{dir_shortref}"
spec.full_gem_path = File.dirname spec.loaded_from if spec
@@ -226,11 +226,11 @@ class Gem::Source::Git < Gem::Source
# A hash for the git gem based on the git repository URI.
def uri_hash # :nodoc:
- require_relative '../openssl'
+ require_relative "../openssl"
normalized =
if @repository =~ %r{^\w+://(\w+@)?}
- uri = URI(@repository).normalize.to_s.sub %r{/$},''
+ uri = URI(@repository).normalize.to_s.sub %r{/$},""
uri.sub(/\A(\w+)/) { $1.downcase }
else
@repository
diff --git a/lib/rubygems/source/installed.rb b/lib/rubygems/source/installed.rb
index 7e1dd7af5a..f5d3f06d6a 100644
--- a/lib/rubygems/source/installed.rb
+++ b/lib/rubygems/source/installed.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Represents an installed gem. This is used for dependency resolution.
@@ -33,6 +34,6 @@ class Gem::Source::Installed < Gem::Source
end
def pretty_print(q) # :nodoc:
- q.text '[Installed]'
+ q.text "[Installed]"
end
end
diff --git a/lib/rubygems/source/local.rb b/lib/rubygems/source/local.rb
index 078b06203f..5068b0b906 100644
--- a/lib/rubygems/source/local.rb
+++ b/lib/rubygems/source/local.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The local source finds gems in the current directory for fulfilling
# dependencies.
@@ -29,7 +30,7 @@ class Gem::Source::Local < Gem::Source
end
def inspect # :nodoc:
- keys = @specs ? @specs.keys.sort : 'NOT LOADED'
+ keys = @specs ? @specs.keys.sort : "NOT LOADED"
"#<%s specs: %p>" % [self.class, keys]
end
@@ -121,7 +122,7 @@ class Gem::Source::Local < Gem::Source
end
def pretty_print(q) # :nodoc:
- q.group 2, '[Local gems:', ']' do
+ q.group 2, "[Local gems:", "]" do
q.breakable
q.seplist @specs.keys do |v|
q.text v.full_name
diff --git a/lib/rubygems/source/lock.rb b/lib/rubygems/source/lock.rb
index 49f097467b..f9388bbd61 100644
--- a/lib/rubygems/source/lock.rb
+++ b/lib/rubygems/source/lock.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A Lock source wraps an installed gem's source and sorts before other sources
# during dependency resolution. This allows RubyGems to prefer gems from
diff --git a/lib/rubygems/source/specific_file.rb b/lib/rubygems/source/specific_file.rb
index 24db1440dd..61965c2644 100644
--- a/lib/rubygems/source/specific_file.rb
+++ b/lib/rubygems/source/specific_file.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A source representing a single .gem file. This is used for installation of
# local gems.
@@ -42,7 +43,7 @@ class Gem::Source::SpecificFile < Gem::Source
end
def pretty_print(q) # :nodoc:
- q.group 2, '[SpecificFile:', ']' do
+ q.group 2, "[SpecificFile:", "]" do
q.breakable
q.text @path
end
diff --git a/lib/rubygems/source/vendor.rb b/lib/rubygems/source/vendor.rb
index 543acf1388..12161b8cf5 100644
--- a/lib/rubygems/source/vendor.rb
+++ b/lib/rubygems/source/vendor.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# This represents a vendored source that is similar to an installed gem.
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index 4033e2efa3..a5a4fa0c0f 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
-require_relative 'remote_fetcher'
-require_relative 'user_interaction'
-require_relative 'errors'
-require_relative 'text'
-require_relative 'name_tuple'
+
+require_relative "remote_fetcher"
+require_relative "user_interaction"
+require_relative "errors"
+require_relative "text"
+require_relative "name_tuple"
##
# SpecFetcher handles metadata updates from remote gem repositories.
@@ -98,7 +99,7 @@ class Gem::SpecFetcher
found[source] = specs.select do |tup|
if dependency.match?(tup)
- if matching_platform and !Gem::Platform.match_gem?(tup.platform, tup.name)
+ if matching_platform && !Gem::Platform.match_gem?(tup.platform, tup.name)
pm = (
rejected_specs[dependency] ||= \
Gem::PlatformMismatch.new(tup.name, tup.version))
@@ -171,19 +172,19 @@ class Gem::SpecFetcher
# alternative gem names.
def suggest_gems_from_name(gem_name, type = :latest, num_results = 5)
- gem_name = gem_name.downcase.tr('_-', '')
+ gem_name = gem_name.downcase.tr("_-", "")
max = gem_name.size / 2
names = available_specs(type).first.values.flatten(1)
matches = names.map do |n|
next unless n.match_platform?
- [n.name, 0] if n.name.downcase.tr('_-', '').include?(gem_name)
+ [n.name, 0] if n.name.downcase.tr("_-", "").include?(gem_name)
end.compact
if matches.length < num_results
matches += names.map do |n|
next unless n.match_platform?
- distance = levenshtein_distance gem_name, n.name.downcase.tr('_-', '')
+ distance = levenshtein_distance gem_name, n.name.downcase.tr("_-", "")
next if distance >= max
return [n.name] if distance == 0
[n.name, distance]
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index a8e52e58d5..7611e1ba1f 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
@@ -6,11 +7,11 @@
# See LICENSE.txt for permissions.
#++
-require_relative 'deprecate'
-require_relative 'basic_specification'
-require_relative 'stub_specification'
-require_relative 'platform'
-require_relative 'util/list'
+require_relative "deprecate"
+require_relative "basic_specification"
+require_relative "stub_specification"
+require_relative "platform"
+require_relative "util/list"
##
# The Specification class contains the information for a gem. Typically
@@ -74,29 +75,29 @@ class Gem::Specification < Gem::BasicSpecification
# key should be equal to the CURRENT_SPECIFICATION_VERSION.
SPECIFICATION_VERSION_HISTORY = { # :nodoc:
- -1 => ['(RubyGems versions up to and including 0.7 did not have versioned specifications)'],
- 1 => [
+ -1 => ["(RubyGems versions up to and including 0.7 did not have versioned specifications)"],
+ 1 => [
'Deprecated "test_suite_file" in favor of the new, but equivalent, "test_files"',
'"test_file=x" is a shortcut for "test_files=[x]"',
],
2 => [
'Added "required_rubygems_version"',
- 'Now forward-compatible with future versions',
+ "Now forward-compatible with future versions",
],
3 => [
- 'Added Fixnum validation to the specification_version',
+ "Added Fixnum validation to the specification_version",
],
4 => [
- 'Added sandboxed freeform metadata to the specification version.',
+ "Added sandboxed freeform metadata to the specification version.",
],
}.freeze
MARSHAL_FIELDS = { # :nodoc:
-1 => 16,
- 1 => 16,
- 2 => 16,
- 3 => 17,
- 4 => 18,
+ 1 => 16,
+ 2 => 16,
+ 3 => 17,
+ 4 => 18,
}.freeze
today = Time.now.utc
@@ -124,35 +125,35 @@ class Gem::Specification < Gem::BasicSpecification
# Map of attribute names to default values.
@@default_value = {
- :authors => [],
- :autorequire => nil,
- :bindir => 'bin',
- :cert_chain => [],
- :date => nil,
- :dependencies => [],
- :description => nil,
- :email => nil,
- :executables => [],
- :extensions => [],
- :extra_rdoc_files => [],
- :files => [],
- :homepage => nil,
- :licenses => [],
- :metadata => {},
- :name => nil,
- :platform => Gem::Platform::RUBY,
- :post_install_message => nil,
- :rdoc_options => [],
- :require_paths => ['lib'],
- :required_ruby_version => Gem::Requirement.default,
+ :authors => [],
+ :autorequire => nil,
+ :bindir => "bin",
+ :cert_chain => [],
+ :date => nil,
+ :dependencies => [],
+ :description => nil,
+ :email => nil,
+ :executables => [],
+ :extensions => [],
+ :extra_rdoc_files => [],
+ :files => [],
+ :homepage => nil,
+ :licenses => [],
+ :metadata => {},
+ :name => nil,
+ :platform => Gem::Platform::RUBY,
+ :post_install_message => nil,
+ :rdoc_options => [],
+ :require_paths => ["lib"],
+ :required_ruby_version => Gem::Requirement.default,
:required_rubygems_version => Gem::Requirement.default,
- :requirements => [],
- :rubygems_version => Gem::VERSION,
- :signing_key => nil,
- :specification_version => CURRENT_SPECIFICATION_VERSION,
- :summary => nil,
- :test_files => [],
- :version => nil,
+ :requirements => [],
+ :rubygems_version => Gem::VERSION,
+ :signing_key => nil,
+ :specification_version => CURRENT_SPECIFICATION_VERSION,
+ :summary => nil,
+ :test_files => [],
+ :version => nil,
}.freeze
# rubocop:disable Style/MutableConstant
@@ -338,7 +339,7 @@ class Gem::Specification < Gem::BasicSpecification
# The simplest way is to specify the standard SPDX ID
# https://spdx.org/licenses/ for the license.
# Ideally, you should pick one that is OSI (Open Source Initiative)
- # http://opensource.org/licenses/alphabetical approved.
+ # https://opensource.org/licenses/ approved.
#
# The most commonly used OSI-approved licenses are MIT and Apache-2.0.
# GitHub also provides a license picker at http://choosealicense.com/.
@@ -473,7 +474,7 @@ class Gem::Specification < Gem::BasicSpecification
# spec.platform = Gem::Platform.local
def platform=(platform)
- if @original_platform.nil? or
+ if @original_platform.nil? ||
@original_platform == Gem::Platform::RUBY
@original_platform = platform
end
@@ -489,12 +490,12 @@ class Gem::Specification < Gem::BasicSpecification
# legacy constants
when nil, Gem::Platform::RUBY then
@new_platform = Gem::Platform::RUBY
- when 'mswin32' then # was Gem::Platform::WIN32
- @new_platform = Gem::Platform.new 'x86-mswin32'
- when 'i586-linux' then # was Gem::Platform::LINUX_586
- @new_platform = Gem::Platform.new 'x86-linux'
- when 'powerpc-darwin' then # was Gem::Platform::DARWIN
- @new_platform = Gem::Platform.new 'ppc-darwin'
+ when "mswin32" then # was Gem::Platform::WIN32
+ @new_platform = Gem::Platform.new "x86-mswin32"
+ when "i586-linux" then # was Gem::Platform::LINUX_586
+ @new_platform = Gem::Platform.new "x86-linux"
+ when "powerpc-darwin" then # was Gem::Platform::DARWIN
+ @new_platform = Gem::Platform.new "ppc-darwin"
else
@new_platform = Gem::Platform.new platform
end
@@ -1022,6 +1023,12 @@ class Gem::Specification < Gem::BasicSpecification
end
##
+ # Find the best specification matching a +full_name+.
+ def self.find_by_full_name(full_name)
+ stubs.find {|s| s.full_name == full_name }&.to_spec
+ end
+
+ ##
# Return the best specification that contains the file matching +path+.
def self.find_by_path(path)
@@ -1041,12 +1048,12 @@ class Gem::Specification < Gem::BasicSpecification
next if s.activated?
s.contains_requirable_file? path
end
- stub && stub.to_spec
+ stub&.to_spec
end
def self.find_active_stub_by_path(path)
stub = @@active_stub_with_requirable_file[path] ||= (stubs.find do |s|
- s.activated? and s.contains_requirable_file? path
+ s.activated? && s.contains_requirable_file?(path)
end || NOT_FOUND)
stub.this
end
@@ -1149,7 +1156,7 @@ class Gem::Specification < Gem::BasicSpecification
file = file.dup.tap(&Gem::UNTAINT)
return unless File.file?(file)
- code = Gem.open_file(file, 'r:UTF-8:-', &:read)
+ code = Gem.open_file(file, "r:UTF-8:-", &:read)
code.tap(&Gem::UNTAINT)
@@ -1234,7 +1241,7 @@ class Gem::Specification < Gem::BasicSpecification
latest_remote = remotes.sort.last
yield [local_spec, latest_remote] if
- latest_remote and local_spec.version < latest_remote
+ latest_remote && local_spec.version < latest_remote
end
nil
@@ -1293,6 +1300,8 @@ class Gem::Specification < Gem::BasicSpecification
def self._load(str)
Gem.load_yaml
+ yaml_set = false
+
array = begin
Marshal.load str
rescue ArgumentError => e
@@ -1305,7 +1314,10 @@ class Gem::Specification < Gem::BasicSpecification
message = e.message
raise unless message.include?("YAML::")
- Object.const_set "YAML", Psych unless Object.const_defined?(:YAML)
+ unless Object.const_defined?(:YAML)
+ Object.const_set "YAML", Psych
+ yaml_set = true
+ end
if message.include?("YAML::Syck::")
YAML.const_set "Syck", YAML unless YAML.const_defined?(:Syck)
@@ -1316,6 +1328,8 @@ class Gem::Specification < Gem::BasicSpecification
end
retry
+ ensure
+ Object.__send__(:remove_const, "YAML") if yaml_set
end
spec = Gem::Specification.new
@@ -1390,7 +1404,7 @@ class Gem::Specification < Gem::BasicSpecification
@required_rubygems_version,
@original_platform,
@dependencies,
- '', # rubyforge_project
+ "", # rubyforge_project
@email,
@authors,
@description,
@@ -1556,7 +1570,7 @@ class Gem::Specification < Gem::BasicSpecification
# Singular reader for #authors. Returns the first author in the list
def author
- val = authors and val.first
+ (val = authors) && val.first
end
##
@@ -1606,9 +1620,11 @@ class Gem::Specification < Gem::BasicSpecification
def build_extensions # :nodoc:
return if extensions.empty?
return if default_gem?
+ # we need to fresh build when same name and version of default gems
+ return if self.class.find_by_full_name(full_name)&.default_gem?
return if File.exist? gem_build_complete_path
return if !File.writable?(base_dir)
- return if !File.exist?(File.join(base_dir, 'extensions'))
+ return if !File.exist?(File.join(base_dir, "extensions"))
begin
# We need to require things in $LOAD_PATH without looking for the
@@ -1616,9 +1632,9 @@ class Gem::Specification < Gem::BasicSpecification
unresolved_deps = Gem::Specification.unresolved_deps.dup
Gem::Specification.unresolved_deps.clear
- require_relative 'config_file'
- require_relative 'ext'
- require_relative 'user_interaction'
+ require_relative "config_file"
+ require_relative "ext"
+ require_relative "user_interaction"
ui = Gem::SilentUI.new
Gem::DefaultUserInteraction.use_ui ui do
@@ -1626,7 +1642,7 @@ class Gem::Specification < Gem::BasicSpecification
builder.build_extensions
end
ensure
- ui.close if ui
+ ui&.close
Gem::Specification.unresolved_deps.replace unresolved_deps
end
end
@@ -1668,7 +1684,7 @@ class Gem::Specification < Gem::BasicSpecification
conflicts = {}
self.runtime_dependencies.each do |dep|
spec = Gem.loaded_specs[dep.name]
- if spec and not spec.satisfies_requirement? dep
+ if spec && !spec.satisfies_requirement?(dep)
(conflicts[spec] ||= []) << dep
end
end
@@ -1695,7 +1711,7 @@ class Gem::Specification < Gem::BasicSpecification
self.dependencies.any? do |dep|
if dep.runtime?
spec = Gem.loaded_specs[dep.name]
- spec and not spec.satisfies_requirement? dep
+ spec && !spec.satisfies_requirement?(dep)
else
false
end
@@ -1716,7 +1732,7 @@ class Gem::Specification < Gem::BasicSpecification
DateLike = Object.new # :nodoc:
def DateLike.===(obj) # :nodoc:
- defined?(::Date) and Date === obj
+ defined?(::Date) && Date === obj
end
DateTimeFormat = # :nodoc:
@@ -1756,9 +1772,9 @@ class Gem::Specification < Gem::BasicSpecification
# executable now. See Gem.bin_path.
def default_executable # :nodoc:
- if defined?(@default_executable) and @default_executable
+ if defined?(@default_executable) && @default_executable
result = @default_executable
- elsif @executables and @executables.size == 1
+ elsif @executables && @executables.size == 1
result = Array(@executables).first
else
result = nil
@@ -1837,7 +1853,7 @@ class Gem::Specification < Gem::BasicSpecification
# spec.doc_dir 'ri' # => "/path/to/gem_repo/doc/a-1/ri"
def doc_dir(type = nil)
- @doc_dir ||= File.join base_dir, 'doc', full_name
+ @doc_dir ||= File.join base_dir, "doc", full_name
if type
File.join @doc_dir, type
@@ -1849,17 +1865,17 @@ class Gem::Specification < Gem::BasicSpecification
def encode_with(coder) # :nodoc:
mark_version
- coder.add 'name', @name
- coder.add 'version', @version
+ coder.add "name", @name
+ coder.add "version", @version
platform = case @original_platform
- when nil, '' then
- 'ruby'
+ when nil, "" then
+ "ruby"
when String then
@original_platform
else
@original_platform.to_s
end
- coder.add 'platform', platform
+ coder.add "platform", platform
attributes = @@attributes.map(&:to_s) - %w[name version platform]
attributes.each do |name|
@@ -1875,7 +1891,7 @@ class Gem::Specification < Gem::BasicSpecification
# Singular accessor for #executables
def executable
- val = executables and val.first
+ (val = executables) && val.first
end
##
@@ -1987,7 +2003,7 @@ class Gem::Specification < Gem::BasicSpecification
# True if this gem has files in test_files
def has_unit_tests? # :nodoc:
- not test_files.empty?
+ !test_files.empty?
end
# :stopdoc:
@@ -2040,7 +2056,7 @@ class Gem::Specification < Gem::BasicSpecification
self.name = name if name
self.version = version if version
- if platform = Gem.platforms.last and platform != Gem::Platform::RUBY and platform != Gem::Platform.local
+ if (platform = Gem.platforms.last) && platform != Gem::Platform::RUBY && platform != Gem::Platform.local
self.platform = platform
end
@@ -2155,8 +2171,8 @@ class Gem::Specification < Gem::BasicSpecification
return
end
- if @specification_version > CURRENT_SPECIFICATION_VERSION and
- sym.to_s.end_with?("=")
+ if @specification_version > CURRENT_SPECIFICATION_VERSION &&
+ sym.to_s.end_with?("=")
warn "ignoring #{sym} loading #{full_name}" if $DEBUG
else
super
@@ -2182,7 +2198,7 @@ class Gem::Specification < Gem::BasicSpecification
# file list.
def normalize
- if defined?(@extra_rdoc_files) and @extra_rdoc_files
+ if defined?(@extra_rdoc_files) && @extra_rdoc_files
@extra_rdoc_files.uniq!
@files ||= []
@files.concat(@extra_rdoc_files)
@@ -2207,7 +2223,7 @@ class Gem::Specification < Gem::BasicSpecification
# platform. For use with legacy gems.
def original_name # :nodoc:
- if platform == Gem::Platform::RUBY or platform.nil?
+ if platform == Gem::Platform::RUBY || platform.nil?
"#{@name}-#{@version}"
else
"#{@name}-#{@version}-#{@original_platform}"
@@ -2225,11 +2241,11 @@ class Gem::Specification < Gem::BasicSpecification
# The platform this gem runs on. See Gem::Platform for details.
def platform
- @new_platform ||= Gem::Platform::RUBY
+ @new_platform ||= Gem::Platform::RUBY # rubocop:disable Naming/MemoizedInstanceVariableName
end
def pretty_print(q) # :nodoc:
- q.group 2, 'Gem::Specification.new do |s|', 'end' do
+ q.group 2, "Gem::Specification.new do |s|", "end" do
q.breakable
attributes = @@attributes - [:name, :version]
@@ -2240,8 +2256,8 @@ class Gem::Specification < Gem::BasicSpecification
attributes.each do |attr_name|
current_value = self.send attr_name
current_value = current_value.sort if %i[files test_files].include? attr_name
- if current_value != default_value(attr_name) or
- self.class.required_attribute? attr_name
+ if current_value != default_value(attr_name) ||
+ self.class.required_attribute?(attr_name)
q.text "s.#{attr_name} = "
@@ -2299,7 +2315,7 @@ class Gem::Specification < Gem::BasicSpecification
# Singular accessor for #require_paths
def require_path
- val = require_paths and val.first
+ (val = require_paths) && val.first
end
##
@@ -2324,7 +2340,7 @@ class Gem::Specification < Gem::BasicSpecification
# Returns the full path to this spec's ri directory.
def ri_dir
- @ri_dir ||= File.join base_dir, 'ri', full_name
+ @ri_dir ||= File.join base_dir, "ri", full_name
end
##
@@ -2334,13 +2350,13 @@ class Gem::Specification < Gem::BasicSpecification
def ruby_code(obj)
case obj
when String then obj.dump + ".freeze"
- when Array then '[' + obj.map {|x| ruby_code x }.join(", ") + ']'
+ when Array then "[" + obj.map {|x| ruby_code x }.join(", ") + "]"
when Hash then
seg = obj.keys.sort.map {|k| "#{k.to_s.dump} => #{obj[k].to_s.dump}" }
"{ #{seg.join(', ')} }"
when Gem::Version then obj.to_s.dump
- when DateLike then obj.strftime('%Y-%m-%d').dump
- when Time then obj.strftime('%Y-%m-%d').dump
+ when DateLike then obj.strftime("%Y-%m-%d").dump
+ when Time then obj.strftime("%Y-%m-%d").dump
when Numeric then obj.inspect
when true, false, nil then obj.inspect
when Gem::Platform then "Gem::Platform.new(#{obj.to_a.inspect})"
@@ -2374,7 +2390,7 @@ class Gem::Specification < Gem::BasicSpecification
def satisfies_requirement?(dependency)
return @name == dependency.name &&
- dependency.requirement.satisfied_by?(@version)
+ dependency.requirement.satisfied_by?(@version)
end
##
@@ -2428,7 +2444,7 @@ class Gem::Specification < Gem::BasicSpecification
# Singular accessor for #test_files
def test_file # :nodoc:
- val = test_files and val.first
+ (val = test_files) && val.first
end
##
@@ -2450,7 +2466,7 @@ class Gem::Specification < Gem::BasicSpecification
@test_files = [@test_suite_file].flatten
@test_suite_file = nil
end
- if defined?(@test_files) and @test_files
+ if defined?(@test_files) && @test_files
@test_files
else
@test_files = []
@@ -2474,13 +2490,13 @@ class Gem::Specification < Gem::BasicSpecification
result << " s.name = #{ruby_code name}"
result << " s.version = #{ruby_code version}"
- unless platform.nil? or platform == Gem::Platform::RUBY
+ unless platform.nil? || platform == Gem::Platform::RUBY
result << " s.platform = #{ruby_code original_platform}"
end
result << ""
result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version="
- if metadata and !metadata.empty?
+ if metadata && !metadata.empty?
result << " s.metadata = #{ruby_code metadata} if s.respond_to? :metadata="
end
result << " s.require_paths = #{ruby_code raw_require_paths}"
@@ -2562,14 +2578,14 @@ class Gem::Specification < Gem::BasicSpecification
# back, we have to check again here to make sure that our
# psych code was properly loaded, and load it if not.
unless Gem.const_defined?(:NoAliasYAMLTree)
- require_relative 'psych_tree'
+ require_relative "psych_tree"
end
builder = Gem::NoAliasYAMLTree.create
builder << self
ast = builder.tree
- require 'stringio'
+ require "stringio"
io = StringIO.new
io.set_encoding Encoding::UTF_8
@@ -2653,10 +2669,12 @@ class Gem::Specification < Gem::BasicSpecification
def version=(version)
@version = Gem::Version.create(version)
+ return if @version.nil?
+
# skip to set required_ruby_version when pre-released rubygems.
# It caused to raise CircularDependencyError
if @version.prerelease? && (@name.nil? || @name.strip != "rubygems")
- self.required_rubygems_version = '> 1.3.1'
+ self.required_rubygems_version = "> 1.3.1"
end
invalidate_memoized_attributes
@@ -2702,6 +2720,8 @@ class Gem::Specification < Gem::BasicSpecification
end
@installed_by_version ||= nil
+
+ nil
end
def flatten_require_paths # :nodoc:
diff --git a/lib/rubygems/specification_policy.rb b/lib/rubygems/specification_policy.rb
index 8b5d01dda2..cee7eb3fdc 100644
--- a/lib/rubygems/specification_policy.rb
+++ b/lib/rubygems/specification_policy.rb
@@ -1,4 +1,6 @@
-require_relative 'user_interaction'
+# frozen_string_literal: true
+
+require_relative "user_interaction"
class Gem::SpecificationPolicy
include Gem::UserInteraction
@@ -120,7 +122,7 @@ class Gem::SpecificationPolicy
metadata = @specification.metadata
unless Hash === metadata
- error 'metadata must be a hash'
+ error "metadata must be a hash"
end
metadata.each do |key, value|
@@ -173,6 +175,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
end
##
+ # Checks that the gem does not depend on itself.
# Checks that dependencies use requirements as we recommend. Warnings are
# issued when dependencies are open-ended or overly strict for semantic
# versioning.
@@ -180,6 +183,10 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
def validate_dependencies # :nodoc:
warning_messages = []
@specification.dependencies.each do |dep|
+ if dep.name == @specification.name # warn on self reference
+ warning_messages << "Self referencing dependency is unnecessary and strongly discouraged."
+ end
+
prerelease_dep = dep.requirements_list.any? do |req|
Gem::Requirement.new(req).prerelease?
end
@@ -188,7 +195,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
prerelease_dep && !@specification.version.prerelease?
open_ended = dep.requirement.requirements.all? do |op, version|
- not version.prerelease? and (op == '>' or op == '>=')
+ !version.prerelease? && (op == ">" || op == ">=")
end
if open_ended
@@ -198,12 +205,12 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
base = segments.first 2
- recommendation = if (op == '>' || op == '>=') && segments == [0]
+ recommendation = if (op == ">" || op == ">=") && segments == [0]
" use a bounded requirement, such as '~> x.y'"
else
- bugfix = if op == '>'
+ bugfix = if op == ">"
", '> #{dep_version}'"
- elsif op == '>=' and base != segments
+ elsif op == ">=" && base != segments
", '>= #{dep_version}'"
end
@@ -286,7 +293,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
def validate_require_paths
return unless @specification.raw_require_paths.empty?
- error 'specification must have at least one require_path'
+ error "specification must have at least one require_path"
end
def validate_non_files
@@ -310,7 +317,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
def validate_specification_version
return if @specification.specification_version.is_a?(Integer)
- error 'specification_version must be an Integer (did you mean version?)'
+ error "specification_version must be an Integer (did you mean version?)"
end
def validate_platform
@@ -338,7 +345,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
String
end
- unless Array === val and val.all? {|x| x.kind_of?(klass) }
+ unless Array === val && val.all? {|x| x.kind_of?(klass) }
error "#{field} must be an Array of #{klass}"
end
end
@@ -380,7 +387,7 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
WARNING
end
- LAZY = '"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, '')
+ LAZY = '"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, "")
LAZY_PATTERN = /\AFI XME|\ATO DO/x.freeze
HOMEPAGE_URI_PATTERN = /\A[a-z][a-z\d+.-]*:/i.freeze
@@ -404,8 +411,8 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
homepage = @specification.homepage
# Make sure a homepage is valid HTTP/HTTPS URI
- if homepage and not homepage.empty?
- require 'uri'
+ if homepage && !homepage.empty?
+ require "uri"
begin
homepage_uri = URI.parse(homepage)
unless [URI::HTTP, URI::HTTPS].member? homepage_uri.class
@@ -445,7 +452,7 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
def validate_shebang_line_in(executable)
executable_path = File.join(@specification.bindir, executable)
- return if File.read(executable_path, 2) == '#!'
+ return if File.read(executable_path, 2) == "#!"
warning "#{executable_path} is missing #! line"
end
@@ -457,11 +464,25 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
end
def validate_extensions # :nodoc:
- require_relative 'ext'
+ require_relative "ext"
builder = Gem::Ext::Builder.new(@specification)
+ validate_rake_extensions(builder)
+ validate_rust_extensions(builder)
+ end
+
+ def validate_rust_extensions(builder) # :nodoc:
+ rust_extension = @specification.extensions.any? {|s| builder.builder_for(s).is_a? Gem::Ext::CargoBuilder }
+ missing_cargo_lock = !@specification.files.any? {|f| f.end_with?("Cargo.lock") }
+
+ error <<-ERROR if rust_extension && missing_cargo_lock
+You have specified rust based extension, but Cargo.lock is not part of the gem files. Please run `cargo generate-lockfile` or any other command to generate Cargo.lock and ensure it is added to your gem files section in gemspec.
+ ERROR
+ end
+
+ def validate_rake_extensions(builder) # :nodoc:
rake_extension = @specification.extensions.any? {|s| builder.builder_for(s) == Gem::Ext::RakeBuilder }
- rake_dependency = @specification.dependencies.any? {|d| d.name == 'rake' }
+ rake_dependency = @specification.dependencies.any? {|d| d.name == "rake" }
warning <<-WARNING if rake_extension && !rake_dependency
You have specified rake based extension, but rake is not added as dependency. It is recommended to add rake as a dependency in gemspec since there's no guarantee rake will be already installed.
diff --git a/lib/rubygems/stub_specification.rb b/lib/rubygems/stub_specification.rb
index b1dd7397ae..d64d611f48 100644
--- a/lib/rubygems/stub_specification.rb
+++ b/lib/rubygems/stub_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Gem::StubSpecification reads the stub: line from the gemspec. This prevents
# us having to eval the entire gemspec in order to find out certain
@@ -6,10 +7,10 @@
class Gem::StubSpecification < Gem::BasicSpecification
# :nodoc:
- PREFIX = "# stub: ".freeze
+ PREFIX = "# stub: "
# :nodoc:
- OPEN_MODE = 'r:UTF-8:-'.freeze
+ OPEN_MODE = "r:UTF-8:-"
class StubLine # :nodoc: all
attr_reader :name, :version, :platform, :require_paths, :extensions,
@@ -19,9 +20,9 @@ class Gem::StubSpecification < Gem::BasicSpecification
# These are common require paths.
REQUIRE_PATHS = { # :nodoc:
- 'lib' => 'lib'.freeze,
- 'test' => 'test'.freeze,
- 'ext' => 'ext'.freeze,
+ "lib" => "lib",
+ "test" => "test",
+ "ext" => "ext",
}.freeze
# These are common require path lists. This hash is used to optimize
@@ -29,11 +30,11 @@ class Gem::StubSpecification < Gem::BasicSpecification
# in their require paths, so lets take advantage of that by pre-allocating
# a require path list for that case.
REQUIRE_PATH_LIST = { # :nodoc:
- 'lib' => ['lib'].freeze,
+ "lib" => ["lib"].freeze,
}.freeze
def initialize(data, extensions)
- parts = data[PREFIX.length..-1].split(" ".freeze, 4)
+ parts = data[PREFIX.length..-1].split(" ", 4)
@name = parts[0].freeze
@version = if Gem::Version.correct?(parts[1])
Gem::Version.new(parts[1])
@@ -50,7 +51,7 @@ class Gem::StubSpecification < Gem::BasicSpecification
end
path_list = parts.last
- @require_paths = REQUIRE_PATH_LIST[path_list] || path_list.split("\0".freeze).map! do |x|
+ @require_paths = REQUIRE_PATH_LIST[path_list] || path_list.split("\0").map! do |x|
REQUIRE_PATHS[x] || x
end
end
@@ -183,7 +184,7 @@ class Gem::StubSpecification < Gem::BasicSpecification
##
# The full Gem::Specification for this gem, loaded from evalling its gemspec
- def to_spec
+ def spec
@spec ||= if @data
loaded = Gem.loaded_specs[name]
loaded if loaded && loaded.version == version
@@ -191,6 +192,7 @@ class Gem::StubSpecification < Gem::BasicSpecification
@spec ||= Gem::Specification.load(loaded_from)
end
+ alias_method :to_spec, :spec
##
# Is this StubSpecification valid? i.e. have we found a stub line, OR does
diff --git a/lib/rubygems/text.rb b/lib/rubygems/text.rb
index d6b891380e..be811525f2 100644
--- a/lib/rubygems/text.rb
+++ b/lib/rubygems/text.rb
@@ -9,7 +9,7 @@ module Gem::Text
# Remove any non-printable characters and make the text suitable for
# printing.
def clean_text(text)
- text.gsub(/[\000-\b\v-\f\016-\037\177]/, ".".freeze)
+ text.gsub(/[\000-\b\v-\f\016-\037\177]/, ".")
end
def truncate_text(text, description, max_length = 100_000)
diff --git a/lib/rubygems/tsort.rb b/lib/rubygems/tsort.rb
index ebe7c3364b..60ebe22e81 100644
--- a/lib/rubygems/tsort.rb
+++ b/lib/rubygems/tsort.rb
@@ -1,3 +1,3 @@
# frozen_string_literal: true
-require_relative 'tsort/lib/tsort'
+require_relative "tsort/lib/tsort"
diff --git a/lib/rubygems/tsort/lib/tsort.rb b/lib/rubygems/tsort/lib/tsort.rb
index f68c5947d3..f825f14257 100644
--- a/lib/rubygems/tsort/lib/tsort.rb
+++ b/lib/rubygems/tsort/lib/tsort.rb
@@ -121,334 +121,332 @@
# <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
#
-module Gem
- module TSort
- class Cyclic < StandardError
- end
-
- # Returns a topologically sorted array of nodes.
- # The array is sorted from children to parents, i.e.
- # the first element has no child and the last node has no parent.
- #
- # If there is a cycle, Gem::TSort::Cyclic is raised.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # p graph.tsort #=> [4, 2, 3, 1]
- #
- # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
- # p graph.tsort # raises Gem::TSort::Cyclic
- #
- def tsort
- each_node = method(:tsort_each_node)
- each_child = method(:tsort_each_child)
- Gem::TSort.tsort(each_node, each_child)
- end
+module Gem::TSort
+ class Cyclic < StandardError
+ end
- # Returns a topologically sorted array of nodes.
- # The array is sorted from children to parents, i.e.
- # the first element has no child and the last node has no parent.
- #
- # The graph is represented by _each_node_ and _each_child_.
- # _each_node_ should have +call+ method which yields for each node in the graph.
- # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
- #
- # If there is a cycle, Gem::TSort::Cyclic is raised.
- #
- # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # p Gem::TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
- #
- # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # p Gem::TSort.tsort(each_node, each_child) # raises Gem::TSort::Cyclic
- #
- def TSort.tsort(each_node, each_child)
- Gem::TSort.tsort_each(each_node, each_child).to_a
- end
+ # Returns a topologically sorted array of nodes.
+ # The array is sorted from children to parents, i.e.
+ # the first element has no child and the last node has no parent.
+ #
+ # If there is a cycle, Gem::TSort::Cyclic is raised.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # p graph.tsort #=> [4, 2, 3, 1]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # p graph.tsort # raises Gem::TSort::Cyclic
+ #
+ def tsort
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.tsort(each_node, each_child)
+ end
- # The iterator version of the #tsort method.
- # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
- # modification of _obj_ during the iteration may lead to unexpected results.
- #
- # #tsort_each returns +nil+.
- # If there is a cycle, Gem::TSort::Cyclic is raised.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # graph.tsort_each {|n| p n }
- # #=> 4
- # # 2
- # # 3
- # # 1
- #
- def tsort_each(&block) # :yields: node
- each_node = method(:tsort_each_node)
- each_child = method(:tsort_each_child)
- Gem::TSort.tsort_each(each_node, each_child, &block)
- end
+ # Returns a topologically sorted array of nodes.
+ # The array is sorted from children to parents, i.e.
+ # the first element has no child and the last node has no parent.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # If there is a cycle, Gem::TSort::Cyclic is raised.
+ #
+ # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.tsort(each_node, each_child) # raises Gem::TSort::Cyclic
+ #
+ def self.tsort(each_node, each_child)
+ tsort_each(each_node, each_child).to_a
+ end
- # The iterator version of the Gem::TSort.tsort method.
- #
- # The graph is represented by _each_node_ and _each_child_.
- # _each_node_ should have +call+ method which yields for each node in the graph.
- # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
- #
- # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # Gem::TSort.tsort_each(each_node, each_child) {|n| p n }
- # #=> 4
- # # 2
- # # 3
- # # 1
- #
- def TSort.tsort_each(each_node, each_child) # :yields: node
- return to_enum(__method__, each_node, each_child) unless block_given?
+ # The iterator version of the #tsort method.
+ # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
+ # modification of _obj_ during the iteration may lead to unexpected results.
+ #
+ # #tsort_each returns +nil+.
+ # If there is a cycle, Gem::TSort::Cyclic is raised.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.tsort_each {|n| p n }
+ # #=> 4
+ # # 2
+ # # 3
+ # # 1
+ #
+ def tsort_each(&block) # :yields: node
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.tsort_each(each_node, each_child, &block)
+ end
- Gem::TSort.each_strongly_connected_component(each_node, each_child) {|component|
- if component.size == 1
- yield component.first
- else
- raise Cyclic.new("topological sort failed: #{component.inspect}")
- end
- }
- end
+ # The iterator version of the Gem::TSort.tsort method.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # Gem::TSort.tsort_each(each_node, each_child) {|n| p n }
+ # #=> 4
+ # # 2
+ # # 3
+ # # 1
+ #
+ def self.tsort_each(each_node, each_child) # :yields: node
+ return to_enum(__method__, each_node, each_child) unless block_given?
- # Returns strongly connected components as an array of arrays of nodes.
- # The array is sorted from children to parents.
- # Each elements of the array represents a strongly connected component.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # p graph.strongly_connected_components #=> [[4], [2], [3], [1]]
- #
- # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
- # p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
- #
- def strongly_connected_components
- each_node = method(:tsort_each_node)
- each_child = method(:tsort_each_child)
- Gem::TSort.strongly_connected_components(each_node, each_child)
- end
+ each_strongly_connected_component(each_node, each_child) {|component|
+ if component.size == 1
+ yield component.first
+ else
+ raise Cyclic.new("topological sort failed: #{component.inspect}")
+ end
+ }
+ end
- # Returns strongly connected components as an array of arrays of nodes.
- # The array is sorted from children to parents.
- # Each elements of the array represents a strongly connected component.
- #
- # The graph is represented by _each_node_ and _each_child_.
- # _each_node_ should have +call+ method which yields for each node in the graph.
- # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
- #
- # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # p Gem::TSort.strongly_connected_components(each_node, each_child)
- # #=> [[4], [2], [3], [1]]
- #
- # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # p Gem::TSort.strongly_connected_components(each_node, each_child)
- # #=> [[4], [2, 3], [1]]
- #
- def TSort.strongly_connected_components(each_node, each_child)
- Gem::TSort.each_strongly_connected_component(each_node, each_child).to_a
- end
+ # Returns strongly connected components as an array of arrays of nodes.
+ # The array is sorted from children to parents.
+ # Each elements of the array represents a strongly connected component.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # p graph.strongly_connected_components #=> [[4], [2], [3], [1]]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
+ #
+ def strongly_connected_components
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.strongly_connected_components(each_node, each_child)
+ end
- # The iterator version of the #strongly_connected_components method.
- # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
- # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
- # modification of _obj_ during the iteration may lead to unexpected results.
- #
- # #each_strongly_connected_component returns +nil+.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # graph.each_strongly_connected_component {|scc| p scc }
- # #=> [4]
- # # [2]
- # # [3]
- # # [1]
- #
- # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
- # graph.each_strongly_connected_component {|scc| p scc }
- # #=> [4]
- # # [2, 3]
- # # [1]
- #
- def each_strongly_connected_component(&block) # :yields: nodes
- each_node = method(:tsort_each_node)
- each_child = method(:tsort_each_child)
- Gem::TSort.each_strongly_connected_component(each_node, each_child, &block)
- end
+ # Returns strongly connected components as an array of arrays of nodes.
+ # The array is sorted from children to parents.
+ # Each elements of the array represents a strongly connected component.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.strongly_connected_components(each_node, each_child)
+ # #=> [[4], [2], [3], [1]]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.strongly_connected_components(each_node, each_child)
+ # #=> [[4], [2, 3], [1]]
+ #
+ def self.strongly_connected_components(each_node, each_child)
+ each_strongly_connected_component(each_node, each_child).to_a
+ end
- # The iterator version of the Gem::TSort.strongly_connected_components method.
- #
- # The graph is represented by _each_node_ and _each_child_.
- # _each_node_ should have +call+ method which yields for each node in the graph.
- # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
- #
- # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
- # #=> [4]
- # # [2]
- # # [3]
- # # [1]
- #
- # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
- # #=> [4]
- # # [2, 3]
- # # [1]
- #
- def TSort.each_strongly_connected_component(each_node, each_child) # :yields: nodes
- return to_enum(__method__, each_node, each_child) unless block_given?
+ # The iterator version of the #strongly_connected_components method.
+ # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
+ # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
+ # modification of _obj_ during the iteration may lead to unexpected results.
+ #
+ # #each_strongly_connected_component returns +nil+.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.each_strongly_connected_component {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ # # [3]
+ # # [1]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # graph.each_strongly_connected_component {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def each_strongly_connected_component(&block) # :yields: nodes
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.each_strongly_connected_component(each_node, each_child, &block)
+ end
- id_map = {}
- stack = []
- each_node.call {|node|
- unless id_map.include? node
- Gem::TSort.each_strongly_connected_component_from(node, each_child, id_map, stack) {|c|
- yield c
- }
- end
- }
- nil
- end
+ # The iterator version of the Gem::TSort.strongly_connected_components method.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ # # [3]
+ # # [1]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def self.each_strongly_connected_component(each_node, each_child) # :yields: nodes
+ return to_enum(__method__, each_node, each_child) unless block_given?
- # Iterates over strongly connected component in the subgraph reachable from
- # _node_.
- #
- # Return value is unspecified.
- #
- # #each_strongly_connected_component_from doesn't call #tsort_each_node.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # graph.each_strongly_connected_component_from(2) {|scc| p scc }
- # #=> [4]
- # # [2]
- #
- # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
- # graph.each_strongly_connected_component_from(2) {|scc| p scc }
- # #=> [4]
- # # [2, 3]
- #
- def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes
- Gem::TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block)
- end
+ id_map = {}
+ stack = []
+ each_node.call {|node|
+ unless id_map.include? node
+ each_strongly_connected_component_from(node, each_child, id_map, stack) {|c|
+ yield c
+ }
+ end
+ }
+ nil
+ end
- # Iterates over strongly connected components in a graph.
- # The graph is represented by _node_ and _each_child_.
- #
- # _node_ is the first node.
- # _each_child_ should have +call+ method which takes a node argument
- # and yields for each child node.
- #
- # Return value is unspecified.
- #
- # #Gem::TSort.each_strongly_connected_component_from is a class method and
- # it doesn't need a class to represent a graph which includes Gem::TSort.
- #
- # graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
- # each_child = lambda {|n, &b| graph[n].each(&b) }
- # Gem::TSort.each_strongly_connected_component_from(1, each_child) {|scc|
- # p scc
- # }
- # #=> [4]
- # # [2, 3]
- # # [1]
- #
- def TSort.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes
- return to_enum(__method__, node, each_child, id_map, stack) unless block_given?
+ # Iterates over strongly connected component in the subgraph reachable from
+ # _node_.
+ #
+ # Return value is unspecified.
+ #
+ # #each_strongly_connected_component_from doesn't call #tsort_each_node.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.each_strongly_connected_component_from(2) {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # graph.each_strongly_connected_component_from(2) {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ #
+ def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes
+ Gem::TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block)
+ end
- minimum_id = node_id = id_map[node] = id_map.size
- stack_length = stack.length
- stack << node
+ # Iterates over strongly connected components in a graph.
+ # The graph is represented by _node_ and _each_child_.
+ #
+ # _node_ is the first node.
+ # _each_child_ should have +call+ method which takes a node argument
+ # and yields for each child node.
+ #
+ # Return value is unspecified.
+ #
+ # #Gem::TSort.each_strongly_connected_component_from is a class method and
+ # it doesn't need a class to represent a graph which includes Gem::TSort.
+ #
+ # graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_child = lambda {|n, &b| graph[n].each(&b) }
+ # Gem::TSort.each_strongly_connected_component_from(1, each_child) {|scc|
+ # p scc
+ # }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def self.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes
+ return to_enum(__method__, node, each_child, id_map, stack) unless block_given?
- each_child.call(node) {|child|
- if id_map.include? child
- child_id = id_map[child]
- minimum_id = child_id if child_id && child_id < minimum_id
- else
- sub_minimum_id =
- Gem::TSort.each_strongly_connected_component_from(child, each_child, id_map, stack) {|c|
- yield c
- }
- minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
- end
- }
+ minimum_id = node_id = id_map[node] = id_map.size
+ stack_length = stack.length
+ stack << node
- if node_id == minimum_id
- component = stack.slice!(stack_length .. -1)
- component.each {|n| id_map[n] = nil}
- yield component
+ each_child.call(node) {|child|
+ if id_map.include? child
+ child_id = id_map[child]
+ minimum_id = child_id if child_id && child_id < minimum_id
+ else
+ sub_minimum_id =
+ each_strongly_connected_component_from(child, each_child, id_map, stack) {|c|
+ yield c
+ }
+ minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
end
+ }
- minimum_id
+ if node_id == minimum_id
+ component = stack.slice!(stack_length .. -1)
+ component.each {|n| id_map[n] = nil}
+ yield component
end
- # Should be implemented by a extended class.
- #
- # #tsort_each_node is used to iterate for all nodes over a graph.
- #
- def tsort_each_node # :yields: node
- raise NotImplementedError.new
- end
+ minimum_id
+ end
- # Should be implemented by a extended class.
- #
- # #tsort_each_child is used to iterate for child nodes of _node_.
- #
- def tsort_each_child(node) # :yields: child
- raise NotImplementedError.new
- end
+ # Should be implemented by a extended class.
+ #
+ # #tsort_each_node is used to iterate for all nodes over a graph.
+ #
+ def tsort_each_node # :yields: node
+ raise NotImplementedError.new
+ end
+
+ # Should be implemented by a extended class.
+ #
+ # #tsort_each_child is used to iterate for child nodes of _node_.
+ #
+ def tsort_each_child(node) # :yields: child
+ raise NotImplementedError.new
end
end
diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb
index a36c5cbe2b..f678f960f0 100644
--- a/lib/rubygems/uninstaller.rb
+++ b/lib/rubygems/uninstaller.rb
@@ -1,16 +1,17 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require 'fileutils'
-require_relative '../rubygems'
-require_relative 'installer_uninstaller_utils'
-require_relative 'dependency_list'
-require_relative 'rdoc'
-require_relative 'user_interaction'
+require "fileutils"
+require_relative "../rubygems"
+require_relative "installer_uninstaller_utils"
+require_relative "dependency_list"
+require_relative "rdoc"
+require_relative "user_interaction"
##
# An Uninstaller.
@@ -105,8 +106,8 @@ class Gem::Uninstaller
@default_specs_matching_uninstall_params = default_specs
list, other_repo_specs = list.partition do |spec|
- @gem_home == spec.base_dir or
- (@user_install and spec.base_dir == Gem.user_dir)
+ @gem_home == spec.base_dir ||
+ (@user_install && spec.base_dir == Gem.user_dir)
end
list.sort!
@@ -239,8 +240,8 @@ class Gem::Uninstaller
# spec:: the spec of the gem to be uninstalled
def remove(spec)
- unless path_ok?(@gem_home, spec) or
- (@user_install and path_ok?(Gem.user_dir, spec))
+ unless path_ok?(@gem_home, spec) ||
+ (@user_install && path_ok?(Gem.user_dir, spec))
e = Gem::GemNotInHomeException.new \
"Gem '#{spec.full_name}' is not installed in directory #{@gem_home}"
e.spec = spec
@@ -302,8 +303,8 @@ class Gem::Uninstaller
# Is +spec+ in +gem_dir+?
def path_ok?(gem_dir, spec)
- full_path = File.join gem_dir, 'gems', spec.full_name
- original_path = File.join gem_dir, 'gems', spec.original_name
+ full_path = File.join gem_dir, "gems", spec.full_name
+ original_path = File.join gem_dir, "gems", spec.original_name
full_path == spec.full_gem_path || original_path == spec.full_gem_path
end
@@ -332,10 +333,10 @@ class Gem::Uninstaller
# Asks if it is OK to remove +spec+. Returns true if it is OK.
def ask_if_ok(spec) # :nodoc:
- msg = ['']
- msg << 'You have requested to uninstall the gem:'
+ msg = [""]
+ msg << "You have requested to uninstall the gem:"
msg << "\t#{spec.full_name}"
- msg << ''
+ msg << ""
siblings = Gem::Specification.select do |s|
s.name == spec.name && s.full_name != spec.full_name
@@ -347,8 +348,8 @@ class Gem::Uninstaller
end
end
- msg << 'If you remove this gem, these dependencies will not be met.'
- msg << 'Continue with Uninstall?'
+ msg << "If you remove this gem, these dependencies will not be met."
+ msg << "Continue with Uninstall?"
return ask_yes_no(msg.join("\n"), false)
end
@@ -360,7 +361,7 @@ class Gem::Uninstaller
# of what it did for us to find rather than trying to recreate
# it again.
if @format_executable
- require_relative 'installer'
+ require_relative "installer"
Gem::Installer.exec_format % File.basename(filename)
else
filename
diff --git a/lib/rubygems/update_suggestion.rb b/lib/rubygems/update_suggestion.rb
new file mode 100644
index 0000000000..c2e81b2374
--- /dev/null
+++ b/lib/rubygems/update_suggestion.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+##
+# Mixin methods for Gem::Command to promote available RubyGems update
+
+module Gem::UpdateSuggestion
+ # list taken from https://github.com/watson/ci-info/blob/7a3c30d/index.js#L56-L66
+ CI_ENV_VARS = [
+ "CI", # Travis CI, CircleCI, Cirrus CI, Gitlab CI, Appveyor, CodeShip, dsari
+ "CONTINUOUS_INTEGRATION", # Travis CI, Cirrus CI
+ "BUILD_NUMBER", # Jenkins, TeamCity
+ "CI_APP_ID", "CI_BUILD_ID", "CI_BUILD_NUMBER", # Applfow
+ "RUN_ID" # TaskCluster, dsari
+ ].freeze
+
+ ONE_WEEK = 7 * 24 * 60 * 60
+
+ ##
+ # Message to promote available RubyGems update with related gem update command.
+
+ def update_suggestion
+ <<-MESSAGE
+
+A new release of RubyGems is available: #{Gem.rubygems_version} → #{Gem.latest_rubygems_version}!
+Run `gem update --system #{Gem.latest_rubygems_version}` to update your installation.
+
+ MESSAGE
+ end
+
+ ##
+ # Determines if current environment is eglible for update suggestion.
+
+ def eglible_for_update?
+ # explicit opt-out
+ return false if Gem.configuration[:prevent_update_suggestion]
+ return false if ENV["RUBYGEMS_PREVENT_UPDATE_SUGGESTION"]
+
+ # focus only on human usage of final RubyGems releases
+ return false unless Gem.ui.tty?
+ return false if Gem.rubygems_version.prerelease?
+ return false if Gem.disable_system_update_message
+ return false if ci?
+
+ # check makes sense only when we can store timestamp of last try
+ # otherwise we will not be able to prevent "annoying" update message
+ # on each command call
+ return unless Gem.configuration.state_file_writable?
+
+ # load time of last check, ensure the difference is enough to repeat the suggestion
+ check_time = Time.now.to_i
+ last_update_check = Gem.configuration.last_update_check
+ return false if (check_time - last_update_check) < ONE_WEEK
+
+ # compare current and latest version, this is the part where
+ # latest rubygems spec is fetched from remote
+ (Gem.rubygems_version < Gem.latest_rubygems_version).tap do |eglible|
+ # store the time of last successful check into state file
+ Gem.configuration.last_update_check = check_time
+
+ return eglible
+ end
+ rescue # don't block install command on any problem
+ false
+ end
+
+ def ci?
+ CI_ENV_VARS.any? {|var| ENV.include?(var) }
+ end
+end
diff --git a/lib/rubygems/uri.rb b/lib/rubygems/uri.rb
index 6acb9041f9..4b5d035aa0 100644
--- a/lib/rubygems/uri.rb
+++ b/lib/rubygems/uri.rb
@@ -66,7 +66,7 @@ class Gem::Uri
def redact_credentials_from(text)
return text unless valid_uri? && password? && text.include?(to_s)
- text.sub(password, 'REDACTED')
+ text.sub(password, "REDACTED")
end
def method_missing(method_name, *args, &blk)
@@ -97,11 +97,11 @@ class Gem::Uri
end
def with_redacted_user
- clone.tap {|uri| uri.user = 'REDACTED' }
+ clone.tap {|uri| uri.user = "REDACTED" }
end
def with_redacted_password
- clone.tap {|uri| uri.password = 'REDACTED' }
+ clone.tap {|uri| uri.password = "REDACTED" }
end
def valid_uri?
@@ -113,7 +113,7 @@ class Gem::Uri
end
def oauth_basic?
- password == 'x-oauth-basic'
+ password == "x-oauth-basic"
end
def token?
diff --git a/lib/rubygems/uri_formatter.rb b/lib/rubygems/uri_formatter.rb
index 3bda896875..3f1d02d774 100644
--- a/lib/rubygems/uri_formatter.rb
+++ b/lib/rubygems/uri_formatter.rb
@@ -17,7 +17,7 @@ class Gem::UriFormatter
# Creates a new URI formatter for +uri+.
def initialize(uri)
- require 'cgi'
+ require "cgi"
@uri = uri
end
diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb
index c726fd21c1..451dba070f 100644
--- a/lib/rubygems/user_interaction.rb
+++ b/lib/rubygems/user_interaction.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative 'deprecate'
-require_relative 'text'
+require_relative "deprecate"
+require_relative "text"
##
# Module that defines the default UserInteraction. Any class including this
@@ -148,7 +149,7 @@ module Gem::UserInteraction
##
# Displays the given +statement+ on the standard output (or equivalent).
- def say(statement = '')
+ def say(statement = "")
ui.say statement
end
@@ -239,6 +240,7 @@ class Gem::StreamUI
return nil, nil unless result
result = result.strip.to_i - 1
+ return nil, nil unless (0...list.size) === result
return list[result], result
end
@@ -259,11 +261,11 @@ class Gem::StreamUI
default_answer = case default
when nil
- 'yn'
+ "yn"
when true
- 'Yn'
+ "Yn"
else
- 'yN'
+ "yN"
end
result = nil
@@ -284,13 +286,13 @@ class Gem::StreamUI
# Ask a question. Returns an answer if connected to a tty, nil otherwise.
def ask(question)
- return nil if not tty?
+ return nil if !tty?
@outs.print(question + " ")
@outs.flush
result = @ins.gets
- result.chomp! if result
+ result&.chomp!
result
end
@@ -298,21 +300,21 @@ class Gem::StreamUI
# Ask for a password. Does not echo response to terminal.
def ask_for_password(question)
- return nil if not tty?
+ return nil if !tty?
@outs.print(question, " ")
@outs.flush
password = _gets_noecho
@outs.puts
- password.chomp! if password
+ password&.chomp!
password
end
def require_io_console
@require_io_console ||= begin
begin
- require 'io/console'
+ require "io/console"
rescue LoadError
end
true
@@ -472,7 +474,7 @@ class Gem::StreamUI
# and the +terminal_message+ when it is complete.
def initialize(out_stream, size, initial_message,
- terminal_message = 'complete')
+ terminal_message = "complete")
@out = out_stream
@total = size
@count = 0
diff --git a/lib/rubygems/util.rb b/lib/rubygems/util.rb
index 9fd78ab2bc..bd6ea92cc9 100644
--- a/lib/rubygems/util.rb
+++ b/lib/rubygems/util.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-require_relative 'deprecate'
+
+require_relative "deprecate"
##
# This module contains various utility methods as module methods.
@@ -10,9 +11,9 @@ module Gem::Util
# Zlib::GzipReader wrapper that unzips +data+.
def self.gunzip(data)
- require 'zlib'
- require 'stringio'
- data = StringIO.new(data, 'r')
+ require "zlib"
+ require "stringio"
+ data = StringIO.new(data, "r")
gzip_reader = begin
Zlib::GzipReader.new(data)
@@ -29,9 +30,9 @@ module Gem::Util
# Zlib::GzipWriter wrapper that zips +data+.
def self.gzip(data)
- require 'zlib'
- require 'stringio'
- zipped = StringIO.new(String.new, 'w')
+ require "zlib"
+ require "stringio"
+ zipped = StringIO.new(String.new, "w")
zipped.set_encoding Encoding::BINARY
Zlib::GzipWriter.wrap zipped do |io|
@@ -45,7 +46,7 @@ module Gem::Util
# A Zlib::Inflate#inflate wrapper
def self.inflate(data)
- require 'zlib'
+ require "zlib"
Zlib::Inflate.inflate data
end
@@ -86,7 +87,7 @@ module Gem::Util
loop do
Dir.chdir here, &block rescue Errno::EACCES
- new_here = File.expand_path('..', here)
+ new_here = File.expand_path("..", here)
return if new_here == here # toplevel
here = new_here
end
@@ -97,11 +98,7 @@ module Gem::Util
# returning absolute paths to the matching files.
def self.glob_files_in_dir(glob, base_path)
- if RUBY_VERSION >= "2.5"
- Dir.glob(glob, base: base_path).map! {|f| File.expand_path(f, base_path) }
- else
- Dir.glob(File.expand_path(glob, base_path))
- end
+ Dir.glob(glob, base: base_path).map! {|f| File.expand_path(f, base_path) }
end
##
@@ -109,7 +106,7 @@ module Gem::Util
# comes with a leading slash.
def self.correct_for_windows_path(path)
- if path[0].chr == '/' && path[1].chr =~ /[a-z]/i && path[2].chr == ':'
+ if path[0].chr == "/" && path[1].chr =~ /[a-z]/i && path[2].chr == ":"
path[1..-1]
else
path
diff --git a/lib/rubygems/util/licenses.rb b/lib/rubygems/util/licenses.rb
index 3f4178c6e0..1d5efde576 100644
--- a/lib/rubygems/util/licenses.rb
+++ b/lib/rubygems/util/licenses.rb
@@ -1,11 +1,12 @@
# frozen_string_literal: true
-require_relative '../text'
+
+require_relative "../text"
class Gem::Licenses
extend Gem::Text
- NONSTANDARD = 'Nonstandard'.freeze
- LICENSE_REF = 'LicenseRef-.+'.freeze
+ NONSTANDARD = "Nonstandard"
+ LICENSE_REF = "LicenseRef-.+"
# Software Package Data Exchange (SPDX) standard open-source software
# license identifiers
diff --git a/lib/rubygems/util/list.rb b/lib/rubygems/util/list.rb
index 33c40af4bb..acb6c73ddb 100644
--- a/lib/rubygems/util/list.rb
+++ b/lib/rubygems/util/list.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem
class List
include Enumerable
diff --git a/lib/rubygems/validator.rb b/lib/rubygems/validator.rb
index 728595e778..63493638b6 100644
--- a/lib/rubygems/validator.rb
+++ b/lib/rubygems/validator.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative 'package'
-require_relative 'installer'
+require_relative "package"
+require_relative "installer"
##
# Validator performs various gem file and gem database validation
@@ -15,7 +16,7 @@ class Gem::Validator
include Gem::UserInteraction
def initialize # :nodoc:
- require 'find'
+ require "find"
end
private
@@ -26,7 +27,7 @@ class Gem::Validator
Find.find gem_directory do |file_name|
fn = file_name[gem_directory.size..file_name.size - 1].sub(/^\//, "")
installed_files << fn unless
- fn =~ /CVS/ || fn.empty? || File.directory?(file_name)
+ fn.empty? || fn.include?("CVS") || File.directory?(file_name)
end
installed_files
@@ -110,11 +111,11 @@ class Gem::Validator
begin
next unless data # HACK `gem check -a mkrf`
- source = File.join gem_directory, entry['path']
+ source = File.join gem_directory, entry["path"]
File.open source, Gem.binary_mode do |f|
unless f.read == data
- errors[gem_name][entry['path']] = "Modified from original"
+ errors[gem_name][entry["path"]] = "Modified from original"
end
end
end
diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb
index c57bf7d6d5..c319e1f820 100644
--- a/lib/rubygems/version.rb
+++ b/lib/rubygems/version.rb
@@ -155,7 +155,7 @@ require_relative "deprecate"
class Gem::Version
include Comparable
- VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?'.freeze # :nodoc:
+ VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?' # :nodoc:
ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/.freeze # :nodoc:
##
@@ -171,9 +171,7 @@ class Gem::Version
# True if the +version+ string matches RubyGems' requirements.
def self.correct?(version)
- unless Gem::Deprecate.skip
- warn "nil versions are discouraged and will be deprecated in Rubygems 4" if version.nil?
- end
+ nil_versions_are_discouraged! if version.nil?
!!(version.to_s =~ ANCHORED_VERSION_PATTERN)
end
@@ -190,6 +188,8 @@ class Gem::Version
if self === input # check yourself before you wreck yourself
input
elsif input.nil?
+ nil_versions_are_discouraged!
+
nil
else
new input
@@ -206,6 +206,14 @@ class Gem::Version
@@all[version] ||= super
end
+ def self.nil_versions_are_discouraged!
+ unless Gem::Deprecate.skip
+ warn "nil versions are discouraged and will be deprecated in Rubygems 4"
+ end
+ end
+
+ private_class_method :nil_versions_are_discouraged!
+
##
# Constructs a Version from the +version+ string. A version string is a
# series of digits or ASCII letters separated by dots.
@@ -244,7 +252,7 @@ class Gem::Version
# same precision. Version "1.0" is not the same as version "1".
def eql?(other)
- self.class === other and @version == other._version
+ self.class === other && @version == other._version
end
def hash # :nodoc:
@@ -264,7 +272,7 @@ class Gem::Version
# string for backwards (RubyGems 1.3.5 and earlier) compatibility.
def marshal_dump
- [version]
+ [@version]
end
##
@@ -276,7 +284,7 @@ class Gem::Version
end
def yaml_initialize(tag, map) # :nodoc:
- @version = map['version']
+ @version = map["version"]
@segments = nil
@hash = nil
end
@@ -286,7 +294,7 @@ class Gem::Version
end
def encode_with(coder) # :nodoc:
- coder.add 'version', @version
+ coder.add "version", @version
end
##
@@ -311,7 +319,7 @@ class Gem::Version
@@release[self] ||= if prerelease?
segments = self.segments
segments.pop while segments.any? {|s| String === s }
- self.class.new segments.join('.')
+ self.class.new segments.join(".")
else
self
end
diff --git a/lib/rubygems/version_option.rb b/lib/rubygems/version_option.rb
index 1db382fa7f..d83a69cf0d 100644
--- a/lib/rubygems/version_option.rb
+++ b/lib/rubygems/version_option.rb
@@ -1,11 +1,12 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative '../rubygems'
+require_relative "../rubygems"
##
# Mixin methods for --version and --platform Gem::Command options.
@@ -24,7 +25,7 @@ module Gem::VersionOption
end
end
- add_option('--platform PLATFORM', Gem::Platform,
+ add_option("--platform PLATFORM", Gem::Platform,
"Specify the platform of gem to #{task}", *wrap) do
|value, options|
unless options[:added_platform]
@@ -55,7 +56,7 @@ module Gem::VersionOption
Gem::Requirement.new(*value.split(/\s*,\s*/))
end
- add_option('-v', '--version VERSION', Gem::Requirement,
+ add_option("-v", "--version VERSION", Gem::Requirement,
"Specify version of gem to #{task}", *wrap) do
|value, options|
# Allow handling for multiple --version operators
diff --git a/lib/securerandom.gemspec b/lib/securerandom.gemspec
index 900713e7dc..e095244ce9 100644
--- a/lib/securerandom.gemspec
+++ b/lib/securerandom.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "securerandom"
- spec.version = "0.2.0"
+ spec.version = "0.2.2"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/set.rb b/lib/set.rb
index 0490654183..df1e68d081 100644
--- a/lib/set.rb
+++ b/lib/set.rb
@@ -66,8 +66,8 @@
#
# First, what's elsewhere. \Class \Set:
#
-# - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html#class-Object-label-What-27s+Here].
-# - Includes {module Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html#module-Enumerable-label-What-27s+Here],
+# - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
+# - Includes {module Enumerable}[rdoc-ref:Enumerable@What-27s+Here],
# which provides dozens of additional methods.
#
# In particular, class \Set does not have many methods of its own
diff --git a/lib/set/set.gemspec b/lib/set/set.gemspec
index 83100df785..0def52e859 100644
--- a/lib/set/set.gemspec
+++ b/lib/set/set.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "set"
- spec.version = "1.0.2"
+ spec.version = "1.0.3"
spec.authors = ["Akinori MUSHA"]
spec.email = ["knu@idaemons.org"]
diff --git a/lib/syntax_suggest.rb b/lib/syntax_suggest.rb
new file mode 100644
index 0000000000..1a45dfa676
--- /dev/null
+++ b/lib/syntax_suggest.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require_relative "syntax_suggest/core_ext"
diff --git a/lib/syntax_suggest/api.rb b/lib/syntax_suggest/api.rb
new file mode 100644
index 0000000000..74e53c2563
--- /dev/null
+++ b/lib/syntax_suggest/api.rb
@@ -0,0 +1,201 @@
+# frozen_string_literal: true
+
+require_relative "version"
+
+require "tmpdir"
+require "stringio"
+require "pathname"
+require "ripper"
+require "timeout"
+
+module SyntaxSuggest
+ # Used to indicate a default value that cannot
+ # be confused with another input.
+ DEFAULT_VALUE = Object.new.freeze
+
+ class Error < StandardError; end
+ TIMEOUT_DEFAULT = ENV.fetch("SYNTAX_SUGGEST_TIMEOUT", 1).to_i
+
+ # SyntaxSuggest.handle_error [Public]
+ #
+ # Takes a `SyntaxError` exception, uses the
+ # error message to locate the file. Then the file
+ # will be analyzed to find the location of the syntax
+ # error and emit that location to stderr.
+ #
+ # Example:
+ #
+ # begin
+ # require 'bad_file'
+ # rescue => e
+ # SyntaxSuggest.handle_error(e)
+ # end
+ #
+ # By default it will re-raise the exception unless
+ # `re_raise: false`. The message output location
+ # can be configured using the `io: $stderr` input.
+ #
+ # If a valid filename cannot be determined, the original
+ # exception will be re-raised (even with
+ # `re_raise: false`).
+ def self.handle_error(e, re_raise: true, io: $stderr)
+ unless e.is_a?(SyntaxError)
+ io.puts("SyntaxSuggest: Must pass a SyntaxError, got: #{e.class}")
+ raise e
+ end
+
+ file = PathnameFromMessage.new(e.message, io: io).call.name
+ raise e unless file
+
+ io.sync = true
+
+ call(
+ io: io,
+ source: file.read,
+ filename: file
+ )
+
+ raise e if re_raise
+ end
+
+ # SyntaxSuggest.call [Private]
+ #
+ # Main private interface
+ def self.call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: DEFAULT_VALUE, timeout: TIMEOUT_DEFAULT, io: $stderr)
+ search = nil
+ filename = nil if filename == DEFAULT_VALUE
+ Timeout.timeout(timeout) do
+ record_dir ||= ENV["DEBUG"] ? "tmp" : nil
+ search = CodeSearch.new(source, record_dir: record_dir).call
+ end
+
+ blocks = search.invalid_blocks
+ DisplayInvalidBlocks.new(
+ io: io,
+ blocks: blocks,
+ filename: filename,
+ terminal: terminal,
+ code_lines: search.code_lines
+ ).call
+ rescue Timeout::Error => e
+ io.puts "Search timed out SYNTAX_SUGGEST_TIMEOUT=#{timeout}, run with SYNTAX_SUGGEST_DEBUG=1 for more info"
+ io.puts e.backtrace.first(3).join($/)
+ end
+
+ # SyntaxSuggest.record_dir [Private]
+ #
+ # Used to generate a unique directory to record
+ # search steps for debugging
+ def self.record_dir(dir)
+ time = Time.now.strftime("%Y-%m-%d-%H-%M-%s-%N")
+ dir = Pathname(dir)
+ dir.join(time).tap { |path|
+ path.mkpath
+ alias_dir = dir.join("last")
+ FileUtils.rm_rf(alias_dir) if alias_dir.exist?
+ FileUtils.ln_sf(time, alias_dir)
+ }
+ end
+
+ # SyntaxSuggest.valid_without? [Private]
+ #
+ # This will tell you if the `code_lines` would be valid
+ # if you removed the `without_lines`. In short it's a
+ # way to detect if we've found the lines with syntax errors
+ # in our document yet.
+ #
+ # code_lines = [
+ # CodeLine.new(line: "def foo\n", index: 0)
+ # CodeLine.new(line: " def bar\n", index: 1)
+ # CodeLine.new(line: "end\n", index: 2)
+ # ]
+ #
+ # SyntaxSuggest.valid_without?(
+ # without_lines: code_lines[1],
+ # code_lines: code_lines
+ # ) # => true
+ #
+ # SyntaxSuggest.valid?(code_lines) # => false
+ def self.valid_without?(without_lines:, code_lines:)
+ lines = code_lines - Array(without_lines).flatten
+
+ if lines.empty?
+ true
+ else
+ valid?(lines)
+ end
+ end
+
+ # SyntaxSuggest.invalid? [Private]
+ #
+ # Opposite of `SyntaxSuggest.valid?`
+ def self.invalid?(source)
+ source = source.join if source.is_a?(Array)
+ source = source.to_s
+
+ Ripper.new(source).tap(&:parse).error?
+ end
+
+ # SyntaxSuggest.valid? [Private]
+ #
+ # Returns truthy if a given input source is valid syntax
+ #
+ # SyntaxSuggest.valid?(<<~EOM) # => true
+ # def foo
+ # end
+ # EOM
+ #
+ # SyntaxSuggest.valid?(<<~EOM) # => false
+ # def foo
+ # def bar # Syntax error here
+ # end
+ # EOM
+ #
+ # You can also pass in an array of lines and they'll be
+ # joined before evaluating
+ #
+ # SyntaxSuggest.valid?(
+ # [
+ # "def foo\n",
+ # "end\n"
+ # ]
+ # ) # => true
+ #
+ # SyntaxSuggest.valid?(
+ # [
+ # "def foo\n",
+ # " def bar\n", # Syntax error here
+ # "end\n"
+ # ]
+ # ) # => false
+ #
+ # As an FYI the CodeLine class instances respond to `to_s`
+ # so passing a CodeLine in as an object or as an array
+ # will convert it to it's code representation.
+ def self.valid?(source)
+ !invalid?(source)
+ end
+end
+
+# Integration
+require_relative "cli"
+
+# Core logic
+require_relative "code_search"
+require_relative "code_frontier"
+require_relative "explain_syntax"
+require_relative "clean_document"
+
+# Helpers
+require_relative "lex_all"
+require_relative "code_line"
+require_relative "code_block"
+require_relative "block_expand"
+require_relative "ripper_errors"
+require_relative "priority_queue"
+require_relative "unvisited_lines"
+require_relative "around_block_scan"
+require_relative "priority_engulf_queue"
+require_relative "pathname_from_message"
+require_relative "display_invalid_blocks"
+require_relative "parse_blocks_from_indent_line"
diff --git a/lib/syntax_suggest/around_block_scan.rb b/lib/syntax_suggest/around_block_scan.rb
new file mode 100644
index 0000000000..ce00431b3a
--- /dev/null
+++ b/lib/syntax_suggest/around_block_scan.rb
@@ -0,0 +1,232 @@
+# frozen_string_literal: true
+
+require_relative "scan_history"
+
+module SyntaxSuggest
+ # This class is useful for exploring contents before and after
+ # a block
+ #
+ # It searches above and below the passed in block to match for
+ # whatever criteria you give it:
+ #
+ # Example:
+ #
+ # def dog # 1
+ # puts "bark" # 2
+ # puts "bark" # 3
+ # end # 4
+ #
+ # scan = AroundBlockScan.new(
+ # code_lines: code_lines
+ # block: CodeBlock.new(lines: code_lines[1])
+ # )
+ #
+ # scan.scan_while { true }
+ #
+ # puts scan.before_index # => 0
+ # puts scan.after_index # => 3
+ #
+ class AroundBlockScan
+ def initialize(code_lines:, block:)
+ @code_lines = code_lines
+ @orig_indent = block.current_indent
+
+ @stop_after_kw = false
+ @force_add_empty = false
+ @force_add_hidden = false
+ @target_indent = nil
+
+ @scanner = ScanHistory.new(code_lines: code_lines, block: block)
+ end
+
+ # When using this flag, `scan_while` will
+ # bypass the block it's given and always add a
+ # line that responds truthy to `CodeLine#hidden?`
+ #
+ # Lines are hidden when they've been evaluated by
+ # the parser as part of a block and found to contain
+ # valid code.
+ def force_add_hidden
+ @force_add_hidden = true
+ self
+ end
+
+ # When using this flag, `scan_while` will
+ # bypass the block it's given and always add a
+ # line that responds truthy to `CodeLine#empty?`
+ #
+ # Empty lines contain no code, only whitespace such
+ # as leading spaces a newline.
+ def force_add_empty
+ @force_add_empty = true
+ self
+ end
+
+ # Tells `scan_while` to look for mismatched keyword/end-s
+ #
+ # When scanning up, if we see more keywords then end-s it will
+ # stop. This might happen when scanning outside of a method body.
+ # the first scan line up would be a keyword and this setting would
+ # trigger a stop.
+ #
+ # When scanning down, stop if there are more end-s than keywords.
+ def stop_after_kw
+ @stop_after_kw = true
+ self
+ end
+
+ # Main work method
+ #
+ # The scan_while method takes a block that yields lines above and
+ # below the block. If the yield returns true, the @before_index
+ # or @after_index are modified to include the matched line.
+ #
+ # In addition to yielding individual lines, the internals of this
+ # object give a mini DSL to handle common situations such as
+ # stopping if we've found a keyword/end mis-match in one direction
+ # or the other.
+ def scan_while
+ stop_next_up = false
+ stop_next_down = false
+
+ @scanner.scan(
+ up: ->(line, kw_count, end_count) {
+ next false if stop_next_up
+ next true if @force_add_hidden && line.hidden?
+ next true if @force_add_empty && line.empty?
+
+ if @stop_after_kw && kw_count > end_count
+ stop_next_up = true
+ end
+
+ yield line
+ },
+ down: ->(line, kw_count, end_count) {
+ next false if stop_next_down
+ next true if @force_add_hidden && line.hidden?
+ next true if @force_add_empty && line.empty?
+
+ if @stop_after_kw && end_count > kw_count
+ stop_next_down = true
+ end
+
+ yield line
+ }
+ )
+
+ self
+ end
+
+ # Scanning is intentionally conservative because
+ # we have no way of rolling back an agressive block (at this time)
+ #
+ # If a block was stopped for some trivial reason, (like an empty line)
+ # but the next line would have caused it to be balanced then we
+ # can check that condition and grab just one more line either up or
+ # down.
+ #
+ # For example, below if we're scanning up, line 2 might cause
+ # the scanning to stop. This is because empty lines might
+ # denote logical breaks where the user intended to chunk code
+ # which is a good place to stop and check validity. Unfortunately
+ # it also means we might have a "dangling" keyword or end.
+ #
+ # 1 def bark
+ # 2
+ # 3 end
+ #
+ # If lines 2 and 3 are in the block, then when this method is
+ # run it would see it is unbalanced, but that acquiring line 1
+ # would make it balanced, so that's what it does.
+ def lookahead_balance_one_line
+ kw_count = 0
+ end_count = 0
+ lines.each do |line|
+ kw_count += 1 if line.is_kw?
+ end_count += 1 if line.is_end?
+ end
+
+ return self if kw_count == end_count # nothing to balance
+
+ @scanner.commit_if_changed # Rollback point if we don't find anything to optimize
+
+ # Try to eat up empty lines
+ @scanner.scan(
+ up: ->(line, _, _) { line.hidden? || line.empty? },
+ down: ->(line, _, _) { line.hidden? || line.empty? }
+ )
+
+ # More ends than keywords, check if we can balance expanding up
+ next_up = @scanner.next_up
+ next_down = @scanner.next_down
+ case end_count - kw_count
+ when 1
+ if next_up&.is_kw? && next_up.indent >= @target_indent
+ @scanner.scan(
+ up: ->(line, _, _) { line == next_up },
+ down: ->(line, _, _) { false }
+ )
+ @scanner.commit_if_changed
+ end
+ when -1
+ if next_down&.is_end? && next_down.indent >= @target_indent
+ @scanner.scan(
+ up: ->(line, _, _) { false },
+ down: ->(line, _, _) { line == next_down }
+ )
+ @scanner.commit_if_changed
+ end
+ end
+ # Rollback any uncommitted changes
+ @scanner.stash_changes
+
+ self
+ end
+
+ # Finds code lines at the same or greater indentation and adds them
+ # to the block
+ def scan_neighbors_not_empty
+ @target_indent = @orig_indent
+ scan_while { |line| line.not_empty? && line.indent >= @target_indent }
+ end
+
+ # Scan blocks based on indentation of next line above/below block
+ #
+ # Determines indentaion of the next line above/below the current block.
+ #
+ # Normally this is called when a block has expanded to capture all "neighbors"
+ # at the same (or greater) indentation and needs to expand out. For example
+ # the `def/end` lines surrounding a method.
+ def scan_adjacent_indent
+ before_after_indent = []
+
+ before_after_indent << (@scanner.next_up&.indent || 0)
+ before_after_indent << (@scanner.next_down&.indent || 0)
+
+ @target_indent = before_after_indent.min
+ scan_while { |line| line.not_empty? && line.indent >= @target_indent }
+
+ self
+ end
+
+ # Return the currently matched lines as a `CodeBlock`
+ #
+ # When a `CodeBlock` is created it will gather metadata about
+ # itself, so this is not a free conversion. Avoid allocating
+ # more CodeBlock's than needed
+ def code_block
+ CodeBlock.new(lines: lines)
+ end
+
+ # Returns the lines matched by the current scan as an
+ # array of CodeLines
+ def lines
+ @scanner.lines
+ end
+
+ # Managable rspec errors
+ def inspect
+ "#<#{self.class}:0x0000123843lol >"
+ end
+ end
+end
diff --git a/lib/syntax_suggest/block_expand.rb b/lib/syntax_suggest/block_expand.rb
new file mode 100644
index 0000000000..e9b486c720
--- /dev/null
+++ b/lib/syntax_suggest/block_expand.rb
@@ -0,0 +1,165 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # This class is responsible for taking a code block that exists
+ # at a far indentaion and then iteratively increasing the block
+ # so that it captures everything within the same indentation block.
+ #
+ # def dog
+ # puts "bow"
+ # puts "wow"
+ # end
+ #
+ # block = BlockExpand.new(code_lines: code_lines)
+ # .call(CodeBlock.new(lines: code_lines[1]))
+ #
+ # puts block.to_s
+ # # => puts "bow"
+ # puts "wow"
+ #
+ #
+ # Once a code block has captured everything at a given indentation level
+ # then it will expand to capture surrounding indentation.
+ #
+ # block = BlockExpand.new(code_lines: code_lines)
+ # .call(block)
+ #
+ # block.to_s
+ # # => def dog
+ # puts "bow"
+ # puts "wow"
+ # end
+ #
+ class BlockExpand
+ def initialize(code_lines:)
+ @code_lines = code_lines
+ end
+
+ # Main interface. Expand current indentation, before
+ # expanding to a lower indentation
+ def call(block)
+ if (next_block = expand_neighbors(block))
+ next_block
+ else
+ expand_indent(block)
+ end
+ end
+
+ # Expands code to the next lowest indentation
+ #
+ # For example:
+ #
+ # 1 def dog
+ # 2 print "dog"
+ # 3 end
+ #
+ # If a block starts on line 2 then it has captured all it's "neighbors" (code at
+ # the same indentation or higher). To continue expanding, this block must capture
+ # lines one and three which are at a different indentation level.
+ #
+ # This method allows fully expanded blocks to decrease their indentation level (so
+ # they can expand to capture more code up and down). It does this conservatively
+ # as there's no undo (currently).
+ def expand_indent(block)
+ now = AroundBlockScan.new(code_lines: @code_lines, block: block)
+ .force_add_hidden
+ .stop_after_kw
+ .scan_adjacent_indent
+
+ now.lookahead_balance_one_line
+
+ now.code_block
+ end
+
+ # A neighbor is code that is at or above the current indent line.
+ #
+ # First we build a block with all neighbors. If we can't go further
+ # then we decrease the indentation threshold and expand via indentation
+ # i.e. `expand_indent`
+ #
+ # Handles two general cases.
+ #
+ # ## Case #1: Check code inside of methods/classes/etc.
+ #
+ # It's important to note, that not everything in a given indentation level can be parsed
+ # as valid code even if it's part of valid code. For example:
+ #
+ # 1 hash = {
+ # 2 name: "richard",
+ # 3 dog: "cinco",
+ # 4 }
+ #
+ # In this case lines 2 and 3 will be neighbors, but they're invalid until `expand_indent`
+ # is called on them.
+ #
+ # When we are adding code within a method or class (at the same indentation level),
+ # use the empty lines to denote the programmer intended logical chunks.
+ # Stop and check each one. For example:
+ #
+ # 1 def dog
+ # 2 print "dog"
+ # 3
+ # 4 hash = {
+ # 5 end
+ #
+ # If we did not stop parsing at empty newlines then the block might mistakenly grab all
+ # the contents (lines 2, 3, and 4) and report them as being problems, instead of only
+ # line 4.
+ #
+ # ## Case #2: Expand/grab other logical blocks
+ #
+ # Once the search algorithm has converted all lines into blocks at a given indentation
+ # it will then `expand_indent`. Once the blocks that generates are expanded as neighbors
+ # we then begin seeing neighbors being other logical blocks i.e. a block's neighbors
+ # may be another method or class (something with keywords/ends).
+ #
+ # For example:
+ #
+ # 1 def bark
+ # 2
+ # 3 end
+ # 4
+ # 5 def sit
+ # 6 end
+ #
+ # In this case if lines 4, 5, and 6 are in a block when it tries to expand neighbors
+ # it will expand up. If it stops after line 2 or 3 it may cause problems since there's a
+ # valid kw/end pair, but the block will be checked without it.
+ #
+ # We try to resolve this edge case with `lookahead_balance_one_line` below.
+ def expand_neighbors(block)
+ now = AroundBlockScan.new(code_lines: @code_lines, block: block)
+
+ # Initial scan
+ now
+ .force_add_hidden
+ .stop_after_kw
+ .scan_neighbors_not_empty
+
+ # Slurp up empties
+ now
+ .scan_while { |line| line.empty? }
+
+ # If next line is kw and it will balance us, take it
+ expanded_lines = now
+ .lookahead_balance_one_line
+ .lines
+
+ # Don't allocate a block if it won't be used
+ #
+ # If nothing was taken, return nil to indicate that status
+ # used in `def call` to determine if
+ # we need to expand up/out (`expand_indent`)
+ if block.lines == expanded_lines
+ nil
+ else
+ CodeBlock.new(lines: expanded_lines)
+ end
+ end
+
+ # Managable rspec errors
+ def inspect
+ "#<SyntaxSuggest::CodeBlock:0x0000123843lol >"
+ end
+ end
+end
diff --git a/lib/syntax_suggest/capture/before_after_keyword_ends.rb b/lib/syntax_suggest/capture/before_after_keyword_ends.rb
new file mode 100644
index 0000000000..f53c57a4d1
--- /dev/null
+++ b/lib/syntax_suggest/capture/before_after_keyword_ends.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ module Capture
+ # Shows surrounding kw/end pairs
+ #
+ # The purpose of showing these extra pairs is due to cases
+ # of ambiguity when only one visible line is matched.
+ #
+ # For example:
+ #
+ # 1 class Dog
+ # 2 def bark
+ # 4 def eat
+ # 5 end
+ # 6 end
+ #
+ # In this case either line 2 could be missing an `end` or
+ # line 4 was an extra line added by mistake (it happens).
+ #
+ # When we detect the above problem it shows the issue
+ # as only being on line 2
+ #
+ # 2 def bark
+ #
+ # Showing "neighbor" keyword pairs gives extra context:
+ #
+ # 2 def bark
+ # 4 def eat
+ # 5 end
+ #
+ #
+ # Example:
+ #
+ # lines = BeforeAfterKeywordEnds.new(
+ # block: block,
+ # code_lines: code_lines
+ # ).call()
+ #
+ class BeforeAfterKeywordEnds
+ def initialize(code_lines:, block:)
+ @scanner = ScanHistory.new(code_lines: code_lines, block: block)
+ @original_indent = block.current_indent
+ end
+
+ def call
+ lines = []
+
+ @scanner.scan(
+ up: ->(line, kw_count, end_count) {
+ next true if line.empty?
+ break if line.indent < @original_indent
+ next true if line.indent != @original_indent
+
+ # If we're going up and have one complete kw/end pair, stop
+ if kw_count != 0 && kw_count == end_count
+ lines << line
+ break
+ end
+
+ lines << line if line.is_kw? || line.is_end?
+ true
+ },
+ down: ->(line, kw_count, end_count) {
+ next true if line.empty?
+ break if line.indent < @original_indent
+ next true if line.indent != @original_indent
+
+ # if we're going down and have one complete kw/end pair,stop
+ if kw_count != 0 && kw_count == end_count
+ lines << line
+ break
+ end
+
+ lines << line if line.is_kw? || line.is_end?
+ true
+ }
+ )
+ @scanner.stash_changes
+
+ lines
+ end
+ end
+ end
+end
diff --git a/lib/syntax_suggest/capture/falling_indent_lines.rb b/lib/syntax_suggest/capture/falling_indent_lines.rb
new file mode 100644
index 0000000000..1e046b2ba5
--- /dev/null
+++ b/lib/syntax_suggest/capture/falling_indent_lines.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ module Capture
+ # Shows the context around code provided by "falling" indentation
+ #
+ # If this is the original code lines:
+ #
+ # class OH
+ # def hello
+ # it "foo" do
+ # end
+ # end
+ #
+ # And this is the line that is captured
+ #
+ # it "foo" do
+ #
+ # It will yield its surrounding context:
+ #
+ # class OH
+ # def hello
+ # end
+ # end
+ #
+ # Example:
+ #
+ # FallingIndentLines.new(
+ # block: block,
+ # code_lines: @code_lines
+ # ).call do |line|
+ # @lines_to_output << line
+ # end
+ #
+ class FallingIndentLines
+ def initialize(code_lines:, block:)
+ @lines = nil
+ @scanner = ScanHistory.new(code_lines: code_lines, block: block)
+ @original_indent = block.current_indent
+ end
+
+ def call(&yieldable)
+ last_indent_up = @original_indent
+ last_indent_down = @original_indent
+
+ @scanner.commit_if_changed
+ @scanner.scan(
+ up: ->(line, _, _) {
+ next true if line.empty?
+
+ if line.indent < last_indent_up
+ yieldable.call(line)
+ last_indent_up = line.indent
+ end
+ true
+ },
+ down: ->(line, _, _) {
+ next true if line.empty?
+
+ if line.indent < last_indent_down
+ yieldable.call(line)
+ last_indent_down = line.indent
+ end
+ true
+ }
+ )
+ @scanner.stash_changes
+ end
+ end
+ end
+end
diff --git a/lib/syntax_suggest/capture_code_context.rb b/lib/syntax_suggest/capture_code_context.rb
new file mode 100644
index 0000000000..6dc7047176
--- /dev/null
+++ b/lib/syntax_suggest/capture_code_context.rb
@@ -0,0 +1,245 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ module Capture
+ end
+end
+
+require_relative "capture/falling_indent_lines"
+require_relative "capture/before_after_keyword_ends"
+
+module SyntaxSuggest
+ # Turns a "invalid block(s)" into useful context
+ #
+ # There are three main phases in the algorithm:
+ #
+ # 1. Sanitize/format input source
+ # 2. Search for invalid blocks
+ # 3. Format invalid blocks into something meaninful
+ #
+ # This class handles the third part.
+ #
+ # The algorithm is very good at capturing all of a syntax
+ # error in a single block in number 2, however the results
+ # can contain ambiguities. Humans are good at pattern matching
+ # and filtering and can mentally remove extraneous data, but
+ # they can't add extra data that's not present.
+ #
+ # In the case of known ambiguious cases, this class adds context
+ # back to the ambiguitiy so the programmer has full information.
+ #
+ # Beyond handling these ambiguities, it also captures surrounding
+ # code context information:
+ #
+ # puts block.to_s # => "def bark"
+ #
+ # context = CaptureCodeContext.new(
+ # blocks: block,
+ # code_lines: code_lines
+ # )
+ #
+ # lines = context.call.map(&:original)
+ # puts lines.join
+ # # =>
+ # class Dog
+ # def bark
+ # end
+ #
+ class CaptureCodeContext
+ attr_reader :code_lines
+
+ def initialize(blocks:, code_lines:)
+ @blocks = Array(blocks)
+ @code_lines = code_lines
+ @visible_lines = @blocks.map(&:visible_lines).flatten
+ @lines_to_output = @visible_lines.dup
+ end
+
+ def call
+ @blocks.each do |block|
+ capture_first_kw_end_same_indent(block)
+ capture_last_end_same_indent(block)
+ capture_before_after_kws(block)
+ capture_falling_indent(block)
+ end
+
+ sorted_lines
+ end
+
+ def sorted_lines
+ @lines_to_output.select!(&:not_empty?)
+ @lines_to_output.uniq!
+ @lines_to_output.sort!
+
+ @lines_to_output
+ end
+
+ # Shows the context around code provided by "falling" indentation
+ #
+ # Converts:
+ #
+ # it "foo" do
+ #
+ # into:
+ #
+ # class OH
+ # def hello
+ # it "foo" do
+ # end
+ # end
+ #
+ def capture_falling_indent(block)
+ Capture::FallingIndentLines.new(
+ block: block,
+ code_lines: @code_lines
+ ).call do |line|
+ @lines_to_output << line
+ end
+ end
+
+ # Shows surrounding kw/end pairs
+ #
+ # The purpose of showing these extra pairs is due to cases
+ # of ambiguity when only one visible line is matched.
+ #
+ # For example:
+ #
+ # 1 class Dog
+ # 2 def bark
+ # 4 def eat
+ # 5 end
+ # 6 end
+ #
+ # In this case either line 2 could be missing an `end` or
+ # line 4 was an extra line added by mistake (it happens).
+ #
+ # When we detect the above problem it shows the issue
+ # as only being on line 2
+ #
+ # 2 def bark
+ #
+ # Showing "neighbor" keyword pairs gives extra context:
+ #
+ # 2 def bark
+ # 4 def eat
+ # 5 end
+ #
+ def capture_before_after_kws(block)
+ return unless block.visible_lines.count == 1
+
+ around_lines = Capture::BeforeAfterKeywordEnds.new(
+ code_lines: @code_lines,
+ block: block
+ ).call
+
+ around_lines -= block.lines
+
+ @lines_to_output.concat(around_lines)
+ end
+
+ # When there is an invalid block with a keyword
+ # missing an end right before another end,
+ # it is unclear where which keyword is missing the
+ # end
+ #
+ # Take this example:
+ #
+ # class Dog # 1
+ # def bark # 2
+ # puts "woof" # 3
+ # end # 4
+ #
+ # However due to https://github.com/ruby/syntax_suggest/issues/32
+ # the problem line will be identified as:
+ #
+ # > class Dog # 1
+ #
+ # Because lines 2, 3, and 4 are technically valid code and are expanded
+ # first, deemed valid, and hidden. We need to un-hide the matching end
+ # line 4. Also work backwards and if there's a mis-matched keyword, show it
+ # too
+ def capture_last_end_same_indent(block)
+ return if block.visible_lines.length != 1
+ return unless block.visible_lines.first.is_kw?
+
+ visible_line = block.visible_lines.first
+ lines = @code_lines[visible_line.index..block.lines.last.index]
+
+ # Find first end with same indent
+ # (this would return line 4)
+ #
+ # end # 4
+ matching_end = lines.detect { |line| line.indent == block.current_indent && line.is_end? }
+ return unless matching_end
+
+ @lines_to_output << matching_end
+
+ # Work backwards from the end to
+ # see if there are mis-matched
+ # keyword/end pairs
+ #
+ # Return the first mis-matched keyword
+ # this would find line 2
+ #
+ # def bark # 2
+ # puts "woof" # 3
+ # end # 4
+ end_count = 0
+ kw_count = 0
+ kw_line = @code_lines[visible_line.index..matching_end.index].reverse.detect do |line|
+ end_count += 1 if line.is_end?
+ kw_count += 1 if line.is_kw?
+
+ !kw_count.zero? && kw_count >= end_count
+ end
+ return unless kw_line
+ @lines_to_output << kw_line
+ end
+
+ # The logical inverse of `capture_last_end_same_indent`
+ #
+ # When there is an invalid block with an `end`
+ # missing a keyword right after another `end`,
+ # it is unclear where which end is missing the
+ # keyword.
+ #
+ # Take this example:
+ #
+ # class Dog # 1
+ # puts "woof" # 2
+ # end # 3
+ # end # 4
+ #
+ # the problem line will be identified as:
+ #
+ # > end # 4
+ #
+ # This happens because lines 1, 2, and 3 are technically valid code and are expanded
+ # first, deemed valid, and hidden. We need to un-hide the matching keyword on
+ # line 1. Also work backwards and if there's a mis-matched end, show it
+ # too
+ def capture_first_kw_end_same_indent(block)
+ return if block.visible_lines.length != 1
+ return unless block.visible_lines.first.is_end?
+
+ visible_line = block.visible_lines.first
+ lines = @code_lines[block.lines.first.index..visible_line.index]
+ matching_kw = lines.reverse.detect { |line| line.indent == block.current_indent && line.is_kw? }
+ return unless matching_kw
+
+ @lines_to_output << matching_kw
+
+ kw_count = 0
+ end_count = 0
+ orphan_end = @code_lines[matching_kw.index..visible_line.index].detect do |line|
+ kw_count += 1 if line.is_kw?
+ end_count += 1 if line.is_end?
+
+ end_count >= kw_count
+ end
+
+ return unless orphan_end
+ @lines_to_output << orphan_end
+ end
+ end
+end
diff --git a/lib/syntax_suggest/clean_document.rb b/lib/syntax_suggest/clean_document.rb
new file mode 100644
index 0000000000..2c26061bfc
--- /dev/null
+++ b/lib/syntax_suggest/clean_document.rb
@@ -0,0 +1,306 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Parses and sanitizes source into a lexically aware document
+ #
+ # Internally the document is represented by an array with each
+ # index containing a CodeLine correlating to a line from the source code.
+ #
+ # There are three main phases in the algorithm:
+ #
+ # 1. Sanitize/format input source
+ # 2. Search for invalid blocks
+ # 3. Format invalid blocks into something meaninful
+ #
+ # This class handles the first part.
+ #
+ # The reason this class exists is to format input source
+ # for better/easier/cleaner exploration.
+ #
+ # The CodeSearch class operates at the line level so
+ # we must be careful to not introduce lines that look
+ # valid by themselves, but when removed will trigger syntax errors
+ # or strange behavior.
+ #
+ # ## Join Trailing slashes
+ #
+ # Code with a trailing slash is logically treated as a single line:
+ #
+ # 1 it "code can be split" \
+ # 2 "across multiple lines" do
+ #
+ # In this case removing line 2 would add a syntax error. We get around
+ # this by internally joining the two lines into a single "line" object
+ #
+ # ## Logically Consecutive lines
+ #
+ # Code that can be broken over multiple
+ # lines such as method calls are on different lines:
+ #
+ # 1 User.
+ # 2 where(name: "schneems").
+ # 3 first
+ #
+ # Removing line 2 can introduce a syntax error. To fix this, all lines
+ # are joined into one.
+ #
+ # ## Heredocs
+ #
+ # A heredoc is an way of defining a multi-line string. They can cause many
+ # problems. If left as a single line, Ripper would try to parse the contents
+ # as ruby code rather than as a string. Even without this problem, we still
+ # hit an issue with indentation
+ #
+ # 1 foo = <<~HEREDOC
+ # 2 "Be yourself; everyone else is already taken.""
+ # 3 ― Oscar Wilde
+ # 4 puts "I look like ruby code" # but i'm still a heredoc
+ # 5 HEREDOC
+ #
+ # If we didn't join these lines then our algorithm would think that line 4
+ # is separate from the rest, has a higher indentation, then look at it first
+ # and remove it.
+ #
+ # If the code evaluates line 5 by itself it will think line 5 is a constant,
+ # remove it, and introduce a syntax errror.
+ #
+ # All of these problems are fixed by joining the whole heredoc into a single
+ # line.
+ #
+ # ## Comments and whitespace
+ #
+ # Comments can throw off the way the lexer tells us that the line
+ # logically belongs with the next line. This is valid ruby but
+ # results in a different lex output than before:
+ #
+ # 1 User.
+ # 2 where(name: "schneems").
+ # 3 # Comment here
+ # 4 first
+ #
+ # To handle this we can replace comment lines with empty lines
+ # and then re-lex the source. This removal and re-lexing preserves
+ # line index and document size, but generates an easier to work with
+ # document.
+ #
+ class CleanDocument
+ def initialize(source:)
+ lines = clean_sweep(source: source)
+ @document = CodeLine.from_source(lines.join, lines: lines)
+ end
+
+ # Call all of the document "cleaners"
+ # and return self
+ def call
+ join_trailing_slash!
+ join_consecutive!
+ join_heredoc!
+
+ self
+ end
+
+ # Return an array of CodeLines in the
+ # document
+ def lines
+ @document
+ end
+
+ # Renders the document back to a string
+ def to_s
+ @document.join
+ end
+
+ # Remove comments
+ #
+ # replace with empty newlines
+ #
+ # source = <<~'EOM'
+ # # Comment 1
+ # puts "hello"
+ # # Comment 2
+ # puts "world"
+ # EOM
+ #
+ # lines = CleanDocument.new(source: source).lines
+ # expect(lines[0].to_s).to eq("\n")
+ # expect(lines[1].to_s).to eq("puts "hello")
+ # expect(lines[2].to_s).to eq("\n")
+ # expect(lines[3].to_s).to eq("puts "world")
+ #
+ # Important: This must be done before lexing.
+ #
+ # After this change is made, we lex the document because
+ # removing comments can change how the doc is parsed.
+ #
+ # For example:
+ #
+ # values = LexAll.new(source: <<~EOM))
+ # User.
+ # # comment
+ # where(name: 'schneems')
+ # EOM
+ # expect(
+ # values.count {|v| v.type == :on_ignored_nl}
+ # ).to eq(1)
+ #
+ # After the comment is removed:
+ #
+ # values = LexAll.new(source: <<~EOM))
+ # User.
+ #
+ # where(name: 'schneems')
+ # EOM
+ # expect(
+ # values.count {|v| v.type == :on_ignored_nl}
+ # ).to eq(2)
+ #
+ def clean_sweep(source:)
+ # Match comments, but not HEREDOC strings with #{variable} interpolation
+ # https://rubular.com/r/HPwtW9OYxKUHXQ
+ source.lines.map do |line|
+ if line.match?(/^\s*#([^{].*|)$/)
+ $/
+ else
+ line
+ end
+ end
+ end
+
+ # Smushes all heredoc lines into one line
+ #
+ # source = <<~'EOM'
+ # foo = <<~HEREDOC
+ # lol
+ # hehehe
+ # HEREDOC
+ # EOM
+ #
+ # lines = CleanDocument.new(source: source).join_heredoc!.lines
+ # expect(lines[0].to_s).to eq(source)
+ # expect(lines[1].to_s).to eq("")
+ def join_heredoc!
+ start_index_stack = []
+ heredoc_beg_end_index = []
+ lines.each do |line|
+ line.lex.each do |lex_value|
+ case lex_value.type
+ when :on_heredoc_beg
+ start_index_stack << line.index
+ when :on_heredoc_end
+ start_index = start_index_stack.pop
+ end_index = line.index
+ heredoc_beg_end_index << [start_index, end_index]
+ end
+ end
+ end
+
+ heredoc_groups = heredoc_beg_end_index.map { |start_index, end_index| @document[start_index..end_index] }
+
+ join_groups(heredoc_groups)
+ self
+ end
+
+ # Smushes logically "consecutive" lines
+ #
+ # source = <<~'EOM'
+ # User.
+ # where(name: 'schneems').
+ # first
+ # EOM
+ #
+ # lines = CleanDocument.new(source: source).join_consecutive!.lines
+ # expect(lines[0].to_s).to eq(source)
+ # expect(lines[1].to_s).to eq("")
+ #
+ # The one known case this doesn't handle is:
+ #
+ # Ripper.lex <<~EOM
+ # a &&
+ # b ||
+ # c
+ # EOM
+ #
+ # For some reason this introduces `on_ignore_newline` but with BEG type
+ #
+ def join_consecutive!
+ consecutive_groups = @document.select(&:ignore_newline_not_beg?).map do |code_line|
+ take_while_including(code_line.index..-1) do |line|
+ line.ignore_newline_not_beg?
+ end
+ end
+
+ join_groups(consecutive_groups)
+ self
+ end
+
+ # Join lines with a trailing slash
+ #
+ # source = <<~'EOM'
+ # it "code can be split" \
+ # "across multiple lines" do
+ # EOM
+ #
+ # lines = CleanDocument.new(source: source).join_consecutive!.lines
+ # expect(lines[0].to_s).to eq(source)
+ # expect(lines[1].to_s).to eq("")
+ def join_trailing_slash!
+ trailing_groups = @document.select(&:trailing_slash?).map do |code_line|
+ take_while_including(code_line.index..-1) { |x| x.trailing_slash? }
+ end
+ join_groups(trailing_groups)
+ self
+ end
+
+ # Helper method for joining "groups" of lines
+ #
+ # Input is expected to be type Array<Array<CodeLine>>
+ #
+ # The outer array holds the various "groups" while the
+ # inner array holds code lines.
+ #
+ # All code lines are "joined" into the first line in
+ # their group.
+ #
+ # To preserve document size, empty lines are placed
+ # in the place of the lines that were "joined"
+ def join_groups(groups)
+ groups.each do |lines|
+ line = lines.first
+
+ # Handle the case of multiple groups in a a row
+ # if one is already replaced, move on
+ next if @document[line.index].empty?
+
+ # Join group into the first line
+ @document[line.index] = CodeLine.new(
+ lex: lines.map(&:lex).flatten,
+ line: lines.join,
+ index: line.index
+ )
+
+ # Hide the rest of the lines
+ lines[1..-1].each do |line|
+ # The above lines already have newlines in them, if add more
+ # then there will be double newline, use an empty line instead
+ @document[line.index] = CodeLine.new(line: "", index: line.index, lex: [])
+ end
+ end
+ self
+ end
+
+ # Helper method for grabbing elements from document
+ #
+ # Like `take_while` except when it stops
+ # iterating, it also returns the line
+ # that caused it to stop
+ def take_while_including(range = 0..-1)
+ take_next_and_stop = false
+ @document[range].take_while do |line|
+ next if take_next_and_stop
+
+ take_next_and_stop = !(yield line)
+ true
+ end
+ end
+ end
+end
diff --git a/lib/syntax_suggest/cli.rb b/lib/syntax_suggest/cli.rb
new file mode 100644
index 0000000000..967f77bf70
--- /dev/null
+++ b/lib/syntax_suggest/cli.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+require "pathname"
+require "optparse"
+
+module SyntaxSuggest
+ # All the logic of the exe/syntax_suggest CLI in one handy spot
+ #
+ # Cli.new(argv: ["--help"]).call
+ # Cli.new(argv: ["<path/to/file>.rb"]).call
+ # Cli.new(argv: ["<path/to/file>.rb", "--record=tmp"]).call
+ # Cli.new(argv: ["<path/to/file>.rb", "--terminal"]).call
+ #
+ class Cli
+ attr_accessor :options
+
+ # ARGV is Everything passed to the executable, does not include executable name
+ #
+ # All other intputs are dependency injection for testing
+ def initialize(argv:, exit_obj: Kernel, io: $stdout, env: ENV)
+ @options = {}
+ @parser = nil
+ options[:record_dir] = env["SYNTAX_SUGGEST_RECORD_DIR"]
+ options[:record_dir] = "tmp" if env["DEBUG"]
+ options[:terminal] = SyntaxSuggest::DEFAULT_VALUE
+
+ @io = io
+ @argv = argv
+ @exit_obj = exit_obj
+ end
+
+ def call
+ if @argv.empty?
+ # Display help if raw command
+ parser.parse! %w[--help]
+ return
+ else
+ # Mutates @argv
+ parse
+ return if options[:exit]
+ end
+
+ file_name = @argv.first
+ if file_name.nil?
+ @io.puts "No file given"
+ @exit_obj.exit(1)
+ return
+ end
+
+ file = Pathname(file_name)
+ if !file.exist?
+ @io.puts "file not found: #{file.expand_path} "
+ @exit_obj.exit(1)
+ return
+ end
+
+ @io.puts "Record dir: #{options[:record_dir]}" if options[:record_dir]
+
+ display = SyntaxSuggest.call(
+ io: @io,
+ source: file.read,
+ filename: file.expand_path,
+ terminal: options.fetch(:terminal, SyntaxSuggest::DEFAULT_VALUE),
+ record_dir: options[:record_dir]
+ )
+
+ if display.document_ok?
+ @io.puts "Syntax OK"
+ @exit_obj.exit(0)
+ else
+ @exit_obj.exit(1)
+ end
+ end
+
+ def parse
+ parser.parse!(@argv)
+
+ self
+ end
+
+ def parser
+ @parser ||= OptionParser.new do |opts|
+ opts.banner = <<~EOM
+ Usage: syntax_suggest <file> [options]
+
+ Parses a ruby source file and searches for syntax error(s) such as
+ unexpected `end', expecting end-of-input.
+
+ Example:
+
+ $ syntax_suggest dog.rb
+
+ # ...
+
+ > 10 defdog
+ > 15 end
+
+ ENV options:
+
+ SYNTAX_SUGGEST_RECORD_DIR=<dir>
+
+ Records the steps used to search for a syntax error
+ to the given directory
+
+ Options:
+ EOM
+
+ opts.version = SyntaxSuggest::VERSION
+
+ opts.on("--help", "Help - displays this message") do |v|
+ @io.puts opts
+ options[:exit] = true
+ @exit_obj.exit
+ end
+
+ opts.on("--record <dir>", "Records the steps used to search for a syntax error to the given directory") do |v|
+ options[:record_dir] = v
+ end
+
+ opts.on("--terminal", "Enable terminal highlighting") do |v|
+ options[:terminal] = true
+ end
+
+ opts.on("--no-terminal", "Disable terminal highlighting") do |v|
+ options[:terminal] = false
+ end
+ end
+ end
+ end
+end
diff --git a/lib/syntax_suggest/code_block.rb b/lib/syntax_suggest/code_block.rb
new file mode 100644
index 0000000000..61e7986da4
--- /dev/null
+++ b/lib/syntax_suggest/code_block.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Multiple lines form a singular CodeBlock
+ #
+ # Source code is made of multiple CodeBlocks.
+ #
+ # Example:
+ #
+ # code_block.to_s # =>
+ # # def foo
+ # # puts "foo"
+ # # end
+ #
+ # code_block.valid? # => true
+ # code_block.in_valid? # => false
+ #
+ #
+ class CodeBlock
+ UNSET = Object.new.freeze
+ attr_reader :lines, :starts_at, :ends_at
+
+ def initialize(lines: [])
+ @lines = Array(lines)
+ @valid = UNSET
+ @deleted = false
+ @starts_at = @lines.first.number
+ @ends_at = @lines.last.number
+ end
+
+ def delete
+ @deleted = true
+ end
+
+ def deleted?
+ @deleted
+ end
+
+ def visible_lines
+ @lines.select(&:visible?).select(&:not_empty?)
+ end
+
+ def mark_invisible
+ @lines.map(&:mark_invisible)
+ end
+
+ def is_end?
+ to_s.strip == "end"
+ end
+
+ def hidden?
+ @lines.all?(&:hidden?)
+ end
+
+ # This is used for frontier ordering, we are searching from
+ # the largest indentation to the smallest. This allows us to
+ # populate an array with multiple code blocks then call `sort!`
+ # on it without having to specify the sorting criteria
+ def <=>(other)
+ out = current_indent <=> other.current_indent
+ return out if out != 0
+
+ # Stable sort
+ starts_at <=> other.starts_at
+ end
+
+ def current_indent
+ @current_indent ||= lines.select(&:not_empty?).map(&:indent).min || 0
+ end
+
+ def invalid?
+ !valid?
+ end
+
+ def valid?
+ if @valid == UNSET
+ # Performance optimization
+ #
+ # If all the lines were previously hidden
+ # and we expand to capture additional empty
+ # lines then the result cannot be invalid
+ #
+ # That means there's no reason to re-check all
+ # lines with ripper (which is expensive).
+ # Benchmark in commit message
+ @valid = if lines.all? { |l| l.hidden? || l.empty? }
+ true
+ else
+ SyntaxSuggest.valid?(lines.map(&:original).join)
+ end
+ else
+ @valid
+ end
+ end
+
+ def to_s
+ @lines.join
+ end
+ end
+end
diff --git a/lib/syntax_suggest/code_frontier.rb b/lib/syntax_suggest/code_frontier.rb
new file mode 100644
index 0000000000..8e93b32514
--- /dev/null
+++ b/lib/syntax_suggest/code_frontier.rb
@@ -0,0 +1,178 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # The main function of the frontier is to hold the edges of our search and to
+ # evaluate when we can stop searching.
+
+ # There are three main phases in the algorithm:
+ #
+ # 1. Sanitize/format input source
+ # 2. Search for invalid blocks
+ # 3. Format invalid blocks into something meaninful
+ #
+ # The Code frontier is a critical part of the second step
+ #
+ # ## Knowing where we've been
+ #
+ # Once a code block is generated it is added onto the frontier. Then it will be
+ # sorted by indentation and frontier can be filtered. Large blocks that fully enclose a
+ # smaller block will cause the smaller block to be evicted.
+ #
+ # CodeFrontier#<<(block) # Adds block to frontier
+ # CodeFrontier#pop # Removes block from frontier
+ #
+ # ## Knowing where we can go
+ #
+ # Internally the frontier keeps track of "unvisited" lines which are exposed via `next_indent_line`
+ # when called, this method returns, a line of code with the highest indentation.
+ #
+ # The returned line of code can be used to build a CodeBlock and then that code block
+ # is added back to the frontier. Then, the lines are removed from the
+ # "unvisited" so we don't double-create the same block.
+ #
+ # CodeFrontier#next_indent_line # Shows next line
+ # CodeFrontier#register_indent_block(block) # Removes lines from unvisited
+ #
+ # ## Knowing when to stop
+ #
+ # The frontier knows how to check the entire document for a syntax error. When blocks
+ # are added onto the frontier, they're removed from the document. When all code containing
+ # syntax errors has been added to the frontier, the document will be parsable without a
+ # syntax error and the search can stop.
+ #
+ # CodeFrontier#holds_all_syntax_errors? # Returns true when frontier holds all syntax errors
+ #
+ # ## Filtering false positives
+ #
+ # Once the search is completed, the frontier may have multiple blocks that do not contain
+ # the syntax error. To limit the result to the smallest subset of "invalid blocks" call:
+ #
+ # CodeFrontier#detect_invalid_blocks
+ #
+ class CodeFrontier
+ def initialize(code_lines:, unvisited: UnvisitedLines.new(code_lines: code_lines))
+ @code_lines = code_lines
+ @unvisited = unvisited
+ @queue = PriorityEngulfQueue.new
+
+ @check_next = true
+ end
+
+ def count
+ @queue.length
+ end
+
+ # Performance optimization
+ #
+ # Parsing with ripper is expensive
+ # If we know we don't have any blocks with invalid
+ # syntax, then we know we cannot have found
+ # the incorrect syntax yet.
+ #
+ # When an invalid block is added onto the frontier
+ # check document state
+ private def can_skip_check?
+ check_next = @check_next
+ @check_next = false
+
+ if check_next
+ false
+ else
+ true
+ end
+ end
+
+ # Returns true if the document is valid with all lines
+ # removed. By default it checks all blocks in present in
+ # the frontier array, but can be used for arbitrary arrays
+ # of codeblocks as well
+ def holds_all_syntax_errors?(block_array = @queue, can_cache: true)
+ return false if can_cache && can_skip_check?
+
+ without_lines = block_array.to_a.flat_map do |block|
+ block.lines
+ end
+
+ SyntaxSuggest.valid_without?(
+ without_lines: without_lines,
+ code_lines: @code_lines
+ )
+ end
+
+ # Returns a code block with the largest indentation possible
+ def pop
+ @queue.pop
+ end
+
+ def next_indent_line
+ @unvisited.peek
+ end
+
+ def expand?
+ return false if @queue.empty?
+ return true if @unvisited.empty?
+
+ frontier_indent = @queue.peek.current_indent
+ unvisited_indent = next_indent_line.indent
+
+ if ENV["SYNTAX_SUGGEST_DEBUG"]
+ puts "```"
+ puts @queue.peek.to_s
+ puts "```"
+ puts " @frontier indent: #{frontier_indent}"
+ puts " @unvisited indent: #{unvisited_indent}"
+ end
+
+ # Expand all blocks before moving to unvisited lines
+ frontier_indent >= unvisited_indent
+ end
+
+ # Keeps track of what lines have been added to blocks and which are not yet
+ # visited.
+ def register_indent_block(block)
+ @unvisited.visit_block(block)
+ self
+ end
+
+ # When one element fully encapsulates another we remove the smaller
+ # block from the frontier. This prevents double expansions and all-around
+ # weird behavior. However this guarantee is quite expensive to maintain
+ def register_engulf_block(block)
+ end
+
+ # Add a block to the frontier
+ #
+ # This method ensures the frontier always remains sorted (in indentation order)
+ # and that each code block's lines are removed from the indentation hash so we
+ # don't re-evaluate the same line multiple times.
+ def <<(block)
+ @unvisited.visit_block(block)
+
+ @queue.push(block)
+
+ @check_next = true if block.invalid?
+
+ self
+ end
+
+ # Example:
+ #
+ # combination([:a, :b, :c, :d])
+ # # => [[:a], [:b], [:c], [:d], [:a, :b], [:a, :c], [:a, :d], [:b, :c], [:b, :d], [:c, :d], [:a, :b, :c], [:a, :b, :d], [:a, :c, :d], [:b, :c, :d], [:a, :b, :c, :d]]
+ def self.combination(array)
+ guesses = []
+ 1.upto(array.length).each do |size|
+ guesses.concat(array.combination(size).to_a)
+ end
+ guesses
+ end
+
+ # Given that we know our syntax error exists somewhere in our frontier, we want to find
+ # the smallest possible set of blocks that contain all the syntax errors
+ def detect_invalid_blocks
+ self.class.combination(@queue.to_a.select(&:invalid?)).detect do |block_array|
+ holds_all_syntax_errors?(block_array, can_cache: false)
+ end || []
+ end
+ end
+end
diff --git a/lib/syntax_suggest/code_line.rb b/lib/syntax_suggest/code_line.rb
new file mode 100644
index 0000000000..a20f34afa4
--- /dev/null
+++ b/lib/syntax_suggest/code_line.rb
@@ -0,0 +1,237 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Represents a single line of code of a given source file
+ #
+ # This object contains metadata about the line such as
+ # amount of indentation, if it is empty or not, and
+ # lexical data, such as if it has an `end` or a keyword
+ # in it.
+ #
+ # Visibility of lines can be toggled off. Marking a line as invisible
+ # indicates that it should not be used for syntax checks.
+ # It's functionally the same as commenting it out.
+ #
+ # Example:
+ #
+ # line = CodeLine.from_source("def foo\n").first
+ # line.number => 1
+ # line.empty? # => false
+ # line.visible? # => true
+ # line.mark_invisible
+ # line.visible? # => false
+ #
+ class CodeLine
+ TRAILING_SLASH = ("\\" + $/).freeze
+
+ # Returns an array of CodeLine objects
+ # from the source string
+ def self.from_source(source, lines: nil)
+ lines ||= source.lines
+ lex_array_for_line = LexAll.new(source: source, source_lines: lines).each_with_object(Hash.new { |h, k| h[k] = [] }) { |lex, hash| hash[lex.line] << lex }
+ lines.map.with_index do |line, index|
+ CodeLine.new(
+ line: line,
+ index: index,
+ lex: lex_array_for_line[index + 1]
+ )
+ end
+ end
+
+ attr_reader :line, :index, :lex, :line_number, :indent
+ def initialize(line:, index:, lex:)
+ @lex = lex
+ @line = line
+ @index = index
+ @original = line
+ @line_number = @index + 1
+ strip_line = line.dup
+ strip_line.lstrip!
+
+ @indent = if (@empty = strip_line.empty?)
+ line.length - 1 # Newline removed from strip_line is not "whitespace"
+ else
+ line.length - strip_line.length
+ end
+
+ set_kw_end
+ end
+
+ # Used for stable sort via indentation level
+ #
+ # Ruby's sort is not "stable" meaning that when
+ # multiple elements have the same value, they are
+ # not guaranteed to return in the same order they
+ # were put in.
+ #
+ # So when multiple code lines have the same indentation
+ # level, they're sorted by their index value which is unique
+ # and consistent.
+ #
+ # This is mostly needed for consistency of the test suite
+ def indent_index
+ @indent_index ||= [indent, index]
+ end
+ alias_method :number, :line_number
+
+ # Returns true if the code line is determined
+ # to contain a keyword that matches with an `end`
+ #
+ # For example: `def`, `do`, `begin`, `ensure`, etc.
+ def is_kw?
+ @is_kw
+ end
+
+ # Returns true if the code line is determined
+ # to contain an `end` keyword
+ def is_end?
+ @is_end
+ end
+
+ # Used to hide lines
+ #
+ # The search alorithm will group lines into blocks
+ # then if those blocks are determined to represent
+ # valid code they will be hidden
+ def mark_invisible
+ @line = ""
+ end
+
+ # Means the line was marked as "invisible"
+ # Confusingly, "empty" lines are visible...they
+ # just don't contain any source code other than a newline ("\n").
+ def visible?
+ !line.empty?
+ end
+
+ # Opposite or `visible?` (note: different than `empty?`)
+ def hidden?
+ !visible?
+ end
+
+ # An `empty?` line is one that was originally left
+ # empty in the source code, while a "hidden" line
+ # is one that we've since marked as "invisible"
+ def empty?
+ @empty
+ end
+
+ # Opposite of `empty?` (note: different than `visible?`)
+ def not_empty?
+ !empty?
+ end
+
+ # Renders the given line
+ #
+ # Also allows us to represent source code as
+ # an array of code lines.
+ #
+ # When we have an array of code line elements
+ # calling `join` on the array will call `to_s`
+ # on each element, which essentially converts
+ # it back into it's original source string.
+ def to_s
+ line
+ end
+
+ # When the code line is marked invisible
+ # we retain the original value of it's line
+ # this is useful for debugging and for
+ # showing extra context
+ #
+ # DisplayCodeWithLineNumbers will render
+ # all lines given to it, not just visible
+ # lines, it uses the original method to
+ # obtain them.
+ attr_reader :original
+
+ # Comparison operator, needed for equality
+ # and sorting
+ def <=>(other)
+ index <=> other.index
+ end
+
+ # [Not stable API]
+ #
+ # Lines that have a `on_ignored_nl` type token and NOT
+ # a `BEG` type seem to be a good proxy for the ability
+ # to join multiple lines into one.
+ #
+ # This predicate method is used to determine when those
+ # two criteria have been met.
+ #
+ # The one known case this doesn't handle is:
+ #
+ # Ripper.lex <<~EOM
+ # a &&
+ # b ||
+ # c
+ # EOM
+ #
+ # For some reason this introduces `on_ignore_newline` but with BEG type
+ def ignore_newline_not_beg?
+ @ignore_newline_not_beg
+ end
+
+ # Determines if the given line has a trailing slash
+ #
+ # lines = CodeLine.from_source(<<~EOM)
+ # it "foo" \
+ # EOM
+ # expect(lines.first.trailing_slash?).to eq(true)
+ #
+ def trailing_slash?
+ last = @lex.last
+ return false unless last
+ return false unless last.type == :on_sp
+
+ last.token == TRAILING_SLASH
+ end
+
+ # Endless method detection
+ #
+ # From https://github.com/ruby/irb/commit/826ae909c9c93a2ddca6f9cfcd9c94dbf53d44ab
+ # Detecting a "oneliner" seems to need a state machine.
+ # This can be done by looking mostly at the "state" (last value):
+ #
+ # ENDFN -> BEG (token = '=' ) -> END
+ #
+ private def set_kw_end
+ oneliner_count = 0
+ in_oneliner_def = nil
+
+ kw_count = 0
+ end_count = 0
+
+ @ignore_newline_not_beg = false
+ @lex.each do |lex|
+ kw_count += 1 if lex.is_kw?
+ end_count += 1 if lex.is_end?
+
+ if lex.type == :on_ignored_nl
+ @ignore_newline_not_beg = !lex.expr_beg?
+ end
+
+ if in_oneliner_def.nil?
+ in_oneliner_def = :ENDFN if lex.state.allbits?(Ripper::EXPR_ENDFN)
+ elsif lex.state.allbits?(Ripper::EXPR_ENDFN)
+ # Continue
+ elsif lex.state.allbits?(Ripper::EXPR_BEG)
+ in_oneliner_def = :BODY if lex.token == "="
+ elsif lex.state.allbits?(Ripper::EXPR_END)
+ # We found an endless method, count it
+ oneliner_count += 1 if in_oneliner_def == :BODY
+
+ in_oneliner_def = nil
+ else
+ in_oneliner_def = nil
+ end
+ end
+
+ kw_count -= oneliner_count
+
+ @is_kw = (kw_count - end_count) > 0
+ @is_end = (end_count - kw_count) > 0
+ end
+ end
+end
diff --git a/lib/syntax_suggest/code_search.rb b/lib/syntax_suggest/code_search.rb
new file mode 100644
index 0000000000..2a86dfea90
--- /dev/null
+++ b/lib/syntax_suggest/code_search.rb
@@ -0,0 +1,139 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Searches code for a syntax error
+ #
+ # There are three main phases in the algorithm:
+ #
+ # 1. Sanitize/format input source
+ # 2. Search for invalid blocks
+ # 3. Format invalid blocks into something meaninful
+ #
+ # This class handles the part.
+ #
+ # The bulk of the heavy lifting is done in:
+ #
+ # - CodeFrontier (Holds information for generating blocks and determining if we can stop searching)
+ # - ParseBlocksFromLine (Creates blocks into the frontier)
+ # - BlockExpand (Expands existing blocks to search more code)
+ #
+ # ## Syntax error detection
+ #
+ # When the frontier holds the syntax error, we can stop searching
+ #
+ # search = CodeSearch.new(<<~EOM)
+ # def dog
+ # def lol
+ # end
+ # EOM
+ #
+ # search.call
+ #
+ # search.invalid_blocks.map(&:to_s) # =>
+ # # => ["def lol\n"]
+ #
+ class CodeSearch
+ private
+
+ attr_reader :frontier
+
+ public
+
+ attr_reader :invalid_blocks, :record_dir, :code_lines
+
+ def initialize(source, record_dir: DEFAULT_VALUE)
+ record_dir = if record_dir == DEFAULT_VALUE
+ ENV["SYNTAX_SUGGEST_RECORD_DIR"] || ENV["SYNTAX_SUGGEST_DEBUG"] ? "tmp" : nil
+ else
+ record_dir
+ end
+
+ if record_dir
+ @record_dir = SyntaxSuggest.record_dir(record_dir)
+ @write_count = 0
+ end
+
+ @tick = 0
+ @source = source
+ @name_tick = Hash.new { |hash, k| hash[k] = 0 }
+ @invalid_blocks = []
+
+ @code_lines = CleanDocument.new(source: source).call.lines
+
+ @frontier = CodeFrontier.new(code_lines: @code_lines)
+ @block_expand = BlockExpand.new(code_lines: @code_lines)
+ @parse_blocks_from_indent_line = ParseBlocksFromIndentLine.new(code_lines: @code_lines)
+ end
+
+ # Used for debugging
+ def record(block:, name: "record")
+ return unless @record_dir
+ @name_tick[name] += 1
+ filename = "#{@write_count += 1}-#{name}-#{@name_tick[name]}-(#{block.starts_at}__#{block.ends_at}).txt"
+ if ENV["SYNTAX_SUGGEST_DEBUG"]
+ puts "\n\n==== #{filename} ===="
+ puts "\n```#{block.starts_at}..#{block.ends_at}"
+ puts block.to_s
+ puts "```"
+ puts " block indent: #{block.current_indent}"
+ end
+ @record_dir.join(filename).open(mode: "a") do |f|
+ document = DisplayCodeWithLineNumbers.new(
+ lines: @code_lines.select(&:visible?),
+ terminal: false,
+ highlight_lines: block.lines
+ ).call
+
+ f.write(" Block lines: #{block.starts_at..block.ends_at} (#{name}) \n\n#{document}")
+ end
+ end
+
+ def push(block, name:)
+ record(block: block, name: name)
+
+ block.mark_invisible if block.valid?
+ frontier << block
+ end
+
+ # Parses the most indented lines into blocks that are marked
+ # and added to the frontier
+ def create_blocks_from_untracked_lines
+ max_indent = frontier.next_indent_line&.indent
+
+ while (line = frontier.next_indent_line) && (line.indent == max_indent)
+ @parse_blocks_from_indent_line.each_neighbor_block(frontier.next_indent_line) do |block|
+ push(block, name: "add")
+ end
+ end
+ end
+
+ # Given an already existing block in the frontier, expand it to see
+ # if it contains our invalid syntax
+ def expand_existing
+ block = frontier.pop
+ return unless block
+
+ record(block: block, name: "before-expand")
+
+ block = @block_expand.call(block)
+ push(block, name: "expand")
+ end
+
+ # Main search loop
+ def call
+ until frontier.holds_all_syntax_errors?
+ @tick += 1
+
+ if frontier.expand?
+ expand_existing
+ else
+ create_blocks_from_untracked_lines
+ end
+ end
+
+ @invalid_blocks.concat(frontier.detect_invalid_blocks)
+ @invalid_blocks.sort_by! { |block| block.starts_at }
+ self
+ end
+ end
+end
diff --git a/lib/syntax_suggest/core_ext.rb b/lib/syntax_suggest/core_ext.rb
new file mode 100644
index 0000000000..e0fd62b81c
--- /dev/null
+++ b/lib/syntax_suggest/core_ext.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+# Ruby 3.2+ has a cleaner way to hook into Ruby that doesn't use `require`
+if SyntaxError.method_defined?(:detailed_message)
+ module SyntaxSuggest
+ # Mini String IO [Private]
+ #
+ # Acts like a StringIO with reduced API, but without having to require that
+ # class.
+ class MiniStringIO
+ def initialize(isatty: $stderr.isatty)
+ @string = +""
+ @isatty = isatty
+ end
+
+ attr_reader :isatty
+ def puts(value = $/, **)
+ @string << value
+ end
+
+ attr_reader :string
+ end
+
+ # SyntaxSuggest.record_dir [Private]
+ #
+ # Used to monkeypatch SyntaxError via Module.prepend
+ def self.module_for_detailed_message
+ Module.new {
+ def detailed_message(highlight: true, syntax_suggest: true, **kwargs)
+ return super unless syntax_suggest
+
+ require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE)
+
+ message = super
+
+ if path
+ file = Pathname.new(path)
+ io = SyntaxSuggest::MiniStringIO.new
+
+ SyntaxSuggest.call(
+ io: io,
+ source: file.read,
+ filename: file,
+ terminal: highlight
+ )
+ annotation = io.string
+
+ annotation += "\n" unless annotation.end_with?("\n")
+
+ annotation + message
+ else
+ message
+ end
+ rescue => e
+ if ENV["SYNTAX_SUGGEST_DEBUG"]
+ $stderr.warn(e.message)
+ $stderr.warn(e.backtrace)
+ end
+
+ # Ignore internal errors
+ message
+ end
+ }
+ end
+ end
+
+ SyntaxError.prepend(SyntaxSuggest.module_for_detailed_message)
+else
+ autoload :Pathname, "pathname"
+
+ #--
+ # Monkey patch kernel to ensure that all `require` calls call the same
+ # method
+ #++
+ module Kernel
+ # :stopdoc:
+
+ module_function
+
+ alias_method :syntax_suggest_original_require, :require
+ alias_method :syntax_suggest_original_require_relative, :require_relative
+ alias_method :syntax_suggest_original_load, :load
+
+ def load(file, wrap = false)
+ syntax_suggest_original_load(file)
+ rescue SyntaxError => e
+ require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE)
+
+ SyntaxSuggest.handle_error(e)
+ end
+
+ def require(file)
+ syntax_suggest_original_require(file)
+ rescue SyntaxError => e
+ require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE)
+
+ SyntaxSuggest.handle_error(e)
+ end
+
+ def require_relative(file)
+ if Pathname.new(file).absolute?
+ syntax_suggest_original_require file
+ else
+ relative_from = caller_locations(1..1).first
+ relative_from_path = relative_from.absolute_path || relative_from.path
+ syntax_suggest_original_require File.expand_path("../#{file}", relative_from_path)
+ end
+ rescue SyntaxError => e
+ require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE)
+
+ SyntaxSuggest.handle_error(e)
+ end
+ end
+end
diff --git a/lib/syntax_suggest/display_code_with_line_numbers.rb b/lib/syntax_suggest/display_code_with_line_numbers.rb
new file mode 100644
index 0000000000..a18d62e54b
--- /dev/null
+++ b/lib/syntax_suggest/display_code_with_line_numbers.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Outputs code with highlighted lines
+ #
+ # Whatever is passed to this class will be rendered
+ # even if it is "marked invisible" any filtering of
+ # output should be done before calling this class.
+ #
+ # DisplayCodeWithLineNumbers.new(
+ # lines: lines,
+ # highlight_lines: [lines[2], lines[3]]
+ # ).call
+ # # =>
+ # 1
+ # 2 def cat
+ # > 3 Dir.chdir
+ # > 4 end
+ # 5 end
+ # 6
+ class DisplayCodeWithLineNumbers
+ TERMINAL_HIGHLIGHT = "\e[1;3m" # Bold, italics
+ TERMINAL_END = "\e[0m"
+
+ def initialize(lines:, highlight_lines: [], terminal: false)
+ @lines = Array(lines).sort
+ @terminal = terminal
+ @highlight_line_hash = Array(highlight_lines).each_with_object({}) { |line, h| h[line] = true }
+ @digit_count = @lines.last&.line_number.to_s.length
+ end
+
+ def call
+ @lines.map do |line|
+ format_line(line)
+ end.join
+ end
+
+ private def format_line(code_line)
+ # Handle trailing slash lines
+ code_line.original.lines.map.with_index do |contents, i|
+ format(
+ empty: code_line.empty?,
+ number: (code_line.number + i).to_s,
+ contents: contents,
+ highlight: @highlight_line_hash[code_line]
+ )
+ end.join
+ end
+
+ private def format(contents:, number:, empty:, highlight: false)
+ string = +""
+ string << if highlight
+ "> "
+ else
+ " "
+ end
+
+ string << number.rjust(@digit_count).to_s
+ if empty
+ string << contents
+ else
+ string << " "
+ string << TERMINAL_HIGHLIGHT if @terminal && highlight
+ string << contents
+ string << TERMINAL_END if @terminal
+ end
+ string
+ end
+ end
+end
diff --git a/lib/syntax_suggest/display_invalid_blocks.rb b/lib/syntax_suggest/display_invalid_blocks.rb
new file mode 100644
index 0000000000..32ec0021a3
--- /dev/null
+++ b/lib/syntax_suggest/display_invalid_blocks.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require_relative "capture_code_context"
+require_relative "display_code_with_line_numbers"
+
+module SyntaxSuggest
+ # Used for formatting invalid blocks
+ class DisplayInvalidBlocks
+ attr_reader :filename
+
+ def initialize(code_lines:, blocks:, io: $stderr, filename: nil, terminal: DEFAULT_VALUE)
+ @io = io
+ @blocks = Array(blocks)
+ @filename = filename
+ @code_lines = code_lines
+
+ @terminal = terminal == DEFAULT_VALUE ? io.isatty : terminal
+ end
+
+ def document_ok?
+ @blocks.none? { |b| !b.hidden? }
+ end
+
+ def call
+ if document_ok?
+ return self
+ end
+
+ if filename
+ @io.puts("--> #{filename}")
+ @io.puts
+ end
+ @blocks.each do |block|
+ display_block(block)
+ end
+
+ self
+ end
+
+ private def display_block(block)
+ # Build explanation
+ explain = ExplainSyntax.new(
+ code_lines: block.lines
+ ).call
+
+ # Enhance code output
+ # Also handles several ambiguious cases
+ lines = CaptureCodeContext.new(
+ blocks: block,
+ code_lines: @code_lines
+ ).call
+
+ # Build code output
+ document = DisplayCodeWithLineNumbers.new(
+ lines: lines,
+ terminal: @terminal,
+ highlight_lines: block.lines
+ ).call
+
+ # Output syntax error explanation
+ explain.errors.each do |e|
+ @io.puts e
+ end
+ @io.puts
+
+ # Output code
+ @io.puts(document)
+ end
+
+ private def code_with_context
+ lines = CaptureCodeContext.new(
+ blocks: @blocks,
+ code_lines: @code_lines
+ ).call
+
+ DisplayCodeWithLineNumbers.new(
+ lines: lines,
+ terminal: @terminal,
+ highlight_lines: @invalid_lines
+ ).call
+ end
+ end
+end
diff --git a/lib/syntax_suggest/explain_syntax.rb b/lib/syntax_suggest/explain_syntax.rb
new file mode 100644
index 0000000000..142ed2e269
--- /dev/null
+++ b/lib/syntax_suggest/explain_syntax.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+require_relative "left_right_lex_count"
+
+module SyntaxSuggest
+ # Explains syntax errors based on their source
+ #
+ # example:
+ #
+ # source = "def foo; puts 'lol'" # Note missing end
+ # explain ExplainSyntax.new(
+ # code_lines: CodeLine.from_source(source)
+ # ).call
+ # explain.errors.first
+ # # => "Unmatched keyword, missing `end' ?"
+ #
+ # When the error cannot be determined by lexical counting
+ # then ripper is run against the input and the raw ripper
+ # errors returned.
+ #
+ # Example:
+ #
+ # source = "1 * " # Note missing a second number
+ # explain ExplainSyntax.new(
+ # code_lines: CodeLine.from_source(source)
+ # ).call
+ # explain.errors.first
+ # # => "syntax error, unexpected end-of-input"
+ class ExplainSyntax
+ INVERSE = {
+ "{" => "}",
+ "}" => "{",
+ "[" => "]",
+ "]" => "[",
+ "(" => ")",
+ ")" => "(",
+ "|" => "|"
+ }.freeze
+
+ def initialize(code_lines:)
+ @code_lines = code_lines
+ @left_right = LeftRightLexCount.new
+ @missing = nil
+ end
+
+ def call
+ @code_lines.each do |line|
+ line.lex.each do |lex|
+ @left_right.count_lex(lex)
+ end
+ end
+
+ self
+ end
+
+ # Returns an array of missing elements
+ #
+ # For example this:
+ #
+ # ExplainSyntax.new(code_lines: lines).missing
+ # # => ["}"]
+ #
+ # Would indicate that the source is missing
+ # a `}` character in the source code
+ def missing
+ @missing ||= @left_right.missing
+ end
+
+ # Converts a missing string to
+ # an human understandable explanation.
+ #
+ # Example:
+ #
+ # explain.why("}")
+ # # => "Unmatched `{', missing `}' ?"
+ #
+ def why(miss)
+ case miss
+ when "keyword"
+ "Unmatched `end', missing keyword (`do', `def`, `if`, etc.) ?"
+ when "end"
+ "Unmatched keyword, missing `end' ?"
+ else
+ inverse = INVERSE.fetch(miss) {
+ raise "Unknown explain syntax char or key: #{miss.inspect}"
+ }
+ "Unmatched `#{inverse}', missing `#{miss}' ?"
+ end
+ end
+
+ # Returns an array of syntax error messages
+ #
+ # If no missing pairs are found it falls back
+ # on the original ripper error messages
+ def errors
+ if missing.empty?
+ return RipperErrors.new(@code_lines.map(&:original).join).call.errors
+ end
+
+ missing.map { |miss| why(miss) }
+ end
+ end
+end
diff --git a/lib/syntax_suggest/left_right_lex_count.rb b/lib/syntax_suggest/left_right_lex_count.rb
new file mode 100644
index 0000000000..6fcae7482b
--- /dev/null
+++ b/lib/syntax_suggest/left_right_lex_count.rb
@@ -0,0 +1,168 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Find mis-matched syntax based on lexical count
+ #
+ # Used for detecting missing pairs of elements
+ # each keyword needs an end, each '{' needs a '}'
+ # etc.
+ #
+ # Example:
+ #
+ # left_right = LeftRightLexCount.new
+ # left_right.count_kw
+ # left_right.missing.first
+ # # => "end"
+ #
+ # left_right = LeftRightLexCount.new
+ # source = "{ a: b, c: d" # Note missing '}'
+ # LexAll.new(source: source).each do |lex|
+ # left_right.count_lex(lex)
+ # end
+ # left_right.missing.first
+ # # => "}"
+ class LeftRightLexCount
+ def initialize
+ @kw_count = 0
+ @end_count = 0
+
+ @count_for_char = {
+ "{" => 0,
+ "}" => 0,
+ "[" => 0,
+ "]" => 0,
+ "(" => 0,
+ ")" => 0,
+ "|" => 0
+ }
+ end
+
+ def count_kw
+ @kw_count += 1
+ end
+
+ def count_end
+ @end_count += 1
+ end
+
+ # Count source code characters
+ #
+ # Example:
+ #
+ # left_right = LeftRightLexCount.new
+ # left_right.count_lex(LexValue.new(1, :on_lbrace, "{", Ripper::EXPR_BEG))
+ # left_right.count_for_char("{")
+ # # => 1
+ # left_right.count_for_char("}")
+ # # => 0
+ def count_lex(lex)
+ case lex.type
+ when :on_tstring_content
+ # ^^^
+ # Means it's a string or a symbol `"{"` rather than being
+ # part of a data structure (like a hash) `{ a: b }`
+ # ignore it.
+ when :on_words_beg, :on_symbos_beg, :on_qwords_beg,
+ :on_qsymbols_beg, :on_regexp_beg, :on_tstring_beg
+ # ^^^
+ # Handle shorthand syntaxes like `%Q{ i am a string }`
+ #
+ # The start token will be the full thing `%Q{` but we
+ # need to count it as if it's a `{`. Any token
+ # can be used
+ char = lex.token[-1]
+ @count_for_char[char] += 1 if @count_for_char.key?(char)
+ when :on_embexpr_beg
+ # ^^^
+ # Embedded string expressions like `"#{foo} <-embed"`
+ # are parsed with chars:
+ #
+ # `#{` as :on_embexpr_beg
+ # `}` as :on_embexpr_end
+ #
+ # We cannot ignore both :on_emb_expr_beg and :on_embexpr_end
+ # because sometimes the lexer thinks something is an embed
+ # string end, when it is not like `lol = }` (no clue why).
+ #
+ # When we see `#{` count it as a `{` or we will
+ # have a mis-match count.
+ #
+ case lex.token
+ when "\#{"
+ @count_for_char["{"] += 1
+ end
+ else
+ @end_count += 1 if lex.is_end?
+ @kw_count += 1 if lex.is_kw?
+ @count_for_char[lex.token] += 1 if @count_for_char.key?(lex.token)
+ end
+ end
+
+ def count_for_char(char)
+ @count_for_char[char]
+ end
+
+ # Returns an array of missing syntax characters
+ # or `"end"` or `"keyword"`
+ #
+ # left_right.missing
+ # # => ["}"]
+ def missing
+ out = missing_pairs
+ out << missing_pipe
+ out << missing_keyword_end
+ out.compact!
+ out
+ end
+
+ PAIRS = {
+ "{" => "}",
+ "[" => "]",
+ "(" => ")"
+ }.freeze
+
+ # Opening characters like `{` need closing characters # like `}`.
+ #
+ # When a mis-match count is detected, suggest the
+ # missing member.
+ #
+ # For example if there are 3 `}` and only two `{`
+ # return `"{"`
+ private def missing_pairs
+ PAIRS.map do |(left, right)|
+ case @count_for_char[left] <=> @count_for_char[right]
+ when 1
+ right
+ when 0
+ nil
+ when -1
+ left
+ end
+ end
+ end
+
+ # Keywords need ends and ends need keywords
+ #
+ # If we have more keywords, there's a missing `end`
+ # if we have more `end`-s, there's a missing keyword
+ private def missing_keyword_end
+ case @kw_count <=> @end_count
+ when 1
+ "end"
+ when 0
+ nil
+ when -1
+ "keyword"
+ end
+ end
+
+ # Pipes come in pairs.
+ # If there's an odd number of pipes then we
+ # are missing one
+ private def missing_pipe
+ if @count_for_char["|"].odd?
+ "|"
+ end
+ end
+ end
+end
diff --git a/lib/syntax_suggest/lex_all.rb b/lib/syntax_suggest/lex_all.rb
new file mode 100644
index 0000000000..132cba9f5d
--- /dev/null
+++ b/lib/syntax_suggest/lex_all.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Ripper.lex is not guaranteed to lex the entire source document
+ #
+ # lex = LexAll.new(source: source)
+ # lex.each do |value|
+ # puts value.line
+ # end
+ class LexAll
+ include Enumerable
+
+ def initialize(source:, source_lines: nil)
+ @lex = Ripper::Lexer.new(source, "-", 1).parse.sort_by(&:pos)
+ lineno = @lex.last.pos.first + 1
+ source_lines ||= source.lines
+ last_lineno = source_lines.length
+
+ until lineno >= last_lineno
+ lines = source_lines[lineno..-1]
+
+ @lex.concat(
+ Ripper::Lexer.new(lines.join, "-", lineno + 1).parse.sort_by(&:pos)
+ )
+ lineno = @lex.last.pos.first + 1
+ end
+
+ last_lex = nil
+ @lex.map! { |elem|
+ last_lex = LexValue.new(elem.pos.first, elem.event, elem.tok, elem.state, last_lex)
+ }
+ end
+
+ def to_a
+ @lex
+ end
+
+ def each
+ return @lex.each unless block_given?
+ @lex.each do |x|
+ yield x
+ end
+ end
+
+ def [](index)
+ @lex[index]
+ end
+
+ def last
+ @lex.last
+ end
+ end
+end
+
+require_relative "lex_value"
diff --git a/lib/syntax_suggest/lex_value.rb b/lib/syntax_suggest/lex_value.rb
new file mode 100644
index 0000000000..008cc105b5
--- /dev/null
+++ b/lib/syntax_suggest/lex_value.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Value object for accessing lex values
+ #
+ # This lex:
+ #
+ # [1, 0], :on_ident, "describe", CMDARG
+ #
+ # Would translate into:
+ #
+ # lex.line # => 1
+ # lex.type # => :on_indent
+ # lex.token # => "describe"
+ class LexValue
+ attr_reader :line, :type, :token, :state
+
+ def initialize(line, type, token, state, last_lex = nil)
+ @line = line
+ @type = type
+ @token = token
+ @state = state
+
+ set_kw_end(last_lex)
+ end
+
+ private def set_kw_end(last_lex)
+ @is_end = false
+ @is_kw = false
+ return if type != :on_kw
+ #
+ return if last_lex && last_lex.fname? # https://github.com/ruby/ruby/commit/776759e300e4659bb7468e2b97c8c2d4359a2953
+
+ case token
+ when "if", "unless", "while", "until"
+ # Only count if/unless when it's not a "trailing" if/unless
+ # https://github.com/ruby/ruby/blob/06b44f819eb7b5ede1ff69cecb25682b56a1d60c/lib/irb/ruby-lex.rb#L374-L375
+ @is_kw = true unless expr_label?
+ when "def", "case", "for", "begin", "class", "module", "do"
+ @is_kw = true
+ when "end"
+ @is_end = true
+ end
+ end
+
+ def fname?
+ state.allbits?(Ripper::EXPR_FNAME)
+ end
+
+ def ignore_newline?
+ type == :on_ignored_nl
+ end
+
+ def is_end?
+ @is_end
+ end
+
+ def is_kw?
+ @is_kw
+ end
+
+ def expr_beg?
+ state.anybits?(Ripper::EXPR_BEG)
+ end
+
+ def expr_label?
+ state.allbits?(Ripper::EXPR_LABEL)
+ end
+ end
+end
diff --git a/lib/syntax_suggest/parse_blocks_from_indent_line.rb b/lib/syntax_suggest/parse_blocks_from_indent_line.rb
new file mode 100644
index 0000000000..241ed6acb4
--- /dev/null
+++ b/lib/syntax_suggest/parse_blocks_from_indent_line.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # This class is responsible for generating initial code blocks
+ # that will then later be expanded.
+ #
+ # The biggest concern when guessing code blocks, is accidentally
+ # grabbing one that contains only an "end". In this example:
+ #
+ # def dog
+ # begonn # mispelled `begin`
+ # puts "bark"
+ # end
+ # end
+ #
+ # The following lines would be matched (from bottom to top):
+ #
+ # 1) end
+ #
+ # 2) puts "bark"
+ # end
+ #
+ # 3) begonn
+ # puts "bark"
+ # end
+ #
+ # At this point it has no where else to expand, and it will yield this inner
+ # code as a block
+ class ParseBlocksFromIndentLine
+ attr_reader :code_lines
+
+ def initialize(code_lines:)
+ @code_lines = code_lines
+ end
+
+ # Builds blocks from bottom up
+ def each_neighbor_block(target_line)
+ scan = AroundBlockScan.new(code_lines: code_lines, block: CodeBlock.new(lines: target_line))
+ .force_add_empty
+ .force_add_hidden
+ .scan_while { |line| line.indent >= target_line.indent }
+
+ neighbors = scan.code_block.lines
+
+ block = CodeBlock.new(lines: neighbors)
+ if neighbors.length <= 2 || block.valid?
+ yield block
+ else
+ until neighbors.empty?
+ lines = [neighbors.pop]
+ while (block = CodeBlock.new(lines: lines)) && block.invalid? && neighbors.any?
+ lines.prepend neighbors.pop
+ end
+
+ yield block if block
+ end
+ end
+ end
+ end
+end
diff --git a/lib/syntax_suggest/pathname_from_message.rb b/lib/syntax_suggest/pathname_from_message.rb
new file mode 100644
index 0000000000..b6fe1617be
--- /dev/null
+++ b/lib/syntax_suggest/pathname_from_message.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Converts a SyntaxError message to a path
+ #
+ # Handles the case where the filename has a colon in it
+ # such as on a windows file system: https://github.com/ruby/syntax_suggest/issues/111
+ #
+ # Example:
+ #
+ # message = "/tmp/scratch:2:in `require_relative': /private/tmp/bad.rb:1: syntax error, unexpected `end' (SyntaxError)"
+ # puts PathnameFromMessage.new(message).call.name
+ # # => "/tmp/scratch.rb"
+ #
+ class PathnameFromMessage
+ EVAL_RE = /^\(eval\):\d+/
+ STREAMING_RE = /^-:\d+/
+ attr_reader :name
+
+ def initialize(message, io: $stderr)
+ @line = message.lines.first
+ @parts = @line.split(":")
+ @guess = []
+ @name = nil
+ @io = io
+ end
+
+ def call
+ if skip_missing_file_name?
+ if ENV["SYNTAX_SUGGEST_DEBUG"]
+ @io.puts "SyntaxSuggest: Could not find filename from #{@line.inspect}"
+ end
+ else
+ until stop?
+ @guess << @parts.shift
+ @name = Pathname(@guess.join(":"))
+ end
+
+ if @parts.empty?
+ @io.puts "SyntaxSuggest: Could not find filename from #{@line.inspect}"
+ @name = nil
+ end
+ end
+
+ self
+ end
+
+ def stop?
+ return true if @parts.empty?
+ return false if @guess.empty?
+
+ @name&.exist?
+ end
+
+ def skip_missing_file_name?
+ @line.match?(EVAL_RE) || @line.match?(STREAMING_RE)
+ end
+ end
+end
diff --git a/lib/syntax_suggest/priority_engulf_queue.rb b/lib/syntax_suggest/priority_engulf_queue.rb
new file mode 100644
index 0000000000..2d1e9b1b63
--- /dev/null
+++ b/lib/syntax_suggest/priority_engulf_queue.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Keeps track of what elements are in the queue in
+ # priority and also ensures that when one element
+ # engulfs/covers/eats another that the larger element
+ # evicts the smaller element
+ class PriorityEngulfQueue
+ def initialize
+ @queue = PriorityQueue.new
+ end
+
+ def to_a
+ @queue.to_a
+ end
+
+ def empty?
+ @queue.empty?
+ end
+
+ def length
+ @queue.length
+ end
+
+ def peek
+ @queue.peek
+ end
+
+ def pop
+ @queue.pop
+ end
+
+ def push(block)
+ prune_engulf(block)
+ @queue << block
+ flush_deleted
+
+ self
+ end
+
+ private def flush_deleted
+ while @queue&.peek&.deleted?
+ @queue.pop
+ end
+ end
+
+ private def prune_engulf(block)
+ # If we're about to pop off the same block, we can skip deleting
+ # things from the frontier this iteration since we'll get it
+ # on the next iteration
+ return if @queue.peek && (block <=> @queue.peek) == 1
+
+ if block.starts_at != block.ends_at # A block of size 1 cannot engulf another
+ @queue.to_a.each { |b|
+ if b.starts_at >= block.starts_at && b.ends_at <= block.ends_at
+ b.delete
+ true
+ end
+ }
+ end
+ end
+ end
+end
diff --git a/lib/syntax_suggest/priority_queue.rb b/lib/syntax_suggest/priority_queue.rb
new file mode 100644
index 0000000000..1abda2a444
--- /dev/null
+++ b/lib/syntax_suggest/priority_queue.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Holds elements in a priority heap on insert
+ #
+ # Instead of constantly calling `sort!`, put
+ # the element where it belongs the first time
+ # around
+ #
+ # Example:
+ #
+ # queue = PriorityQueue.new
+ # queue << 33
+ # queue << 44
+ # queue << 1
+ #
+ # puts queue.peek # => 44
+ #
+ class PriorityQueue
+ attr_reader :elements
+
+ def initialize
+ @elements = []
+ end
+
+ def <<(element)
+ @elements << element
+ bubble_up(last_index, element)
+ end
+
+ def pop
+ exchange(0, last_index)
+ max = @elements.pop
+ bubble_down(0)
+ max
+ end
+
+ def length
+ @elements.length
+ end
+
+ def empty?
+ @elements.empty?
+ end
+
+ def peek
+ @elements.first
+ end
+
+ def to_a
+ @elements
+ end
+
+ # Used for testing, extremely not performant
+ def sorted
+ out = []
+ elements = @elements.dup
+ while (element = pop)
+ out << element
+ end
+ @elements = elements
+ out.reverse
+ end
+
+ private def last_index
+ @elements.size - 1
+ end
+
+ private def bubble_up(index, element)
+ return if index <= 0
+
+ parent_index = (index - 1) / 2
+ parent = @elements[parent_index]
+
+ return if (parent <=> element) >= 0
+
+ exchange(index, parent_index)
+ bubble_up(parent_index, element)
+ end
+
+ private def bubble_down(index)
+ child_index = (index * 2) + 1
+
+ return if child_index > last_index
+
+ not_the_last_element = child_index < last_index
+ left_element = @elements[child_index]
+ right_element = @elements[child_index + 1]
+
+ child_index += 1 if not_the_last_element && (right_element <=> left_element) == 1
+
+ return if (@elements[index] <=> @elements[child_index]) >= 0
+
+ exchange(index, child_index)
+ bubble_down(child_index)
+ end
+
+ def exchange(source, target)
+ a = @elements[source]
+ b = @elements[target]
+ @elements[source] = b
+ @elements[target] = a
+ end
+ end
+end
diff --git a/lib/syntax_suggest/ripper_errors.rb b/lib/syntax_suggest/ripper_errors.rb
new file mode 100644
index 0000000000..48eb206e48
--- /dev/null
+++ b/lib/syntax_suggest/ripper_errors.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Capture parse errors from ripper
+ #
+ # Example:
+ #
+ # puts RipperErrors.new(" def foo").call.errors
+ # # => ["syntax error, unexpected end-of-input, expecting ';' or '\\n'"]
+ class RipperErrors < Ripper
+ attr_reader :errors
+
+ # Comes from ripper, called
+ # on every parse error, msg
+ # is a string
+ def on_parse_error(msg)
+ @errors ||= []
+ @errors << msg
+ end
+
+ alias_method :on_alias_error, :on_parse_error
+ alias_method :on_assign_error, :on_parse_error
+ alias_method :on_class_name_error, :on_parse_error
+ alias_method :on_param_error, :on_parse_error
+ alias_method :compile_error, :on_parse_error
+
+ def call
+ @run_once ||= begin
+ @errors = []
+ parse
+ true
+ end
+ self
+ end
+ end
+end
diff --git a/lib/syntax_suggest/scan_history.rb b/lib/syntax_suggest/scan_history.rb
new file mode 100644
index 0000000000..d15597c440
--- /dev/null
+++ b/lib/syntax_suggest/scan_history.rb
@@ -0,0 +1,134 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Scans up/down from the given block
+ #
+ # You can try out a change, stash it, or commit it to save for later
+ #
+ # Example:
+ #
+ # scanner = ScanHistory.new(code_lines: code_lines, block: block)
+ # scanner.scan(
+ # up: ->(_, _, _) { true },
+ # down: ->(_, _, _) { true }
+ # )
+ # scanner.changed? # => true
+ # expect(scanner.lines).to eq(code_lines)
+ #
+ # scanner.stash_changes
+ #
+ # expect(scanner.lines).to_not eq(code_lines)
+ class ScanHistory
+ attr_reader :before_index, :after_index
+
+ def initialize(code_lines:, block:)
+ @code_lines = code_lines
+ @history = [block]
+ refresh_index
+ end
+
+ def commit_if_changed
+ if changed?
+ @history << CodeBlock.new(lines: @code_lines[before_index..after_index])
+ end
+
+ self
+ end
+
+ # Discards any changes that have not been committed
+ def stash_changes
+ refresh_index
+ self
+ end
+
+ # Discard changes that have not been committed and revert the last commit
+ #
+ # Cannot revert the first commit
+ def revert_last_commit
+ if @history.length > 1
+ @history.pop
+ refresh_index
+ end
+
+ self
+ end
+
+ def changed?
+ @before_index != current.lines.first.index ||
+ @after_index != current.lines.last.index
+ end
+
+ # Iterates up and down
+ #
+ # Returns line, kw_count, end_count for each iteration
+ def scan(up:, down:)
+ kw_count = 0
+ end_count = 0
+
+ up_index = before_lines.reverse_each.take_while do |line|
+ kw_count += 1 if line.is_kw?
+ end_count += 1 if line.is_end?
+ up.call(line, kw_count, end_count)
+ end.last&.index
+
+ kw_count = 0
+ end_count = 0
+
+ down_index = after_lines.each.take_while do |line|
+ kw_count += 1 if line.is_kw?
+ end_count += 1 if line.is_end?
+ down.call(line, kw_count, end_count)
+ end.last&.index
+
+ @before_index = if up_index && up_index < @before_index
+ up_index
+ else
+ @before_index
+ end
+
+ @after_index = if down_index && down_index > @after_index
+ down_index
+ else
+ @after_index
+ end
+
+ self
+ end
+
+ def next_up
+ return nil if @before_index <= 0
+
+ @code_lines[@before_index - 1]
+ end
+
+ def next_down
+ return nil if @after_index >= @code_lines.length
+
+ @code_lines[@after_index + 1]
+ end
+
+ def lines
+ @code_lines[@before_index..@after_index]
+ end
+
+ private def before_lines
+ @code_lines[0...@before_index] || []
+ end
+
+ # Returns an array of all the CodeLines that exist after
+ # the currently scanned block
+ private def after_lines
+ @code_lines[@after_index.next..-1] || []
+ end
+
+ private def current
+ @history.last
+ end
+
+ private def refresh_index
+ @before_index = current.lines.first.index
+ @after_index = current.lines.last.index
+ self
+ end
+ end
+end
diff --git a/lib/syntax_suggest/syntax_suggest.gemspec b/lib/syntax_suggest/syntax_suggest.gemspec
new file mode 100644
index 0000000000..0e611c13d0
--- /dev/null
+++ b/lib/syntax_suggest/syntax_suggest.gemspec
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+begin
+ require_relative "lib/syntax_suggest/version"
+rescue LoadError # Fallback to load version file in ruby core repository
+ require_relative "version"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "syntax_suggest"
+ spec.version = SyntaxSuggest::VERSION
+ spec.authors = ["schneems"]
+ spec.email = ["richard.schneeman+foo@gmail.com"]
+
+ spec.summary = "Find syntax errors in your source in a snap"
+ spec.description = 'When you get an "unexpected end" in your syntax this gem helps you find it'
+ spec.homepage = "https://github.com/ruby/syntax_suggest.git"
+ spec.license = "MIT"
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
+
+ spec.metadata["homepage_uri"] = spec.homepage
+ spec.metadata["source_code_uri"] = "https://github.com/ruby/syntax_suggest.git"
+
+ # 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|assets)/}) }
+ end
+ spec.bindir = "exe"
+ spec.executables = ["syntax_suggest"]
+ spec.require_paths = ["lib"]
+end
diff --git a/lib/syntax_suggest/unvisited_lines.rb b/lib/syntax_suggest/unvisited_lines.rb
new file mode 100644
index 0000000000..32808db634
--- /dev/null
+++ b/lib/syntax_suggest/unvisited_lines.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Tracks which lines various code blocks have expanded to
+ # and which are still unexplored
+ class UnvisitedLines
+ def initialize(code_lines:)
+ @unvisited = code_lines.sort_by(&:indent_index)
+ @visited_lines = {}
+ @visited_lines.compare_by_identity
+ end
+
+ def empty?
+ @unvisited.empty?
+ end
+
+ def peek
+ @unvisited.last
+ end
+
+ def pop
+ @unvisited.pop
+ end
+
+ def visit_block(block)
+ block.lines.each do |line|
+ next if @visited_lines[line]
+ @visited_lines[line] = true
+ end
+
+ while @visited_lines[@unvisited.last]
+ @unvisited.pop
+ end
+ end
+ end
+end
diff --git a/lib/syntax_suggest/version.rb b/lib/syntax_suggest/version.rb
new file mode 100644
index 0000000000..ac8c2f62e5
--- /dev/null
+++ b/lib/syntax_suggest/version.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ VERSION = "1.1.0"
+end
diff --git a/lib/tempfile.gemspec b/lib/tempfile.gemspec
index 1fa4e05196..acb26b68db 100644
--- a/lib/tempfile.gemspec
+++ b/lib/tempfile.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "tempfile"
- spec.version = "0.1.2"
+ spec.version = "0.1.3"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
diff --git a/lib/tempfile.rb b/lib/tempfile.rb
index 8a6bcc3e14..c3263ed3c6 100644
--- a/lib/tempfile.rb
+++ b/lib/tempfile.rb
@@ -104,7 +104,7 @@ class Tempfile < DelegateClass(File)
# - Directory is the system temporary directory (system-dependent).
# - Generated filename is unique in that directory.
# - Permissions are <tt>0600</tt>;
- # see {File Permissions}[https://docs.ruby-lang.org/en/master/File.html#label-File+Permissions].
+ # see {File Permissions}[rdoc-ref:File@File+Permissions].
# - Mode is <tt>'w+'</tt> (read/write mode, positioned at the end).
#
# The underlying file is removed when the \Tempfile object dies
@@ -136,12 +136,12 @@ class Tempfile < DelegateClass(File)
# Tempfile.new('foo', '.') # => #<Tempfile:./foo20220505-17839-xfstr8>
#
# Keyword arguments +mode+ and +options+ are passed directly to method
- # {File.open}[https://docs.ruby-lang.org/en/master/File.html#method-c-open]:
+ # {File.open}[rdoc-ref:File.open]:
#
# - The value given with +mode+ must be an integer,
# and may be expressed as the logical OR of constants defined in
- # {File::Constants}[https://docs.ruby-lang.org/en/master/File/Constants.html].
- # - For +options+, see {Open Options}[https://docs.ruby-lang.org/en/master/IO.html#class-IO-label-Open+Options].
+ # {File::Constants}[rdoc-ref:File::Constants].
+ # - For +options+, see {Open Options}[rdoc-ref:IO@Open+Options].
#
# Related: Tempfile.create.
#
@@ -344,11 +344,11 @@ end
#
# With no block given and no arguments, creates and returns file whose:
#
-# - Class is {File}[https://docs.ruby-lang.org/en/master/File.html] (not \Tempfile).
+# - Class is {File}[rdoc-ref:File] (not \Tempfile).
# - Directory is the system temporary directory (system-dependent).
# - Generated filename is unique in that directory.
# - Permissions are <tt>0600</tt>;
-# see {File Permissions}[https://docs.ruby-lang.org/en/master/File.html#label-File+Permissions].
+# see {File Permissions}[rdoc-ref:File@File+Permissions].
# - Mode is <tt>'w+'</tt> (read/write mode, positioned at the end).
#
# With no block, the file is not removed automatically,
@@ -380,12 +380,12 @@ end
# Tempfile.create('foo', '.') # => #<File:./foo20220505-9795-1emu6g8>
#
# Keyword arguments +mode+ and +options+ are passed directly to method
-# {File.open}[https://docs.ruby-lang.org/en/master/File.html#method-c-open]:
+# {File.open}[rdoc-ref:File.open]:
#
# - The value given with +mode+ must be an integer,
# and may be expressed as the logical OR of constants defined in
-# {File::Constants}[https://docs.ruby-lang.org/en/master/File/Constants.html].
-# - For +options+, see {Open Options}[https://docs.ruby-lang.org/en/master/IO.html#class-IO-label-Open+Options].
+# {File::Constants}[rdoc-ref:File::Constants].
+# - For +options+, see {Open Options}[rdoc-ref:IO@Open+Options].
#
# With a block given, creates the file as above, passes it to the block,
# and returns the block's value;
diff --git a/lib/time.gemspec b/lib/time.gemspec
index 72fba34204..bada91a30b 100644
--- a/lib/time.gemspec
+++ b/lib/time.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "time"
- spec.version = "0.2.0"
+ spec.version = "0.2.2"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/time.rb b/lib/time.rb
index bd20a1a8e9..6a13212a49 100644
--- a/lib/time.rb
+++ b/lib/time.rb
@@ -509,8 +509,8 @@ class Time
(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+
(\d{2,})\s+
(\d{2})\s*
- :\s*(\d{2})\s*
- (?::\s*(\d{2}))?\s+
+ :\s*(\d{2})
+ (?:\s*:\s*(\d\d))?\s+
([+-]\d{4}|
UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Z])/ix =~ date
# Since RFC 2822 permit comments, the regexp has no right anchor.
@@ -701,7 +701,7 @@ class Time
#
# If self is a UTC time, Z is used as TZD. [+-]hh:mm is used otherwise.
#
- # +fractional_digits+ specifies a number of digits to use for fractional
+ # +fraction_digits+ specifies a number of digits to use for fractional
# seconds. Its default value is 0.
#
# require 'time'
diff --git a/lib/timeout.rb b/lib/timeout.rb
index 2aad1d7465..7f40bafa4d 100644
--- a/lib/timeout.rb
+++ b/lib/timeout.rb
@@ -23,7 +23,7 @@
# Copyright:: (C) 2000 Information-technology Promotion Agency, Japan
module Timeout
- VERSION = "0.3.0"
+ VERSION = "0.3.1"
# Raised by Timeout.timeout when the block times out.
class Error < RuntimeError
@@ -120,6 +120,7 @@ module Timeout
requests.reject!(&:done?)
end
end
+ ThreadGroup::Default.add(watcher)
watcher.name = "Timeout stdlib thread"
watcher.thread_variable_set(:"\0__detached_thread__", true)
watcher
diff --git a/lib/tmpdir.gemspec b/lib/tmpdir.gemspec
index 7b76403002..e42662ea3b 100644
--- a/lib/tmpdir.gemspec
+++ b/lib/tmpdir.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "tmpdir"
- spec.version = "0.1.2"
+ spec.version = "0.1.3"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
spec.description = %q{Extends the Dir class to manage the OS temporary file path.}
spec.homepage = "https://github.com/ruby/tmpdir"
spec.licenses = ["Ruby", "BSD-2-Clause"]
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
diff --git a/lib/tmpdir.rb b/lib/tmpdir.rb
index 3b67164039..55920a4a74 100644
--- a/lib/tmpdir.rb
+++ b/lib/tmpdir.rb
@@ -19,9 +19,10 @@ class Dir
# Returns the operating system's temporary file path.
def self.tmpdir
- tmp = nil
- ['TMPDIR', 'TMP', 'TEMP', ['system temporary path', @@systmpdir], ['/tmp']*2, ['.']*2].each do |name, dir = ENV[name]|
- next if !dir
+ ['TMPDIR', 'TMP', 'TEMP', ['system temporary path', @@systmpdir], ['/tmp']*2, ['.']*2].find do |name, dir|
+ unless dir
+ next if !(dir = ENV[name]) or dir.empty?
+ end
dir = File.expand_path(dir)
stat = File.stat(dir) rescue next
case
@@ -32,12 +33,9 @@ class Dir
when stat.world_writable? && !stat.sticky?
warn "#{name} is world-writable: #{dir}"
else
- tmp = dir
- break
+ break dir
end
- end
- raise ArgumentError, "could not find a temporary directory" unless tmp
- tmp
+ end or raise ArgumentError, "could not find a temporary directory"
end
# Dir.mktmpdir creates a temporary directory.
@@ -108,6 +106,7 @@ class Dir
end
end
+ # Temporary name generator
module Tmpname # :nodoc:
module_function
@@ -115,16 +114,23 @@ class Dir
Dir.tmpdir
end
+ # Unusable characters as path name
UNUSABLE_CHARS = "^,-.0-9A-Z_a-z~"
- class << (RANDOM = Random.new)
+ # Dedicated random number generator
+ RANDOM = Random.new
+ class << RANDOM # :nodoc:
+ # Maximum random number
MAX = 36**6 # < 0x100000000
+
+ # Returns new random string upto 6 bytes
def next
rand(MAX).to_s(36)
end
end
private_constant :RANDOM
+ # Generates and yields random names to create a temporary name
def create(basename, tmpdir=nil, max_try: nil, **opts)
origdir = tmpdir
tmpdir ||= tmpdir()
diff --git a/lib/tsort.gemspec b/lib/tsort.gemspec
index 4656d0b845..a82393b70b 100644
--- a/lib/tsort.gemspec
+++ b/lib/tsort.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "tsort"
- spec.version = "0.1.0"
+ spec.version = "0.1.1"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/un.gemspec b/lib/un.gemspec
index de8e6ec312..1a466fdebe 100644
--- a/lib/un.gemspec
+++ b/lib/un.gemspec
@@ -2,7 +2,7 @@
Gem::Specification.new do |spec|
spec.name = "un"
- spec.version = "0.2.0"
+ spec.version = "0.2.1"
spec.authors = ["WATANABE Hirofumi"]
spec.email = ["eban@ruby-lang.org"]
diff --git a/lib/un.rb b/lib/un.rb
index 9242357215..796b36fbab 100644
--- a/lib/un.rb
+++ b/lib/un.rb
@@ -256,7 +256,7 @@ def wait_writable
wait = (wait = options[:w]) ? Float(wait) : 0.2
argv.each do |file|
begin
- open(file, "r+b")
+ File.open(file, "r+b") {}
rescue Errno::ENOENT
break
rescue Errno::EACCES => e
@@ -422,7 +422,7 @@ module UN # :nodoc:
messages = {}
store = proc {|msg| messages[cmd] = msg}
end
- open(__FILE__) do |me|
+ File.open(__FILE__) do |me|
while me.gets("##\n")
if help = me.gets("\n\n")
if all or argv.include?(cmd = help[/^#\s*ruby\s.*-e\s+(\w+)/, 1])
diff --git a/lib/unicode_normalize/tables.rb b/lib/unicode_normalize/tables.rb
index 75c4e031e3..7448fad13f 100644
--- a/lib/unicode_normalize/tables.rb
+++ b/lib/unicode_normalize/tables.rb
@@ -150,6 +150,7 @@ module UnicodeNormalize # :nodoc:
"\u{10AE5}\u{10AE6}" \
"\u{10D24}-\u{10D27}" \
"\u{10EAB}\u{10EAC}" \
+ "\u{10EFD}-\u{10EFF}" \
"\u{10F46}-\u{10F50}" \
"\u{10F82}-\u{10F85}" \
"\u{11046}" \
@@ -194,6 +195,7 @@ module UnicodeNormalize # :nodoc:
"\u{11D42}" \
"\u{11D44}\u{11D45}" \
"\u{11D97}" \
+ "\u{11F41}\u{11F42}" \
"\u{16AF0}-\u{16AF4}" \
"\u{16B30}-\u{16B36}" \
"\u{16FF0}\u{16FF1}" \
@@ -209,9 +211,11 @@ module UnicodeNormalize # :nodoc:
"\u{1E01B}-\u{1E021}" \
"\u{1E023}\u{1E024}" \
"\u{1E026}-\u{1E02A}" \
+ "\u{1E08F}" \
"\u{1E130}-\u{1E136}" \
"\u{1E2AE}" \
"\u{1E2EC}-\u{1E2EF}" \
+ "\u{1E4EC}-\u{1E4EF}" \
"\u{1E8D0}-\u{1E8D6}" \
"\u{1E944}-\u{1E94A}" \
"]"
@@ -1457,6 +1461,7 @@ module UnicodeNormalize # :nodoc:
"\u{1D552}-\u{1D6A5}" \
"\u{1D6A8}-\u{1D7CB}" \
"\u{1D7CE}-\u{1D7FF}" \
+ "\u{1E030}-\u{1E06D}" \
"\u{1EE00}-\u{1EE03}" \
"\u{1EE05}-\u{1EE1F}" \
"\u{1EE21}\u{1EE22}" \
@@ -2231,6 +2236,9 @@ module UnicodeNormalize # :nodoc:
"\u{10D27}"=>230,
"\u{10EAB}"=>230,
"\u{10EAC}"=>230,
+ "\u{10EFD}"=>220,
+ "\u{10EFE}"=>220,
+ "\u{10EFF}"=>220,
"\u{10F46}"=>220,
"\u{10F47}"=>220,
"\u{10F48}"=>230,
@@ -2303,6 +2311,8 @@ module UnicodeNormalize # :nodoc:
"\u{11D44}"=>9,
"\u{11D45}"=>9,
"\u{11D97}"=>9,
+ "\u{11F41}"=>9,
+ "\u{11F42}"=>9,
"\u{16AF0}"=>1,
"\u{16AF1}"=>1,
"\u{16AF2}"=>1,
@@ -2389,6 +2399,7 @@ module UnicodeNormalize # :nodoc:
"\u{1E028}"=>230,
"\u{1E029}"=>230,
"\u{1E02A}"=>230,
+ "\u{1E08F}"=>230,
"\u{1E130}"=>230,
"\u{1E131}"=>230,
"\u{1E132}"=>230,
@@ -2401,6 +2412,10 @@ module UnicodeNormalize # :nodoc:
"\u{1E2ED}"=>230,
"\u{1E2EE}"=>230,
"\u{1E2EF}"=>230,
+ "\u{1E4EC}"=>232,
+ "\u{1E4ED}"=>232,
+ "\u{1E4EE}"=>220,
+ "\u{1E4EF}"=>230,
"\u{1E8D0}"=>220,
"\u{1E8D1}"=>220,
"\u{1E8D2}"=>220,
@@ -7931,6 +7946,68 @@ module UnicodeNormalize # :nodoc:
"\u{1D7FD}"=>"7",
"\u{1D7FE}"=>"8",
"\u{1D7FF}"=>"9",
+ "\u{1E030}"=>"\u0430",
+ "\u{1E031}"=>"\u0431",
+ "\u{1E032}"=>"\u0432",
+ "\u{1E033}"=>"\u0433",
+ "\u{1E034}"=>"\u0434",
+ "\u{1E035}"=>"\u0435",
+ "\u{1E036}"=>"\u0436",
+ "\u{1E037}"=>"\u0437",
+ "\u{1E038}"=>"\u0438",
+ "\u{1E039}"=>"\u043A",
+ "\u{1E03A}"=>"\u043B",
+ "\u{1E03B}"=>"\u043C",
+ "\u{1E03C}"=>"\u043E",
+ "\u{1E03D}"=>"\u043F",
+ "\u{1E03E}"=>"\u0440",
+ "\u{1E03F}"=>"\u0441",
+ "\u{1E040}"=>"\u0442",
+ "\u{1E041}"=>"\u0443",
+ "\u{1E042}"=>"\u0444",
+ "\u{1E043}"=>"\u0445",
+ "\u{1E044}"=>"\u0446",
+ "\u{1E045}"=>"\u0447",
+ "\u{1E046}"=>"\u0448",
+ "\u{1E047}"=>"\u044B",
+ "\u{1E048}"=>"\u044D",
+ "\u{1E049}"=>"\u044E",
+ "\u{1E04A}"=>"\uA689",
+ "\u{1E04B}"=>"\u04D9",
+ "\u{1E04C}"=>"\u0456",
+ "\u{1E04D}"=>"\u0458",
+ "\u{1E04E}"=>"\u04E9",
+ "\u{1E04F}"=>"\u04AF",
+ "\u{1E050}"=>"\u04CF",
+ "\u{1E051}"=>"\u0430",
+ "\u{1E052}"=>"\u0431",
+ "\u{1E053}"=>"\u0432",
+ "\u{1E054}"=>"\u0433",
+ "\u{1E055}"=>"\u0434",
+ "\u{1E056}"=>"\u0435",
+ "\u{1E057}"=>"\u0436",
+ "\u{1E058}"=>"\u0437",
+ "\u{1E059}"=>"\u0438",
+ "\u{1E05A}"=>"\u043A",
+ "\u{1E05B}"=>"\u043B",
+ "\u{1E05C}"=>"\u043E",
+ "\u{1E05D}"=>"\u043F",
+ "\u{1E05E}"=>"\u0441",
+ "\u{1E05F}"=>"\u0443",
+ "\u{1E060}"=>"\u0444",
+ "\u{1E061}"=>"\u0445",
+ "\u{1E062}"=>"\u0446",
+ "\u{1E063}"=>"\u0447",
+ "\u{1E064}"=>"\u0448",
+ "\u{1E065}"=>"\u044A",
+ "\u{1E066}"=>"\u044B",
+ "\u{1E067}"=>"\u0491",
+ "\u{1E068}"=>"\u0456",
+ "\u{1E069}"=>"\u0455",
+ "\u{1E06A}"=>"\u045F",
+ "\u{1E06B}"=>"\u04AB",
+ "\u{1E06C}"=>"\uA651",
+ "\u{1E06D}"=>"\u04B1",
"\u{1EE00}"=>"\u0627",
"\u{1EE01}"=>"\u0628",
"\u{1EE02}"=>"\u062C",
diff --git a/lib/uri/common.rb b/lib/uri/common.rb
index ca38bec7ec..0c4064a67a 100644
--- a/lib/uri/common.rb
+++ b/lib/uri/common.rb
@@ -19,6 +19,8 @@ module URI
Parser = RFC2396_Parser
RFC3986_PARSER = RFC3986_Parser.new
Ractor.make_shareable(RFC3986_PARSER) if defined?(Ractor)
+ RFC2396_PARSER = RFC2396_Parser.new
+ Ractor.make_shareable(RFC2396_PARSER) if defined?(Ractor)
# URI::Parser.new
DEFAULT_PARSER = Parser.new
diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb
index 69698c4e2d..f7eed57924 100644
--- a/lib/uri/generic.rb
+++ b/lib/uri/generic.rb
@@ -1133,17 +1133,16 @@ module URI
base.fragment=(nil)
# RFC2396, Section 5.2, 4)
- if !authority
- base.set_path(merge_path(base.path, rel.path)) if base.path && rel.path
- else
- # RFC2396, Section 5.2, 4)
- base.set_path(rel.path) if rel.path
+ if authority
+ base.set_userinfo(rel.userinfo)
+ base.set_host(rel.host)
+ base.set_port(rel.port || base.default_port)
+ base.set_path(rel.path)
+ elsif base.path && rel.path
+ base.set_path(merge_path(base.path, rel.path))
end
# RFC2396, Section 5.2, 7)
- base.set_userinfo(rel.userinfo) if rel.userinfo
- base.set_host(rel.host) if rel.host
- base.set_port(rel.port) if rel.port
base.query = rel.query if rel.query
base.fragment=(rel.fragment) if rel.fragment
diff --git a/lib/uri/rfc2396_parser.rb b/lib/uri/rfc2396_parser.rb
index 76a8f99fd4..00c66cf042 100644
--- a/lib/uri/rfc2396_parser.rb
+++ b/lib/uri/rfc2396_parser.rb
@@ -497,8 +497,8 @@ module URI
ret = {}
# for URI::split
- ret[:ABS_URI] = Regexp.new('\A\s*' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
- ret[:REL_URI] = Regexp.new('\A\s*' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:ABS_URI] = Regexp.new('\A\s*+' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:REL_URI] = Regexp.new('\A\s*+' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
# for URI::extract
ret[:URI_REF] = Regexp.new(pattern[:URI_REF])
diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb
index 3e07de4805..9b1663dbb6 100644
--- a/lib/uri/rfc3986_parser.rb
+++ b/lib/uri/rfc3986_parser.rb
@@ -2,9 +2,8 @@
module URI
class RFC3986_Parser # :nodoc:
# URI defined in RFC3986
- # this regexp is modified not to host is not empty string
- RFC3986_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
- RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+)\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
+ RFC3986_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*+):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+))(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
+ RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
attr_reader :regexp
def initialize
@@ -101,7 +100,7 @@ module URI
QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
OPAQUE: /\A(?:[^\/].*)?\z/,
- PORT: /\A[\x09\x0a\x0c\x0d ]*\d*[\x09\x0a\x0c\x0d ]*\z/,
+ PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/,
}
end
diff --git a/lib/uri/version.rb b/lib/uri/version.rb
index 82188e25ad..c93c97cf6f 100644
--- a/lib/uri/version.rb
+++ b/lib/uri/version.rb
@@ -1,6 +1,6 @@
module URI
# :stopdoc:
- VERSION_CODE = '001100'.freeze
+ VERSION_CODE = '001204'.freeze
VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
# :startdoc:
end
diff --git a/lib/weakref.rb b/lib/weakref.rb
index 78aad1f96e..2bbadf68f9 100644
--- a/lib/weakref.rb
+++ b/lib/weakref.rb
@@ -17,7 +17,7 @@ require "delegate"
#
class WeakRef < Delegator
- VERSION = "0.1.1"
+ VERSION = "0.1.2"
##
# RefError is raised when a referenced object has been recycled by the
diff --git a/lib/yaml/yaml.gemspec b/lib/yaml/yaml.gemspec
index e8f20b8c2e..80554d8a7a 100644
--- a/lib/yaml/yaml.gemspec
+++ b/lib/yaml/yaml.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "yaml"
- spec.version = "0.2.0"
+ spec.version = "0.2.1"
spec.authors = ["Aaron Patterson", "SHIBATA Hiroshi"]
spec.email = ["aaron@tenderlovemaking.com", "hsbt@ruby-lang.org"]
diff --git a/libexec/bundle b/libexec/bundle
index 73a6397e77..90c62627f8 100755
--- a/libexec/bundle
+++ b/libexec/bundle
@@ -10,15 +10,12 @@ end
base_path = File.expand_path("../lib", __dir__)
if File.exist?(base_path)
- require_relative "../lib/bundler"
-else
- require "bundler"
+ $LOAD_PATH.unshift(base_path)
end
-# Workaround for non-activated bundler spec due to missing https://github.com/rubygems/rubygems/commit/4e306d7bcdee924b8d80ca9db6125aa59ee4e5a3
-gem "bundler", Bundler::VERSION if Gem.rubygems_version < Gem::Version.new("2.6.2")
+require "bundler"
-if Gem.rubygems_version < Gem::Version.new("3.2.3") && Gem.ruby_version < Gem::Version.new("2.6.a") && !ENV["BUNDLER_NO_OLD_RUBYGEMS_WARNING"]
+if Gem.rubygems_version < Gem::Version.new("3.2.3") && Gem.ruby_version < Gem::Version.new("2.7.a") && !ENV["BUNDLER_NO_OLD_RUBYGEMS_WARNING"]
Bundler.ui.warn \
"Your RubyGems version (#{Gem::VERSION}) has a bug that prevents " \
"`required_ruby_version` from working for Bundler. Any scripts that use " \
@@ -27,18 +24,10 @@ if Gem.rubygems_version < Gem::Version.new("3.2.3") && Gem.ruby_version < Gem::V
"and silence this warning by running `gem update --system 3.2.3`"
end
-if File.exist?(base_path)
- require_relative "../lib/bundler/friendly_errors"
-else
- require "bundler/friendly_errors"
-end
+require "bundler/friendly_errors"
Bundler.with_friendly_errors do
- if File.exist?(base_path)
- require_relative "../lib/bundler/cli"
- else
- require "bundler/cli"
- end
+ require "bundler/cli"
# Allow any command to use --help flag to show help for that command
help_flags = %w[--help -h]
diff --git a/libexec/erb b/libexec/erb
index ded76991dc..4381671f25 100755
--- a/libexec/erb
+++ b/libexec/erb
@@ -74,11 +74,6 @@ class ERB
$DEBUG = true
when '-r' # require
require ARGV.req_arg
- when '-S' # security level
- warn 'warning: -S option of erb command is deprecated. Please do not use this.'
- arg = ARGV.req_arg
- raise "invalid safe_level #{arg.dump}" unless arg =~ /\A[0-1]\z/
- safe_level = arg.to_i
when '-T' # trim mode
arg = ARGV.req_arg
if arg == '-'
@@ -127,12 +122,7 @@ EOU
filename = $FILENAME
exit 2 unless src
trim = trim_mode_opt(trim_mode, disable_percent)
- if safe_level.nil?
- erb = factory.new(src, trim_mode: trim)
- else
- # [deprecated] This will be removed at Ruby 2.7.
- erb = factory.new(src, safe_level, trim_mode: trim)
- end
+ erb = factory.new(src, trim_mode: trim)
erb.filename = filename
if output
if number
diff --git a/libexec/syntax_suggest b/libexec/syntax_suggest
new file mode 100755
index 0000000000..e4a0b0b658
--- /dev/null
+++ b/libexec/syntax_suggest
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+
+require_relative "../lib/syntax_suggest/api"
+
+SyntaxSuggest::Cli.new(
+ argv: ARGV
+).call
diff --git a/load.c b/load.c
index a66940e0b7..ecb21faa72 100644
--- a/load.c
+++ b/load.c
@@ -54,39 +54,39 @@ rb_construct_expanded_load_path(rb_vm_t *vm, enum expand_type type, int *has_rel
VALUE ary;
long i;
- ary = rb_ary_tmp_new(RARRAY_LEN(load_path));
+ ary = rb_ary_hidden_new(RARRAY_LEN(load_path));
for (i = 0; i < RARRAY_LEN(load_path); ++i) {
- VALUE path, as_str, expanded_path;
- int is_string, non_cache;
- char *as_cstr;
- as_str = path = RARRAY_AREF(load_path, i);
- is_string = RB_TYPE_P(path, T_STRING) ? 1 : 0;
- non_cache = !is_string ? 1 : 0;
+ VALUE path, as_str, expanded_path;
+ int is_string, non_cache;
+ char *as_cstr;
+ as_str = path = RARRAY_AREF(load_path, i);
+ is_string = RB_TYPE_P(path, T_STRING) ? 1 : 0;
+ non_cache = !is_string ? 1 : 0;
as_str = rb_get_path_check_to_string(path);
- as_cstr = RSTRING_PTR(as_str);
-
- if (!non_cache) {
- if ((type == EXPAND_RELATIVE &&
- rb_is_absolute_path(as_cstr)) ||
- (type == EXPAND_HOME &&
- (!as_cstr[0] || as_cstr[0] != '~')) ||
- (type == EXPAND_NON_CACHE)) {
- /* Use cached expanded path. */
- rb_ary_push(ary, RARRAY_AREF(expanded_load_path, i));
- continue;
- }
- }
- if (!*has_relative && !rb_is_absolute_path(as_cstr))
- *has_relative = 1;
- if (!*has_non_cache && non_cache)
- *has_non_cache = 1;
- /* Freeze only string object. We expand other objects every time. */
- if (is_string)
- rb_str_freeze(path);
+ as_cstr = RSTRING_PTR(as_str);
+
+ if (!non_cache) {
+ if ((type == EXPAND_RELATIVE &&
+ rb_is_absolute_path(as_cstr)) ||
+ (type == EXPAND_HOME &&
+ (!as_cstr[0] || as_cstr[0] != '~')) ||
+ (type == EXPAND_NON_CACHE)) {
+ /* Use cached expanded path. */
+ rb_ary_push(ary, RARRAY_AREF(expanded_load_path, i));
+ continue;
+ }
+ }
+ if (!*has_relative && !rb_is_absolute_path(as_cstr))
+ *has_relative = 1;
+ if (!*has_non_cache && non_cache)
+ *has_non_cache = 1;
+ /* Freeze only string object. We expand other objects every time. */
+ if (is_string)
+ rb_str_freeze(path);
as_str = rb_get_path_check_convert(as_str);
- expanded_path = rb_check_realpath(Qnil, as_str, NULL);
- if (NIL_P(expanded_path)) expanded_path = as_str;
- rb_ary_push(ary, rb_fstring(expanded_path));
+ expanded_path = rb_check_realpath(Qnil, as_str, NULL);
+ if (NIL_P(expanded_path)) expanded_path = as_str;
+ rb_ary_push(ary, rb_fstring(expanded_path));
}
rb_obj_freeze(ary);
vm->expanded_load_path = ary;
@@ -99,41 +99,41 @@ get_expanded_load_path(rb_vm_t *vm)
const VALUE non_cache = Qtrue;
if (!rb_ary_shared_with_p(vm->load_path_snapshot, vm->load_path)) {
- /* The load path was modified. Rebuild the expanded load path. */
- int has_relative = 0, has_non_cache = 0;
- rb_construct_expanded_load_path(vm, EXPAND_ALL, &has_relative, &has_non_cache);
- if (has_relative) {
- vm->load_path_check_cache = rb_dir_getwd_ospath();
- }
- else if (has_non_cache) {
- /* Non string object. */
- vm->load_path_check_cache = non_cache;
- }
- else {
- vm->load_path_check_cache = 0;
- }
+ /* The load path was modified. Rebuild the expanded load path. */
+ int has_relative = 0, has_non_cache = 0;
+ rb_construct_expanded_load_path(vm, EXPAND_ALL, &has_relative, &has_non_cache);
+ if (has_relative) {
+ vm->load_path_check_cache = rb_dir_getwd_ospath();
+ }
+ else if (has_non_cache) {
+ /* Non string object. */
+ vm->load_path_check_cache = non_cache;
+ }
+ else {
+ vm->load_path_check_cache = 0;
+ }
}
else if (vm->load_path_check_cache == non_cache) {
- int has_relative = 1, has_non_cache = 1;
- /* Expand only non-cacheable objects. */
- rb_construct_expanded_load_path(vm, EXPAND_NON_CACHE,
- &has_relative, &has_non_cache);
+ int has_relative = 1, has_non_cache = 1;
+ /* Expand only non-cacheable objects. */
+ rb_construct_expanded_load_path(vm, EXPAND_NON_CACHE,
+ &has_relative, &has_non_cache);
}
else if (vm->load_path_check_cache) {
- int has_relative = 1, has_non_cache = 1;
- VALUE cwd = rb_dir_getwd_ospath();
- if (!rb_str_equal(vm->load_path_check_cache, cwd)) {
- /* Current working directory or filesystem encoding was changed.
- Expand relative load path and non-cacheable objects again. */
- vm->load_path_check_cache = cwd;
- rb_construct_expanded_load_path(vm, EXPAND_RELATIVE,
- &has_relative, &has_non_cache);
- }
- else {
- /* Expand only tilde (User HOME) and non-cacheable objects. */
- rb_construct_expanded_load_path(vm, EXPAND_HOME,
- &has_relative, &has_non_cache);
- }
+ int has_relative = 1, has_non_cache = 1;
+ VALUE cwd = rb_dir_getwd_ospath();
+ if (!rb_str_equal(vm->load_path_check_cache, cwd)) {
+ /* Current working directory or filesystem encoding was changed.
+ Expand relative load path and non-cacheable objects again. */
+ vm->load_path_check_cache = cwd;
+ rb_construct_expanded_load_path(vm, EXPAND_RELATIVE,
+ &has_relative, &has_non_cache);
+ }
+ else {
+ /* Expand only tilde (User HOME) and non-cacheable objects. */
+ rb_construct_expanded_load_path(vm, EXPAND_HOME,
+ &has_relative, &has_non_cache);
+ }
}
return vm->expanded_load_path;
}
@@ -164,6 +164,12 @@ get_loaded_features_realpaths(rb_vm_t *vm)
}
static VALUE
+get_loaded_features_realpath_map(rb_vm_t *vm)
+{
+ return vm->loaded_features_realpath_map;
+}
+
+static VALUE
get_LOADED_FEATURES(ID _x, VALUE *_y)
{
return get_loaded_features(GET_VM());
@@ -311,10 +317,10 @@ features_index_add(rb_vm_t *vm, VALUE feature, VALUE offset)
feature_end = feature_str + RSTRING_LEN(feature);
for (ext = feature_end; ext > feature_str; ext--)
- if (*ext == '.' || *ext == '/')
- break;
+ if (*ext == '.' || *ext == '/')
+ break;
if (*ext != '.')
- ext = NULL;
+ ext = NULL;
else
rb = IS_RBEXT(ext);
/* Now `ext` points to the only string matching %r{^\.[^./]*$} that is
@@ -322,20 +328,20 @@ features_index_add(rb_vm_t *vm, VALUE feature, VALUE offset)
p = ext ? ext : feature_end;
while (1) {
- p--;
- while (p >= feature_str && *p != '/')
- p--;
- if (p < feature_str)
- break;
- /* Now *p == '/'. We reach this point for every '/' in `feature`. */
- features_index_add_single(vm, p + 1, feature_end - p - 1, offset, false);
- if (ext) {
- features_index_add_single(vm, p + 1, ext - p - 1, offset, rb);
- }
+ p--;
+ while (p >= feature_str && *p != '/')
+ p--;
+ if (p < feature_str)
+ break;
+ /* Now *p == '/'. We reach this point for every '/' in `feature`. */
+ features_index_add_single(vm, p + 1, feature_end - p - 1, offset, false);
+ if (ext) {
+ features_index_add_single(vm, p + 1, ext - p - 1, offset, rb);
+ }
}
features_index_add_single(vm, feature_str, feature_end - feature_str, offset, false);
if (ext) {
- features_index_add_single(vm, feature_str, ext - feature_str, offset, rb);
+ features_index_add_single(vm, feature_str, ext - feature_str, offset, rb);
}
}
@@ -356,31 +362,39 @@ get_loaded_features_index(rb_vm_t *vm)
int i;
if (!rb_ary_shared_with_p(vm->loaded_features_snapshot, vm->loaded_features)) {
- /* The sharing was broken; something (other than us in rb_provide_feature())
- modified loaded_features. Rebuild the index. */
- st_foreach(vm->loaded_features_index, loaded_features_index_clear_i, 0);
+ /* The sharing was broken; something (other than us in rb_provide_feature())
+ modified loaded_features. Rebuild the index. */
+ st_foreach(vm->loaded_features_index, loaded_features_index_clear_i, 0);
VALUE realpaths = vm->loaded_features_realpaths;
+ VALUE realpath_map = vm->loaded_features_realpath_map;
+ VALUE previous_realpath_map = rb_hash_dup(realpath_map);
rb_hash_clear(realpaths);
- features = vm->loaded_features;
- for (i = 0; i < RARRAY_LEN(features); i++) {
- VALUE entry, as_str;
- as_str = entry = rb_ary_entry(features, i);
- StringValue(as_str);
- as_str = rb_fstring(rb_str_freeze(as_str));
- if (as_str != entry)
- rb_ary_store(features, i, as_str);
- features_index_add(vm, as_str, INT2FIX(i));
- }
- reset_loaded_features_snapshot(vm);
+ rb_hash_clear(realpath_map);
+ features = vm->loaded_features;
+ for (i = 0; i < RARRAY_LEN(features); i++) {
+ VALUE entry, as_str;
+ as_str = entry = rb_ary_entry(features, i);
+ StringValue(as_str);
+ as_str = rb_fstring(rb_str_freeze(as_str));
+ if (as_str != entry)
+ rb_ary_store(features, i, as_str);
+ features_index_add(vm, as_str, INT2FIX(i));
+ }
+ reset_loaded_features_snapshot(vm);
features = rb_ary_dup(vm->loaded_features_snapshot);
long j = RARRAY_LEN(features);
for (i = 0; i < j; i++) {
VALUE as_str = rb_ary_entry(features, i);
- VALUE realpath = rb_check_realpath(Qnil, as_str, NULL);
- if (NIL_P(realpath)) realpath = as_str;
- rb_hash_aset(realpaths, rb_fstring(realpath), Qtrue);
+ VALUE realpath = rb_hash_aref(previous_realpath_map, as_str);
+ if (NIL_P(realpath)) {
+ realpath = rb_check_realpath(Qnil, as_str, NULL);
+ if (NIL_P(realpath)) realpath = as_str;
+ realpath = rb_fstring(realpath);
+ }
+ rb_hash_aset(realpaths, realpath, Qtrue);
+ rb_hash_aset(realpath_map, as_str, realpath);
}
}
return vm->loaded_features_index;
@@ -399,7 +413,7 @@ get_loaded_features_index(rb_vm_t *vm)
*/
static VALUE
loaded_feature_path(const char *name, long vlen, const char *feature, long len,
- int type, VALUE load_path)
+ int type, VALUE load_path)
{
long i;
long plen;
@@ -407,36 +421,36 @@ loaded_feature_path(const char *name, long vlen, const char *feature, long len,
if (vlen < len+1) return 0;
if (strchr(feature, '.') && !strncmp(name+(vlen-len), feature, len)) {
- plen = vlen - len;
+ plen = vlen - len;
}
else {
- for (e = name + vlen; name != e && *e != '.' && *e != '/'; --e);
- if (*e != '.' ||
- e-name < len ||
- strncmp(e-len, feature, len))
- return 0;
- plen = e - name - len;
+ for (e = name + vlen; name != e && *e != '.' && *e != '/'; --e);
+ if (*e != '.' ||
+ e-name < len ||
+ strncmp(e-len, feature, len))
+ return 0;
+ plen = e - name - len;
}
if (plen > 0 && name[plen-1] != '/') {
- return 0;
+ return 0;
}
if (type == 's' ? !IS_DLEXT(&name[plen+len]) :
- type == 'r' ? !IS_RBEXT(&name[plen+len]) :
- 0) {
- return 0;
+ type == 'r' ? !IS_RBEXT(&name[plen+len]) :
+ 0) {
+ return 0;
}
/* Now name == "#{prefix}/#{feature}#{ext}" where ext is acceptable
(possibly empty) and prefix is some string of length plen. */
if (plen > 0) --plen; /* exclude '.' */
for (i = 0; i < RARRAY_LEN(load_path); ++i) {
- VALUE p = RARRAY_AREF(load_path, i);
- const char *s = StringValuePtr(p);
- long n = RSTRING_LEN(p);
+ VALUE p = RARRAY_AREF(load_path, i);
+ const char *s = StringValuePtr(p);
+ long n = RSTRING_LEN(p);
- if (n != plen) continue;
- if (n && strncmp(name, s, n)) continue;
- return p;
+ if (n != plen) continue;
+ if (n && strncmp(name, s, n)) continue;
+ return p;
}
return 0;
}
@@ -455,7 +469,7 @@ loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f)
const char *s = (const char *)v;
struct loaded_feature_searching *fp = (struct loaded_feature_searching *)f;
VALUE p = loaded_feature_path(s, strlen(s), fp->name, fp->len,
- fp->type, fp->load_path);
+ fp->type, fp->load_path);
if (!p) return ST_CONTINUE;
fp->result = s;
return ST_STOP;
@@ -474,14 +488,14 @@ rb_feature_p(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expa
if (fn) *fn = 0;
if (ext) {
- elen = strlen(ext);
- len = strlen(feature) - elen;
- type = rb ? 'r' : 's';
+ elen = strlen(ext);
+ len = strlen(feature) - elen;
+ type = rb ? 'r' : 's';
}
else {
- len = strlen(feature);
- elen = 0;
- type = 0;
+ len = strlen(feature);
+ elen = 0;
+ type = 0;
}
features = get_loaded_features(vm);
features_index = get_loaded_features_index(vm);
@@ -515,89 +529,89 @@ rb_feature_p(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expa
*/
if (st_lookup(features_index, key, &data) && !NIL_P(this_feature_index = (VALUE)data)) {
for (size_t i = 0; ; i++) {
- long index;
+ long index;
if (FIXNUM_P(this_feature_index)) {
- if (i > 0) break;
+ if (i > 0) break;
index = FIX2LONG(this_feature_index);
- }
+ }
else {
feature_indexes_t feature_indexes = (feature_indexes_t)this_feature_index;
if (i >= rb_darray_size(feature_indexes)) break;
index = rb_darray_get(feature_indexes, i);
}
- v = RARRAY_AREF(features, index);
- f = StringValuePtr(v);
- if ((n = RSTRING_LEN(v)) < len) continue;
- if (strncmp(f, feature, len) != 0) {
- if (expanded) continue;
- if (!load_path) load_path = get_expanded_load_path(vm);
- if (!(p = loaded_feature_path(f, n, feature, len, type, load_path)))
- continue;
- expanded = 1;
- f += RSTRING_LEN(p) + 1;
- }
- if (!*(e = f + len)) {
- if (ext) continue;
- return 'u';
- }
- if (*e != '.') continue;
- if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) {
- return 's';
- }
- if ((rb || !ext) && (IS_RBEXT(e))) {
- return 'r';
- }
- }
+ v = RARRAY_AREF(features, index);
+ f = StringValuePtr(v);
+ if ((n = RSTRING_LEN(v)) < len) continue;
+ if (strncmp(f, feature, len) != 0) {
+ if (expanded) continue;
+ if (!load_path) load_path = get_expanded_load_path(vm);
+ if (!(p = loaded_feature_path(f, n, feature, len, type, load_path)))
+ continue;
+ expanded = 1;
+ f += RSTRING_LEN(p) + 1;
+ }
+ if (!*(e = f + len)) {
+ if (ext) continue;
+ return 'u';
+ }
+ if (*e != '.') continue;
+ if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) {
+ return 's';
+ }
+ if ((rb || !ext) && (IS_RBEXT(e))) {
+ return 'r';
+ }
+ }
}
loading_tbl = get_loading_table(vm);
f = 0;
- if (!expanded) {
- struct loaded_feature_searching fs;
- fs.name = feature;
- fs.len = len;
- fs.type = type;
- fs.load_path = load_path ? load_path : get_expanded_load_path(vm);
- fs.result = 0;
- st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs);
- if ((f = fs.result) != 0) {
- if (fn) *fn = f;
- goto loading;
- }
+ if (!expanded && !rb_is_absolute_path(feature)) {
+ struct loaded_feature_searching fs;
+ fs.name = feature;
+ fs.len = len;
+ fs.type = type;
+ fs.load_path = load_path ? load_path : get_expanded_load_path(vm);
+ fs.result = 0;
+ st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs);
+ if ((f = fs.result) != 0) {
+ if (fn) *fn = f;
+ goto loading;
+ }
}
if (st_get_key(loading_tbl, (st_data_t)feature, &data)) {
- if (fn) *fn = (const char*)data;
+ if (fn) *fn = (const char*)data;
goto loading;
}
else {
- VALUE bufstr;
- char *buf;
- static const char so_ext[][4] = {
- ".so", ".o",
- };
-
- if (ext && *ext) return 0;
- bufstr = rb_str_tmp_new(len + DLEXT_MAXLEN);
- buf = RSTRING_PTR(bufstr);
- MEMCPY(buf, feature, char, len);
- for (i = 0; (e = loadable_ext[i]) != 0; i++) {
- strlcpy(buf + len, e, DLEXT_MAXLEN + 1);
- if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
- rb_str_resize(bufstr, 0);
- if (fn) *fn = (const char*)data;
- return i ? 's' : 'r';
- }
- }
- for (i = 0; i < numberof(so_ext); i++) {
- strlcpy(buf + len, so_ext[i], DLEXT_MAXLEN + 1);
- if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
- rb_str_resize(bufstr, 0);
- if (fn) *fn = (const char*)data;
- return 's';
- }
- }
- rb_str_resize(bufstr, 0);
+ VALUE bufstr;
+ char *buf;
+ static const char so_ext[][4] = {
+ ".so", ".o",
+ };
+
+ if (ext && *ext) return 0;
+ bufstr = rb_str_tmp_new(len + DLEXT_MAXLEN);
+ buf = RSTRING_PTR(bufstr);
+ MEMCPY(buf, feature, char, len);
+ for (i = 0; (e = loadable_ext[i]) != 0; i++) {
+ strlcpy(buf + len, e, DLEXT_MAXLEN + 1);
+ if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
+ rb_str_resize(bufstr, 0);
+ if (fn) *fn = (const char*)data;
+ return i ? 's' : 'r';
+ }
+ }
+ for (i = 0; i < numberof(so_ext); i++) {
+ strlcpy(buf + len, so_ext[i], DLEXT_MAXLEN + 1);
+ if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
+ rb_str_resize(bufstr, 0);
+ if (fn) *fn = (const char*)data;
+ return 's';
+ }
+ }
+ rb_str_resize(bufstr, 0);
}
return 0;
@@ -619,22 +633,22 @@ feature_provided(rb_vm_t *vm, const char *feature, const char **loading)
VALUE fullpath = 0;
if (*feature == '.' &&
- (feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) {
- fullpath = rb_file_expand_path_fast(rb_get_path(rb_str_new2(feature)), Qnil);
- feature = RSTRING_PTR(fullpath);
+ (feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) {
+ fullpath = rb_file_expand_path_fast(rb_get_path(rb_str_new2(feature)), Qnil);
+ feature = RSTRING_PTR(fullpath);
}
if (ext && !strchr(ext, '/')) {
- if (IS_RBEXT(ext)) {
- if (rb_feature_p(vm, feature, ext, TRUE, FALSE, loading)) return TRUE;
- return FALSE;
- }
- else if (IS_SOEXT(ext) || IS_DLEXT(ext)) {
- if (rb_feature_p(vm, feature, ext, FALSE, FALSE, loading)) return TRUE;
- return FALSE;
- }
+ if (IS_RBEXT(ext)) {
+ if (rb_feature_p(vm, feature, ext, TRUE, FALSE, loading)) return TRUE;
+ return FALSE;
+ }
+ else if (IS_SOEXT(ext) || IS_DLEXT(ext)) {
+ if (rb_feature_p(vm, feature, ext, FALSE, FALSE, loading)) return TRUE;
+ return FALSE;
+ }
}
if (rb_feature_p(vm, feature, 0, TRUE, FALSE, loading))
- return TRUE;
+ return TRUE;
RB_GC_GUARD(fullpath);
return FALSE;
}
@@ -652,8 +666,8 @@ rb_provide_feature(rb_vm_t *vm, VALUE feature)
features = get_loaded_features(vm);
if (OBJ_FROZEN(features)) {
- rb_raise(rb_eRuntimeError,
- "$LOADED_FEATURES is frozen; cannot append feature");
+ rb_raise(rb_eRuntimeError,
+ "$LOADED_FEATURES is frozen; cannot append feature");
}
rb_str_freeze(feature);
@@ -681,6 +695,8 @@ load_iseq_eval(rb_execution_context_t *ec, VALUE fname)
const rb_iseq_t *iseq = rb_iseq_load_iseq(fname);
if (!iseq) {
+ rb_execution_context_t *ec = GET_EC();
+ VALUE v = rb_vm_push_frame_fname(ec, fname);
rb_ast_t *ast;
VALUE parser = rb_parser_new();
rb_parser_set_context(parser, NULL, FALSE);
@@ -688,6 +704,8 @@ load_iseq_eval(rb_execution_context_t *ec, VALUE fname)
iseq = rb_iseq_new_top(&ast->body, rb_fstring_lit("<top (required)>"),
fname, rb_realpath_internal(Qnil, fname, 1), NULL);
rb_ast_dispose(ast);
+ rb_vm_pop_frame(ec);
+ RB_GC_GUARD(v);
}
rb_exec_event_hook_script_compiled(ec, iseq, Qnil);
rb_iseq_eval(iseq);
@@ -822,9 +840,9 @@ rb_f_load(int argc, VALUE *argv, VALUE _)
path = rb_find_file(fname);
if (!path) {
- if (!rb_file_load_ok(RSTRING_PTR(fname)))
- load_failed(orig_fname);
- path = fname;
+ if (!rb_file_load_ok(RSTRING_PTR(fname)))
+ load_failed(orig_fname);
+ path = fname;
}
rb_load_internal(path, wrap);
@@ -840,29 +858,22 @@ load_lock(rb_vm_t *vm, const char *ftptr, bool warn)
st_table *loading_tbl = get_loading_table(vm);
if (!st_lookup(loading_tbl, (st_data_t)ftptr, &data)) {
- /* partial state */
- ftptr = ruby_strdup(ftptr);
- data = (st_data_t)rb_thread_shield_new();
- st_insert(loading_tbl, (st_data_t)ftptr, data);
- return (char *)ftptr;
- }
- else if (imemo_type_p(data, imemo_memo)) {
- struct MEMO *memo = MEMO_CAST(data);
- void (*init)(void) = memo->u3.func;
- data = (st_data_t)rb_thread_shield_new();
- st_insert(loading_tbl, (st_data_t)ftptr, data);
- (*init)();
- return (char *)"";
+ /* partial state */
+ ftptr = ruby_strdup(ftptr);
+ data = (st_data_t)rb_thread_shield_new();
+ st_insert(loading_tbl, (st_data_t)ftptr, data);
+ return (char *)ftptr;
}
- if (warn) {
- VALUE warning = rb_warning_string("loading in progress, circular require considered harmful - %s", ftptr);
- rb_backtrace_each(rb_str_append, warning);
- rb_warning("%"PRIsVALUE, warning);
+
+ if (warn && rb_thread_shield_owned((VALUE)data)) {
+ VALUE warning = rb_warning_string("loading in progress, circular require considered harmful - %s", ftptr);
+ rb_backtrace_each(rb_str_append, warning);
+ rb_warning("%"PRIsVALUE, warning);
}
switch (rb_thread_shield_wait((VALUE)data)) {
case Qfalse:
case Qnil:
- return 0;
+ return 0;
}
return (char *)ftptr;
}
@@ -873,13 +884,13 @@ release_thread_shield(st_data_t *key, st_data_t *value, st_data_t done, int exis
VALUE thread_shield = (VALUE)*value;
if (!existing) return ST_STOP;
if (done) {
- rb_thread_shield_destroy(thread_shield);
- /* Delete the entry even if there are waiting threads, because they
- * won't load the file and won't delete the entry. */
+ rb_thread_shield_destroy(thread_shield);
+ /* Delete the entry even if there are waiting threads, because they
+ * won't load the file and won't delete the entry. */
}
else if (rb_thread_shield_release(thread_shield)) {
- /* still in-use */
- return ST_CONTINUE;
+ /* still in-use */
+ return ST_CONTINUE;
}
xfree((char *)*key);
return ST_DELETE;
@@ -889,13 +900,14 @@ static void
load_unlock(rb_vm_t *vm, const char *ftptr, int done)
{
if (ftptr) {
- st_data_t key = (st_data_t)ftptr;
- st_table *loading_tbl = get_loading_table(vm);
+ st_data_t key = (st_data_t)ftptr;
+ st_table *loading_tbl = get_loading_table(vm);
- st_update(loading_tbl, key, release_thread_shield, done);
+ st_update(loading_tbl, key, release_thread_shield, done);
}
}
+static VALUE rb_require_string_internal(VALUE fname);
/*
* call-seq:
@@ -955,10 +967,10 @@ rb_f_require_relative(VALUE obj, VALUE fname)
{
VALUE base = rb_current_realfilepath();
if (NIL_P(base)) {
- rb_loaderror("cannot infer basepath");
+ rb_loaderror("cannot infer basepath");
}
base = rb_file_dirname(base);
- return rb_require_string(rb_file_absolute_path(fname, base));
+ return rb_require_string_internal(rb_file_absolute_path(fname, base));
}
typedef int (*feature_func)(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn);
@@ -974,74 +986,93 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_
*path = 0;
ext = strrchr(ftptr = RSTRING_PTR(fname), '.');
if (ext && !strchr(ext, '/')) {
- if (IS_RBEXT(ext)) {
- if (rb_feature_p(vm, ftptr, ext, TRUE, FALSE, &loading)) {
- if (loading) *path = rb_filesystem_str_new_cstr(loading);
- return 'r';
- }
+ if (IS_RBEXT(ext)) {
+ if (rb_feature_p(vm, ftptr, ext, TRUE, FALSE, &loading)) {
+ if (loading) *path = rb_filesystem_str_new_cstr(loading);
+ return 'r';
+ }
if ((tmp = rb_find_file(fname)) != 0) {
- ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
- if (!rb_feature_p(vm, ftptr, ext, TRUE, TRUE, &loading) || loading)
- *path = tmp;
- return 'r';
- }
- return 0;
- }
- else if (IS_SOEXT(ext)) {
- if (rb_feature_p(vm, ftptr, ext, FALSE, FALSE, &loading)) {
- if (loading) *path = rb_filesystem_str_new_cstr(loading);
- return 's';
- }
- tmp = rb_str_subseq(fname, 0, ext - RSTRING_PTR(fname));
- rb_str_cat2(tmp, DLEXT);
- OBJ_FREEZE(tmp);
+ ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
+ if (!rb_feature_p(vm, ftptr, ext, TRUE, TRUE, &loading) || loading)
+ *path = tmp;
+ return 'r';
+ }
+ return 0;
+ }
+ else if (IS_SOEXT(ext)) {
+ if (rb_feature_p(vm, ftptr, ext, FALSE, FALSE, &loading)) {
+ if (loading) *path = rb_filesystem_str_new_cstr(loading);
+ return 's';
+ }
+ tmp = rb_str_subseq(fname, 0, ext - RSTRING_PTR(fname));
+ rb_str_cat2(tmp, DLEXT);
+ OBJ_FREEZE(tmp);
if ((tmp = rb_find_file(tmp)) != 0) {
- ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
- if (!rb_feature_p(vm, ftptr, ext, FALSE, TRUE, &loading) || loading)
- *path = tmp;
- return 's';
- }
- }
- else if (IS_DLEXT(ext)) {
- if (rb_feature_p(vm, ftptr, ext, FALSE, FALSE, &loading)) {
- if (loading) *path = rb_filesystem_str_new_cstr(loading);
- return 's';
- }
+ ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
+ if (!rb_feature_p(vm, ftptr, ext, FALSE, TRUE, &loading) || loading)
+ *path = tmp;
+ return 's';
+ }
+ }
+ else if (IS_DLEXT(ext)) {
+ if (rb_feature_p(vm, ftptr, ext, FALSE, FALSE, &loading)) {
+ if (loading) *path = rb_filesystem_str_new_cstr(loading);
+ return 's';
+ }
if ((tmp = rb_find_file(fname)) != 0) {
- ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
- if (!rb_feature_p(vm, ftptr, ext, FALSE, TRUE, &loading) || loading)
- *path = tmp;
- return 's';
- }
- }
+ ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
+ if (!rb_feature_p(vm, ftptr, ext, FALSE, TRUE, &loading) || loading)
+ *path = tmp;
+ return 's';
+ }
+ }
}
else if ((ft = rb_feature_p(vm, ftptr, 0, FALSE, FALSE, &loading)) == 'r') {
- if (loading) *path = rb_filesystem_str_new_cstr(loading);
- return 'r';
+ if (loading) *path = rb_filesystem_str_new_cstr(loading);
+ return 'r';
}
tmp = fname;
type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext);
+
+ // Check if it's a statically linked extension when
+ // not already a feature and not found as a dynamic library.
+ if (!ft && type != 1 && vm->static_ext_inits) {
+ VALUE lookup_name = tmp;
+ // Append ".so" if not already present so for example "etc" can find "etc.so".
+ // We always register statically linked extensions with a ".so" extension.
+ // See encinit.c and extinit.c (generated at build-time).
+ if (!ext) {
+ lookup_name = rb_str_dup(lookup_name);
+ rb_str_cat_cstr(lookup_name, ".so");
+ }
+ ftptr = RSTRING_PTR(lookup_name);
+ if (st_lookup(vm->static_ext_inits, (st_data_t)ftptr, NULL)) {
+ *path = rb_filesystem_str_new_cstr(ftptr);
+ return 's';
+ }
+ }
+
switch (type) {
case 0:
- if (ft)
- goto statically_linked;
- ftptr = RSTRING_PTR(tmp);
- return rb_feature_p(vm, ftptr, 0, FALSE, TRUE, 0);
+ if (ft)
+ goto feature_present;
+ ftptr = RSTRING_PTR(tmp);
+ return rb_feature_p(vm, ftptr, 0, FALSE, TRUE, 0);
default:
- if (ft) {
- goto statically_linked;
- }
+ if (ft) {
+ goto feature_present;
+ }
/* fall through */
case 1:
- ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
- if (rb_feature_p(vm, ftptr, ext, !--type, TRUE, &loading) && !loading)
- break;
- *path = tmp;
+ ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
+ if (rb_feature_p(vm, ftptr, ext, !--type, TRUE, &loading) && !loading)
+ break;
+ *path = tmp;
}
return type ? 's' : 'r';
- statically_linked:
+ feature_present:
if (loading) *path = rb_filesystem_str_new_cstr(loading);
return ft;
}
@@ -1059,6 +1090,19 @@ load_ext(VALUE path)
return (VALUE)dln_load(RSTRING_PTR(path));
}
+static bool
+run_static_ext_init(rb_vm_t *vm, const char *feature)
+{
+ st_data_t key = (st_data_t)feature;
+ st_data_t init_func;
+
+ if (vm->static_ext_inits && st_delete(vm->static_ext_inits, &key, &init_func)) {
+ ((void (*)(void))init_func)();
+ return true;
+ }
+ return false;
+}
+
static int
no_feature_p(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn)
{
@@ -1133,10 +1177,10 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
volatile VALUE saved_path;
volatile VALUE realpath = 0;
VALUE realpaths = get_loaded_features_realpaths(th->vm);
+ VALUE realpath_map = get_loaded_features_realpath_map(th->vm);
volatile bool reset_ext_config = false;
struct rb_ext_config prev_ext_config;
- fname = rb_get_path(fname);
path = rb_str_encode_ospath(fname);
RUBY_DTRACE_HOOK(REQUIRE_ENTRY, RSTRING_PTR(fname));
saved_path = path;
@@ -1145,42 +1189,45 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
ec->errinfo = Qnil; /* ensure */
th->top_wrapper = 0;
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- long handle;
- int found;
+ long handle;
+ int found;
- RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, RSTRING_PTR(fname));
+ RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, RSTRING_PTR(fname));
found = search_required(th->vm, path, &saved_path, rb_feature_p);
- RUBY_DTRACE_HOOK(FIND_REQUIRE_RETURN, RSTRING_PTR(fname));
+ RUBY_DTRACE_HOOK(FIND_REQUIRE_RETURN, RSTRING_PTR(fname));
path = saved_path;
- if (found) {
+ if (found) {
if (!path || !(ftptr = load_lock(th->vm, RSTRING_PTR(path), warn))) {
- result = 0;
- }
- else if (!*ftptr) {
- result = TAG_RETURN;
- }
+ result = 0;
+ }
+ else if (!*ftptr) {
+ result = TAG_RETURN;
+ }
+ else if (found == 's' && run_static_ext_init(th->vm, RSTRING_PTR(path))) {
+ result = TAG_RETURN;
+ }
else if (RTEST(rb_hash_aref(realpaths,
realpath = rb_realpath_internal(Qnil, path, 1)))) {
result = 0;
}
- else {
- switch (found) {
- case 'r':
+ else {
+ switch (found) {
+ case 'r':
load_iseq_eval(ec, path);
- break;
+ break;
- case 's':
+ case 's':
reset_ext_config = true;
ext_config_push(th, &prev_ext_config);
- handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext,
- path, VM_BLOCK_HANDLER_NONE, path);
- rb_ary_push(ruby_dln_librefs, LONG2NUM(handle));
- break;
- }
+ handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext,
+ path, VM_BLOCK_HANDLER_NONE, path);
+ rb_ary_push(ruby_dln_librefs, LONG2NUM(handle));
+ break;
+ }
result = TAG_RETURN;
- }
- }
+ }
+ }
}
EC_POP_TAG();
@@ -1206,9 +1253,9 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
else if (state == TAG_RETURN) {
return TAG_RAISE;
}
- RB_GC_GUARD(fname);
- /* never TAG_RETURN */
- return state;
+ RB_GC_GUARD(fname);
+ /* never TAG_RETURN */
+ return state;
}
if (!NIL_P(ec->errinfo)) {
if (!exception) return TAG_RAISE;
@@ -1219,7 +1266,9 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
rb_provide_feature(th2->vm, path);
VALUE real = realpath;
if (real) {
- rb_hash_aset(realpaths, rb_fstring(real), Qtrue);
+ real = rb_fstring(real);
+ rb_hash_aset(realpaths, real, Qtrue);
+ rb_hash_aset(realpath_map, path, real);
}
}
ec->errinfo = saved.errinfo;
@@ -1257,6 +1306,12 @@ ruby_require_internal(const char *fname, unsigned int len)
VALUE
rb_require_string(VALUE fname)
{
+ return rb_require_string_internal(FilePathValue(fname));
+}
+
+static VALUE
+rb_require_string_internal(VALUE fname)
+{
rb_execution_context_t *ec = GET_EC();
int result = require_internal(ec, fname, 1, RTEST(ruby_verbose));
@@ -1264,7 +1319,7 @@ rb_require_string(VALUE fname)
EC_JUMP_TAG(ec, result);
}
if (result < 0) {
- load_failed(fname);
+ load_failed(fname);
}
return RBOOL(result);
@@ -1273,7 +1328,9 @@ rb_require_string(VALUE fname)
VALUE
rb_require(const char *fname)
{
- return rb_require_string(rb_str_new_cstr(fname));
+ struct RString fake;
+ VALUE str = rb_setup_fake_str(&fake, fname, strlen(fname), 0);
+ return rb_require_string_internal(str);
}
static int
@@ -1281,25 +1338,33 @@ register_init_ext(st_data_t *key, st_data_t *value, st_data_t init, int existing
{
const char *name = (char *)*key;
if (existing) {
- /* already registered */
- rb_warn("%s is already registered", name);
+ /* already registered */
+ rb_warn("%s is already registered", name);
}
else {
- *value = (st_data_t)MEMO_NEW(0, 0, init);
- *key = (st_data_t)ruby_strdup(name);
+ *value = (st_data_t)init;
}
return ST_CONTINUE;
}
-RUBY_FUNC_EXPORTED void
+// Private API for statically linked extensions.
+// Used with the ext/Setup file, the --with-setup and
+// --with-static-linked-ext configuration option, etc.
+void
ruby_init_ext(const char *name, void (*init)(void))
{
+ st_table *inits_table;
rb_vm_t *vm = GET_VM();
- st_table *loading_tbl = get_loading_table(vm);
if (feature_provided(vm, name, 0))
- return;
- st_update(loading_tbl, (st_data_t)name, register_init_ext, (st_data_t)init);
+ return;
+
+ inits_table = vm->static_ext_inits;
+ if (!inits_table) {
+ inits_table = st_init_strtable();
+ vm->static_ext_inits = inits_table;
+ }
+ st_update(inits_table, (st_data_t)name, register_init_ext, (st_data_t)init);
}
/*
@@ -1364,7 +1429,7 @@ rb_mod_autoload_p(int argc, VALUE *argv, VALUE mod)
ID id = rb_check_id(&sym);
if (!id) {
- return Qnil;
+ return Qnil;
}
return rb_autoload_at_p(mod, id, recur);
}
@@ -1389,7 +1454,7 @@ rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
{
VALUE klass = rb_class_real(rb_vm_cbase());
if (!klass) {
- rb_raise(rb_eTypeError, "Can not set autoload on singleton class");
+ rb_raise(rb_eTypeError, "Can not set autoload on singleton class");
}
return rb_mod_autoload(klass, sym, file);
}
@@ -1411,7 +1476,7 @@ rb_f_autoload_p(int argc, VALUE *argv, VALUE obj)
/* use rb_vm_cbase() as same as rb_f_autoload. */
VALUE klass = rb_vm_cbase();
if (NIL_P(klass)) {
- return Qnil;
+ return Qnil;
}
return rb_mod_autoload_p(argc, argv, klass);
}
@@ -1427,18 +1492,20 @@ Init_load(void)
rb_alias_variable(rb_intern_const("$-I"), id_load_path);
rb_alias_variable(rb_intern_const("$LOAD_PATH"), id_load_path);
vm->load_path = rb_ary_new();
- vm->expanded_load_path = rb_ary_tmp_new(0);
- vm->load_path_snapshot = rb_ary_tmp_new(0);
+ vm->expanded_load_path = rb_ary_hidden_new(0);
+ vm->load_path_snapshot = rb_ary_hidden_new(0);
vm->load_path_check_cache = 0;
rb_define_singleton_method(vm->load_path, "resolve_feature_path", rb_resolve_feature_path, 1);
rb_define_virtual_variable("$\"", get_LOADED_FEATURES, 0);
rb_define_virtual_variable("$LOADED_FEATURES", get_LOADED_FEATURES, 0);
vm->loaded_features = rb_ary_new();
- vm->loaded_features_snapshot = rb_ary_tmp_new(0);
+ vm->loaded_features_snapshot = rb_ary_hidden_new(0);
vm->loaded_features_index = st_init_numtable();
vm->loaded_features_realpaths = rb_hash_new();
rb_obj_hide(vm->loaded_features_realpaths);
+ vm->loaded_features_realpath_map = rb_hash_new();
+ rb_obj_hide(vm->loaded_features_realpath_map);
rb_define_global_function("load", rb_f_load, -1);
rb_define_global_function("require", rb_f_require, 1);
@@ -1448,6 +1515,6 @@ Init_load(void)
rb_define_global_function("autoload", rb_f_autoload, 2);
rb_define_global_function("autoload?", rb_f_autoload_p, -1);
- ruby_dln_librefs = rb_ary_tmp_new(0);
+ ruby_dln_librefs = rb_ary_hidden_new(0);
rb_gc_register_mark_object(ruby_dln_librefs);
}
diff --git a/localeinit.c b/localeinit.c
index 050b22350a..cea2e309f5 100644
--- a/localeinit.c
+++ b/localeinit.c
@@ -46,11 +46,11 @@ locale_charmap(VALUE (*conv)(const char *))
codeset = nl_langinfo_codeset();
# endif
if (!codeset) {
- UINT codepage = ruby_w32_codepage[0];
- if (!codepage) codepage = GetConsoleCP();
- if (!codepage) codepage = GetACP();
- CP_FORMAT(cp, codepage);
- codeset = cp;
+ UINT codepage = ruby_w32_codepage[0];
+ if (!codepage) codepage = GetConsoleCP();
+ if (!codepage) codepage = GetACP();
+ CP_FORMAT(cp, codepage);
+ codeset = cp;
}
#elif defined HAVE_LANGINFO_H
codeset = nl_langinfo(CODESET);
diff --git a/main.c b/main.c
index 0d0ec147cd..c87355e46e 100644
--- a/main.c
+++ b/main.c
@@ -43,6 +43,12 @@ int rb_wasm_rt_start(int (main)(int argc, char **argv), int argc, char **argv);
#define rb_main(argc, argv) rb_wasm_rt_start(rb_main, argc, argv)
#endif
+#ifdef _WIN32
+#define main(argc, argv) w32_main(argc, argv)
+static int main(int argc, char **argv);
+int wmain(void) {return main(0, NULL);}
+#endif
+
int
main(int argc, char **argv)
{
diff --git a/man/irb.1 b/man/irb.1
index 50db899177..c589c99c78 100644
--- a/man/irb.1
+++ b/man/irb.1
@@ -173,8 +173,19 @@ The default value is 16.
.El
.Pp
.Sh ENVIRONMENT
-.Bl -tag -compact
+.Bl -tag -compact -width "XDG_CONFIG_HOME"
+.It Ev IRB_LANG
+The locale used for
+.Nm .
+.Pp
.It Ev IRBRC
+The path to the personal initialization file.
+.Pp
+.It Ev XDG_CONFIG_HOME
+.Nm
+respects XDG_CONFIG_HOME. If this is set, load
+.Pa $XDG_CONFIG_HOME/irb/irbrc
+as a personal initialization file.
.Pp
.El
.Pp
@@ -186,7 +197,17 @@ depends on same variables as
.Sh FILES
.Bl -tag -compact
.It Pa ~/.irbrc
-Personal irb initialization.
+Personal irb initialization. If
+.Ev IRBRC
+is set, read
+.Pa $IRBRC
+instead. If
+.Ev IRBRC
+is not set and
+.Ev XDG_CONFIG_HOME
+is set,
+.Pa $XDG_CONFIG_HOME/irb/irbrc
+is loaded.
.Pp
.El
.Pp
diff --git a/marshal.c b/marshal.c
index 8267658f5c..4ea4255971 100644
--- a/marshal.c
+++ b/marshal.c
@@ -28,6 +28,7 @@
#include "internal/encoding.h"
#include "internal/error.h"
#include "internal/hash.h"
+#include "internal/numeric.h"
#include "internal/object.h"
#include "internal/struct.h"
#include "internal/symbol.h"
@@ -38,6 +39,8 @@
#include "ruby/st.h"
#include "ruby/util.h"
#include "builtin.h"
+#include "shape.h"
+#include "ruby/internal/attr/nonstring.h"
#define BITSPERSHORT (2*CHAR_BIT)
#define SHORTMASK ((1<<BITSPERSHORT)-1)
@@ -54,8 +57,8 @@ shortlen(size_t len, BDIGIT *ds)
num = ds[len-1];
while (num) {
- num = SHORTDN(num);
- offset++;
+ num = SHORTDN(num);
+ offset++;
}
return (len - 1)*SIZEOF_BDIGIT/2 + offset;
}
@@ -171,6 +174,7 @@ struct dump_arg {
st_table *data;
st_table *compat_tbl;
st_table *encodings;
+ unsigned long num_entries;
};
struct dump_call_arg {
@@ -184,20 +188,20 @@ check_dump_arg(VALUE ret, struct dump_arg *arg, const char *name)
{
if (!arg->symbols) {
rb_raise(rb_eRuntimeError, "Marshal.dump reentered at %s",
- name);
+ name);
}
return ret;
}
static VALUE
check_userdump_arg(VALUE obj, ID sym, int argc, const VALUE *argv,
- struct dump_arg *arg, const char *name)
+ struct dump_arg *arg, const char *name)
{
VALUE ret = rb_funcallv(obj, sym, argc, argv);
VALUE klass = CLASS_OF(obj);
if (CLASS_OF(ret) == klass) {
rb_raise(rb_eRuntimeError, "%"PRIsVALUE"#%s returned same class instance",
- klass, name);
+ klass, name);
}
return check_dump_arg(ret, arg, name);
}
@@ -246,13 +250,13 @@ must_not_be_anonymous(const char *type, VALUE path)
char *n = RSTRING_PTR(path);
if (!rb_enc_asciicompat(rb_enc_get(path))) {
- /* cannot occur? */
- rb_raise(rb_eTypeError, "can't dump non-ascii %s name % "PRIsVALUE,
- type, path);
+ /* cannot occur? */
+ rb_raise(rb_eTypeError, "can't dump non-ascii %s name % "PRIsVALUE,
+ type, path);
}
if (n[0] == '#') {
- rb_raise(rb_eTypeError, "can't dump anonymous %s % "PRIsVALUE,
- type, path);
+ rb_raise(rb_eTypeError, "can't dump anonymous %s % "PRIsVALUE,
+ type, path);
}
return path;
}
@@ -264,7 +268,7 @@ class2path(VALUE klass)
must_not_be_anonymous((RB_TYPE_P(klass, T_CLASS) ? "class" : "module"), path);
if (rb_path_to_class(path) != rb_class_real(klass)) {
- rb_raise(rb_eTypeError, "% "PRIsVALUE" can't be referred to", path);
+ rb_raise(rb_eTypeError, "% "PRIsVALUE" can't be referred to", path);
}
return path;
}
@@ -280,8 +284,8 @@ w_nbyte(const char *s, long n, struct dump_arg *arg)
VALUE buf = arg->str;
rb_str_buf_cat(buf, s, n);
if (arg->dest && RSTRING_LEN(buf) >= BUFSIZ) {
- rb_io_write(arg->dest, buf);
- rb_str_resize(buf, 0);
+ rb_io_write(arg->dest, buf);
+ rb_str_resize(buf, 0);
}
}
@@ -325,7 +329,7 @@ ruby_marshal_write_long(long x, char *buf)
#if SIZEOF_LONG > 4
if (!(RSHIFT(x, 31) == 0 || RSHIFT(x, 31) == -1)) {
- /* big long does not fit in 4 bytes */
+ /* big long does not fit in 4 bytes */
return -1;
}
#endif
@@ -343,16 +347,16 @@ ruby_marshal_write_long(long x, char *buf)
return 1;
}
for (i=1;i<(int)sizeof(long)+1;i++) {
- buf[i] = (char)(x & 0xff);
- x = RSHIFT(x,8);
- if (x == 0) {
- buf[0] = i;
- break;
- }
- if (x == -1) {
- buf[0] = -i;
- break;
- }
+ buf[i] = (char)(x & 0xff);
+ x = RSHIFT(x,8);
+ if (x == 0) {
+ buf[0] = i;
+ break;
+ }
+ if (x == -1) {
+ buf[0] = -i;
+ break;
+ }
}
return i+1;
}
@@ -375,13 +379,13 @@ load_mantissa(double d, const char *buf, long len)
{
if (!len) return d;
if (--len > 0 && !*buf++) { /* binary mantissa mark */
- int e, s = d < 0, dig = 0;
- unsigned long m;
+ int e, s = d < 0, dig = 0;
+ unsigned long m;
- modf(ldexp(frexp(fabs(d), &e), DECIMAL_MANT), &d);
- do {
- m = 0;
- switch (len) {
+ modf(ldexp(frexp(fabs(d), &e), DECIMAL_MANT), &d);
+ do {
+ m = 0;
+ switch (len) {
default: m = *buf++ & 0xff; /* fall through */
#if MANT_BITS > 24
case 3: m = (m << 8) | (*buf++ & 0xff); /* fall through */
@@ -390,14 +394,14 @@ load_mantissa(double d, const char *buf, long len)
case 2: m = (m << 8) | (*buf++ & 0xff); /* fall through */
#endif
#if MANT_BITS > 8
- case 1: m = (m << 8) | (*buf++ & 0xff);
+ case 1: m = (m << 8) | (*buf++ & 0xff);
#endif
- }
- dig -= len < MANT_BITS / 8 ? 8 * (unsigned)len : MANT_BITS;
- d += ldexp((double)m, dig);
- } while ((len -= MANT_BITS / 8) > 0);
- d = ldexp(d, e - DECIMAL_MANT);
- if (s) d = -d;
+ }
+ dig -= len < MANT_BITS / 8 ? 8 * (unsigned)len : MANT_BITS;
+ d += ldexp((double)m, dig);
+ } while ((len -= MANT_BITS / 8) > 0);
+ d = ldexp(d, e - DECIMAL_MANT);
+ if (s) d = -d;
}
return d;
}
@@ -417,49 +421,49 @@ w_float(double d, struct dump_arg *arg)
char buf[FLOAT_DIG + (DECIMAL_MANT + 7) / 8 + 10];
if (isinf(d)) {
- if (d < 0) w_cstr("-inf", arg);
- else w_cstr("inf", arg);
+ if (d < 0) w_cstr("-inf", arg);
+ else w_cstr("inf", arg);
}
else if (isnan(d)) {
- w_cstr("nan", arg);
+ w_cstr("nan", arg);
}
else if (d == 0.0) {
if (signbit(d)) w_cstr("-0", arg);
else w_cstr("0", arg);
}
else {
- int decpt, sign, digs, len = 0;
- char *e, *p = ruby_dtoa(d, 0, 0, &decpt, &sign, &e);
- if (sign) buf[len++] = '-';
- digs = (int)(e - p);
- if (decpt < -3 || decpt > digs) {
- buf[len++] = p[0];
- if (--digs > 0) buf[len++] = '.';
- memcpy(buf + len, p + 1, digs);
- len += digs;
- len += snprintf(buf + len, sizeof(buf) - len, "e%d", decpt - 1);
- }
- else if (decpt > 0) {
- memcpy(buf + len, p, decpt);
- len += decpt;
- if ((digs -= decpt) > 0) {
- buf[len++] = '.';
- memcpy(buf + len, p + decpt, digs);
- len += digs;
- }
- }
- else {
- buf[len++] = '0';
- buf[len++] = '.';
- if (decpt) {
- memset(buf + len, '0', -decpt);
- len -= decpt;
- }
- memcpy(buf + len, p, digs);
- len += digs;
- }
- xfree(p);
- w_bytes(buf, len, arg);
+ int decpt, sign, digs, len = 0;
+ char *e, *p = ruby_dtoa(d, 0, 0, &decpt, &sign, &e);
+ if (sign) buf[len++] = '-';
+ digs = (int)(e - p);
+ if (decpt < -3 || decpt > digs) {
+ buf[len++] = p[0];
+ if (--digs > 0) buf[len++] = '.';
+ memcpy(buf + len, p + 1, digs);
+ len += digs;
+ len += snprintf(buf + len, sizeof(buf) - len, "e%d", decpt - 1);
+ }
+ else if (decpt > 0) {
+ memcpy(buf + len, p, decpt);
+ len += decpt;
+ if ((digs -= decpt) > 0) {
+ buf[len++] = '.';
+ memcpy(buf + len, p + decpt, digs);
+ len += digs;
+ }
+ }
+ else {
+ buf[len++] = '0';
+ buf[len++] = '.';
+ if (decpt) {
+ memset(buf + len, '0', -decpt);
+ len -= decpt;
+ }
+ memcpy(buf + len, p, digs);
+ len += digs;
+ }
+ xfree(p);
+ w_bytes(buf, len, arg);
}
}
@@ -470,33 +474,33 @@ w_symbol(VALUE sym, struct dump_arg *arg)
VALUE encname;
if (st_lookup(arg->symbols, sym, &num)) {
- w_byte(TYPE_SYMLINK, arg);
- w_long((long)num, arg);
+ w_byte(TYPE_SYMLINK, arg);
+ w_long((long)num, arg);
}
else {
- const VALUE orig_sym = sym;
- sym = rb_sym2str(sym);
- if (!sym) {
- rb_raise(rb_eTypeError, "can't dump anonymous ID %"PRIdVALUE, sym);
- }
- encname = encoding_name(sym, arg);
- if (NIL_P(encname) ||
- is_ascii_string(sym)) {
- encname = Qnil;
- }
- else {
- w_byte(TYPE_IVAR, arg);
- }
- w_byte(TYPE_SYMBOL, arg);
- w_bytes(RSTRING_PTR(sym), RSTRING_LEN(sym), arg);
- st_add_direct(arg->symbols, orig_sym, arg->symbols->num_entries);
- if (!NIL_P(encname)) {
- struct dump_call_arg c_arg;
- c_arg.limit = 1;
- c_arg.arg = arg;
- w_long(1L, arg);
- w_encoding(encname, &c_arg);
- }
+ const VALUE orig_sym = sym;
+ sym = rb_sym2str(sym);
+ if (!sym) {
+ rb_raise(rb_eTypeError, "can't dump anonymous ID %"PRIdVALUE, sym);
+ }
+ encname = encoding_name(sym, arg);
+ if (NIL_P(encname) ||
+ is_ascii_string(sym)) {
+ encname = Qnil;
+ }
+ else {
+ w_byte(TYPE_IVAR, arg);
+ }
+ w_byte(TYPE_SYMBOL, arg);
+ w_bytes(RSTRING_PTR(sym), RSTRING_LEN(sym), arg);
+ st_add_direct(arg->symbols, orig_sym, arg->symbols->num_entries);
+ if (!NIL_P(encname)) {
+ struct dump_call_arg c_arg;
+ c_arg.limit = 1;
+ c_arg.arg = arg;
+ w_long(1L, arg);
+ w_encoding(encname, &c_arg);
+ }
}
}
@@ -520,18 +524,18 @@ hash_each(VALUE key, VALUE value, VALUE v)
#define SINGLETON_DUMP_UNABLE_P(klass) \
(rb_id_table_size(RCLASS_M_TBL(klass)) > 0 || \
- (RCLASS_IV_TBL(klass) && RCLASS_IV_TBL(klass)->num_entries > 1))
+ rb_ivar_count(klass) > 1)
static void
w_extended(VALUE klass, struct dump_arg *arg, int check)
{
if (check && FL_TEST(klass, FL_SINGLETON)) {
- VALUE origin = RCLASS_ORIGIN(klass);
- if (SINGLETON_DUMP_UNABLE_P(klass) ||
- (origin != klass && SINGLETON_DUMP_UNABLE_P(origin))) {
- rb_raise(rb_eTypeError, "singleton can't be dumped");
- }
- klass = RCLASS_SUPER(klass);
+ VALUE origin = RCLASS_ORIGIN(klass);
+ if (SINGLETON_DUMP_UNABLE_P(klass) ||
+ (origin != klass && SINGLETON_DUMP_UNABLE_P(origin))) {
+ rb_raise(rb_eTypeError, "singleton can't be dumped");
+ }
+ klass = RCLASS_SUPER(klass);
}
while (BUILTIN_TYPE(klass) == T_ICLASS) {
if (!FL_TEST(klass, RICLASS_IS_ORIGIN) ||
@@ -552,7 +556,7 @@ w_class(char type, VALUE obj, struct dump_arg *arg, int check)
VALUE klass;
if (arg->compat_tbl &&
- st_lookup(arg->compat_tbl, (st_data_t)obj, &real_obj)) {
+ st_lookup(arg->compat_tbl, (st_data_t)obj, &real_obj)) {
obj = (VALUE)real_obj;
}
klass = CLASS_OF(obj);
@@ -570,8 +574,8 @@ w_uclass(VALUE obj, VALUE super, struct dump_arg *arg)
w_extended(klass, arg, TRUE);
klass = rb_class_real(klass);
if (klass != super) {
- w_byte(TYPE_UCLASS, arg);
- w_unique(class2path(klass), arg);
+ w_byte(TYPE_UCLASS, arg);
+ w_unique(class2path(klass), arg);
}
}
@@ -620,10 +624,6 @@ w_obj_each(st_data_t key, st_data_t val, st_data_t a)
}
return ST_CONTINUE;
}
- if (!ivarg->num_ivar) {
- rb_raise(rb_eRuntimeError, "instance variable added to %"PRIsVALUE" instance",
- CLASS_OF(arg->obj));
- }
--ivarg->num_ivar;
w_symbol(ID2SYM(id), arg->arg);
w_object(value, arg->arg, arg->limit);
@@ -682,7 +682,7 @@ w_encoding(VALUE encname, struct dump_call_arg *arg)
case Qfalse:
case Qtrue:
w_symbol(ID2SYM(s_encoding_short), arg->arg);
- w_object(encname, arg->arg, limit);
+ w_object(encname, arg->arg, limit);
return 1;
case Qnil:
return 0;
@@ -702,14 +702,14 @@ has_ivars(VALUE obj, VALUE encname, VALUE *ivobj)
case T_OBJECT:
case T_CLASS:
case T_MODULE:
- break; /* counted elsewhere */
+ break; /* counted elsewhere */
case T_HASH:
if (rb_hash_ruby2_keywords_p(obj)) ++num;
/* fall through */
default:
generic:
- rb_ivar_foreach(obj, obj_count_ivars, (st_data_t)&num);
- if (num) *ivobj = obj;
+ rb_ivar_foreach(obj, obj_count_ivars, (st_data_t)&num);
+ if (num) *ivobj = obj;
}
return num;
@@ -718,12 +718,27 @@ has_ivars(VALUE obj, VALUE encname, VALUE *ivobj)
static void
w_ivar_each(VALUE obj, st_index_t num, struct dump_call_arg *arg)
{
+ shape_id_t shape_id = rb_shape_get_shape_id(arg->obj);
struct w_ivar_arg ivarg = {arg, num};
if (!num) return;
rb_ivar_foreach(obj, w_obj_each, (st_data_t)&ivarg);
- if (ivarg.num_ivar) {
- rb_raise(rb_eRuntimeError, "instance variable removed from %"PRIsVALUE" instance",
- CLASS_OF(arg->obj));
+
+ if (shape_id != rb_shape_get_shape_id(arg->obj)) {
+ rb_shape_t * expected_shape = rb_shape_get_shape_by_id(shape_id);
+ rb_shape_t * actual_shape = rb_shape_get_shape(arg->obj);
+
+ // If the shape tree got _shorter_ then we probably removed an IV
+ // If the shape tree got longer, then we probably added an IV.
+ // The exception message might not be accurate when someone adds and
+ // removes the same number of IVs, but they will still get an exception
+ if (rb_shape_depth(expected_shape) > rb_shape_depth(actual_shape)) {
+ rb_raise(rb_eRuntimeError, "instance variable removed from %"PRIsVALUE" instance",
+ CLASS_OF(arg->obj));
+ }
+ else {
+ rb_raise(rb_eRuntimeError, "instance variable added to %"PRIsVALUE" instance",
+ CLASS_OF(arg->obj));
+ }
}
}
@@ -736,10 +751,10 @@ w_ivar(st_index_t num, VALUE ivobj, VALUE encname, struct dump_call_arg *arg)
int limit = arg->limit;
if (limit >= 0) ++limit;
w_symbol(ID2SYM(s_ruby2_keywords_flag), arg->arg);
- w_object(Qtrue, arg->arg, limit);
+ w_object(Qtrue, arg->arg, limit);
num--;
}
- if (ivobj != Qundef && num) {
+ if (!UNDEF_P(ivobj) && num) {
w_ivar_each(ivobj, num, arg);
}
}
@@ -754,6 +769,62 @@ w_objivar(VALUE obj, struct dump_call_arg *arg)
w_ivar_each(obj, num, arg);
}
+#if SIZEOF_LONG > 4
+// Optimized dump for fixnum larger than 31-bits
+static void
+w_bigfixnum(VALUE obj, struct dump_arg *arg)
+{
+ RUBY_ASSERT(FIXNUM_P(obj));
+
+ w_byte(TYPE_BIGNUM, arg);
+
+#if SIZEOF_LONG == SIZEOF_VALUE
+ long num, slen_num;
+ num = FIX2LONG(obj);
+#else
+ long long num, slen_num;
+ num = NUM2LL(obj);
+#endif
+
+ char sign = num < 0 ? '-' : '+';
+ w_byte(sign, arg);
+
+ // Guaranteed not to overflow, as FIXNUM is 1-bit less than long
+ if (num < 0) num = -num;
+
+ // calculate the size in shorts
+ int slen = 0;
+ {
+ slen_num = num;
+ while (slen_num) {
+ slen++;
+ slen_num = SHORTDN(slen_num);
+ }
+ }
+
+ RUBY_ASSERT(slen > 0 && slen <= SIZEOF_LONG / 2);
+
+ w_long((long)slen, arg);
+
+ for (int i = 0; i < slen; i++) {
+ w_short(num & SHORTMASK, arg);
+ num = SHORTDN(num);
+ }
+
+ // We aren't adding this object to the link table, but we need to increment
+ // the index.
+ arg->num_entries++;
+
+ RUBY_ASSERT(num == 0);
+}
+#endif
+
+static void
+w_remember(VALUE obj, struct dump_arg *arg)
+{
+ st_add_direct(arg->data, obj, arg->num_entries++);
+}
+
static void
w_object(VALUE obj, struct dump_arg *arg, int limit)
{
@@ -764,97 +835,99 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
VALUE encname = Qnil;
if (limit == 0) {
- rb_raise(rb_eArgError, "exceed depth limit");
- }
-
- if (limit > 0) limit--;
- c_arg.limit = limit;
- c_arg.arg = arg;
- c_arg.obj = obj;
-
- if (st_lookup(arg->data, obj, &num)) {
- w_byte(TYPE_LINK, arg);
- w_long((long)num, arg);
- return;
+ rb_raise(rb_eArgError, "exceed depth limit");
}
if (NIL_P(obj)) {
- w_byte(TYPE_NIL, arg);
+ w_byte(TYPE_NIL, arg);
}
else if (obj == Qtrue) {
- w_byte(TYPE_TRUE, arg);
+ w_byte(TYPE_TRUE, arg);
}
else if (obj == Qfalse) {
- w_byte(TYPE_FALSE, arg);
+ w_byte(TYPE_FALSE, arg);
}
else if (FIXNUM_P(obj)) {
#if SIZEOF_LONG <= 4
- w_byte(TYPE_FIXNUM, arg);
- w_long(FIX2INT(obj), arg);
+ w_byte(TYPE_FIXNUM, arg);
+ w_long(FIX2INT(obj), arg);
#else
- if (RSHIFT((long)obj, 31) == 0 || RSHIFT((long)obj, 31) == -1) {
- w_byte(TYPE_FIXNUM, arg);
- w_long(FIX2LONG(obj), arg);
- }
- else {
- w_object(rb_int2big(FIX2LONG(obj)), arg, limit);
- }
+ if (RSHIFT((long)obj, 31) == 0 || RSHIFT((long)obj, 31) == -1) {
+ w_byte(TYPE_FIXNUM, arg);
+ w_long(FIX2LONG(obj), arg);
+ }
+ else {
+ w_bigfixnum(obj, arg);
+ }
#endif
}
else if (SYMBOL_P(obj)) {
- w_symbol(obj, arg);
- }
- else if (FLONUM_P(obj)) {
- st_add_direct(arg->data, obj, arg->data->num_entries);
- w_byte(TYPE_FLOAT, arg);
- w_float(RFLOAT_VALUE(obj), arg);
+ w_symbol(obj, arg);
}
else {
- VALUE v;
-
- if (!RBASIC_CLASS(obj)) {
- rb_raise(rb_eTypeError, "can't dump internal %s",
- rb_builtin_type_name(BUILTIN_TYPE(obj)));
- }
-
- if (rb_obj_respond_to(obj, s_mdump, TRUE)) {
- st_add_direct(arg->data, obj, arg->data->num_entries);
-
- v = dump_funcall(arg, obj, s_mdump, 0, 0);
- w_class(TYPE_USRMARSHAL, obj, arg, FALSE);
- w_object(v, arg, limit);
- return;
- }
- if (rb_obj_respond_to(obj, s_dump, TRUE)) {
- VALUE ivobj2 = Qundef;
- st_index_t hasiv2;
- VALUE encname2;
-
- v = INT2NUM(limit);
- v = dump_funcall(arg, obj, s_dump, 1, &v);
- if (!RB_TYPE_P(v, T_STRING)) {
- rb_raise(rb_eTypeError, "_dump() must return string");
- }
- hasiv = has_ivars(obj, (encname = encoding_name(obj, arg)), &ivobj);
- hasiv2 = has_ivars(v, (encname2 = encoding_name(v, arg)), &ivobj2);
- if (hasiv2) {
- hasiv = hasiv2;
- ivobj = ivobj2;
- encname = encname2;
- }
- if (hasiv) w_byte(TYPE_IVAR, arg);
- w_class(TYPE_USERDEF, obj, arg, FALSE);
- w_bytes(RSTRING_PTR(v), RSTRING_LEN(v), arg);
- if (hasiv) {
- w_ivar(hasiv, ivobj, encname, &c_arg);
- }
- st_add_direct(arg->data, obj, arg->data->num_entries);
- return;
- }
-
- st_add_direct(arg->data, obj, arg->data->num_entries);
-
- hasiv = has_ivars(obj, (encname = encoding_name(obj, arg)), &ivobj);
+ if (st_lookup(arg->data, obj, &num)) {
+ w_byte(TYPE_LINK, arg);
+ w_long((long)num, arg);
+ return;
+ }
+
+ if (limit > 0) limit--;
+ c_arg.limit = limit;
+ c_arg.arg = arg;
+ c_arg.obj = obj;
+
+ if (FLONUM_P(obj)) {
+ w_remember(obj, arg);
+ w_byte(TYPE_FLOAT, arg);
+ w_float(RFLOAT_VALUE(obj), arg);
+ return;
+ }
+
+ VALUE v;
+
+ if (!RBASIC_CLASS(obj)) {
+ rb_raise(rb_eTypeError, "can't dump internal %s",
+ rb_builtin_type_name(BUILTIN_TYPE(obj)));
+ }
+
+ if (rb_obj_respond_to(obj, s_mdump, TRUE)) {
+ w_remember(obj, arg);
+
+ v = dump_funcall(arg, obj, s_mdump, 0, 0);
+ w_class(TYPE_USRMARSHAL, obj, arg, FALSE);
+ w_object(v, arg, limit);
+ return;
+ }
+ if (rb_obj_respond_to(obj, s_dump, TRUE)) {
+ VALUE ivobj2 = Qundef;
+ st_index_t hasiv2;
+ VALUE encname2;
+
+ v = INT2NUM(limit);
+ v = dump_funcall(arg, obj, s_dump, 1, &v);
+ if (!RB_TYPE_P(v, T_STRING)) {
+ rb_raise(rb_eTypeError, "_dump() must return string");
+ }
+ hasiv = has_ivars(obj, (encname = encoding_name(obj, arg)), &ivobj);
+ hasiv2 = has_ivars(v, (encname2 = encoding_name(v, arg)), &ivobj2);
+ if (hasiv2) {
+ hasiv = hasiv2;
+ ivobj = ivobj2;
+ encname = encname2;
+ }
+ if (hasiv) w_byte(TYPE_IVAR, arg);
+ w_class(TYPE_USERDEF, obj, arg, FALSE);
+ w_bytes(RSTRING_PTR(v), RSTRING_LEN(v), arg);
+ if (hasiv) {
+ w_ivar(hasiv, ivobj, encname, &c_arg);
+ }
+ w_remember(obj, arg);
+ return;
+ }
+
+ w_remember(obj, arg);
+
+ hasiv = has_ivars(obj, (encname = encoding_name(obj, arg)), &ivobj);
{
st_data_t compat_data;
rb_alloc_func_t allocator = rb_get_alloc_func(RBASIC(obj)->klass);
@@ -868,79 +941,79 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
arg->compat_tbl = rb_init_identtable();
}
st_insert(arg->compat_tbl, (st_data_t)obj, (st_data_t)real_obj);
- if (obj != real_obj && ivobj == Qundef) hasiv = 0;
+ if (obj != real_obj && UNDEF_P(ivobj)) hasiv = 0;
}
}
- if (hasiv) w_byte(TYPE_IVAR, arg);
-
- switch (BUILTIN_TYPE(obj)) {
- case T_CLASS:
- if (FL_TEST(obj, FL_SINGLETON)) {
- rb_raise(rb_eTypeError, "singleton class can't be dumped");
- }
- w_byte(TYPE_CLASS, arg);
- {
- VALUE path = class2path(obj);
- w_bytes(RSTRING_PTR(path), RSTRING_LEN(path), arg);
- RB_GC_GUARD(path);
- }
- break;
-
- case T_MODULE:
- w_byte(TYPE_MODULE, arg);
- {
- VALUE path = class2path(obj);
- w_bytes(RSTRING_PTR(path), RSTRING_LEN(path), arg);
- RB_GC_GUARD(path);
- }
- break;
-
- case T_FLOAT:
- w_byte(TYPE_FLOAT, arg);
- w_float(RFLOAT_VALUE(obj), arg);
- break;
-
- case T_BIGNUM:
- w_byte(TYPE_BIGNUM, arg);
- {
- char sign = BIGNUM_SIGN(obj) ? '+' : '-';
- size_t len = BIGNUM_LEN(obj);
- size_t slen;
+ if (hasiv) w_byte(TYPE_IVAR, arg);
+
+ switch (BUILTIN_TYPE(obj)) {
+ case T_CLASS:
+ if (FL_TEST(obj, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "singleton class can't be dumped");
+ }
+ w_byte(TYPE_CLASS, arg);
+ {
+ VALUE path = class2path(obj);
+ w_bytes(RSTRING_PTR(path), RSTRING_LEN(path), arg);
+ RB_GC_GUARD(path);
+ }
+ break;
+
+ case T_MODULE:
+ w_byte(TYPE_MODULE, arg);
+ {
+ VALUE path = class2path(obj);
+ w_bytes(RSTRING_PTR(path), RSTRING_LEN(path), arg);
+ RB_GC_GUARD(path);
+ }
+ break;
+
+ case T_FLOAT:
+ w_byte(TYPE_FLOAT, arg);
+ w_float(RFLOAT_VALUE(obj), arg);
+ break;
+
+ case T_BIGNUM:
+ w_byte(TYPE_BIGNUM, arg);
+ {
+ char sign = BIGNUM_SIGN(obj) ? '+' : '-';
+ size_t len = BIGNUM_LEN(obj);
+ size_t slen;
size_t j;
- BDIGIT *d = BIGNUM_DIGITS(obj);
+ BDIGIT *d = BIGNUM_DIGITS(obj);
slen = SHORTLEN(len);
if (LONG_MAX < slen) {
rb_raise(rb_eTypeError, "too big Bignum can't be dumped");
}
- w_byte(sign, arg);
- w_long((long)slen, arg);
+ w_byte(sign, arg);
+ w_long((long)slen, arg);
for (j = 0; j < len; j++) {
#if SIZEOF_BDIGIT > SIZEOF_SHORT
- BDIGIT num = *d;
- int i;
+ BDIGIT num = *d;
+ int i;
- for (i=0; i<SIZEOF_BDIGIT; i+=SIZEOF_SHORT) {
- w_short(num & SHORTMASK, arg);
- num = SHORTDN(num);
+ for (i=0; i<SIZEOF_BDIGIT; i+=SIZEOF_SHORT) {
+ w_short(num & SHORTMASK, arg);
+ num = SHORTDN(num);
if (j == len - 1 && num == 0) break;
- }
+ }
#else
- w_short(*d, arg);
+ w_short(*d, arg);
#endif
- d++;
- }
- }
- break;
-
- case T_STRING:
- w_uclass(obj, rb_cString, arg);
- w_byte(TYPE_STRING, arg);
- w_bytes(RSTRING_PTR(obj), RSTRING_LEN(obj), arg);
- break;
-
- case T_REGEXP:
+ d++;
+ }
+ }
+ break;
+
+ case T_STRING:
+ w_uclass(obj, rb_cString, arg);
+ w_byte(TYPE_STRING, arg);
+ w_bytes(RSTRING_PTR(obj), RSTRING_LEN(obj), arg);
+ break;
+
+ case T_REGEXP:
w_uclass(obj, rb_cRegexp, arg);
w_byte(TYPE_REGEXP, arg);
{
@@ -948,91 +1021,91 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
w_bytes(RREGEXP_SRC_PTR(obj), RREGEXP_SRC_LEN(obj), arg);
w_byte((char)opts, arg);
}
- break;
-
- case T_ARRAY:
- w_uclass(obj, rb_cArray, arg);
- w_byte(TYPE_ARRAY, arg);
- {
- long i, len = RARRAY_LEN(obj);
-
- w_long(len, arg);
- for (i=0; i<RARRAY_LEN(obj); i++) {
- w_object(RARRAY_AREF(obj, i), arg, limit);
- if (len != RARRAY_LEN(obj)) {
- rb_raise(rb_eRuntimeError, "array modified during dump");
- }
- }
- }
- break;
-
- case T_HASH:
- w_uclass(obj, rb_cHash, arg);
- if (rb_hash_compare_by_id_p(obj)) {
- w_byte(TYPE_UCLASS, arg);
- w_symbol(rb_sym_intern_ascii_cstr("Hash"), arg);
- }
- if (NIL_P(RHASH_IFNONE(obj))) {
- w_byte(TYPE_HASH, arg);
- }
+ break;
+
+ case T_ARRAY:
+ w_uclass(obj, rb_cArray, arg);
+ w_byte(TYPE_ARRAY, arg);
+ {
+ long i, len = RARRAY_LEN(obj);
+
+ w_long(len, arg);
+ for (i=0; i<RARRAY_LEN(obj); i++) {
+ w_object(RARRAY_AREF(obj, i), arg, limit);
+ if (len != RARRAY_LEN(obj)) {
+ rb_raise(rb_eRuntimeError, "array modified during dump");
+ }
+ }
+ }
+ break;
+
+ case T_HASH:
+ w_uclass(obj, rb_cHash, arg);
+ if (rb_hash_compare_by_id_p(obj)) {
+ w_byte(TYPE_UCLASS, arg);
+ w_symbol(rb_sym_intern_ascii_cstr("Hash"), arg);
+ }
+ if (NIL_P(RHASH_IFNONE(obj))) {
+ w_byte(TYPE_HASH, arg);
+ }
else if (FL_TEST(obj, RHASH_PROC_DEFAULT)) {
- rb_raise(rb_eTypeError, "can't dump hash with default proc");
- }
- else {
- w_byte(TYPE_HASH_DEF, arg);
- }
+ rb_raise(rb_eTypeError, "can't dump hash with default proc");
+ }
+ else {
+ w_byte(TYPE_HASH_DEF, arg);
+ }
w_long(rb_hash_size_num(obj), arg);
- rb_hash_foreach(obj, hash_each, (st_data_t)&c_arg);
- if (!NIL_P(RHASH_IFNONE(obj))) {
- w_object(RHASH_IFNONE(obj), arg, limit);
- }
- break;
-
- case T_STRUCT:
- w_class(TYPE_STRUCT, obj, arg, TRUE);
- {
- long len = RSTRUCT_LEN(obj);
- VALUE mem;
- long i;
-
- w_long(len, arg);
- mem = rb_struct_members(obj);
- for (i=0; i<len; i++) {
- w_symbol(RARRAY_AREF(mem, i), arg);
- w_object(RSTRUCT_GET(obj, i), arg, limit);
- }
- }
- break;
-
- case T_OBJECT:
- w_class(TYPE_OBJECT, obj, arg, TRUE);
- w_objivar(obj, &c_arg);
- break;
-
- case T_DATA:
- {
- VALUE v;
-
- if (!rb_obj_respond_to(obj, s_dump_data, TRUE)) {
- rb_raise(rb_eTypeError,
- "no _dump_data is defined for class %"PRIsVALUE,
- rb_obj_class(obj));
- }
- v = dump_funcall(arg, obj, s_dump_data, 0, 0);
- w_class(TYPE_DATA, obj, arg, TRUE);
- w_object(v, arg, limit);
- }
- break;
-
- default:
- rb_raise(rb_eTypeError, "can't dump %"PRIsVALUE,
- rb_obj_class(obj));
- break;
- }
- RB_GC_GUARD(obj);
+ rb_hash_foreach(obj, hash_each, (st_data_t)&c_arg);
+ if (!NIL_P(RHASH_IFNONE(obj))) {
+ w_object(RHASH_IFNONE(obj), arg, limit);
+ }
+ break;
+
+ case T_STRUCT:
+ w_class(TYPE_STRUCT, obj, arg, TRUE);
+ {
+ long len = RSTRUCT_LEN(obj);
+ VALUE mem;
+ long i;
+
+ w_long(len, arg);
+ mem = rb_struct_members(obj);
+ for (i=0; i<len; i++) {
+ w_symbol(RARRAY_AREF(mem, i), arg);
+ w_object(RSTRUCT_GET(obj, i), arg, limit);
+ }
+ }
+ break;
+
+ case T_OBJECT:
+ w_class(TYPE_OBJECT, obj, arg, TRUE);
+ w_objivar(obj, &c_arg);
+ break;
+
+ case T_DATA:
+ {
+ VALUE v;
+
+ if (!rb_obj_respond_to(obj, s_dump_data, TRUE)) {
+ rb_raise(rb_eTypeError,
+ "no _dump_data is defined for class %"PRIsVALUE,
+ rb_obj_class(obj));
+ }
+ v = dump_funcall(arg, obj, s_dump_data, 0, 0);
+ w_class(TYPE_DATA, obj, arg, TRUE);
+ w_object(v, arg, limit);
+ }
+ break;
+
+ default:
+ rb_raise(rb_eTypeError, "can't dump %"PRIsVALUE,
+ rb_obj_class(obj));
+ break;
+ }
+ RB_GC_GUARD(obj);
}
if (hasiv) {
- w_ivar(hasiv, ivobj, encname, &c_arg);
+ w_ivar(hasiv, ivobj, encname, &c_arg);
}
}
@@ -1044,13 +1117,14 @@ clear_dump_arg(struct dump_arg *arg)
arg->symbols = 0;
st_free_table(arg->data);
arg->data = 0;
+ arg->num_entries = 0;
if (arg->compat_tbl) {
- st_free_table(arg->compat_tbl);
- arg->compat_tbl = 0;
+ st_free_table(arg->compat_tbl);
+ arg->compat_tbl = 0;
}
if (arg->encodings) {
- st_free_table(arg->encodings);
- arg->encodings = 0;
+ st_free_table(arg->encodings);
+ arg->encodings = 0;
}
}
@@ -1104,14 +1178,14 @@ marshal_dump(int argc, VALUE *argv, VALUE _)
port = Qnil;
rb_scan_args(argc, argv, "12", &obj, &a1, &a2);
if (argc == 3) {
- if (!NIL_P(a2)) limit = NUM2INT(a2);
- if (NIL_P(a1)) io_needed();
- port = a1;
+ if (!NIL_P(a2)) limit = NUM2INT(a2);
+ if (NIL_P(a1)) io_needed();
+ port = a1;
}
else if (argc == 2) {
- if (FIXNUM_P(a1)) limit = FIX2INT(a1);
- else if (NIL_P(a1)) io_needed();
- else port = a1;
+ if (FIXNUM_P(a1)) limit = FIX2INT(a1);
+ else if (NIL_P(a1)) io_needed();
+ else port = a1;
}
return rb_marshal_dump_limited(obj, port, limit);
}
@@ -1126,18 +1200,19 @@ rb_marshal_dump_limited(VALUE obj, VALUE port, int limit)
arg->dest = 0;
arg->symbols = st_init_numtable();
arg->data = rb_init_identtable();
+ arg->num_entries = 0;
arg->compat_tbl = 0;
arg->encodings = 0;
arg->str = rb_str_buf_new(0);
if (!NIL_P(port)) {
- if (!rb_respond_to(port, s_write)) {
- io_needed();
- }
- arg->dest = port;
- dump_check_funcall(arg, port, s_binmode, 0, 0);
+ if (!rb_respond_to(port, s_write)) {
+ io_needed();
+ }
+ arg->dest = port;
+ dump_check_funcall(arg, port, s_binmode, 0, 0);
}
else {
- port = arg->str;
+ port = arg->str;
}
w_byte(MARSHAL_MAJOR, arg);
@@ -1145,8 +1220,8 @@ rb_marshal_dump_limited(VALUE obj, VALUE port, int limit)
w_object(obj, arg, limit);
if (arg->dest) {
- rb_io_write(arg->dest, arg->str);
- rb_str_resize(arg->str, 0);
+ rb_io_write(arg->dest, arg->str);
+ rb_str_resize(arg->str, 0);
}
clear_dump_arg(arg);
RB_GC_GUARD(wrapper);
@@ -1173,7 +1248,7 @@ check_load_arg(VALUE ret, struct load_arg *arg, const char *name)
{
if (!arg->symbols) {
rb_raise(rb_eRuntimeError, "Marshal.load reentered at %s",
- name);
+ name);
}
return ret;
}
@@ -1237,15 +1312,15 @@ static unsigned char
r_byte1_buffered(struct load_arg *arg)
{
if (arg->buflen == 0) {
- long readable = arg->readable < BUFSIZ ? arg->readable : BUFSIZ;
- VALUE str, n = LONG2NUM(readable);
+ long readable = arg->readable < BUFSIZ ? arg->readable : BUFSIZ;
+ VALUE str, n = LONG2NUM(readable);
- str = load_funcall(arg, arg->src, s_read, 1, &n);
- if (NIL_P(str)) too_short();
- StringValue(str);
- memcpy(arg->buf, RSTRING_PTR(str), RSTRING_LEN(str));
- arg->offset = 0;
- arg->buflen = RSTRING_LEN(str);
+ str = load_funcall(arg, arg->src, s_read, 1, &n);
+ if (NIL_P(str)) too_short();
+ StringValue(str);
+ memcpy(arg->buf, RSTRING_PTR(str), RSTRING_LEN(str));
+ arg->offset = 0;
+ arg->buflen = RSTRING_LEN(str);
}
arg->buflen--;
return arg->buf[arg->offset++];
@@ -1257,22 +1332,22 @@ r_byte(struct load_arg *arg)
int c;
if (RB_TYPE_P(arg->src, T_STRING)) {
- if (RSTRING_LEN(arg->src) > arg->offset) {
- c = (unsigned char)RSTRING_PTR(arg->src)[arg->offset++];
- }
- else {
- too_short();
- }
+ if (RSTRING_LEN(arg->src) > arg->offset) {
+ c = (unsigned char)RSTRING_PTR(arg->src)[arg->offset++];
+ }
+ else {
+ too_short();
+ }
}
else {
- if (arg->readable >0 || arg->buflen > 0) {
- c = r_byte1_buffered(arg);
- }
- else {
- VALUE v = load_funcall(arg, arg->src, s_getbyte, 0, 0);
- if (NIL_P(v)) rb_eof_error();
- c = (unsigned char)NUM2CHR(v);
- }
+ if (arg->readable >0 || arg->buflen > 0) {
+ c = r_byte1_buffered(arg);
+ }
+ else {
+ VALUE v = load_funcall(arg, arg->src, s_getbyte, 0, 0);
+ if (NIL_P(v)) rb_eof_error();
+ c = (unsigned char)NUM2CHR(v);
+ }
}
return c;
}
@@ -1283,7 +1358,7 @@ static void
long_toobig(int size)
{
rb_raise(rb_eTypeError, "long too big for this architecture (size "
- STRINGIZE(SIZEOF_LONG)", given %d)", size);
+ STRINGIZE(SIZEOF_LONG)", given %d)", size);
}
static long
@@ -1295,26 +1370,26 @@ r_long(struct load_arg *arg)
if (c == 0) return 0;
if (c > 0) {
- if (4 < c && c < 128) {
- return c - 5;
- }
- if (c > (int)sizeof(long)) long_toobig(c);
- x = 0;
- for (i=0;i<c;i++) {
- x |= (long)r_byte(arg) << (8*i);
- }
+ if (4 < c && c < 128) {
+ return c - 5;
+ }
+ if (c > (int)sizeof(long)) long_toobig(c);
+ x = 0;
+ for (i=0;i<c;i++) {
+ x |= (long)r_byte(arg) << (8*i);
+ }
}
else {
- if (-129 < c && c < -4) {
- return c + 5;
- }
- c = -c;
- if (c > (int)sizeof(long)) long_toobig(c);
- x = -1;
- for (i=0;i<c;i++) {
- x &= ~((long)0xff << (8*i));
- x |= (long)r_byte(arg) << (8*i);
- }
+ if (-129 < c && c < -4) {
+ return c + 5;
+ }
+ c = -c;
+ if (c > (int)sizeof(long)) long_toobig(c);
+ x = -1;
+ for (i=0;i<c;i++) {
+ x &= ~((long)0xff << (8*i));
+ x |= (long)r_byte(arg) << (8*i);
+ }
}
return x;
}
@@ -1351,39 +1426,39 @@ r_bytes1_buffered(long len, struct load_arg *arg)
VALUE str;
if (len <= arg->buflen) {
- str = rb_str_new(arg->buf+arg->offset, len);
- arg->offset += len;
- arg->buflen -= len;
+ str = rb_str_new(arg->buf+arg->offset, len);
+ arg->offset += len;
+ arg->buflen -= len;
}
else {
- long buflen = arg->buflen;
- long readable = arg->readable + 1;
- long tmp_len, read_len, need_len = len - buflen;
- VALUE tmp, n;
+ long buflen = arg->buflen;
+ long readable = arg->readable + 1;
+ long tmp_len, read_len, need_len = len - buflen;
+ VALUE tmp, n;
- readable = readable < BUFSIZ ? readable : BUFSIZ;
- read_len = need_len > readable ? need_len : readable;
- n = LONG2NUM(read_len);
- tmp = load_funcall(arg, arg->src, s_read, 1, &n);
- if (NIL_P(tmp)) too_short();
- StringValue(tmp);
+ readable = readable < BUFSIZ ? readable : BUFSIZ;
+ read_len = need_len > readable ? need_len : readable;
+ n = LONG2NUM(read_len);
+ tmp = load_funcall(arg, arg->src, s_read, 1, &n);
+ if (NIL_P(tmp)) too_short();
+ StringValue(tmp);
- tmp_len = RSTRING_LEN(tmp);
+ tmp_len = RSTRING_LEN(tmp);
- if (tmp_len < need_len) too_short();
+ if (tmp_len < need_len) too_short();
- str = rb_str_new(arg->buf+arg->offset, buflen);
- rb_str_cat(str, RSTRING_PTR(tmp), need_len);
+ str = rb_str_new(arg->buf+arg->offset, buflen);
+ rb_str_cat(str, RSTRING_PTR(tmp), need_len);
- if (tmp_len > need_len) {
- buflen = tmp_len - need_len;
- memcpy(arg->buf, RSTRING_PTR(tmp)+need_len, buflen);
- arg->buflen = buflen;
- }
- else {
- arg->buflen = 0;
- }
- arg->offset = 0;
+ if (tmp_len > need_len) {
+ buflen = tmp_len - need_len;
+ memcpy(arg->buf, RSTRING_PTR(tmp)+need_len, buflen);
+ arg->buflen = buflen;
+ }
+ else {
+ arg->buflen = 0;
+ }
+ arg->offset = 0;
}
return str;
@@ -1398,21 +1473,21 @@ r_bytes0(long len, struct load_arg *arg)
if (len == 0) return rb_str_new(0, 0);
if (RB_TYPE_P(arg->src, T_STRING)) {
- if (RSTRING_LEN(arg->src) - arg->offset >= len) {
- str = rb_str_new(RSTRING_PTR(arg->src)+arg->offset, len);
- arg->offset += len;
- }
- else {
- too_short();
- }
+ if (RSTRING_LEN(arg->src) - arg->offset >= len) {
+ str = rb_str_new(RSTRING_PTR(arg->src)+arg->offset, len);
+ arg->offset += len;
+ }
+ else {
+ too_short();
+ }
}
else {
- if (arg->readable > 0 || arg->buflen > 0) {
- str = r_bytes1_buffered(len, arg);
- }
- else {
- str = r_bytes1(len, arg);
- }
+ if (arg->readable > 0 || arg->buflen > 0) {
+ str = r_bytes1_buffered(len, arg);
+ }
+ else {
+ str = r_bytes1(len, arg);
+ }
}
return str;
}
@@ -1427,20 +1502,20 @@ name_equal(const char *name, size_t nlen, const char *p, long l)
static int
sym2encidx(VALUE sym, VALUE val)
{
- static const char name_encoding[8] = "encoding";
+ RBIMPL_ATTR_NONSTRING() static const char name_encoding[8] = "encoding";
const char *p;
long l;
if (rb_enc_get_index(sym) != ENCINDEX_US_ASCII) return -1;
RSTRING_GETMEM(sym, p, l);
if (l <= 0) return -1;
if (name_equal(name_encoding, sizeof(name_encoding), p, l)) {
- int idx = rb_enc_find_index(StringValueCStr(val));
- return idx;
+ int idx = rb_enc_find_index(StringValueCStr(val));
+ return idx;
}
if (name_equal(name_s_encoding_short, rb_strlen_lit(name_s_encoding_short), p, l)) {
- if (val == Qfalse) return rb_usascii_encindex();
- else if (val == Qtrue) return rb_utf8_encindex();
- /* bogus ignore */
+ if (val == Qfalse) return rb_usascii_encindex();
+ else if (val == Qtrue) return rb_utf8_encindex();
+ /* bogus ignore */
}
return -1;
}
@@ -1468,7 +1543,7 @@ r_symlink(struct load_arg *arg)
long num = r_long(arg);
if (!st_lookup(arg->symbols, num, &sym)) {
- rb_raise(rb_eArgError, "bad symbol");
+ rb_raise(rb_eArgError, "bad symbol");
}
return (VALUE)sym;
}
@@ -1484,15 +1559,15 @@ r_symreal(struct load_arg *arg, int ivar)
if (rb_enc_str_asciionly_p(s)) rb_enc_associate_index(s, ENCINDEX_US_ASCII);
st_insert(arg->symbols, (st_data_t)n, (st_data_t)s);
if (ivar) {
- long num = r_long(arg);
- while (num-- > 0) {
- sym = r_symbol(arg);
- idx = sym2encidx(sym, r_object(arg));
- }
+ long num = r_long(arg);
+ while (num-- > 0) {
+ sym = r_symbol(arg);
+ idx = sym2encidx(sym, r_object(arg));
+ }
}
if (idx > 0) {
rb_enc_associate_index(s, idx);
- if (rb_enc_str_coderange(s) == ENC_CODERANGE_BROKEN) {
+ if (is_broken_string(s)) {
rb_raise(rb_eArgError, "invalid byte sequence in %s: %+"PRIsVALUE,
rb_enc_name(rb_enc_from_index(idx)), s);
}
@@ -1509,17 +1584,17 @@ r_symbol(struct load_arg *arg)
again:
switch ((type = r_byte(arg))) {
default:
- rb_raise(rb_eArgError, "dump format error for symbol(0x%x)", type);
+ rb_raise(rb_eArgError, "dump format error for symbol(0x%x)", type);
case TYPE_IVAR:
- ivar = 1;
- goto again;
+ ivar = 1;
+ goto again;
case TYPE_SYMBOL:
- return r_symreal(arg, ivar);
+ return r_symreal(arg, ivar);
case TYPE_SYMLINK:
- if (ivar) {
- rb_raise(rb_eArgError, "dump format error (symlink with encoding)");
- }
- return r_symlink(arg);
+ if (ivar) {
+ rb_raise(rb_eArgError, "dump format error (symlink with encoding)");
+ }
+ return r_symlink(arg);
}
}
@@ -1569,7 +1644,7 @@ static VALUE
r_post_proc(VALUE v, struct load_arg *arg)
{
if (arg->proc) {
- v = load_funcall(arg, arg->proc, s_call, 1, &v);
+ v = load_funcall(arg, arg->proc, s_call, 1, &v);
}
return v;
}
@@ -1579,21 +1654,21 @@ r_leave(VALUE v, struct load_arg *arg, bool partial)
{
v = r_fixup_compat(v, arg);
if (!partial) {
- st_data_t data;
- st_data_t key = (st_data_t)v;
- st_delete(arg->partial_objects, &key, &data);
- if (arg->freeze) {
- if (RB_TYPE_P(v, T_MODULE) || RB_TYPE_P(v, T_CLASS)) {
- // noop
- }
- else if (RB_TYPE_P(v, T_STRING)) {
- v = rb_str_to_interned_str(v);
- }
- else {
- OBJ_FREEZE(v);
- }
- }
- v = r_post_proc(v, arg);
+ st_data_t data;
+ st_data_t key = (st_data_t)v;
+ st_delete(arg->partial_objects, &key, &data);
+ if (arg->freeze) {
+ if (RB_TYPE_P(v, T_MODULE) || RB_TYPE_P(v, T_CLASS)) {
+ // noop
+ }
+ else if (RB_TYPE_P(v, T_STRING)) {
+ v = rb_str_to_interned_str(v);
+ }
+ else {
+ OBJ_FREEZE(v);
+ }
+ }
+ v = r_post_proc(v, arg);
}
return v;
}
@@ -1605,7 +1680,7 @@ copy_ivar_i(st_data_t key, st_data_t val, st_data_t arg)
ID vid = (ID)key;
if (!rb_ivar_defined(obj, vid))
- rb_ivar_set(obj, vid, value);
+ rb_ivar_set(obj, vid, value);
return ST_CONTINUE;
}
@@ -1623,20 +1698,20 @@ r_ivar(VALUE obj, int *has_encoding, struct load_arg *arg)
len = r_long(arg);
if (len > 0) {
- do {
- VALUE sym = r_symbol(arg);
- VALUE val = r_object(arg);
- int idx = sym2encidx(sym, val);
- if (idx >= 0) {
+ do {
+ VALUE sym = r_symbol(arg);
+ VALUE val = r_object(arg);
+ int idx = sym2encidx(sym, val);
+ if (idx >= 0) {
if (rb_enc_capable(obj)) {
rb_enc_associate_index(obj, idx);
}
else {
rb_raise(rb_eArgError, "%"PRIsVALUE" is not enc_capable", obj);
}
- if (has_encoding) *has_encoding = TRUE;
- }
- else if (symname_equal_lit(sym, name_s_ruby2_keywords_flag)) {
+ if (has_encoding) *has_encoding = TRUE;
+ }
+ else if (symname_equal_lit(sym, name_s_ruby2_keywords_flag)) {
if (RB_TYPE_P(obj, T_HASH)) {
rb_hash_ruby2_keywords(obj);
}
@@ -1646,8 +1721,8 @@ r_ivar(VALUE obj, int *has_encoding, struct load_arg *arg)
}
else {
rb_ivar_set(obj, rb_intern_str(sym), val);
- }
- } while (--len > 0);
+ }
+ } while (--len > 0);
}
}
@@ -1657,7 +1732,7 @@ path2class(VALUE path)
VALUE v = rb_path_to_class(path);
if (!RB_TYPE_P(v, T_CLASS)) {
- rb_raise(rb_eArgError, "%"PRIsVALUE" does not refer to class", path);
+ rb_raise(rb_eArgError, "%"PRIsVALUE" does not refer to class", path);
}
return v;
}
@@ -1668,7 +1743,7 @@ static VALUE
must_be_module(VALUE v, VALUE path)
{
if (!RB_TYPE_P(v, T_MODULE)) {
- rb_raise(rb_eArgError, "%"PRIsVALUE" does not refer to module", path);
+ rb_raise(rb_eArgError, "%"PRIsVALUE" does not refer to module", path);
}
return v;
}
@@ -1684,7 +1759,7 @@ obj_alloc_by_klass(VALUE klass, struct load_arg *arg, VALUE *oldclass)
marshal_compat_t *compat = (marshal_compat_t*)data;
VALUE real_obj = rb_obj_alloc(klass);
VALUE obj = rb_obj_alloc(compat->oldclass);
- if (oldclass) *oldclass = compat->oldclass;
+ if (oldclass) *oldclass = compat->oldclass;
if (!arg->compat_tbl) {
arg->compat_tbl = rb_init_identtable();
@@ -1707,17 +1782,17 @@ append_extmod(VALUE obj, VALUE extmod)
{
long i = RARRAY_LEN(extmod);
while (i > 0) {
- VALUE m = RARRAY_AREF(extmod, --i);
- rb_extend_object(obj, m);
+ VALUE m = RARRAY_AREF(extmod, --i);
+ rb_extend_object(obj, m);
}
return obj;
}
#define prohibit_ivar(type, str) do { \
- if (!ivp || !*ivp) break; \
- rb_raise(rb_eTypeError, \
- "can't override instance variable of "type" `%"PRIsVALUE"'", \
- (str)); \
+ if (!ivp || !*ivp) break; \
+ rb_raise(rb_eTypeError, \
+ "can't override instance variable of "type" `%"PRIsVALUE"'", \
+ (str)); \
} while (0)
static VALUE r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int type);
@@ -1729,6 +1804,20 @@ r_object0(struct load_arg *arg, bool partial, int *ivp, VALUE extmod)
return r_object_for(arg, partial, ivp, extmod, type);
}
+static int
+r_move_ivar(st_data_t k, st_data_t v, st_data_t d)
+{
+ ID key = (ID)k;
+ VALUE value = (VALUE)v;
+ VALUE dest = (VALUE)d;
+
+ if (rb_is_instance_id(key)) {
+ rb_ivar_set(dest, key, value);
+ return ST_DELETE;
+ }
+ return ST_CONTINUE;
+}
+
static VALUE
r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int type)
{
@@ -1739,437 +1828,458 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ
switch (type) {
case TYPE_LINK:
- id = r_long(arg);
- if (!st_lookup(arg->data, (st_data_t)id, &link)) {
- rb_raise(rb_eArgError, "dump format error (unlinked)");
- }
- v = (VALUE)link;
- if (!st_lookup(arg->partial_objects, (st_data_t)v, &link)) {
- v = r_post_proc(v, arg);
- }
- break;
+ id = r_long(arg);
+ if (!st_lookup(arg->data, (st_data_t)id, &link)) {
+ rb_raise(rb_eArgError, "dump format error (unlinked)");
+ }
+ v = (VALUE)link;
+ if (!st_lookup(arg->partial_objects, (st_data_t)v, &link)) {
+ v = r_post_proc(v, arg);
+ }
+ break;
case TYPE_IVAR:
{
- int ivar = TRUE;
-
- v = r_object0(arg, true, &ivar, extmod);
- if (ivar) r_ivar(v, NULL, arg);
- v = r_leave(v, arg, partial);
- }
- break;
+ int ivar = TRUE;
+ v = r_object0(arg, true, &ivar, extmod);
+ if (ivar) r_ivar(v, NULL, arg);
+ v = r_leave(v, arg, partial);
+ }
+ break;
case TYPE_EXTENDED:
- {
- VALUE path = r_unique(arg);
- VALUE m = rb_path_to_class(path);
- if (NIL_P(extmod)) extmod = rb_ary_tmp_new(0);
-
- if (RB_TYPE_P(m, T_CLASS)) { /* prepended */
- VALUE c;
-
- v = r_object0(arg, true, 0, Qnil);
- c = CLASS_OF(v);
- if (c != m || FL_TEST(c, FL_SINGLETON)) {
- rb_raise(rb_eArgError,
- "prepended class %"PRIsVALUE" differs from class %"PRIsVALUE,
- path, rb_class_name(c));
- }
- c = rb_singleton_class(v);
- while (RARRAY_LEN(extmod) > 0) {
- m = rb_ary_pop(extmod);
- rb_prepend_module(c, m);
- }
- }
- else {
- must_be_module(m, path);
- rb_ary_push(extmod, m);
-
- v = r_object0(arg, true, 0, extmod);
- while (RARRAY_LEN(extmod) > 0) {
- m = rb_ary_pop(extmod);
- rb_extend_object(v, m);
- }
- }
- }
- break;
+ {
+ VALUE path = r_unique(arg);
+ VALUE m = rb_path_to_class(path);
+ if (NIL_P(extmod)) extmod = rb_ary_hidden_new(0);
+
+ if (RB_TYPE_P(m, T_CLASS)) { /* prepended */
+ VALUE c;
+
+ v = r_object0(arg, true, 0, Qnil);
+ c = CLASS_OF(v);
+ if (c != m || FL_TEST(c, FL_SINGLETON)) {
+ rb_raise(rb_eArgError,
+ "prepended class %"PRIsVALUE" differs from class %"PRIsVALUE,
+ path, rb_class_name(c));
+ }
+ c = rb_singleton_class(v);
+ while (RARRAY_LEN(extmod) > 0) {
+ m = rb_ary_pop(extmod);
+ rb_prepend_module(c, m);
+ }
+ }
+ else {
+ must_be_module(m, path);
+ rb_ary_push(extmod, m);
+
+ v = r_object0(arg, true, 0, extmod);
+ while (RARRAY_LEN(extmod) > 0) {
+ m = rb_ary_pop(extmod);
+ rb_extend_object(v, m);
+ }
+ }
+ }
+ break;
case TYPE_UCLASS:
- {
- VALUE c = path2class(r_unique(arg));
-
- if (FL_TEST(c, FL_SINGLETON)) {
- rb_raise(rb_eTypeError, "singleton can't be loaded");
- }
- type = r_byte(arg);
- if ((c == rb_cHash) &&
- /* Hack for compare_by_identify */
- (type == TYPE_HASH || type == TYPE_HASH_DEF)) {
- hash_new_with_size = rb_ident_hash_new_with_size;
- goto type_hash;
- }
- v = r_object_for(arg, partial, 0, extmod, type);
- if (rb_special_const_p(v) || RB_TYPE_P(v, T_OBJECT) || RB_TYPE_P(v, T_CLASS)) {
+ {
+ VALUE c = path2class(r_unique(arg));
+
+ if (FL_TEST(c, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "singleton can't be loaded");
+ }
+ type = r_byte(arg);
+ if ((c == rb_cHash) &&
+ /* Hack for compare_by_identify */
+ (type == TYPE_HASH || type == TYPE_HASH_DEF)) {
+ hash_new_with_size = rb_ident_hash_new_with_size;
+ goto type_hash;
+ }
+ v = r_object_for(arg, partial, 0, extmod, type);
+ if (rb_special_const_p(v) || RB_TYPE_P(v, T_OBJECT) || RB_TYPE_P(v, T_CLASS)) {
goto format_error;
- }
- if (RB_TYPE_P(v, T_MODULE) || !RTEST(rb_class_inherited_p(c, RBASIC(v)->klass))) {
- VALUE tmp = rb_obj_alloc(c);
+ }
+ if (RB_TYPE_P(v, T_MODULE) || !RTEST(rb_class_inherited_p(c, RBASIC(v)->klass))) {
+ VALUE tmp = rb_obj_alloc(c);
- if (TYPE(v) != TYPE(tmp)) goto format_error;
- }
- RBASIC_SET_CLASS(v, c);
- }
- break;
+ if (TYPE(v) != TYPE(tmp)) goto format_error;
+ }
+ RBASIC_SET_CLASS(v, c);
+ }
+ break;
format_error:
rb_raise(rb_eArgError, "dump format error (user class)");
case TYPE_NIL:
- v = Qnil;
- v = r_leave(v, arg, false);
- break;
+ v = Qnil;
+ v = r_leave(v, arg, false);
+ break;
case TYPE_TRUE:
- v = Qtrue;
- v = r_leave(v, arg, false);
- break;
+ v = Qtrue;
+ v = r_leave(v, arg, false);
+ break;
case TYPE_FALSE:
- v = Qfalse;
- v = r_leave(v, arg, false);
- break;
+ v = Qfalse;
+ v = r_leave(v, arg, false);
+ break;
case TYPE_FIXNUM:
- {
- long i = r_long(arg);
- v = LONG2FIX(i);
- }
- v = r_leave(v, arg, false);
- break;
+ {
+ long i = r_long(arg);
+ v = LONG2FIX(i);
+ }
+ v = r_leave(v, arg, false);
+ break;
case TYPE_FLOAT:
- {
- double d;
- VALUE str = r_bytes(arg);
- const char *ptr = RSTRING_PTR(str);
-
- if (strcmp(ptr, "nan") == 0) {
- d = nan("");
- }
- else if (strcmp(ptr, "inf") == 0) {
- d = HUGE_VAL;
- }
- else if (strcmp(ptr, "-inf") == 0) {
- d = -HUGE_VAL;
- }
- else {
- char *e;
- d = strtod(ptr, &e);
- d = load_mantissa(d, e, RSTRING_LEN(str) - (e - ptr));
- }
- v = DBL2NUM(d);
- v = r_entry(v, arg);
+ {
+ double d;
+ VALUE str = r_bytes(arg);
+ const char *ptr = RSTRING_PTR(str);
+
+ if (strcmp(ptr, "nan") == 0) {
+ d = nan("");
+ }
+ else if (strcmp(ptr, "inf") == 0) {
+ d = HUGE_VAL;
+ }
+ else if (strcmp(ptr, "-inf") == 0) {
+ d = -HUGE_VAL;
+ }
+ else {
+ char *e;
+ d = strtod(ptr, &e);
+ d = load_mantissa(d, e, RSTRING_LEN(str) - (e - ptr));
+ }
+ v = DBL2NUM(d);
+ v = r_entry(v, arg);
v = r_leave(v, arg, false);
- }
- break;
+ }
+ break;
case TYPE_BIGNUM:
- {
- long len;
- VALUE data;
+ {
+ long len;
+ VALUE data;
int sign;
- sign = r_byte(arg);
- len = r_long(arg);
- data = r_bytes0(len * 2, arg);
- v = rb_integer_unpack(RSTRING_PTR(data), len, 2, 0,
- INTEGER_PACK_LITTLE_ENDIAN | (sign == '-' ? INTEGER_PACK_NEGATIVE : 0));
- rb_str_resize(data, 0L);
- v = r_entry(v, arg);
+ sign = r_byte(arg);
+ len = r_long(arg);
+
+ if (SIZEOF_VALUE >= 8 && len <= 4) {
+ // Representable within uintptr, likely FIXNUM
+ VALUE num = 0;
+ for (int i = 0; i < len; i++) {
+ num |= (VALUE)r_byte(arg) << (i * 16);
+ num |= (VALUE)r_byte(arg) << (i * 16 + 8);
+ }
+#if SIZEOF_VALUE == SIZEOF_LONG
+ v = ULONG2NUM(num);
+#else
+ v = ULL2NUM(num);
+#endif
+ if (sign == '-') {
+ v = rb_int_uminus(v);
+ }
+ }
+ else {
+ data = r_bytes0(len * 2, arg);
+ v = rb_integer_unpack(RSTRING_PTR(data), len, 2, 0,
+ INTEGER_PACK_LITTLE_ENDIAN | (sign == '-' ? INTEGER_PACK_NEGATIVE : 0));
+ rb_str_resize(data, 0L);
+ }
+ v = r_entry(v, arg);
v = r_leave(v, arg, false);
- }
- break;
+ }
+ break;
case TYPE_STRING:
- v = r_entry(r_string(arg), arg);
- v = r_leave(v, arg, partial);
- break;
+ v = r_entry(r_string(arg), arg);
+ v = r_leave(v, arg, partial);
+ break;
case TYPE_REGEXP:
- {
- VALUE str = r_bytes(arg);
- int options = r_byte(arg);
- int has_encoding = FALSE;
- st_index_t idx = r_prepare(arg);
-
- if (ivp) {
- r_ivar(str, &has_encoding, arg);
- *ivp = FALSE;
- }
- if (!has_encoding) {
- /* 1.8 compatibility; remove escapes undefined in 1.8 */
- char *ptr = RSTRING_PTR(str), *dst = ptr, *src = ptr;
- long len = RSTRING_LEN(str);
- long bs = 0;
- for (; len-- > 0; *dst++ = *src++) {
- switch (*src) {
- case '\\': bs++; break;
- case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'o': case 'p': case 'q': case 'u': case 'y':
- case 'E': case 'F': case 'H': case 'I': case 'J': case 'K':
- case 'L': case 'N': case 'O': case 'P': case 'Q': case 'R':
- case 'S': case 'T': case 'U': case 'V': case 'X': case 'Y':
- if (bs & 1) --dst;
+ {
+ VALUE str = r_bytes(arg);
+ int options = r_byte(arg);
+ int has_encoding = FALSE;
+ st_index_t idx = r_prepare(arg);
+
+ if (ivp) {
+ r_ivar(str, &has_encoding, arg);
+ *ivp = FALSE;
+ }
+ if (!has_encoding) {
+ /* 1.8 compatibility; remove escapes undefined in 1.8 */
+ char *ptr = RSTRING_PTR(str), *dst = ptr, *src = ptr;
+ long len = RSTRING_LEN(str);
+ long bs = 0;
+ for (; len-- > 0; *dst++ = *src++) {
+ switch (*src) {
+ case '\\': bs++; break;
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'o': case 'p': case 'q': case 'u': case 'y':
+ case 'E': case 'F': case 'H': case 'I': case 'J': case 'K':
+ case 'L': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'X': case 'Y':
+ if (bs & 1) --dst;
/* fall through */
- default: bs = 0; break;
- }
- }
- rb_str_set_len(str, dst - ptr);
- }
- v = r_entry0(rb_reg_new_str(str, options), idx, arg);
- v = r_leave(v, arg, partial);
- }
- break;
+ default: bs = 0; break;
+ }
+ }
+ rb_str_set_len(str, dst - ptr);
+ }
+ VALUE regexp = rb_reg_new_str(str, options);
+ rb_ivar_foreach(str, r_move_ivar, regexp);
+
+ v = r_entry0(regexp, idx, arg);
+ v = r_leave(v, arg, partial);
+ }
+ break;
case TYPE_ARRAY:
- {
- long len = r_long(arg);
-
- v = rb_ary_new2(len);
- v = r_entry(v, arg);
- arg->readable += len - 1;
- while (len--) {
- rb_ary_push(v, r_object(arg));
- arg->readable--;
- }
+ {
+ long len = r_long(arg);
+
+ v = rb_ary_new2(len);
+ v = r_entry(v, arg);
+ arg->readable += len - 1;
+ while (len--) {
+ rb_ary_push(v, r_object(arg));
+ arg->readable--;
+ }
v = r_leave(v, arg, partial);
- arg->readable++;
- }
- break;
+ arg->readable++;
+ }
+ break;
case TYPE_HASH:
case TYPE_HASH_DEF:
type_hash:
- {
- long len = r_long(arg);
-
- v = hash_new_with_size(len);
- v = r_entry(v, arg);
- arg->readable += (len - 1) * 2;
- while (len--) {
- VALUE key = r_object(arg);
- VALUE value = r_object(arg);
- rb_hash_aset(v, key, value);
- arg->readable -= 2;
- }
- arg->readable += 2;
- if (type == TYPE_HASH_DEF) {
- RHASH_SET_IFNONE(v, r_object(arg));
- }
+ {
+ long len = r_long(arg);
+
+ v = hash_new_with_size(len);
+ v = r_entry(v, arg);
+ arg->readable += (len - 1) * 2;
+ while (len--) {
+ VALUE key = r_object(arg);
+ VALUE value = r_object(arg);
+ rb_hash_aset(v, key, value);
+ arg->readable -= 2;
+ }
+ arg->readable += 2;
+ if (type == TYPE_HASH_DEF) {
+ RHASH_SET_IFNONE(v, r_object(arg));
+ }
v = r_leave(v, arg, partial);
- }
- break;
+ }
+ break;
case TYPE_STRUCT:
- {
- VALUE mem, values;
- long i;
- VALUE slot;
- st_index_t idx = r_prepare(arg);
- VALUE klass = path2class(r_unique(arg));
- long len = r_long(arg);
+ {
+ VALUE mem, values;
+ long i;
+ VALUE slot;
+ st_index_t idx = r_prepare(arg);
+ VALUE klass = path2class(r_unique(arg));
+ long len = r_long(arg);
v = rb_obj_alloc(klass);
- if (!RB_TYPE_P(v, T_STRUCT)) {
- rb_raise(rb_eTypeError, "class %"PRIsVALUE" not a struct", rb_class_name(klass));
- }
- mem = rb_struct_s_members(klass);
+ if (!RB_TYPE_P(v, T_STRUCT)) {
+ rb_raise(rb_eTypeError, "class %"PRIsVALUE" not a struct", rb_class_name(klass));
+ }
+ mem = rb_struct_s_members(klass);
if (RARRAY_LEN(mem) != len) {
rb_raise(rb_eTypeError, "struct %"PRIsVALUE" not compatible (struct size differs)",
rb_class_name(klass));
}
- arg->readable += (len - 1) * 2;
- v = r_entry0(v, idx, arg);
- values = rb_ary_new2(len);
- {
- VALUE keywords = Qfalse;
- if (RTEST(rb_struct_s_keyword_init(klass))) {
- keywords = rb_hash_new();
- rb_ary_push(values, keywords);
- }
-
- for (i=0; i<len; i++) {
- VALUE n = rb_sym2str(RARRAY_AREF(mem, i));
- slot = r_symbol(arg);
-
- if (!rb_str_equal(n, slot)) {
- rb_raise(rb_eTypeError, "struct %"PRIsVALUE" not compatible (:%"PRIsVALUE" for :%"PRIsVALUE")",
- rb_class_name(klass),
- slot, n);
- }
- if (keywords) {
- rb_hash_aset(keywords, RARRAY_AREF(mem, i), r_object(arg));
- }
- else {
- rb_ary_push(values, r_object(arg));
- }
- arg->readable -= 2;
- }
- }
+ arg->readable += (len - 1) * 2;
+ v = r_entry0(v, idx, arg);
+ values = rb_ary_new2(len);
+ {
+ VALUE keywords = Qfalse;
+ if (RTEST(rb_struct_s_keyword_init(klass))) {
+ keywords = rb_hash_new();
+ rb_ary_push(values, keywords);
+ }
+
+ for (i=0; i<len; i++) {
+ VALUE n = rb_sym2str(RARRAY_AREF(mem, i));
+ slot = r_symbol(arg);
+
+ if (!rb_str_equal(n, slot)) {
+ rb_raise(rb_eTypeError, "struct %"PRIsVALUE" not compatible (:%"PRIsVALUE" for :%"PRIsVALUE")",
+ rb_class_name(klass),
+ slot, n);
+ }
+ if (keywords) {
+ rb_hash_aset(keywords, RARRAY_AREF(mem, i), r_object(arg));
+ }
+ else {
+ rb_ary_push(values, r_object(arg));
+ }
+ arg->readable -= 2;
+ }
+ }
rb_struct_initialize(v, values);
v = r_leave(v, arg, partial);
- arg->readable += 2;
- }
- break;
+ arg->readable += 2;
+ }
+ break;
case TYPE_USERDEF:
{
- VALUE name = r_unique(arg);
- VALUE klass = path2class(name);
- VALUE data;
- st_data_t d;
-
- if (!rb_obj_respond_to(klass, s_load, TRUE)) {
- rb_raise(rb_eTypeError, "class %"PRIsVALUE" needs to have method `_load'",
- name);
- }
- data = r_string(arg);
- if (ivp) {
- r_ivar(data, NULL, arg);
- *ivp = FALSE;
- }
- v = load_funcall(arg, klass, s_load, 1, &data);
- v = r_entry(v, arg);
- if (st_lookup(compat_allocator_tbl, (st_data_t)rb_get_alloc_func(klass), &d)) {
- marshal_compat_t *compat = (marshal_compat_t*)d;
- v = compat->loader(klass, v);
- }
- if (!partial) v = r_post_proc(v, arg);
- }
+ VALUE name = r_unique(arg);
+ VALUE klass = path2class(name);
+ VALUE data;
+ st_data_t d;
+
+ if (!rb_obj_respond_to(klass, s_load, TRUE)) {
+ rb_raise(rb_eTypeError, "class %"PRIsVALUE" needs to have method `_load'",
+ name);
+ }
+ data = r_string(arg);
+ if (ivp) {
+ r_ivar(data, NULL, arg);
+ *ivp = FALSE;
+ }
+ v = load_funcall(arg, klass, s_load, 1, &data);
+ v = r_entry(v, arg);
+ if (st_lookup(compat_allocator_tbl, (st_data_t)rb_get_alloc_func(klass), &d)) {
+ marshal_compat_t *compat = (marshal_compat_t*)d;
+ v = compat->loader(klass, v);
+ }
+ if (!partial) v = r_post_proc(v, arg);
+ }
break;
case TYPE_USRMARSHAL:
{
- VALUE name = r_unique(arg);
- VALUE klass = path2class(name);
- VALUE oldclass = 0;
- VALUE data;
+ VALUE name = r_unique(arg);
+ VALUE klass = path2class(name);
+ VALUE oldclass = 0;
+ VALUE data;
- v = obj_alloc_by_klass(klass, arg, &oldclass);
+ v = obj_alloc_by_klass(klass, arg, &oldclass);
+ if (!NIL_P(extmod)) {
+ /* for the case marshal_load is overridden */
+ append_extmod(v, extmod);
+ }
+ if (!rb_obj_respond_to(v, s_mload, TRUE)) {
+ rb_raise(rb_eTypeError, "instance of %"PRIsVALUE" needs to have method `marshal_load'",
+ name);
+ }
+ v = r_entry(v, arg);
+ data = r_object(arg);
+ load_funcall(arg, v, s_mload, 1, &data);
+ v = r_fixup_compat(v, arg);
+ v = r_copy_ivar(v, data);
+ v = r_post_proc(v, arg);
if (!NIL_P(extmod)) {
- /* for the case marshal_load is overridden */
- append_extmod(v, extmod);
+ if (oldclass) append_extmod(v, extmod);
+ rb_ary_clear(extmod);
}
- if (!rb_obj_respond_to(v, s_mload, TRUE)) {
- rb_raise(rb_eTypeError, "instance of %"PRIsVALUE" needs to have method `marshal_load'",
- name);
- }
- v = r_entry(v, arg);
- data = r_object(arg);
- load_funcall(arg, v, s_mload, 1, &data);
- v = r_fixup_compat(v, arg);
- v = r_copy_ivar(v, data);
- v = r_post_proc(v, arg);
- if (!NIL_P(extmod)) {
- if (oldclass) append_extmod(v, extmod);
- rb_ary_clear(extmod);
- }
- }
+ }
break;
case TYPE_OBJECT:
- {
- st_index_t idx = r_prepare(arg);
+ {
+ st_index_t idx = r_prepare(arg);
v = obj_alloc_by_path(r_unique(arg), arg);
- if (!RB_TYPE_P(v, T_OBJECT)) {
- rb_raise(rb_eArgError, "dump format error");
- }
- v = r_entry0(v, idx, arg);
- r_ivar(v, NULL, arg);
- v = r_leave(v, arg, partial);
- }
- break;
+ if (!RB_TYPE_P(v, T_OBJECT)) {
+ rb_raise(rb_eArgError, "dump format error");
+ }
+ v = r_entry0(v, idx, arg);
+ r_ivar(v, NULL, arg);
+ v = r_leave(v, arg, partial);
+ }
+ break;
case TYPE_DATA:
- {
- VALUE name = r_unique(arg);
- VALUE klass = path2class(name);
- VALUE oldclass = 0;
- VALUE r;
-
- v = obj_alloc_by_klass(klass, arg, &oldclass);
- if (!RB_TYPE_P(v, T_DATA)) {
- rb_raise(rb_eArgError, "dump format error");
- }
- v = r_entry(v, arg);
- if (!rb_obj_respond_to(v, s_load_data, TRUE)) {
- rb_raise(rb_eTypeError,
- "class %"PRIsVALUE" needs to have instance method `_load_data'",
- name);
- }
- r = r_object0(arg, partial, 0, extmod);
- load_funcall(arg, v, s_load_data, 1, &r);
- v = r_leave(v, arg, partial);
- }
- break;
+ {
+ VALUE name = r_unique(arg);
+ VALUE klass = path2class(name);
+ VALUE oldclass = 0;
+ VALUE r;
+
+ v = obj_alloc_by_klass(klass, arg, &oldclass);
+ if (!RB_TYPE_P(v, T_DATA)) {
+ rb_raise(rb_eArgError, "dump format error");
+ }
+ v = r_entry(v, arg);
+ if (!rb_obj_respond_to(v, s_load_data, TRUE)) {
+ rb_raise(rb_eTypeError,
+ "class %"PRIsVALUE" needs to have instance method `_load_data'",
+ name);
+ }
+ r = r_object0(arg, partial, 0, extmod);
+ load_funcall(arg, v, s_load_data, 1, &r);
+ v = r_leave(v, arg, partial);
+ }
+ break;
case TYPE_MODULE_OLD:
{
- VALUE str = r_bytes(arg);
+ VALUE str = r_bytes(arg);
- v = rb_path_to_class(str);
- prohibit_ivar("class/module", str);
- v = r_entry(v, arg);
+ v = rb_path_to_class(str);
+ prohibit_ivar("class/module", str);
+ v = r_entry(v, arg);
v = r_leave(v, arg, partial);
- }
- break;
+ }
+ break;
case TYPE_CLASS:
{
- VALUE str = r_bytes(arg);
+ VALUE str = r_bytes(arg);
- v = path2class(str);
- prohibit_ivar("class", str);
- v = r_entry(v, arg);
+ v = path2class(str);
+ prohibit_ivar("class", str);
+ v = r_entry(v, arg);
v = r_leave(v, arg, partial);
- }
- break;
+ }
+ break;
case TYPE_MODULE:
{
- VALUE str = r_bytes(arg);
+ VALUE str = r_bytes(arg);
- v = path2module(str);
- prohibit_ivar("module", str);
- v = r_entry(v, arg);
+ v = path2module(str);
+ prohibit_ivar("module", str);
+ v = r_entry(v, arg);
v = r_leave(v, arg, partial);
- }
- break;
+ }
+ break;
case TYPE_SYMBOL:
- if (ivp) {
- v = r_symreal(arg, *ivp);
- *ivp = FALSE;
- }
- else {
- v = r_symreal(arg, 0);
- }
- v = rb_str_intern(v);
- v = r_leave(v, arg, partial);
- break;
+ if (ivp) {
+ v = r_symreal(arg, *ivp);
+ *ivp = FALSE;
+ }
+ else {
+ v = r_symreal(arg, 0);
+ }
+ v = rb_str_intern(v);
+ v = r_leave(v, arg, partial);
+ break;
case TYPE_SYMLINK:
- v = rb_str_intern(r_symlink(arg));
- break;
+ v = rb_str_intern(r_symlink(arg));
+ break;
default:
- rb_raise(rb_eArgError, "dump format error(0x%x)", type);
- break;
+ rb_raise(rb_eArgError, "dump format error(0x%x)", type);
+ break;
}
- if (v == Qundef) {
- rb_raise(rb_eArgError, "dump format error (bad link)");
+ if (UNDEF_P(v)) {
+ rb_raise(rb_eArgError, "dump format error (bad link)");
}
return v;
@@ -2185,8 +2295,8 @@ static void
clear_load_arg(struct load_arg *arg)
{
if (arg->buf) {
- xfree(arg->buf);
- arg->buf = 0;
+ xfree(arg->buf);
+ arg->buf = 0;
}
arg->buflen = 0;
arg->offset = 0;
@@ -2199,8 +2309,8 @@ clear_load_arg(struct load_arg *arg)
st_free_table(arg->partial_objects);
arg->partial_objects = 0;
if (arg->compat_tbl) {
- st_free_table(arg->compat_tbl);
- arg->compat_tbl = 0;
+ st_free_table(arg->compat_tbl);
+ arg->compat_tbl = 0;
}
}
@@ -2214,13 +2324,13 @@ rb_marshal_load_with_proc(VALUE port, VALUE proc, bool freeze)
v = rb_check_string_type(port);
if (!NIL_P(v)) {
- port = v;
+ port = v;
}
else if (rb_respond_to(port, s_getbyte) && rb_respond_to(port, s_read)) {
- rb_check_funcall(port, s_binmode, 0, 0);
+ rb_check_funcall(port, s_binmode, 0, 0);
}
else {
- io_needed();
+ io_needed();
}
wrapper = TypedData_Make_Struct(0, struct load_arg, &load_arg_data, arg);
arg->src = port;
@@ -2234,22 +2344,22 @@ rb_marshal_load_with_proc(VALUE port, VALUE proc, bool freeze)
arg->freeze = freeze;
if (NIL_P(v))
- arg->buf = xmalloc(BUFSIZ);
+ arg->buf = xmalloc(BUFSIZ);
else
- arg->buf = 0;
+ arg->buf = 0;
major = r_byte(arg);
minor = r_byte(arg);
if (major != MARSHAL_MAJOR || minor > MARSHAL_MINOR) {
- clear_load_arg(arg);
- rb_raise(rb_eTypeError, "incompatible marshal file format (can't be read)\n\
+ clear_load_arg(arg);
+ rb_raise(rb_eTypeError, "incompatible marshal file format (can't be read)\n\
\tformat version %d.%d required; %d.%d given",
- MARSHAL_MAJOR, MARSHAL_MINOR, major, minor);
+ MARSHAL_MAJOR, MARSHAL_MINOR, major, minor);
}
if (RTEST(ruby_verbose) && minor != MARSHAL_MINOR) {
- rb_warn("incompatible marshal file format (can be read)\n\
+ rb_warn("incompatible marshal file format (can be read)\n\
\tformat version %d.%d required; %d.%d given",
- MARSHAL_MAJOR, MARSHAL_MINOR, major, minor);
+ MARSHAL_MAJOR, MARSHAL_MINOR, major, minor);
}
if (!NIL_P(proc)) arg->proc = proc;
@@ -2260,7 +2370,8 @@ rb_marshal_load_with_proc(VALUE port, VALUE proc, bool freeze)
return v;
}
-static VALUE marshal_load(rb_execution_context_t *ec, VALUE mod, VALUE source, VALUE proc, VALUE freeze)
+static VALUE
+marshal_load(rb_execution_context_t *ec, VALUE mod, VALUE source, VALUE proc, VALUE freeze)
{
return rb_marshal_load_with_proc(source, proc, RTEST(freeze));
}
@@ -2414,7 +2525,7 @@ compat_allocator_table(void)
#undef RUBY_UNTYPED_DATA_WARNING
#define RUBY_UNTYPED_DATA_WARNING 0
compat_allocator_tbl_wrapper =
- Data_Wrap_Struct(0, mark_marshal_compat_t, 0, compat_allocator_tbl);
+ Data_Wrap_Struct(0, mark_marshal_compat_t, 0, compat_allocator_tbl);
rb_gc_register_mark_object(compat_allocator_tbl_wrapper);
return compat_allocator_tbl;
}
diff --git a/math.c b/math.c
index 4996718b10..67bf8b7b63 100644
--- a/math.c
+++ b/math.c
@@ -65,23 +65,23 @@ math_atan2(VALUE unused_obj, VALUE y, VALUE x)
dx = Get_Double(x);
dy = Get_Double(y);
if (dx == 0.0 && dy == 0.0) {
- if (!signbit(dx))
- return DBL2NUM(dy);
+ if (!signbit(dx))
+ return DBL2NUM(dy);
if (!signbit(dy))
- return DBL2NUM(M_PI);
- return DBL2NUM(-M_PI);
+ return DBL2NUM(M_PI);
+ return DBL2NUM(-M_PI);
}
#ifndef ATAN2_INF_C99
if (isinf(dx) && isinf(dy)) {
- /* optimization for FLONUM */
- if (dx < 0.0) {
- const double dz = (3.0 * M_PI / 4.0);
- return (dy < 0.0) ? DBL2NUM(-dz) : DBL2NUM(dz);
- }
- else {
- const double dz = (M_PI / 4.0);
- return (dy < 0.0) ? DBL2NUM(-dz) : DBL2NUM(dz);
- }
+ /* optimization for FLONUM */
+ if (dx < 0.0) {
+ const double dz = (3.0 * M_PI / 4.0);
+ return (dy < 0.0) ? DBL2NUM(-dz) : DBL2NUM(dz);
+ }
+ else {
+ const double dz = (M_PI / 4.0);
+ return (dy < 0.0) ? DBL2NUM(-dz) : DBL2NUM(dz);
+ }
}
#endif
return DBL2NUM(atan2(dy, dx));
@@ -520,7 +520,7 @@ rb_math_log(int argc, const VALUE *argv)
rb_scan_args(argc, argv, "11", &x, &base);
d = math_log1(x);
if (argc == 2) {
- d /= math_log1(base);
+ d /= math_log1(base);
}
return DBL2NUM(d);
}
@@ -536,7 +536,7 @@ get_double_rshift(VALUE x, size_t *pnumbits)
x = rb_big_rshift(x, SIZET2NUM(numbits));
}
else {
- numbits = 0;
+ numbits = 0;
}
*pnumbits = numbits;
return Get_Double(x);
@@ -681,13 +681,13 @@ rb_math_sqrt(VALUE x)
double d;
if (RB_TYPE_P(x, T_COMPLEX)) {
- VALUE neg = f_signbit(RCOMPLEX(x)->imag);
- double re = Get_Double(RCOMPLEX(x)->real), im;
- d = Get_Double(rb_complex_abs(x));
- im = sqrt((d - re) / 2.0);
- re = sqrt((d + re) / 2.0);
- if (neg) im = -im;
- return rb_complex_new(DBL2NUM(re), DBL2NUM(im));
+ VALUE neg = f_signbit(RCOMPLEX(x)->imag);
+ double re = Get_Double(RCOMPLEX(x)->real), im;
+ d = Get_Double(rb_complex_abs(x));
+ im = sqrt((d - re) / 2.0);
+ re = sqrt((d + re) / 2.0);
+ if (neg) im = -im;
+ return rb_complex_new(DBL2NUM(re), DBL2NUM(im));
}
d = Get_Double(x);
domain_check_min(d, 0.0, "sqrt");
@@ -727,7 +727,7 @@ math_cbrt(VALUE unused_obj, VALUE x)
double r = cbrt(f);
#if defined __GLIBC__
if (isfinite(r) && !(f == 0.0 && r == 0.0)) {
- r = (2.0 * r + (f / r / r)) / 3.0;
+ r = (2.0 * r + (f / r / r)) / 3.0;
}
#endif
return DBL2NUM(r);
@@ -945,17 +945,17 @@ math_gamma(VALUE unused_obj, VALUE x)
d = Get_Double(x);
/* check for domain error */
if (isinf(d)) {
- if (signbit(d)) domain_error("gamma");
- return DBL2NUM(HUGE_VAL);
+ if (signbit(d)) domain_error("gamma");
+ return DBL2NUM(HUGE_VAL);
}
if (d == 0.0) {
- return signbit(d) ? DBL2NUM(-HUGE_VAL) : DBL2NUM(HUGE_VAL);
+ return signbit(d) ? DBL2NUM(-HUGE_VAL) : DBL2NUM(HUGE_VAL);
}
if (d == floor(d)) {
- domain_check_min(d, 0.0, "gamma");
- if (1.0 <= d && d <= (double)NFACT_TABLE) {
- return DBL2NUM(fact_table[(int)d - 1]);
- }
+ domain_check_min(d, 0.0, "gamma");
+ if (1.0 <= d && d <= (double)NFACT_TABLE) {
+ return DBL2NUM(fact_table[(int)d - 1]);
+ }
}
return DBL2NUM(tgamma(d));
}
@@ -1007,12 +1007,12 @@ math_lgamma(VALUE unused_obj, VALUE x)
d = Get_Double(x);
/* check for domain error */
if (isinf(d)) {
- if (signbit(d)) domain_error("lgamma");
- return rb_assoc_new(DBL2NUM(HUGE_VAL), INT2FIX(1));
+ if (signbit(d)) domain_error("lgamma");
+ return rb_assoc_new(DBL2NUM(HUGE_VAL), INT2FIX(1));
}
if (d == 0.0) {
- VALUE vsign = signbit(d) ? INT2FIX(-1) : INT2FIX(+1);
- return rb_assoc_new(DBL2NUM(HUGE_VAL), vsign);
+ VALUE vsign = signbit(d) ? INT2FIX(-1) : INT2FIX(+1);
+ return rb_assoc_new(DBL2NUM(HUGE_VAL), vsign);
}
v = DBL2NUM(lgamma_r(d, &sign));
return rb_assoc_new(v, INT2FIX(sign));
diff --git a/memory_view.c b/memory_view.c
index 935b8d983f..3fb79202f9 100644
--- a/memory_view.c
+++ b/memory_view.c
@@ -61,9 +61,9 @@ exported_object_registry_free(void *ptr)
const rb_data_type_t rb_memory_view_exported_object_registry_data_type = {
"memory_view/exported_object_registry",
{
- exported_object_registry_mark,
- exported_object_registry_free,
- 0,
+ exported_object_registry_mark,
+ exported_object_registry_free,
+ 0,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
@@ -120,9 +120,9 @@ static ID id_memory_view;
static const rb_data_type_t memory_view_entry_data_type = {
"memory_view/entry",
{
- 0,
- 0,
- 0,
+ 0,
+ 0,
+ 0,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
diff --git a/method.h b/method.h
index 16d212a1c8..ec89bf26bb 100644
--- a/method.h
+++ b/method.h
@@ -101,8 +101,9 @@ static inline void
METHOD_ENTRY_FLAGS_COPY(rb_method_entry_t *dst, const rb_method_entry_t *src)
{
dst->flags =
- (dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2)) |
- (src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2));
+ (dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2
+ |IMEMO_FL_USER3)) |
+ (src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2|IMEMO_FL_USER3));
}
typedef enum {
@@ -178,9 +179,9 @@ typedef struct rb_method_optimized {
struct rb_method_definition_struct {
BITFIELD(rb_method_type_t, type, VM_METHOD_TYPE_MINIMUM_BITS);
unsigned int iseq_overload: 1;
- int alias_count : 27;
- int complemented_count : 28;
unsigned int no_redef_warning: 1;
+ unsigned int aliased : 1;
+ int reference_count : 28;
union {
rb_method_iseq_t iseq;
@@ -213,7 +214,7 @@ void rb_add_method_optimized(VALUE klass, ID mid, enum method_optimized_type, un
void rb_add_refined_method_entry(VALUE refined_class, ID mid);
rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_visibility_t noex);
-rb_method_entry_t *rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, const rb_method_definition_t *def);
+rb_method_entry_t *rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, rb_method_definition_t *def);
const rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id);
@@ -226,6 +227,7 @@ const rb_method_entry_t *rb_resolve_me_location(const rb_method_entry_t *, VALUE
RUBY_SYMBOL_EXPORT_END
const rb_callable_method_entry_t *rb_callable_method_entry(VALUE klass, ID id);
+const rb_callable_method_entry_t *rb_callable_method_entry_or_negative(VALUE klass, ID id);
const rb_callable_method_entry_t *rb_callable_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class);
const rb_callable_method_entry_t *rb_callable_method_entry_without_refinements(VALUE klass, ID id, VALUE *defined_class);
diff --git a/mini_builtin.c b/mini_builtin.c
index 8c8cf66263..c263d1ee71 100644
--- a/mini_builtin.c
+++ b/mini_builtin.c
@@ -36,7 +36,7 @@ builtin_iseq_load(const char *feature_name, const struct rb_builtin_function *ta
FALSE, /* unsigned int coverage_enabled; */
0, /* int debug_level; */
};
- const rb_iseq_t *iseq = rb_iseq_new_with_opt(&ast->body, name_str, name_str, Qnil, INT2FIX(0), NULL, 0, ISEQ_TYPE_TOP, &optimization);
+ const rb_iseq_t *iseq = rb_iseq_new_with_opt(&ast->body, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization);
GET_VM()->builtin_function_table = NULL;
rb_ast_dispose(ast);
diff --git a/misc/lldb_cruby.py b/misc/lldb_cruby.py
index c38b9c62a0..9ef9d3967b 100755
--- a/misc/lldb_cruby.py
+++ b/misc/lldb_cruby.py
@@ -9,15 +9,16 @@
from __future__ import print_function
import lldb
import os
+import inspect
+import sys
import shlex
import platform
+import glob
-HEAP_PAGE_ALIGN_LOG = 16
-
-HEAP_PAGE_ALIGN_MASK = (~(~0 << HEAP_PAGE_ALIGN_LOG))
-HEAP_PAGE_ALIGN = (1 << HEAP_PAGE_ALIGN_LOG)
-HEAP_PAGE_SIZE = HEAP_PAGE_ALIGN
+from lldb_rb.constants import *
+# BEGIN FUNCTION STYLE DECLS
+# This will be refactored to use class style decls in the misc/commands dir
class BackTrace:
VM_FRAME_MAGIC_METHOD = 0x11110001
VM_FRAME_MAGIC_BLOCK = 0x22220001
@@ -417,6 +418,7 @@ def lldb_inspect(debugger, target, result, val):
elif flType == RUBY_T_IMEMO:
# I'm not sure how to get IMEMO_MASK out of lldb. It's not in globals()
imemo_type = (flags >> RUBY_FL_USHIFT) & 0x0F # IMEMO_MASK
+
print("T_IMEMO: ", file=result)
append_command_output(debugger, "p (enum imemo_type) %d" % imemo_type, result)
append_command_output(debugger, "p *(struct MEMO *) %0#x" % val.GetValueAsUnsigned(), result)
@@ -468,19 +470,6 @@ def check_bits(page, bitmap_name, bitmap_index, bitmap_bit, v):
else:
return ' '
-def heap_page(debugger, command, ctx, result, internal_dict):
- target = debugger.GetSelectedTarget()
- process = target.GetProcess()
- thread = process.GetSelectedThread()
- frame = thread.GetSelectedFrame()
-
- val = frame.EvaluateExpression(command)
- page = get_page(lldb, target, val)
- page_type = target.FindFirstType("struct heap_page").GetPointerType()
- page.Cast(page_type)
- append_command_output(debugger, "p (struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result)
- append_command_output(debugger, "p *(struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result)
-
def heap_page_body(debugger, command, ctx, result, internal_dict):
target = debugger.GetSelectedTarget()
process = target.GetProcess()
@@ -723,37 +712,37 @@ def rb_id2str(debugger, command, result, internal_dict):
pos = (num % ID_ENTRY_UNIT) * ID_ENTRY_SIZE
id_str = rb_ary_entry(target, ary, pos, result)
lldb_inspect(debugger, target, result, id_str)
+# END FUNCTION STYLE DECLS
-def rb_rclass_ext(debugger, command, result, internal_dict):
- if not ('RUBY_Qfalse' in globals()):
- lldb_init(debugger)
- target = debugger.GetSelectedTarget()
- process = target.GetProcess()
- thread = process.GetSelectedThread()
- frame = thread.GetSelectedFrame()
+load_dir, _ = os.path.split(os.path.realpath(__file__))
- uintptr_t = target.FindFirstType("uintptr_t")
- rclass_t = target.FindFirstType("struct RClass")
- rclass_ext_t = target.FindFirstType("rb_classext_t")
-
- rclass_addr = target.EvaluateExpression(command).Cast(uintptr_t)
- rclass_ext_addr = (rclass_addr.GetValueAsUnsigned() + rclass_t.GetByteSize())
- debugger.HandleCommand("p *(rb_classext_t *)%0#x" % rclass_ext_addr)
+for fname in glob.glob(f"{load_dir}/lldb_rb/commands/*_command.py"):
+ _, basename = os.path.split(fname)
+ mname, _ = os.path.splitext(basename)
+ exec(f"import lldb_rb.commands.{mname}")
def __lldb_init_module(debugger, internal_dict):
+ # Register all classes that subclass RbBaseCommand
+
+ for memname, mem in inspect.getmembers(sys.modules["lldb_rb.rb_base_command"]):
+ if inspect.isclass(mem):
+ for sclass in mem.__subclasses__():
+ sclass.register_lldb_command(debugger, f"{__name__}.{sclass.__module__}")
+
+
+ ## FUNCTION INITS - These should be removed when converted to class commands
debugger.HandleCommand("command script add -f lldb_cruby.lldb_rp rp")
debugger.HandleCommand("command script add -f lldb_cruby.count_objects rb_count_objects")
debugger.HandleCommand("command script add -f lldb_cruby.stack_dump_raw SDR")
debugger.HandleCommand("command script add -f lldb_cruby.dump_node dump_node")
- debugger.HandleCommand("command script add -f lldb_cruby.heap_page heap_page")
debugger.HandleCommand("command script add -f lldb_cruby.heap_page_body heap_page_body")
debugger.HandleCommand("command script add -f lldb_cruby.rb_backtrace rbbt")
debugger.HandleCommand("command script add -f lldb_cruby.dump_page dump_page")
debugger.HandleCommand("command script add -f lldb_cruby.dump_page_rvalue dump_page_rvalue")
debugger.HandleCommand("command script add -f lldb_cruby.rb_id2str rb_id2str")
- debugger.HandleCommand("command script add -f lldb_cruby.rb_rclass_ext rclass_ext")
- lldb_init(debugger)
+ lldb_rb.rb_base_command.RbBaseCommand.lldb_init(debugger)
+
print("lldb scripts for ruby has been installed.")
diff --git a/misc/lldb_rb/commands/command_template.py b/misc/lldb_rb/commands/command_template.py
new file mode 100644
index 0000000000..21014a993e
--- /dev/null
+++ b/misc/lldb_rb/commands/command_template.py
@@ -0,0 +1,30 @@
+# This is a command template for implementing a helper function inside LLDB. To
+# use this file
+# 1. Copy it and rename the copy so it ends with `_command.py`.
+# 2. Rename the class to something descriptive that ends with Command.
+# 3. Change the program variable to be a descriptive command name
+# 4. Ensure you are inheriting from RbBaseCommand or another command that
+# implements the same interface
+
+import lldb
+
+from lldb_rb.constants import *
+from lldb_rb.rb_base_command import RbBaseCommand
+
+# This test command inherits from RbBaseCommand which provides access to Ruby
+# globals and utility helpers
+class TestCommand(RbBaseCommand):
+ # program is the keyword the user will type in lldb to execute this command
+ program = "test"
+
+ # help_string will be displayed in lldb when the user uses the help functions
+ help_string = "This is a test command to show how to implement lldb commands"
+
+ # call is where our command logic will be implemented
+ def call(self, debugger, command, exe_ctx, result):
+ # This method will be called once the LLDB environment has been setup.
+ # You will have access to self.target, self.process, self.frame, and
+ # self.thread
+ #
+ # This is where we should implement our command logic
+ pass
diff --git a/misc/lldb_rb/commands/heap_page_command.py b/misc/lldb_rb/commands/heap_page_command.py
new file mode 100644
index 0000000000..edb74a415b
--- /dev/null
+++ b/misc/lldb_rb/commands/heap_page_command.py
@@ -0,0 +1,26 @@
+import lldb
+
+from lldb_rb.constants import *
+from lldb_rb.rb_base_command import RbBaseCommand
+
+class HeapPageCommand(RbBaseCommand):
+ program = "heap_page"
+ help_string = "prints out 'struct heap_page' for a VALUE pointer in the page"
+
+ def call(self, debugger, command, exe_ctx, result):
+ self.t_heap_page_body = self.target.FindFirstType("struct heap_page_body")
+ self.t_heap_page_ptr = self.target.FindFirstType("struct heap_page").GetPointerType()
+
+ page = self._get_page(self.frame.EvaluateExpression(command))
+ page.Cast(self.t_heap_page_ptr)
+
+ self._append_command_output(debugger, "p (struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result)
+ self._append_command_output(debugger, "p *(struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result)
+
+ def _get_page(self, val):
+ addr = val.GetValueAsUnsigned()
+ page_addr = addr & ~(HEAP_PAGE_ALIGN_MASK)
+ address = lldb.SBAddress(page_addr, self.target)
+ body = self.target.CreateValueFromAddress("page", address, self.t_heap_page_body)
+
+ return body.GetValueForExpressionPath("->header.page")
diff --git a/misc/lldb_rb/commands/rclass_ext_command.py b/misc/lldb_rb/commands/rclass_ext_command.py
new file mode 100644
index 0000000000..8bae911457
--- /dev/null
+++ b/misc/lldb_rb/commands/rclass_ext_command.py
@@ -0,0 +1,14 @@
+from lldb_rb.rb_base_command import RbBaseCommand
+
+class RclassExtCommand(RbBaseCommand):
+ program = "rclass_ext"
+ help_string = "retrieves and prints the rb_classext_struct for the VALUE pointer passed in"
+
+ def call(self, debugger, command, exe_ctx, result):
+ uintptr_t = self.target.FindFirstType("uintptr_t")
+ rclass_t = self.target.FindFirstType("struct RClass")
+ rclass_ext_t = self.target.FindFirstType("rb_classext_t")
+
+ rclass_addr = self.target.EvaluateExpression(command).Cast(uintptr_t)
+ rclass_ext_addr = (rclass_addr.GetValueAsUnsigned() + rclass_t.GetByteSize())
+ debugger.HandleCommand("p *(rb_classext_t *)%0#x" % rclass_ext_addr)
diff --git a/misc/lldb_rb/constants.py b/misc/lldb_rb/constants.py
new file mode 100644
index 0000000000..ec3050a399
--- /dev/null
+++ b/misc/lldb_rb/constants.py
@@ -0,0 +1,4 @@
+HEAP_PAGE_ALIGN_LOG = 16
+HEAP_PAGE_ALIGN_MASK = (~(~0 << HEAP_PAGE_ALIGN_LOG))
+HEAP_PAGE_ALIGN = (1 << HEAP_PAGE_ALIGN_LOG)
+HEAP_PAGE_SIZE = HEAP_PAGE_ALIGN
diff --git a/misc/lldb_rb/rb_base_command.py b/misc/lldb_rb/rb_base_command.py
new file mode 100644
index 0000000000..de90a1617c
--- /dev/null
+++ b/misc/lldb_rb/rb_base_command.py
@@ -0,0 +1,69 @@
+import lldb
+from pydoc import locate
+
+class RbBaseCommand:
+ @classmethod
+ def register_lldb_command(cls, debugger, module_name):
+ # Add any commands contained in this module to LLDB
+ command = f"command script add -c {module_name}.{cls.__name__} {cls.program}"
+ debugger.HandleCommand(command)
+
+ @classmethod
+ def lldb_init(cls, debugger):
+ target = debugger.GetSelectedTarget()
+ global SIZEOF_VALUE
+ SIZEOF_VALUE = target.FindFirstType("VALUE").GetByteSize()
+
+ value_types = []
+ g = globals()
+
+ imemo_types = target.FindFirstType("enum imemo_type")
+
+ #for member in imemo_types.GetEnumMembers():
+ # g[member.GetName()] = member.GetValueAsUnsigned()
+
+ for enum in target.FindFirstGlobalVariable("ruby_dummy_gdb_enums"):
+ enum = enum.GetType()
+ members = enum.GetEnumMembers()
+ for i in range(0, members.GetSize()):
+ member = members.GetTypeEnumMemberAtIndex(i)
+ name = member.GetName()
+ value = member.GetValueAsUnsigned()
+ g[name] = value
+
+ if name.startswith("RUBY_T_"):
+ value_types.append(name)
+ g["value_types"] = value_types
+
+ def __init__(self, debugger, _internal_dict):
+ self.internal_dict = _internal_dict
+
+ def __call__(self, debugger, command, exe_ctx, result):
+ if not ("RUBY_Qfalse" in globals()):
+ RbBaseCommand.lldb_init(debugger)
+
+ self.build_environment(debugger)
+ self.call(debugger, command, exe_ctx, result)
+
+ def call(self, debugger, command, exe_ctx, result):
+ raise NotImplementedError("subclasses must implement call")
+
+ def get_short_help(self):
+ return self.__class__.help_string
+
+ def get_long_help(self):
+ return self.__class__.help_string
+
+ def build_environment(self, debugger):
+ self.target = debugger.GetSelectedTarget()
+ self.process = self.target.GetProcess()
+ self.thread = self.process.GetSelectedThread()
+ self.frame = self.thread.GetSelectedFrame()
+
+ def _append_command_output(self, debugger, command, result):
+ output1 = result.GetOutput()
+ debugger.GetCommandInterpreter().HandleCommand(command, result)
+ output2 = result.GetOutput()
+ result.Clear()
+ result.write(output1)
+ result.write(output2)
diff --git a/missing/flock.c b/missing/flock.c
index 9daf1c38cc..0b76961762 100644
--- a/missing/flock.c
+++ b/missing/flock.c
@@ -5,9 +5,11 @@
#elif defined __wasi__
#include <errno.h>
-int flock(int fd, int operation) {
- errno = EINVAL;
- return -1;
+int
+flock(int fd, int operation)
+{
+ errno = EINVAL;
+ return -1;
}
#elif defined HAVE_FCNTL && defined HAVE_FCNTL_H
diff --git a/mjit.c b/mjit.c
index da3fd9d61e..3200ee2621 100644
--- a/mjit.c
+++ b/mjit.c
@@ -1,8 +1,9 @@
/**********************************************************************
- mjit.c - MRI method JIT compiler functions for Ruby's main thread
+ mjit.c - MRI method JIT compiler functions
Copyright (C) 2017 Vladimir Makarov <vmakarov@redhat.com>.
+ Copyright (C) 2017 Takashi Kokubun <k0kubun@ruby-lang.org>.
**********************************************************************/
@@ -76,6 +77,7 @@
#include "internal/cont.h"
#include "internal/file.h"
#include "internal/hash.h"
+#include "internal/process.h"
#include "internal/warnings.h"
#include "vm_sync.h"
#include "ractor_core.h"
@@ -87,7 +89,7 @@
#include "vm_core.h"
#include "vm_callinfo.h"
#include "mjit.h"
-#include "mjit_unit.h"
+#include "mjit_c.h"
#include "gc.h"
#include "ruby_assert.h"
#include "ruby/debug.h"
@@ -98,14 +100,9 @@
#include "insns_info.inc"
#include "internal/compile.h"
-#ifdef _WIN32
-#include <winsock2.h>
-#include <windows.h>
-#else
#include <sys/wait.h>
#include <sys/time.h>
#include <dlfcn.h>
-#endif
#include <errno.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
@@ -122,51 +119,16 @@
# define MAXPATHLEN 1024
#endif
-#ifdef _WIN32
-#define dlopen(name,flag) ((void*)LoadLibrary(name))
-#define dlerror() strerror(rb_w32_map_errno(GetLastError()))
-#define dlsym(handle,name) ((void*)GetProcAddress((handle),(name)))
-#define dlclose(handle) (!FreeLibrary(handle))
-#define RTLD_NOW -1
-
-#define waitpid(pid,stat_loc,options) (WaitForSingleObject((HANDLE)(pid), INFINITE), GetExitCodeProcess((HANDLE)(pid), (LPDWORD)(stat_loc)), CloseHandle((HANDLE)pid), (pid))
-#define WIFEXITED(S) ((S) != STILL_ACTIVE)
-#define WEXITSTATUS(S) (S)
-#define WIFSIGNALED(S) (0)
-typedef intptr_t pid_t;
-#endif
-
// Atomically set function pointer if possible.
#define MJIT_ATOMIC_SET(var, val) (void)ATOMIC_PTR_EXCHANGE(var, val)
#define MJIT_TMP_PREFIX "_ruby_mjit_"
-// JIT compaction requires the header transformation because linking multiple .o files
-// doesn't work without having `static` in the same function definitions. We currently
-// don't support transforming the MJIT header on Windows.
-#ifdef _WIN32
-# define USE_JIT_COMPACTION 0
-#else
-# define USE_JIT_COMPACTION 1
-#endif
-
-// Linked list of struct rb_mjit_unit.
-struct rb_mjit_unit_list {
- struct ccan_list_head head;
- int length; // the list length
-};
-
extern void rb_native_mutex_lock(rb_nativethread_lock_t *lock);
extern void rb_native_mutex_unlock(rb_nativethread_lock_t *lock);
extern void rb_native_mutex_initialize(rb_nativethread_lock_t *lock);
extern void rb_native_mutex_destroy(rb_nativethread_lock_t *lock);
-extern void rb_native_cond_initialize(rb_nativethread_cond_t *cond);
-extern void rb_native_cond_destroy(rb_nativethread_cond_t *cond);
-extern void rb_native_cond_signal(rb_nativethread_cond_t *cond);
-extern void rb_native_cond_broadcast(rb_nativethread_cond_t *cond);
-extern void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex);
-
// process.c
extern void mjit_add_waiting_pid(rb_vm_t *vm, rb_pid_t pid);
@@ -180,6 +142,15 @@ bool mjit_enabled = false;
// true if JIT-ed code should be called. When `ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS`
// and `mjit_call_p == false`, any JIT-ed code execution is cancelled as soon as possible.
bool mjit_call_p = false;
+// A flag to communicate that mjit_call_p should be disabled while it's temporarily false.
+bool mjit_cancel_p = false;
+// There's an ISEQ in unit_queue whose total_calls reached 2 * call_threshold.
+// If this is true, check_unit_queue will start compiling ISEQs in unit_queue.
+static bool mjit_compile_p = false;
+// The actual number of units in active_units
+static int active_units_length = 0;
+// The actual number of units in compact_units
+static int compact_units_length = 0;
// Priority queue of iseqs waiting for JIT compilation.
// This variable is a pointer to head unit of the queue.
@@ -194,20 +165,6 @@ static struct rb_mjit_unit_list stale_units = { CCAN_LIST_HEAD_INIT(stale_units.
static int current_unit_num;
// A mutex for conitionals and critical sections.
static rb_nativethread_lock_t mjit_engine_mutex;
-// A thread conditional to wake up `mjit_finish` at the end of PCH thread.
-static rb_nativethread_cond_t mjit_pch_wakeup;
-// A thread conditional to wake up the client if there is a change in
-// executed unit status.
-static rb_nativethread_cond_t mjit_client_wakeup;
-// A thread conditional to wake up a worker if there we have something
-// to add or we need to stop MJIT engine.
-static rb_nativethread_cond_t mjit_worker_wakeup;
-// A thread conditional to wake up workers if at the end of GC.
-static rb_nativethread_cond_t mjit_gc_wakeup;
-// The times when unload_units is requested. unload_units is called after some requests.
-static int unload_requests = 0;
-// The total number of unloaded units.
-static int total_unloads = 0;
// Set to true to stop worker.
static bool stop_worker_p;
// Set to true if worker is stopped.
@@ -237,15 +194,8 @@ static struct rb_mjit_unit *current_cc_unit = NULL;
// PID of currently running C compiler process. 0 if nothing is running.
static pid_t current_cc_pid = 0; // TODO: make this part of unit?
-#ifndef _MSC_VER
// Name of the header file.
static char *header_file;
-#endif
-
-#ifdef _WIN32
-// Linker option to enable libruby.
-static char *libruby_pathflag;
-#endif
#include "mjit_config.h"
@@ -261,7 +211,7 @@ static char *libruby_pathflag;
// Use `-nodefaultlibs -nostdlib` for GCC where possible, which does not work on cygwin, AIX, and OpenBSD.
// This seems to improve MJIT performance on GCC.
-#if defined __GNUC__ && !defined __clang__ && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(_AIX) && !defined(__OpenBSD__)
+#if defined __GNUC__ && !defined __clang__ && !defined(__CYGWIN__) && !defined(_AIX) && !defined(__OpenBSD__)
# define GCC_NOSTDLIB_FLAGS "-nodefaultlibs", "-nostdlib",
#else
# define GCC_NOSTDLIB_FLAGS // empty
@@ -286,7 +236,7 @@ static const char *const CC_LINKER_ARGS[] = {
};
static const char *const CC_LIBS[] = {
-#if defined(_WIN32) || defined(__CYGWIN__)
+#if defined(__CYGWIN__)
MJIT_LIBS // mswin, cygwin
#endif
#if defined __GNUC__ && !defined __clang__
@@ -340,11 +290,6 @@ mjit_warning(const char *format, ...)
static void
add_to_list(struct rb_mjit_unit *unit, struct rb_mjit_unit_list *list)
{
- (void)RB_DEBUG_COUNTER_INC_IF(mjit_length_unit_queue, list == &unit_queue);
- (void)RB_DEBUG_COUNTER_INC_IF(mjit_length_active_units, list == &active_units);
- (void)RB_DEBUG_COUNTER_INC_IF(mjit_length_compact_units, list == &compact_units);
- (void)RB_DEBUG_COUNTER_INC_IF(mjit_length_stale_units, list == &stale_units);
-
ccan_list_add_tail(&list->head, &unit->unode);
list->length++;
}
@@ -352,13 +297,6 @@ add_to_list(struct rb_mjit_unit *unit, struct rb_mjit_unit_list *list)
static void
remove_from_list(struct rb_mjit_unit *unit, struct rb_mjit_unit_list *list)
{
-#if USE_DEBUG_COUNTER
- rb_debug_counter_add(RB_DEBUG_COUNTER_mjit_length_unit_queue, -1, list == &unit_queue);
- rb_debug_counter_add(RB_DEBUG_COUNTER_mjit_length_active_units, -1, list == &active_units);
- rb_debug_counter_add(RB_DEBUG_COUNTER_mjit_length_compact_units, -1, list == &compact_units);
- rb_debug_counter_add(RB_DEBUG_COUNTER_mjit_length_stale_units, -1, list == &stale_units);
-#endif
-
ccan_list_del(&unit->unode);
list->length--;
}
@@ -371,36 +309,20 @@ remove_file(const char *filename)
}
}
-// Lazily delete .so files.
-static void
-clean_temp_files(struct rb_mjit_unit *unit)
-{
-#if defined(_WIN32)
- if (unit->so_file) {
- char *so_file = unit->so_file;
-
- unit->so_file = NULL;
- // unit->so_file is set only when mjit_opts.save_temps is false.
- remove_file(so_file);
- free(so_file);
- }
-#endif
-}
-
// This is called in the following situations:
// 1) On dequeue or `unload_units()`, associated ISeq is already GCed.
// 2) The unit is not called often and unloaded by `unload_units()`.
// 3) Freeing lists on `mjit_finish()`.
//
// `jit_func` value does not matter for 1 and 3 since the unit won't be used anymore.
-// For the situation 2, this sets the ISeq's JIT state to NOT_COMPILED_JIT_ISEQ_FUNC
+// For the situation 2, this sets the ISeq's JIT state to MJIT_FUNC_FAILED
// to prevent the situation that the same methods are continuously compiled.
static void
free_unit(struct rb_mjit_unit *unit)
{
if (unit->iseq) { // ISeq is not GCed
- ISEQ_BODY(unit->iseq)->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
- ISEQ_BODY(unit->iseq)->jit_unit = NULL;
+ ISEQ_BODY(unit->iseq)->jit_func = (jit_func_t)MJIT_FUNC_FAILED;
+ ISEQ_BODY(unit->iseq)->mjit_unit = NULL;
}
if (unit->cc_entries) {
void *entries = (void *)unit->cc_entries;
@@ -409,8 +331,7 @@ free_unit(struct rb_mjit_unit *unit)
if (unit->handle && dlclose(unit->handle)) { // handle is NULL if it's in queue
mjit_warning("failed to close handle for u%d: %s", unit->id, dlerror());
}
- clean_temp_files(unit);
- free(unit);
+ xfree(unit);
}
// Start a critical section. Use message `msg` to print debug info at `level`.
@@ -431,10 +352,12 @@ CRITICAL_SECTION_FINISH(int level, const char *msg)
rb_native_mutex_unlock(&mjit_engine_mutex);
}
+static pid_t mjit_pid = 0;
+
static int
sprint_uniq_filename(char *str, size_t size, unsigned long id, const char *prefix, const char *suffix)
{
- return snprintf(str, size, "%s/%sp%"PRI_PIDT_PREFIX"uu%lu%s", tmp_dir, prefix, getpid(), id, suffix);
+ return snprintf(str, size, "%s/%sp%"PRI_PIDT_PREFIX"uu%lu%s", tmp_dir, prefix, mjit_pid, id, suffix);
}
// Return time in milliseconds as a double.
@@ -554,22 +477,6 @@ start_process(const char *abspath, char *const *argv)
}
pid_t pid;
-#ifdef _WIN32
- extern HANDLE rb_w32_start_process(const char *abspath, char *const *argv, int out_fd);
- int out_fd = 0;
- if (mjit_opts.verbose <= 1) {
- // Discard cl.exe's outputs like:
- // _ruby_mjit_p12u3.c
- // Creating library C:.../_ruby_mjit_p12u3.lib and object C:.../_ruby_mjit_p12u3.exp
- out_fd = dev_null;
- }
-
- pid = (pid_t)rb_w32_start_process(abspath, argv, out_fd);
- if (pid == 0) {
- verbose(1, "MJIT: Failed to create process: %s", dlerror());
- return -1;
- }
-#else
if ((pid = vfork()) == 0) { /* TODO: reuse some function in process.c */
umask(0077);
if (mjit_opts.verbose == 0) {
@@ -587,7 +494,6 @@ start_process(const char *abspath, char *const *argv)
verbose(1, "MJIT: Error in execv: %s", abspath);
_exit(1);
}
-#endif
(void)close(dev_null);
return pid;
}
@@ -627,23 +533,16 @@ exec_process(const char *path, char *const argv[])
static void
remove_so_file(const char *so_file, struct rb_mjit_unit *unit)
{
-#if defined(_WIN32)
- // Windows can't remove files while it's used.
- unit->so_file = strdup(so_file); // lazily delete on `clean_temp_files()`
- if (unit->so_file == NULL)
- mjit_warning("failed to allocate memory to lazily remove '%s': %s", so_file, strerror(errno));
-#else
remove_file(so_file);
-#endif
}
// Print _mjitX, but make a human-readable funcname when --mjit-debug is used
static void
-sprint_funcname(char *funcname, const struct rb_mjit_unit *unit)
+sprint_funcname(char *funcname, size_t funcname_size, const struct rb_mjit_unit *unit)
{
const rb_iseq_t *iseq = unit->iseq;
if (iseq == NULL || (!mjit_opts.debug && !mjit_opts.debug_flags)) {
- sprintf(funcname, "_mjit%d", unit->id);
+ snprintf(funcname, funcname_size, "_mjit%d", unit->id);
return;
}
@@ -662,7 +561,7 @@ sprint_funcname(char *funcname, const struct rb_mjit_unit *unit)
if (!strcmp(method, "[]=")) method = "ASET";
// Print and normalize
- sprintf(funcname, "_mjit%d_%s_%s", unit->id, path, method);
+ snprintf(funcname, funcname_size, "_mjit%d_%s_%s", unit->id, path, method);
for (size_t i = 0; i < strlen(funcname); i++) {
char c = funcname[i];
if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_')) {
@@ -681,93 +580,13 @@ static const int c_file_access_mode =
#define append_str(p, str) append_str2(p, str, sizeof(str)-1)
#define append_lit(p, str) append_str2(p, str, rb_strlen_lit(str))
-#ifdef _MSC_VER
-// Compile C file to so. It returns true if it succeeds. (mswin)
-static bool
-compile_c_to_so(const char *c_file, const char *so_file)
-{
- const char *files[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-link", libruby_pathflag, NULL };
- char *p;
-
- // files[0] = "-Fe*.dll"
- files[0] = p = alloca(sizeof(char) * (rb_strlen_lit("-Fe") + strlen(so_file) + 1));
- p = append_lit(p, "-Fe");
- p = append_str2(p, so_file, strlen(so_file));
- *p = '\0';
-
- // files[1] = "-Fo*.obj"
- // We don't need .obj file, but it's somehow created to cwd without -Fo and we want to control the output directory.
- files[1] = p = alloca(sizeof(char) * (rb_strlen_lit("-Fo") + strlen(so_file) - rb_strlen_lit(DLEXT) + rb_strlen_lit(".obj") + 1));
- char *obj_file = p = append_lit(p, "-Fo");
- p = append_str2(p, so_file, strlen(so_file) - rb_strlen_lit(DLEXT));
- p = append_lit(p, ".obj");
- *p = '\0';
-
- // files[2] = "-Yu*.pch"
- files[2] = p = alloca(sizeof(char) * (rb_strlen_lit("-Yu") + strlen(pch_file) + 1));
- p = append_lit(p, "-Yu");
- p = append_str2(p, pch_file, strlen(pch_file));
- *p = '\0';
-
- // files[3] = "C:/.../rb_mjit_header-*.obj"
- files[3] = p = alloca(sizeof(char) * (strlen(pch_file) + 1));
- p = append_str2(p, pch_file, strlen(pch_file) - strlen(".pch"));
- p = append_lit(p, ".obj");
- *p = '\0';
-
- // files[4] = "-Tc*.c"
- files[4] = p = alloca(sizeof(char) * (rb_strlen_lit("-Tc") + strlen(c_file) + 1));
- p = append_lit(p, "-Tc");
- p = append_str2(p, c_file, strlen(c_file));
- *p = '\0';
-
- // files[5] = "-Fd*.pdb"
- // Generate .pdb file in temporary directory instead of cwd.
- files[5] = p = alloca(sizeof(char) * (rb_strlen_lit("-Fd") + strlen(so_file) - rb_strlen_lit(DLEXT) + rb_strlen_lit(".pdb") + 1));
- p = append_lit(p, "-Fd");
- p = append_str2(p, so_file, strlen(so_file) - rb_strlen_lit(DLEXT));
- p = append_lit(p, ".pdb");
- *p = '\0';
-
- // files[6] = "-Z7"
- // Put this last to override any debug options that came previously.
- files[6] = p = alloca(sizeof(char) * rb_strlen_lit("-Z7") + 1);
- p = append_lit(p, "-Z7");
- *p = '\0';
-
- char **args = form_args(5, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS,
- files, CC_LIBS, CC_DLDFLAGS_ARGS);
- if (args == NULL)
- return false;
-
- int exit_code = exec_process(cc_path, args);
- free(args);
-
- if (exit_code == 0) {
- // remove never-used files (.obj, .lib, .exp, .pdb). XXX: Is there any way not to generate this?
- if (!mjit_opts.save_temps) {
- char *before_dot;
- remove_file(obj_file);
-
- before_dot = obj_file + strlen(obj_file) - rb_strlen_lit(".obj");
- append_lit(before_dot, ".lib"); remove_file(obj_file);
- append_lit(before_dot, ".exp"); remove_file(obj_file);
- append_lit(before_dot, ".pdb"); remove_file(obj_file);
- }
- }
- else {
- verbose(2, "compile_c_to_so: compile error: %d", exit_code);
- }
- return exit_code == 0;
-}
-#else // _MSC_VER
-
// The function producing the pre-compiled header.
static void
make_pch(void)
{
const char *rest_args[] = {
# ifdef __clang__
+ "-Xclang",
"-emit-pch",
"-c",
# endif
@@ -798,43 +617,62 @@ make_pch(void)
}
}
-static pid_t
-start_compiling_c_to_so(const char *c_file, const char *so_file)
+static int
+c_compile(const char *c_file, const char *so_file)
{
const char *so_args[] = {
"-o", so_file,
-# ifdef _WIN32
- libruby_pathflag,
-# endif
# ifdef __clang__
"-include-pch", pch_file,
# endif
c_file, NULL
};
- char **args = form_args(7, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS, cc_added_args,
- so_args, CC_LIBS, CC_DLDFLAGS_ARGS, CC_LINKER_ARGS);
- if (args == NULL) return -1;
- rb_vm_t *vm = GET_VM();
- rb_native_mutex_lock(&vm->waitpid_lock);
+# if defined(__MACH__)
+ extern VALUE rb_libruby_selfpath;
+ const char *loader_args[] = {"-bundle_loader", StringValuePtr(rb_libruby_selfpath), NULL};
+# else
+ const char *loader_args[] = {NULL};
+# endif
- pid_t pid = start_process(cc_path, args);
- mjit_add_waiting_pid(vm, pid);
+ char **args = form_args(8, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS, cc_added_args,
+ so_args, loader_args, CC_LIBS, CC_DLDFLAGS_ARGS, CC_LINKER_ARGS);
+ if (args == NULL) return 1;
- rb_native_mutex_unlock(&vm->waitpid_lock);
+ int exit_code = exec_process(cc_path, args);
+ if (!mjit_opts.save_temps)
+ remove_file(c_file);
free(args);
- return pid;
+ return exit_code;
+}
+
+static int
+c_compile_unit(struct rb_mjit_unit *unit)
+{
+ static const char c_ext[] = ".c";
+ static const char so_ext[] = DLEXT;
+ char c_file[MAXPATHLEN], so_file[MAXPATHLEN];
+
+ sprint_uniq_filename(c_file, (int)sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
+ sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
+
+ return c_compile(c_file, so_file);
}
-#endif // _MSC_VER
-#if USE_JIT_COMPACTION
static void compile_prelude(FILE *f);
-// Compile all JIT code into a single .c file
static bool
-mjit_compact(char* c_file)
+mjit_batch(struct rb_mjit_unit *unit)
{
+ VM_ASSERT(unit->type == MJIT_UNIT_BATCH);
+ static const char c_ext[] = ".c";
+ static const char so_ext[] = DLEXT;
+ char c_file[MAXPATHLEN], so_file[MAXPATHLEN];
+
+ sprint_uniq_filename(c_file, (int)sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
+ sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
+
FILE *f;
int fd = rb_cloexec_open(c_file, c_file_access_mode, 0600);
if (fd < 0 || (f = fdopen(fd, "w")) == NULL) {
@@ -846,31 +684,21 @@ mjit_compact(char* c_file)
compile_prelude(f);
- // This entire loop lock GC so that we do not need to consider a case that
- // ISeq is GC-ed in a middle of re-compilation. It takes 3~4ms with 100 methods
- // on my machine. It's not too bad compared to compilation time of C (7200~8000ms),
- // but it might be larger if we use a larger --jit-max-cache.
- //
- // TODO: Consider using a more granular lock after we implement inlining across
- // compacted functions (not done yet).
bool success = true;
struct rb_mjit_unit *child_unit = 0;
- ccan_list_for_each(&active_units.head, child_unit, unode) {
+ ccan_list_for_each(&unit->units.head, child_unit, unode) {
if (!success) continue;
- if (ISEQ_BODY(child_unit->iseq)->jit_unit == NULL) continue; // Sometimes such units are created. TODO: Investigate why
+ if (child_unit->iseq == NULL) continue; // ISEQ is GCed
char funcname[MAXPATHLEN];
- sprint_funcname(funcname, child_unit);
+ sprint_funcname(funcname, sizeof(funcname), child_unit);
- long iseq_lineno = 0;
- if (FIXNUM_P(ISEQ_BODY(child_unit->iseq)->location.first_lineno))
- // FIX2INT may fallback to rb_num2long(), which is a method call and dangerous in MJIT worker. So using only FIX2LONG.
- iseq_lineno = FIX2LONG(ISEQ_BODY(child_unit->iseq)->location.first_lineno);
+ int iseq_lineno = ISEQ_BODY(child_unit->iseq)->location.first_lineno;
const char *sep = "@";
const char *iseq_label = RSTRING_PTR(ISEQ_BODY(child_unit->iseq)->location.label);
const char *iseq_path = RSTRING_PTR(rb_iseq_path(child_unit->iseq));
if (!iseq_label) iseq_label = sep = "";
- fprintf(f, "\n/* %s%s%s:%ld */\n", iseq_label, sep, iseq_path, iseq_lineno);
+ fprintf(f, "\n/* %s%s%s:%d */\n", iseq_label, sep, iseq_path, iseq_lineno);
success &= mjit_compile(f, child_unit->iseq, funcname, child_unit->id);
}
@@ -880,9 +708,10 @@ mjit_compact(char* c_file)
// Compile all cached .c files and build a single .so file. Reload all JIT func from it.
// This improves the code locality for better performance in terms of iTLB and iCache.
-static pid_t
-start_mjit_compact(struct rb_mjit_unit *unit)
+static bool
+mjit_compact(struct rb_mjit_unit *unit)
{
+ VM_ASSERT(unit->type == MJIT_UNIT_COMPACT);
static const char c_ext[] = ".c";
static const char so_ext[] = DLEXT;
char c_file[MAXPATHLEN], so_file[MAXPATHLEN];
@@ -890,66 +719,130 @@ start_mjit_compact(struct rb_mjit_unit *unit)
sprint_uniq_filename(c_file, (int)sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
- bool success = mjit_compact(c_file);
- if (success) {
- return start_compiling_c_to_so(c_file, so_file);
+ FILE *f;
+ int fd = rb_cloexec_open(c_file, c_file_access_mode, 0600);
+ if (fd < 0 || (f = fdopen(fd, "w")) == NULL) {
+ int e = errno;
+ if (fd >= 0) (void)close(fd);
+ verbose(1, "Failed to fopen '%s', giving up JIT for it (%s)", c_file, strerror(e));
+ return false;
+ }
+
+ compile_prelude(f);
+
+ bool success = true;
+ compact_units_length = 0;
+ struct rb_mjit_unit *batch_unit = 0, *child_unit = 0;
+ ccan_list_for_each(&active_units.head, batch_unit, unode) {
+ ccan_list_for_each(&batch_unit->units.head, child_unit, unode) {
+ if (!success) continue;
+ if (child_unit->iseq == NULL) continue; // ISEQ is GCed
+
+ char funcname[MAXPATHLEN];
+ sprint_funcname(funcname, sizeof(funcname), child_unit);
+
+ int iseq_lineno = ISEQ_BODY(child_unit->iseq)->location.first_lineno;
+ const char *sep = "@";
+ const char *iseq_label = RSTRING_PTR(ISEQ_BODY(child_unit->iseq)->location.label);
+ const char *iseq_path = RSTRING_PTR(rb_iseq_path(child_unit->iseq));
+ if (!iseq_label) iseq_label = sep = "";
+ fprintf(f, "\n/* %s%s%s:%d */\n", iseq_label, sep, iseq_path, iseq_lineno);
+ success &= mjit_compile(f, child_unit->iseq, funcname, child_unit->id);
+ compact_units_length++;
+ }
}
- return -1;
+
+ fclose(f);
+ return success;
}
static void
-load_compact_funcs_from_so(struct rb_mjit_unit *unit, char *c_file, char *so_file)
+load_batch_funcs_from_so(struct rb_mjit_unit *unit, char *c_file, char *so_file)
{
- struct rb_mjit_unit *cur = 0;
double end_time = real_ms_time();
void *handle = dlopen(so_file, RTLD_NOW);
if (handle == NULL) {
- mjit_warning("failure in loading code from compacted '%s': %s", so_file, dlerror());
- free(unit);
+ mjit_warning("failure in loading code from batched '%s': %s", so_file, dlerror());
+ xfree(unit);
return;
}
unit->handle = handle;
- // lazily dlclose handle (and .so file for win32) on `mjit_finish()`.
- add_to_list(unit, &compact_units);
+ // lazily dlclose handle on `mjit_finish()`.
+ add_to_list(unit, &active_units);
+ active_units_length += unit->units.length;
if (!mjit_opts.save_temps)
remove_so_file(so_file, unit);
- ccan_list_for_each(&active_units.head, cur, unode) {
- void *func;
+ struct rb_mjit_unit *child_unit = 0;
+ ccan_list_for_each(&unit->units.head, child_unit, unode) {
char funcname[MAXPATHLEN];
- sprint_funcname(funcname, cur);
+ sprint_funcname(funcname, sizeof(funcname), child_unit);
+ void *func;
if ((func = dlsym(handle, funcname)) == NULL) {
- mjit_warning("skipping to reload '%s' from '%s': %s", funcname, so_file, dlerror());
+ mjit_warning("skipping to load '%s' from '%s': %s", funcname, so_file, dlerror());
continue;
}
- if (cur->iseq) { // Check whether GCed or not
+ if (child_unit->iseq) { // Check whether GCed or not
// Usage of jit_code might be not in a critical section.
- MJIT_ATOMIC_SET(ISEQ_BODY(cur->iseq)->jit_func, (mjit_func_t)func);
+ const rb_iseq_t *iseq = child_unit->iseq;
+ MJIT_ATOMIC_SET(ISEQ_BODY(iseq)->jit_func, (jit_func_t)func);
+
+ verbose(1, "JIT success: %s@%s:%d",
+ RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
+ RSTRING_PTR(rb_iseq_path(iseq)), ISEQ_BODY(iseq)->location.first_lineno);
+ }
+ else {
+ verbose(1, "JIT skip: A compiled method has been GCed");
}
}
- verbose(1, "JIT compaction (%.1fms): Compacted %d methods %s -> %s", end_time - current_cc_ms, active_units.length, c_file, so_file);
+ verbose(1, "JIT batch (%.1fms): Batched %d methods %s -> %s", end_time - current_cc_ms, unit->units.length, c_file, so_file);
}
-#endif // USE_JIT_COMPACTION
-static void *
-load_func_from_so(const char *so_file, const char *funcname, struct rb_mjit_unit *unit)
+static void
+load_compact_funcs_from_so(struct rb_mjit_unit *unit, char *c_file, char *so_file)
{
- void *handle, *func;
+ double end_time = real_ms_time();
- handle = dlopen(so_file, RTLD_NOW);
+ void *handle = dlopen(so_file, RTLD_NOW);
if (handle == NULL) {
- mjit_warning("failure in loading code from '%s': %s", so_file, dlerror());
- return (void *)NOT_COMPILED_JIT_ISEQ_FUNC;
+ mjit_warning("failure in loading code from compacted '%s': %s", so_file, dlerror());
+ xfree(unit);
+ return;
}
-
- func = dlsym(handle, funcname);
unit->handle = handle;
- return func;
+
+ // lazily dlclose handle on `mjit_finish()`.
+ add_to_list(unit, &compact_units);
+
+ if (!mjit_opts.save_temps)
+ remove_so_file(so_file, unit);
+
+ struct rb_mjit_unit *batch_unit = 0, *child_unit = 0;
+ ccan_list_for_each(&active_units.head, batch_unit, unode) {
+ ccan_list_for_each(&batch_unit->units.head, child_unit, unode) {
+ if (child_unit->iseq == NULL) continue; // ISEQ is GCed
+
+ char funcname[MAXPATHLEN];
+ sprint_funcname(funcname, sizeof(funcname), child_unit);
+
+ void *func;
+ if ((func = dlsym(handle, funcname)) == NULL) {
+ mjit_warning("skipping to reload '%s' from '%s': %s", funcname, so_file, dlerror());
+ continue;
+ }
+
+ if (child_unit->iseq) { // Check whether GCed or not
+ // Usage of jit_code might be not in a critical section.
+ MJIT_ATOMIC_SET(ISEQ_BODY(child_unit->iseq)->jit_func, (jit_func_t)func);
+ }
+ }
+ }
+ verbose(1, "JIT compaction (%.1fms): Compacted %d methods %s -> %s", end_time - current_cc_ms, active_units_length, c_file, so_file);
}
#ifndef __clang__
@@ -988,138 +881,26 @@ compile_prelude(FILE *f)
}
fprintf(f, "\"\n");
#endif
-
-#ifdef _WIN32
- fprintf(f, "void _pei386_runtime_relocator(void){}\n");
- fprintf(f, "int __stdcall DllMainCRTStartup(void* hinstDLL, unsigned int fdwReason, void* lpvReserved) { return 1; }\n");
-#endif
}
-// Compile ISeq in UNIT and return function pointer of JIT-ed code.
-// It may return NOT_COMPILED_JIT_ISEQ_FUNC if something went wrong.
static pid_t
-start_mjit_compile(struct rb_mjit_unit *unit)
-{
- static const char c_ext[] = ".c";
- static const char so_ext[] = DLEXT;
- char c_file[MAXPATHLEN], so_file[MAXPATHLEN], funcname[MAXPATHLEN];
-
- sprint_uniq_filename(c_file, (int)sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
- sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
- sprint_funcname(funcname, unit);
-
- FILE *f;
- int fd = rb_cloexec_open(c_file, c_file_access_mode, 0600);
- if (fd < 0 || (f = fdopen(fd, "w")) == NULL) {
- int e = errno;
- if (fd >= 0) (void)close(fd);
- verbose(1, "Failed to fopen '%s', giving up JIT for it (%s)", c_file, strerror(e));
- return -1;
- }
-
- // print #include of MJIT header, etc.
- compile_prelude(f);
-
- // To make MJIT worker thread-safe against GC.compact, copy ISeq values while `in_jit` is true.
- long iseq_lineno = 0;
- if (FIXNUM_P(ISEQ_BODY(unit->iseq)->location.first_lineno))
- // FIX2INT may fallback to rb_num2long(), which is a method call and dangerous in MJIT worker. So using only FIX2LONG.
- iseq_lineno = FIX2LONG(ISEQ_BODY(unit->iseq)->location.first_lineno);
- char *iseq_label = alloca(RSTRING_LEN(ISEQ_BODY(unit->iseq)->location.label) + 1);
- char *iseq_path = alloca(RSTRING_LEN(rb_iseq_path(unit->iseq)) + 1);
- strcpy(iseq_label, RSTRING_PTR(ISEQ_BODY(unit->iseq)->location.label));
- strcpy(iseq_path, RSTRING_PTR(rb_iseq_path(unit->iseq)));
-
- verbose(2, "start compilation: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file);
- fprintf(f, "/* %s@%s:%ld */\n\n", iseq_label, iseq_path, iseq_lineno);
- bool success = mjit_compile(f, unit->iseq, funcname, unit->id);
-
- fclose(f);
- if (!success) {
- if (!mjit_opts.save_temps)
- remove_file(c_file);
- verbose(1, "JIT failure: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file);
- return -1;
- }
-
- return start_compiling_c_to_so(c_file, so_file);
-}
-
-#ifdef _WIN32
-// Compile ISeq in UNIT and return function pointer of JIT-ed code.
-// It may return NOT_COMPILED_JIT_ISEQ_FUNC if something went wrong.
-static mjit_func_t
-convert_unit_to_func(struct rb_mjit_unit *unit)
+start_c_compile_unit(struct rb_mjit_unit *unit)
{
- static const char c_ext[] = ".c";
- static const char so_ext[] = DLEXT;
- char c_file[MAXPATHLEN], so_file[MAXPATHLEN], funcname[MAXPATHLEN];
-
- sprint_uniq_filename(c_file, (int)sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
- sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
- sprint_funcname(funcname, unit);
-
- FILE *f;
- int fd = rb_cloexec_open(c_file, c_file_access_mode, 0600);
- if (fd < 0 || (f = fdopen(fd, "w")) == NULL) {
- int e = errno;
- if (fd >= 0) (void)close(fd);
- verbose(1, "Failed to fopen '%s', giving up JIT for it (%s)", c_file, strerror(e));
- return (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
- }
-
- // print #include of MJIT header, etc.
- compile_prelude(f);
-
- // To make MJIT worker thread-safe against GC.compact, copy ISeq values while `in_jit` is true.
- long iseq_lineno = 0;
- if (FIXNUM_P(ISEQ_BODY(unit->iseq)->location.first_lineno))
- // FIX2INT may fallback to rb_num2long(), which is a method call and dangerous in MJIT worker. So using only FIX2LONG.
- iseq_lineno = FIX2LONG(ISEQ_BODY(unit->iseq)->location.first_lineno);
- char *iseq_label = alloca(RSTRING_LEN(ISEQ_BODY(unit->iseq)->location.label) + 1);
- char *iseq_path = alloca(RSTRING_LEN(rb_iseq_path(unit->iseq)) + 1);
- strcpy(iseq_label, RSTRING_PTR(ISEQ_BODY(unit->iseq)->location.label));
- strcpy(iseq_path, RSTRING_PTR(rb_iseq_path(unit->iseq)));
-
- verbose(2, "start compilation: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file);
- fprintf(f, "/* %s@%s:%ld */\n\n", iseq_label, iseq_path, iseq_lineno);
- bool success = mjit_compile(f, unit->iseq, funcname, unit->id);
-
- fclose(f);
- if (!success) {
- if (!mjit_opts.save_temps)
- remove_file(c_file);
- verbose(1, "JIT failure: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file);
- return (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
- }
-
- double start_time = real_ms_time();
- success = compile_c_to_so(c_file, so_file);
- if (!mjit_opts.save_temps)
- remove_file(c_file);
- double end_time = real_ms_time();
-
- if (!success) {
- verbose(2, "Failed to generate so: %s", so_file);
- return (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
+ extern pid_t rb_mjit_fork();
+ pid_t pid = rb_mjit_fork();
+ if (pid == 0) {
+ int exit_code = c_compile_unit(unit);
+ exit(exit_code);
}
-
- void *func = load_func_from_so(so_file, funcname, unit);
- if (!mjit_opts.save_temps)
- remove_so_file(so_file, unit);
-
- if ((uintptr_t)func > (uintptr_t)LAST_JIT_ISEQ_FUNC) {
- verbose(1, "JIT success (%.1fms): %s@%s:%ld -> %s",
- end_time - start_time, iseq_label, iseq_path, iseq_lineno, c_file);
+ else {
+ return pid;
}
- return (mjit_func_t)func;
}
-#endif
-// Capture cc entries of `captured_iseq` and append them to `compiled_iseq->jit_unit->cc_entries`.
+// Capture cc entries of `captured_iseq` and append them to `compiled_iseq->mjit_unit->cc_entries`.
// This is needed when `captured_iseq` is inlined by `compiled_iseq` and GC needs to mark inlined cc.
//
-// Index to refer to `compiled_iseq->jit_unit->cc_entries` is returned instead of the address
+// Index to refer to `compiled_iseq->mjit_unit->cc_entries` is returned instead of the address
// because old addresses may be invalidated by `realloc` later. -1 is returned on failure.
//
// This assumes that it's safe to reference cc without acquiring GVL.
@@ -1127,10 +908,10 @@ int
mjit_capture_cc_entries(const struct rb_iseq_constant_body *compiled_iseq, const struct rb_iseq_constant_body *captured_iseq)
{
VM_ASSERT(compiled_iseq != NULL);
- VM_ASSERT(compiled_iseq->jit_unit != NULL);
+ VM_ASSERT(compiled_iseq->mjit_unit != NULL);
VM_ASSERT(captured_iseq != NULL);
- struct rb_mjit_unit *unit = compiled_iseq->jit_unit;
+ struct rb_mjit_unit *unit = compiled_iseq->mjit_unit;
unsigned int new_entries_size = unit->cc_entries_size + captured_iseq->ci_size;
VM_ASSERT(captured_iseq->ci_size > 0);
@@ -1159,105 +940,7 @@ mjit_capture_cc_entries(const struct rb_iseq_constant_body *compiled_iseq, const
return cc_entries_index;
}
-// Set up field `used_code_p` for unit iseqs whose iseq on the stack of ec.
-static void
-mark_ec_units(rb_execution_context_t *ec)
-{
- const rb_control_frame_t *cfp;
-
- if (ec->vm_stack == NULL)
- return;
- for (cfp = RUBY_VM_END_CONTROL_FRAME(ec) - 1; ; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
- const rb_iseq_t *iseq;
- if (cfp->pc && (iseq = cfp->iseq) != NULL
- && imemo_type((VALUE) iseq) == imemo_iseq
- && (ISEQ_BODY(iseq)->jit_unit) != NULL) {
- ISEQ_BODY(iseq)->jit_unit->used_code_p = true;
- }
-
- if (cfp == ec->cfp)
- break; // reached the most recent cfp
- }
-}
-
-// MJIT info related to an existing continutaion.
-struct mjit_cont {
- rb_execution_context_t *ec; // continuation ec
- struct mjit_cont *prev, *next; // used to form lists
-};
-
-// Double linked list of registered continuations. This is used to detect
-// units which are in use in unload_units.
-static struct mjit_cont *first_cont;
-
-// Unload JIT code of some units to satisfy the maximum permitted
-// number of units with a loaded code.
-static void
-unload_units(void)
-{
- struct rb_mjit_unit *unit = 0, *next;
- struct mjit_cont *cont;
- int units_num = active_units.length;
-
- // For now, we don't unload units when ISeq is GCed. We should
- // unload such ISeqs first here.
- ccan_list_for_each_safe(&active_units.head, unit, next, unode) {
- if (unit->iseq == NULL) { // ISeq is GCed.
- remove_from_list(unit, &active_units);
- free_unit(unit);
- }
- }
-
- // Detect units which are in use and can't be unloaded.
- ccan_list_for_each(&active_units.head, unit, unode) {
- VM_ASSERT(unit->iseq != NULL && unit->handle != NULL);
- unit->used_code_p = false;
- }
- // All threads have a root_fiber which has a mjit_cont. Other normal fibers also
- // have a mjit_cont. Thus we can check ISeqs in use by scanning ec of mjit_conts.
- for (cont = first_cont; cont != NULL; cont = cont->next) {
- mark_ec_units(cont->ec);
- }
- // TODO: check stale_units and unload unused ones! (note that the unit is not associated to ISeq anymore)
-
- // Unload units whose total_calls is smaller than any total_calls in unit_queue.
- // TODO: make the algorithm more efficient
- long unsigned prev_queue_calls = -1;
- while (true) {
- // Calculate the next max total_calls in unit_queue
- long unsigned max_queue_calls = 0;
- ccan_list_for_each(&unit_queue.head, unit, unode) {
- if (unit->iseq != NULL && max_queue_calls < ISEQ_BODY(unit->iseq)->total_calls
- && ISEQ_BODY(unit->iseq)->total_calls < prev_queue_calls) {
- max_queue_calls = ISEQ_BODY(unit->iseq)->total_calls;
- }
- }
- prev_queue_calls = max_queue_calls;
-
- bool unloaded_p = false;
- ccan_list_for_each_safe(&active_units.head, unit, next, unode) {
- if (unit->used_code_p) // We can't unload code on stack.
- continue;
-
- if (max_queue_calls > ISEQ_BODY(unit->iseq)->total_calls) {
- verbose(2, "Unloading unit %d (calls=%lu, threshold=%lu)",
- unit->id, ISEQ_BODY(unit->iseq)->total_calls, max_queue_calls);
- VM_ASSERT(unit->handle != NULL);
- remove_from_list(unit, &active_units);
- free_unit(unit);
- unloaded_p = true;
- }
- }
- if (!unloaded_p) break;
- }
-
- if (units_num > active_units.length) {
- verbose(1, "Too many JIT code -- %d units unloaded", units_num - active_units.length);
- total_unloads += units_num - active_units.length;
- }
-}
-
-static void mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info, bool worker_p);
+static void mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info);
// Return an unique file name in /tmp with PREFIX and SUFFIX and
// number ID. Use getpid if ID == 0. The return file name exists
@@ -1287,6 +970,7 @@ mjit_cancel_all(const char *reason)
return;
mjit_call_p = false;
+ mjit_cancel_p = true;
if (mjit_opts.warnings || mjit_opts.verbose) {
fprintf(stderr, "JIT cancel: Disabled JIT-ed code because %s\n", reason);
}
@@ -1300,8 +984,8 @@ mjit_update_references(const rb_iseq_t *iseq)
return;
CRITICAL_SECTION_START(4, "mjit_update_references");
- if (ISEQ_BODY(iseq)->jit_unit) {
- ISEQ_BODY(iseq)->jit_unit->iseq = (rb_iseq_t *)rb_gc_location((VALUE)ISEQ_BODY(iseq)->jit_unit->iseq);
+ if (ISEQ_BODY(iseq)->mjit_unit) {
+ ISEQ_BODY(iseq)->mjit_unit->iseq = (rb_iseq_t *)rb_gc_location((VALUE)ISEQ_BODY(iseq)->mjit_unit->iseq);
// We need to invalidate JIT-ed code for the ISeq because it embeds pointer addresses.
// To efficiently do that, we use the same thing as TracePoint and thus everything is cancelled for now.
// See mjit.h and tool/ruby_vm/views/_mjit_compile_insn.erb for how `mjit_call_p` is used.
@@ -1309,7 +993,7 @@ mjit_update_references(const rb_iseq_t *iseq)
}
// Units in stale_units (list of over-speculated and invalidated code) are not referenced from
- // `ISEQ_BODY(iseq)->jit_unit` anymore (because new one replaces that). So we need to check them too.
+ // `ISEQ_BODY(iseq)->mjit_unit` anymore (because new one replaces that). So we need to check them too.
// TODO: we should be able to reduce the number of units checked here.
struct rb_mjit_unit *unit = NULL;
ccan_list_for_each(&stale_units.head, unit, unode) {
@@ -1328,13 +1012,13 @@ mjit_free_iseq(const rb_iseq_t *iseq)
if (!mjit_enabled)
return;
- if (ISEQ_BODY(iseq)->jit_unit) {
- // jit_unit is not freed here because it may be referred by multiple
+ if (ISEQ_BODY(iseq)->mjit_unit) {
+ // mjit_unit is not freed here because it may be referred by multiple
// lists of units. `get_from_list` and `mjit_finish` do the job.
- ISEQ_BODY(iseq)->jit_unit->iseq = NULL;
+ ISEQ_BODY(iseq)->mjit_unit->iseq = NULL;
}
// Units in stale_units (list of over-speculated and invalidated code) are not referenced from
- // `ISEQ_BODY(iseq)->jit_unit` anymore (because new one replaces that). So we need to check them too.
+ // `ISEQ_BODY(iseq)->mjit_unit` anymore (because new one replaces that). So we need to check them too.
// TODO: we should be able to reduce the number of units checked here.
struct rb_mjit_unit *unit = NULL;
ccan_list_for_each(&stale_units.head, unit, unode) {
@@ -1364,8 +1048,7 @@ free_list(struct rb_mjit_unit_list *list, bool close_handle_p)
if (unit->handle && dlclose(unit->handle)) {
mjit_warning("failed to close handle for u%d: %s", unit->id, dlerror());
}
- clean_temp_files(unit);
- free(unit);
+ xfree(unit);
}
else {
free_unit(unit);
@@ -1374,192 +1057,132 @@ free_list(struct rb_mjit_unit_list *list, bool close_handle_p)
list->length = 0;
}
-// Register a new continuation with execution context `ec`. Return MJIT info about
-// the continuation.
-struct mjit_cont *
-mjit_cont_new(rb_execution_context_t *ec)
-{
- struct mjit_cont *cont;
-
- // We need to use calloc instead of something like ZALLOC to avoid triggering GC here.
- // When this function is called from rb_thread_alloc through rb_threadptr_root_fiber_setup,
- // the thread is still being prepared and marking it causes SEGV.
- cont = calloc(1, sizeof(struct mjit_cont));
- if (cont == NULL)
- rb_memerror();
- cont->ec = ec;
-
- CRITICAL_SECTION_START(3, "in mjit_cont_new");
- if (first_cont == NULL) {
- cont->next = cont->prev = NULL;
- }
- else {
- cont->prev = NULL;
- cont->next = first_cont;
- first_cont->prev = cont;
- }
- first_cont = cont;
- CRITICAL_SECTION_FINISH(3, "in mjit_cont_new");
-
- return cont;
-}
-
-// Unregister continuation `cont`.
-void
-mjit_cont_free(struct mjit_cont *cont)
+static struct rb_mjit_unit*
+create_unit(enum rb_mjit_unit_type type)
{
- CRITICAL_SECTION_START(3, "in mjit_cont_new");
- if (cont == first_cont) {
- first_cont = cont->next;
- if (first_cont != NULL)
- first_cont->prev = NULL;
- }
- else {
- cont->prev->next = cont->next;
- if (cont->next != NULL)
- cont->next->prev = cont->prev;
+ struct rb_mjit_unit *unit = ZALLOC_N(struct rb_mjit_unit, 1);
+ unit->id = current_unit_num++;
+ unit->type = type;
+ if (type == MJIT_UNIT_BATCH) {
+ ccan_list_head_init(&unit->units.head);
}
- CRITICAL_SECTION_FINISH(3, "in mjit_cont_new");
-
- free(cont);
+ return unit;
}
-// Finish work with continuation info.
-static void
-finish_conts(void)
+static struct rb_mjit_unit*
+create_iseq_unit(const rb_iseq_t *iseq)
{
- struct mjit_cont *cont, *next;
-
- for (cont = first_cont; cont != NULL; cont = next) {
- next = cont->next;
- xfree(cont);
- }
+ struct rb_mjit_unit *unit = create_unit(MJIT_UNIT_ISEQ);
+ unit->iseq = (rb_iseq_t *)iseq;
+ ISEQ_BODY(iseq)->mjit_unit = unit;
+ return unit;
}
-static void mjit_wait(struct rb_iseq_constant_body *body);
+static void mjit_wait(struct rb_mjit_unit *unit);
// Check the unit queue and start mjit_compile if nothing is in progress.
static void
check_unit_queue(void)
{
+ if (mjit_opts.custom) return; // Custom RubyVM::MJIT.compile is in use
if (worker_stopped) return;
if (current_cc_pid != 0) return; // still compiling
- // Run unload_units after it's requested `max_cache_size / 10` (default: 10) times.
- // This throttles the call to mitigate locking in unload_units. It also throttles JIT compaction.
- int throttle_threshold = mjit_opts.max_cache_size / 10;
- if (unload_requests >= throttle_threshold) {
- unload_units();
- unload_requests = 0;
- if (active_units.length == mjit_opts.max_cache_size && mjit_opts.wait) { // Sometimes all methods may be in use
- mjit_opts.max_cache_size++; // avoid infinite loop on `rb_mjit_wait_call`. Note that --jit-wait is just for testing.
- verbose(1, "No units can be unloaded -- incremented max-cache-size to %d for --jit-wait", mjit_opts.max_cache_size);
- }
- }
- if (active_units.length >= mjit_opts.max_cache_size) return; // wait until unload_units makes a progress
+ // TODO: resurrect unload_units
+ if (active_units_length >= mjit_opts.max_cache_size) return; // wait until unload_units makes a progress
- // Dequeue a unit
- struct rb_mjit_unit *unit = get_from_list(&unit_queue);
- if (unit == NULL) return;
+ // No ISEQ in unit_queue has enough calls to trigger JIT
+ if (!mjit_compile_p) return;
+ mjit_compile_p = false;
-#ifdef _WIN32
- // Synchronously compile methods on Windows.
- // mswin: No SIGCHLD, MinGW: directly compiling .c to .so doesn't work
- mjit_func_t func = convert_unit_to_func(unit);
- MJIT_ATOMIC_SET(ISEQ_BODY(unit->iseq)->jit_func, func);
- if ((uintptr_t)func > (uintptr_t)LAST_JIT_ISEQ_FUNC) {
- add_to_list(unit, &active_units);
+ // Compile all ISEQs in unit_queue together
+ struct rb_mjit_unit *unit = create_unit(MJIT_UNIT_BATCH);
+ struct rb_mjit_unit *child_unit = NULL;
+ VM_ASSERT(unit_queue.length > 0);
+ while ((child_unit = get_from_list(&unit_queue)) != NULL && (active_units_length + unit->units.length) < mjit_opts.max_cache_size) {
+ add_to_list(child_unit, &unit->units);
+ ISEQ_BODY(child_unit->iseq)->jit_func = (jit_func_t)MJIT_FUNC_COMPILING;
}
-#else
+
+ // Run the MJIT compiler synchronously
current_cc_ms = real_ms_time();
current_cc_unit = unit;
- current_cc_pid = start_mjit_compile(unit);
-
- // JIT failure
- if (current_cc_pid == -1) {
- current_cc_pid = 0;
- current_cc_unit->iseq->body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC; // TODO: consider unit->compact_p
- current_cc_unit = NULL;
+ bool success = mjit_batch(unit);
+ if (!success) {
+ mjit_notify_waitpid(1);
return;
}
+ // Run the C compiler asynchronously (unless --mjit-wait)
if (mjit_opts.wait) {
- mjit_wait(unit->iseq->body);
+ int exit_code = c_compile_unit(unit);
+ mjit_notify_waitpid(exit_code);
}
-#endif
-}
-
-// Create unit for `iseq`. This function may be called from an MJIT worker.
-static struct rb_mjit_unit*
-create_unit(const rb_iseq_t *iseq)
-{
- // To prevent GC, don't use ZALLOC // TODO: just use ZALLOC
- struct rb_mjit_unit *unit = calloc(1, sizeof(struct rb_mjit_unit));
- if (unit == NULL)
- return NULL;
-
- unit->id = current_unit_num++;
- if (iseq == NULL) { // Compact unit
- unit->compact_p = true;
- } else { // Normal unit
- unit->iseq = (rb_iseq_t *)iseq;
- ISEQ_BODY(iseq)->jit_unit = unit;
+ else {
+ current_cc_pid = start_c_compile_unit(unit);
+ if (current_cc_pid == -1) { // JIT failure
+ mjit_notify_waitpid(1);
+ }
}
- return unit;
}
// Check if it should compact all JIT code and start it as needed
static void
check_compaction(void)
{
-#if USE_JIT_COMPACTION
// Allow only `max_cache_size / 100` times (default: 100) of compaction.
// Note: GC of compacted code has not been implemented yet.
int max_compact_size = mjit_opts.max_cache_size / 100;
if (max_compact_size < 10) max_compact_size = 10;
- // Run unload_units after it's requested `max_cache_size / 10` (default: 10) times.
- // This throttles the call to mitigate locking in unload_units. It also throttles JIT compaction.
- int throttle_threshold = mjit_opts.max_cache_size / 10;
+ // Run JIT compaction only when it's going to add 10%+ units.
+ int throttle_threshold = active_units_length / 10;
if (compact_units.length < max_compact_size
+ && active_units_length - compact_units_length > throttle_threshold
&& ((!mjit_opts.wait && unit_queue.length == 0 && active_units.length > 1)
- || (active_units.length == mjit_opts.max_cache_size && compact_units.length * throttle_threshold <= total_unloads))) { // throttle compaction by total_unloads
- struct rb_mjit_unit *unit = create_unit(NULL);
- if (unit != NULL) {
- // TODO: assert unit is null
- current_cc_ms = real_ms_time();
- current_cc_unit = unit;
- current_cc_pid = start_mjit_compact(unit);
- // TODO: check -1
+ || (active_units_length == mjit_opts.max_cache_size))) {
+ struct rb_mjit_unit *unit = create_unit(MJIT_UNIT_COMPACT);
+
+ // Run the MJIT compiler synchronously
+ current_cc_ms = real_ms_time();
+ current_cc_unit = unit;
+ bool success = mjit_compact(unit);
+ if (!success) {
+ mjit_notify_waitpid(1);
+ return;
+ }
+
+ // Run the C compiler asynchronously (unless --mjit-wait)
+ if (mjit_opts.wait) {
+ int exit_code = c_compile_unit(unit);
+ mjit_notify_waitpid(exit_code);
+ }
+ else {
+ current_cc_pid = start_c_compile_unit(unit);
+ if (current_cc_pid == -1) { // JIT failure
+ mjit_notify_waitpid(1);
+ }
}
}
-#endif
}
// Check the current CC process if any, and start a next C compiler process as needed.
void
-mjit_notify_waitpid(int status)
+mjit_notify_waitpid(int exit_code)
{
- // TODO: check current_cc_pid?
+ VM_ASSERT(mjit_opts.wait || current_cc_pid != 0);
current_cc_pid = 0;
// Delete .c file
char c_file[MAXPATHLEN];
sprint_uniq_filename(c_file, (int)sizeof(c_file), current_cc_unit->id, MJIT_TMP_PREFIX, ".c");
- if (!mjit_opts.save_temps)
- remove_file(c_file);
// Check the result
- bool success = false;
- if (WIFEXITED(status)) {
- success = (WEXITSTATUS(status) == 0);
- }
- if (!success) {
+ if (exit_code != 0) {
verbose(2, "Failed to generate so");
- if (!current_cc_unit->compact_p) {
- current_cc_unit->iseq->body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
- }
+ // TODO: set MJIT_FUNC_FAILED to unit->units
+ // TODO: free list of unit->units
free_unit(current_cc_unit);
current_cc_unit = NULL;
return;
@@ -1568,43 +1191,22 @@ mjit_notify_waitpid(int status)
// Load .so file
char so_file[MAXPATHLEN];
sprint_uniq_filename(so_file, (int)sizeof(so_file), current_cc_unit->id, MJIT_TMP_PREFIX, DLEXT);
- if (current_cc_unit->compact_p) { // Compact unit
-#if USE_JIT_COMPACTION
- load_compact_funcs_from_so(current_cc_unit, c_file, so_file);
- current_cc_unit = NULL;
-#else
- RUBY_ASSERT(!current_cc_unit->compact_p);
-#endif
- }
- else { // Normal unit
- // Load the function from so
- char funcname[MAXPATHLEN];
- sprint_funcname(funcname, current_cc_unit);
- void *func = load_func_from_so(so_file, funcname, current_cc_unit);
-
- // Delete .so file
- if (!mjit_opts.save_temps)
- remove_file(so_file);
-
- // Set the jit_func if successful
- if (current_cc_unit->iseq != NULL) { // mjit_free_iseq could nullify this
- rb_iseq_t *iseq = current_cc_unit->iseq;
- if ((uintptr_t)func > (uintptr_t)LAST_JIT_ISEQ_FUNC) {
- double end_time = real_ms_time();
- verbose(1, "JIT success (%.1fms): %s@%s:%ld -> %s",
- end_time - current_cc_ms, RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
- RSTRING_PTR(rb_iseq_path(iseq)), FIX2LONG(ISEQ_BODY(iseq)->location.first_lineno), c_file);
-
- add_to_list(current_cc_unit, &active_units);
- }
- MJIT_ATOMIC_SET(ISEQ_BODY(iseq)->jit_func, func);
- } // TODO: free unit on else?
+ switch (current_cc_unit->type) {
+ case MJIT_UNIT_ISEQ:
+ rb_bug("unreachable: current_cc_unit->type must not be MJIT_UNIT_ISEQ");
+ case MJIT_UNIT_BATCH:
+ load_batch_funcs_from_so(current_cc_unit, c_file, so_file);
current_cc_unit = NULL;
// Run compaction if it should
if (!stop_worker_p) {
check_compaction();
}
+ break;
+ case MJIT_UNIT_COMPACT:
+ load_compact_funcs_from_so(current_cc_unit, c_file, so_file);
+ current_cc_unit = NULL;
+ break;
}
// Skip further compilation if mjit_finish is trying to stop it
@@ -1621,33 +1223,154 @@ mjit_target_iseq_p(const rb_iseq_t *iseq)
struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
return (body->type == ISEQ_TYPE_METHOD || body->type == ISEQ_TYPE_BLOCK)
&& !body->builtin_inline_p
- && strcmp("<internal:mjit>", RSTRING_PTR(rb_iseq_path(iseq)));
+ && strcmp("<internal:mjit>", RSTRING_PTR(rb_iseq_path(iseq))) != 0;
+}
+
+// RubyVM::MJIT
+static VALUE rb_mMJIT = 0;
+// RubyVM::MJIT::C
+static VALUE rb_mMJITC = 0;
+// RubyVM::MJIT::Compiler
+static VALUE rb_cMJITCompiler = 0;
+// RubyVM::MJIT::CPointer::Struct_rb_iseq_t
+static VALUE rb_cMJITIseqPtr = 0;
+// RubyVM::MJIT::CPointer::Struct_IC
+static VALUE rb_cMJITICPtr = 0;
+// RubyVM::MJIT::Compiler
+static VALUE rb_mMJITHooks = 0;
+
+#define WITH_MJIT_DISABLED(stmt) do { \
+ bool original_call_p = mjit_call_p; \
+ mjit_call_p = false; \
+ stmt; \
+ mjit_call_p = original_call_p; \
+ if (mjit_cancel_p) mjit_call_p = false; \
+} while (0);
+
+// Hook MJIT when BOP is redefined.
+MJIT_FUNC_EXPORTED void
+rb_mjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop)
+{
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ WITH_MJIT_DISABLED({
+ rb_funcall(rb_mMJITHooks, rb_intern("on_bop_redefined"), 2, INT2NUM(redefined_flag), INT2NUM((int)bop));
+ });
+}
+
+// Hook MJIT when CME is invalidated.
+MJIT_FUNC_EXPORTED void
+rb_mjit_cme_invalidate(rb_callable_method_entry_t *cme)
+{
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ WITH_MJIT_DISABLED({
+ VALUE cme_klass = rb_funcall(rb_mMJITC, rb_intern("rb_callable_method_entry_struct"), 0);
+ VALUE cme_ptr = rb_funcall(cme_klass, rb_intern("new"), 1, SIZET2NUM((size_t)cme));
+ rb_funcall(rb_mMJITHooks, rb_intern("on_cme_invalidate"), 1, cme_ptr);
+ });
+}
+
+// Hook MJIT when Ractor is spawned.
+void
+rb_mjit_before_ractor_spawn(void)
+{
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ WITH_MJIT_DISABLED({
+ rb_funcall(rb_mMJITHooks, rb_intern("on_ractor_spawn"), 0);
+ });
}
-// If recompile_p is true, the call is initiated by mjit_recompile.
-// This assumes the caller holds CRITICAL_SECTION when recompile_p is true.
static void
-mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info, bool recompile_p)
+mjit_constant_state_changed(void *data)
+{
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ ID id = (ID)data;
+ WITH_MJIT_DISABLED({
+ rb_funcall(rb_mMJITHooks, rb_intern("on_constant_state_changed"), 1, ID2SYM(id));
+ });
+}
+
+// Hook MJIT when constant state is changed.
+MJIT_FUNC_EXPORTED void
+rb_mjit_constant_state_changed(ID id)
+{
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ // Asynchronously hook the Ruby code since this is hooked during a "Ruby critical section".
+ extern int rb_workqueue_register(unsigned flags, rb_postponed_job_func_t func, void *data);
+ rb_workqueue_register(0, mjit_constant_state_changed, (void *)id);
+}
+
+// Hook MJIT when constant IC is updated.
+MJIT_FUNC_EXPORTED void
+rb_mjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx)
{
- // TODO: Support non-main Ractors
- if (!mjit_enabled || pch_status == PCH_FAILED || !rb_ractor_main_p())
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ WITH_MJIT_DISABLED({
+ VALUE iseq_ptr = rb_funcall(rb_cMJITIseqPtr, rb_intern("new"), 1, SIZET2NUM((size_t)iseq));
+ VALUE ic_ptr = rb_funcall(rb_cMJITICPtr, rb_intern("new"), 1, SIZET2NUM((size_t)ic));
+ rb_funcall(rb_mMJITHooks, rb_intern("on_constant_ic_update"), 3, iseq_ptr, ic_ptr, UINT2NUM(insn_idx));
+ });
+}
+
+// Hook MJIT when TracePoint is enabled.
+MJIT_FUNC_EXPORTED void
+rb_mjit_tracing_invalidate_all(rb_event_flag_t new_iseq_events)
+{
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ WITH_MJIT_DISABLED({
+ rb_funcall(rb_mMJITHooks, rb_intern("on_tracing_invalidate_all"), 1, UINT2NUM(new_iseq_events));
+ });
+}
+
+// [experimental] Call custom RubyVM::MJIT.compile if defined
+static void
+mjit_hook_custom_compile(const rb_iseq_t *iseq)
+{
+ WITH_MJIT_DISABLED({
+ VALUE iseq_class = rb_funcall(rb_mMJITC, rb_intern("rb_iseq_t"), 0);
+ VALUE iseq_ptr = rb_funcall(iseq_class, rb_intern("new"), 1, ULONG2NUM((size_t)iseq));
+ VALUE jit_func = rb_funcall(rb_mMJIT, rb_intern("compile"), 1, iseq_ptr);
+ ISEQ_BODY(iseq)->jit_func = (jit_func_t)NUM2ULONG(jit_func);
+ });
+}
+
+static void
+mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info)
+{
+ if (!mjit_enabled) return;
+ if (mjit_opts.custom) { // Hook custom RubyVM::MJIT.compile if defined
+ mjit_hook_custom_compile(iseq);
+ return;
+ }
+ if (pch_status != PCH_SUCCESS || !rb_ractor_main_p()) // TODO: Support non-main Ractors
return;
if (!mjit_target_iseq_p(iseq)) {
- ISEQ_BODY(iseq)->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC; // skip mjit_wait
+ ISEQ_BODY(iseq)->jit_func = (jit_func_t)MJIT_FUNC_FAILED; // skip mjit_wait
return;
}
- RB_DEBUG_COUNTER_INC(mjit_add_iseq_to_process);
- ISEQ_BODY(iseq)->jit_func = (mjit_func_t)NOT_READY_JIT_ISEQ_FUNC;
- create_unit(iseq);
- if (ISEQ_BODY(iseq)->jit_unit == NULL)
- // Failure in creating the unit.
- return;
- if (compile_info != NULL)
- ISEQ_BODY(iseq)->jit_unit->compile_info = *compile_info;
- add_to_list(ISEQ_BODY(iseq)->jit_unit, &unit_queue);
- if (active_units.length >= mjit_opts.max_cache_size) {
- unload_requests++;
+ // For batching multiple ISEQs, we only enqueue ISEQs when total_calls reaches call_threshold,
+ // and compile all enqueued ISEQs when any ISEQ reaches call_threshold * 2.
+ bool recompile_p = !MJIT_FUNC_STATE_P(ISEQ_BODY(iseq)->jit_func);
+ if (!ISEQ_BODY(iseq)->mjit_unit || recompile_p) { // call_threshold, or recompile
+ // Discard an old unit with recompile_p
+ if (recompile_p) {
+ ISEQ_BODY(iseq)->mjit_unit->iseq = NULL; // Ignore this from compaction
+ ISEQ_BODY(iseq)->jit_func = (jit_func_t)MJIT_FUNC_NOT_COMPILED;
+ active_units_length--;
+ }
+
+ // Create a new unit and enqueue it
+ struct rb_mjit_unit *unit = create_iseq_unit(iseq);
+ if (recompile_p) {
+ VM_ASSERT(compile_info != NULL);
+ unit->compile_info = *compile_info;
+ }
+ add_to_list(unit, &unit_queue);
+ ISEQ_BODY(iseq)->total_calls = 0; // come here again :)
+ }
+ else { // call_threshold * 2
+ VM_ASSERT(compile_info == NULL);
+ mjit_compile_p = true; // compile all ISEQs in unit_queue
}
}
@@ -1656,26 +1379,30 @@ mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_inf
void
rb_mjit_add_iseq_to_process(const rb_iseq_t *iseq)
{
- mjit_add_iseq_to_process(iseq, NULL, false);
+ mjit_add_iseq_to_process(iseq, NULL);
check_unit_queue();
}
-// For this timeout seconds, --jit-wait will wait for JIT compilation finish.
-#define MJIT_WAIT_TIMEOUT_SECONDS 60
+// For this timeout seconds, mjit_finish will wait for JIT compilation finish.
+#define MJIT_WAIT_TIMEOUT_SECONDS 5
static void
-mjit_wait(struct rb_iseq_constant_body *body)
+mjit_wait(struct rb_mjit_unit *unit)
{
pid_t initial_pid = current_cc_pid;
- struct timeval tv;
+ if (initial_pid == 0) {
+ mjit_warning("initial_pid was 0 on mjit_wait");
+ return;
+ }
+ if (pch_status == PCH_FAILED) return;
+
int tries = 0;
- tv.tv_sec = 0;
- tv.tv_usec = 1000;
- while (body == NULL ? current_cc_pid == initial_pid : body->jit_func == (mjit_func_t)NOT_READY_JIT_ISEQ_FUNC) { // TODO: refactor this
+ struct timeval tv = { .tv_sec = 0, .tv_usec = 1000 };
+ while (current_cc_pid == initial_pid) {
tries++;
- if (tries / 1000 > MJIT_WAIT_TIMEOUT_SECONDS || pch_status == PCH_FAILED) {
- if (body != NULL) {
- body->jit_func = (mjit_func_t) NOT_COMPILED_JIT_ISEQ_FUNC; // JIT worker seems dead. Give up.
+ if (tries / 1000 > MJIT_WAIT_TIMEOUT_SECONDS) {
+ if (unit->type == MJIT_UNIT_ISEQ) {
+ unit->iseq->body->jit_func = (jit_func_t)MJIT_FUNC_FAILED; // C compiler was too slow. Give up.
}
mjit_warning("timed out to wait for JIT finish");
break;
@@ -1685,50 +1412,24 @@ mjit_wait(struct rb_iseq_constant_body *body)
}
}
-static void
-mjit_wait_unit(struct rb_mjit_unit *unit)
-{
- if (unit->compact_p) {
- mjit_wait(NULL);
- }
- else {
- mjit_wait(current_cc_unit->iseq->body);
- }
-}
-
-// Wait for JIT compilation finish for --jit-wait, and call the function pointer
-// if the compiled result is not NOT_COMPILED_JIT_ISEQ_FUNC.
-VALUE
-rb_mjit_wait_call(rb_execution_context_t *ec, struct rb_iseq_constant_body *body)
-{
- if (worker_stopped)
- return Qundef;
-
- mjit_wait(body);
- if ((uintptr_t)body->jit_func <= (uintptr_t)LAST_JIT_ISEQ_FUNC) {
- return Qundef;
- }
- return body->jit_func(ec, ec->cfp);
-}
-
struct rb_mjit_compile_info*
rb_mjit_iseq_compile_info(const struct rb_iseq_constant_body *body)
{
- VM_ASSERT(body->jit_unit != NULL);
- return &body->jit_unit->compile_info;
+ VM_ASSERT(body->mjit_unit != NULL);
+ return &body->mjit_unit->compile_info;
}
static void
mjit_recompile(const rb_iseq_t *iseq)
{
- if ((uintptr_t)ISEQ_BODY(iseq)->jit_func <= (uintptr_t)LAST_JIT_ISEQ_FUNC)
+ if (MJIT_FUNC_STATE_P(ISEQ_BODY(iseq)->jit_func))
return;
verbose(1, "JIT recompile: %s@%s:%d", RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
- RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(ISEQ_BODY(iseq)->location.first_lineno));
- VM_ASSERT(ISEQ_BODY(iseq)->jit_unit != NULL);
+ RSTRING_PTR(rb_iseq_path(iseq)), ISEQ_BODY(iseq)->location.first_lineno);
+ VM_ASSERT(ISEQ_BODY(iseq)->mjit_unit != NULL);
- mjit_add_iseq_to_process(iseq, &ISEQ_BODY(iseq)->jit_unit->compile_info, true);
+ mjit_add_iseq_to_process(iseq, &ISEQ_BODY(iseq)->mjit_unit->compile_info);
check_unit_queue();
}
@@ -1786,16 +1487,6 @@ init_header_filename(void)
const char *basedir = "";
size_t baselen = 0;
char *p;
-#ifdef _WIN32
- static const char libpathflag[] =
-# ifdef _MSC_VER
- "-LIBPATH:"
-# else
- "-L"
-# endif
- ;
- const size_t libpathflag_len = sizeof(libpathflag) - 1;
-#endif
#ifdef LOAD_RELATIVE
basedir_val = ruby_prefix_path;
@@ -1837,7 +1528,6 @@ init_header_filename(void)
}
else
#endif
-#ifndef _MSC_VER
{
// A name of the header file included in any C file generated by MJIT for iseqs.
static const char header_name[] = MJIT_HEADER_INSTALL_DIR "/" MJIT_MIN_HEADER_NAME;
@@ -1857,56 +1547,15 @@ init_header_filename(void)
}
pch_file = get_uniq_filename(0, MJIT_TMP_PREFIX "h", ".h.gch");
-#else
- {
- static const char pch_name[] = MJIT_HEADER_INSTALL_DIR "/" MJIT_PRECOMPILED_HEADER_NAME;
- const size_t pch_name_len = sizeof(pch_name) - 1;
-
- pch_file = xmalloc(baselen + pch_name_len + 1);
- p = append_str2(pch_file, basedir, baselen);
- p = append_str2(p, pch_name, pch_name_len + 1);
- if ((fd = rb_cloexec_open(pch_file, O_RDONLY, 0)) < 0) {
- verbose(1, "Cannot access precompiled header file: %s", pch_file);
- xfree(pch_file);
- pch_file = NULL;
- return false;
- }
- (void)close(fd);
- }
-#endif
-
-#ifdef _WIN32
- basedir_val = ruby_archlibdir_path;
- basedir = StringValuePtr(basedir_val);
- baselen = RSTRING_LEN(basedir_val);
- libruby_pathflag = p = xmalloc(libpathflag_len + baselen + 1);
- p = append_str(p, libpathflag);
- p = append_str2(p, basedir, baselen);
- *p = '\0';
-#endif
return true;
}
-#ifdef _WIN32
-UINT rb_w32_system_tmpdir(WCHAR *path, UINT len);
-#endif
-
static char *
system_default_tmpdir(void)
{
// c.f. ext/etc/etc.c:etc_systmpdir()
-#ifdef _WIN32
- WCHAR tmppath[_MAX_PATH];
- UINT len = rb_w32_system_tmpdir(tmppath, numberof(tmppath));
- if (len) {
- int blen = WideCharToMultiByte(CP_UTF8, 0, tmppath, len, NULL, 0, NULL, NULL);
- char *tmpdir = xmalloc(blen + 1);
- WideCharToMultiByte(CP_UTF8, 0, tmppath, len, tmpdir, blen, NULL, NULL);
- tmpdir[blen] = '\0';
- return tmpdir;
- }
-#elif defined _CS_DARWIN_USER_TEMP_DIR
+#if defined _CS_DARWIN_USER_TEMP_DIR
char path[MAXPATHLEN];
size_t len = confstr(_CS_DARWIN_USER_TEMP_DIR, path, sizeof(path));
if (len > 0) {
@@ -1934,19 +1583,17 @@ check_tmpdir(const char *dir)
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
if (!S_ISDIR(st.st_mode)) return FALSE;
-#ifndef _WIN32
-# ifndef S_IWOTH
+#ifndef S_IWOTH
# define S_IWOTH 002
-# endif
+#endif
if (st.st_mode & S_IWOTH) {
-# ifdef S_ISVTX
+#ifdef S_ISVTX
if (!(st.st_mode & S_ISVTX)) return FALSE;
-# else
+#else
return FALSE;
-# endif
+#endif
}
if (access(dir, W_OK)) return FALSE;
-#endif
return TRUE;
}
@@ -1967,9 +1614,9 @@ system_tmpdir(void)
// Minimum value for JIT cache size.
#define MIN_CACHE_SIZE 10
// Default permitted number of units with a JIT code kept in memory.
-#define DEFAULT_MAX_CACHE_SIZE 10000
+#define DEFAULT_MAX_CACHE_SIZE 100
// A default threshold used to add iseq to JIT.
-#define DEFAULT_MIN_CALLS_TO_ADD 10000
+#define DEFAULT_CALL_THRESHOLD 10000
// Start MJIT worker. Return TRUE if worker is successfully started.
static bool
@@ -2030,19 +1677,19 @@ mjit_setup_options(const char *s, struct mjit_options *mjit_opt)
return;
}
else if (opt_match_noarg(s, l, "warnings")) {
- mjit_opt->warnings = 1;
+ mjit_opt->warnings = true;
}
else if (opt_match(s, l, "debug")) {
if (*s)
mjit_opt->debug_flags = strdup(s + 1);
else
- mjit_opt->debug = 1;
+ mjit_opt->debug = true;
}
else if (opt_match_noarg(s, l, "wait")) {
- mjit_opt->wait = 1;
+ mjit_opt->wait = true;
}
else if (opt_match_noarg(s, l, "save-temps")) {
- mjit_opt->save_temps = 1;
+ mjit_opt->save_temps = true;
}
else if (opt_match(s, l, "verbose")) {
mjit_opt->verbose = *s ? atoi(s + 1) : 1;
@@ -2050,8 +1697,12 @@ mjit_setup_options(const char *s, struct mjit_options *mjit_opt)
else if (opt_match_arg(s, l, "max-cache")) {
mjit_opt->max_cache_size = atoi(s + 1);
}
- else if (opt_match_arg(s, l, "min-calls")) {
- mjit_opt->min_calls = atoi(s + 1);
+ else if (opt_match_arg(s, l, "call-threshold")) {
+ mjit_opt->call_threshold = atoi(s + 1);
+ }
+ // --mjit=pause is an undocumented feature for experiments
+ else if (opt_match_noarg(s, l, "pause")) {
+ mjit_opt->pause = true;
}
else {
rb_raise(rb_eRuntimeError,
@@ -2061,15 +1712,15 @@ mjit_setup_options(const char *s, struct mjit_options *mjit_opt)
#define M(shortopt, longopt, desc) RUBY_OPT_MESSAGE(shortopt, longopt, desc)
const struct ruby_opt_message mjit_option_messages[] = {
- M("--mjit-warnings", "", "Enable printing JIT warnings"),
- M("--mjit-debug", "", "Enable JIT debugging (very slow), or add cflags if specified"),
- M("--mjit-wait", "", "Wait until JIT compilation finishes every time (for testing)"),
- M("--mjit-save-temps", "", "Save JIT temporary files in $TMP or /tmp (for testing)"),
- M("--mjit-verbose=num", "", "Print JIT logs of level num or less to stderr (default: 0)"),
- M("--mjit-max-cache=num", "", "Max number of methods to be JIT-ed in a cache (default: "
+ M("--mjit-warnings", "", "Enable printing JIT warnings"),
+ M("--mjit-debug", "", "Enable JIT debugging (very slow), or add cflags if specified"),
+ M("--mjit-wait", "", "Wait until JIT compilation finishes every time (for testing)"),
+ M("--mjit-save-temps", "", "Save JIT temporary files in $TMP or /tmp (for testing)"),
+ M("--mjit-verbose=num", "", "Print JIT logs of level num or less to stderr (default: 0)"),
+ M("--mjit-max-cache=num", "", "Max number of methods to be JIT-ed in a cache (default: "
STRINGIZE(DEFAULT_MAX_CACHE_SIZE) ")"),
- M("--mjit-min-calls=num", "", "Number of calls to trigger JIT (for testing, default: "
- STRINGIZE(DEFAULT_MIN_CALLS_TO_ADD) ")"),
+ M("--mjit-call-threshold=num", "", "Number of calls to trigger JIT (for testing, default: "
+ STRINGIZE(DEFAULT_CALL_THRESHOLD) ")"),
{0}
};
#undef M
@@ -2080,24 +1731,41 @@ const struct ruby_opt_message mjit_option_messages[] = {
void
mjit_init(const struct mjit_options *opts)
{
+ VM_ASSERT(mjit_enabled);
mjit_opts = *opts;
- mjit_enabled = true;
+
+ // MJIT doesn't support miniruby, but it might reach here by MJIT_FORCE_ENABLE.
+ rb_mMJIT = rb_const_get(rb_cRubyVM, rb_intern("MJIT"));
+ if (!rb_const_defined(rb_mMJIT, rb_intern("Compiler"))) {
+ verbose(1, "Disabling MJIT because RubyVM::MJIT::Compiler is not defined");
+ mjit_enabled = false;
+ return;
+ }
+ rb_mMJITC = rb_const_get(rb_mMJIT, rb_intern("C"));
+ rb_cMJITCompiler = rb_funcall(rb_const_get(rb_mMJIT, rb_intern("Compiler")), rb_intern("new"), 0);
+ rb_cMJITIseqPtr = rb_funcall(rb_mMJITC, rb_intern("rb_iseq_t"), 0);
+ rb_cMJITICPtr = rb_funcall(rb_mMJITC, rb_intern("IC"), 0);
+ rb_funcall(rb_cMJITICPtr, rb_intern("new"), 1, SIZET2NUM(0)); // Trigger no-op constant events before enabling hooks
+ rb_mMJITHooks = rb_const_get(rb_mMJIT, rb_intern("Hooks"));
+
mjit_call_p = true;
+ mjit_pid = getpid();
// Normalize options
- if (mjit_opts.min_calls == 0)
- mjit_opts.min_calls = DEFAULT_MIN_CALLS_TO_ADD;
+ if (mjit_opts.call_threshold == 0)
+ mjit_opts.call_threshold = DEFAULT_CALL_THRESHOLD;
+ if (mjit_opts.call_threshold % 2 == 1) {
+ mjit_opts.call_threshold += 1;
+ mjit_warning("--mjit-call-threshold must be an even number. Using %d instead.", mjit_opts.call_threshold);
+ }
+ mjit_opts.call_threshold /= 2; // Half for enqueue, half for trigger
if (mjit_opts.max_cache_size <= 0)
mjit_opts.max_cache_size = DEFAULT_MAX_CACHE_SIZE;
if (mjit_opts.max_cache_size < MIN_CACHE_SIZE)
mjit_opts.max_cache_size = MIN_CACHE_SIZE;
// Initialize variables for compilation
-#ifdef _MSC_VER
- pch_status = PCH_SUCCESS; // has prebuilt precompiled header
-#else
pch_status = PCH_NOT_READY;
-#endif
cc_path = CC_COMMON_ARGS[0];
verbose(2, "MJIT: CC defaults to %s", cc_path);
cc_common_args = xmalloc(sizeof(CC_COMMON_ARGS));
@@ -2105,10 +1773,10 @@ mjit_init(const struct mjit_options *opts)
cc_added_args = split_flags(opts->debug_flags);
xfree(opts->debug_flags);
#if MJIT_CFLAGS_PIPE
- // eliminate a flag incompatible with `-pipe`
+ // Filter out `-save-temps`. It's a C compiler flag used by update-deps and not compatible with `-pipe`.
for (size_t i = 0, j = 0; i < sizeof(CC_COMMON_ARGS) / sizeof(char *); i++) {
if (CC_COMMON_ARGS[i] && strncmp("-save-temps", CC_COMMON_ARGS[i], strlen("-save-temps")) == 0)
- continue; // skip -save-temps flag
+ continue; // Skip `-save-temps`
cc_common_args[j] = CC_COMMON_ARGS[i];
j++;
}
@@ -2126,25 +1794,16 @@ mjit_init(const struct mjit_options *opts)
// Initialize mutex
rb_native_mutex_initialize(&mjit_engine_mutex);
- rb_native_cond_initialize(&mjit_pch_wakeup);
- rb_native_cond_initialize(&mjit_client_wakeup);
- rb_native_cond_initialize(&mjit_worker_wakeup);
- rb_native_cond_initialize(&mjit_gc_wakeup);
-
- // Make sure the saved_ec of the initial thread's root_fiber is scanned by mark_ec_units.
- //
- // rb_threadptr_root_fiber_setup for the initial thread is called before mjit_init,
- // meaning mjit_cont_new is skipped for the root_fiber. Therefore we need to call
- // rb_fiber_init_mjit_cont again with mjit_enabled=true to set the root_fiber's mjit_cont.
- rb_fiber_init_mjit_cont(GET_EC()->fiber_ptr);
-
- // Initialize worker thread
- start_worker();
-#ifndef _MSC_VER
- // TODO: Consider running C compiler asynchronously
- make_pch();
-#endif
+ // If --mjit=pause is given, lazily start MJIT when RubyVM::MJIT.resume is called.
+ // You can use it to control MJIT warmup, or to customize the JIT implementation.
+ if (!mjit_opts.pause) {
+ // TODO: Consider running C compiler asynchronously
+ make_pch();
+
+ // Enable MJIT compilation
+ start_worker();
+ }
}
static void
@@ -2152,7 +1811,7 @@ stop_worker(void)
{
stop_worker_p = true;
if (current_cc_unit != NULL) {
- mjit_wait_unit(current_cc_unit);
+ mjit_wait(current_cc_unit);
}
worker_stopped = true;
}
@@ -2171,7 +1830,7 @@ mjit_pause(bool wait_p)
// Flush all queued units with no option or `wait: true`
if (wait_p) {
while (current_cc_unit != NULL) {
- mjit_wait_unit(current_cc_unit);
+ mjit_wait(current_cc_unit);
}
}
@@ -2190,6 +1849,19 @@ mjit_resume(void)
return Qfalse;
}
+ // Lazily prepare PCH when --mjit=pause is given
+ if (pch_status == PCH_NOT_READY) {
+ if (rb_respond_to(rb_mMJIT, rb_intern("compile"))) {
+ // [experimental] defining RubyVM::MJIT.compile allows you to replace JIT
+ mjit_opts.custom = true;
+ pch_status = PCH_SUCCESS;
+ }
+ else {
+ // Lazy MJIT boot
+ make_pch();
+ }
+ }
+
if (!start_worker()) {
rb_raise(rb_eRuntimeError, "Failed to resume MJIT worker");
}
@@ -2221,22 +1893,6 @@ mjit_child_after_fork(void)
start_worker();
}
-// Edit 0 to 1 to enable this feature for investigating hot methods
-#define MJIT_COUNTER 0
-#if MJIT_COUNTER
-static void
-mjit_dump_total_calls(void)
-{
- struct rb_mjit_unit *unit;
- fprintf(stderr, "[MJIT_COUNTER] total_calls of active_units:\n");
- ccan_list_for_each(&active_units.head, unit, unode) {
- const rb_iseq_t *iseq = unit->iseq;
- fprintf(stderr, "%8ld: %s@%s:%d\n", ISEQ_BODY(iseq)->total_calls, RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
- RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(ISEQ_BODY(iseq)->location.first_lineno));
- }
-}
-#endif
-
// Finish the threads processing units and creating PCH, finalize
// and free MJIT data. It should be called last during MJIT
// life.
@@ -2254,21 +1910,11 @@ mjit_finish(bool close_handle_p)
stop_worker();
rb_native_mutex_destroy(&mjit_engine_mutex);
- rb_native_cond_destroy(&mjit_pch_wakeup);
- rb_native_cond_destroy(&mjit_client_wakeup);
- rb_native_cond_destroy(&mjit_worker_wakeup);
- rb_native_cond_destroy(&mjit_gc_wakeup);
-
-#if MJIT_COUNTER
- mjit_dump_total_calls();
-#endif
-#ifndef _MSC_VER // mswin has prebuilt precompiled header
- if (!mjit_opts.save_temps && getpid() == pch_owner_pid)
+ if (!mjit_opts.save_temps && getpid() == pch_owner_pid && pch_status == PCH_SUCCESS && !mjit_opts.custom)
remove_file(pch_file);
xfree(header_file); header_file = NULL;
-#endif
xfree((void *)cc_common_args); cc_common_args = NULL;
for (char **flag = cc_added_args; *flag != NULL; flag++)
xfree(*flag);
@@ -2281,7 +1927,6 @@ mjit_finish(bool close_handle_p)
free_list(&active_units, close_handle_p);
free_list(&compact_units, close_handle_p);
free_list(&stale_units, close_handle_p);
- finish_conts();
mjit_enabled = false;
verbose(1, "Successful MJIT finish");
@@ -2298,6 +1943,13 @@ mjit_mark(void)
return;
RUBY_MARK_ENTER("mjit");
+ // Mark objects used by the MJIT compiler
+ rb_gc_mark(rb_cMJITCompiler);
+ rb_gc_mark(rb_cMJITIseqPtr);
+ rb_gc_mark(rb_cMJITICPtr);
+ rb_gc_mark(rb_mMJITHooks);
+
+ // Mark JIT-compiled ISEQs
struct rb_mjit_unit *unit = NULL;
ccan_list_for_each(&active_units.head, unit, unode) {
rb_gc_mark((VALUE)unit->iseq);
@@ -2311,9 +1963,9 @@ void
mjit_mark_cc_entries(const struct rb_iseq_constant_body *const body)
{
const struct rb_callcache **cc_entries;
- if (body->jit_unit && (cc_entries = body->jit_unit->cc_entries) != NULL) {
- // It must be `body->jit_unit->cc_entries_size` instead of `body->ci_size` to mark children's cc_entries
- for (unsigned int i = 0; i < body->jit_unit->cc_entries_size; i++) {
+ if (body->mjit_unit && (cc_entries = body->mjit_unit->cc_entries) != NULL) {
+ // It must be `body->mjit_unit->cc_entries_size` instead of `body->ci_size` to mark children's cc_entries
+ for (unsigned int i = 0; i < body->mjit_unit->cc_entries_size; i++) {
const struct rb_callcache *cc = cc_entries[i];
if (cc != NULL && vm_cc_markable(cc)) {
// Pin `cc` and `cc->cme` against GC.compact as their addresses may be written in JIT-ed code.
@@ -2324,6 +1976,24 @@ mjit_mark_cc_entries(const struct rb_iseq_constant_body *const body)
}
}
+// Compile ISeq to C code in `f`. It returns true if it succeeds to compile.
+bool
+mjit_compile(FILE *f, const rb_iseq_t *iseq, const char *funcname, int id)
+{
+ bool original_call_p = mjit_call_p;
+ mjit_call_p = false; // Avoid impacting JIT metrics by itself
+
+ VALUE iseq_ptr = rb_funcall(rb_cMJITIseqPtr, rb_intern("new"), 1, ULONG2NUM((size_t)iseq));
+ VALUE src = rb_funcall(rb_cMJITCompiler, rb_intern("compile"), 3,
+ iseq_ptr, rb_str_new_cstr(funcname), INT2NUM(id));
+ if (!NIL_P(src)) {
+ fprintf(f, "%s", RSTRING_PTR(src));
+ }
+
+ mjit_call_p = original_call_p;
+ return !NIL_P(src);
+}
+
#include "mjit.rbinc"
#endif // USE_MJIT
diff --git a/mjit.h b/mjit.h
index fad18208fb..260cf4af78 100644
--- a/mjit.h
+++ b/mjit.h
@@ -2,9 +2,10 @@
#define RUBY_MJIT_H 1
/**********************************************************************
- mjit.h - Interface to MRI method JIT compiler for Ruby's main thread
+ mjit.h - Interface to MRI method JIT compiler
Copyright (C) 2017 Vladimir Makarov <vmakarov@redhat.com>.
+ Copyright (C) 2017 Takashi Kokubun <k0kubun@ruby-lang.org>.
**********************************************************************/
@@ -14,52 +15,54 @@
# if USE_MJIT
-#include "debug_counter.h"
#include "ruby.h"
#include "vm_core.h"
-#include "yjit.h"
// Special address values of a function generated from the
// corresponding iseq by MJIT:
-enum rb_mjit_iseq_func {
- // ISEQ has never been enqueued to unit_queue yet
- NOT_ADDED_JIT_ISEQ_FUNC = 0,
+enum rb_mjit_func_state {
+ // ISEQ has not been compiled yet
+ MJIT_FUNC_NOT_COMPILED = 0,
// ISEQ is already queued for the machine code generation but the
// code is not ready yet for the execution
- NOT_READY_JIT_ISEQ_FUNC = 1,
+ MJIT_FUNC_COMPILING = 1,
// ISEQ included not compilable insn, some internal assertion failed
// or the unit is unloaded
- NOT_COMPILED_JIT_ISEQ_FUNC = 2,
- // End mark
- LAST_JIT_ISEQ_FUNC = 3
+ MJIT_FUNC_FAILED = 2,
};
+// Return true if jit_func is part of enum rb_mjit_func_state
+#define MJIT_FUNC_STATE_P(jit_func) ((uintptr_t)(jit_func) <= (uintptr_t)MJIT_FUNC_FAILED)
// MJIT options which can be defined on the MRI command line.
struct mjit_options {
// Converted from "jit" feature flag to tell the enablement
// information to ruby_show_version().
- char on;
+ bool on;
// Save temporary files after MRI finish. The temporary files
// include the pre-compiled header, C code file generated for ISEQ,
// and the corresponding object file.
- char save_temps;
+ bool save_temps;
// Print MJIT warnings to stderr.
- char warnings;
+ bool warnings;
// Disable compiler optimization and add debug symbols. It can be
// very slow.
- char debug;
+ bool debug;
// Add arbitrary cflags.
char* debug_flags;
- // If not 0, all ISeqs are synchronously compiled. For testing.
- unsigned int wait;
+ // If true, all ISeqs are synchronously compiled. For testing.
+ bool wait;
// Number of calls to trigger JIT compilation. For testing.
- unsigned int min_calls;
+ unsigned int call_threshold;
// Force printing info about MJIT work of level VERBOSE or
// less. 0=silence, 1=medium, 2=verbose.
int verbose;
// Maximal permitted number of iseq JIT codes in a MJIT memory
// cache.
int max_cache_size;
+ // [experimental] Do not start MJIT until MJIT.resume is called.
+ bool pause;
+ // [experimental] Call custom RubyVM::MJIT.compile instead of MJIT.
+ bool custom;
};
// State of optimization switches
@@ -76,14 +79,13 @@ struct rb_mjit_compile_info {
bool disable_const_cache;
};
-typedef VALUE (*mjit_func_t)(rb_execution_context_t *, rb_control_frame_t *);
+typedef VALUE (*jit_func_t)(rb_execution_context_t *, rb_control_frame_t *);
RUBY_SYMBOL_EXPORT_BEGIN
RUBY_EXTERN struct mjit_options mjit_opts;
RUBY_EXTERN bool mjit_call_p;
extern void rb_mjit_add_iseq_to_process(const rb_iseq_t *iseq);
-extern VALUE rb_mjit_wait_call(rb_execution_context_t *ec, struct rb_iseq_constant_body *body);
extern struct rb_mjit_compile_info* rb_mjit_iseq_compile_info(const struct rb_iseq_constant_body *body);
extern void rb_mjit_recompile_send(const rb_iseq_t *iseq);
extern void rb_mjit_recompile_ivar(const rb_iseq_t *iseq);
@@ -98,105 +100,15 @@ extern void mjit_init(const struct mjit_options *opts);
extern void mjit_free_iseq(const rb_iseq_t *iseq);
extern void mjit_update_references(const rb_iseq_t *iseq);
extern void mjit_mark(void);
-extern struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec);
-extern void mjit_cont_free(struct mjit_cont *cont);
extern void mjit_mark_cc_entries(const struct rb_iseq_constant_body *const body);
-extern void mjit_notify_waitpid(int status);
+extern void mjit_notify_waitpid(int exit_code);
-# ifdef MJIT_HEADER
-NOINLINE(static COLDFUNC VALUE mjit_exec_slowpath(rb_execution_context_t *ec, const rb_iseq_t *iseq, struct rb_iseq_constant_body *body));
-# else
-static inline VALUE mjit_exec_slowpath(rb_execution_context_t *ec, const rb_iseq_t *iseq, struct rb_iseq_constant_body *body);
-# endif
-static VALUE
-mjit_exec_slowpath(rb_execution_context_t *ec, const rb_iseq_t *iseq, struct rb_iseq_constant_body *body)
-{
- uintptr_t func_i = (uintptr_t)(body->jit_func);
- ASSUME(func_i <= LAST_JIT_ISEQ_FUNC);
- switch ((enum rb_mjit_iseq_func)func_i) {
- case NOT_ADDED_JIT_ISEQ_FUNC:
- RB_DEBUG_COUNTER_INC(mjit_exec_not_added);
- if (body->total_calls == mjit_opts.min_calls) {
- rb_mjit_add_iseq_to_process(iseq);
- if (UNLIKELY(mjit_opts.wait)) {
- return rb_mjit_wait_call(ec, body);
- }
- }
- break;
- case NOT_READY_JIT_ISEQ_FUNC:
- RB_DEBUG_COUNTER_INC(mjit_exec_not_ready);
- break;
- case NOT_COMPILED_JIT_ISEQ_FUNC:
- RB_DEBUG_COUNTER_INC(mjit_exec_not_compiled);
- break;
- default: // to avoid warning with LAST_JIT_ISEQ_FUNC
- break;
- }
- return Qundef;
-}
-
-// Try to execute the current iseq in ec. Use JIT code if it is ready.
-// If it is not, add ISEQ to the compilation queue and return Qundef for MJIT.
-// YJIT compiles on the thread running the iseq.
-static inline VALUE
-mjit_exec(rb_execution_context_t *ec)
-{
- const rb_iseq_t *iseq = ec->cfp->iseq;
- struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
- bool yjit_enabled = false;
-#ifndef MJIT_HEADER
- // Don't want to compile with YJIT or use code generated by YJIT
- // when running inside code generated by MJIT.
- yjit_enabled = rb_yjit_enabled_p();
-#endif
-
- if (mjit_call_p || yjit_enabled) {
- body->total_calls++;
- }
-
-#ifndef MJIT_HEADER
- if (yjit_enabled && !mjit_call_p && body->total_calls == rb_yjit_call_threshold()) {
- // If we couldn't generate any code for this iseq, then return
- // Qundef so the interpreter will handle the call.
- if (!rb_yjit_compile_iseq(iseq, ec)) {
- return Qundef;
- }
- }
-#endif
-
- if (!(mjit_call_p || yjit_enabled))
- return Qundef;
-
- RB_DEBUG_COUNTER_INC(mjit_exec);
-
- mjit_func_t func = body->jit_func;
-
- // YJIT tried compiling this function once before and couldn't do
- // it, so return Qundef so the interpreter handles it.
- if (yjit_enabled && func == 0) {
- return Qundef;
- }
-
- if (UNLIKELY((uintptr_t)func <= LAST_JIT_ISEQ_FUNC)) {
-# ifdef MJIT_HEADER
- RB_DEBUG_COUNTER_INC(mjit_frame_JT2VM);
-# else
- RB_DEBUG_COUNTER_INC(mjit_frame_VM2VM);
-# endif
- return mjit_exec_slowpath(ec, iseq, body);
- }
-
-# ifdef MJIT_HEADER
- RB_DEBUG_COUNTER_INC(mjit_frame_JT2JT);
-# else
- RB_DEBUG_COUNTER_INC(mjit_frame_VM2JT);
-# endif
- RB_DEBUG_COUNTER_INC(mjit_exec_call_func);
- // Under SystemV x64 calling convention
- // ec -> RDI
- // cfp -> RSI
- return func(ec, ec->cfp);
-}
+extern void rb_mjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop);
+extern void rb_mjit_cme_invalidate(rb_callable_method_entry_t *cme);
+extern void rb_mjit_before_ractor_spawn(void);
+extern void rb_mjit_constant_state_changed(ID id);
+extern void rb_mjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx);
+extern void rb_mjit_tracing_invalidate_all(rb_event_flag_t new_iseq_events);
void mjit_child_after_fork(void);
@@ -212,13 +124,18 @@ void mjit_finish(bool close_handle_p);
# else // USE_MJIT
static inline void mjit_cancel_all(const char *reason){}
-static inline struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec){return NULL;}
-static inline void mjit_cont_free(struct mjit_cont *cont){}
static inline void mjit_free_iseq(const rb_iseq_t *iseq){}
static inline void mjit_mark(void){}
-static inline VALUE mjit_exec(rb_execution_context_t *ec) { return Qundef; /* unreachable */ }
+static inline VALUE jit_exec(rb_execution_context_t *ec) { return Qundef; /* unreachable */ }
static inline void mjit_child_after_fork(void){}
+static inline void rb_mjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop) {}
+static inline void rb_mjit_cme_invalidate(rb_callable_method_entry_t *cme) {}
+static inline void rb_mjit_before_ractor_spawn(void) {}
+static inline void rb_mjit_constant_state_changed(ID id) {}
+static inline void rb_mjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx) {}
+static inline void rb_mjit_tracing_invalidate_all(rb_event_flag_t new_iseq_events) {}
+
#define mjit_enabled false
static inline VALUE mjit_pause(bool wait_p){ return Qnil; } // unreachable
static inline VALUE mjit_resume(void){ return Qnil; } // unreachable
diff --git a/mjit.rb b/mjit.rb
index baa107d6dc..717ab832a4 100644
--- a/mjit.rb
+++ b/mjit.rb
@@ -1,13 +1,37 @@
module RubyVM::MJIT
+ # Return true if MJIT is enabled.
def self.enabled?
Primitive.cexpr! 'RBOOL(mjit_enabled)'
end
+ # Stop generating JITed code.
def self.pause(wait: true)
Primitive.cexpr! 'mjit_pause(RTEST(wait))'
end
+ # Start generating JITed code again after pause.
def self.resume
Primitive.cexpr! 'mjit_resume()'
end
end
+
+if RubyVM::MJIT.enabled?
+ begin
+ require 'fiddle'
+ require 'fiddle/import'
+ rescue LoadError
+ return # miniruby doesn't support MJIT
+ end
+
+ # forward declaration for ruby_vm/mjit/compiler
+ RubyVM::MJIT::C = Object.new # :nodoc:
+
+ require 'ruby_vm/mjit/c_type'
+ require 'ruby_vm/mjit/instruction'
+ require 'ruby_vm/mjit/compiler'
+ require 'ruby_vm/mjit/hooks'
+
+ module RubyVM::MJIT
+ private_constant(*constants)
+ end
+end
diff --git a/mjit_c.c b/mjit_c.c
new file mode 100644
index 0000000000..9ba023e84b
--- /dev/null
+++ b/mjit_c.c
@@ -0,0 +1,43 @@
+/**********************************************************************
+
+ mjit_c.c - C helpers for MJIT
+
+ Copyright (C) 2017 Takashi Kokubun <k0kubun@ruby-lang.org>.
+
+**********************************************************************/
+
+#include "ruby/internal/config.h" // defines USE_MJIT
+
+#if USE_MJIT
+
+#include "mjit.h"
+#include "mjit_c.h"
+#include "internal.h"
+#include "internal/compile.h"
+#include "internal/hash.h"
+#include "yjit.h"
+#include "vm_insnhelper.h"
+
+#include "insns.inc"
+#include "insns_info.inc"
+
+#include "mjit_sp_inc.inc"
+
+#if SIZEOF_LONG == SIZEOF_VOIDP
+#define NUM2PTR(x) NUM2ULONG(x)
+#define PTR2NUM(x) ULONG2NUM(x)
+#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
+#define NUM2PTR(x) NUM2ULL(x)
+#define PTR2NUM(x) ULL2NUM(x)
+#endif
+
+// An offsetof implementation that works for unnamed struct and union.
+// Multiplying 8 for compatibility with libclang's offsetof.
+#define OFFSETOF(ptr, member) RB_SIZE2NUM(((char *)&ptr.member - (char*)&ptr) * 8)
+
+#define SIZEOF(type) RB_SIZE2NUM(sizeof(type))
+#define SIGNED_TYPE_P(type) RBOOL((type)(-1) < (type)(1))
+
+#include "mjit_c.rbinc"
+
+#endif // USE_MJIT
diff --git a/mjit_c.h b/mjit_c.h
new file mode 100644
index 0000000000..cc4040c9df
--- /dev/null
+++ b/mjit_c.h
@@ -0,0 +1,97 @@
+// This file is parsed by tool/mjit/generate.rb to generate mjit_c.rb
+#ifndef MJIT_C_H
+#define MJIT_C_H
+
+#include "ruby/internal/config.h"
+#include "vm_core.h"
+#include "vm_callinfo.h"
+#include "builtin.h"
+#include "ccan/list/list.h"
+#include "mjit.h"
+#include "shape.h"
+
+// Macros to check if a position is already compiled using compile_status.stack_size_for_pos
+#define NOT_COMPILED_STACK_SIZE -1
+#define ALREADY_COMPILED_P(status, pos) (status->stack_size_for_pos[pos] != NOT_COMPILED_STACK_SIZE)
+
+// Linked list of struct rb_mjit_unit.
+struct rb_mjit_unit_list {
+ struct ccan_list_head head;
+ int length; // the list length
+};
+
+enum rb_mjit_unit_type {
+ // Single-ISEQ unit for unit_queue
+ MJIT_UNIT_ISEQ = 0,
+ // Multi-ISEQ unit for mjit_batch
+ MJIT_UNIT_BATCH = 1,
+ // All-ISEQ unit for mjit_compact
+ MJIT_UNIT_COMPACT = 2,
+};
+
+// The unit structure that holds metadata of ISeq for MJIT.
+// TODO: Use different structs for ISEQ and BATCH/COMPACT
+struct rb_mjit_unit {
+ struct ccan_list_node unode;
+ // Unique order number of unit.
+ int id;
+ // Type of this unit
+ enum rb_mjit_unit_type type;
+
+ /* MJIT_UNIT_ISEQ */
+ // ISEQ for a non-batch unit
+ rb_iseq_t *iseq;
+ // Only used by unload_units. Flag to check this unit is currently on stack or not.
+ bool used_code_p;
+ // mjit_compile's optimization switches
+ struct rb_mjit_compile_info compile_info;
+ // captured CC values, they should be marked with iseq.
+ const struct rb_callcache **cc_entries;
+ // ISEQ_BODY(iseq)->ci_size + ones of inlined iseqs
+ unsigned int cc_entries_size;
+
+ /* MJIT_UNIT_BATCH, MJIT_UNIT_COMPACT */
+ // Dlopen handle of the loaded object file.
+ void *handle;
+ // Units compacted by this batch
+ struct rb_mjit_unit_list units; // MJIT_UNIT_BATCH only
+};
+
+// Storage to keep data which is consistent in each conditional branch.
+// This is created and used for one `compile_insns` call and its values
+// should be copied for extra `compile_insns` call.
+struct compile_branch {
+ unsigned int stack_size; // this simulates sp (stack pointer) of YARV
+ bool finish_p; // if true, compilation in this branch should stop and let another branch to be compiled
+};
+
+// For propagating information needed for lazily pushing a frame.
+struct inlined_call_context {
+ int orig_argc; // ci->orig_argc
+ VALUE me; // vm_cc_cme(cc)
+ int param_size; // def_iseq_ptr(vm_cc_cme(cc)->def)->body->param.size
+ int local_size; // def_iseq_ptr(vm_cc_cme(cc)->def)->body->local_table_size
+};
+
+// Storage to keep compiler's status. This should have information
+// which is global during one `mjit_compile` call. Ones conditional
+// in each branch should be stored in `compile_branch`.
+struct compile_status {
+ bool success; // has true if compilation has had no issue
+ int *stack_size_for_pos; // stack_size_for_pos[pos] has stack size for the position (otherwise -1)
+ // If true, JIT-ed code will use local variables to store pushed values instead of
+ // using VM's stack and moving stack pointer.
+ bool local_stack_p;
+ // Index of call cache entries captured to compiled_iseq to be marked on GC
+ int cc_entries_index;
+ // A pointer to root (i.e. not inlined) iseq being compiled.
+ const struct rb_iseq_constant_body *compiled_iseq;
+ int compiled_id; // Just a copy of compiled_iseq->jit_unit->id
+ // Mutated optimization levels
+ struct rb_mjit_compile_info *compile_info;
+ // If `inlined_iseqs[pos]` is not NULL, `mjit_compile_body` tries to inline ISeq there.
+ const struct rb_iseq_constant_body **inlined_iseqs;
+ struct inlined_call_context inline_context;
+};
+
+#endif /* MJIT_C_H */
diff --git a/mjit_c.rb b/mjit_c.rb
new file mode 100644
index 0000000000..966289c5ab
--- /dev/null
+++ b/mjit_c.rb
@@ -0,0 +1,807 @@
+# frozen_string_literal: true
+# Part of this file is generated by tool/mjit/bindgen.rb.
+# Run `make mjit-bindgen` to update code between "MJIT bindgen begin" and "MJIT bindgen end".
+module RubyVM::MJIT # :nodoc: all
+ # This `class << C` section is for calling C functions. For importing variables
+ # or macros as is, please consider using tool/mjit/bindgen.rb instead.
+ class << C
+ def rb_hash_values(cdhash_addr)
+ Primitive.cexpr! 'rb_hash_values((VALUE)NUM2PTR(cdhash_addr))'
+ end
+
+ def builtin_compiler(buf, bf_ptr, index, stack_size, builtin_inline_p)
+ _bf_addr = bf_ptr.to_i
+ # Call "mjit_compile_invokebuiltin_for_#{func}" in mk_builtin_loader.rb
+ Primitive.cstmt! %{
+ RB_BUILTIN bf = (RB_BUILTIN)NUM2PTR(_bf_addr);
+ bf->compiler(buf, NIL_P(index) ? -1 : NUM2LONG(index), NUM2UINT(stack_size), RTEST(builtin_inline_p));
+ return Qnil;
+ }
+ end
+
+ def has_cache_for_send(cc_ptr, insn)
+ _cc_addr = cc_ptr.to_i
+ Primitive.cstmt! %{
+ extern bool rb_vm_opt_cfunc_p(CALL_CACHE cc, int insn);
+ CALL_CACHE cc = (CALL_CACHE)NUM2PTR(_cc_addr);
+ bool has_cache = vm_cc_cme(cc) != NULL &&
+ !(vm_cc_cme(cc)->def->type == VM_METHOD_TYPE_CFUNC && rb_vm_opt_cfunc_p(cc, NUM2INT(insn)));
+ return RBOOL(has_cache);
+ }
+ end
+
+ def rb_shape_get_shape_by_id(shape_id)
+ _shape_id = shape_id.to_i
+ shape_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_shape_get_shape_by_id((shape_id_t)NUM2UINT(_shape_id)))'
+ rb_shape_t.new(shape_addr)
+ end
+
+ def rb_iseq_check(iseq)
+ _iseq_addr = iseq.to_i
+ iseq_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_iseq_check((rb_iseq_t *)NUM2PTR(_iseq_addr)))'
+ rb_iseq_t.new(iseq_addr)
+ end
+
+ def rb_iseq_path(iseq)
+ _iseq_addr = iseq.to_i
+ Primitive.cexpr! 'rb_iseq_path((rb_iseq_t *)NUM2PTR(_iseq_addr))'
+ end
+
+ def rb_iseq_first_lineno(iseq)
+ _iseq_addr = iseq.to_i
+ Primitive.cexpr! 'rb_iseq_first_lineno((rb_iseq_t *)NUM2PTR(_iseq_addr))'
+ end
+
+ def vm_ci_argc(ci)
+ _ci_addr = ci.to_i
+ Primitive.cexpr! 'UINT2NUM(vm_ci_argc((CALL_INFO)NUM2PTR(_ci_addr)))'
+ end
+
+ def vm_ci_flag(ci)
+ _ci_addr = ci.to_i
+ Primitive.cexpr! 'UINT2NUM(vm_ci_flag((CALL_INFO)NUM2PTR(_ci_addr)))'
+ end
+
+ def rb_splat_or_kwargs_p(ci)
+ _ci_addr = ci.to_i
+ Primitive.cstmt! %{
+ extern bool rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci);
+ return RBOOL(rb_splat_or_kwargs_p((CALL_INFO)NUM2PTR(_ci_addr)));
+ }
+ end
+
+ # Returns true if iseq can use fastpath for setup, otherwise NULL. This becomes true in the same condition
+ # as CC_SET_FASTPATH (in vm_callee_setup_arg) is called from vm_call_iseq_setup.
+ def fastpath_applied_iseq_p(ci_ptr, cc_ptr, iseq_ptr)
+ _ci_addr = ci_ptr.to_i
+ _cc_addr = cc_ptr.to_i
+ _iseq_addr = iseq_ptr.to_i
+ Primitive.cstmt! %q{
+ extern bool rb_simple_iseq_p(const rb_iseq_t *iseq);
+ CALL_INFO ci = (CALL_INFO)NUM2PTR(_ci_addr);
+ CALL_CACHE cc = (CALL_CACHE)NUM2PTR(_cc_addr);
+ const rb_iseq_t *iseq = (rb_iseq_t *)NUM2PTR(_iseq_addr);
+
+ bool result = iseq != NULL
+ && !(vm_ci_flag(ci) & VM_CALL_KW_SPLAT) && rb_simple_iseq_p(iseq) // Top of vm_callee_setup_arg. In this case, opt_pc is 0.
+ && vm_ci_argc(ci) == (unsigned int)ISEQ_BODY(iseq)->param.lead_num // exclude argument_arity_error (assumption: `calling->argc == ci->orig_argc` in send insns)
+ && vm_call_iseq_optimizable_p(ci, cc); // CC_SET_FASTPATH condition
+ return RBOOL(result);
+ }
+ end
+
+ def mjit_opts
+ addr = Primitive.cexpr! 'PTR2NUM((VALUE)&mjit_opts)'
+ mjit_options.new(addr)
+ end
+
+ def mjit_call_attribute_sp_inc(insn, operands)
+ _operands_addr = operands.to_i
+ Primitive.cexpr! 'LONG2NUM(mjit_call_attribute_sp_inc(NUM2INT(insn), (VALUE *)NUM2PTR(_operands_addr)))'
+ end
+
+ def mjit_capture_cc_entries(compiled_body, captured_body)
+ _compiled_body_addr = compiled_body.to_i
+ _captured_body_addr = captured_body.to_i
+ Primitive.cstmt! %{
+ extern int mjit_capture_cc_entries(const struct rb_iseq_constant_body *compiled_iseq, const struct rb_iseq_constant_body *captured_iseq);
+ return INT2NUM(mjit_capture_cc_entries((struct rb_iseq_constant_body *)NUM2PTR(_compiled_body_addr), (struct rb_iseq_constant_body *)NUM2PTR(_captured_body_addr)));
+ }
+ end
+
+ def mjit_cancel_all(reason)
+ Primitive.cstmt! %{
+ mjit_cancel_all(RSTRING_PTR(reason));
+ return Qnil;
+ }
+ end
+
+ # Convert encoded VM pointers to insn BINs.
+ def rb_vm_insn_decode(encoded)
+ Primitive.cexpr! 'INT2NUM(rb_vm_insn_decode(NUM2PTR(encoded)))'
+ end
+
+ # Convert insn BINs to encoded VM pointers. This one is not used by the compiler, but useful for debugging.
+ def rb_vm_insn_encode(bin)
+ Primitive.cexpr! 'PTR2NUM((VALUE)rb_vm_get_insns_address_table()[NUM2INT(bin)])'
+ end
+
+ def insn_may_depend_on_sp_or_pc(insn, opes)
+ _opes_addr = opes.to_i
+ Primitive.cexpr! 'RBOOL(insn_may_depend_on_sp_or_pc(NUM2INT(insn), (VALUE *)NUM2PTR(_opes_addr)))'
+ end
+
+ # Convert Integer VALUE to an actual Ruby object
+ def to_ruby(value)
+ Primitive.cexpr! '(VALUE)NUM2PTR(value)'
+ end
+
+ # Convert RubyVM::InstructionSequence to C.rb_iseq_t. Not used by the compiler, but useful for debugging.
+ def rb_iseqw_to_iseq(iseqw)
+ iseq_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_iseqw_to_iseq(iseqw))'
+ rb_iseq_t.new(iseq_addr)
+ end
+ end
+
+ ### MJIT bindgen begin ###
+
+ def C.USE_LAZY_LOAD
+ Primitive.cexpr! %q{ RBOOL(USE_LAZY_LOAD != 0) }
+ end
+
+ def C.NOT_COMPILED_STACK_SIZE
+ Primitive.cexpr! %q{ INT2NUM(NOT_COMPILED_STACK_SIZE) }
+ end
+
+ def C.VM_CALL_KW_SPLAT
+ Primitive.cexpr! %q{ INT2NUM(VM_CALL_KW_SPLAT) }
+ end
+
+ def C.VM_CALL_KW_SPLAT_bit
+ Primitive.cexpr! %q{ INT2NUM(VM_CALL_KW_SPLAT_bit) }
+ end
+
+ def C.VM_CALL_TAILCALL
+ Primitive.cexpr! %q{ INT2NUM(VM_CALL_TAILCALL) }
+ end
+
+ def C.VM_CALL_TAILCALL_bit
+ Primitive.cexpr! %q{ INT2NUM(VM_CALL_TAILCALL_bit) }
+ end
+
+ def C.VM_METHOD_TYPE_CFUNC
+ Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_CFUNC) }
+ end
+
+ def C.VM_METHOD_TYPE_ISEQ
+ Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_ISEQ) }
+ end
+
+ def C.RUBY_EVENT_CLASS
+ Primitive.cexpr! %q{ UINT2NUM(RUBY_EVENT_CLASS) }
+ end
+
+ def C.SHAPE_CAPACITY_CHANGE
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_CAPACITY_CHANGE) }
+ end
+
+ def C.SHAPE_FLAG_SHIFT
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_FLAG_SHIFT) }
+ end
+
+ def C.SHAPE_FROZEN
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_FROZEN) }
+ end
+
+ def C.SHAPE_ID_NUM_BITS
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_ID_NUM_BITS) }
+ end
+
+ def C.SHAPE_INITIAL_CAPACITY
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_INITIAL_CAPACITY) }
+ end
+
+ def C.SHAPE_IVAR
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_IVAR) }
+ end
+
+ def C.SHAPE_ROOT
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_ROOT) }
+ end
+
+ def C.INVALID_SHAPE_ID
+ Primitive.cexpr! %q{ ULONG2NUM(INVALID_SHAPE_ID) }
+ end
+
+ def C.SHAPE_MASK
+ Primitive.cexpr! %q{ ULONG2NUM(SHAPE_MASK) }
+ end
+
+ def C.rb_cFalseClass
+ Primitive.cexpr! %q{ PTR2NUM(rb_cFalseClass) }
+ end
+
+ def C.rb_cFloat
+ Primitive.cexpr! %q{ PTR2NUM(rb_cFloat) }
+ end
+
+ def C.rb_cInteger
+ Primitive.cexpr! %q{ PTR2NUM(rb_cInteger) }
+ end
+
+ def C.rb_cNilClass
+ Primitive.cexpr! %q{ PTR2NUM(rb_cNilClass) }
+ end
+
+ def C.rb_cSymbol
+ Primitive.cexpr! %q{ PTR2NUM(rb_cSymbol) }
+ end
+
+ def C.rb_cTrueClass
+ Primitive.cexpr! %q{ PTR2NUM(rb_cTrueClass) }
+ end
+
+ def C.CALL_DATA
+ @CALL_DATA ||= self.rb_call_data
+ end
+
+ def C.IC
+ @IC ||= self.iseq_inline_constant_cache
+ end
+
+ def C.IVC
+ @IVC ||= self.iseq_inline_iv_cache_entry
+ end
+
+ def C.RB_BUILTIN
+ @RB_BUILTIN ||= self.rb_builtin_function
+ end
+
+ def C.attr_index_t
+ @attr_index_t ||= CType::Immediate.parse("uint32_t")
+ end
+
+ def C.compile_branch
+ @compile_branch ||= CType::Struct.new(
+ "compile_branch", Primitive.cexpr!("SIZEOF(struct compile_branch)"),
+ stack_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct compile_branch *)NULL)), stack_size)")],
+ finish_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_branch *)NULL)), finish_p)")],
+ )
+ end
+
+ def C.compile_status
+ @compile_status ||= CType::Struct.new(
+ "compile_status", Primitive.cexpr!("SIZEOF(struct compile_status)"),
+ success: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), success)")],
+ stack_size_for_pos: [CType::Pointer.new { CType::Immediate.parse("int") }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), stack_size_for_pos)")],
+ local_stack_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), local_stack_p)")],
+ cc_entries_index: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), cc_entries_index)")],
+ compiled_iseq: [CType::Pointer.new { self.rb_iseq_constant_body }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_iseq)")],
+ compiled_id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_id)")],
+ compile_info: [CType::Pointer.new { self.rb_mjit_compile_info }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compile_info)")],
+ inlined_iseqs: [CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inlined_iseqs)")],
+ inline_context: [self.inlined_call_context, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inline_context)")],
+ )
+ end
+
+ def C.inlined_call_context
+ @inlined_call_context ||= CType::Struct.new(
+ "inlined_call_context", Primitive.cexpr!("SIZEOF(struct inlined_call_context)"),
+ orig_argc: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), orig_argc)")],
+ me: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), me)")],
+ param_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), param_size)")],
+ local_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), local_size)")],
+ )
+ end
+
+ def C.iseq_inline_constant_cache
+ @iseq_inline_constant_cache ||= CType::Struct.new(
+ "iseq_inline_constant_cache", Primitive.cexpr!("SIZEOF(struct iseq_inline_constant_cache)"),
+ entry: [CType::Pointer.new { self.iseq_inline_constant_cache_entry }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache *)NULL)), entry)")],
+ segments: [CType::Pointer.new { self.ID }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache *)NULL)), segments)")],
+ )
+ end
+
+ def C.iseq_inline_constant_cache_entry
+ @iseq_inline_constant_cache_entry ||= CType::Struct.new(
+ "iseq_inline_constant_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_constant_cache_entry)"),
+ flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), flags)")],
+ value: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), value)")],
+ _unused1: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), _unused1)")],
+ _unused2: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), _unused2)")],
+ ic_cref: [CType::Pointer.new { self.rb_cref_t }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), ic_cref)")],
+ )
+ end
+
+ def C.iseq_inline_iv_cache_entry
+ @iseq_inline_iv_cache_entry ||= CType::Struct.new(
+ "iseq_inline_iv_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_iv_cache_entry)"),
+ value: [CType::Immediate.parse("uintptr_t"), Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), value)")],
+ iv_set_name: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), iv_set_name)")],
+ )
+ end
+
+ def C.iseq_inline_storage_entry
+ @iseq_inline_storage_entry ||= CType::Union.new(
+ "iseq_inline_storage_entry", Primitive.cexpr!("SIZEOF(union iseq_inline_storage_entry)"),
+ once: CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((union iseq_inline_storage_entry *)NULL)->once)"),
+ running_thread: [CType::Pointer.new { self.rb_thread_struct }, Primitive.cexpr!("OFFSETOF(((union iseq_inline_storage_entry *)NULL)->once, running_thread)")],
+ value: [self.VALUE, Primitive.cexpr!("OFFSETOF(((union iseq_inline_storage_entry *)NULL)->once, value)")],
+ ),
+ ic_cache: self.iseq_inline_constant_cache,
+ iv_cache: self.iseq_inline_iv_cache_entry,
+ )
+ end
+
+ def C.mjit_options
+ @mjit_options ||= CType::Struct.new(
+ "mjit_options", Primitive.cexpr!("SIZEOF(struct mjit_options)"),
+ on: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), on)")],
+ save_temps: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), save_temps)")],
+ warnings: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), warnings)")],
+ debug: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug)")],
+ debug_flags: [CType::Pointer.new { CType::Immediate.parse("char") }, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug_flags)")],
+ wait: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), wait)")],
+ call_threshold: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), call_threshold)")],
+ verbose: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), verbose)")],
+ max_cache_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), max_cache_size)")],
+ pause: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), pause)")],
+ custom: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), custom)")],
+ )
+ end
+
+ def C.rb_builtin_function
+ @rb_builtin_function ||= CType::Struct.new(
+ "rb_builtin_function", Primitive.cexpr!("SIZEOF(struct rb_builtin_function)"),
+ func_ptr: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), func_ptr)")],
+ argc: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), argc)")],
+ index: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), index)")],
+ name: [CType::Pointer.new { CType::Immediate.parse("char") }, Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), name)")],
+ compiler: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), compiler)")],
+ )
+ end
+
+ def C.rb_call_data
+ @rb_call_data ||= CType::Struct.new(
+ "rb_call_data", Primitive.cexpr!("SIZEOF(struct rb_call_data)"),
+ ci: [CType::Pointer.new { self.rb_callinfo }, Primitive.cexpr!("OFFSETOF((*((struct rb_call_data *)NULL)), ci)")],
+ cc: [CType::Pointer.new { self.rb_callcache }, Primitive.cexpr!("OFFSETOF((*((struct rb_call_data *)NULL)), cc)")],
+ )
+ end
+
+ def C.rb_callable_method_entry_struct
+ @rb_callable_method_entry_struct ||= CType::Struct.new(
+ "rb_callable_method_entry_struct", Primitive.cexpr!("SIZEOF(struct rb_callable_method_entry_struct)"),
+ flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), flags)")],
+ defined_class: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), defined_class)")],
+ def: [CType::Pointer.new { self.rb_method_definition_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), def)")],
+ called_id: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), called_id)")],
+ owner: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), owner)")],
+ )
+ end
+
+ def C.rb_callcache
+ @rb_callcache ||= CType::Struct.new(
+ "rb_callcache", Primitive.cexpr!("SIZEOF(struct rb_callcache)"),
+ flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), flags)")],
+ klass: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), klass)")],
+ cme_: [CType::Pointer.new { self.rb_callable_method_entry_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), cme_)")],
+ call_: [self.vm_call_handler, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), call_)")],
+ aux_: [CType::Union.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_)"),
+ attr: CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_.attr)"),
+ value: [CType::Immediate.parse("uintptr_t"), Primitive.cexpr!("OFFSETOF(((struct rb_callcache *)NULL)->aux_.attr, value)")],
+ ),
+ method_missing_reason: self.method_missing_reason,
+ v: self.VALUE,
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), aux_)")],
+ )
+ end
+
+ def C.rb_callinfo
+ @rb_callinfo ||= CType::Struct.new(
+ "rb_callinfo", Primitive.cexpr!("SIZEOF(struct rb_callinfo)"),
+ flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), flags)")],
+ kwarg: [CType::Pointer.new { self.rb_callinfo_kwarg }, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), kwarg)")],
+ mid: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), mid)")],
+ flag: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), flag)")],
+ argc: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), argc)")],
+ )
+ end
+
+ def C.rb_control_frame_t
+ @rb_control_frame_t ||= CType::Struct.new(
+ "rb_control_frame_struct", Primitive.cexpr!("SIZEOF(struct rb_control_frame_struct)"),
+ pc: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), pc)")],
+ sp: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), sp)")],
+ iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), iseq)")],
+ self: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), self)")],
+ ep: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), ep)")],
+ block_code: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), block_code)")],
+ __bp__: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), __bp__)")],
+ jit_return: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), jit_return)")],
+ )
+ end
+
+ def C.rb_cref_t
+ @rb_cref_t ||= CType::Struct.new(
+ "rb_cref_struct", Primitive.cexpr!("SIZEOF(struct rb_cref_struct)"),
+ flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), flags)")],
+ refinements: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), refinements)")],
+ klass_or_self: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), klass_or_self)")],
+ next: [CType::Pointer.new { self.rb_cref_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), next)")],
+ scope_visi: [self.rb_scope_visibility_t, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), scope_visi)")],
+ )
+ end
+
+ def C.rb_execution_context_struct
+ @rb_execution_context_struct ||= CType::Struct.new(
+ "rb_execution_context_struct", Primitive.cexpr!("SIZEOF(struct rb_execution_context_struct)"),
+ vm_stack: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), vm_stack)")],
+ vm_stack_size: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), vm_stack_size)")],
+ cfp: [CType::Pointer.new { self.rb_control_frame_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), cfp)")],
+ tag: [CType::Pointer.new { self.rb_vm_tag }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), tag)")],
+ interrupt_flag: [self.rb_atomic_t, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), interrupt_flag)")],
+ interrupt_mask: [self.rb_atomic_t, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), interrupt_mask)")],
+ fiber_ptr: [CType::Pointer.new { self.rb_fiber_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), fiber_ptr)")],
+ thread_ptr: [CType::Pointer.new { self.rb_thread_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), thread_ptr)")],
+ local_storage: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage)")],
+ local_storage_recursive_hash: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage_recursive_hash)")],
+ local_storage_recursive_hash_for_trace: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage_recursive_hash_for_trace)")],
+ storage: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), storage)")],
+ root_lep: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), root_lep)")],
+ root_svar: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), root_svar)")],
+ ensure_list: [CType::Pointer.new { self.rb_ensure_list_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), ensure_list)")],
+ trace_arg: [CType::Pointer.new { self.rb_trace_arg_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), trace_arg)")],
+ errinfo: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), errinfo)")],
+ passed_block_handler: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), passed_block_handler)")],
+ raised_flag: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), raised_flag)")],
+ private_const_reference: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), private_const_reference)")],
+ machine: [CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_execution_context_struct *)NULL)->machine)"),
+ stack_start: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_start)")],
+ stack_end: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_end)")],
+ stack_maxsize: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_maxsize)")],
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), machine)")],
+ )
+ end
+
+ def C.rb_execution_context_t
+ @rb_execution_context_t ||= self.rb_execution_context_struct
+ end
+
+ def C.rb_iseq_constant_body
+ @rb_iseq_constant_body ||= CType::Struct.new(
+ "rb_iseq_constant_body", Primitive.cexpr!("SIZEOF(struct rb_iseq_constant_body)"),
+ type: [self.rb_iseq_type, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), type)")],
+ iseq_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), iseq_size)")],
+ iseq_encoded: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), iseq_encoded)")],
+ param: [CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->param)"),
+ flags: [CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->param.flags)"),
+ has_lead: [CType::BitField.new(1, 0), 0],
+ has_opt: [CType::BitField.new(1, 1), 1],
+ has_rest: [CType::BitField.new(1, 2), 2],
+ has_post: [CType::BitField.new(1, 3), 3],
+ has_kw: [CType::BitField.new(1, 4), 4],
+ has_kwrest: [CType::BitField.new(1, 5), 5],
+ has_block: [CType::BitField.new(1, 6), 6],
+ ambiguous_param0: [CType::BitField.new(1, 7), 7],
+ accepts_no_kwarg: [CType::BitField.new(1, 0), 8],
+ ruby2_keywords: [CType::BitField.new(1, 1), 9],
+ ), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, flags)")],
+ size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, size)")],
+ lead_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, lead_num)")],
+ opt_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, opt_num)")],
+ rest_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, rest_start)")],
+ post_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, post_start)")],
+ post_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, post_num)")],
+ block_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, block_start)")],
+ opt_table: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, opt_table)")],
+ keyword: [CType::Pointer.new { self.rb_iseq_param_keyword }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, keyword)")],
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), param)")],
+ location: [self.rb_iseq_location_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), location)")],
+ insns_info: [self.iseq_insn_info, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), insns_info)")],
+ local_table: [CType::Pointer.new { self.ID }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_table)")],
+ catch_table: [CType::Pointer.new { self.iseq_catch_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), catch_table)")],
+ parent_iseq: [CType::Pointer.new { self.rb_iseq_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), parent_iseq)")],
+ local_iseq: [CType::Pointer.new { self.rb_iseq_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_iseq)")],
+ is_entries: [CType::Pointer.new { self.iseq_inline_storage_entry }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), is_entries)")],
+ call_data: [CType::Pointer.new { self.rb_call_data }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), call_data)")],
+ variable: [CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->variable)"),
+ flip_count: [self.rb_snum_t, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, flip_count)")],
+ script_lines: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, script_lines)")],
+ coverage: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, coverage)")],
+ pc2branchindex: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, pc2branchindex)")],
+ original_iseq: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, original_iseq)")],
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), variable)")],
+ local_table_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_table_size)")],
+ ic_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ic_size)")],
+ ise_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ise_size)")],
+ ivc_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ivc_size)")],
+ icvarc_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), icvarc_size)")],
+ ci_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ci_size)")],
+ stack_max: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), stack_max)")],
+ catch_except_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), catch_except_p)")],
+ builtin_inline_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), builtin_inline_p)")],
+ mark_bits: [CType::Union.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->mark_bits)"),
+ list: CType::Pointer.new { self.iseq_bits_t },
+ single: self.iseq_bits_t,
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mark_bits)")],
+ outer_variables: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), outer_variables)")],
+ mandatory_only_iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mandatory_only_iseq)")],
+ jit_func: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_func)")],
+ total_calls: [CType::Immediate.parse("unsigned long"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), total_calls)")],
+ mjit_unit: [CType::Pointer.new { self.rb_mjit_unit }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mjit_unit)")],
+ )
+ end
+
+ def C.rb_iseq_location_t
+ @rb_iseq_location_t ||= CType::Struct.new(
+ "rb_iseq_location_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_location_struct)"),
+ pathobj: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), pathobj)"), true],
+ base_label: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), base_label)"), true],
+ label: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), label)"), true],
+ first_lineno: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), first_lineno)"), true],
+ node_id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), node_id)")],
+ code_location: [self.rb_code_location_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), code_location)")],
+ )
+ end
+
+ def C.rb_iseq_struct
+ @rb_iseq_struct ||= CType::Struct.new(
+ "rb_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_struct)"),
+ flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), flags)")],
+ wrapper: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), wrapper)")],
+ body: [CType::Pointer.new { self.rb_iseq_constant_body }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), body)")],
+ aux: [CType::Union.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux)"),
+ compile_data: CType::Pointer.new { self.iseq_compile_data },
+ loader: CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux.loader)"),
+ obj: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.loader, obj)")],
+ index: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.loader, index)")],
+ ),
+ exec: CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux.exec)"),
+ local_hooks: [CType::Pointer.new { self.rb_hook_list_struct }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.exec, local_hooks)")],
+ global_trace_events: [self.rb_event_flag_t, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.exec, global_trace_events)")],
+ ),
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), aux)")],
+ )
+ end
+
+ def C.rb_iseq_t
+ @rb_iseq_t ||= self.rb_iseq_struct
+ end
+
+ def C.rb_method_definition_struct
+ @rb_method_definition_struct ||= CType::Struct.new(
+ "rb_method_definition_struct", Primitive.cexpr!("SIZEOF(struct rb_method_definition_struct)"),
+ type: [CType::BitField.new(4, 0), 0],
+ iseq_overload: [CType::BitField.new(1, 4), 4],
+ no_redef_warning: [CType::BitField.new(1, 5), 5],
+ aliased: [CType::BitField.new(1, 6), 6],
+ reference_count: [CType::BitField.new(28, 0), 32],
+ body: [CType::Union.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_method_definition_struct *)NULL)->body)"),
+ iseq: self.rb_method_iseq_t,
+ cfunc: self.rb_method_cfunc_t,
+ attr: self.rb_method_attr_t,
+ alias: self.rb_method_alias_t,
+ refined: self.rb_method_refined_t,
+ bmethod: self.rb_method_bmethod_t,
+ optimized: self.rb_method_optimized_t,
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), body)")],
+ original_id: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), original_id)")],
+ method_serial: [CType::Immediate.parse("uintptr_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), method_serial)")],
+ )
+ end
+
+ def C.rb_method_iseq_t
+ @rb_method_iseq_t ||= CType::Struct.new(
+ "rb_method_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_method_iseq_struct)"),
+ iseqptr: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_method_iseq_struct *)NULL)), iseqptr)")],
+ cref: [CType::Pointer.new { self.rb_cref_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_method_iseq_struct *)NULL)), cref)")],
+ )
+ end
+
+ def C.rb_method_type_t
+ @rb_method_type_t ||= CType::Immediate.parse("int")
+ end
+
+ def C.rb_mjit_compile_info
+ @rb_mjit_compile_info ||= CType::Struct.new(
+ "rb_mjit_compile_info", Primitive.cexpr!("SIZEOF(struct rb_mjit_compile_info)"),
+ disable_ivar_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_ivar_cache)")],
+ disable_exivar_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_exivar_cache)")],
+ disable_send_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_send_cache)")],
+ disable_inlining: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_inlining)")],
+ disable_const_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_const_cache)")],
+ )
+ end
+
+ def C.rb_mjit_unit
+ @rb_mjit_unit ||= CType::Struct.new(
+ "rb_mjit_unit", Primitive.cexpr!("SIZEOF(struct rb_mjit_unit)"),
+ unode: [self.ccan_list_node, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), unode)")],
+ id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), id)")],
+ type: [self.rb_mjit_unit_type, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), type)")],
+ iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), iseq)")],
+ used_code_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), used_code_p)")],
+ compile_info: [self.rb_mjit_compile_info, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), compile_info)")],
+ cc_entries: [CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), cc_entries)")],
+ cc_entries_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), cc_entries_size)")],
+ handle: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), handle)")],
+ units: [self.rb_mjit_unit_list, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), units)")],
+ )
+ end
+
+ def C.rb_serial_t
+ @rb_serial_t ||= CType::Immediate.parse("unsigned long long")
+ end
+
+ def C.rb_shape
+ @rb_shape ||= CType::Struct.new(
+ "rb_shape", Primitive.cexpr!("SIZEOF(struct rb_shape)"),
+ edges: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), edges)")],
+ edge_name: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), edge_name)")],
+ next_iv_index: [self.attr_index_t, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), next_iv_index)")],
+ capacity: [CType::Immediate.parse("uint32_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), capacity)")],
+ type: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), type)")],
+ size_pool_index: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), size_pool_index)")],
+ parent_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), parent_id)")],
+ )
+ end
+
+ def C.rb_shape_t
+ @rb_shape_t ||= self.rb_shape
+ end
+
+ def C.VALUE
+ @VALUE ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(VALUE)"), Primitive.cexpr!("SIGNED_TYPE_P(VALUE)"))
+ end
+
+ def C.shape_id_t
+ @shape_id_t ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(shape_id_t)"), Primitive.cexpr!("SIGNED_TYPE_P(shape_id_t)"))
+ end
+
+ def C._Bool
+ CType::Bool.new
+ end
+
+ def C.ID
+ CType::Stub.new(:ID)
+ end
+
+ def C.rb_thread_struct
+ CType::Stub.new(:rb_thread_struct)
+ end
+
+ def C.vm_call_handler
+ CType::Stub.new(:vm_call_handler)
+ end
+
+ def C.method_missing_reason
+ CType::Stub.new(:method_missing_reason)
+ end
+
+ def C.rb_callinfo_kwarg
+ CType::Stub.new(:rb_callinfo_kwarg)
+ end
+
+ def C.rb_cref_struct
+ CType::Stub.new(:rb_cref_struct)
+ end
+
+ def C.rb_scope_visibility_t
+ CType::Stub.new(:rb_scope_visibility_t)
+ end
+
+ def C.rb_vm_tag
+ CType::Stub.new(:rb_vm_tag)
+ end
+
+ def C.rb_atomic_t
+ CType::Stub.new(:rb_atomic_t)
+ end
+
+ def C.rb_fiber_t
+ CType::Stub.new(:rb_fiber_t)
+ end
+
+ def C.rb_id_table
+ CType::Stub.new(:rb_id_table)
+ end
+
+ def C.rb_ensure_list_t
+ CType::Stub.new(:rb_ensure_list_t)
+ end
+
+ def C.rb_trace_arg_struct
+ CType::Stub.new(:rb_trace_arg_struct)
+ end
+
+ def C.rb_iseq_type
+ CType::Stub.new(:rb_iseq_type)
+ end
+
+ def C.rb_iseq_param_keyword
+ CType::Stub.new(:rb_iseq_param_keyword)
+ end
+
+ def C.iseq_insn_info
+ CType::Stub.new(:iseq_insn_info)
+ end
+
+ def C.iseq_catch_table
+ CType::Stub.new(:iseq_catch_table)
+ end
+
+ def C.rb_snum_t
+ CType::Stub.new(:rb_snum_t)
+ end
+
+ def C.iseq_bits_t
+ CType::Stub.new(:iseq_bits_t)
+ end
+
+ def C.rb_code_location_t
+ CType::Stub.new(:rb_code_location_t)
+ end
+
+ def C.iseq_compile_data
+ CType::Stub.new(:iseq_compile_data)
+ end
+
+ def C.rb_hook_list_struct
+ CType::Stub.new(:rb_hook_list_struct)
+ end
+
+ def C.rb_event_flag_t
+ CType::Stub.new(:rb_event_flag_t)
+ end
+
+ def C.rb_method_cfunc_t
+ CType::Stub.new(:rb_method_cfunc_t)
+ end
+
+ def C.rb_method_attr_t
+ CType::Stub.new(:rb_method_attr_t)
+ end
+
+ def C.rb_method_alias_t
+ CType::Stub.new(:rb_method_alias_t)
+ end
+
+ def C.rb_method_refined_t
+ CType::Stub.new(:rb_method_refined_t)
+ end
+
+ def C.rb_method_bmethod_t
+ CType::Stub.new(:rb_method_bmethod_t)
+ end
+
+ def C.rb_method_optimized_t
+ CType::Stub.new(:rb_method_optimized_t)
+ end
+
+ def C.ccan_list_node
+ CType::Stub.new(:ccan_list_node)
+ end
+
+ def C.rb_mjit_unit_type
+ CType::Stub.new(:rb_mjit_unit_type)
+ end
+
+ def C.rb_mjit_unit_list
+ CType::Stub.new(:rb_mjit_unit_list)
+ end
+
+ ### MJIT bindgen end ###
+end if RubyVM::MJIT.enabled? && RubyVM::MJIT.const_defined?(:C) # not defined for miniruby
diff --git a/mjit_compile.c b/mjit_compile.c
deleted file mode 100644
index 2c7996c258..0000000000
--- a/mjit_compile.c
+++ /dev/null
@@ -1,598 +0,0 @@
-/**********************************************************************
-
- mjit_compile.c - MRI method JIT compiler
-
- Copyright (C) 2017 Takashi Kokubun <takashikkbn@gmail.com>.
-
-**********************************************************************/
-
-// NOTE: All functions in this file are executed on MJIT worker. So don't
-// call Ruby methods (C functions that may call rb_funcall) or trigger
-// GC (using ZALLOC, xmalloc, xfree, etc.) in this file.
-
-#include "ruby/internal/config.h" // defines USE_MJIT
-
-#if USE_MJIT
-
-#include "internal.h"
-#include "internal/compile.h"
-#include "internal/hash.h"
-#include "internal/object.h"
-#include "internal/variable.h"
-#include "mjit.h"
-#include "mjit_unit.h"
-#include "vm_core.h"
-#include "vm_callinfo.h"
-#include "vm_exec.h"
-#include "vm_insnhelper.h"
-
-#include "builtin.h"
-#include "insns.inc"
-#include "insns_info.inc"
-
-// Macros to check if a position is already compiled using compile_status.stack_size_for_pos
-#define NOT_COMPILED_STACK_SIZE -1
-#define ALREADY_COMPILED_P(status, pos) (status->stack_size_for_pos[pos] != NOT_COMPILED_STACK_SIZE)
-
-// For propagating information needed for lazily pushing a frame.
-struct inlined_call_context {
- int orig_argc; // ci->orig_argc
- VALUE me; // vm_cc_cme(cc)
- int param_size; // def_iseq_ptr(vm_cc_cme(cc)->def)->body->param.size
- int local_size; // def_iseq_ptr(vm_cc_cme(cc)->def)->body->local_table_size
-};
-
-// Storage to keep compiler's status. This should have information
-// which is global during one `mjit_compile` call. Ones conditional
-// in each branch should be stored in `compile_branch`.
-struct compile_status {
- bool success; // has true if compilation has had no issue
- int *stack_size_for_pos; // stack_size_for_pos[pos] has stack size for the position (otherwise -1)
- // If true, JIT-ed code will use local variables to store pushed values instead of
- // using VM's stack and moving stack pointer.
- bool local_stack_p;
- // Safely-accessible ivar cache entries copied from main thread.
- union iseq_inline_storage_entry *is_entries;
- // Index of call cache entries captured to compiled_iseq to be marked on GC
- int cc_entries_index;
- // A pointer to root (i.e. not inlined) iseq being compiled.
- const struct rb_iseq_constant_body *compiled_iseq;
- int compiled_id; // Just a copy of compiled_iseq->jit_unit->id
- // Mutated optimization levels
- struct rb_mjit_compile_info *compile_info;
- bool merge_ivar_guards_p; // If true, merge guards of ivar accesses
- rb_serial_t ivar_serial; // ic_serial of IVC in is_entries (used only when merge_ivar_guards_p)
- size_t max_ivar_index; // Max IVC index in is_entries (used only when merge_ivar_guards_p)
- // If `inlined_iseqs[pos]` is not NULL, `mjit_compile_body` tries to inline ISeq there.
- const struct rb_iseq_constant_body **inlined_iseqs;
- struct inlined_call_context inline_context;
-};
-
-// Storage to keep data which is consistent in each conditional branch.
-// This is created and used for one `compile_insns` call and its values
-// should be copied for extra `compile_insns` call.
-struct compile_branch {
- unsigned int stack_size; // this simulates sp (stack pointer) of YARV
- bool finish_p; // if true, compilation in this branch should stop and let another branch to be compiled
-};
-
-struct case_dispatch_var {
- FILE *f;
- unsigned int base_pos;
- VALUE last_value;
-};
-
-static size_t
-call_data_index(CALL_DATA cd, const struct rb_iseq_constant_body *body)
-{
- return cd - body->call_data;
-}
-
-// Using this function to refer to cc_entries allocated by `mjit_capture_cc_entries`
-// instead of storing cc_entries in status directly so that we always refer to a new address
-// returned by `realloc` inside it.
-static const struct rb_callcache **
-captured_cc_entries(const struct compile_status *status)
-{
- VM_ASSERT(status->cc_entries_index != -1);
- return status->compiled_iseq->jit_unit->cc_entries + status->cc_entries_index;
-}
-
-// Returns true if call cache is still not obsoleted and vm_cc_cme(cc)->def->type is available.
-static bool
-has_valid_method_type(CALL_CACHE cc)
-{
- return vm_cc_cme(cc) != NULL;
-}
-
-// Returns true if MJIT thinks this cc's opt_* insn may fallback to opt_send_without_block.
-static bool
-has_cache_for_send(CALL_CACHE cc, int insn)
-{
- extern bool rb_vm_opt_cfunc_p(CALL_CACHE cc, int insn);
- return has_valid_method_type(cc) &&
- !(vm_cc_cme(cc)->def->type == VM_METHOD_TYPE_CFUNC && rb_vm_opt_cfunc_p(cc, insn));
-}
-
-// Returns true if iseq can use fastpath for setup, otherwise NULL. This becomes true in the same condition
-// as CC_SET_FASTPATH (in vm_callee_setup_arg) is called from vm_call_iseq_setup.
-static bool
-fastpath_applied_iseq_p(const CALL_INFO ci, const CALL_CACHE cc, const rb_iseq_t *iseq)
-{
- extern bool rb_simple_iseq_p(const rb_iseq_t *iseq);
- return iseq != NULL
- && !(vm_ci_flag(ci) & VM_CALL_KW_SPLAT) && rb_simple_iseq_p(iseq) // Top of vm_callee_setup_arg. In this case, opt_pc is 0.
- && vm_ci_argc(ci) == (unsigned int)ISEQ_BODY(iseq)->param.lead_num // exclude argument_arity_error (assumption: `calling->argc == ci->orig_argc` in send insns)
- && vm_call_iseq_optimizable_p(ci, cc); // CC_SET_FASTPATH condition
-}
-
-// Return true if an object of the klass may be a special const. See: rb_class_of
-static bool
-maybe_special_const_class_p(const VALUE klass)
-{
- return klass == rb_cFalseClass
- || klass == rb_cNilClass
- || klass == rb_cTrueClass
- || klass == rb_cInteger
- || klass == rb_cSymbol
- || klass == rb_cFloat;
-}
-
-static int
-compile_case_dispatch_each(VALUE key, VALUE value, VALUE arg)
-{
- struct case_dispatch_var *var = (struct case_dispatch_var *)arg;
- unsigned int offset;
-
- if (var->last_value != value) {
- offset = FIX2INT(value);
- var->last_value = value;
- fprintf(var->f, " case %d:\n", offset);
- fprintf(var->f, " goto label_%d;\n", var->base_pos + offset);
- fprintf(var->f, " break;\n");
- }
- return ST_CONTINUE;
-}
-
-// Calling rb_id2str in MJIT worker causes random SEGV. So this is disabled by default.
-static void
-comment_id(FILE *f, ID id)
-{
-#ifdef MJIT_COMMENT_ID
- VALUE name = rb_id2str(id);
- const char *p, *e;
- char c, prev = '\0';
-
- if (!name) return;
- p = RSTRING_PTR(name);
- e = RSTRING_END(name);
- fputs("/* :\"", f);
- for (; p < e; ++p) {
- switch (c = *p) {
- case '*': case '/': if (prev != (c ^ ('/' ^ '*'))) break;
- case '\\': case '"': fputc('\\', f);
- }
- fputc(c, f);
- prev = c;
- }
- fputs("\" */", f);
-#endif
-}
-
-static void compile_insns(FILE *f, const struct rb_iseq_constant_body *body, unsigned int stack_size,
- unsigned int pos, struct compile_status *status);
-
-// Main function of JIT compilation, vm_exec_core counterpart for JIT. Compile one insn to `f`, may modify
-// b->stack_size and return next position.
-//
-// When you add a new instruction to insns.def, it would be nice to have JIT compilation support here but
-// it's optional. This JIT compiler just ignores ISeq which includes unknown instruction, and ISeq which
-// does not have it can be compiled as usual.
-static unsigned int
-compile_insn(FILE *f, const struct rb_iseq_constant_body *body, const int insn, const VALUE *operands,
- const unsigned int pos, struct compile_status *status, struct compile_branch *b)
-{
- unsigned int next_pos = pos + insn_len(insn);
-
-/*****************/
- #include "mjit_compile.inc"
-/*****************/
-
- // If next_pos is already compiled and this branch is not finished yet,
- // next instruction won't be compiled in C code next and will need `goto`.
- if (!b->finish_p && next_pos < body->iseq_size && ALREADY_COMPILED_P(status, next_pos)) {
- fprintf(f, "goto label_%d;\n", next_pos);
-
- // Verify stack size assumption is the same among multiple branches
- if ((unsigned int)status->stack_size_for_pos[next_pos] != b->stack_size) {
- if (mjit_opts.warnings || mjit_opts.verbose)
- fprintf(stderr, "MJIT warning: JIT stack assumption is not the same between branches (%d != %u)\n",
- status->stack_size_for_pos[next_pos], b->stack_size);
- status->success = false;
- }
- }
-
- return next_pos;
-}
-
-// Compile one conditional branch. If it has branchXXX insn, this should be
-// called multiple times for each branch.
-static void
-compile_insns(FILE *f, const struct rb_iseq_constant_body *body, unsigned int stack_size,
- unsigned int pos, struct compile_status *status)
-{
- struct compile_branch branch;
-
- branch.stack_size = stack_size;
- branch.finish_p = false;
-
- while (pos < body->iseq_size && !ALREADY_COMPILED_P(status, pos) && !branch.finish_p) {
- int insn = rb_vm_insn_decode(body->iseq_encoded[pos]);
- status->stack_size_for_pos[pos] = (int)branch.stack_size;
-
- fprintf(f, "\nlabel_%d: /* %s */\n", pos, insn_name(insn));
- pos = compile_insn(f, body, insn, body->iseq_encoded + (pos+1), pos, status, &branch);
- if (status->success && branch.stack_size > body->stack_max) {
- if (mjit_opts.warnings || mjit_opts.verbose)
- fprintf(stderr, "MJIT warning: JIT stack size (%d) exceeded its max size (%d)\n", branch.stack_size, body->stack_max);
- status->success = false;
- }
- if (!status->success)
- break;
- }
-}
-
-// Print the block to cancel inlined method call. It's supporting only `opt_send_without_block` for now.
-static void
-compile_inlined_cancel_handler(FILE *f, const struct rb_iseq_constant_body *body, struct inlined_call_context *inline_context)
-{
- fprintf(f, "\ncancel:\n");
- fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel);\n");
- fprintf(f, " rb_mjit_recompile_inlining(original_iseq);\n");
-
- // Swap pc/sp set on cancel with original pc/sp.
- fprintf(f, " const VALUE *current_pc = reg_cfp->pc;\n");
- fprintf(f, " VALUE *current_sp = reg_cfp->sp;\n");
- fprintf(f, " reg_cfp->pc = orig_pc;\n");
- fprintf(f, " reg_cfp->sp = orig_sp;\n\n");
-
- // Lazily push the current call frame.
- fprintf(f, " struct rb_calling_info calling;\n");
- fprintf(f, " calling.block_handler = VM_BLOCK_HANDLER_NONE;\n"); // assumes `opt_send_without_block`
- fprintf(f, " calling.argc = %d;\n", inline_context->orig_argc);
- fprintf(f, " calling.recv = reg_cfp->self;\n");
- fprintf(f, " reg_cfp->self = orig_self;\n");
- fprintf(f, " vm_call_iseq_setup_normal(ec, reg_cfp, &calling, (const rb_callable_method_entry_t *)0x%"PRIxVALUE", 0, %d, %d);\n\n",
- inline_context->me, inline_context->param_size, inline_context->local_size); // fastpath_applied_iseq_p checks rb_simple_iseq_p, which ensures has_opt == FALSE
-
- // Start usual cancel from here.
- fprintf(f, " reg_cfp = ec->cfp;\n"); // work on the new frame
- fprintf(f, " reg_cfp->pc = current_pc;\n");
- fprintf(f, " reg_cfp->sp = current_sp;\n");
- for (unsigned int i = 0; i < body->stack_max; i++) { // should be always `status->local_stack_p`
- fprintf(f, " *(vm_base_ptr(reg_cfp) + %d) = stack[%d];\n", i, i);
- }
- // We're not just returning Qundef here so that caller's normal cancel handler can
- // push back `stack` to `cfp->sp`.
- fprintf(f, " return vm_exec(ec, false);\n");
-}
-
-// Print the block to cancel JIT execution.
-static void
-compile_cancel_handler(FILE *f, const struct rb_iseq_constant_body *body, struct compile_status *status)
-{
- if (status->inlined_iseqs == NULL) { // the current ISeq is being inlined
- compile_inlined_cancel_handler(f, body, &status->inline_context);
- return;
- }
-
- fprintf(f, "\nsend_cancel:\n");
- fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel_send_inline);\n");
- fprintf(f, " rb_mjit_recompile_send(original_iseq);\n");
- fprintf(f, " goto cancel;\n");
-
- fprintf(f, "\nivar_cancel:\n");
- fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel_ivar_inline);\n");
- fprintf(f, " rb_mjit_recompile_ivar(original_iseq);\n");
- fprintf(f, " goto cancel;\n");
-
- fprintf(f, "\nexivar_cancel:\n");
- fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel_exivar_inline);\n");
- fprintf(f, " rb_mjit_recompile_exivar(original_iseq);\n");
- fprintf(f, " goto cancel;\n");
-
- fprintf(f, "\nconst_cancel:\n");
- fprintf(f, " rb_mjit_recompile_const(original_iseq);\n");
- fprintf(f, " goto cancel;\n");
-
- fprintf(f, "\ncancel:\n");
- fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel);\n");
- if (status->local_stack_p) {
- for (unsigned int i = 0; i < body->stack_max; i++) {
- fprintf(f, " *(vm_base_ptr(reg_cfp) + %d) = stack[%d];\n", i, i);
- }
- }
- fprintf(f, " return Qundef;\n");
-}
-
-extern int
-mjit_capture_cc_entries(const struct rb_iseq_constant_body *compiled_iseq, const struct rb_iseq_constant_body *captured_iseq);
-
-// Copy current is_entries and use it throughout the current compilation consistently.
-// While ic->entry has been immutable since https://github.com/ruby/ruby/pull/3662,
-// we still need this to avoid a race condition between entries and ivar_serial/max_ivar_index.
-static void
-mjit_capture_is_entries(const struct rb_iseq_constant_body *body, union iseq_inline_storage_entry *is_entries)
-{
- if (is_entries == NULL)
- return;
- memcpy(is_entries, body->is_entries, sizeof(union iseq_inline_storage_entry) * ISEQ_IS_SIZE(body));
-}
-
-static bool
-mjit_compile_body(FILE *f, const rb_iseq_t *iseq, struct compile_status *status)
-{
- const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
- status->success = true;
- status->local_stack_p = !body->catch_except_p;
-
- if (status->local_stack_p) {
- fprintf(f, " VALUE stack[%d];\n", body->stack_max);
- }
- else {
- fprintf(f, " VALUE *stack = reg_cfp->sp;\n");
- }
- if (status->inlined_iseqs != NULL) // i.e. compile root
- fprintf(f, " static const rb_iseq_t *original_iseq = (const rb_iseq_t *)0x%"PRIxVALUE";\n", (VALUE)iseq);
- fprintf(f, " static const VALUE *const original_body_iseq = (VALUE *)0x%"PRIxVALUE";\n",
- (VALUE)body->iseq_encoded);
- fprintf(f, " VALUE cfp_self = reg_cfp->self;\n"); // cache self across the method
- fprintf(f, "#undef GET_SELF\n");
- fprintf(f, "#define GET_SELF() cfp_self\n");
-
- // Generate merged ivar guards first if needed
- if (!status->compile_info->disable_ivar_cache && status->merge_ivar_guards_p) {
- fprintf(f, " if (UNLIKELY(!(RB_TYPE_P(GET_SELF(), T_OBJECT) && (rb_serial_t)%"PRI_SERIALT_PREFIX"u == RCLASS_SERIAL(RBASIC(GET_SELF())->klass) &&", status->ivar_serial);
-#if USE_RVARGC
- fprintf(f, "%"PRIuSIZE" < ROBJECT_NUMIV(GET_SELF())", status->max_ivar_index); // index < ROBJECT_NUMIV(obj)
-#else
- if (status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX) {
- fprintf(f, "%"PRIuSIZE" < ROBJECT_NUMIV(GET_SELF())", status->max_ivar_index); // index < ROBJECT_NUMIV(obj) && !RB_FL_ANY_RAW(obj, ROBJECT_EMBED)
- }
- else {
- fprintf(f, "ROBJECT_EMBED_LEN_MAX == ROBJECT_NUMIV(GET_SELF())"); // index < ROBJECT_NUMIV(obj) && RB_FL_ANY_RAW(obj, ROBJECT_EMBED)
- }
-#endif
- fprintf(f, "))) {\n");
- fprintf(f, " goto ivar_cancel;\n");
- fprintf(f, " }\n");
- }
-
- // Simulate `opt_pc` in setup_parameters_complex. Other PCs which may be passed by catch tables
- // are not considered since vm_exec doesn't call mjit_exec for catch tables.
- if (body->param.flags.has_opt) {
- int i;
- fprintf(f, "\n");
- fprintf(f, " switch (reg_cfp->pc - ISEQ_BODY(reg_cfp->iseq)->iseq_encoded) {\n");
- for (i = 0; i <= body->param.opt_num; i++) {
- VALUE pc_offset = body->param.opt_table[i];
- fprintf(f, " case %"PRIdVALUE":\n", pc_offset);
- fprintf(f, " goto label_%"PRIdVALUE";\n", pc_offset);
- }
- fprintf(f, " }\n");
- }
-
- compile_insns(f, body, 0, 0, status);
- compile_cancel_handler(f, body, status);
- fprintf(f, "#undef GET_SELF");
- return status->success;
-}
-
-// Return true if the ISeq can be inlined without pushing a new control frame.
-static bool
-inlinable_iseq_p(const struct rb_iseq_constant_body *body)
-{
- // 1) If catch_except_p, caller frame should be preserved when callee catches an exception.
- // Then we need to wrap `vm_exec()` but then we can't inline the call inside it.
- //
- // 2) If `body->catch_except_p` is false and `handles_sp?` of an insn is false,
- // sp is not moved as we assume `status->local_stack_p = !body->catch_except_p`.
- //
- // 3) If `body->catch_except_p` is false and `always_leaf?` of an insn is true,
- // pc is not moved.
- if (body->catch_except_p)
- return false;
-
- unsigned int pos = 0;
- while (pos < body->iseq_size) {
- int insn = rb_vm_insn_decode(body->iseq_encoded[pos]);
- // All insns in the ISeq except `leave` (to be overridden in the inlined code)
- // should meet following strong assumptions:
- // * Do not require `cfp->sp` motion
- // * Do not move `cfp->pc`
- // * Do not read any `cfp->pc`
- if (insn == BIN(invokebuiltin) || insn == BIN(opt_invokebuiltin_delegate) || insn == BIN(opt_invokebuiltin_delegate_leave)) {
- // builtin insn's inlinability is handled by `Primitive.attr! 'inline'` per iseq
- if (!body->builtin_inline_p)
- return false;
- }
- else if (insn != BIN(leave) && insn_may_depend_on_sp_or_pc(insn, body->iseq_encoded + (pos + 1)))
- return false;
- // At this moment, `cfp->ep` in an inlined method is not working.
- switch (insn) {
- case BIN(getlocal):
- case BIN(getlocal_WC_0):
- case BIN(getlocal_WC_1):
- case BIN(setlocal):
- case BIN(setlocal_WC_0):
- case BIN(setlocal_WC_1):
- case BIN(getblockparam):
- case BIN(getblockparamproxy):
- case BIN(setblockparam):
- return false;
- }
- pos += insn_len(insn);
- }
- return true;
-}
-
-// Return an iseq pointer if cc has inlinable iseq.
-const rb_iseq_t *
-rb_mjit_inlinable_iseq(const struct rb_callinfo *ci, const struct rb_callcache *cc)
-{
- const rb_iseq_t *iseq;
- if (has_valid_method_type(cc) &&
- !(vm_ci_flag(ci) & VM_CALL_TAILCALL) && // inlining only non-tailcall path
- vm_cc_cme(cc)->def->type == VM_METHOD_TYPE_ISEQ &&
- fastpath_applied_iseq_p(ci, cc, iseq = def_iseq_ptr(vm_cc_cme(cc)->def)) &&
- // CC_SET_FASTPATH in vm_callee_setup_arg
- inlinable_iseq_p(ISEQ_BODY(iseq))) {
- return iseq;
- }
- return NULL;
-}
-
-static void
-init_ivar_compile_status(const struct rb_iseq_constant_body *body, struct compile_status *status)
-{
- mjit_capture_is_entries(body, status->is_entries);
-
- int num_ivars = 0;
- unsigned int pos = 0;
- status->max_ivar_index = 0;
- status->ivar_serial = 0;
-
- while (pos < body->iseq_size) {
- int insn = rb_vm_insn_decode(body->iseq_encoded[pos]);
- if (insn == BIN(getinstancevariable) || insn == BIN(setinstancevariable)) {
- IVC ic = (IVC)body->iseq_encoded[pos+2];
- IVC ic_copy = &(status->is_entries + ((union iseq_inline_storage_entry *)ic - body->is_entries))->iv_cache;
- if (ic_copy->entry) { // Only initialized (ic_serial > 0) IVCs are optimized
- num_ivars++;
-
- if (status->max_ivar_index < ic_copy->entry->index) {
- status->max_ivar_index = ic_copy->entry->index;
- }
-
- if (status->ivar_serial == 0) {
- status->ivar_serial = ic_copy->entry->class_serial;
- }
- else if (status->ivar_serial != ic_copy->entry->class_serial) {
- // Multiple classes have used this ISeq. Give up assuming one serial.
- status->merge_ivar_guards_p = false;
- return;
- }
- }
- }
- pos += insn_len(insn);
- }
- status->merge_ivar_guards_p = status->ivar_serial > 0 && num_ivars >= 2;
-}
-
-// This needs to be macro instead of a function because it's using `alloca`.
-#define INIT_COMPILE_STATUS(status, body, compile_root_p) do { \
- status = (struct compile_status){ \
- .stack_size_for_pos = (int *)alloca(sizeof(int) * body->iseq_size), \
- .inlined_iseqs = compile_root_p ? \
- alloca(sizeof(const struct rb_iseq_constant_body *) * body->iseq_size) : NULL, \
- .is_entries = (ISEQ_IS_SIZE(body) > 0) ? \
- alloca(sizeof(union iseq_inline_storage_entry) * ISEQ_IS_SIZE(body)) : NULL, \
- .cc_entries_index = (body->ci_size > 0) ? \
- mjit_capture_cc_entries(status.compiled_iseq, body) : -1, \
- .compiled_id = status.compiled_id, \
- .compiled_iseq = status.compiled_iseq, \
- .compile_info = compile_root_p ? \
- rb_mjit_iseq_compile_info(body) : alloca(sizeof(struct rb_mjit_compile_info)) \
- }; \
- memset(status.stack_size_for_pos, NOT_COMPILED_STACK_SIZE, sizeof(int) * body->iseq_size); \
- if (compile_root_p) \
- memset((void *)status.inlined_iseqs, 0, sizeof(const struct rb_iseq_constant_body *) * body->iseq_size); \
- else \
- memset(status.compile_info, 0, sizeof(struct rb_mjit_compile_info)); \
-} while (0)
-
-static bool
-precompile_inlinable_child_iseq(FILE *f, const rb_iseq_t *child_iseq, struct compile_status *status,
- const struct rb_callinfo *ci, const struct rb_callcache *cc, unsigned int pos)
-{
- struct compile_status child_status = { .compiled_iseq = status->compiled_iseq, .compiled_id = status->compiled_id };
- INIT_COMPILE_STATUS(child_status, ISEQ_BODY(child_iseq), false);
- child_status.inline_context = (struct inlined_call_context){
- .orig_argc = vm_ci_argc(ci),
- .me = (VALUE)vm_cc_cme(cc),
- .param_size = ISEQ_BODY(child_iseq)->param.size,
- .local_size = ISEQ_BODY(child_iseq)->local_table_size
- };
- if (ISEQ_BODY(child_iseq)->ci_size > 0 && child_status.cc_entries_index == -1) {
- return false;
- }
- init_ivar_compile_status(ISEQ_BODY(child_iseq), &child_status);
-
- fprintf(f, "ALWAYS_INLINE(static VALUE _mjit%d_inlined_%d(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE orig_self, const rb_iseq_t *original_iseq));\n", status->compiled_id, pos);
- fprintf(f, "static inline VALUE\n_mjit%d_inlined_%d(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE orig_self, const rb_iseq_t *original_iseq)\n{\n", status->compiled_id, pos);
- fprintf(f, " const VALUE *orig_pc = reg_cfp->pc;\n");
- fprintf(f, " VALUE *orig_sp = reg_cfp->sp;\n");
- bool success = mjit_compile_body(f, child_iseq, &child_status);
- fprintf(f, "\n} /* end of _mjit%d_inlined_%d */\n\n", status->compiled_id, pos);
-
- return success;
-}
-
-// Compile inlinable ISeqs to C code in `f`. It returns true if it succeeds to compile them.
-static bool
-precompile_inlinable_iseqs(FILE *f, const rb_iseq_t *iseq, struct compile_status *status)
-{
- const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
- unsigned int pos = 0;
- while (pos < body->iseq_size) {
- int insn = rb_vm_insn_decode(body->iseq_encoded[pos]);
- if (insn == BIN(opt_send_without_block) || insn == BIN(opt_size)) { // `compile_inlined_cancel_handler` supports only `opt_send_without_block`
- CALL_DATA cd = (CALL_DATA)body->iseq_encoded[pos + 1];
- const struct rb_callinfo *ci = cd->ci;
- const struct rb_callcache *cc = captured_cc_entries(status)[call_data_index(cd, body)]; // use copy to avoid race condition
-
- const rb_iseq_t *child_iseq;
- if ((child_iseq = rb_mjit_inlinable_iseq(ci, cc)) != NULL) {
- status->inlined_iseqs[pos] = ISEQ_BODY(child_iseq);
-
- if (mjit_opts.verbose >= 1) // print beforehand because ISeq may be GCed during copy job.
- fprintf(stderr, "JIT inline: %s@%s:%d => %s@%s:%d\n",
- RSTRING_PTR(ISEQ_BODY(child_iseq)->location.label),
- RSTRING_PTR(rb_iseq_path(child_iseq)), FIX2INT(ISEQ_BODY(child_iseq)->location.first_lineno),
- RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
- RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(ISEQ_BODY(iseq)->location.first_lineno));
- if (!precompile_inlinable_child_iseq(f, child_iseq, status, ci, cc, pos))
- return false;
- }
- }
- pos += insn_len(insn);
- }
- return true;
-}
-
-// Compile ISeq to C code in `f`. It returns true if it succeeds to compile.
-bool
-mjit_compile(FILE *f, const rb_iseq_t *iseq, const char *funcname, int id)
-{
- struct compile_status status = { .compiled_iseq = ISEQ_BODY(iseq), .compiled_id = id };
- INIT_COMPILE_STATUS(status, ISEQ_BODY(iseq), true);
- if (ISEQ_BODY(iseq)->ci_size > 0 && status.cc_entries_index == -1) {
- return false;
- }
- init_ivar_compile_status(ISEQ_BODY(iseq), &status);
-
- if (!status.compile_info->disable_send_cache && !status.compile_info->disable_inlining) {
- if (!precompile_inlinable_iseqs(f, iseq, &status))
- return false;
- }
-
-#ifdef _WIN32
- fprintf(f, "__declspec(dllexport)\n");
-#endif
- fprintf(f, "VALUE\n%s(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)\n{\n", funcname);
- bool success = mjit_compile_body(f, iseq, &status);
- fprintf(f, "\n} // end of %s\n", funcname);
- return success;
-}
-
-#endif // USE_MJIT
diff --git a/mjit_unit.h b/mjit_unit.h
deleted file mode 100644
index 2e23a8d5fc..0000000000
--- a/mjit_unit.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef INTERNAL_MJIT_H
-#define INTERNAL_MJIT_H
-
-#include "ccan/list/list.h"
-
-// The unit structure that holds metadata of ISeq for MJIT.
-struct rb_mjit_unit {
- struct ccan_list_node unode;
- // Unique order number of unit.
- int id;
- // Dlopen handle of the loaded object file.
- void *handle;
- rb_iseq_t *iseq;
-#if defined(_WIN32)
- // DLL cannot be removed while loaded on Windows. If this is set, it'll be lazily deleted.
- char *so_file;
-#endif
- // Only used by unload_units. Flag to check this unit is currently on stack or not.
- bool used_code_p;
- // True if it's a unit for JIT compaction
- bool compact_p;
- // mjit_compile's optimization switches
- struct rb_mjit_compile_info compile_info;
- // captured CC values, they should be marked with iseq.
- const struct rb_callcache **cc_entries;
- unsigned int cc_entries_size; // ISEQ_BODY(iseq)->ci_size + ones of inlined iseqs
-};
-
-#endif /* INTERNAL_MJIT_H */
diff --git a/node.c b/node.c
index d8c1da95ef..a6cb498778 100644
--- a/node.c
+++ b/node.c
@@ -29,10 +29,10 @@
#define A_LIT(lit) AR(rb_dump_literal(lit))
#define A_NODE_HEADER(node, term) \
rb_str_catf(buf, "@ %s (id: %d, line: %d, location: (%d,%d)-(%d,%d))%s"term, \
- ruby_node_name(nd_type(node)), nd_node_id(node), nd_line(node), \
- nd_first_lineno(node), nd_first_column(node), \
- nd_last_lineno(node), nd_last_column(node), \
- (node->flags & NODE_FL_NEWLINE ? "*" : ""))
+ ruby_node_name(nd_type(node)), nd_node_id(node), nd_line(node), \
+ nd_first_lineno(node), nd_first_column(node), \
+ nd_last_lineno(node), nd_last_column(node), \
+ (node->flags & NODE_FL_NEWLINE ? "*" : ""))
#define A_FIELD_HEADER(len, name, term) \
rb_str_catf(buf, "+- %.*s:"term, (len), (name))
#define D_FIELD_HEADER(len, name, term) (A_INDENT, A_FIELD_HEADER(len, name, term))
@@ -45,20 +45,20 @@
#define COMPOUND_FIELD1(name, ann) \
COMPOUND_FIELD(FIELD_NAME_LEN(name, ann), \
- FIELD_NAME_DESC(name, ann))
+ FIELD_NAME_DESC(name, ann))
#define FIELD_NAME_DESC(name, ann) name " (" ann ")"
#define FIELD_NAME_LEN(name, ann) (int)( \
- comment ? \
- rb_strlen_lit(FIELD_NAME_DESC(name, ann)) : \
- rb_strlen_lit(name))
+ comment ? \
+ rb_strlen_lit(FIELD_NAME_DESC(name, ann)) : \
+ rb_strlen_lit(name))
#define SIMPLE_FIELD(len, name) \
FIELD_BLOCK(D_FIELD_HEADER((len), (name), " "), A("\n"))
#define FIELD_BLOCK(init, reset) \
for (init, field_flag = 1; \
- field_flag; /* should be optimized away */ \
- reset, field_flag = 0)
+ field_flag; /* should be optimized away */ \
+ reset, field_flag = 0)
#define SIMPLE_FIELD1(name, ann) SIMPLE_FIELD(FIELD_NAME_LEN(name, ann), FIELD_NAME_DESC(name, ann))
#define F_CUSTOM1(name, ann) SIMPLE_FIELD1(#name, ann)
@@ -74,7 +74,7 @@
#define ANN(ann) \
if (comment) { \
- A_INDENT; A("| # " ann "\n"); \
+ A_INDENT; A("| # " ann "\n"); \
}
#define LAST_NODE (next_indent = " ")
@@ -108,16 +108,16 @@ static void
add_id(VALUE buf, ID id)
{
if (id == 0) {
- A("(null)");
+ A("(null)");
}
else {
- VALUE str = rb_id2str(id);
- if (str) {
- A(":"); AR(str);
- }
- else {
+ VALUE str = rb_id2str(id);
+ if (str) {
+ A(":"); AR(str);
+ }
+ else {
rb_str_catf(buf, "(internal variable: 0x%"PRIsVALUE")", id);
- }
+ }
}
}
@@ -137,8 +137,8 @@ dump_array(VALUE buf, VALUE indent, int comment, const NODE *node)
F_LONG(nd_alen, "length");
F_NODE(nd_head, "element");
while (node->nd_next && nd_type_p(node->nd_next, NODE_LIST)) {
- node = node->nd_next;
- F_NODE(nd_head, "element");
+ node = node->nd_next;
+ F_NODE(nd_head, "element");
}
LAST_NODE;
F_NODE(nd_next, "next element");
@@ -153,8 +153,8 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
enum node_type type;
if (!node) {
- D_NULL_NODE;
- return;
+ D_NULL_NODE;
+ return;
}
D_NODE_HEADER(node);
@@ -162,63 +162,63 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
type = nd_type(node);
switch (type) {
case NODE_BLOCK:
- ANN("statement sequence");
- ANN("format: [nd_head]; ...; [nd_next]");
- ANN("example: foo; bar");
- i = 0;
- do {
- A_INDENT;
- rb_str_catf(buf, "+- nd_head (%s%d):\n",
- comment ? "statement #" : "", ++i);
- if (!node->nd_next) LAST_NODE;
- D_INDENT;
- dump_node(buf, indent, comment, node->nd_head);
- D_DEDENT;
- } while (node->nd_next &&
- nd_type_p(node->nd_next, NODE_BLOCK) &&
- (node = node->nd_next, 1));
- if (node->nd_next) {
- LAST_NODE;
- F_NODE(nd_next, "next block");
- }
- return;
+ ANN("statement sequence");
+ ANN("format: [nd_head]; ...; [nd_next]");
+ ANN("example: foo; bar");
+ i = 0;
+ do {
+ A_INDENT;
+ rb_str_catf(buf, "+- nd_head (%s%d):\n",
+ comment ? "statement #" : "", ++i);
+ if (!node->nd_next) LAST_NODE;
+ D_INDENT;
+ dump_node(buf, indent, comment, node->nd_head);
+ D_DEDENT;
+ } while (node->nd_next &&
+ nd_type_p(node->nd_next, NODE_BLOCK) &&
+ (node = node->nd_next, 1));
+ if (node->nd_next) {
+ LAST_NODE;
+ F_NODE(nd_next, "next block");
+ }
+ return;
case NODE_IF:
- ANN("if statement");
- ANN("format: if [nd_cond] then [nd_body] else [nd_else] end");
- ANN("example: if x == 1 then foo else bar end");
- F_NODE(nd_cond, "condition expr");
- F_NODE(nd_body, "then clause");
- LAST_NODE;
- F_NODE(nd_else, "else clause");
- return;
+ ANN("if statement");
+ ANN("format: if [nd_cond] then [nd_body] else [nd_else] end");
+ ANN("example: if x == 1 then foo else bar end");
+ F_NODE(nd_cond, "condition expr");
+ F_NODE(nd_body, "then clause");
+ LAST_NODE;
+ F_NODE(nd_else, "else clause");
+ return;
case NODE_UNLESS:
- ANN("unless statement");
- ANN("format: unless [nd_cond] then [nd_body] else [nd_else] end");
- ANN("example: unless x == 1 then foo else bar end");
- F_NODE(nd_cond, "condition expr");
- F_NODE(nd_body, "then clause");
- LAST_NODE;
- F_NODE(nd_else, "else clause");
- return;
+ ANN("unless statement");
+ ANN("format: unless [nd_cond] then [nd_body] else [nd_else] end");
+ ANN("example: unless x == 1 then foo else bar end");
+ F_NODE(nd_cond, "condition expr");
+ F_NODE(nd_body, "then clause");
+ LAST_NODE;
+ F_NODE(nd_else, "else clause");
+ return;
case NODE_CASE:
- ANN("case statement");
- ANN("format: case [nd_head]; [nd_body]; end");
- ANN("example: case x; when 1; foo; when 2; bar; else baz; end");
- F_NODE(nd_head, "case expr");
- LAST_NODE;
- F_NODE(nd_body, "when clauses");
- return;
+ ANN("case statement");
+ ANN("format: case [nd_head]; [nd_body]; end");
+ ANN("example: case x; when 1; foo; when 2; bar; else baz; end");
+ F_NODE(nd_head, "case expr");
+ LAST_NODE;
+ F_NODE(nd_body, "when clauses");
+ return;
case NODE_CASE2:
- ANN("case statement with no head");
- ANN("format: case; [nd_body]; end");
- ANN("example: case; when 1; foo; when 2; bar; else baz; end");
- F_NODE(nd_head, "case expr");
- LAST_NODE;
- F_NODE(nd_body, "when clauses");
- return;
+ ANN("case statement with no head");
+ ANN("format: case; [nd_body]; end");
+ ANN("example: case; when 1; foo; when 2; bar; else baz; end");
+ F_NODE(nd_head, "case expr");
+ LAST_NODE;
+ F_NODE(nd_body, "when clauses");
+ return;
case NODE_CASE3:
ANN("case statement (pattern matching)");
ANN("format: case [nd_head]; [nd_body]; end");
@@ -229,14 +229,14 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
return;
case NODE_WHEN:
- ANN("when clause");
- ANN("format: when [nd_head]; [nd_body]; (when or else) [nd_next]");
- ANN("example: case x; when 1; foo; when 2; bar; else baz; end");
- F_NODE(nd_head, "when value");
- F_NODE(nd_body, "when body");
- LAST_NODE;
- F_NODE(nd_next, "next when clause");
- return;
+ ANN("when clause");
+ ANN("format: when [nd_head]; [nd_body]; (when or else) [nd_next]");
+ ANN("example: case x; when 1; foo; when 2; bar; else baz; end");
+ F_NODE(nd_head, "when value");
+ F_NODE(nd_body, "when body");
+ LAST_NODE;
+ F_NODE(nd_next, "next when clause");
+ return;
case NODE_IN:
ANN("in clause");
@@ -249,278 +249,278 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
return;
case NODE_WHILE:
- ANN("while statement");
- ANN("format: while [nd_cond]; [nd_body]; end");
- ANN("example: while x == 1; foo; end");
- goto loop;
+ ANN("while statement");
+ ANN("format: while [nd_cond]; [nd_body]; end");
+ ANN("example: while x == 1; foo; end");
+ goto loop;
case NODE_UNTIL:
- ANN("until statement");
- ANN("format: until [nd_cond]; [nd_body]; end");
- ANN("example: until x == 1; foo; end");
+ ANN("until statement");
+ ANN("format: until [nd_cond]; [nd_body]; end");
+ ANN("example: until x == 1; foo; end");
loop:
- F_CUSTOM1(nd_state, "begin-end-while?") {
- A_INT((int)node->nd_state);
- A((node->nd_state == 1) ? " (while-end)" : " (begin-end-while)");
- }
- F_NODE(nd_cond, "condition");
- LAST_NODE;
- F_NODE(nd_body, "body");
- return;
+ F_CUSTOM1(nd_state, "begin-end-while?") {
+ A_INT((int)node->nd_state);
+ A((node->nd_state == 1) ? " (while-end)" : " (begin-end-while)");
+ }
+ F_NODE(nd_cond, "condition");
+ LAST_NODE;
+ F_NODE(nd_body, "body");
+ return;
case NODE_ITER:
- ANN("method call with block");
- ANN("format: [nd_iter] { [nd_body] }");
- ANN("example: 3.times { foo }");
- goto iter;
+ ANN("method call with block");
+ ANN("format: [nd_iter] { [nd_body] }");
+ ANN("example: 3.times { foo }");
+ goto iter;
case NODE_FOR:
- ANN("for statement");
- ANN("format: for * in [nd_iter] do [nd_body] end");
- ANN("example: for i in 1..3 do foo end");
+ ANN("for statement");
+ ANN("format: for * in [nd_iter] do [nd_body] end");
+ ANN("example: for i in 1..3 do foo end");
iter:
- F_NODE(nd_iter, "iteration receiver");
- LAST_NODE;
- F_NODE(nd_body, "body");
- return;
+ F_NODE(nd_iter, "iteration receiver");
+ LAST_NODE;
+ F_NODE(nd_body, "body");
+ return;
case NODE_FOR_MASGN:
- ANN("vars of for statement with masgn");
- ANN("format: for [nd_var] in ... do ... end");
- ANN("example: for x, y in 1..3 do foo end");
- LAST_NODE;
- F_NODE(nd_var, "var");
- return;
+ ANN("vars of for statement with masgn");
+ ANN("format: for [nd_var] in ... do ... end");
+ ANN("example: for x, y in 1..3 do foo end");
+ LAST_NODE;
+ F_NODE(nd_var, "var");
+ return;
case NODE_BREAK:
- ANN("break statement");
- ANN("format: break [nd_stts]");
- ANN("example: break 1");
- goto jump;
+ ANN("break statement");
+ ANN("format: break [nd_stts]");
+ ANN("example: break 1");
+ goto jump;
case NODE_NEXT:
- ANN("next statement");
- ANN("format: next [nd_stts]");
- ANN("example: next 1");
- goto jump;
+ ANN("next statement");
+ ANN("format: next [nd_stts]");
+ ANN("example: next 1");
+ goto jump;
case NODE_RETURN:
- ANN("return statement");
- ANN("format: return [nd_stts]");
- ANN("example: return 1");
+ ANN("return statement");
+ ANN("format: return [nd_stts]");
+ ANN("example: return 1");
jump:
- LAST_NODE;
- F_NODE(nd_stts, "value");
- return;
+ LAST_NODE;
+ F_NODE(nd_stts, "value");
+ return;
case NODE_REDO:
- ANN("redo statement");
- ANN("format: redo");
- ANN("example: redo");
- return;
+ ANN("redo statement");
+ ANN("format: redo");
+ ANN("example: redo");
+ return;
case NODE_RETRY:
- ANN("retry statement");
- ANN("format: retry");
- ANN("example: retry");
- return;
+ ANN("retry statement");
+ ANN("format: retry");
+ ANN("example: retry");
+ return;
case NODE_BEGIN:
- ANN("begin statement");
- ANN("format: begin; [nd_body]; end");
- ANN("example: begin; 1; end");
- LAST_NODE;
- F_NODE(nd_body, "body");
- return;
+ ANN("begin statement");
+ ANN("format: begin; [nd_body]; end");
+ ANN("example: begin; 1; end");
+ LAST_NODE;
+ F_NODE(nd_body, "body");
+ return;
case NODE_RESCUE:
- ANN("rescue clause");
- ANN("format: begin; [nd_body]; (rescue) [nd_resq]; else [nd_else]; end");
- ANN("example: begin; foo; rescue; bar; else; baz; end");
- F_NODE(nd_head, "body");
- F_NODE(nd_resq, "rescue clause list");
- LAST_NODE;
- F_NODE(nd_else, "rescue else clause");
- return;
+ ANN("rescue clause");
+ ANN("format: begin; [nd_body]; (rescue) [nd_resq]; else [nd_else]; end");
+ ANN("example: begin; foo; rescue; bar; else; baz; end");
+ F_NODE(nd_head, "body");
+ F_NODE(nd_resq, "rescue clause list");
+ LAST_NODE;
+ F_NODE(nd_else, "rescue else clause");
+ return;
case NODE_RESBODY:
- ANN("rescue clause (cont'd)");
- ANN("format: rescue [nd_args]; [nd_body]; (rescue) [nd_head]");
- ANN("example: begin; foo; rescue; bar; else; baz; end");
- F_NODE(nd_args, "rescue exceptions");
- F_NODE(nd_body, "rescue clause");
- LAST_NODE;
- F_NODE(nd_head, "next rescue clause");
- return;
+ ANN("rescue clause (cont'd)");
+ ANN("format: rescue [nd_args]; [nd_body]; (rescue) [nd_head]");
+ ANN("example: begin; foo; rescue; bar; else; baz; end");
+ F_NODE(nd_args, "rescue exceptions");
+ F_NODE(nd_body, "rescue clause");
+ LAST_NODE;
+ F_NODE(nd_head, "next rescue clause");
+ return;
case NODE_ENSURE:
- ANN("ensure clause");
- ANN("format: begin; [nd_head]; ensure; [nd_ensr]; end");
- ANN("example: begin; foo; ensure; bar; end");
- F_NODE(nd_head, "body");
- LAST_NODE;
- F_NODE(nd_ensr, "ensure clause");
- return;
+ ANN("ensure clause");
+ ANN("format: begin; [nd_head]; ensure; [nd_ensr]; end");
+ ANN("example: begin; foo; ensure; bar; end");
+ F_NODE(nd_head, "body");
+ LAST_NODE;
+ F_NODE(nd_ensr, "ensure clause");
+ return;
case NODE_AND:
- ANN("&& operator");
- ANN("format: [nd_1st] && [nd_2nd]");
- ANN("example: foo && bar");
- goto andor;
+ ANN("&& operator");
+ ANN("format: [nd_1st] && [nd_2nd]");
+ ANN("example: foo && bar");
+ goto andor;
case NODE_OR:
- ANN("|| operator");
- ANN("format: [nd_1st] || [nd_2nd]");
- ANN("example: foo || bar");
+ ANN("|| operator");
+ ANN("format: [nd_1st] || [nd_2nd]");
+ ANN("example: foo || bar");
andor:
- while (1) {
- F_NODE(nd_1st, "left expr");
- if (!node->nd_2nd || !nd_type_p(node->nd_2nd, type))
- break;
- node = node->nd_2nd;
- }
- LAST_NODE;
- F_NODE(nd_2nd, "right expr");
- return;
+ while (1) {
+ F_NODE(nd_1st, "left expr");
+ if (!node->nd_2nd || !nd_type_p(node->nd_2nd, type))
+ break;
+ node = node->nd_2nd;
+ }
+ LAST_NODE;
+ F_NODE(nd_2nd, "right expr");
+ return;
case NODE_MASGN:
- ANN("multiple assignment");
- ANN("format: [nd_head], [nd_args] = [nd_value]");
- ANN("example: a, b = foo");
- F_NODE(nd_value, "rhsn");
- F_NODE(nd_head, "lhsn");
- if (NODE_NAMED_REST_P(node->nd_args)) {
- LAST_NODE;
- F_NODE(nd_args, "splatn");
- }
- else {
- F_MSG(nd_args, "splatn", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
- }
- return;
+ ANN("multiple assignment");
+ ANN("format: [nd_head], [nd_args] = [nd_value]");
+ ANN("example: a, b = foo");
+ F_NODE(nd_value, "rhsn");
+ F_NODE(nd_head, "lhsn");
+ if (NODE_NAMED_REST_P(node->nd_args)) {
+ LAST_NODE;
+ F_NODE(nd_args, "splatn");
+ }
+ else {
+ F_MSG(nd_args, "splatn", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
+ }
+ return;
case NODE_LASGN:
- ANN("local variable assignment");
- ANN("format: [nd_vid](lvar) = [nd_value]");
- ANN("example: x = foo");
- F_ID(nd_vid, "local variable");
- if (NODE_REQUIRED_KEYWORD_P(node)) {
- F_MSG(nd_value, "rvalue", "NODE_SPECIAL_REQUIRED_KEYWORD (required keyword argument)");
- }
- else {
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- }
- return;
+ ANN("local variable assignment");
+ ANN("format: [nd_vid](lvar) = [nd_value]");
+ ANN("example: x = foo");
+ F_ID(nd_vid, "local variable");
+ if (NODE_REQUIRED_KEYWORD_P(node)) {
+ F_MSG(nd_value, "rvalue", "NODE_SPECIAL_REQUIRED_KEYWORD (required keyword argument)");
+ }
+ else {
+ LAST_NODE;
+ F_NODE(nd_value, "rvalue");
+ }
+ return;
case NODE_DASGN:
- ANN("dynamic variable assignment");
- ANN("format: [nd_vid](dvar) = [nd_value]");
- ANN("example: x = nil; 1.times { x = foo }");
- ANN("example: 1.times { x = foo }");
- F_ID(nd_vid, "local variable");
- if (NODE_REQUIRED_KEYWORD_P(node)) {
- F_MSG(nd_value, "rvalue", "NODE_SPECIAL_REQUIRED_KEYWORD (required keyword argument)");
- }
- else {
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- }
- return;
+ ANN("dynamic variable assignment");
+ ANN("format: [nd_vid](dvar) = [nd_value]");
+ ANN("example: x = nil; 1.times { x = foo }");
+ ANN("example: 1.times { x = foo }");
+ F_ID(nd_vid, "local variable");
+ if (NODE_REQUIRED_KEYWORD_P(node)) {
+ F_MSG(nd_value, "rvalue", "NODE_SPECIAL_REQUIRED_KEYWORD (required keyword argument)");
+ }
+ else {
+ LAST_NODE;
+ F_NODE(nd_value, "rvalue");
+ }
+ return;
case NODE_IASGN:
- ANN("instance variable assignment");
- ANN("format: [nd_vid](ivar) = [nd_value]");
- ANN("example: @x = foo");
- F_ID(nd_vid, "instance variable");
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
+ ANN("instance variable assignment");
+ ANN("format: [nd_vid](ivar) = [nd_value]");
+ ANN("example: @x = foo");
+ F_ID(nd_vid, "instance variable");
+ LAST_NODE;
+ F_NODE(nd_value, "rvalue");
+ return;
case NODE_CVASGN:
- ANN("class variable assignment");
- ANN("format: [nd_vid](cvar) = [nd_value]");
- ANN("example: @@x = foo");
- F_ID(nd_vid, "class variable");
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
+ ANN("class variable assignment");
+ ANN("format: [nd_vid](cvar) = [nd_value]");
+ ANN("example: @@x = foo");
+ F_ID(nd_vid, "class variable");
+ LAST_NODE;
+ F_NODE(nd_value, "rvalue");
+ return;
case NODE_GASGN:
- ANN("global variable assignment");
- ANN("format: [nd_entry](gvar) = [nd_value]");
- ANN("example: $x = foo");
- F_GENTRY(nd_entry, "global variable");
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
+ ANN("global variable assignment");
+ ANN("format: [nd_entry](gvar) = [nd_value]");
+ ANN("example: $x = foo");
+ F_GENTRY(nd_entry, "global variable");
+ LAST_NODE;
+ F_NODE(nd_value, "rvalue");
+ return;
case NODE_CDECL:
- ANN("constant declaration");
- ANN("format: [nd_else]::[nd_vid](constant) = [nd_value]");
- ANN("example: X = foo");
- if (node->nd_vid) {
- F_ID(nd_vid, "constant");
- F_MSG(nd_else, "extension", "not used");
- }
- else {
- F_MSG(nd_vid, "constant", "0 (see extension field)");
- F_NODE(nd_else, "extension");
- }
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
+ ANN("constant declaration");
+ ANN("format: [nd_else]::[nd_vid](constant) = [nd_value]");
+ ANN("example: X = foo");
+ if (node->nd_vid) {
+ F_ID(nd_vid, "constant");
+ F_MSG(nd_else, "extension", "not used");
+ }
+ else {
+ F_MSG(nd_vid, "constant", "0 (see extension field)");
+ F_NODE(nd_else, "extension");
+ }
+ LAST_NODE;
+ F_NODE(nd_value, "rvalue");
+ return;
case NODE_OP_ASGN1:
- ANN("array assignment with operator");
- ANN("format: [nd_recv] [ [nd_args->nd_head] ] [nd_mid]= [nd_args->nd_body]");
- ANN("example: ary[1] += foo");
- F_NODE(nd_recv, "receiver");
- F_ID(nd_mid, "operator");
- F_NODE(nd_args->nd_head, "index");
- LAST_NODE;
- F_NODE(nd_args->nd_body, "rvalue");
- return;
+ ANN("array assignment with operator");
+ ANN("format: [nd_recv] [ [nd_args->nd_head] ] [nd_mid]= [nd_args->nd_body]");
+ ANN("example: ary[1] += foo");
+ F_NODE(nd_recv, "receiver");
+ F_ID(nd_mid, "operator");
+ F_NODE(nd_args->nd_head, "index");
+ LAST_NODE;
+ F_NODE(nd_args->nd_body, "rvalue");
+ return;
case NODE_OP_ASGN2:
- ANN("attr assignment with operator");
- ANN("format: [nd_recv].[attr] [nd_next->nd_mid]= [nd_value]");
- ANN(" where [attr]: [nd_next->nd_vid]");
- ANN("example: struct.field += foo");
- F_NODE(nd_recv, "receiver");
- F_CUSTOM1(nd_next->nd_vid, "attr") {
- if (node->nd_next->nd_aid) A("? ");
- A_ID(node->nd_next->nd_vid);
- }
- F_ID(nd_next->nd_mid, "operator");
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
+ ANN("attr assignment with operator");
+ ANN("format: [nd_recv].[attr] [nd_next->nd_mid]= [nd_value]");
+ ANN(" where [attr]: [nd_next->nd_vid]");
+ ANN("example: struct.field += foo");
+ F_NODE(nd_recv, "receiver");
+ F_CUSTOM1(nd_next->nd_vid, "attr") {
+ if (node->nd_next->nd_aid) A("? ");
+ A_ID(node->nd_next->nd_vid);
+ }
+ F_ID(nd_next->nd_mid, "operator");
+ LAST_NODE;
+ F_NODE(nd_value, "rvalue");
+ return;
case NODE_OP_ASGN_AND:
- ANN("assignment with && operator");
- ANN("format: [nd_head] &&= [nd_value]");
- ANN("example: foo &&= bar");
- goto asgn_andor;
+ ANN("assignment with && operator");
+ ANN("format: [nd_head] &&= [nd_value]");
+ ANN("example: foo &&= bar");
+ goto asgn_andor;
case NODE_OP_ASGN_OR:
- ANN("assignment with || operator");
- ANN("format: [nd_head] ||= [nd_value]");
- ANN("example: foo ||= bar");
+ ANN("assignment with || operator");
+ ANN("format: [nd_head] ||= [nd_value]");
+ ANN("example: foo ||= bar");
asgn_andor:
- F_NODE(nd_head, "variable");
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
+ F_NODE(nd_head, "variable");
+ LAST_NODE;
+ F_NODE(nd_value, "rvalue");
+ return;
case NODE_OP_CDECL:
- ANN("constant declaration with operator");
- ANN("format: [nd_head](constant) [nd_aid]= [nd_value]");
- ANN("example: A::B ||= 1");
- F_NODE(nd_head, "constant");
- F_ID(nd_aid, "operator");
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
+ ANN("constant declaration with operator");
+ ANN("format: [nd_head](constant) [nd_aid]= [nd_value]");
+ ANN("example: A::B ||= 1");
+ F_NODE(nd_head, "constant");
+ F_ID(nd_aid, "operator");
+ LAST_NODE;
+ F_NODE(nd_value, "rvalue");
+ return;
case NODE_CALL:
- ANN("method invocation");
- ANN("format: [nd_recv].[nd_mid]([nd_args])");
- ANN("example: obj.foo(1)");
- F_ID(nd_mid, "method id");
- F_NODE(nd_recv, "receiver");
- LAST_NODE;
- F_NODE(nd_args, "arguments");
- return;
+ ANN("method invocation");
+ ANN("format: [nd_recv].[nd_mid]([nd_args])");
+ ANN("example: obj.foo(1)");
+ F_ID(nd_mid, "method id");
+ F_NODE(nd_recv, "receiver");
+ LAST_NODE;
+ F_NODE(nd_args, "arguments");
+ return;
case NODE_OPCALL:
ANN("method invocation");
@@ -533,490 +533,490 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
return;
case NODE_FCALL:
- ANN("function call");
- ANN("format: [nd_mid]([nd_args])");
- ANN("example: foo(1)");
- F_ID(nd_mid, "method id");
- LAST_NODE;
- F_NODE(nd_args, "arguments");
- return;
+ ANN("function call");
+ ANN("format: [nd_mid]([nd_args])");
+ ANN("example: foo(1)");
+ F_ID(nd_mid, "method id");
+ LAST_NODE;
+ F_NODE(nd_args, "arguments");
+ return;
case NODE_VCALL:
- ANN("function call with no argument");
- ANN("format: [nd_mid]");
- ANN("example: foo");
- F_ID(nd_mid, "method id");
- return;
+ ANN("function call with no argument");
+ ANN("format: [nd_mid]");
+ ANN("example: foo");
+ F_ID(nd_mid, "method id");
+ return;
case NODE_QCALL:
- ANN("safe method invocation");
- ANN("format: [nd_recv]&.[nd_mid]([nd_args])");
- ANN("example: obj&.foo(1)");
- F_ID(nd_mid, "method id");
- F_NODE(nd_recv, "receiver");
- LAST_NODE;
- F_NODE(nd_args, "arguments");
- return;
+ ANN("safe method invocation");
+ ANN("format: [nd_recv]&.[nd_mid]([nd_args])");
+ ANN("example: obj&.foo(1)");
+ F_ID(nd_mid, "method id");
+ F_NODE(nd_recv, "receiver");
+ LAST_NODE;
+ F_NODE(nd_args, "arguments");
+ return;
case NODE_SUPER:
- ANN("super invocation");
- ANN("format: super [nd_args]");
- ANN("example: super 1");
- LAST_NODE;
- F_NODE(nd_args, "arguments");
- return;
+ ANN("super invocation");
+ ANN("format: super [nd_args]");
+ ANN("example: super 1");
+ LAST_NODE;
+ F_NODE(nd_args, "arguments");
+ return;
case NODE_ZSUPER:
- ANN("super invocation with no argument");
- ANN("format: super");
- ANN("example: super");
- return;
+ ANN("super invocation with no argument");
+ ANN("format: super");
+ ANN("example: super");
+ return;
case NODE_LIST:
- ANN("list constructor");
- ANN("format: [ [nd_head], [nd_next].. ] (length: [nd_alen])");
- ANN("example: [1, 2, 3]");
- goto ary;
+ ANN("list constructor");
+ ANN("format: [ [nd_head], [nd_next].. ] (length: [nd_alen])");
+ ANN("example: [1, 2, 3]");
+ goto ary;
case NODE_VALUES:
- ANN("return arguments");
- ANN("format: [ [nd_head], [nd_next].. ] (length: [nd_alen])");
- ANN("example: return 1, 2, 3");
+ ANN("return arguments");
+ ANN("format: [ [nd_head], [nd_next].. ] (length: [nd_alen])");
+ ANN("example: return 1, 2, 3");
ary:
- dump_array(buf, indent, comment, node);
- return;
+ dump_array(buf, indent, comment, node);
+ return;
case NODE_ZLIST:
- ANN("empty list constructor");
- ANN("format: []");
- ANN("example: []");
- return;
+ ANN("empty list constructor");
+ ANN("format: []");
+ ANN("example: []");
+ return;
case NODE_HASH:
if (!node->nd_brace) {
- ANN("keyword arguments");
- ANN("format: nd_head");
- ANN("example: a: 1, b: 2");
- }
- else {
- ANN("hash constructor");
- ANN("format: { [nd_head] }");
- ANN("example: { 1 => 2, 3 => 4 }");
- }
+ ANN("keyword arguments");
+ ANN("format: nd_head");
+ ANN("example: a: 1, b: 2");
+ }
+ else {
+ ANN("hash constructor");
+ ANN("format: { [nd_head] }");
+ ANN("example: { 1 => 2, 3 => 4 }");
+ }
F_CUSTOM1(nd_brace, "keyword arguments or hash literal") {
switch (node->nd_brace) {
- case 0: A("0 (keyword argument)"); break;
- case 1: A("1 (hash literal)"); break;
- }
- }
- LAST_NODE;
- F_NODE(nd_head, "contents");
- return;
+ case 0: A("0 (keyword argument)"); break;
+ case 1: A("1 (hash literal)"); break;
+ }
+ }
+ LAST_NODE;
+ F_NODE(nd_head, "contents");
+ return;
case NODE_YIELD:
- ANN("yield invocation");
- ANN("format: yield [nd_head]");
- ANN("example: yield 1");
- LAST_NODE;
- F_NODE(nd_head, "arguments");
- return;
+ ANN("yield invocation");
+ ANN("format: yield [nd_head]");
+ ANN("example: yield 1");
+ LAST_NODE;
+ F_NODE(nd_head, "arguments");
+ return;
case NODE_LVAR:
- ANN("local variable reference");
- ANN("format: [nd_vid](lvar)");
- ANN("example: x");
- F_ID(nd_vid, "local variable");
- return;
+ ANN("local variable reference");
+ ANN("format: [nd_vid](lvar)");
+ ANN("example: x");
+ F_ID(nd_vid, "local variable");
+ return;
case NODE_DVAR:
- ANN("dynamic variable reference");
- ANN("format: [nd_vid](dvar)");
- ANN("example: 1.times { x = 1; x }");
- F_ID(nd_vid, "local variable");
- return;
+ ANN("dynamic variable reference");
+ ANN("format: [nd_vid](dvar)");
+ ANN("example: 1.times { x = 1; x }");
+ F_ID(nd_vid, "local variable");
+ return;
case NODE_IVAR:
- ANN("instance variable reference");
- ANN("format: [nd_vid](ivar)");
- ANN("example: @x");
- F_ID(nd_vid, "instance variable");
- return;
+ ANN("instance variable reference");
+ ANN("format: [nd_vid](ivar)");
+ ANN("example: @x");
+ F_ID(nd_vid, "instance variable");
+ return;
case NODE_CONST:
- ANN("constant reference");
- ANN("format: [nd_vid](constant)");
- ANN("example: X");
- F_ID(nd_vid, "constant");
- return;
+ ANN("constant reference");
+ ANN("format: [nd_vid](constant)");
+ ANN("example: X");
+ F_ID(nd_vid, "constant");
+ return;
case NODE_CVAR:
- ANN("class variable reference");
- ANN("format: [nd_vid](cvar)");
- ANN("example: @@x");
- F_ID(nd_vid, "class variable");
- return;
+ ANN("class variable reference");
+ ANN("format: [nd_vid](cvar)");
+ ANN("example: @@x");
+ F_ID(nd_vid, "class variable");
+ return;
case NODE_GVAR:
- ANN("global variable reference");
- ANN("format: [nd_entry](gvar)");
- ANN("example: $x");
- F_GENTRY(nd_entry, "global variable");
- return;
+ ANN("global variable reference");
+ ANN("format: [nd_entry](gvar)");
+ ANN("example: $x");
+ F_GENTRY(nd_entry, "global variable");
+ return;
case NODE_NTH_REF:
- ANN("nth special variable reference");
- ANN("format: $[nd_nth]");
- ANN("example: $1, $2, ..");
- F_CUSTOM1(nd_nth, "variable") { A("$"); A_LONG(node->nd_nth); }
- return;
+ ANN("nth special variable reference");
+ ANN("format: $[nd_nth]");
+ ANN("example: $1, $2, ..");
+ F_CUSTOM1(nd_nth, "variable") { A("$"); A_LONG(node->nd_nth); }
+ return;
case NODE_BACK_REF:
- ANN("back special variable reference");
- ANN("format: $[nd_nth]");
- ANN("example: $&, $`, $', $+");
- F_CUSTOM1(nd_nth, "variable") {
- char name[3] = "$ ";
- name[1] = (char)node->nd_nth;
- A(name);
- }
- return;
+ ANN("back special variable reference");
+ ANN("format: $[nd_nth]");
+ ANN("example: $&, $`, $', $+");
+ F_CUSTOM1(nd_nth, "variable") {
+ char name[3] = "$ ";
+ name[1] = (char)node->nd_nth;
+ A(name);
+ }
+ return;
case NODE_MATCH:
- ANN("match expression (against $_ implicitly)");
+ ANN("match expression (against $_ implicitly)");
ANN("format: [nd_lit] (in condition)");
- ANN("example: if /foo/; foo; end");
- F_LIT(nd_lit, "regexp");
- return;
+ ANN("example: if /foo/; foo; end");
+ F_LIT(nd_lit, "regexp");
+ return;
case NODE_MATCH2:
- ANN("match expression (regexp first)");
+ ANN("match expression (regexp first)");
ANN("format: [nd_recv] =~ [nd_value]");
- ANN("example: /foo/ =~ 'foo'");
- F_NODE(nd_recv, "regexp (receiver)");
- if (!node->nd_args) LAST_NODE;
- F_NODE(nd_value, "string (argument)");
- if (node->nd_args) {
- LAST_NODE;
- F_NODE(nd_args, "named captures");
- }
- return;
+ ANN("example: /foo/ =~ 'foo'");
+ F_NODE(nd_recv, "regexp (receiver)");
+ if (!node->nd_args) LAST_NODE;
+ F_NODE(nd_value, "string (argument)");
+ if (node->nd_args) {
+ LAST_NODE;
+ F_NODE(nd_args, "named captures");
+ }
+ return;
case NODE_MATCH3:
- ANN("match expression (regexp second)");
+ ANN("match expression (regexp second)");
ANN("format: [nd_recv] =~ [nd_value]");
- ANN("example: 'foo' =~ /foo/");
- F_NODE(nd_recv, "string (receiver)");
- LAST_NODE;
- F_NODE(nd_value, "regexp (argument)");
- return;
+ ANN("example: 'foo' =~ /foo/");
+ F_NODE(nd_recv, "string (receiver)");
+ LAST_NODE;
+ F_NODE(nd_value, "regexp (argument)");
+ return;
case NODE_LIT:
- ANN("literal");
- ANN("format: [nd_lit]");
- ANN("example: 1, /foo/");
- goto lit;
+ ANN("literal");
+ ANN("format: [nd_lit]");
+ ANN("example: 1, /foo/");
+ goto lit;
case NODE_STR:
- ANN("string literal");
- ANN("format: [nd_lit]");
- ANN("example: 'foo'");
- goto lit;
+ ANN("string literal");
+ ANN("format: [nd_lit]");
+ ANN("example: 'foo'");
+ goto lit;
case NODE_XSTR:
- ANN("xstring literal");
- ANN("format: [nd_lit]");
- ANN("example: `foo`");
+ ANN("xstring literal");
+ ANN("format: [nd_lit]");
+ ANN("example: `foo`");
lit:
- F_LIT(nd_lit, "literal");
- return;
+ F_LIT(nd_lit, "literal");
+ return;
case NODE_ONCE:
- ANN("once evaluation");
- ANN("format: [nd_body]");
- ANN("example: /foo#{ bar }baz/o");
- LAST_NODE;
- F_NODE(nd_body, "body");
- return;
+ ANN("once evaluation");
+ ANN("format: [nd_body]");
+ ANN("example: /foo#{ bar }baz/o");
+ LAST_NODE;
+ F_NODE(nd_body, "body");
+ return;
case NODE_DSTR:
- ANN("string literal with interpolation");
- ANN("format: [nd_lit]");
- ANN("example: \"foo#{ bar }baz\"");
- goto dlit;
+ ANN("string literal with interpolation");
+ ANN("format: [nd_lit]");
+ ANN("example: \"foo#{ bar }baz\"");
+ goto dlit;
case NODE_DXSTR:
- ANN("xstring literal with interpolation");
- ANN("format: [nd_lit]");
- ANN("example: `foo#{ bar }baz`");
- goto dlit;
+ ANN("xstring literal with interpolation");
+ ANN("format: [nd_lit]");
+ ANN("example: `foo#{ bar }baz`");
+ goto dlit;
case NODE_DREGX:
- ANN("regexp literal with interpolation");
- ANN("format: [nd_lit]");
- ANN("example: /foo#{ bar }baz/");
- goto dlit;
+ ANN("regexp literal with interpolation");
+ ANN("format: [nd_lit]");
+ ANN("example: /foo#{ bar }baz/");
+ goto dlit;
case NODE_DSYM:
- ANN("symbol literal with interpolation");
- ANN("format: [nd_lit]");
- ANN("example: :\"foo#{ bar }baz\"");
+ ANN("symbol literal with interpolation");
+ ANN("format: [nd_lit]");
+ ANN("example: :\"foo#{ bar }baz\"");
dlit:
- F_LIT(nd_lit, "preceding string");
- if (!node->nd_next) return;
- F_NODE(nd_next->nd_head, "interpolation");
- LAST_NODE;
- F_NODE(nd_next->nd_next, "tailing strings");
- return;
+ F_LIT(nd_lit, "preceding string");
+ if (!node->nd_next) return;
+ F_NODE(nd_next->nd_head, "interpolation");
+ LAST_NODE;
+ F_NODE(nd_next->nd_next, "tailing strings");
+ return;
case NODE_EVSTR:
- ANN("interpolation expression");
- ANN("format: \"..#{ [nd_lit] }..\"");
- ANN("example: \"foo#{ bar }baz\"");
- LAST_NODE;
- F_NODE(nd_body, "body");
- return;
+ ANN("interpolation expression");
+ ANN("format: \"..#{ [nd_lit] }..\"");
+ ANN("example: \"foo#{ bar }baz\"");
+ LAST_NODE;
+ F_NODE(nd_body, "body");
+ return;
case NODE_ARGSCAT:
- ANN("splat argument following arguments");
- ANN("format: ..(*[nd_head], [nd_body..])");
- ANN("example: foo(*ary, post_arg1, post_arg2)");
- F_NODE(nd_head, "preceding array");
- LAST_NODE;
- F_NODE(nd_body, "following array");
- return;
+ ANN("splat argument following arguments");
+ ANN("format: ..(*[nd_head], [nd_body..])");
+ ANN("example: foo(*ary, post_arg1, post_arg2)");
+ F_NODE(nd_head, "preceding array");
+ LAST_NODE;
+ F_NODE(nd_body, "following array");
+ return;
case NODE_ARGSPUSH:
- ANN("splat argument following one argument");
- ANN("format: ..(*[nd_head], [nd_body])");
- ANN("example: foo(*ary, post_arg)");
- F_NODE(nd_head, "preceding array");
- LAST_NODE;
- F_NODE(nd_body, "following element");
- return;
+ ANN("splat argument following one argument");
+ ANN("format: ..(*[nd_head], [nd_body])");
+ ANN("example: foo(*ary, post_arg)");
+ F_NODE(nd_head, "preceding array");
+ LAST_NODE;
+ F_NODE(nd_body, "following element");
+ return;
case NODE_SPLAT:
- ANN("splat argument");
- ANN("format: *[nd_head]");
- ANN("example: foo(*ary)");
- LAST_NODE;
- F_NODE(nd_head, "splat'ed array");
- return;
+ ANN("splat argument");
+ ANN("format: *[nd_head]");
+ ANN("example: foo(*ary)");
+ LAST_NODE;
+ F_NODE(nd_head, "splat'ed array");
+ return;
case NODE_BLOCK_PASS:
- ANN("arguments with block argument");
- ANN("format: ..([nd_head], &[nd_body])");
- ANN("example: foo(x, &blk)");
- F_NODE(nd_head, "other arguments");
- LAST_NODE;
- F_NODE(nd_body, "block argument");
- return;
+ ANN("arguments with block argument");
+ ANN("format: ..([nd_head], &[nd_body])");
+ ANN("example: foo(x, &blk)");
+ F_NODE(nd_head, "other arguments");
+ LAST_NODE;
+ F_NODE(nd_body, "block argument");
+ return;
case NODE_DEFN:
- ANN("method definition");
- ANN("format: def [nd_mid] [nd_defn]; end");
- ANN("example: def foo; bar; end");
- F_ID(nd_mid, "method name");
- LAST_NODE;
- F_NODE(nd_defn, "method definition");
- return;
+ ANN("method definition");
+ ANN("format: def [nd_mid] [nd_defn]; end");
+ ANN("example: def foo; bar; end");
+ F_ID(nd_mid, "method name");
+ LAST_NODE;
+ F_NODE(nd_defn, "method definition");
+ return;
case NODE_DEFS:
- ANN("singleton method definition");
- ANN("format: def [nd_recv].[nd_mid] [nd_defn]; end");
- ANN("example: def obj.foo; bar; end");
- F_NODE(nd_recv, "receiver");
- F_ID(nd_mid, "method name");
- LAST_NODE;
- F_NODE(nd_defn, "method definition");
- return;
+ ANN("singleton method definition");
+ ANN("format: def [nd_recv].[nd_mid] [nd_defn]; end");
+ ANN("example: def obj.foo; bar; end");
+ F_NODE(nd_recv, "receiver");
+ F_ID(nd_mid, "method name");
+ LAST_NODE;
+ F_NODE(nd_defn, "method definition");
+ return;
case NODE_ALIAS:
- ANN("method alias statement");
- ANN("format: alias [nd_1st] [nd_2nd]");
- ANN("example: alias bar foo");
- F_NODE(nd_1st, "new name");
- LAST_NODE;
- F_NODE(nd_2nd, "old name");
- return;
+ ANN("method alias statement");
+ ANN("format: alias [nd_1st] [nd_2nd]");
+ ANN("example: alias bar foo");
+ F_NODE(nd_1st, "new name");
+ LAST_NODE;
+ F_NODE(nd_2nd, "old name");
+ return;
case NODE_VALIAS:
- ANN("global variable alias statement");
- ANN("format: alias [nd_alias](gvar) [nd_orig](gvar)");
- ANN("example: alias $y $x");
- F_ID(nd_alias, "new name");
- F_ID(nd_orig, "old name");
- return;
+ ANN("global variable alias statement");
+ ANN("format: alias [nd_alias](gvar) [nd_orig](gvar)");
+ ANN("example: alias $y $x");
+ F_ID(nd_alias, "new name");
+ F_ID(nd_orig, "old name");
+ return;
case NODE_UNDEF:
- ANN("method undef statement");
- ANN("format: undef [nd_undef]");
- ANN("example: undef foo");
- LAST_NODE;
- F_NODE(nd_undef, "old name");
- return;
+ ANN("method undef statement");
+ ANN("format: undef [nd_undef]");
+ ANN("example: undef foo");
+ LAST_NODE;
+ F_NODE(nd_undef, "old name");
+ return;
case NODE_CLASS:
- ANN("class definition");
- ANN("format: class [nd_cpath] < [nd_super]; [nd_body]; end");
- ANN("example: class C2 < C; ..; end");
- F_NODE(nd_cpath, "class path");
- F_NODE(nd_super, "superclass");
- LAST_NODE;
- F_NODE(nd_body, "class definition");
- return;
+ ANN("class definition");
+ ANN("format: class [nd_cpath] < [nd_super]; [nd_body]; end");
+ ANN("example: class C2 < C; ..; end");
+ F_NODE(nd_cpath, "class path");
+ F_NODE(nd_super, "superclass");
+ LAST_NODE;
+ F_NODE(nd_body, "class definition");
+ return;
case NODE_MODULE:
- ANN("module definition");
- ANN("format: module [nd_cpath]; [nd_body]; end");
- ANN("example: module M; ..; end");
- F_NODE(nd_cpath, "module path");
- LAST_NODE;
- F_NODE(nd_body, "module definition");
- return;
+ ANN("module definition");
+ ANN("format: module [nd_cpath]; [nd_body]; end");
+ ANN("example: module M; ..; end");
+ F_NODE(nd_cpath, "module path");
+ LAST_NODE;
+ F_NODE(nd_body, "module definition");
+ return;
case NODE_SCLASS:
- ANN("singleton class definition");
- ANN("format: class << [nd_recv]; [nd_body]; end");
- ANN("example: class << obj; ..; end");
- F_NODE(nd_recv, "receiver");
- LAST_NODE;
- F_NODE(nd_body, "singleton class definition");
- return;
+ ANN("singleton class definition");
+ ANN("format: class << [nd_recv]; [nd_body]; end");
+ ANN("example: class << obj; ..; end");
+ F_NODE(nd_recv, "receiver");
+ LAST_NODE;
+ F_NODE(nd_body, "singleton class definition");
+ return;
case NODE_COLON2:
- ANN("scoped constant reference");
- ANN("format: [nd_head]::[nd_mid]");
- ANN("example: M::C");
- F_ID(nd_mid, "constant name");
- LAST_NODE;
- F_NODE(nd_head, "receiver");
- return;
+ ANN("scoped constant reference");
+ ANN("format: [nd_head]::[nd_mid]");
+ ANN("example: M::C");
+ F_ID(nd_mid, "constant name");
+ LAST_NODE;
+ F_NODE(nd_head, "receiver");
+ return;
case NODE_COLON3:
- ANN("top-level constant reference");
- ANN("format: ::[nd_mid]");
- ANN("example: ::Object");
- F_ID(nd_mid, "constant name");
- return;
+ ANN("top-level constant reference");
+ ANN("format: ::[nd_mid]");
+ ANN("example: ::Object");
+ F_ID(nd_mid, "constant name");
+ return;
case NODE_DOT2:
- ANN("range constructor (incl.)");
- ANN("format: [nd_beg]..[nd_end]");
- ANN("example: 1..5");
- goto dot;
+ ANN("range constructor (incl.)");
+ ANN("format: [nd_beg]..[nd_end]");
+ ANN("example: 1..5");
+ goto dot;
case NODE_DOT3:
- ANN("range constructor (excl.)");
- ANN("format: [nd_beg]...[nd_end]");
- ANN("example: 1...5");
- goto dot;
+ ANN("range constructor (excl.)");
+ ANN("format: [nd_beg]...[nd_end]");
+ ANN("example: 1...5");
+ goto dot;
case NODE_FLIP2:
- ANN("flip-flop condition (incl.)");
- ANN("format: [nd_beg]..[nd_end]");
- ANN("example: if (x==1)..(x==5); foo; end");
- goto dot;
+ ANN("flip-flop condition (incl.)");
+ ANN("format: [nd_beg]..[nd_end]");
+ ANN("example: if (x==1)..(x==5); foo; end");
+ goto dot;
case NODE_FLIP3:
- ANN("flip-flop condition (excl.)");
- ANN("format: [nd_beg]...[nd_end]");
- ANN("example: if (x==1)...(x==5); foo; end");
+ ANN("flip-flop condition (excl.)");
+ ANN("format: [nd_beg]...[nd_end]");
+ ANN("example: if (x==1)...(x==5); foo; end");
dot:
- F_NODE(nd_beg, "begin");
- LAST_NODE;
- F_NODE(nd_end, "end");
- return;
+ F_NODE(nd_beg, "begin");
+ LAST_NODE;
+ F_NODE(nd_end, "end");
+ return;
case NODE_SELF:
- ANN("self");
- ANN("format: self");
- ANN("example: self");
- return;
+ ANN("self");
+ ANN("format: self");
+ ANN("example: self");
+ return;
case NODE_NIL:
- ANN("nil");
- ANN("format: nil");
- ANN("example: nil");
- return;
+ ANN("nil");
+ ANN("format: nil");
+ ANN("example: nil");
+ return;
case NODE_TRUE:
- ANN("true");
- ANN("format: true");
- ANN("example: true");
- return;
+ ANN("true");
+ ANN("format: true");
+ ANN("example: true");
+ return;
case NODE_FALSE:
- ANN("false");
- ANN("format: false");
- ANN("example: false");
- return;
+ ANN("false");
+ ANN("format: false");
+ ANN("example: false");
+ return;
case NODE_ERRINFO:
- ANN("virtual reference to $!");
- ANN("format: rescue => id");
- ANN("example: rescue => id");
- return;
+ ANN("virtual reference to $!");
+ ANN("format: rescue => id");
+ ANN("example: rescue => id");
+ return;
case NODE_DEFINED:
- ANN("defined? expression");
- ANN("format: defined?([nd_head])");
- ANN("example: defined?(foo)");
- F_NODE(nd_head, "expr");
- return;
+ ANN("defined? expression");
+ ANN("format: defined?([nd_head])");
+ ANN("example: defined?(foo)");
+ F_NODE(nd_head, "expr");
+ return;
case NODE_POSTEXE:
- ANN("post-execution");
- ANN("format: END { [nd_body] }");
- ANN("example: END { foo }");
- LAST_NODE;
- F_NODE(nd_body, "END clause");
- return;
+ ANN("post-execution");
+ ANN("format: END { [nd_body] }");
+ ANN("example: END { foo }");
+ LAST_NODE;
+ F_NODE(nd_body, "END clause");
+ return;
case NODE_ATTRASGN:
- ANN("attr assignment");
- ANN("format: [nd_recv].[nd_mid] = [nd_args]");
- ANN("example: struct.field = foo");
- F_NODE(nd_recv, "receiver");
- F_ID(nd_mid, "method name");
- LAST_NODE;
- F_NODE(nd_args, "arguments");
- return;
+ ANN("attr assignment");
+ ANN("format: [nd_recv].[nd_mid] = [nd_args]");
+ ANN("example: struct.field = foo");
+ F_NODE(nd_recv, "receiver");
+ F_ID(nd_mid, "method name");
+ LAST_NODE;
+ F_NODE(nd_args, "arguments");
+ return;
case NODE_LAMBDA:
- ANN("lambda expression");
- ANN("format: -> [nd_body]");
- ANN("example: -> { foo }");
- LAST_NODE;
- F_NODE(nd_body, "lambda clause");
- return;
+ ANN("lambda expression");
+ ANN("format: -> [nd_body]");
+ ANN("example: -> { foo }");
+ LAST_NODE;
+ F_NODE(nd_body, "lambda clause");
+ return;
case NODE_OPT_ARG:
- ANN("optional arguments");
- ANN("format: def method_name([nd_body=some], [nd_next..])");
- ANN("example: def foo(a, b=1, c); end");
- F_NODE(nd_body, "body");
- LAST_NODE;
- F_NODE(nd_next, "next");
- return;
+ ANN("optional arguments");
+ ANN("format: def method_name([nd_body=some], [nd_next..])");
+ ANN("example: def foo(a, b=1, c); end");
+ F_NODE(nd_body, "body");
+ LAST_NODE;
+ F_NODE(nd_next, "next");
+ return;
case NODE_KW_ARG:
- ANN("keyword arguments");
- ANN("format: def method_name([nd_body=some], [nd_next..])");
- ANN("example: def foo(a:1, b:2); end");
- F_NODE(nd_body, "body");
- LAST_NODE;
- F_NODE(nd_next, "next");
- return;
+ ANN("keyword arguments");
+ ANN("format: def method_name([nd_body=some], [nd_next..])");
+ ANN("example: def foo(a:1, b:2); end");
+ F_NODE(nd_body, "body");
+ LAST_NODE;
+ F_NODE(nd_next, "next");
+ return;
case NODE_POSTARG:
- ANN("post arguments");
- ANN("format: *[nd_1st], [nd_2nd..] = ..");
- ANN("example: a, *rest, z = foo");
- if (NODE_NAMED_REST_P(node->nd_1st)) {
- F_NODE(nd_1st, "rest argument");
- }
- else {
- F_MSG(nd_1st, "rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
- }
- LAST_NODE;
- F_NODE(nd_2nd, "post arguments");
- return;
+ ANN("post arguments");
+ ANN("format: *[nd_1st], [nd_2nd..] = ..");
+ ANN("example: a, *rest, z = foo");
+ if (NODE_NAMED_REST_P(node->nd_1st)) {
+ F_NODE(nd_1st, "rest argument");
+ }
+ else {
+ F_MSG(nd_1st, "rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
+ }
+ LAST_NODE;
+ F_NODE(nd_2nd, "post arguments");
+ return;
case NODE_ARGS:
- ANN("method parameters");
- ANN("format: def method_name(.., [nd_ainfo->nd_optargs], *[nd_ainfo->rest_arg], [nd_ainfo->first_post_arg], .., [nd_ainfo->kw_args], **[nd_ainfo->kw_rest_arg], &[nd_ainfo->block_arg])");
- ANN("example: def foo(a, b, opt1=1, opt2=2, *rest, y, z, kw: 1, **kwrest, &blk); end");
- F_INT(nd_ainfo->pre_args_num, "count of mandatory (pre-)arguments");
- F_NODE(nd_ainfo->pre_init, "initialization of (pre-)arguments");
- F_INT(nd_ainfo->post_args_num, "count of mandatory post-arguments");
- F_NODE(nd_ainfo->post_init, "initialization of post-arguments");
- F_ID(nd_ainfo->first_post_arg, "first post argument");
+ ANN("method parameters");
+ ANN("format: def method_name(.., [nd_ainfo->nd_optargs], *[nd_ainfo->rest_arg], [nd_ainfo->first_post_arg], .., [nd_ainfo->kw_args], **[nd_ainfo->kw_rest_arg], &[nd_ainfo->block_arg])");
+ ANN("example: def foo(a, b, opt1=1, opt2=2, *rest, y, z, kw: 1, **kwrest, &blk); end");
+ F_INT(nd_ainfo->pre_args_num, "count of mandatory (pre-)arguments");
+ F_NODE(nd_ainfo->pre_init, "initialization of (pre-)arguments");
+ F_INT(nd_ainfo->post_args_num, "count of mandatory post-arguments");
+ F_NODE(nd_ainfo->post_init, "initialization of post-arguments");
+ F_ID(nd_ainfo->first_post_arg, "first post argument");
F_CUSTOM1(nd_ainfo->rest_arg, "rest argument") {
if (node->nd_ainfo->rest_arg == NODE_SPECIAL_EXCESSIVE_COMMA) {
A("1 (excessed comma)");
@@ -1025,29 +1025,29 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
A_ID(node->nd_ainfo->rest_arg);
}
}
- F_ID(nd_ainfo->block_arg, "block argument");
- F_NODE(nd_ainfo->opt_args, "optional arguments");
- F_NODE(nd_ainfo->kw_args, "keyword arguments");
- LAST_NODE;
- F_NODE(nd_ainfo->kw_rest_arg, "keyword rest argument");
- return;
+ F_ID(nd_ainfo->block_arg, "block argument");
+ F_NODE(nd_ainfo->opt_args, "optional arguments");
+ F_NODE(nd_ainfo->kw_args, "keyword arguments");
+ LAST_NODE;
+ F_NODE(nd_ainfo->kw_rest_arg, "keyword rest argument");
+ return;
case NODE_SCOPE:
- ANN("new scope");
- ANN("format: [nd_tbl]: local table, [nd_args]: arguments, [nd_body]: body");
- F_CUSTOM1(nd_tbl, "local table") {
- rb_ast_id_table_t *tbl = node->nd_tbl;
- int i;
- int size = tbl ? tbl->size : 0;
- if (size == 0) A("(empty)");
- for (i = 0; i < size; i++) {
- A_ID(tbl->ids[i]); if (i < size - 1) A(",");
- }
- }
- F_NODE(nd_args, "arguments");
- LAST_NODE;
- F_NODE(nd_body, "body");
- return;
+ ANN("new scope");
+ ANN("format: [nd_tbl]: local table, [nd_args]: arguments, [nd_body]: body");
+ F_CUSTOM1(nd_tbl, "local table") {
+ rb_ast_id_table_t *tbl = node->nd_tbl;
+ int i;
+ int size = tbl ? tbl->size : 0;
+ if (size == 0) A("(empty)");
+ for (i = 0; i < size; i++) {
+ A_ID(tbl->ids[i]); if (i < size - 1) A(",");
+ }
+ }
+ F_NODE(nd_args, "arguments");
+ LAST_NODE;
+ F_NODE(nd_body, "body");
+ return;
case NODE_ARYPTN:
ANN("array pattern");
@@ -1098,10 +1098,13 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
F_NODE(nd_pkwrestarg, "keyword rest argument");
}
return;
+ case NODE_ERROR:
+ ANN("Broken input recovered by Error Tolerant mode");
+ return;
case NODE_ARGS_AUX:
case NODE_LAST:
- break;
+ break;
}
rb_bug("dump_node: unknown node: %s", ruby_node_name(nd_type(node)));
@@ -1111,10 +1114,10 @@ VALUE
rb_parser_dump_tree(const NODE *node, int comment)
{
VALUE buf = rb_str_new_cstr(
- "###########################################################\n"
- "## Do NOT use this node dump for any purpose other than ##\n"
- "## debug and research. Compatibility is not guaranteed. ##\n"
- "###########################################################\n\n"
+ "###########################################################\n"
+ "## Do NOT use this node dump for any purpose other than ##\n"
+ "## debug and research. Compatibility is not guaranteed. ##\n"
+ "###########################################################\n\n"
);
dump_node(buf, rb_str_new_cstr("# "), comment, node);
return buf;
@@ -1138,6 +1141,7 @@ rb_node_init(NODE *n, enum node_type type, VALUE a0, VALUE a1, VALUE a2)
n->nd_loc.beg_pos.column = 0;
n->nd_loc.end_pos.lineno = 0;
n->nd_loc.end_pos.column = 0;
+ n->node_id = -1;
}
typedef struct node_buffer_elem_struct {
@@ -1157,6 +1161,12 @@ struct node_buffer_struct {
node_buffer_list_t markable;
struct rb_ast_local_table_link *local_tables;
VALUE mark_hash;
+ // - id (sequence number)
+ // - token_type
+ // - text of token
+ // - location info
+ // Array, whose entry is array
+ VALUE tokens;
};
static void
@@ -1183,6 +1193,7 @@ rb_node_buffer_new(void)
init_node_buffer_list(&nb->markable, (node_buffer_elem_t*)((size_t)nb->unmarkable.head + bucket_size));
nb->local_tables = 0;
nb->mark_hash = Qnil;
+ nb->tokens = Qnil;
return nb;
}
@@ -1192,9 +1203,9 @@ node_buffer_list_free(node_buffer_list_t * nb)
node_buffer_elem_t *nbe = nb->head;
while (nbe != nb->last) {
- void *buf = nbe;
- nbe = nbe->next;
- xfree(buf);
+ void *buf = nbe;
+ nbe = nbe->next;
+ xfree(buf);
}
}
@@ -1224,14 +1235,14 @@ static NODE *
ast_newnode_in_bucket(node_buffer_list_t *nb)
{
if (nb->idx >= nb->len) {
- long n = nb->len * 2;
- node_buffer_elem_t *nbe;
+ long n = nb->len * 2;
+ node_buffer_elem_t *nbe;
nbe = rb_xmalloc_mul_add(n, sizeof(NODE), offsetof(node_buffer_elem_t, buf));
nbe->len = n;
- nb->idx = 0;
- nb->len = n;
- nbe->next = nb->head;
- nb->head = nbe;
+ nb->idx = 0;
+ nb->len = n;
+ nbe->next = nb->head;
+ nb->head = nbe;
}
return &nb->head->buf[nb->idx++];
}
@@ -1414,7 +1425,10 @@ rb_ast_update_references(rb_ast_t *ast)
void
rb_ast_mark(rb_ast_t *ast)
{
- if (ast->node_buffer) rb_gc_mark(ast->node_buffer->mark_hash);
+ if (ast->node_buffer) {
+ rb_gc_mark(ast->node_buffer->mark_hash);
+ rb_gc_mark(ast->node_buffer->tokens);
+ }
if (ast->body.compile_option) rb_gc_mark(ast->body.compile_option);
if (ast->node_buffer) {
node_buffer_t *nb = ast->node_buffer;
@@ -1428,8 +1442,8 @@ void
rb_ast_free(rb_ast_t *ast)
{
if (ast->node_buffer) {
- rb_node_buffer_free(ast->node_buffer);
- ast->node_buffer = 0;
+ rb_node_buffer_free(ast->node_buffer);
+ ast->node_buffer = 0;
}
}
@@ -1473,3 +1487,15 @@ rb_ast_add_mark_object(rb_ast_t *ast, VALUE obj)
}
rb_hash_aset(ast->node_buffer->mark_hash, obj, Qtrue);
}
+
+VALUE
+rb_ast_tokens(rb_ast_t *ast)
+{
+ return ast->node_buffer->tokens;
+}
+
+void
+rb_ast_set_tokens(rb_ast_t *ast, VALUE tokens)
+{
+ RB_OBJ_WRITE(ast, &ast->node_buffer->tokens, tokens);
+}
diff --git a/node.h b/node.h
index ebe82b85be..befb1328fb 100644
--- a/node.h
+++ b/node.h
@@ -125,6 +125,7 @@ enum node_type {
NODE_ARYPTN,
NODE_HSHPTN,
NODE_FNDPTN,
+ NODE_ERROR,
NODE_LAST
};
@@ -155,25 +156,25 @@ typedef struct rb_ast_id_table {
typedef struct RNode {
VALUE flags;
union {
- struct RNode *node;
- ID id;
- VALUE value;
- rb_ast_id_table_t *tbl;
+ struct RNode *node;
+ ID id;
+ VALUE value;
+ rb_ast_id_table_t *tbl;
} u1;
union {
- struct RNode *node;
- ID id;
- long argc;
- VALUE value;
+ struct RNode *node;
+ ID id;
+ long argc;
+ VALUE value;
} u2;
union {
- struct RNode *node;
- ID id;
- long state;
- struct rb_args_info *args;
- struct rb_ary_pattern_info *apinfo;
- struct rb_fnd_pattern_info *fpinfo;
- VALUE value;
+ struct RNode *node;
+ ID id;
+ long state;
+ struct rb_args_info *args;
+ struct rb_ary_pattern_info *apinfo;
+ struct rb_fnd_pattern_info *fpinfo;
+ VALUE value;
} u3;
rb_code_location_t nd_loc;
int node_id;
@@ -383,9 +384,9 @@ typedef struct RNode {
#define NEW_FALSE(loc) NEW_NODE(NODE_FALSE,0,0,0,loc)
#define NEW_ERRINFO(loc) NEW_NODE(NODE_ERRINFO,0,0,0,loc)
#define NEW_DEFINED(e,loc) NEW_NODE(NODE_DEFINED,e,0,0,loc)
-#define NEW_PREEXE(b,loc) NEW_SCOPE(b,loc)
#define NEW_POSTEXE(b,loc) NEW_NODE(NODE_POSTEXE,0,b,0,loc)
#define NEW_ATTRASGN(r,m,a,loc) NEW_NODE(NODE_ATTRASGN,r,m,a,loc)
+#define NEW_ERROR(loc) NEW_NODE(NODE_ERROR,0,0,0,loc)
#define NODE_SPECIAL_REQUIRED_KEYWORD ((NODE *)-1)
#define NODE_REQUIRED_KEYWORD_P(node) ((node)->nd_value == NODE_SPECIAL_REQUIRED_KEYWORD)
@@ -420,6 +421,8 @@ void rb_ast_dispose(rb_ast_t*);
void rb_ast_free(rb_ast_t*);
size_t rb_ast_memsize(const rb_ast_t*);
void rb_ast_add_mark_object(rb_ast_t*, VALUE);
+void rb_ast_set_tokens(rb_ast_t*, VALUE);
+VALUE rb_ast_tokens(rb_ast_t *ast);
NODE *rb_ast_newnode(rb_ast_t*, enum node_type type);
void rb_ast_delete_node(rb_ast_t*, NODE *n);
rb_ast_id_table_t *rb_ast_new_local_table(rb_ast_t*, int);
@@ -460,6 +463,7 @@ struct rb_args_info {
NODE *opt_args;
unsigned int no_kwarg: 1;
unsigned int ruby2_keywords: 1;
+ unsigned int forwarding: 1;
VALUE imemo;
};
diff --git a/numeric.c b/numeric.c
index 1b1c8d99b9..6dd1ff6d2f 100644
--- a/numeric.c
+++ b/numeric.c
@@ -95,12 +95,12 @@ round(double x)
double f;
if (x > 0.0) {
- f = floor(x);
- x = f + (x - f >= 0.5);
+ f = floor(x);
+ x = f + (x - f >= 0.5);
}
else if (x < 0.0) {
- f = ceil(x);
- x = f - (f - x >= 0.5);
+ f = ceil(x);
+ x = f - (f - x >= 0.5);
}
return x;
}
@@ -114,12 +114,12 @@ round_half_up(double x, double s)
f = round(xs);
if (s == 1.0) return f;
if (x > 0) {
- if ((double)((f + 0.5) / s) <= x) f += 1;
- x = f;
+ if ((double)((f + 0.5) / s) <= x) f += 1;
+ x = f;
}
else {
- if ((double)((f - 0.5) / s) >= x) f -= 1;
- x = f;
+ if ((double)((f - 0.5) / s) >= x) f -= 1;
+ x = f;
}
return x;
}
@@ -131,12 +131,12 @@ round_half_down(double x, double s)
f = round(xs);
if (x > 0) {
- if ((double)((f - 0.5) / s) >= x) f -= 1;
- x = f;
+ if ((double)((f - 0.5) / s) >= x) f -= 1;
+ x = f;
}
else {
- if ((double)((f + 0.5) / s) <= x) f += 1;
- x = f;
+ if ((double)((f + 0.5) / s) <= x) f += 1;
+ x = f;
}
return x;
}
@@ -147,26 +147,26 @@ round_half_even(double x, double s)
double f, d, xs = x * s;
if (x > 0.0) {
- f = floor(xs);
- d = xs - f;
- if (d > 0.5)
- d = 1.0;
- else if (d == 0.5 || ((double)((f + 0.5) / s) <= x))
- d = fmod(f, 2.0);
- else
- d = 0.0;
- x = f + d;
+ f = floor(xs);
+ d = xs - f;
+ if (d > 0.5)
+ d = 1.0;
+ else if (d == 0.5 || ((double)((f + 0.5) / s) <= x))
+ d = fmod(f, 2.0);
+ else
+ d = 0.0;
+ x = f + d;
}
else if (x < 0.0) {
- f = ceil(xs);
- d = f - xs;
- if (d > 0.5)
- d = 1.0;
- else if (d == 0.5 || ((double)((f - 0.5) / s) >= x))
- d = fmod(-f, 2.0);
- else
- d = 0.0;
- x = f - d;
+ f = ceil(xs);
+ d = f - xs;
+ if (d > 0.5)
+ d = 1.0;
+ else if (d == 0.5 || ((double)((f - 0.5) / s) >= x))
+ d = fmod(-f, 2.0);
+ else
+ d = 0.0;
+ x = f - d;
}
return x;
}
@@ -211,36 +211,36 @@ rb_num_get_rounding_option(VALUE opts)
const char *s;
if (!NIL_P(opts)) {
- if (!round_kwds[0]) {
- round_kwds[0] = rb_intern_const("half");
- }
- if (!rb_get_kwargs(opts, round_kwds, 0, 1, &rounding)) goto noopt;
- if (SYMBOL_P(rounding)) {
- str = rb_sym2str(rounding);
- }
- else if (NIL_P(rounding)) {
- goto noopt;
- }
- else if (!RB_TYPE_P(str = rounding, T_STRING)) {
- str = rb_check_string_type(rounding);
- if (NIL_P(str)) goto invalid;
- }
+ if (!round_kwds[0]) {
+ round_kwds[0] = rb_intern_const("half");
+ }
+ if (!rb_get_kwargs(opts, round_kwds, 0, 1, &rounding)) goto noopt;
+ if (SYMBOL_P(rounding)) {
+ str = rb_sym2str(rounding);
+ }
+ else if (NIL_P(rounding)) {
+ goto noopt;
+ }
+ else if (!RB_TYPE_P(str = rounding, T_STRING)) {
+ str = rb_check_string_type(rounding);
+ if (NIL_P(str)) goto invalid;
+ }
rb_must_asciicompat(str);
- s = RSTRING_PTR(str);
- switch (RSTRING_LEN(str)) {
- case 2:
- if (rb_memcicmp(s, "up", 2) == 0)
- return RUBY_NUM_ROUND_HALF_UP;
- break;
- case 4:
- if (rb_memcicmp(s, "even", 4) == 0)
- return RUBY_NUM_ROUND_HALF_EVEN;
- if (strncasecmp(s, "down", 4) == 0)
- return RUBY_NUM_ROUND_HALF_DOWN;
- break;
- }
+ s = RSTRING_PTR(str);
+ switch (RSTRING_LEN(str)) {
+ case 2:
+ if (rb_memcicmp(s, "up", 2) == 0)
+ return RUBY_NUM_ROUND_HALF_UP;
+ break;
+ case 4:
+ if (rb_memcicmp(s, "even", 4) == 0)
+ return RUBY_NUM_ROUND_HALF_EVEN;
+ if (strncasecmp(s, "down", 4) == 0)
+ return RUBY_NUM_ROUND_HALF_DOWN;
+ break;
+ }
invalid:
- rb_raise(rb_eArgError, "invalid rounding mode: % "PRIsVALUE, rounding);
+ rb_raise(rb_eArgError, "invalid rounding mode: % "PRIsVALUE, rounding);
}
noopt:
return RUBY_NUM_ROUND_DEFAULT;
@@ -254,25 +254,25 @@ rb_num_to_uint(VALUE val, unsigned int *ret)
#define NUMERR_NEGATIVE 2
#define NUMERR_TOOLARGE 3
if (FIXNUM_P(val)) {
- long v = FIX2LONG(val);
+ long v = FIX2LONG(val);
#if SIZEOF_INT < SIZEOF_LONG
- if (v > (long)UINT_MAX) return NUMERR_TOOLARGE;
+ if (v > (long)UINT_MAX) return NUMERR_TOOLARGE;
#endif
- if (v < 0) return NUMERR_NEGATIVE;
- *ret = (unsigned int)v;
- return 0;
+ if (v < 0) return NUMERR_NEGATIVE;
+ *ret = (unsigned int)v;
+ return 0;
}
if (RB_BIGNUM_TYPE_P(val)) {
- if (BIGNUM_NEGATIVE_P(val)) return NUMERR_NEGATIVE;
+ if (BIGNUM_NEGATIVE_P(val)) return NUMERR_NEGATIVE;
#if SIZEOF_INT < SIZEOF_LONG
- /* long is 64bit */
- return NUMERR_TOOLARGE;
+ /* long is 64bit */
+ return NUMERR_TOOLARGE;
#else
- /* long is 32bit */
- if (rb_absint_size(val, NULL) > sizeof(int)) return NUMERR_TOOLARGE;
- *ret = (unsigned int)rb_big2ulong((VALUE)val);
- return 0;
+ /* long is 32bit */
+ if (rb_absint_size(val, NULL) > sizeof(int)) return NUMERR_TOOLARGE;
+ *ret = (unsigned int)rb_big2ulong((VALUE)val);
+ return 0;
#endif
}
return NUMERR_TYPE;
@@ -284,10 +284,10 @@ static inline int
int_pos_p(VALUE num)
{
if (FIXNUM_P(num)) {
- return FIXNUM_POSITIVE_P(num);
+ return FIXNUM_POSITIVE_P(num);
}
else if (RB_BIGNUM_TYPE_P(num)) {
- return BIGNUM_POSITIVE_P(num);
+ return BIGNUM_POSITIVE_P(num);
}
rb_raise(rb_eTypeError, "not an Integer");
}
@@ -296,10 +296,10 @@ static inline int
int_neg_p(VALUE num)
{
if (FIXNUM_P(num)) {
- return FIXNUM_NEGATIVE_P(num);
+ return FIXNUM_NEGATIVE_P(num);
}
else if (RB_BIGNUM_TYPE_P(num)) {
- return BIGNUM_NEGATIVE_P(num);
+ return BIGNUM_NEGATIVE_P(num);
}
rb_raise(rb_eTypeError, "not an Integer");
}
@@ -327,19 +327,19 @@ num_funcall_op_0(VALUE x, VALUE arg, int recursive)
{
ID func = (ID)arg;
if (recursive) {
- const char *name = rb_id2name(func);
- if (ISALNUM(name[0])) {
- rb_name_error(func, "%"PRIsVALUE".%"PRIsVALUE,
- x, ID2SYM(func));
- }
- else if (name[0] && name[1] == '@' && !name[2]) {
- rb_name_error(func, "%c%"PRIsVALUE,
- name[0], x);
- }
- else {
- rb_name_error(func, "%"PRIsVALUE"%"PRIsVALUE,
- ID2SYM(func), x);
- }
+ const char *name = rb_id2name(func);
+ if (ISALNUM(name[0])) {
+ rb_name_error(func, "%"PRIsVALUE".%"PRIsVALUE,
+ x, ID2SYM(func));
+ }
+ else if (name[0] && name[1] == '@' && !name[2]) {
+ rb_name_error(func, "%c%"PRIsVALUE,
+ name[0], x);
+ }
+ else {
+ rb_name_error(func, "%"PRIsVALUE"%"PRIsVALUE,
+ ID2SYM(func), x);
+ }
}
return rb_funcallv(x, func, 0, 0);
}
@@ -357,12 +357,12 @@ num_funcall_op_1_recursion(VALUE x, ID func, VALUE y)
{
const char *name = rb_id2name(func);
if (ISALNUM(name[0])) {
- rb_name_error(func, "%"PRIsVALUE".%"PRIsVALUE"(%"PRIsVALUE")",
- x, ID2SYM(func), y);
+ rb_name_error(func, "%"PRIsVALUE".%"PRIsVALUE"(%"PRIsVALUE")",
+ x, ID2SYM(func), y);
}
else {
- rb_name_error(func, "%"PRIsVALUE"%"PRIsVALUE"%"PRIsVALUE,
- x, ID2SYM(func), y);
+ rb_name_error(func, "%"PRIsVALUE"%"PRIsVALUE"%"PRIsVALUE,
+ x, ID2SYM(func), y);
}
}
@@ -372,7 +372,7 @@ num_funcall_op_1(VALUE y, VALUE arg, int recursive)
ID func = (ID)((VALUE *)arg)[0];
VALUE x = ((VALUE *)arg)[1];
if (recursive) {
- num_funcall_op_1_recursion(x, func, y);
+ num_funcall_op_1_recursion(x, func, y);
}
return rb_funcall(x, func, 1, y);
}
@@ -425,7 +425,7 @@ static VALUE
num_coerce(VALUE x, VALUE y)
{
if (CLASS_OF(x) == CLASS_OF(y))
- return rb_assoc_new(y, x);
+ return rb_assoc_new(y, x);
x = rb_Float(x);
y = rb_Float(y);
return rb_assoc_new(y, x);
@@ -436,30 +436,30 @@ static void
coerce_failed(VALUE x, VALUE y)
{
if (SPECIAL_CONST_P(y) || SYMBOL_P(y) || RB_FLOAT_TYPE_P(y)) {
- y = rb_inspect(y);
+ y = rb_inspect(y);
}
else {
- y = rb_obj_class(y);
+ y = rb_obj_class(y);
}
rb_raise(rb_eTypeError, "%"PRIsVALUE" can't be coerced into %"PRIsVALUE,
- y, rb_obj_class(x));
+ y, rb_obj_class(x));
}
static int
do_coerce(VALUE *x, VALUE *y, int err)
{
VALUE ary = rb_check_funcall(*y, id_coerce, 1, x);
- if (ary == Qundef) {
- if (err) {
- coerce_failed(*x, *y);
- }
- return FALSE;
+ if (UNDEF_P(ary)) {
+ if (err) {
+ coerce_failed(*x, *y);
+ }
+ return FALSE;
}
if (!err && NIL_P(ary)) {
- return FALSE;
+ return FALSE;
}
if (!RB_TYPE_P(ary, T_ARRAY) || RARRAY_LEN(ary) != 2) {
- rb_raise(rb_eTypeError, "coerce must return [x, y]");
+ rb_raise(rb_eTypeError, "coerce must return [x, y]");
}
*x = RARRAY_AREF(ary, 0);
@@ -478,7 +478,7 @@ VALUE
rb_num_coerce_cmp(VALUE x, VALUE y, ID func)
{
if (do_coerce(&x, &y, FALSE))
- return rb_funcall(x, func, 1, y);
+ return rb_funcall(x, func, 1, y);
return Qnil;
}
@@ -495,8 +495,8 @@ rb_num_coerce_relop(VALUE x, VALUE y, ID func)
VALUE x0 = x, y0 = y;
if (!do_coerce(&x, &y, FALSE)) {
- rb_cmperr(x0, y0);
- UNREACHABLE_RETURN(Qnil);
+ rb_cmperr(x0, y0);
+ UNREACHABLE_RETURN(Qnil);
}
return ensure_cmp(rb_funcall(x, func, 1, y), x0, y0);
}
@@ -518,9 +518,9 @@ num_sadded(VALUE x, VALUE name)
/* ruby_frame = ruby_frame->prev; */ /* pop frame for "singleton_method_added" */
rb_remove_method_id(rb_singleton_class(x), mid);
rb_raise(rb_eTypeError,
- "can't define singleton method \"%"PRIsVALUE"\" for %"PRIsVALUE,
- rb_id2str(mid),
- rb_obj_class(x));
+ "can't define singleton method \"%"PRIsVALUE"\" for %"PRIsVALUE,
+ rb_id2str(mid),
+ rb_obj_class(x));
UNREACHABLE_RETURN(Qnil);
}
@@ -697,7 +697,7 @@ num_modulo(VALUE x, VALUE y)
{
VALUE q = num_funcall1(x, id_div, y);
return rb_funcall(x, '-', 1,
- rb_funcall(y, '*', 1, q));
+ rb_funcall(y, '*', 1, q));
}
/*
@@ -737,16 +737,16 @@ num_remainder(VALUE x, VALUE y)
VALUE z = num_funcall1(x, '%', y);
if ((!rb_equal(z, INT2FIX(0))) &&
- ((rb_num_negative_int_p(x) &&
- rb_num_positive_int_p(y)) ||
- (rb_num_positive_int_p(x) &&
- rb_num_negative_int_p(y)))) {
+ ((rb_num_negative_int_p(x) &&
+ rb_num_positive_int_p(y)) ||
+ (rb_num_positive_int_p(x) &&
+ rb_num_negative_int_p(y)))) {
if (RB_FLOAT_TYPE_P(y)) {
if (isinf(RFLOAT_VALUE(y))) {
return x;
}
}
- return rb_funcall(z, '-', 1, y);
+ return rb_funcall(z, '-', 1, y);
}
return z;
}
@@ -803,7 +803,7 @@ static VALUE
num_abs(VALUE num)
{
if (rb_num_negative_int_p(num)) {
- return num_funcall0(num, idUMinus);
+ return num_funcall0(num, idUMinus);
}
return num;
}
@@ -863,7 +863,7 @@ static VALUE
num_nonzero_p(VALUE num)
{
if (RTEST(num_funcall0(num, rb_intern("zero?")))) {
- return Qnil;
+ return Qnil;
}
return num;
}
@@ -907,12 +907,12 @@ num_positive_p(VALUE num)
const ID mid = '>';
if (FIXNUM_P(num)) {
- if (method_basic_p(rb_cInteger))
- return RBOOL((SIGNED_VALUE)num > (SIGNED_VALUE)INT2FIX(0));
+ if (method_basic_p(rb_cInteger))
+ return RBOOL((SIGNED_VALUE)num > (SIGNED_VALUE)INT2FIX(0));
}
else if (RB_BIGNUM_TYPE_P(num)) {
- if (method_basic_p(rb_cInteger))
- return RBOOL(BIGNUM_POSITIVE_P(num) && !rb_bigzero_p(num));
+ if (method_basic_p(rb_cInteger))
+ return RBOOL(BIGNUM_POSITIVE_P(num) && !rb_bigzero_p(num));
}
return rb_num_compare_with_zero(num, mid);
}
@@ -1055,19 +1055,19 @@ flo_to_s(VALUE flt)
{
enum {decimal_mant = DBL_MANT_DIG-DBL_DIG};
enum {float_dig = DBL_DIG+1};
- char buf[float_dig + (decimal_mant + CHAR_BIT - 1) / CHAR_BIT + 10];
+ char buf[float_dig + roomof(decimal_mant, CHAR_BIT) + 10];
double value = RFLOAT_VALUE(flt);
VALUE s;
char *p, *e;
int sign, decpt, digs;
if (isinf(value)) {
- static const char minf[] = "-Infinity";
- const int pos = (value > 0); /* skip "-" */
- return rb_usascii_str_new(minf+pos, strlen(minf)-pos);
+ static const char minf[] = "-Infinity";
+ const int pos = (value > 0); /* skip "-" */
+ return rb_usascii_str_new(minf+pos, strlen(minf)-pos);
}
else if (isnan(value))
- return rb_usascii_str_new2("NaN");
+ return rb_usascii_str_new2("NaN");
p = ruby_dtoa(value, 0, 0, &decpt, &sign, &e);
s = sign ? rb_usascii_str_new_cstr("-") : rb_usascii_str_new(0, 0);
@@ -1075,35 +1075,35 @@ flo_to_s(VALUE flt)
memcpy(buf, p, digs);
xfree(p);
if (decpt > 0) {
- if (decpt < digs) {
- memmove(buf + decpt + 1, buf + decpt, digs - decpt);
- buf[decpt] = '.';
- rb_str_cat(s, buf, digs + 1);
- }
- else if (decpt <= DBL_DIG) {
- long len;
- char *ptr;
- rb_str_cat(s, buf, digs);
- rb_str_resize(s, (len = RSTRING_LEN(s)) + decpt - digs + 2);
- ptr = RSTRING_PTR(s) + len;
- if (decpt > digs) {
- memset(ptr, '0', decpt - digs);
- ptr += decpt - digs;
- }
- memcpy(ptr, ".0", 2);
- }
- else {
- goto exp;
- }
+ if (decpt < digs) {
+ memmove(buf + decpt + 1, buf + decpt, digs - decpt);
+ buf[decpt] = '.';
+ rb_str_cat(s, buf, digs + 1);
+ }
+ else if (decpt <= DBL_DIG) {
+ long len;
+ char *ptr;
+ rb_str_cat(s, buf, digs);
+ rb_str_resize(s, (len = RSTRING_LEN(s)) + decpt - digs + 2);
+ ptr = RSTRING_PTR(s) + len;
+ if (decpt > digs) {
+ memset(ptr, '0', decpt - digs);
+ ptr += decpt - digs;
+ }
+ memcpy(ptr, ".0", 2);
+ }
+ else {
+ goto exp;
+ }
}
else if (decpt > -4) {
- long len;
- char *ptr;
- rb_str_cat(s, "0.", 2);
- rb_str_resize(s, (len = RSTRING_LEN(s)) - decpt + digs);
- ptr = RSTRING_PTR(s);
- memset(ptr += len, '0', -decpt);
- memcpy(ptr -= decpt, buf, digs);
+ long len;
+ char *ptr;
+ rb_str_cat(s, "0.", 2);
+ rb_str_resize(s, (len = RSTRING_LEN(s)) - decpt + digs);
+ ptr = RSTRING_PTR(s);
+ memset(ptr += len, '0', -decpt);
+ memcpy(ptr -= decpt, buf, digs);
}
else {
goto exp;
@@ -1171,16 +1171,16 @@ VALUE
rb_float_plus(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- return DBL2NUM(RFLOAT_VALUE(x) + (double)FIX2LONG(y));
+ return DBL2NUM(RFLOAT_VALUE(x) + (double)FIX2LONG(y));
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return DBL2NUM(RFLOAT_VALUE(x) + rb_big2dbl(y));
+ return DBL2NUM(RFLOAT_VALUE(x) + rb_big2dbl(y));
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM(RFLOAT_VALUE(x) + RFLOAT_VALUE(y));
+ return DBL2NUM(RFLOAT_VALUE(x) + RFLOAT_VALUE(y));
}
else {
- return rb_num_coerce_bin(x, y, '+');
+ return rb_num_coerce_bin(x, y, '+');
}
}
@@ -1202,16 +1202,16 @@ VALUE
rb_float_minus(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- return DBL2NUM(RFLOAT_VALUE(x) - (double)FIX2LONG(y));
+ return DBL2NUM(RFLOAT_VALUE(x) - (double)FIX2LONG(y));
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return DBL2NUM(RFLOAT_VALUE(x) - rb_big2dbl(y));
+ return DBL2NUM(RFLOAT_VALUE(x) - rb_big2dbl(y));
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM(RFLOAT_VALUE(x) - RFLOAT_VALUE(y));
+ return DBL2NUM(RFLOAT_VALUE(x) - RFLOAT_VALUE(y));
}
else {
- return rb_num_coerce_bin(x, y, '-');
+ return rb_num_coerce_bin(x, y, '-');
}
}
@@ -1232,16 +1232,16 @@ VALUE
rb_float_mul(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- return DBL2NUM(RFLOAT_VALUE(x) * (double)FIX2LONG(y));
+ return DBL2NUM(RFLOAT_VALUE(x) * (double)FIX2LONG(y));
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return DBL2NUM(RFLOAT_VALUE(x) * rb_big2dbl(y));
+ return DBL2NUM(RFLOAT_VALUE(x) * rb_big2dbl(y));
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM(RFLOAT_VALUE(x) * RFLOAT_VALUE(y));
+ return DBL2NUM(RFLOAT_VALUE(x) * RFLOAT_VALUE(y));
}
else {
- return rb_num_coerce_bin(x, y, '*');
+ return rb_num_coerce_bin(x, y, '*');
}
}
@@ -1300,7 +1300,7 @@ rb_float_div(VALUE x, VALUE y)
den = RFLOAT_VALUE(y);
}
else {
- return rb_num_coerce_bin(x, y, '/');
+ return rb_num_coerce_bin(x, y, '/');
}
ret = double_div_double(num, den);
@@ -1335,28 +1335,28 @@ flodivmod(double x, double y, double *divp, double *modp)
double div, mod;
if (isnan(y)) {
- /* y is NaN so all results are NaN */
- if (modp) *modp = y;
- if (divp) *divp = y;
- return;
+ /* y is NaN so all results are NaN */
+ if (modp) *modp = y;
+ if (divp) *divp = y;
+ return;
}
if (y == 0.0) rb_num_zerodiv();
if ((x == 0.0) || (isinf(y) && !isinf(x)))
mod = x;
else {
#ifdef HAVE_FMOD
- mod = fmod(x, y);
+ mod = fmod(x, y);
#else
- double z;
+ double z;
- modf(x/y, &z);
- mod = x - z * y;
+ modf(x/y, &z);
+ mod = x - z * y;
#endif
}
if (isinf(x) && !isinf(y))
- div = x;
+ div = x;
else {
- div = (x - mod) / y;
+ div = (x - mod) / y;
if (modp && divp) div = round(div);
}
if (y*mod < 0) {
@@ -1417,16 +1417,16 @@ flo_mod(VALUE x, VALUE y)
double fy;
if (FIXNUM_P(y)) {
- fy = (double)FIX2LONG(y);
+ fy = (double)FIX2LONG(y);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- fy = rb_big2dbl(y);
+ fy = rb_big2dbl(y);
}
else if (RB_FLOAT_TYPE_P(y)) {
- fy = RFLOAT_VALUE(y);
+ fy = RFLOAT_VALUE(y);
}
else {
- return rb_num_coerce_bin(x, y, '%');
+ return rb_num_coerce_bin(x, y, '%');
}
return DBL2NUM(ruby_float_mod(RFLOAT_VALUE(x), fy));
}
@@ -1435,7 +1435,7 @@ static VALUE
dbl2ival(double d)
{
if (FIXABLE(d)) {
- return LONG2FIX((long)d);
+ return LONG2FIX((long)d);
}
return rb_dbl2big(d);
}
@@ -1473,16 +1473,16 @@ flo_divmod(VALUE x, VALUE y)
volatile VALUE a, b;
if (FIXNUM_P(y)) {
- fy = (double)FIX2LONG(y);
+ fy = (double)FIX2LONG(y);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- fy = rb_big2dbl(y);
+ fy = rb_big2dbl(y);
}
else if (RB_FLOAT_TYPE_P(y)) {
- fy = RFLOAT_VALUE(y);
+ fy = RFLOAT_VALUE(y);
}
else {
- return rb_num_coerce_bin(x, y, id_divmod);
+ return rb_num_coerce_bin(x, y, id_divmod);
}
flodivmod(RFLOAT_VALUE(x), fy, &div, &mod);
a = dbl2ival(div);
@@ -1510,25 +1510,25 @@ rb_float_pow(VALUE x, VALUE y)
{
double dx, dy;
if (y == INT2FIX(2)) {
- dx = RFLOAT_VALUE(x);
+ dx = RFLOAT_VALUE(x);
return DBL2NUM(dx * dx);
}
else if (FIXNUM_P(y)) {
- dx = RFLOAT_VALUE(x);
- dy = (double)FIX2LONG(y);
+ dx = RFLOAT_VALUE(x);
+ dy = (double)FIX2LONG(y);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- dx = RFLOAT_VALUE(x);
- dy = rb_big2dbl(y);
+ dx = RFLOAT_VALUE(x);
+ dy = rb_big2dbl(y);
}
else if (RB_FLOAT_TYPE_P(y)) {
- dx = RFLOAT_VALUE(x);
- dy = RFLOAT_VALUE(y);
- if (dx < 0 && dy != round(dy))
+ dx = RFLOAT_VALUE(x);
+ dy = RFLOAT_VALUE(y);
+ if (dx < 0 && dy != round(dy))
return rb_dbl_complex_new_polar_pi(pow(-dx, dy), dy);
}
else {
- return rb_num_coerce_bin(x, y, idPow);
+ return rb_num_coerce_bin(x, y, idPow);
}
return DBL2NUM(pow(dx, dy));
}
@@ -1560,7 +1560,7 @@ num_eql(VALUE x, VALUE y)
if (TYPE(x) != TYPE(y)) return Qfalse;
if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_eql(x, y);
+ return rb_big_eql(x, y);
}
return rb_equal(x, y);
@@ -1618,13 +1618,13 @@ rb_float_equal(VALUE x, VALUE y)
return rb_integer_float_eq(y, x);
}
else if (RB_FLOAT_TYPE_P(y)) {
- b = RFLOAT_VALUE(y);
+ b = RFLOAT_VALUE(y);
#if MSC_VERSION_BEFORE(1300)
- if (isnan(b)) return Qfalse;
+ if (isnan(b)) return Qfalse;
#endif
}
else {
- return num_equal(x, y);
+ return num_equal(x, y);
}
a = RFLOAT_VALUE(x);
#if MSC_VERSION_BEFORE(1300)
@@ -1710,19 +1710,19 @@ flo_cmp(VALUE x, VALUE y)
return rel;
}
else if (RB_FLOAT_TYPE_P(y)) {
- b = RFLOAT_VALUE(y);
+ b = RFLOAT_VALUE(y);
}
else {
- if (isinf(a) && (i = rb_check_funcall(y, rb_intern("infinite?"), 0, 0)) != Qundef) {
- if (RTEST(i)) {
- int j = rb_cmpint(i, x, y);
- j = (a > 0.0) ? (j > 0 ? 0 : +1) : (j < 0 ? 0 : -1);
- return INT2FIX(j);
- }
- if (a > 0.0) return INT2FIX(1);
- return INT2FIX(-1);
- }
- return rb_num_coerce_cmp(x, y, id_cmp);
+ if (isinf(a) && !UNDEF_P(i = rb_check_funcall(y, rb_intern("infinite?"), 0, 0))) {
+ if (RTEST(i)) {
+ int j = rb_cmpint(i, x, y);
+ j = (a > 0.0) ? (j > 0 ? 0 : +1) : (j < 0 ? 0 : -1);
+ return INT2FIX(j);
+ }
+ if (a > 0.0) return INT2FIX(1);
+ return INT2FIX(-1);
+ }
+ return rb_num_coerce_cmp(x, y, id_cmp);
}
return rb_dbl_cmp(a, b);
}
@@ -1761,13 +1761,13 @@ rb_float_gt(VALUE x, VALUE y)
return Qfalse;
}
else if (RB_FLOAT_TYPE_P(y)) {
- b = RFLOAT_VALUE(y);
+ b = RFLOAT_VALUE(y);
#if MSC_VERSION_BEFORE(1300)
- if (isnan(b)) return Qfalse;
+ if (isnan(b)) return Qfalse;
#endif
}
else {
- return rb_num_coerce_relop(x, y, '>');
+ return rb_num_coerce_relop(x, y, '>');
}
#if MSC_VERSION_BEFORE(1300)
if (isnan(a)) return Qfalse;
@@ -1804,13 +1804,13 @@ flo_ge(VALUE x, VALUE y)
return Qfalse;
}
else if (RB_FLOAT_TYPE_P(y)) {
- b = RFLOAT_VALUE(y);
+ b = RFLOAT_VALUE(y);
#if MSC_VERSION_BEFORE(1300)
- if (isnan(b)) return Qfalse;
+ if (isnan(b)) return Qfalse;
#endif
}
else {
- return rb_num_coerce_relop(x, y, idGE);
+ return rb_num_coerce_relop(x, y, idGE);
}
#if MSC_VERSION_BEFORE(1300)
if (isnan(a)) return Qfalse;
@@ -1846,13 +1846,13 @@ flo_lt(VALUE x, VALUE y)
return Qfalse;
}
else if (RB_FLOAT_TYPE_P(y)) {
- b = RFLOAT_VALUE(y);
+ b = RFLOAT_VALUE(y);
#if MSC_VERSION_BEFORE(1300)
- if (isnan(b)) return Qfalse;
+ if (isnan(b)) return Qfalse;
#endif
}
else {
- return rb_num_coerce_relop(x, y, '<');
+ return rb_num_coerce_relop(x, y, '<');
}
#if MSC_VERSION_BEFORE(1300)
if (isnan(a)) return Qfalse;
@@ -1889,13 +1889,13 @@ flo_le(VALUE x, VALUE y)
return Qfalse;
}
else if (RB_FLOAT_TYPE_P(y)) {
- b = RFLOAT_VALUE(y);
+ b = RFLOAT_VALUE(y);
#if MSC_VERSION_BEFORE(1300)
- if (isnan(b)) return Qfalse;
+ if (isnan(b)) return Qfalse;
#endif
}
else {
- return rb_num_coerce_relop(x, y, idLE);
+ return rb_num_coerce_relop(x, y, idLE);
}
#if MSC_VERSION_BEFORE(1300)
if (isnan(a)) return Qfalse;
@@ -1925,10 +1925,10 @@ MJIT_FUNC_EXPORTED VALUE
rb_float_eql(VALUE x, VALUE y)
{
if (RB_FLOAT_TYPE_P(y)) {
- double a = RFLOAT_VALUE(x);
- double b = RFLOAT_VALUE(y);
+ double a = RFLOAT_VALUE(x);
+ double b = RFLOAT_VALUE(y);
#if MSC_VERSION_BEFORE(1300)
- if (isnan(a) || isnan(b)) return Qfalse;
+ if (isnan(a) || isnan(b)) return Qfalse;
#endif
return RBOOL(a == b);
}
@@ -1993,7 +1993,7 @@ rb_flo_is_infinite_p(VALUE num)
double value = RFLOAT_VALUE(num);
if (isinf(value)) {
- return INT2FIX( value < 0 ? -1 : 1 );
+ return INT2FIX( value < 0 ? -1 : 1 );
}
return Qnil;
@@ -2131,16 +2131,16 @@ rb_float_floor(VALUE num, int ndigits)
double number;
number = RFLOAT_VALUE(num);
if (number == 0.0) {
- return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
+ return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
}
if (ndigits > 0) {
- int binexp;
+ int binexp;
double f, mul, res;
- frexp(number, &binexp);
- if (float_round_overflow(ndigits, binexp)) return num;
- if (number > 0.0 && float_round_underflow(ndigits, binexp))
- return DBL2NUM(0.0);
- f = pow(10, ndigits);
+ frexp(number, &binexp);
+ if (float_round_overflow(ndigits, binexp)) return num;
+ if (number > 0.0 && float_round_underflow(ndigits, binexp))
+ return DBL2NUM(0.0);
+ f = pow(10, ndigits);
mul = floor(number * f);
res = (mul + 1) / f;
if (res > number)
@@ -2148,9 +2148,9 @@ rb_float_floor(VALUE num, int ndigits)
return DBL2NUM(res);
}
else {
- num = dbl2ival(floor(number));
- if (ndigits < 0) num = rb_int_floor(num, ndigits);
- return num;
+ num = dbl2ival(floor(number));
+ if (ndigits < 0) num = rb_int_floor(num, ndigits);
+ return num;
}
}
@@ -2158,7 +2158,7 @@ static int
flo_ndigits(int argc, VALUE *argv)
{
if (rb_check_arity(argc, 0, 1)) {
- return NUM2INT(argv[0]);
+ return NUM2INT(argv[0]);
}
return 0;
}
@@ -2256,22 +2256,22 @@ rb_float_ceil(VALUE num, int ndigits)
number = RFLOAT_VALUE(num);
if (number == 0.0) {
- return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
+ return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
}
if (ndigits > 0) {
- int binexp;
- frexp(number, &binexp);
- if (float_round_overflow(ndigits, binexp)) return num;
- if (number < 0.0 && float_round_underflow(ndigits, binexp))
- return DBL2NUM(0.0);
- f = pow(10, ndigits);
- f = ceil(number * f) / f;
- return DBL2NUM(f);
+ int binexp;
+ frexp(number, &binexp);
+ if (float_round_overflow(ndigits, binexp)) return num;
+ if (number < 0.0 && float_round_underflow(ndigits, binexp))
+ return DBL2NUM(0.0);
+ f = pow(10, ndigits);
+ f = ceil(number * f) / f;
+ return DBL2NUM(f);
}
else {
- num = dbl2ival(ceil(number));
- if (ndigits < 0) num = rb_int_ceil(num, ndigits);
- return num;
+ num = dbl2ival(ceil(number));
+ if (ndigits < 0) num = rb_int_ceil(num, ndigits);
+ return num;
}
}
@@ -2282,13 +2282,13 @@ int_round_zero_p(VALUE num, int ndigits)
/* If 10**N / 2 > num, then return 0 */
/* We have log_256(10) > 0.415241 and log_256(1/2) = -0.125, so */
if (FIXNUM_P(num)) {
- bytes = sizeof(long);
+ bytes = sizeof(long);
}
else if (RB_BIGNUM_TYPE_P(num)) {
- bytes = rb_big_size(num);
+ bytes = rb_big_size(num);
}
else {
- bytes = NUM2LONG(rb_funcall(num, idSize, 0));
+ bytes = NUM2LONG(rb_funcall(num, idSize, 0));
}
return (-0.415241 * ndigits - 0.125 > bytes);
}
@@ -2298,7 +2298,7 @@ int_round_half_even(SIGNED_VALUE x, SIGNED_VALUE y)
{
SIGNED_VALUE z = +(x + y / 2) / y;
if ((z * y - x) * 2 == y) {
- z &= ~1;
+ z &= ~1;
}
return z * y;
}
@@ -2342,29 +2342,29 @@ rb_int_round(VALUE num, int ndigits, enum ruby_num_rounding_mode mode)
VALUE n, f, h, r;
if (int_round_zero_p(num, ndigits)) {
- return INT2FIX(0);
+ return INT2FIX(0);
}
f = int_pow(10, -ndigits);
if (FIXNUM_P(num) && FIXNUM_P(f)) {
- SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
- int neg = x < 0;
- if (neg) x = -x;
- x = ROUND_CALL(mode, int_round, (x, y));
- if (neg) x = -x;
- return LONG2NUM(x);
+ SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
+ int neg = x < 0;
+ if (neg) x = -x;
+ x = ROUND_CALL(mode, int_round, (x, y));
+ if (neg) x = -x;
+ return LONG2NUM(x);
}
if (RB_FLOAT_TYPE_P(f)) {
- /* then int_pow overflow */
- return INT2FIX(0);
+ /* then int_pow overflow */
+ return INT2FIX(0);
}
h = rb_int_idiv(f, INT2FIX(2));
r = rb_int_modulo(num, f);
n = rb_int_minus(num, r);
r = rb_int_cmp(r, h);
if (FIXNUM_POSITIVE_P(r) ||
- (FIXNUM_ZERO_P(r) && ROUND_CALL(mode, int_half_p, (num, n, f)))) {
- n = rb_int_plus(n, f);
+ (FIXNUM_ZERO_P(r) && ROUND_CALL(mode, int_half_p, (num, n, f)))) {
+ n = rb_int_plus(n, f);
}
return n;
}
@@ -2372,48 +2372,47 @@ rb_int_round(VALUE num, int ndigits, enum ruby_num_rounding_mode mode)
static VALUE
rb_int_floor(VALUE num, int ndigits)
{
- VALUE f;
-
- if (int_round_zero_p(num, ndigits))
- return INT2FIX(0);
- f = int_pow(10, -ndigits);
+ VALUE f = int_pow(10, -ndigits);
if (FIXNUM_P(num) && FIXNUM_P(f)) {
- SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
- int neg = x < 0;
- if (neg) x = -x + y - 1;
- x = x / y * y;
- if (neg) x = -x;
- return LONG2NUM(x);
+ SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
+ int neg = x < 0;
+ if (neg) x = -x + y - 1;
+ x = x / y * y;
+ if (neg) x = -x;
+ return LONG2NUM(x);
}
- if (RB_FLOAT_TYPE_P(f)) {
- /* then int_pow overflow */
- return INT2FIX(0);
+ else {
+ bool neg = int_neg_p(num);
+ if (neg) num = rb_int_minus(rb_int_plus(rb_int_uminus(num), f), INT2FIX(1));
+ num = rb_int_mul(rb_int_div(num, f), f);
+ if (neg) num = rb_int_uminus(num);
+ return num;
}
- return rb_int_minus(num, rb_int_modulo(num, f));
}
static VALUE
rb_int_ceil(VALUE num, int ndigits)
{
- VALUE f;
-
- if (int_round_zero_p(num, ndigits))
- return INT2FIX(0);
- f = int_pow(10, -ndigits);
+ VALUE f = int_pow(10, -ndigits);
if (FIXNUM_P(num) && FIXNUM_P(f)) {
- SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
- int neg = x < 0;
- if (neg) x = -x;
- else x += y - 1;
- x = (x / y) * y;
- if (neg) x = -x;
- return LONG2NUM(x);
+ SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
+ int neg = x < 0;
+ if (neg) x = -x;
+ else x += y - 1;
+ x = (x / y) * y;
+ if (neg) x = -x;
+ return LONG2NUM(x);
}
- if (RB_FLOAT_TYPE_P(f)) {
- /* then int_pow overflow */
- return INT2FIX(0);
+ else {
+ bool neg = int_neg_p(num);
+ if (neg)
+ num = rb_int_uminus(num);
+ else
+ num = rb_int_plus(num, rb_int_minus(f, INT2FIX(1)));
+ num = rb_int_mul(rb_int_div(num, f), f);
+ if (neg) num = rb_int_uminus(num);
+ return num;
}
- return rb_int_plus(num, rb_int_minus(f, rb_int_modulo(num, f)));
}
VALUE
@@ -2423,26 +2422,26 @@ rb_int_truncate(VALUE num, int ndigits)
VALUE m;
if (int_round_zero_p(num, ndigits))
- return INT2FIX(0);
+ return INT2FIX(0);
f = int_pow(10, -ndigits);
if (FIXNUM_P(num) && FIXNUM_P(f)) {
- SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
- int neg = x < 0;
- if (neg) x = -x;
- x = x / y * y;
- if (neg) x = -x;
- return LONG2NUM(x);
+ SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
+ int neg = x < 0;
+ if (neg) x = -x;
+ x = x / y * y;
+ if (neg) x = -x;
+ return LONG2NUM(x);
}
if (RB_FLOAT_TYPE_P(f)) {
- /* then int_pow overflow */
- return INT2FIX(0);
+ /* then int_pow overflow */
+ return INT2FIX(0);
}
m = rb_int_modulo(num, f);
if (int_neg_p(num)) {
- return rb_int_plus(num, rb_int_minus(f, m));
+ return rb_int_plus(num, rb_int_minus(f, m));
}
else {
- return rb_int_minus(num, m);
+ return rb_int_minus(num, m);
}
}
@@ -2510,32 +2509,32 @@ flo_round(int argc, VALUE *argv, VALUE num)
enum ruby_num_rounding_mode mode;
if (rb_scan_args(argc, argv, "01:", &nd, &opt)) {
- ndigits = NUM2INT(nd);
+ ndigits = NUM2INT(nd);
}
mode = rb_num_get_rounding_option(opt);
number = RFLOAT_VALUE(num);
if (number == 0.0) {
- return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
+ return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
}
if (ndigits < 0) {
- return rb_int_round(flo_to_i(num), ndigits, mode);
+ return rb_int_round(flo_to_i(num), ndigits, mode);
}
if (ndigits == 0) {
- x = ROUND_CALL(mode, round, (number, 1.0));
- return dbl2ival(x);
+ x = ROUND_CALL(mode, round, (number, 1.0));
+ return dbl2ival(x);
}
if (isfinite(number)) {
- int binexp;
- frexp(number, &binexp);
- if (float_round_overflow(ndigits, binexp)) return num;
- if (float_round_underflow(ndigits, binexp)) return DBL2NUM(0);
+ int binexp;
+ frexp(number, &binexp);
+ if (float_round_overflow(ndigits, binexp)) return num;
+ if (float_round_underflow(ndigits, binexp)) return DBL2NUM(0);
if (ndigits > 14) {
/* In this case, pow(10, ndigits) may not be accurate. */
return rb_flo_round_by_rational(argc, argv, num);
}
- f = pow(10, ndigits);
- x = ROUND_CALL(mode, round, (number, f));
- return DBL2NUM(x / f);
+ f = pow(10, ndigits);
+ x = ROUND_CALL(mode, round, (number, f));
+ return DBL2NUM(x / f);
}
return num;
}
@@ -2553,17 +2552,17 @@ float_round_overflow(int ndigits, int binexp)
If ndigits + exp <= 0, the result is 0 or "1e#{exp}", so
if ndigits + exp < 0, the result is 0.
We have:
- 2 ** (binexp-1) <= |number| < 2 ** binexp
- 10 ** ((binexp-1)/log_2(10)) <= |number| < 10 ** (binexp/log_2(10))
- If binexp >= 0, and since log_2(10) = 3.322259:
- 10 ** (binexp/4 - 1) < |number| < 10 ** (binexp/3)
- floor(binexp/4) <= exp <= ceil(binexp/3)
- If binexp <= 0, swap the /4 and the /3
- So if ndigits + floor(binexp/(4 or 3)) >= float_dig, the result is number
- If ndigits + ceil(binexp/(3 or 4)) < 0 the result is 0
+ 2 ** (binexp-1) <= |number| < 2 ** binexp
+ 10 ** ((binexp-1)/log_2(10)) <= |number| < 10 ** (binexp/log_2(10))
+ If binexp >= 0, and since log_2(10) = 3.322259:
+ 10 ** (binexp/4 - 1) < |number| < 10 ** (binexp/3)
+ floor(binexp/4) <= exp <= ceil(binexp/3)
+ If binexp <= 0, swap the /4 and the /3
+ So if ndigits + floor(binexp/(4 or 3)) >= float_dig, the result is number
+ If ndigits + ceil(binexp/(3 or 4)) < 0 the result is 0
*/
if (ndigits >= float_dig - (binexp > 0 ? binexp / 4 : binexp / 3 - 1)) {
- return TRUE;
+ return TRUE;
}
return FALSE;
}
@@ -2572,7 +2571,7 @@ static int
float_round_underflow(int ndigits, int binexp)
{
if (ndigits < - (binexp > 0 ? binexp / 3 + 1 : binexp / 4)) {
- return TRUE;
+ return TRUE;
}
return FALSE;
}
@@ -2644,9 +2643,9 @@ static VALUE
flo_truncate(int argc, VALUE *argv, VALUE num)
{
if (signbit(RFLOAT_VALUE(num)))
- return flo_ceil(argc, argv, num);
+ return flo_ceil(argc, argv, num);
else
- return flo_floor(argc, argv, num);
+ return flo_floor(argc, argv, num);
}
/*
@@ -2727,17 +2726,17 @@ ruby_float_step_size(double beg, double end, double unit, int excl)
return HUGE_VAL;
}
if (isinf(unit)) {
- return unit > 0 ? beg <= end : beg >= end;
+ return unit > 0 ? beg <= end : beg >= end;
}
n= (end - beg)/unit;
err = (fabs(beg) + fabs(end) + fabs(end-beg)) / fabs(unit) * epsilon;
if (err>0.5) err=0.5;
if (excl) {
- if (n<=0) return 0;
- if (n<1)
- n = 0;
- else
- n = floor(n - err);
+ if (n<=0) return 0;
+ if (n<1)
+ n = 0;
+ else
+ n = floor(n - err);
d = +((n + 1) * unit) + beg;
if (beg < end) {
if (d < end)
@@ -2749,8 +2748,8 @@ ruby_float_step_size(double beg, double end, double unit, int excl)
}
}
else {
- if (n<0) return 0;
- n = floor(n + err);
+ if (n<0) return 0;
+ n = floor(n + err);
d = +((n + 1) * unit) + beg;
if (beg < end) {
if (d <= end)
@@ -2769,28 +2768,28 @@ ruby_float_step(VALUE from, VALUE to, VALUE step, int excl, int allow_endless)
{
if (RB_FLOAT_TYPE_P(from) || RB_FLOAT_TYPE_P(to) || RB_FLOAT_TYPE_P(step)) {
double unit = NUM2DBL(step);
- double beg = NUM2DBL(from);
+ double beg = NUM2DBL(from);
double end = (allow_endless && NIL_P(to)) ? (unit < 0 ? -1 : 1)*HUGE_VAL : NUM2DBL(to);
- double n = ruby_float_step_size(beg, end, unit, excl);
- long i;
-
- if (isinf(unit)) {
- /* if unit is infinity, i*unit+beg is NaN */
- if (n) rb_yield(DBL2NUM(beg));
- }
- else if (unit == 0) {
- VALUE val = DBL2NUM(beg);
- for (;;)
- rb_yield(val);
- }
- else {
- for (i=0; i<n; i++) {
- double d = i*unit+beg;
- if (unit >= 0 ? end < d : d < end) d = end;
- rb_yield(DBL2NUM(d));
- }
- }
- return TRUE;
+ double n = ruby_float_step_size(beg, end, unit, excl);
+ long i;
+
+ if (isinf(unit)) {
+ /* if unit is infinity, i*unit+beg is NaN */
+ if (n) rb_yield(DBL2NUM(beg));
+ }
+ else if (unit == 0) {
+ VALUE val = DBL2NUM(beg);
+ for (;;)
+ rb_yield(val);
+ }
+ else {
+ for (i=0; i<n; i++) {
+ double d = i*unit+beg;
+ if (unit >= 0 ? end < d : d < end) d = end;
+ rb_yield(DBL2NUM(d));
+ }
+ }
+ return TRUE;
}
return FALSE;
}
@@ -2799,45 +2798,45 @@ VALUE
ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl)
{
if (FIXNUM_P(from) && FIXNUM_P(to) && FIXNUM_P(step)) {
- long delta, diff;
-
- diff = FIX2LONG(step);
- if (diff == 0) {
- return DBL2NUM(HUGE_VAL);
- }
- delta = FIX2LONG(to) - FIX2LONG(from);
- if (diff < 0) {
- diff = -diff;
- delta = -delta;
- }
- if (excl) {
- delta--;
- }
- if (delta < 0) {
- return INT2FIX(0);
- }
- return ULONG2NUM(delta / diff + 1UL);
+ long delta, diff;
+
+ diff = FIX2LONG(step);
+ if (diff == 0) {
+ return DBL2NUM(HUGE_VAL);
+ }
+ delta = FIX2LONG(to) - FIX2LONG(from);
+ if (diff < 0) {
+ diff = -diff;
+ delta = -delta;
+ }
+ if (excl) {
+ delta--;
+ }
+ if (delta < 0) {
+ return INT2FIX(0);
+ }
+ return ULONG2NUM(delta / diff + 1UL);
}
else if (RB_FLOAT_TYPE_P(from) || RB_FLOAT_TYPE_P(to) || RB_FLOAT_TYPE_P(step)) {
- double n = ruby_float_step_size(NUM2DBL(from), NUM2DBL(to), NUM2DBL(step), excl);
+ double n = ruby_float_step_size(NUM2DBL(from), NUM2DBL(to), NUM2DBL(step), excl);
- if (isinf(n)) return DBL2NUM(n);
- if (POSFIXABLE(n)) return LONG2FIX((long)n);
- return rb_dbl2big(n);
+ if (isinf(n)) return DBL2NUM(n);
+ if (POSFIXABLE(n)) return LONG2FIX((long)n);
+ return rb_dbl2big(n);
}
else {
- VALUE result;
- ID cmp = '>';
- switch (rb_cmpint(rb_num_coerce_cmp(step, INT2FIX(0), id_cmp), step, INT2FIX(0))) {
- case 0: return DBL2NUM(HUGE_VAL);
- case -1: cmp = '<'; break;
- }
- if (RTEST(rb_funcall(from, cmp, 1, to))) return INT2FIX(0);
- result = rb_funcall(rb_funcall(to, '-', 1, from), id_div, 1, step);
- if (!excl || RTEST(rb_funcall(rb_funcall(from, '+', 1, rb_funcall(result, '*', 1, step)), cmp, 1, to))) {
- result = rb_funcall(result, '+', 1, INT2FIX(1));
- }
- return result;
+ VALUE result;
+ ID cmp = '>';
+ switch (rb_cmpint(rb_num_coerce_cmp(step, INT2FIX(0), id_cmp), step, INT2FIX(0))) {
+ case 0: return DBL2NUM(HUGE_VAL);
+ case -1: cmp = '<'; break;
+ }
+ if (RTEST(rb_funcall(from, cmp, 1, to))) return INT2FIX(0);
+ result = rb_funcall(rb_funcall(to, '-', 1, from), id_div, 1, step);
+ if (!excl || RTEST(rb_funcall(rb_funcall(from, '+', 1, rb_funcall(result, '*', 1, step)), cmp, 1, to))) {
+ result = rb_funcall(result, '+', 1, INT2FIX(1));
+ }
+ return result;
}
}
@@ -2849,17 +2848,17 @@ num_step_negative_p(VALUE num)
VALUE r;
if (FIXNUM_P(num)) {
- if (method_basic_p(rb_cInteger))
- return (SIGNED_VALUE)num < 0;
+ if (method_basic_p(rb_cInteger))
+ return (SIGNED_VALUE)num < 0;
}
else if (RB_BIGNUM_TYPE_P(num)) {
- if (method_basic_p(rb_cInteger))
- return BIGNUM_NEGATIVE_P(num);
+ if (method_basic_p(rb_cInteger))
+ return BIGNUM_NEGATIVE_P(num);
}
r = rb_check_funcall(num, '>', 1, &zero);
- if (r == Qundef) {
- coerce_failed(num, INT2FIX(0));
+ if (UNDEF_P(r)) {
+ coerce_failed(num, INT2FIX(0));
}
return !RTEST(r);
}
@@ -2871,19 +2870,19 @@ num_step_extract_args(int argc, const VALUE *argv, VALUE *to, VALUE *step, VALUE
argc = rb_scan_args(argc, argv, "02:", to, step, &hash);
if (!NIL_P(hash)) {
- ID keys[2];
- VALUE values[2];
- keys[0] = id_to;
- keys[1] = id_by;
- rb_get_kwargs(hash, keys, 0, 2, values);
- if (values[0] != Qundef) {
- if (argc > 0) rb_raise(rb_eArgError, "to is given twice");
- *to = values[0];
- }
- if (values[1] != Qundef) {
- if (argc > 1) rb_raise(rb_eArgError, "step is given twice");
- *by = values[1];
- }
+ ID keys[2];
+ VALUE values[2];
+ keys[0] = id_to;
+ keys[1] = id_by;
+ rb_get_kwargs(hash, keys, 0, 2, values);
+ if (!UNDEF_P(values[0])) {
+ if (argc > 0) rb_raise(rb_eArgError, "to is given twice");
+ *to = values[0];
+ }
+ if (!UNDEF_P(values[1])) {
+ if (argc > 1) rb_raise(rb_eArgError, "step is given twice");
+ *by = values[1];
+ }
}
return argc;
@@ -2893,7 +2892,7 @@ static int
num_step_check_fix_args(int argc, VALUE *to, VALUE *step, VALUE by, int fix_nil, int allow_zero_step)
{
int desc;
- if (by != Qundef) {
+ if (!UNDEF_P(by)) {
*step = by;
}
else {
@@ -2906,7 +2905,7 @@ num_step_check_fix_args(int argc, VALUE *to, VALUE *step, VALUE by, int fix_nil,
rb_raise(rb_eArgError, "step can't be 0");
}
if (NIL_P(*step)) {
- *step = INT2FIX(1);
+ *step = INT2FIX(1);
}
desc = num_step_negative_p(*step);
if (fix_nil && NIL_P(*to)) {
@@ -3041,7 +3040,7 @@ num_step(int argc, VALUE *argv, VALUE from)
VALUE by = Qundef;
num_step_extract_args(argc, argv, &to, &step, &by);
- if (by != Qundef) {
+ if (!UNDEF_P(by)) {
step = by;
}
if (NIL_P(step)) {
@@ -3056,53 +3055,53 @@ num_step(int argc, VALUE *argv, VALUE from)
num_step_size, from, to, step, FALSE);
}
- return SIZED_ENUMERATOR(from, 2, ((VALUE [2]){to, step}), num_step_size);
+ return SIZED_ENUMERATOR_KW(from, 2, ((VALUE [2]){to, step}), num_step_size, FALSE);
}
desc = num_step_scan_args(argc, argv, &to, &step, TRUE, FALSE);
if (rb_equal(step, INT2FIX(0))) {
- inf = 1;
+ inf = 1;
}
else if (RB_FLOAT_TYPE_P(to)) {
- double f = RFLOAT_VALUE(to);
- inf = isinf(f) && (signbit(f) ? desc : !desc);
+ double f = RFLOAT_VALUE(to);
+ inf = isinf(f) && (signbit(f) ? desc : !desc);
}
else inf = 0;
if (FIXNUM_P(from) && (inf || FIXNUM_P(to)) && FIXNUM_P(step)) {
- long i = FIX2LONG(from);
- long diff = FIX2LONG(step);
-
- if (inf) {
- for (;; i += diff)
- rb_yield(LONG2FIX(i));
- }
- else {
- long end = FIX2LONG(to);
-
- if (desc) {
- for (; i >= end; i += diff)
- rb_yield(LONG2FIX(i));
- }
- else {
- for (; i <= end; i += diff)
- rb_yield(LONG2FIX(i));
- }
- }
+ long i = FIX2LONG(from);
+ long diff = FIX2LONG(step);
+
+ if (inf) {
+ for (;; i += diff)
+ rb_yield(LONG2FIX(i));
+ }
+ else {
+ long end = FIX2LONG(to);
+
+ if (desc) {
+ for (; i >= end; i += diff)
+ rb_yield(LONG2FIX(i));
+ }
+ else {
+ for (; i <= end; i += diff)
+ rb_yield(LONG2FIX(i));
+ }
+ }
}
else if (!ruby_float_step(from, to, step, FALSE, FALSE)) {
- VALUE i = from;
+ VALUE i = from;
- if (inf) {
- for (;; i = rb_funcall(i, '+', 1, step))
- rb_yield(i);
- }
- else {
- ID cmp = desc ? '<' : '>';
+ if (inf) {
+ for (;; i = rb_funcall(i, '+', 1, step))
+ rb_yield(i);
+ }
+ else {
+ ID cmp = desc ? '<' : '>';
- for (; !RTEST(rb_funcall(i, cmp, 1, to)); i = rb_funcall(i, '+', 1, step))
- rb_yield(i);
- }
+ for (; !RTEST(rb_funcall(i, cmp, 1, to)); i = rb_funcall(i, '+', 1, step))
+ rb_yield(i);
+ }
}
return from;
}
@@ -3121,7 +3120,7 @@ out_of_range_float(char (*pbuf)[24], VALUE val)
#define FLOAT_OUT_OF_RANGE(val, type) do { \
char buf[24]; \
rb_raise(rb_eRangeError, "float %s out of range of "type, \
- out_of_range_float(&buf, (val))); \
+ out_of_range_float(&buf, (val))); \
} while (0)
#define LONG_MIN_MINUS_ONE ((double)LONG_MIN-1)
@@ -3137,26 +3136,26 @@ rb_num2long(VALUE val)
{
again:
if (NIL_P(val)) {
- rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
+ rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
}
if (FIXNUM_P(val)) return FIX2LONG(val);
else if (RB_FLOAT_TYPE_P(val)) {
- if (RFLOAT_VALUE(val) < LONG_MAX_PLUS_ONE
- && LONG_MIN_MINUS_ONE_IS_LESS_THAN(RFLOAT_VALUE(val))) {
- return (long)RFLOAT_VALUE(val);
- }
- else {
- FLOAT_OUT_OF_RANGE(val, "integer");
- }
+ if (RFLOAT_VALUE(val) < LONG_MAX_PLUS_ONE
+ && LONG_MIN_MINUS_ONE_IS_LESS_THAN(RFLOAT_VALUE(val))) {
+ return (long)RFLOAT_VALUE(val);
+ }
+ else {
+ FLOAT_OUT_OF_RANGE(val, "integer");
+ }
}
else if (RB_BIGNUM_TYPE_P(val)) {
- return rb_big2long(val);
+ return rb_big2long(val);
}
else {
- val = rb_to_int(val);
- goto again;
+ val = rb_to_int(val);
+ goto again;
}
}
@@ -3175,17 +3174,17 @@ rb_num2ulong_internal(VALUE val, int *wrap_p)
return (unsigned long)l;
}
else if (RB_FLOAT_TYPE_P(val)) {
- double d = RFLOAT_VALUE(val);
- if (d < ULONG_MAX_PLUS_ONE && LONG_MIN_MINUS_ONE_IS_LESS_THAN(d)) {
- if (wrap_p)
- *wrap_p = d <= -1.0; /* NUM2ULONG(v) uses v.to_int conceptually. */
- if (0 <= d)
- return (unsigned long)d;
- return (unsigned long)(long)d;
- }
- else {
- FLOAT_OUT_OF_RANGE(val, "integer");
- }
+ double d = RFLOAT_VALUE(val);
+ if (d < ULONG_MAX_PLUS_ONE && LONG_MIN_MINUS_ONE_IS_LESS_THAN(d)) {
+ if (wrap_p)
+ *wrap_p = d <= -1.0; /* NUM2ULONG(v) uses v.to_int conceptually. */
+ if (0 <= d)
+ return (unsigned long)d;
+ return (unsigned long)(long)d;
+ }
+ else {
+ FLOAT_OUT_OF_RANGE(val, "integer");
+ }
}
else if (RB_BIGNUM_TYPE_P(val)) {
{
@@ -3211,7 +3210,7 @@ void
rb_out_of_int(SIGNED_VALUE num)
{
rb_raise(rb_eRangeError, "integer %"PRIdVALUE " too %s to convert to `int'",
- num, num < 0 ? "small" : "big");
+ num, num < 0 ? "small" : "big");
}
#if SIZEOF_INT < SIZEOF_LONG
@@ -3219,7 +3218,7 @@ static void
check_int(long num)
{
if ((long)(int)num != num) {
- rb_out_of_int(num);
+ rb_out_of_int(num);
}
}
@@ -3227,14 +3226,14 @@ static void
check_uint(unsigned long num, int sign)
{
if (sign) {
- /* minus */
- if (num < (unsigned long)INT_MIN)
- rb_raise(rb_eRangeError, "integer %ld too small to convert to `unsigned int'", (long)num);
+ /* minus */
+ if (num < (unsigned long)INT_MIN)
+ rb_raise(rb_eRangeError, "integer %ld too small to convert to `unsigned int'", (long)num);
}
else {
- /* plus */
- if (UINT_MAX < num)
- rb_raise(rb_eRangeError, "integer %lu too big to convert to `unsigned int'", num);
+ /* plus */
+ if (UINT_MAX < num)
+ rb_raise(rb_eRangeError, "integer %lu too big to convert to `unsigned int'", num);
}
}
@@ -3272,7 +3271,7 @@ rb_fix2uint(VALUE val)
unsigned long num;
if (!FIXNUM_P(val)) {
- return rb_num2uint(val);
+ return rb_num2uint(val);
}
num = FIX2ULONG(val);
@@ -3310,14 +3309,14 @@ static void
rb_out_of_short(SIGNED_VALUE num)
{
rb_raise(rb_eRangeError, "integer %"PRIdVALUE " too %s to convert to `short'",
- num, num < 0 ? "small" : "big");
+ num, num < 0 ? "small" : "big");
}
static void
check_short(long num)
{
if ((long)(short)num != num) {
- rb_out_of_short(num);
+ rb_out_of_short(num);
}
}
@@ -3325,14 +3324,14 @@ static void
check_ushort(unsigned long num, int sign)
{
if (sign) {
- /* minus */
- if (num < (unsigned long)SHRT_MIN)
- rb_raise(rb_eRangeError, "integer %ld too small to convert to `unsigned short'", (long)num);
+ /* minus */
+ if (num < (unsigned long)SHRT_MIN)
+ rb_raise(rb_eRangeError, "integer %ld too small to convert to `unsigned short'", (long)num);
}
else {
- /* plus */
- if (USHRT_MAX < num)
- rb_raise(rb_eRangeError, "integer %lu too big to convert to `unsigned short'", num);
+ /* plus */
+ if (USHRT_MAX < num)
+ rb_raise(rb_eRangeError, "integer %lu too big to convert to `unsigned short'", num);
}
}
@@ -3370,7 +3369,7 @@ rb_fix2ushort(VALUE val)
unsigned long num;
if (!FIXNUM_P(val)) {
- return rb_num2ushort(val);
+ return rb_num2ushort(val);
}
num = FIX2ULONG(val);
@@ -3387,7 +3386,7 @@ rb_num2fix(VALUE val)
v = rb_num2long(val);
if (!FIXABLE(v))
- rb_raise(rb_eRangeError, "integer %ld out of range of fixnum", v);
+ rb_raise(rb_eRangeError, "integer %ld out of range of fixnum", v);
return LONG2FIX(v);
}
@@ -3408,28 +3407,28 @@ LONG_LONG
rb_num2ll(VALUE val)
{
if (NIL_P(val)) {
- rb_raise(rb_eTypeError, "no implicit conversion from nil");
+ rb_raise(rb_eTypeError, "no implicit conversion from nil");
}
if (FIXNUM_P(val)) return (LONG_LONG)FIX2LONG(val);
else if (RB_FLOAT_TYPE_P(val)) {
- double d = RFLOAT_VALUE(val);
- if (d < LLONG_MAX_PLUS_ONE && (LLONG_MIN_MINUS_ONE_IS_LESS_THAN(d))) {
- return (LONG_LONG)d;
- }
- else {
- FLOAT_OUT_OF_RANGE(val, "long long");
- }
+ double d = RFLOAT_VALUE(val);
+ if (d < LLONG_MAX_PLUS_ONE && (LLONG_MIN_MINUS_ONE_IS_LESS_THAN(d))) {
+ return (LONG_LONG)d;
+ }
+ else {
+ FLOAT_OUT_OF_RANGE(val, "long long");
+ }
}
else if (RB_BIGNUM_TYPE_P(val)) {
- return rb_big2ll(val);
+ return rb_big2ll(val);
}
else if (RB_TYPE_P(val, T_STRING)) {
- rb_raise(rb_eTypeError, "no implicit conversion from string");
+ rb_raise(rb_eTypeError, "no implicit conversion from string");
}
else if (RB_TYPE_P(val, T_TRUE) || RB_TYPE_P(val, T_FALSE)) {
- rb_raise(rb_eTypeError, "no implicit conversion from boolean");
+ rb_raise(rb_eTypeError, "no implicit conversion from boolean");
}
val = rb_to_int(val);
@@ -3440,30 +3439,30 @@ unsigned LONG_LONG
rb_num2ull(VALUE val)
{
if (NIL_P(val)) {
- rb_raise(rb_eTypeError, "no implicit conversion from nil");
+ rb_raise(rb_eTypeError, "no implicit conversion from nil");
}
else if (FIXNUM_P(val)) {
- return (LONG_LONG)FIX2LONG(val); /* this is FIX2LONG, intended */
+ return (LONG_LONG)FIX2LONG(val); /* this is FIX2LONG, intended */
}
else if (RB_FLOAT_TYPE_P(val)) {
- double d = RFLOAT_VALUE(val);
- if (d < ULLONG_MAX_PLUS_ONE && LLONG_MIN_MINUS_ONE_IS_LESS_THAN(d)) {
- if (0 <= d)
- return (unsigned LONG_LONG)d;
- return (unsigned LONG_LONG)(LONG_LONG)d;
- }
- else {
- FLOAT_OUT_OF_RANGE(val, "unsigned long long");
- }
+ double d = RFLOAT_VALUE(val);
+ if (d < ULLONG_MAX_PLUS_ONE && LLONG_MIN_MINUS_ONE_IS_LESS_THAN(d)) {
+ if (0 <= d)
+ return (unsigned LONG_LONG)d;
+ return (unsigned LONG_LONG)(LONG_LONG)d;
+ }
+ else {
+ FLOAT_OUT_OF_RANGE(val, "unsigned long long");
+ }
}
else if (RB_BIGNUM_TYPE_P(val)) {
- return rb_big2ull(val);
+ return rb_big2ull(val);
}
else if (RB_TYPE_P(val, T_STRING)) {
- rb_raise(rb_eTypeError, "no implicit conversion from string");
+ rb_raise(rb_eTypeError, "no implicit conversion from string");
}
else if (RB_TYPE_P(val, T_TRUE) || RB_TYPE_P(val, T_FALSE)) {
- rb_raise(rb_eTypeError, "no implicit conversion from boolean");
+ rb_raise(rb_eTypeError, "no implicit conversion from boolean");
}
val = rb_to_int(val);
@@ -3574,7 +3573,7 @@ rb_int_odd_p(VALUE num)
}
else {
assert(RB_BIGNUM_TYPE_P(num));
- return rb_big_odd_p(num);
+ return rb_big_odd_p(num);
}
}
@@ -3586,7 +3585,7 @@ int_even_p(VALUE num)
}
else {
assert(RB_BIGNUM_TYPE_P(num));
- return rb_big_even_p(num);
+ return rb_big_even_p(num);
}
}
@@ -3704,11 +3703,11 @@ VALUE
rb_int_succ(VALUE num)
{
if (FIXNUM_P(num)) {
- long i = FIX2LONG(num) + 1;
- return LONG2NUM(i);
+ long i = FIX2LONG(num) + 1;
+ return LONG2NUM(i);
}
if (RB_BIGNUM_TYPE_P(num)) {
- return rb_big_plus(num, INT2FIX(1));
+ return rb_big_plus(num, INT2FIX(1));
}
return num_funcall1(num, '+', INT2FIX(1));
}
@@ -3732,11 +3731,11 @@ static VALUE
rb_int_pred(VALUE num)
{
if (FIXNUM_P(num)) {
- long i = FIX2LONG(num) - 1;
- return LONG2NUM(i);
+ long i = FIX2LONG(num) - 1;
+ return LONG2NUM(i);
}
if (RB_BIGNUM_TYPE_P(num)) {
- return rb_big_minus(num, INT2FIX(1));
+ return rb_big_minus(num, INT2FIX(1));
}
return num_funcall1(num, '-', INT2FIX(1));
}
@@ -3750,17 +3749,17 @@ rb_enc_uint_chr(unsigned int code, rb_encoding *enc)
VALUE str;
switch (n = rb_enc_codelen(code, enc)) {
case ONIGERR_INVALID_CODE_POINT_VALUE:
- rb_raise(rb_eRangeError, "invalid codepoint 0x%X in %s", code, rb_enc_name(enc));
- break;
+ rb_raise(rb_eRangeError, "invalid codepoint 0x%X in %s", code, rb_enc_name(enc));
+ break;
case ONIGERR_TOO_BIG_WIDE_CHAR_VALUE:
case 0:
- rb_raise(rb_eRangeError, "%u out of char range", code);
- break;
+ rb_raise(rb_eRangeError, "%u out of char range", code);
+ break;
}
str = rb_enc_str_new(0, n, enc);
rb_enc_mbcput(code, RSTRING_PTR(str), enc);
if (rb_enc_precise_mbclen(RSTRING_PTR(str), RSTRING_END(str), enc) != n) {
- rb_raise(rb_eRangeError, "invalid codepoint 0x%X in %s", code, rb_enc_name(enc));
+ rb_raise(rb_eRangeError, "invalid codepoint 0x%X in %s", code, rb_enc_name(enc));
}
return str;
}
@@ -3794,30 +3793,30 @@ int_chr(int argc, VALUE *argv, VALUE num)
if (rb_num_to_uint(num, &i) == 0) {
}
else if (FIXNUM_P(num)) {
- rb_raise(rb_eRangeError, "%ld out of char range", FIX2LONG(num));
+ rb_raise(rb_eRangeError, "%ld out of char range", FIX2LONG(num));
}
else {
- rb_raise(rb_eRangeError, "bignum out of char range");
+ rb_raise(rb_eRangeError, "bignum out of char range");
}
switch (argc) {
case 0:
- if (0xff < i) {
- enc = rb_default_internal_encoding();
- if (!enc) {
- rb_raise(rb_eRangeError, "%u out of char range", i);
- }
- goto decode;
- }
- c = (char)i;
- if (i < 0x80) {
- return rb_usascii_str_new(&c, 1);
- }
- else {
- return rb_str_new(&c, 1);
- }
+ if (0xff < i) {
+ enc = rb_default_internal_encoding();
+ if (!enc) {
+ rb_raise(rb_eRangeError, "%u out of char range", i);
+ }
+ goto decode;
+ }
+ c = (char)i;
+ if (i < 0x80) {
+ return rb_usascii_str_new(&c, 1);
+ }
+ else {
+ return rb_str_new(&c, 1);
+ }
case 1:
- break;
+ break;
default:
rb_error_arity(argc, 0, 1);
}
@@ -3841,11 +3840,11 @@ VALUE
rb_int_uminus(VALUE num)
{
if (FIXNUM_P(num)) {
- return fix_uminus(num);
+ return fix_uminus(num);
}
else {
assert(RB_BIGNUM_TYPE_P(num));
- return rb_big_uminus(num);
+ return rb_big_uminus(num);
}
}
@@ -3858,13 +3857,13 @@ rb_fix2str(VALUE x, int base)
int neg = 0;
if (base < 2 || 36 < base) {
- rb_raise(rb_eArgError, "invalid radix %d", base);
+ rb_raise(rb_eArgError, "invalid radix %d", base);
}
#if SIZEOF_LONG < SIZEOF_VOIDP
# if SIZEOF_VOIDP == SIZEOF_LONG_LONG
if ((val >= 0 && (x & 0xFFFFFFFF00000000ull)) ||
- (val < 0 && (x & 0xFFFFFFFF00000000ull) != 0xFFFFFFFF00000000ull)) {
- rb_bug("Unnormalized Fixnum value %p", (void *)x);
+ (val < 0 && (x & 0xFFFFFFFF00000000ull) != 0xFFFFFFFF00000000ull)) {
+ rb_bug("Unnormalized Fixnum value %p", (void *)x);
}
# else
/* should do something like above code, but currently ruby does not know */
@@ -3872,20 +3871,20 @@ rb_fix2str(VALUE x, int base)
# endif
#endif
if (val == 0) {
- return rb_usascii_str_new2("0");
+ return rb_usascii_str_new2("0");
}
if (val < 0) {
- u = 1 + (unsigned long)(-(val + 1)); /* u = -val avoiding overflow */
- neg = 1;
+ u = 1 + (unsigned long)(-(val + 1)); /* u = -val avoiding overflow */
+ neg = 1;
}
else {
- u = val;
+ u = val;
}
do {
- *--b = ruby_digitmap[(int)(u % base)];
+ *--b = ruby_digitmap[(int)(u % base)];
} while (u /= base);
if (neg) {
- *--b = '-';
+ *--b = '-';
}
return rb_usascii_str_new(b, e - b);
@@ -3930,9 +3929,9 @@ rb_int_to_s(int argc, VALUE *argv, VALUE x)
int base;
if (rb_check_arity(argc, 0, 1))
- base = NUM2INT(argv[0]);
+ base = NUM2INT(argv[0]);
else
- base = 10;
+ base = 10;
return rb_int2str(x, base);
}
@@ -3940,10 +3939,10 @@ VALUE
rb_int2str(VALUE x, int base)
{
if (FIXNUM_P(x)) {
- return rb_fix2str(x, base);
+ return rb_fix2str(x, base);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big2str(x, base);
+ return rb_big2str(x, base);
}
return rb_any_to_s(x);
@@ -3953,19 +3952,19 @@ static VALUE
fix_plus(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- return rb_fix_plus_fix(x, y);
+ return rb_fix_plus_fix(x, y);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return rb_big_plus(y, x);
+ return rb_big_plus(y, x);
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM((double)FIX2LONG(x) + RFLOAT_VALUE(y));
+ return DBL2NUM((double)FIX2LONG(x) + RFLOAT_VALUE(y));
}
else if (RB_TYPE_P(y, T_COMPLEX)) {
- return rb_complex_plus(y, x);
+ return rb_complex_plus(y, x);
}
else {
- return rb_num_coerce_bin(x, y, '+');
+ return rb_num_coerce_bin(x, y, '+');
}
}
@@ -3994,10 +3993,10 @@ VALUE
rb_int_plus(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_plus(x, y);
+ return fix_plus(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_plus(x, y);
+ return rb_big_plus(x, y);
}
return rb_num_coerce_bin(x, y, '+');
}
@@ -4006,17 +4005,17 @@ static VALUE
fix_minus(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- return rb_fix_minus_fix(x, y);
+ return rb_fix_minus_fix(x, y);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- x = rb_int2big(FIX2LONG(x));
- return rb_big_minus(x, y);
+ x = rb_int2big(FIX2LONG(x));
+ return rb_big_minus(x, y);
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM((double)FIX2LONG(x) - RFLOAT_VALUE(y));
+ return DBL2NUM((double)FIX2LONG(x) - RFLOAT_VALUE(y));
}
else {
- return rb_num_coerce_bin(x, y, '-');
+ return rb_num_coerce_bin(x, y, '-');
}
}
@@ -4039,10 +4038,10 @@ VALUE
rb_int_minus(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_minus(x, y);
+ return fix_minus(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_minus(x, y);
+ return rb_big_minus(x, y);
}
return rb_num_coerce_bin(x, y, '-');
}
@@ -4056,23 +4055,23 @@ static VALUE
fix_mul(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- return rb_fix_mul_fix(x, y);
+ return rb_fix_mul_fix(x, y);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- switch (x) {
- case INT2FIX(0): return x;
- case INT2FIX(1): return y;
- }
- return rb_big_mul(y, x);
+ switch (x) {
+ case INT2FIX(0): return x;
+ case INT2FIX(1): return y;
+ }
+ return rb_big_mul(y, x);
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM((double)FIX2LONG(x) * RFLOAT_VALUE(y));
+ return DBL2NUM((double)FIX2LONG(x) * RFLOAT_VALUE(y));
}
else if (RB_TYPE_P(y, T_COMPLEX)) {
- return rb_complex_mul(y, x);
+ return rb_complex_mul(y, x);
}
else {
- return rb_num_coerce_bin(x, y, '*');
+ return rb_num_coerce_bin(x, y, '*');
}
}
@@ -4094,10 +4093,10 @@ VALUE
rb_int_mul(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_mul(x, y);
+ return fix_mul(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_mul(x, y);
+ return rb_big_mul(x, y);
}
return rb_num_coerce_bin(x, y, '*');
}
@@ -4123,11 +4122,11 @@ double
rb_int_fdiv_double(VALUE x, VALUE y)
{
if (RB_INTEGER_TYPE_P(y) && !FIXNUM_ZERO_P(y)) {
- VALUE gcd = rb_gcd(x, y);
- if (!FIXNUM_ZERO_P(gcd)) {
- x = rb_int_idiv(x, gcd);
- y = rb_int_idiv(y, gcd);
- }
+ VALUE gcd = rb_gcd(x, y);
+ if (!FIXNUM_ZERO_P(gcd)) {
+ x = rb_int_idiv(x, gcd);
+ y = rb_int_idiv(y, gcd);
+ }
}
if (FIXNUM_P(x)) {
return fix_fdiv_double(x, y);
@@ -4169,30 +4168,30 @@ static VALUE
fix_divide(VALUE x, VALUE y, ID op)
{
if (FIXNUM_P(y)) {
- if (FIXNUM_ZERO_P(y)) rb_num_zerodiv();
- return rb_fix_div_fix(x, y);
+ if (FIXNUM_ZERO_P(y)) rb_num_zerodiv();
+ return rb_fix_div_fix(x, y);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- x = rb_int2big(FIX2LONG(x));
- return rb_big_div(x, y);
+ x = rb_int2big(FIX2LONG(x));
+ return rb_big_div(x, y);
}
else if (RB_FLOAT_TYPE_P(y)) {
- if (op == '/') {
+ if (op == '/') {
double d = FIX2LONG(x);
return rb_flo_div_flo(DBL2NUM(d), y);
- }
- else {
+ }
+ else {
VALUE v;
- if (RFLOAT_VALUE(y) == 0) rb_num_zerodiv();
+ if (RFLOAT_VALUE(y) == 0) rb_num_zerodiv();
v = fix_divide(x, y, '/');
return flo_floor(0, 0, v);
- }
+ }
}
else {
- if (RB_TYPE_P(y, T_RATIONAL) &&
- op == '/' && FIX2LONG(x) == 1)
- return rb_rational_reciprocal(y);
- return rb_num_coerce_bin(x, y, op);
+ if (RB_TYPE_P(y, T_RATIONAL) &&
+ op == '/' && FIX2LONG(x) == 1)
+ return rb_rational_reciprocal(y);
+ return rb_num_coerce_bin(x, y, op);
}
}
@@ -4225,10 +4224,10 @@ VALUE
rb_int_div(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_div(x, y);
+ return fix_div(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_div(x, y);
+ return rb_big_div(x, y);
}
return Qnil;
}
@@ -4261,10 +4260,10 @@ VALUE
rb_int_idiv(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_idiv(x, y);
+ return fix_idiv(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_idiv(x, y);
+ return rb_big_idiv(x, y);
}
return num_div(x, y);
}
@@ -4273,18 +4272,18 @@ static VALUE
fix_mod(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- if (FIXNUM_ZERO_P(y)) rb_num_zerodiv();
- return rb_fix_mod_fix(x, y);
+ if (FIXNUM_ZERO_P(y)) rb_num_zerodiv();
+ return rb_fix_mod_fix(x, y);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- x = rb_int2big(FIX2LONG(x));
- return rb_big_modulo(x, y);
+ x = rb_int2big(FIX2LONG(x));
+ return rb_big_modulo(x, y);
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM(ruby_float_mod((double)FIX2LONG(x), RFLOAT_VALUE(y)));
+ return DBL2NUM(ruby_float_mod((double)FIX2LONG(x), RFLOAT_VALUE(y)));
}
else {
- return rb_num_coerce_bin(x, y, '%');
+ return rb_num_coerce_bin(x, y, '%');
}
}
@@ -4322,10 +4321,10 @@ VALUE
rb_int_modulo(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_mod(x, y);
+ return fix_mod(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_modulo(x, y);
+ return rb_big_modulo(x, y);
}
return num_modulo(x, y);
}
@@ -4357,10 +4356,10 @@ static VALUE
int_remainder(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return num_remainder(x, y);
+ return num_remainder(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_remainder(x, y);
+ return rb_big_remainder(x, y);
}
return Qnil;
}
@@ -4369,28 +4368,28 @@ static VALUE
fix_divmod(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- VALUE div, mod;
- if (FIXNUM_ZERO_P(y)) rb_num_zerodiv();
- rb_fix_divmod_fix(x, y, &div, &mod);
- return rb_assoc_new(div, mod);
+ VALUE div, mod;
+ if (FIXNUM_ZERO_P(y)) rb_num_zerodiv();
+ rb_fix_divmod_fix(x, y, &div, &mod);
+ return rb_assoc_new(div, mod);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- x = rb_int2big(FIX2LONG(x));
- return rb_big_divmod(x, y);
+ x = rb_int2big(FIX2LONG(x));
+ return rb_big_divmod(x, y);
}
else if (RB_FLOAT_TYPE_P(y)) {
- {
- double div, mod;
- volatile VALUE a, b;
+ {
+ double div, mod;
+ volatile VALUE a, b;
- flodivmod((double)FIX2LONG(x), RFLOAT_VALUE(y), &div, &mod);
- a = dbl2ival(div);
- b = DBL2NUM(mod);
- return rb_assoc_new(a, b);
- }
+ flodivmod((double)FIX2LONG(x), RFLOAT_VALUE(y), &div, &mod);
+ a = dbl2ival(div);
+ b = DBL2NUM(mod);
+ return rb_assoc_new(a, b);
+ }
}
else {
- return rb_num_coerce_bin(x, y, id_divmod);
+ return rb_num_coerce_bin(x, y, id_divmod);
}
}
@@ -4423,10 +4422,10 @@ VALUE
rb_int_divmod(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_divmod(x, y);
+ return fix_divmod(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_divmod(x, y);
+ return rb_big_divmod(x, y);
}
return Qnil;
}
@@ -4457,24 +4456,24 @@ int_pow(long x, unsigned long y)
if (y == 1) return LONG2NUM(x);
if (neg) x = -x;
if (y & 1)
- z = x;
+ z = x;
else
- neg = 0;
+ neg = 0;
y &= ~1;
do {
- while (y % 2 == 0) {
- if (!FIT_SQRT_LONG(x)) {
+ while (y % 2 == 0) {
+ if (!FIT_SQRT_LONG(x)) {
goto bignum;
- }
- x = x * x;
- y >>= 1;
- }
- {
+ }
+ x = x * x;
+ y >>= 1;
+ }
+ {
if (MUL_OVERFLOW_FIXNUM_P(x, z)) {
- goto bignum;
- }
- z = x * z;
- }
+ goto bignum;
+ }
+ z = x * z;
+ }
} while (--y);
if (neg) z = -z;
return LONG2NUM(z);
@@ -4520,37 +4519,37 @@ fix_pow(VALUE x, VALUE y)
long a = FIX2LONG(x);
if (FIXNUM_P(y)) {
- long b = FIX2LONG(y);
+ long b = FIX2LONG(y);
- if (a == 1) return INT2FIX(1);
+ if (a == 1) return INT2FIX(1);
if (a == -1) return INT2FIX(b % 2 ? -1 : 1);
if (b < 0) return fix_pow_inverted(x, fix_uminus(y));
- if (b == 0) return INT2FIX(1);
- if (b == 1) return x;
- if (a == 0) return INT2FIX(0);
- return int_pow(a, b);
+ if (b == 0) return INT2FIX(1);
+ if (b == 1) return x;
+ if (a == 0) return INT2FIX(0);
+ return int_pow(a, b);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- if (a == 1) return INT2FIX(1);
+ if (a == 1) return INT2FIX(1);
if (a == -1) return INT2FIX(int_even_p(y) ? 1 : -1);
if (BIGNUM_NEGATIVE_P(y)) return fix_pow_inverted(x, rb_big_uminus(y));
- if (a == 0) return INT2FIX(0);
- x = rb_int2big(FIX2LONG(x));
- return rb_big_pow(x, y);
+ if (a == 0) return INT2FIX(0);
+ x = rb_int2big(FIX2LONG(x));
+ return rb_big_pow(x, y);
}
else if (RB_FLOAT_TYPE_P(y)) {
- double dy = RFLOAT_VALUE(y);
- if (dy == 0.0) return DBL2NUM(1.0);
- if (a == 0) {
- return DBL2NUM(dy < 0 ? HUGE_VAL : 0.0);
- }
- if (a == 1) return DBL2NUM(1.0);
+ double dy = RFLOAT_VALUE(y);
+ if (dy == 0.0) return DBL2NUM(1.0);
+ if (a == 0) {
+ return DBL2NUM(dy < 0 ? HUGE_VAL : 0.0);
+ }
+ if (a == 1) return DBL2NUM(1.0);
if (a < 0 && dy != round(dy))
return rb_dbl_complex_new_polar_pi(pow(-(double)a, dy), dy);
return DBL2NUM(pow((double)a, dy));
}
else {
- return rb_num_coerce_bin(x, y, idPow);
+ return rb_num_coerce_bin(x, y, idPow);
}
}
@@ -4573,10 +4572,10 @@ VALUE
rb_int_pow(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_pow(x, y);
+ return fix_pow(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_pow(x, y);
+ return rb_big_pow(x, y);
}
return Qnil;
}
@@ -4605,13 +4604,13 @@ fix_equal(VALUE x, VALUE y)
if (x == y) return Qtrue;
if (FIXNUM_P(y)) return Qfalse;
else if (RB_BIGNUM_TYPE_P(y)) {
- return rb_big_eq(y, x);
+ return rb_big_eq(y, x);
}
else if (RB_FLOAT_TYPE_P(y)) {
return rb_integer_float_eq(x, y);
}
else {
- return num_equal(x, y);
+ return num_equal(x, y);
}
}
@@ -4634,10 +4633,10 @@ VALUE
rb_int_equal(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_equal(x, y);
+ return fix_equal(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_eq(x, y);
+ return rb_big_eq(x, y);
}
return Qnil;
}
@@ -4647,22 +4646,22 @@ fix_cmp(VALUE x, VALUE y)
{
if (x == y) return INT2FIX(0);
if (FIXNUM_P(y)) {
- if (FIX2LONG(x) > FIX2LONG(y)) return INT2FIX(1);
- return INT2FIX(-1);
+ if (FIX2LONG(x) > FIX2LONG(y)) return INT2FIX(1);
+ return INT2FIX(-1);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- VALUE cmp = rb_big_cmp(y, x);
- switch (cmp) {
- case INT2FIX(+1): return INT2FIX(-1);
- case INT2FIX(-1): return INT2FIX(+1);
- }
- return cmp;
+ VALUE cmp = rb_big_cmp(y, x);
+ switch (cmp) {
+ case INT2FIX(+1): return INT2FIX(-1);
+ case INT2FIX(-1): return INT2FIX(+1);
+ }
+ return cmp;
}
else if (RB_FLOAT_TYPE_P(y)) {
- return rb_integer_float_cmp(x, y);
+ return rb_integer_float_cmp(x, y);
}
else {
- return rb_num_coerce_cmp(x, y, id_cmp);
+ return rb_num_coerce_cmp(x, y, id_cmp);
}
}
@@ -4696,13 +4695,13 @@ VALUE
rb_int_cmp(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_cmp(x, y);
+ return fix_cmp(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_cmp(x, y);
+ return rb_big_cmp(x, y);
}
else {
- rb_raise(rb_eNotImpError, "need to define `<=>' in %s", rb_obj_classname(x));
+ rb_raise(rb_eNotImpError, "need to define `<=>' in %s", rb_obj_classname(x));
}
}
@@ -4713,13 +4712,13 @@ fix_gt(VALUE x, VALUE y)
return RBOOL(FIX2LONG(x) > FIX2LONG(y));
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return RBOOL(rb_big_cmp(y, x) == INT2FIX(-1));
+ return RBOOL(rb_big_cmp(y, x) == INT2FIX(-1));
}
else if (RB_FLOAT_TYPE_P(y)) {
return RBOOL(rb_integer_float_cmp(x, y) == INT2FIX(1));
}
else {
- return rb_num_coerce_relop(x, y, '>');
+ return rb_num_coerce_relop(x, y, '>');
}
}
@@ -4743,10 +4742,10 @@ VALUE
rb_int_gt(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_gt(x, y);
+ return fix_gt(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_gt(x, y);
+ return rb_big_gt(x, y);
}
return Qnil;
}
@@ -4758,14 +4757,14 @@ fix_ge(VALUE x, VALUE y)
return RBOOL(FIX2LONG(x) >= FIX2LONG(y));
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return RBOOL(rb_big_cmp(y, x) != INT2FIX(+1));
+ return RBOOL(rb_big_cmp(y, x) != INT2FIX(+1));
}
else if (RB_FLOAT_TYPE_P(y)) {
- VALUE rel = rb_integer_float_cmp(x, y);
- return RBOOL(rel == INT2FIX(1) || rel == INT2FIX(0));
+ VALUE rel = rb_integer_float_cmp(x, y);
+ return RBOOL(rel == INT2FIX(1) || rel == INT2FIX(0));
}
else {
- return rb_num_coerce_relop(x, y, idGE);
+ return rb_num_coerce_relop(x, y, idGE);
}
}
@@ -4790,10 +4789,10 @@ VALUE
rb_int_ge(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_ge(x, y);
+ return fix_ge(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_ge(x, y);
+ return rb_big_ge(x, y);
}
return Qnil;
}
@@ -4805,13 +4804,13 @@ fix_lt(VALUE x, VALUE y)
return RBOOL(FIX2LONG(x) < FIX2LONG(y));
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return RBOOL(rb_big_cmp(y, x) == INT2FIX(+1));
+ return RBOOL(rb_big_cmp(y, x) == INT2FIX(+1));
}
else if (RB_FLOAT_TYPE_P(y)) {
return RBOOL(rb_integer_float_cmp(x, y) == INT2FIX(-1));
}
else {
- return rb_num_coerce_relop(x, y, '<');
+ return rb_num_coerce_relop(x, y, '<');
}
}
@@ -4835,10 +4834,10 @@ static VALUE
int_lt(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_lt(x, y);
+ return fix_lt(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_lt(x, y);
+ return rb_big_lt(x, y);
}
return Qnil;
}
@@ -4850,14 +4849,14 @@ fix_le(VALUE x, VALUE y)
return RBOOL(FIX2LONG(x) <= FIX2LONG(y));
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return RBOOL(rb_big_cmp(y, x) != INT2FIX(-1));
+ return RBOOL(rb_big_cmp(y, x) != INT2FIX(-1));
}
else if (RB_FLOAT_TYPE_P(y)) {
- VALUE rel = rb_integer_float_cmp(x, y);
- return RBOOL(rel == INT2FIX(-1) || rel == INT2FIX(0));
+ VALUE rel = rb_integer_float_cmp(x, y);
+ return RBOOL(rel == INT2FIX(-1) || rel == INT2FIX(0));
}
else {
- return rb_num_coerce_relop(x, y, idLE);
+ return rb_num_coerce_relop(x, y, idLE);
}
}
@@ -4882,10 +4881,10 @@ static VALUE
int_le(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_le(x, y);
+ return fix_le(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_le(x, y);
+ return rb_big_le(x, y);
}
return Qnil;
}
@@ -4900,10 +4899,10 @@ VALUE
rb_int_comp(VALUE num)
{
if (FIXNUM_P(num)) {
- return fix_comp(num);
+ return fix_comp(num);
}
else if (RB_BIGNUM_TYPE_P(num)) {
- return rb_big_comp(num);
+ return rb_big_comp(num);
}
return Qnil;
}
@@ -4914,7 +4913,7 @@ num_funcall_bit_1(VALUE y, VALUE arg, int recursive)
ID func = (ID)((VALUE *)arg)[0];
VALUE x = ((VALUE *)arg)[1];
if (recursive) {
- num_funcall_op_1_recursion(x, func, y);
+ num_funcall_op_1_recursion(x, func, y);
}
return rb_check_funcall(x, func, 1, &y);
}
@@ -4929,10 +4928,10 @@ rb_num_coerce_bit(VALUE x, VALUE y, ID func)
args[2] = y;
do_coerce(&args[1], &args[2], TRUE);
ret = rb_exec_recursive_paired(num_funcall_bit_1,
- args[2], args[1], (VALUE)args);
- if (ret == Qundef) {
- /* show the original object, not coerced object */
- coerce_failed(x, y);
+ args[2], args[1], (VALUE)args);
+ if (UNDEF_P(ret)) {
+ /* show the original object, not coerced object */
+ coerce_failed(x, y);
}
return ret;
}
@@ -4941,12 +4940,12 @@ static VALUE
fix_and(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- long val = FIX2LONG(x) & FIX2LONG(y);
- return LONG2NUM(val);
+ long val = FIX2LONG(x) & FIX2LONG(y);
+ return LONG2NUM(val);
}
if (RB_BIGNUM_TYPE_P(y)) {
- return rb_big_and(y, x);
+ return rb_big_and(y, x);
}
return rb_num_coerce_bit(x, y, '&');
@@ -4971,10 +4970,10 @@ VALUE
rb_int_and(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_and(x, y);
+ return fix_and(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_and(x, y);
+ return rb_big_and(x, y);
}
return Qnil;
}
@@ -4983,12 +4982,12 @@ static VALUE
fix_or(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- long val = FIX2LONG(x) | FIX2LONG(y);
- return LONG2NUM(val);
+ long val = FIX2LONG(x) | FIX2LONG(y);
+ return LONG2NUM(val);
}
if (RB_BIGNUM_TYPE_P(y)) {
- return rb_big_or(y, x);
+ return rb_big_or(y, x);
}
return rb_num_coerce_bit(x, y, '|');
@@ -5013,10 +5012,10 @@ static VALUE
int_or(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_or(x, y);
+ return fix_or(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_or(x, y);
+ return rb_big_or(x, y);
}
return Qnil;
}
@@ -5025,12 +5024,12 @@ static VALUE
fix_xor(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- long val = FIX2LONG(x) ^ FIX2LONG(y);
- return LONG2NUM(val);
+ long val = FIX2LONG(x) ^ FIX2LONG(y);
+ return LONG2NUM(val);
}
if (RB_BIGNUM_TYPE_P(y)) {
- return rb_big_xor(y, x);
+ return rb_big_xor(y, x);
}
return rb_num_coerce_bit(x, y, '^');
@@ -5055,10 +5054,10 @@ static VALUE
int_xor(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return fix_xor(x, y);
+ return fix_xor(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_xor(x, y);
+ return rb_big_xor(x, y);
}
return Qnil;
}
@@ -5071,10 +5070,10 @@ rb_fix_lshift(VALUE x, VALUE y)
val = NUM2LONG(x);
if (!val) return (rb_to_int(y), INT2FIX(0));
if (!FIXNUM_P(y))
- return rb_big_lshift(rb_int2big(val), y);
+ return rb_big_lshift(rb_int2big(val), y);
width = FIX2LONG(y);
if (width < 0)
- return fix_rshift(val, (unsigned long)-width);
+ return fix_rshift(val, (unsigned long)-width);
return fix_lshift(val, width);
}
@@ -5082,8 +5081,8 @@ static VALUE
fix_lshift(long val, unsigned long width)
{
if (width > (SIZEOF_LONG*CHAR_BIT-1)
- || ((unsigned long)val)>>(SIZEOF_LONG*CHAR_BIT-1-width) > 0) {
- return rb_big_lshift(rb_int2big(val), ULONG2NUM(width));
+ || ((unsigned long)val)>>(SIZEOF_LONG*CHAR_BIT-1-width) > 0) {
+ return rb_big_lshift(rb_int2big(val), ULONG2NUM(width));
}
val = val << width;
return LONG2NUM(val);
@@ -5110,10 +5109,10 @@ VALUE
rb_int_lshift(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return rb_fix_lshift(x, y);
+ return rb_fix_lshift(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_lshift(x, y);
+ return rb_big_lshift(x, y);
}
return Qnil;
}
@@ -5126,11 +5125,11 @@ rb_fix_rshift(VALUE x, VALUE y)
val = FIX2LONG(x);
if (!val) return (rb_to_int(y), INT2FIX(0));
if (!FIXNUM_P(y))
- return rb_big_rshift(rb_int2big(val), y);
+ return rb_big_rshift(rb_int2big(val), y);
i = FIX2LONG(y);
if (i == 0) return x;
if (i < 0)
- return fix_lshift(val, (unsigned long)-i);
+ return fix_lshift(val, (unsigned long)-i);
return fix_rshift(val, i);
}
@@ -5138,8 +5137,8 @@ static VALUE
fix_rshift(long val, unsigned long i)
{
if (i >= sizeof(long)*CHAR_BIT-1) {
- if (val < 0) return INT2FIX(-1);
- return INT2FIX(0);
+ if (val < 0) return INT2FIX(-1);
+ return INT2FIX(0);
}
val = RSHIFT(val, i);
return LONG2FIX(val);
@@ -5166,10 +5165,10 @@ static VALUE
rb_int_rshift(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
- return rb_fix_rshift(x, y);
+ return rb_fix_rshift(x, y);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return rb_big_rshift(x, y);
+ return rb_big_rshift(x, y);
}
return Qnil;
}
@@ -5182,22 +5181,22 @@ rb_fix_aref(VALUE fix, VALUE idx)
idx = rb_to_int(idx);
if (!FIXNUM_P(idx)) {
- idx = rb_big_norm(idx);
- if (!FIXNUM_P(idx)) {
- if (!BIGNUM_SIGN(idx) || val >= 0)
- return INT2FIX(0);
- return INT2FIX(1);
- }
+ idx = rb_big_norm(idx);
+ if (!FIXNUM_P(idx)) {
+ if (!BIGNUM_SIGN(idx) || val >= 0)
+ return INT2FIX(0);
+ return INT2FIX(1);
+ }
}
i = FIX2LONG(idx);
if (i < 0) return INT2FIX(0);
if (SIZEOF_LONG*CHAR_BIT-1 <= i) {
- if (val < 0) return INT2FIX(1);
- return INT2FIX(0);
+ if (val < 0) return INT2FIX(1);
+ return INT2FIX(0);
}
if (val & (1L<<i))
- return INT2FIX(1);
+ return INT2FIX(1);
return INT2FIX(0);
}
@@ -5358,13 +5357,13 @@ int_to_f(VALUE num)
double val;
if (FIXNUM_P(num)) {
- val = (double)FIX2LONG(num);
+ val = (double)FIX2LONG(num);
}
else if (RB_BIGNUM_TYPE_P(num)) {
- val = rb_big2dbl(num);
+ val = rb_big2dbl(num);
}
else {
- rb_raise(rb_eNotImpError, "Unknown subclass for to_f: %s", rb_obj_classname(num));
+ rb_raise(rb_eNotImpError, "Unknown subclass for to_f: %s", rb_obj_classname(num));
}
return DBL2NUM(val);
@@ -5384,10 +5383,10 @@ VALUE
rb_int_abs(VALUE num)
{
if (FIXNUM_P(num)) {
- return fix_abs(num);
+ return fix_abs(num);
}
else if (RB_BIGNUM_TYPE_P(num)) {
- return rb_big_abs(num);
+ return rb_big_abs(num);
}
return Qnil;
}
@@ -5402,10 +5401,10 @@ MJIT_FUNC_EXPORTED VALUE
rb_int_size(VALUE num)
{
if (FIXNUM_P(num)) {
- return fix_size(num);
+ return fix_size(num);
}
else if (RB_BIGNUM_TYPE_P(num)) {
- return rb_big_size_m(num);
+ return rb_big_size_m(num);
}
return Qnil;
}
@@ -5423,10 +5422,10 @@ VALUE
rb_int_bit_length(VALUE num)
{
if (FIXNUM_P(num)) {
- return rb_fix_bit_length(num);
+ return rb_fix_bit_length(num);
}
else if (RB_BIGNUM_TYPE_P(num)) {
- return rb_big_bit_length(num);
+ return rb_big_bit_length(num);
}
return Qnil;
}
@@ -5588,21 +5587,21 @@ int_upto(VALUE from, VALUE to)
{
RETURN_SIZED_ENUMERATOR(from, 1, &to, int_upto_size);
if (FIXNUM_P(from) && FIXNUM_P(to)) {
- long i, end;
+ long i, end;
- end = FIX2LONG(to);
- for (i = FIX2LONG(from); i <= end; i++) {
- rb_yield(LONG2FIX(i));
- }
+ end = FIX2LONG(to);
+ for (i = FIX2LONG(from); i <= end; i++) {
+ rb_yield(LONG2FIX(i));
+ }
}
else {
- VALUE i = from, c;
+ VALUE i = from, c;
- while (!(c = rb_funcall(i, '>', 1, to))) {
- rb_yield(i);
- i = rb_funcall(i, '+', 1, INT2FIX(1));
- }
- ensure_cmp(c, i, to);
+ while (!(c = rb_funcall(i, '>', 1, to))) {
+ rb_yield(i);
+ i = rb_funcall(i, '+', 1, INT2FIX(1));
+ }
+ ensure_cmp(c, i, to);
}
return from;
}
@@ -5638,21 +5637,21 @@ int_downto(VALUE from, VALUE to)
{
RETURN_SIZED_ENUMERATOR(from, 1, &to, int_downto_size);
if (FIXNUM_P(from) && FIXNUM_P(to)) {
- long i, end;
+ long i, end;
- end = FIX2LONG(to);
- for (i=FIX2LONG(from); i >= end; i--) {
- rb_yield(LONG2FIX(i));
- }
+ end = FIX2LONG(to);
+ for (i=FIX2LONG(from); i >= end; i--) {
+ rb_yield(LONG2FIX(i));
+ }
}
else {
- VALUE i = from, c;
+ VALUE i = from, c;
- while (!(c = rb_funcall(i, '<', 1, to))) {
- rb_yield(i);
- i = rb_funcall(i, '-', 1, INT2FIX(1));
- }
- if (NIL_P(c)) rb_cmperr(i, to);
+ while (!(c = rb_funcall(i, '<', 1, to))) {
+ rb_yield(i);
+ i = rb_funcall(i, '-', 1, INT2FIX(1));
+ }
+ if (NIL_P(c)) rb_cmperr(i, to);
}
return from;
}
@@ -5661,10 +5660,10 @@ static VALUE
int_dotimes_size(VALUE num, VALUE args, VALUE eobj)
{
if (FIXNUM_P(num)) {
- if (NUM2LONG(num) <= 0) return INT2FIX(0);
+ if (NUM2LONG(num) <= 0) return INT2FIX(0);
}
else {
- if (RTEST(rb_funcall(num, '<', 1, INT2FIX(0)))) return INT2FIX(0);
+ if (RTEST(rb_funcall(num, '<', 1, INT2FIX(0)))) return INT2FIX(0);
}
return num;
}
@@ -5690,21 +5689,21 @@ int_dotimes(VALUE num)
RETURN_SIZED_ENUMERATOR(num, 0, 0, int_dotimes_size);
if (FIXNUM_P(num)) {
- long i, end;
+ long i, end;
- end = FIX2LONG(num);
- for (i=0; i<end; i++) {
- rb_yield_1(LONG2FIX(i));
- }
+ end = FIX2LONG(num);
+ for (i=0; i<end; i++) {
+ rb_yield_1(LONG2FIX(i));
+ }
}
else {
- VALUE i = INT2FIX(0);
+ VALUE i = INT2FIX(0);
- for (;;) {
+ for (;;) {
if (!RTEST(int_le(i, num))) break;
- rb_yield(i);
+ rb_yield(i);
i = rb_int_plus(i, INT2FIX(1));
- }
+ }
}
return num;
}
@@ -5769,7 +5768,7 @@ int_round(int argc, VALUE* argv, VALUE num)
ndigits = NUM2INT(nd);
mode = rb_num_get_rounding_option(opt);
if (ndigits >= 0) {
- return num;
+ return num;
}
return rb_int_round(num, ndigits, mode);
}
@@ -5806,7 +5805,7 @@ int_floor(int argc, VALUE* argv, VALUE num)
if (!rb_check_arity(argc, 0, 1)) return num;
ndigits = NUM2INT(argv[0]);
if (ndigits >= 0) {
- return num;
+ return num;
}
return rb_int_floor(num, ndigits);
}
@@ -5843,7 +5842,7 @@ int_ceil(int argc, VALUE* argv, VALUE num)
if (!rb_check_arity(argc, 0, 1)) return num;
ndigits = NUM2INT(argv[0]);
if (ndigits >= 0) {
- return num;
+ return num;
}
return rb_int_ceil(num, ndigits);
}
@@ -5879,7 +5878,7 @@ int_truncate(int argc, VALUE* argv, VALUE num)
if (!rb_check_arity(argc, 0, 1)) return num;
ndigits = NUM2INT(argv[0]);
if (ndigits >= 0) {
- return num;
+ return num;
}
return rb_int_truncate(num, ndigits);
}
@@ -5889,12 +5888,12 @@ rettype \
prefix##_isqrt(argtype n) \
{ \
if (!argtype##_IN_DOUBLE_P(n)) { \
- unsigned int b = bit_length(n); \
- argtype t; \
- rettype x = (rettype)(n >> (b/2+1)); \
- x |= ((rettype)1LU << (b-1)/2); \
- while ((t = n/x) < (argtype)x) x = (rettype)((x + t) >> 1); \
- return x; \
+ unsigned int b = bit_length(n); \
+ argtype t; \
+ rettype x = (rettype)(n >> (b/2+1)); \
+ x |= ((rettype)1LU << (b-1)/2); \
+ while ((t = n/x) < (argtype)x) x = (rettype)((x + t) >> 1); \
+ return x; \
} \
return (rettype)sqrt(argtype##_TO_DOUBLE(n)); \
}
@@ -5963,29 +5962,29 @@ rb_int_s_isqrt(VALUE self, VALUE num)
unsigned long n, sq;
num = rb_to_int(num);
if (FIXNUM_P(num)) {
- if (FIXNUM_NEGATIVE_P(num)) {
- domain_error("isqrt");
- }
- n = FIX2ULONG(num);
- sq = rb_ulong_isqrt(n);
- return LONG2FIX(sq);
+ if (FIXNUM_NEGATIVE_P(num)) {
+ domain_error("isqrt");
+ }
+ n = FIX2ULONG(num);
+ sq = rb_ulong_isqrt(n);
+ return LONG2FIX(sq);
}
else {
- size_t biglen;
- if (RBIGNUM_NEGATIVE_P(num)) {
- domain_error("isqrt");
- }
- biglen = BIGNUM_LEN(num);
- if (biglen == 0) return INT2FIX(0);
+ size_t biglen;
+ if (RBIGNUM_NEGATIVE_P(num)) {
+ domain_error("isqrt");
+ }
+ biglen = BIGNUM_LEN(num);
+ if (biglen == 0) return INT2FIX(0);
#if SIZEOF_BDIGIT <= SIZEOF_LONG
- /* short-circuit */
- if (biglen == 1) {
- n = BIGNUM_DIGITS(num)[0];
- sq = rb_ulong_isqrt(n);
- return ULONG2NUM(sq);
- }
+ /* short-circuit */
+ if (biglen == 1) {
+ n = BIGNUM_DIGITS(num)[0];
+ sq = rb_ulong_isqrt(n);
+ return ULONG2NUM(sq);
+ }
#endif
- return rb_big_isqrt(num);
+ return rb_big_isqrt(num);
}
}
diff --git a/numeric.rb b/numeric.rb
index 9f2200d2a8..f026679210 100644
--- a/numeric.rb
+++ b/numeric.rb
@@ -6,7 +6,17 @@ class Numeric
# Returns +true+ if +num+ is a real number (i.e. not Complex).
#
def real?
- return true
+ true
+ end
+
+ #
+ # call-seq:
+ # num.real -> self
+ #
+ # Returns self.
+ #
+ def real
+ self
end
#
@@ -19,7 +29,7 @@ class Numeric
# 1.integer? #=> true
#
def integer?
- return false
+ false
end
#
@@ -29,7 +39,7 @@ class Numeric
# Returns +true+ if +num+ is a finite number, otherwise returns +false+.
#
def finite?
- return true
+ true
end
#
@@ -40,8 +50,34 @@ class Numeric
# finite, <code>-Infinity</code>, or <code>+Infinity</code>.
#
def infinite?
- return nil
+ nil
+ end
+
+ #
+ # call-seq:
+ # num.imag -> 0
+ # num.imaginary -> 0
+ #
+ # Returns zero.
+ #
+ def imaginary
+ 0
+ end
+
+ alias imag imaginary
+
+ #
+ # call-seq:
+ # num.conj -> self
+ # num.conjugate -> self
+ #
+ # Returns self.
+ #
+ def conjugate
+ self
end
+
+ alias conj conjugate
end
class Integer
@@ -146,7 +182,7 @@ class Integer
#
# Since +int+ is already an Integer, this always returns +true+.
def integer?
- return true
+ true
end
alias magnitude abs
@@ -178,7 +214,7 @@ class Integer
#
# For example, <code>?a.ord</code> returns 97 both in 1.8 and 1.9.
def ord
- return self
+ self
end
#
@@ -208,7 +244,7 @@ class Integer
#
# #to_int is an alias for #to_i.
def to_i
- return self
+ self
end
# call-seq:
@@ -216,7 +252,7 @@ class Integer
#
# Since +int+ is already an Integer, returns +self+.
def to_int
- return self
+ self
end
# call-seq:
@@ -227,6 +263,43 @@ class Integer
Primitive.attr! 'inline'
Primitive.cexpr! 'rb_int_zero_p(self)'
end
+
+ # call-seq:
+ # ceildiv(other) -> integer
+ #
+ # Returns the result of division +self+ by +other+. The result is rounded up to the nearest integer.
+ #
+ # 3.ceildiv(3) # => 1
+ # 4.ceildiv(3) # => 2
+ #
+ # 4.ceildiv(-3) # => -1
+ # -4.ceildiv(3) # => -1
+ # -4.ceildiv(-3) # => 2
+ #
+ # 3.ceildiv(1.2) # => 3
+ def ceildiv(other)
+ -div(-other)
+ end
+
+ #
+ # call-seq:
+ # int.numerator -> self
+ #
+ # Returns self.
+ #
+ def numerator
+ self
+ end
+
+ #
+ # call-seq:
+ # int.denominator -> 1
+ #
+ # Returns 1.
+ #
+ def denominator
+ 1
+ end
end
# call-seq:
@@ -259,7 +332,7 @@ class Float
# Since +float+ is already a Float, returns +self+.
#
def to_f
- return self
+ self
end
#
diff --git a/object.c b/object.c
index 4baf7ffc56..e1fc72c89f 100644
--- a/object.c
+++ b/object.c
@@ -33,12 +33,14 @@
#include "internal/string.h"
#include "internal/symbol.h"
#include "internal/variable.h"
+#include "variable.h"
#include "probes.h"
#include "ruby/encoding.h"
#include "ruby/st.h"
#include "ruby/util.h"
#include "ruby/assert.h"
#include "builtin.h"
+#include "shape.h"
/*!
* \addtogroup object
@@ -82,7 +84,7 @@ VALUE
rb_obj_hide(VALUE obj)
{
if (!SPECIAL_CONST_P(obj)) {
- RBASIC_CLEAR_CLASS(obj);
+ RBASIC_CLEAR_CLASS(obj);
}
return obj;
}
@@ -91,7 +93,7 @@ VALUE
rb_obj_reveal(VALUE obj, VALUE klass)
{
if (!SPECIAL_CONST_P(obj)) {
- RBASIC_SET_CLASS(obj, klass);
+ RBASIC_SET_CLASS(obj, klass);
}
return obj;
}
@@ -99,7 +101,8 @@ rb_obj_reveal(VALUE obj, VALUE klass)
VALUE
rb_obj_setup(VALUE obj, VALUE klass, VALUE type)
{
- RBASIC(obj)->flags = type;
+ VALUE ignored_flags = RUBY_FL_PROMOTED | RUBY_FL_SEEN_OBJ_ID;
+ RBASIC(obj)->flags = (type & ~ignored_flags) | (RBASIC(obj)->flags & ignored_flags);
RBASIC_SET_CLASS(obj, klass);
return obj;
}
@@ -123,8 +126,8 @@ rb_equal(VALUE obj1, VALUE obj2)
if (obj1 == obj2) return Qtrue;
result = rb_equal_opt(obj1, obj2);
- if (result == Qundef) {
- result = rb_funcall(obj1, id_eq, 1, obj2);
+ if (UNDEF_P(result)) {
+ result = rb_funcall(obj1, id_eq, 1, obj2);
}
return RBOOL(RTEST(result));
}
@@ -134,12 +137,12 @@ rb_eql(VALUE obj1, VALUE obj2)
{
VALUE result;
- if (obj1 == obj2) return Qtrue;
+ if (obj1 == obj2) return TRUE;
result = rb_eql_opt(obj1, obj2);
- if (result == Qundef) {
- result = rb_funcall(obj1, id_eql, 1, obj2);
+ if (UNDEF_P(result)) {
+ result = rb_funcall(obj1, id_eql, 1, obj2);
}
- return RBOOL(RTEST(result));
+ return RTEST(result);
}
/**
@@ -229,7 +232,7 @@ rb_class_real(VALUE cl)
{
while (cl &&
((RBASIC(cl)->flags & FL_SINGLETON) || BUILTIN_TYPE(cl) == T_ICLASS)) {
- cl = RCLASS_SUPER(cl);
+ cl = RCLASS_SUPER(cl);
}
return cl;
}
@@ -267,13 +270,61 @@ rb_obj_singleton_class(VALUE obj)
MJIT_FUNC_EXPORTED void
rb_obj_copy_ivar(VALUE dest, VALUE obj)
{
- VALUE *dest_buf = ROBJECT_IVPTR(dest);
- VALUE *src_buf = ROBJECT_IVPTR(obj);
- uint32_t dest_len = ROBJECT_NUMIV(dest);
- uint32_t src_len = ROBJECT_NUMIV(obj);
- uint32_t len = dest_len < src_len ? dest_len : src_len;
+ RUBY_ASSERT(!RB_TYPE_P(obj, T_CLASS) && !RB_TYPE_P(obj, T_MODULE));
+
+ RUBY_ASSERT(BUILTIN_TYPE(dest) == BUILTIN_TYPE(obj));
+ rb_shape_t * src_shape = rb_shape_get_shape(obj);
+
+ if (rb_shape_id(src_shape) == OBJ_TOO_COMPLEX_SHAPE_ID) {
+ st_table *table = st_copy(ROBJECT_IV_HASH(obj));
+
+ rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table);
+ rb_shape_set_too_complex(dest);
+
+ ROBJECT(dest)->as.heap.ivptr = (VALUE *)table;
+
+ return;
+ }
+
+ uint32_t src_num_ivs = RBASIC_IV_COUNT(obj);
+ rb_shape_t * shape_to_set_on_dest = src_shape;
+ VALUE * src_buf;
+ VALUE * dest_buf;
+
+ if (!src_num_ivs) {
+ return;
+ }
+
+ // The copy should be mutable, so we don't want the frozen shape
+ if (rb_shape_frozen_shape_p(src_shape)) {
+ shape_to_set_on_dest = rb_shape_get_parent(src_shape);
+ }
+
+ src_buf = ROBJECT_IVPTR(obj);
+ dest_buf = ROBJECT_IVPTR(dest);
+
+ rb_shape_t * initial_shape = rb_shape_get_shape(dest);
+
+ if (initial_shape->size_pool_index != src_shape->size_pool_index) {
+ RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
+
+ shape_to_set_on_dest = rb_shape_rebuild_shape(initial_shape, src_shape);
+ }
+
+ RUBY_ASSERT(src_num_ivs <= shape_to_set_on_dest->capacity);
+ if (initial_shape->capacity < shape_to_set_on_dest->capacity) {
+ rb_ensure_iv_list_size(dest, initial_shape->capacity, shape_to_set_on_dest->capacity);
+ dest_buf = ROBJECT_IVPTR(dest);
+ }
+
+ MEMCPY(dest_buf, src_buf, VALUE, src_num_ivs);
+
+ // Fire write barriers
+ for (uint32_t i = 0; i < src_num_ivs; i++) {
+ RB_OBJ_WRITTEN(dest, Qundef, dest_buf[i]);
+ }
- MEMCPY(dest_buf, src_buf, VALUE, len);
+ rb_shape_set_shape(dest, shape_to_set_on_dest);
}
static void
@@ -283,12 +334,14 @@ init_copy(VALUE dest, VALUE obj)
rb_raise(rb_eTypeError, "[bug] frozen object (%s) allocated", rb_obj_classname(dest));
}
RBASIC(dest)->flags &= ~(T_MASK|FL_EXIVAR);
+ // Copies the shape id from obj to dest
RBASIC(dest)->flags |= RBASIC(obj)->flags & (T_MASK|FL_EXIVAR);
rb_copy_wb_protected_attribute(dest, obj);
rb_copy_generic_ivar(dest, obj);
rb_gc_copy_finalizer(dest, obj);
+
if (RB_TYPE_P(obj, T_OBJECT)) {
- rb_obj_copy_ivar(dest, obj);
+ rb_obj_copy_ivar(dest, obj);
}
}
@@ -305,10 +358,10 @@ special_object_p(VALUE obj)
case T_SYMBOL:
case T_RATIONAL:
case T_COMPLEX:
- /* not a comprehensive list */
- return TRUE;
+ /* not a comprehensive list */
+ return TRUE;
default:
- return FALSE;
+ return FALSE;
}
}
@@ -332,7 +385,7 @@ rb_obj_clone2(rb_execution_context_t *ec, VALUE obj, VALUE freeze)
{
VALUE kwfreeze = obj_freeze_opt(freeze);
if (!special_object_p(obj))
- return mutable_obj_clone(obj, kwfreeze);
+ return mutable_obj_clone(obj, kwfreeze);
return immutable_obj_clone(obj, kwfreeze);
}
@@ -352,12 +405,12 @@ rb_get_freeze_opt(int argc, VALUE *argv)
VALUE kwfreeze = Qnil;
if (!keyword_ids[0]) {
- CONST_ID(keyword_ids[0], "freeze");
+ CONST_ID(keyword_ids[0], "freeze");
}
rb_scan_args(argc, argv, "0:", &opt);
if (!NIL_P(opt)) {
- rb_get_kwargs(opt, keyword_ids, 0, 1, &kwfreeze);
- if (kwfreeze != Qundef)
+ rb_get_kwargs(opt, keyword_ids, 0, 1, &kwfreeze);
+ if (!UNDEF_P(kwfreeze))
kwfreeze = obj_freeze_opt(kwfreeze);
}
return kwfreeze;
@@ -367,8 +420,8 @@ static VALUE
immutable_obj_clone(VALUE obj, VALUE kwfreeze)
{
if (kwfreeze == Qfalse)
- rb_raise(rb_eArgError, "can't unfreeze %"PRIsVALUE,
- rb_obj_class(obj));
+ rb_raise(rb_eArgError, "can't unfreeze %"PRIsVALUE,
+ rb_obj_class(obj));
return obj;
}
@@ -383,7 +436,7 @@ mutable_obj_clone(VALUE obj, VALUE kwfreeze)
singleton = rb_singleton_class_clone_and_attach(obj, clone);
RBASIC_SET_CLASS(clone, singleton);
if (FL_TEST(singleton, FL_SINGLETON)) {
- rb_singleton_class_attached(singleton, clone);
+ rb_singleton_class_attached(singleton, clone);
}
init_copy(clone, obj);
@@ -391,7 +444,10 @@ mutable_obj_clone(VALUE obj, VALUE kwfreeze)
switch (kwfreeze) {
case Qnil:
rb_funcall(clone, id_init_clone, 1, obj);
- RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE;
+ RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE;
+ if (RB_OBJ_FROZEN(obj)) {
+ rb_shape_transition_shape_frozen(clone);
+ }
break;
case Qtrue:
{
@@ -407,6 +463,7 @@ mutable_obj_clone(VALUE obj, VALUE kwfreeze)
argv[1] = freeze_true_hash;
rb_funcallv_kw(clone, id_init_clone, 2, argv, RB_PASS_KEYWORDS);
RBASIC(clone)->flags |= FL_FREEZE;
+ rb_shape_transition_shape_frozen(clone);
break;
}
case Qfalse:
@@ -483,7 +540,7 @@ rb_obj_dup(VALUE obj)
VALUE dup;
if (special_object_p(obj)) {
- return obj;
+ return obj;
}
dup = rb_obj_alloc(rb_obj_class(obj));
init_copy(dup, obj);
@@ -535,7 +592,7 @@ rb_obj_init_copy(VALUE obj, VALUE orig)
if (obj == orig) return obj;
rb_check_frozen(obj);
if (TYPE(obj) != TYPE(orig) || rb_obj_class(obj) != rb_obj_class(orig)) {
- rb_raise(rb_eTypeError, "initialize_copy should take same class object");
+ rb_raise(rb_eTypeError, "initialize_copy should take same class object");
}
return obj;
}
@@ -607,12 +664,12 @@ rb_inspect(VALUE obj)
rb_encoding *enc = rb_default_internal_encoding();
if (enc == NULL) enc = rb_default_external_encoding();
if (!rb_enc_asciicompat(enc)) {
- if (!rb_enc_str_asciionly_p(str))
- return rb_str_escape(str);
- return str;
+ if (!rb_enc_str_asciionly_p(str))
+ return rb_str_escape(str);
+ return str;
}
if (rb_enc_get(str) != enc && !rb_enc_str_asciionly_p(str))
- return rb_str_escape(str);
+ return rb_str_escape(str);
return str;
}
@@ -627,14 +684,14 @@ inspect_i(st_data_t k, st_data_t v, st_data_t a)
if (CLASS_OF(value) == 0) return ST_CONTINUE;
if (!rb_is_instance_id(id)) return ST_CONTINUE;
if (RSTRING_PTR(str)[0] == '-') { /* first element */
- RSTRING_PTR(str)[0] = '#';
- rb_str_cat2(str, " ");
+ RSTRING_PTR(str)[0] = '#';
+ rb_str_cat2(str, " ");
}
else {
- rb_str_cat2(str, ", ");
+ rb_str_cat2(str, ", ");
}
- rb_str_catf(str, "%"PRIsVALUE"=%+"PRIsVALUE,
- rb_id2str(id), value);
+ rb_str_catf(str, "%"PRIsVALUE"=", rb_id2str(id));
+ rb_str_buf_append(str, rb_inspect(value));
return ST_CONTINUE;
}
@@ -643,10 +700,10 @@ static VALUE
inspect_obj(VALUE obj, VALUE str, int recur)
{
if (recur) {
- rb_str_cat2(str, " ...");
+ rb_str_cat2(str, " ...");
}
else {
- rb_ivar_foreach(obj, inspect_i, str);
+ rb_ivar_foreach(obj, inspect_i, str);
}
rb_str_cat2(str, ">");
RSTRING_PTR(str)[0] = '#';
@@ -685,14 +742,14 @@ static VALUE
rb_obj_inspect(VALUE obj)
{
if (rb_ivar_count(obj) > 0) {
- VALUE str;
- VALUE c = rb_class_name(CLASS_OF(obj));
+ VALUE str;
+ VALUE c = rb_class_name(CLASS_OF(obj));
- str = rb_sprintf("-<%"PRIsVALUE":%p", c, (void*)obj);
- return rb_exec_recursive(inspect_obj, obj, str);
+ str = rb_sprintf("-<%"PRIsVALUE":%p", c, (void*)obj);
+ return rb_exec_recursive(inspect_obj, obj, str);
}
else {
- return rb_any_to_s(obj);
+ return rb_any_to_s(obj);
}
}
@@ -703,10 +760,10 @@ class_or_module_required(VALUE c)
case T_MODULE:
case T_CLASS:
case T_ICLASS:
- break;
+ break;
default:
- rb_raise(rb_eTypeError, "class or module required");
+ rb_raise(rb_eTypeError, "class or module required");
}
return c;
}
@@ -1185,10 +1242,10 @@ VALUE
rb_obj_freeze(VALUE obj)
{
if (!OBJ_FROZEN(obj)) {
- OBJ_FREEZE(obj);
- if (SPECIAL_CONST_P(obj)) {
- rb_bug("special consts should be frozen.");
- }
+ OBJ_FREEZE(obj);
+ if (SPECIAL_CONST_P(obj)) {
+ rb_bug("special consts should be frozen.");
+ }
}
return obj;
}
@@ -1490,7 +1547,7 @@ static VALUE
rb_obj_cmp(VALUE obj1, VALUE obj2)
{
if (rb_equal(obj1, obj2))
- return INT2FIX(0);
+ return INT2FIX(0);
return Qnil;
}
@@ -1538,30 +1595,30 @@ rb_mod_to_s(VALUE klass)
VALUE refined_class, defined_at;
if (FL_TEST(klass, FL_SINGLETON)) {
- VALUE s = rb_usascii_str_new2("#<Class:");
- VALUE v = rb_ivar_get(klass, id__attached__);
-
- if (CLASS_OR_MODULE_P(v)) {
- rb_str_append(s, rb_inspect(v));
- }
- else {
- rb_str_append(s, rb_any_to_s(v));
- }
- rb_str_cat2(s, ">");
-
- return s;
+ VALUE s = rb_usascii_str_new2("#<Class:");
+ VALUE v = rb_ivar_get(klass, id__attached__);
+
+ if (CLASS_OR_MODULE_P(v)) {
+ rb_str_append(s, rb_inspect(v));
+ }
+ else {
+ rb_str_append(s, rb_any_to_s(v));
+ }
+ rb_str_cat2(s, ">");
+
+ return s;
}
refined_class = rb_refinement_module_get_refined_class(klass);
if (!NIL_P(refined_class)) {
- VALUE s = rb_usascii_str_new2("#<refinement:");
-
- rb_str_concat(s, rb_inspect(refined_class));
- rb_str_cat2(s, "@");
- CONST_ID(id_defined_at, "__defined_at__");
- defined_at = rb_attr_get(klass, id_defined_at);
- rb_str_concat(s, rb_inspect(defined_at));
- rb_str_cat2(s, ">");
- return s;
+ VALUE s = rb_usascii_str_new2("#<refinement:");
+
+ rb_str_concat(s, rb_inspect(refined_class));
+ rb_str_cat2(s, "@");
+ CONST_ID(id_defined_at, "__defined_at__");
+ defined_at = rb_attr_get(klass, id_defined_at);
+ rb_str_concat(s, rb_inspect(defined_at));
+ rb_str_cat2(s, ">");
+ return s;
}
return rb_class_name(klass);
}
@@ -1623,16 +1680,19 @@ rb_class_inherited_p(VALUE mod, VALUE arg)
return RCLASS_SUPERCLASSES(mod)[arg_depth] == arg ?
Qtrue :
Qnil;
- } else if (arg_depth > mod_depth) {
+ }
+ else if (arg_depth > mod_depth) {
// check if mod > arg
return RCLASS_SUPERCLASSES(arg)[mod_depth] == mod ?
Qfalse :
Qnil;
- } else {
+ }
+ else {
// Depths match, and we know they aren't equal: no relation
return Qnil;
}
- } else {
+ }
+ else {
if (!CLASS_OR_MODULE_P(arg) && !RB_TYPE_P(arg, T_ICLASS)) {
rb_raise(rb_eTypeError, "compared with non class/module");
}
@@ -1684,7 +1744,7 @@ static VALUE
rb_mod_ge(VALUE mod, VALUE arg)
{
if (!CLASS_OR_MODULE_P(arg)) {
- rb_raise(rb_eTypeError, "compared with non class/module");
+ rb_raise(rb_eTypeError, "compared with non class/module");
}
return rb_class_inherited_p(arg, mod);
@@ -1729,13 +1789,13 @@ rb_mod_cmp(VALUE mod, VALUE arg)
if (mod == arg) return INT2FIX(0);
if (!CLASS_OR_MODULE_P(arg)) {
- return Qnil;
+ return Qnil;
}
cmp = rb_class_inherited_p(mod, arg);
if (NIL_P(cmp)) return Qnil;
if (cmp) {
- return INT2FIX(-1);
+ return INT2FIX(-1);
}
return INT2FIX(1);
}
@@ -1778,7 +1838,7 @@ static VALUE
rb_mod_initialize_exec(VALUE module)
{
if (rb_block_given_p()) {
- rb_mod_module_exec(1, &module, module);
+ rb_mod_module_exec(1, &module, module);
}
return Qnil;
}
@@ -1831,17 +1891,17 @@ rb_class_initialize(int argc, VALUE *argv, VALUE klass)
VALUE super;
if (RCLASS_SUPER(klass) != 0 || klass == rb_cBasicObject) {
- rb_raise(rb_eTypeError, "already initialized class");
+ rb_raise(rb_eTypeError, "already initialized class");
}
if (rb_check_arity(argc, 0, 1) == 0) {
- super = rb_cObject;
+ super = rb_cObject;
}
else {
super = argv[0];
- rb_check_inheritable(super);
- if (super != rb_cBasicObject && !RCLASS_SUPER(super)) {
- rb_raise(rb_eTypeError, "can't inherit uninitialized class");
- }
+ rb_check_inheritable(super);
+ if (super != rb_cBasicObject && !RCLASS_SUPER(super)) {
+ rb_raise(rb_eTypeError, "can't inherit uninitialized class");
+ }
}
RCLASS_SET_SUPER(klass, super);
rb_make_metaclass(klass, RBASIC(super)->klass);
@@ -1856,7 +1916,7 @@ void
rb_undefined_alloc(VALUE klass)
{
rb_raise(rb_eTypeError, "allocator undefined for %"PRIsVALUE,
- klass);
+ klass);
}
static rb_alloc_func_t class_get_alloc_func(VALUE klass);
@@ -1908,14 +1968,14 @@ class_get_alloc_func(VALUE klass)
rb_alloc_func_t allocator;
if (RCLASS_SUPER(klass) == 0 && klass != rb_cBasicObject) {
- rb_raise(rb_eTypeError, "can't instantiate uninitialized class");
+ rb_raise(rb_eTypeError, "can't instantiate uninitialized class");
}
if (FL_TEST(klass, FL_SINGLETON)) {
- rb_raise(rb_eTypeError, "can't create instance of singleton class");
+ rb_raise(rb_eTypeError, "can't create instance of singleton class");
}
allocator = rb_get_alloc_func(klass);
if (!allocator) {
- rb_undefined_alloc(klass);
+ rb_undefined_alloc(klass);
}
return allocator;
}
@@ -1930,7 +1990,7 @@ class_call_alloc_func(rb_alloc_func_t allocator, VALUE klass)
obj = (*allocator)(klass);
if (rb_obj_class(obj) != rb_class_real(klass)) {
- rb_raise(rb_eTypeError, "wrong instance allocation");
+ rb_raise(rb_eTypeError, "wrong instance allocation");
}
return obj;
}
@@ -1979,13 +2039,7 @@ rb_class_new_instance_kw(int argc, const VALUE *argv, VALUE klass, int kw_splat)
VALUE
rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
{
- VALUE obj;
- Check_Type(klass, T_CLASS);
-
- obj = rb_class_alloc(klass);
- rb_obj_call_init_kw(obj, argc, argv, RB_NO_KEYWORDS);
-
- return obj;
+ return rb_class_new_instance_kw(argc, argv, klass, RB_NO_KEYWORDS);
}
/**
@@ -2023,9 +2077,14 @@ rb_class_superclass(VALUE klass)
VALUE super = RCLASS_SUPER(klass);
if (!super) {
- if (klass == rb_cBasicObject) return Qnil;
- rb_raise(rb_eTypeError, "uninitialized class");
- } else {
+ if (klass == rb_cBasicObject) return Qnil;
+ rb_raise(rb_eTypeError, "uninitialized class");
+ }
+
+ if (!RCLASS_SUPERCLASS_DEPTH(klass)) {
+ return Qnil;
+ }
+ else {
super = RCLASS_SUPERCLASSES(klass)[RCLASS_SUPERCLASS_DEPTH(klass) - 1];
RUBY_ASSERT(RB_TYPE_P(klass, T_CLASS));
return super;
@@ -2051,15 +2110,15 @@ static const char bad_attr_name[] = "invalid attribute name `%1$s'";
check_setter_id(obj, &(name), rb_is_##type##_id, rb_is_##type##_name, message, strlen(message))
static ID
check_setter_id(VALUE obj, VALUE *pname,
- int (*valid_id_p)(ID), int (*valid_name_p)(VALUE),
- const char *message, size_t message_len)
+ int (*valid_id_p)(ID), int (*valid_name_p)(VALUE),
+ const char *message, size_t message_len)
{
ID id = rb_check_id(pname);
VALUE name = *pname;
if (id ? !valid_id_p(id) : !valid_name_p(name)) {
- rb_name_err_raise_str(rb_fstring_new(message, message_len),
- obj, name);
+ rb_name_err_raise_str(rb_fstring_new(message, message_len),
+ obj, name);
}
return id;
}
@@ -2105,9 +2164,9 @@ rb_mod_attr_reader(int argc, VALUE *argv, VALUE klass)
VALUE names = rb_ary_new2(argc);
for (i=0; i<argc; i++) {
- ID id = id_for_attr(klass, argv[i]);
- rb_attr(klass, id, TRUE, FALSE, TRUE);
- rb_ary_push(names, ID2SYM(id));
+ ID id = id_for_attr(klass, argv[i]);
+ rb_attr(klass, id, TRUE, FALSE, TRUE);
+ rb_ary_push(names, ID2SYM(id));
}
return names;
}
@@ -2131,14 +2190,14 @@ VALUE
rb_mod_attr(int argc, VALUE *argv, VALUE klass)
{
if (argc == 2 && (argv[1] == Qtrue || argv[1] == Qfalse)) {
- ID id = id_for_attr(klass, argv[0]);
- VALUE names = rb_ary_new();
-
- rb_category_warning(RB_WARN_CATEGORY_DEPRECATED, "optional boolean argument is obsoleted");
- rb_attr(klass, id, 1, RTEST(argv[1]), TRUE);
- rb_ary_push(names, ID2SYM(id));
- if (argv[1] == Qtrue) rb_ary_push(names, ID2SYM(rb_id_attrset(id)));
- return names;
+ ID id = id_for_attr(klass, argv[0]);
+ VALUE names = rb_ary_new();
+
+ rb_category_warning(RB_WARN_CATEGORY_DEPRECATED, "optional boolean argument is obsoleted");
+ rb_attr(klass, id, 1, RTEST(argv[1]), TRUE);
+ rb_ary_push(names, ID2SYM(id));
+ if (argv[1] == Qtrue) rb_ary_push(names, ID2SYM(rb_id_attrset(id)));
+ return names;
}
return rb_mod_attr_reader(argc, argv, klass);
}
@@ -2161,9 +2220,9 @@ rb_mod_attr_writer(int argc, VALUE *argv, VALUE klass)
VALUE names = rb_ary_new2(argc);
for (i=0; i<argc; i++) {
- ID id = id_for_attr(klass, argv[i]);
- rb_attr(klass, id, FALSE, TRUE, TRUE);
- rb_ary_push(names, ID2SYM(rb_id_attrset(id)));
+ ID id = id_for_attr(klass, argv[i]);
+ rb_attr(klass, id, FALSE, TRUE, TRUE);
+ rb_ary_push(names, ID2SYM(rb_id_attrset(id)));
}
return names;
}
@@ -2193,11 +2252,11 @@ rb_mod_attr_accessor(int argc, VALUE *argv, VALUE klass)
VALUE names = rb_ary_new2(argc * 2);
for (i=0; i<argc; i++) {
- ID id = id_for_attr(klass, argv[i]);
+ ID id = id_for_attr(klass, argv[i]);
- rb_attr(klass, id, TRUE, TRUE, TRUE);
- rb_ary_push(names, ID2SYM(id));
- rb_ary_push(names, ID2SYM(rb_id_attrset(id)));
+ rb_attr(klass, id, TRUE, TRUE, TRUE);
+ rb_ary_push(names, ID2SYM(id));
+ rb_ary_push(names, ID2SYM(rb_id_attrset(id)));
}
return names;
}
@@ -2255,17 +2314,17 @@ rb_mod_const_get(int argc, VALUE *argv, VALUE mod)
recur = (argc == 1) ? Qtrue : argv[1];
if (SYMBOL_P(name)) {
- if (!rb_is_const_sym(name)) goto wrong_name;
- id = rb_check_id(&name);
- if (!id) return rb_const_missing(mod, name);
- return RTEST(recur) ? rb_const_get(mod, id) : rb_const_get_at(mod, id);
+ if (!rb_is_const_sym(name)) goto wrong_name;
+ id = rb_check_id(&name);
+ if (!id) return rb_const_missing(mod, name);
+ return RTEST(recur) ? rb_const_get(mod, id) : rb_const_get_at(mod, id);
}
path = StringValuePtr(name);
enc = rb_enc_get(name);
if (!rb_enc_asciicompat(enc)) {
- rb_raise(rb_eArgError, "invalid class path encoding (non ASCII)");
+ rb_raise(rb_eArgError, "invalid class path encoding (non ASCII)");
}
pbeg = p = path;
@@ -2276,53 +2335,53 @@ rb_mod_const_get(int argc, VALUE *argv, VALUE mod)
}
if (p + 2 < pend && p[0] == ':' && p[1] == ':') {
- mod = rb_cObject;
- p += 2;
- pbeg = p;
+ mod = rb_cObject;
+ p += 2;
+ pbeg = p;
}
while (p < pend) {
- VALUE part;
- long len, beglen;
-
- while (p < pend && *p != ':') p++;
-
- if (pbeg == p) goto wrong_name;
-
- id = rb_check_id_cstr(pbeg, len = p-pbeg, enc);
- beglen = pbeg-path;
-
- if (p < pend && p[0] == ':') {
- if (p + 2 >= pend || p[1] != ':') goto wrong_name;
- p += 2;
- pbeg = p;
- }
-
- if (!RB_TYPE_P(mod, T_MODULE) && !RB_TYPE_P(mod, T_CLASS)) {
- rb_raise(rb_eTypeError, "%"PRIsVALUE" does not refer to class/module",
- QUOTE(name));
- }
-
- if (!id) {
- part = rb_str_subseq(name, beglen, len);
- OBJ_FREEZE(part);
- if (!rb_is_const_name(part)) {
- name = part;
- goto wrong_name;
- }
- else if (!rb_method_basic_definition_p(CLASS_OF(mod), id_const_missing)) {
- part = rb_str_intern(part);
- mod = rb_const_missing(mod, part);
- continue;
- }
- else {
- rb_mod_const_missing(mod, part);
- }
- }
- if (!rb_is_const_id(id)) {
- name = ID2SYM(id);
- goto wrong_name;
- }
+ VALUE part;
+ long len, beglen;
+
+ while (p < pend && *p != ':') p++;
+
+ if (pbeg == p) goto wrong_name;
+
+ id = rb_check_id_cstr(pbeg, len = p-pbeg, enc);
+ beglen = pbeg-path;
+
+ if (p < pend && p[0] == ':') {
+ if (p + 2 >= pend || p[1] != ':') goto wrong_name;
+ p += 2;
+ pbeg = p;
+ }
+
+ if (!RB_TYPE_P(mod, T_MODULE) && !RB_TYPE_P(mod, T_CLASS)) {
+ rb_raise(rb_eTypeError, "%"PRIsVALUE" does not refer to class/module",
+ QUOTE(name));
+ }
+
+ if (!id) {
+ part = rb_str_subseq(name, beglen, len);
+ OBJ_FREEZE(part);
+ if (!rb_is_const_name(part)) {
+ name = part;
+ goto wrong_name;
+ }
+ else if (!rb_method_basic_definition_p(CLASS_OF(mod), id_const_missing)) {
+ part = rb_str_intern(part);
+ mod = rb_const_missing(mod, part);
+ continue;
+ }
+ else {
+ rb_mod_const_missing(mod, part);
+ }
+ }
+ if (!rb_is_const_id(id)) {
+ name = ID2SYM(id);
+ goto wrong_name;
+ }
#if 0
mod = rb_const_get_0(mod, id, beglen > 0 || !RTEST(recur), RTEST(recur), FALSE);
#else
@@ -2428,17 +2487,17 @@ rb_mod_const_defined(int argc, VALUE *argv, VALUE mod)
recur = (argc == 1) ? Qtrue : argv[1];
if (SYMBOL_P(name)) {
- if (!rb_is_const_sym(name)) goto wrong_name;
- id = rb_check_id(&name);
- if (!id) return Qfalse;
- return RTEST(recur) ? rb_const_defined(mod, id) : rb_const_defined_at(mod, id);
+ if (!rb_is_const_sym(name)) goto wrong_name;
+ id = rb_check_id(&name);
+ if (!id) return Qfalse;
+ return RTEST(recur) ? rb_const_defined(mod, id) : rb_const_defined_at(mod, id);
}
path = StringValuePtr(name);
enc = rb_enc_get(name);
if (!rb_enc_asciicompat(enc)) {
- rb_raise(rb_eArgError, "invalid class path encoding (non ASCII)");
+ rb_raise(rb_eArgError, "invalid class path encoding (non ASCII)");
}
pbeg = p = path;
@@ -2449,54 +2508,54 @@ rb_mod_const_defined(int argc, VALUE *argv, VALUE mod)
}
if (p + 2 < pend && p[0] == ':' && p[1] == ':') {
- mod = rb_cObject;
- p += 2;
- pbeg = p;
+ mod = rb_cObject;
+ p += 2;
+ pbeg = p;
}
while (p < pend) {
- VALUE part;
- long len, beglen;
-
- while (p < pend && *p != ':') p++;
-
- if (pbeg == p) goto wrong_name;
-
- id = rb_check_id_cstr(pbeg, len = p-pbeg, enc);
- beglen = pbeg-path;
-
- if (p < pend && p[0] == ':') {
- if (p + 2 >= pend || p[1] != ':') goto wrong_name;
- p += 2;
- pbeg = p;
- }
-
- if (!id) {
- part = rb_str_subseq(name, beglen, len);
- OBJ_FREEZE(part);
- if (!rb_is_const_name(part)) {
- name = part;
- goto wrong_name;
- }
- else {
- return Qfalse;
- }
- }
- if (!rb_is_const_id(id)) {
- name = ID2SYM(id);
- goto wrong_name;
- }
+ VALUE part;
+ long len, beglen;
+
+ while (p < pend && *p != ':') p++;
+
+ if (pbeg == p) goto wrong_name;
+
+ id = rb_check_id_cstr(pbeg, len = p-pbeg, enc);
+ beglen = pbeg-path;
+
+ if (p < pend && p[0] == ':') {
+ if (p + 2 >= pend || p[1] != ':') goto wrong_name;
+ p += 2;
+ pbeg = p;
+ }
+
+ if (!id) {
+ part = rb_str_subseq(name, beglen, len);
+ OBJ_FREEZE(part);
+ if (!rb_is_const_name(part)) {
+ name = part;
+ goto wrong_name;
+ }
+ else {
+ return Qfalse;
+ }
+ }
+ if (!rb_is_const_id(id)) {
+ name = ID2SYM(id);
+ goto wrong_name;
+ }
#if 0
mod = rb_const_search(mod, id, beglen > 0 || !RTEST(recur), RTEST(recur), FALSE);
- if (mod == Qundef) return Qfalse;
+ if (UNDEF_P(mod)) return Qfalse;
#else
if (!RTEST(recur)) {
- if (!rb_const_defined_at(mod, id))
- return Qfalse;
+ if (!rb_const_defined_at(mod, id))
+ return Qfalse;
if (p == pend) return Qtrue;
- mod = rb_const_get_at(mod, id);
- }
+ mod = rb_const_get_at(mod, id);
+ }
else if (beglen == 0) {
if (!rb_const_defined(mod, id))
return Qfalse;
@@ -2511,10 +2570,10 @@ rb_mod_const_defined(int argc, VALUE *argv, VALUE mod)
}
#endif
- if (p < pend && !RB_TYPE_P(mod, T_MODULE) && !RB_TYPE_P(mod, T_CLASS)) {
- rb_raise(rb_eTypeError, "%"PRIsVALUE" does not refer to class/module",
- QUOTE(name));
- }
+ if (p < pend && !RB_TYPE_P(mod, T_MODULE) && !RB_TYPE_P(mod, T_CLASS)) {
+ rb_raise(rb_eTypeError, "%"PRIsVALUE" does not refer to class/module",
+ QUOTE(name));
+ }
}
return Qtrue;
@@ -2705,7 +2764,7 @@ rb_obj_ivar_get(VALUE obj, VALUE iv)
ID id = id_for_var(obj, iv, instance);
if (!id) {
- return Qnil;
+ return Qnil;
}
return rb_ivar_get(obj, id);
}
@@ -2734,7 +2793,7 @@ rb_obj_ivar_get(VALUE obj, VALUE iv)
*/
static VALUE
-rb_obj_ivar_set(VALUE obj, VALUE iv, VALUE val)
+rb_obj_ivar_set_m(VALUE obj, VALUE iv, VALUE val)
{
ID id = id_for_var(obj, iv, instance);
if (!id) id = rb_intern_str(iv);
@@ -2767,7 +2826,7 @@ rb_obj_ivar_defined(VALUE obj, VALUE iv)
ID id = id_for_var(obj, iv, instance);
if (!id) {
- return Qfalse;
+ return Qfalse;
}
return rb_ivar_defined(obj, id);
}
@@ -2794,8 +2853,8 @@ rb_mod_cvar_get(VALUE obj, VALUE iv)
ID id = id_for_var(obj, iv, class);
if (!id) {
- rb_name_err_raise("uninitialized class variable %1$s in %2$s",
- obj, iv);
+ rb_name_err_raise("uninitialized class variable %1$s in %2$s",
+ obj, iv);
}
return rb_cvar_get(obj, id);
}
@@ -2851,7 +2910,7 @@ rb_mod_cvar_defined(VALUE obj, VALUE iv)
ID id = id_for_var(obj, iv, class);
if (!id) {
- return Qfalse;
+ return Qfalse;
}
return rb_cvar_defined(obj, id);
}
@@ -2903,14 +2962,14 @@ conv_method_index(const char *method)
static const char prefix[] = "to_";
if (strncmp(prefix, method, sizeof(prefix)-1) == 0) {
- const char *const meth = &method[sizeof(prefix)-1];
- int i;
- for (i=0; i < numberof(conv_method_names); i++) {
- if (conv_method_names[i].method[0] == meth[0] &&
- strcmp(conv_method_names[i].method, meth) == 0) {
- return i;
- }
- }
+ const char *const meth = &method[sizeof(prefix)-1];
+ int i;
+ for (i=0; i < numberof(conv_method_names); i++) {
+ if (conv_method_names[i].method[0] == meth[0] &&
+ strcmp(conv_method_names[i].method, meth) == 0) {
+ return i;
+ }
+ }
}
return numberof(conv_method_names);
}
@@ -2919,23 +2978,23 @@ static VALUE
convert_type_with_id(VALUE val, const char *tname, ID method, int raise, int index)
{
VALUE r = rb_check_funcall(val, method, 0, 0);
- if (r == Qundef) {
- if (raise) {
- const char *msg =
- ((index < 0 ? conv_method_index(rb_id2name(method)) : index)
- < IMPLICIT_CONVERSIONS) ?
- "no implicit conversion of" : "can't convert";
- const char *cname = NIL_P(val) ? "nil" :
- val == Qtrue ? "true" :
- val == Qfalse ? "false" :
- NULL;
- if (cname)
- rb_raise(rb_eTypeError, "%s %s into %s", msg, cname, tname);
- rb_raise(rb_eTypeError, "%s %"PRIsVALUE" into %s", msg,
- rb_obj_class(val),
- tname);
- }
- return Qnil;
+ if (UNDEF_P(r)) {
+ if (raise) {
+ const char *msg =
+ ((index < 0 ? conv_method_index(rb_id2name(method)) : index)
+ < IMPLICIT_CONVERSIONS) ?
+ "no implicit conversion of" : "can't convert";
+ const char *cname = NIL_P(val) ? "nil" :
+ val == Qtrue ? "true" :
+ val == Qfalse ? "false" :
+ NULL;
+ if (cname)
+ rb_raise(rb_eTypeError, "%s %s into %s", msg, cname, tname);
+ rb_raise(rb_eTypeError, "%s %"PRIsVALUE" into %s", msg,
+ rb_obj_class(val),
+ tname);
+ }
+ return Qnil;
}
return r;
}
@@ -2945,7 +3004,7 @@ convert_type(VALUE val, const char *tname, const char *method, int raise)
{
int i = conv_method_index(method);
ID m = i < numberof(conv_method_names) ?
- conv_method_names[i].id : rb_intern(method);
+ conv_method_names[i].id : rb_intern(method);
return convert_type_with_id(val, tname, m, raise, i);
}
@@ -2956,8 +3015,8 @@ conversion_mismatch(VALUE val, const char *tname, const char *method, VALUE resu
{
VALUE cname = rb_obj_class(val);
rb_raise(rb_eTypeError,
- "can't convert %"PRIsVALUE" to %s (%"PRIsVALUE"#%s gives %"PRIsVALUE")",
- cname, tname, cname, method, rb_obj_class(result));
+ "can't convert %"PRIsVALUE" to %s (%"PRIsVALUE"#%s gives %"PRIsVALUE")",
+ cname, tname, cname, method, rb_obj_class(result));
}
VALUE
@@ -2968,7 +3027,7 @@ rb_convert_type(VALUE val, int type, const char *tname, const char *method)
if (TYPE(val) == type) return val;
v = convert_type(val, tname, method, TRUE);
if (TYPE(v) != type) {
- conversion_mismatch(val, tname, method, v);
+ conversion_mismatch(val, tname, method, v);
}
return v;
}
@@ -2982,7 +3041,7 @@ rb_convert_type_with_id(VALUE val, int type, const char *tname, ID method)
if (TYPE(val) == type) return val;
v = convert_type_with_id(val, tname, method, TRUE, -1);
if (TYPE(v) != type) {
- conversion_mismatch(val, tname, RSTRING_PTR(rb_id2str(method)), v);
+ conversion_mismatch(val, tname, RSTRING_PTR(rb_id2str(method)), v);
}
return v;
}
@@ -2997,7 +3056,7 @@ rb_check_convert_type(VALUE val, int type, const char *tname, const char *method
v = convert_type(val, tname, method, FALSE);
if (NIL_P(v)) return Qnil;
if (TYPE(v) != type) {
- conversion_mismatch(val, tname, method, v);
+ conversion_mismatch(val, tname, method, v);
}
return v;
}
@@ -3013,7 +3072,7 @@ rb_check_convert_type_with_id(VALUE val, int type, const char *tname, ID method)
v = convert_type_with_id(val, tname, method, FALSE, -1);
if (NIL_P(v)) return Qnil;
if (TYPE(v) != type) {
- conversion_mismatch(val, tname, RSTRING_PTR(rb_id2str(method)), v);
+ conversion_mismatch(val, tname, RSTRING_PTR(rb_id2str(method)), v);
}
return v;
}
@@ -3114,6 +3173,9 @@ rb_convert_to_integer(VALUE val, int base, int raise_exception)
tmp = rb_protect(rb_check_to_int, val, NULL);
if (RB_INTEGER_TYPE_P(tmp)) return tmp;
rb_set_errinfo(Qnil);
+ if (!NIL_P(tmp = rb_check_string_type(val))) {
+ return rb_str_convert_to_inum(tmp, base, TRUE, raise_exception);
+ }
if (!raise_exception) {
VALUE result = rb_protect(rb_check_to_i, val, NULL);
@@ -3177,6 +3239,11 @@ rb_opts_exception_p(VALUE opts, int default_value)
* using +to_int+ first and +to_i+ second;
* see below for exceptions.
*
+ * With a non-zero +base+, +object+ must be a string or convertible
+ * to a string.
+ *
+ * ==== numeric objects
+ *
* With integer argument +object+ given, returns +object+:
*
* Integer(1) # => 1
@@ -3188,6 +3255,8 @@ rb_opts_exception_p(VALUE opts, int default_value)
* Integer(1.9) # => 1 # Rounds toward zero.
* Integer(-1.9) # => -1 # Rounds toward zero.
*
+ * ==== string objects
+ *
* With string argument +object+ and zero +base+ given,
* returns +object+ converted to an integer in base 10:
*
@@ -3195,32 +3264,48 @@ rb_opts_exception_p(VALUE opts, int default_value)
* Integer('-100') # => -100
*
* With +base+ zero, string +object+ may contain leading characters
- * to specify the actual base:
+ * to specify the actual base (radix indicator):
*
* Integer('0100') # => 64 # Leading '0' specifies base 8.
* Integer('0b100') # => 4 # Leading '0b', specifies base 2.
* Integer('0x100') # => 256 # Leading '0x' specifies base 16.
*
- * With a non-zero +base+ (in range 2..36) given
- * (in which case +object+ must be a string),
- * returns +object+ converted to an integer in the given base:
+ * With a positive +base+ (in range 2..36) given, returns +object+
+ * converted to an integer in the given base:
*
* Integer('100', 2) # => 4
* Integer('100', 8) # => 64
* Integer('-100', 16) # => -256
*
+ * With a negative +base+ (in range -36..-2) given, returns +object+
+ * converted to an integer in the radix indicator if exists or
+ * +-base+:
+ *
+ * Integer('0x100', -2) # => 256
+ * Integer('100', -2) # => 4
+ * Integer('0b100', -8) # => 4
+ * Integer('100', -8) # => 64
+ * Integer('0o100', -10) # => 64
+ * Integer('100', -10) # => 100
+ *
+ * +base+ -1 is equal the -10 case.
+ *
* When converting strings, surrounding whitespace and embedded underscores
* are allowed and ignored:
*
* Integer(' 100 ') # => 100
* Integer('-1_0_0', 16) # => -256
*
+ * ==== other classes
+ *
* Examples with +object+ of various other classes:
*
* Integer(Rational(9, 10)) # => 0 # Rounds toward zero.
* Integer(Complex(2, 0)) # => 2 # Imaginary part must be zero.
* Integer(Time.now) # => 1650974042
*
+ * ==== keywords
+ *
* With optional keyword argument +exception+ given as +true+ (the default):
*
* - Raises TypeError if +object+ does not respond to +to_int+ or +to_i+.
@@ -3390,27 +3475,28 @@ rb_str_to_dbl_raise(VALUE str, int badcheck, int raise, int *error)
VALUE v = 0;
StringValue(str);
+ rb_must_asciicompat(str);
s = RSTRING_PTR(str);
len = RSTRING_LEN(str);
if (s) {
- if (badcheck && memchr(s, '\0', len)) {
+ if (badcheck && memchr(s, '\0', len)) {
if (raise)
rb_raise(rb_eArgError, "string for Float contains null byte");
else {
if (error) *error = 1;
return 0.0;
}
- }
- if (s[len]) { /* no sentinel somehow */
- char *p = ALLOCV(v, (size_t)len + 1);
- MEMCPY(p, s, char, len);
- p[len] = '\0';
- s = p;
- }
+ }
+ if (s[len]) { /* no sentinel somehow */
+ char *p = ALLOCV(v, (size_t)len + 1);
+ MEMCPY(p, s, char, len);
+ p[len] = '\0';
+ s = p;
+ }
}
ret = rb_cstr_to_dbl_raise(s, badcheck, raise, error);
if (v)
- ALLOCV_END(v);
+ ALLOCV_END(v);
return ret;
}
@@ -3442,11 +3528,11 @@ rat2dbl_without_to_f(VALUE x)
#define special_const_to_float(val, pre, post) \
switch (val) { \
case Qnil: \
- rb_raise_static(rb_eTypeError, pre "nil" post); \
+ rb_raise_static(rb_eTypeError, pre "nil" post); \
case Qtrue: \
- rb_raise_static(rb_eTypeError, pre "true" post); \
+ rb_raise_static(rb_eTypeError, pre "true" post); \
case Qfalse: \
- rb_raise_static(rb_eTypeError, pre "false" post); \
+ rb_raise_static(rb_eTypeError, pre "false" post); \
}
/*! \endcond */
@@ -3467,31 +3553,31 @@ to_float(VALUE *valp, int raise_exception)
{
VALUE val = *valp;
if (SPECIAL_CONST_P(val)) {
- if (FIXNUM_P(val)) {
- *valp = DBL2NUM(fix2dbl_without_to_f(val));
- return T_FLOAT;
- }
- else if (FLONUM_P(val)) {
- return T_FLOAT;
- }
- else if (raise_exception) {
- conversion_to_float(val);
- }
+ if (FIXNUM_P(val)) {
+ *valp = DBL2NUM(fix2dbl_without_to_f(val));
+ return T_FLOAT;
+ }
+ else if (FLONUM_P(val)) {
+ return T_FLOAT;
+ }
+ else if (raise_exception) {
+ conversion_to_float(val);
+ }
}
else {
- int type = BUILTIN_TYPE(val);
- switch (type) {
- case T_FLOAT:
- return T_FLOAT;
- case T_BIGNUM:
- *valp = DBL2NUM(big2dbl_without_to_f(val));
- return T_FLOAT;
- case T_RATIONAL:
- *valp = DBL2NUM(rat2dbl_without_to_f(val));
- return T_FLOAT;
- case T_STRING:
- return T_STRING;
- }
+ int type = BUILTIN_TYPE(val);
+ switch (type) {
+ case T_FLOAT:
+ return T_FLOAT;
+ case T_BIGNUM:
+ *valp = DBL2NUM(big2dbl_without_to_f(val));
+ return T_FLOAT;
+ case T_RATIONAL:
+ *valp = DBL2NUM(rat2dbl_without_to_f(val));
+ return T_FLOAT;
+ case T_STRING:
+ return T_STRING;
+ }
}
return T_NONE;
}
@@ -3507,7 +3593,7 @@ rb_convert_to_float(VALUE val, int raise_exception)
{
switch (to_float(&val, raise_exception)) {
case T_FLOAT:
- return val;
+ return val;
case T_STRING:
if (!raise_exception) {
int e = 0;
@@ -3555,8 +3641,8 @@ static VALUE
numeric_to_float(VALUE val)
{
if (!rb_obj_is_kind_of(val, rb_cNumeric)) {
- rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into Float",
- rb_obj_class(val));
+ rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into Float",
+ rb_obj_class(val));
}
return rb_convert_type_with_id(val, T_FLOAT, "Float", id_to_f);
}
@@ -3566,7 +3652,7 @@ rb_to_float(VALUE val)
{
switch (to_float(&val, TRUE)) {
case T_FLOAT:
- return val;
+ return val;
}
return numeric_to_float(val);
}
@@ -3576,7 +3662,7 @@ rb_check_to_float(VALUE val)
{
if (RB_FLOAT_TYPE_P(val)) return val;
if (!rb_obj_is_kind_of(val, rb_cNumeric)) {
- return Qnil;
+ return Qnil;
}
return rb_check_convert_type_with_id(val, T_FLOAT, "Float", id_to_f);
}
@@ -3592,32 +3678,32 @@ double
rb_num_to_dbl(VALUE val)
{
if (SPECIAL_CONST_P(val)) {
- if (FIXNUM_P(val)) {
- if (basic_to_f_p(rb_cInteger))
- return fix2dbl_without_to_f(val);
- }
- else if (FLONUM_P(val)) {
- return rb_float_flonum_value(val);
- }
- else {
- conversion_to_float(val);
- }
+ if (FIXNUM_P(val)) {
+ if (basic_to_f_p(rb_cInteger))
+ return fix2dbl_without_to_f(val);
+ }
+ else if (FLONUM_P(val)) {
+ return rb_float_flonum_value(val);
+ }
+ else {
+ conversion_to_float(val);
+ }
}
else {
- switch (BUILTIN_TYPE(val)) {
- case T_FLOAT:
- return rb_float_noflonum_value(val);
- case T_BIGNUM:
- if (basic_to_f_p(rb_cInteger))
- return big2dbl_without_to_f(val);
- break;
- case T_RATIONAL:
- if (basic_to_f_p(rb_cRational))
- return rat2dbl_without_to_f(val);
- break;
+ switch (BUILTIN_TYPE(val)) {
+ case T_FLOAT:
+ return rb_float_noflonum_value(val);
+ case T_BIGNUM:
+ if (basic_to_f_p(rb_cInteger))
+ return big2dbl_without_to_f(val);
+ break;
+ case T_RATIONAL:
+ if (basic_to_f_p(rb_cRational))
+ return rat2dbl_without_to_f(val);
+ break;
default:
- break;
- }
+ break;
+ }
}
val = numeric_to_float(val);
return RFLOAT_VALUE(val);
@@ -3627,29 +3713,29 @@ double
rb_num2dbl(VALUE val)
{
if (SPECIAL_CONST_P(val)) {
- if (FIXNUM_P(val)) {
- return fix2dbl_without_to_f(val);
- }
- else if (FLONUM_P(val)) {
- return rb_float_flonum_value(val);
- }
- else {
- implicit_conversion_to_float(val);
- }
+ if (FIXNUM_P(val)) {
+ return fix2dbl_without_to_f(val);
+ }
+ else if (FLONUM_P(val)) {
+ return rb_float_flonum_value(val);
+ }
+ else {
+ implicit_conversion_to_float(val);
+ }
}
else {
- switch (BUILTIN_TYPE(val)) {
- case T_FLOAT:
- return rb_float_noflonum_value(val);
- case T_BIGNUM:
- return big2dbl_without_to_f(val);
- case T_RATIONAL:
- return rat2dbl_without_to_f(val);
- case T_STRING:
- rb_raise(rb_eTypeError, "no implicit conversion to float from string");
+ switch (BUILTIN_TYPE(val)) {
+ case T_FLOAT:
+ return rb_float_noflonum_value(val);
+ case T_BIGNUM:
+ return big2dbl_without_to_f(val);
+ case T_RATIONAL:
+ return rat2dbl_without_to_f(val);
+ case T_STRING:
+ rb_raise(rb_eTypeError, "no implicit conversion to float from string");
default:
- break;
- }
+ break;
+ }
}
val = rb_convert_type_with_id(val, T_FLOAT, "Float", id_to_f);
return RFLOAT_VALUE(val);
@@ -3660,7 +3746,7 @@ rb_String(VALUE val)
{
VALUE tmp = rb_check_string_type(val);
if (NIL_P(tmp))
- tmp = rb_convert_type_with_id(val, T_STRING, "String", idTo_s);
+ tmp = rb_convert_type_with_id(val, T_STRING, "String", idTo_s);
return tmp;
}
@@ -3693,10 +3779,10 @@ rb_Array(VALUE val)
VALUE tmp = rb_check_array_type(val);
if (NIL_P(tmp)) {
- tmp = rb_check_to_array(val);
- if (NIL_P(tmp)) {
- return rb_ary_new3(1, val);
- }
+ tmp = rb_check_to_array(val);
+ if (NIL_P(tmp)) {
+ return rb_ary_new3(1, val);
+ }
}
return tmp;
}
@@ -3738,9 +3824,9 @@ rb_Hash(VALUE val)
if (NIL_P(val)) return rb_hash_new();
tmp = rb_check_hash_type(val);
if (NIL_P(tmp)) {
- if (RB_TYPE_P(val, T_ARRAY) && RARRAY_LEN(val) == 0)
- return rb_hash_new();
- rb_raise(rb_eTypeError, "can't convert %s into Hash", rb_obj_classname(val));
+ if (RB_TYPE_P(val, T_ARRAY) && RARRAY_LEN(val) == 0)
+ return rb_hash_new();
+ rb_raise(rb_eTypeError, "can't convert %s into Hash", rb_obj_classname(val));
}
return tmp;
}
@@ -3786,8 +3872,8 @@ dig_basic_p(VALUE obj, struct dig_method *cache)
{
VALUE klass = RBASIC_CLASS(obj);
if (klass != cache->klass) {
- cache->klass = klass;
- cache->basic = rb_method_basic_definition_p(klass, id_dig);
+ cache->klass = klass;
+ cache->basic = rb_method_basic_definition_p(klass, id_dig);
}
return cache->basic;
}
@@ -3796,8 +3882,8 @@ static void
no_dig_method(int found, VALUE recv, ID mid, int argc, const VALUE *argv, VALUE data)
{
if (!found) {
- rb_raise(rb_eTypeError, "%"PRIsVALUE" does not have #dig method",
- CLASS_OF(data));
+ rb_raise(rb_eTypeError, "%"PRIsVALUE" does not have #dig method",
+ CLASS_OF(data));
}
}
@@ -3808,31 +3894,31 @@ rb_obj_dig(int argc, VALUE *argv, VALUE obj, VALUE notfound)
struct dig_method hash = {Qnil}, ary = {Qnil}, strt = {Qnil};
for (; argc > 0; ++argv, --argc) {
- if (NIL_P(obj)) return notfound;
- if (!SPECIAL_CONST_P(obj)) {
- switch (BUILTIN_TYPE(obj)) {
- case T_HASH:
- if (dig_basic_p(obj, &hash)) {
- obj = rb_hash_aref(obj, *argv);
- continue;
- }
- break;
- case T_ARRAY:
- if (dig_basic_p(obj, &ary)) {
- obj = rb_ary_at(obj, *argv);
- continue;
- }
- break;
- case T_STRUCT:
- if (dig_basic_p(obj, &strt)) {
- obj = rb_struct_lookup(obj, *argv);
- continue;
- }
- break;
+ if (NIL_P(obj)) return notfound;
+ if (!SPECIAL_CONST_P(obj)) {
+ switch (BUILTIN_TYPE(obj)) {
+ case T_HASH:
+ if (dig_basic_p(obj, &hash)) {
+ obj = rb_hash_aref(obj, *argv);
+ continue;
+ }
+ break;
+ case T_ARRAY:
+ if (dig_basic_p(obj, &ary)) {
+ obj = rb_ary_at(obj, *argv);
+ continue;
+ }
+ break;
+ case T_STRUCT:
+ if (dig_basic_p(obj, &strt)) {
+ obj = rb_struct_lookup(obj, *argv);
+ continue;
+ }
+ break;
default:
break;
- }
- }
+ }
+ }
return rb_check_funcall_with_hook_kw(obj, id_dig, argc, argv,
no_dig_method, obj,
RB_NO_KEYWORDS);
@@ -4317,10 +4403,10 @@ InitVM_Object(void)
rb_define_method(rb_mKernel, "public_methods", rb_obj_public_methods, -1); /* in class.c */
rb_define_method(rb_mKernel, "instance_variables", rb_obj_instance_variables, 0); /* in variable.c */
rb_define_method(rb_mKernel, "instance_variable_get", rb_obj_ivar_get, 1);
- rb_define_method(rb_mKernel, "instance_variable_set", rb_obj_ivar_set, 2);
+ rb_define_method(rb_mKernel, "instance_variable_set", rb_obj_ivar_set_m, 2);
rb_define_method(rb_mKernel, "instance_variable_defined?", rb_obj_ivar_defined, 1);
rb_define_method(rb_mKernel, "remove_instance_variable",
- rb_obj_remove_instance_variable, 1); /* in variable.c */
+ rb_obj_remove_instance_variable, 1); /* in variable.c */
rb_define_method(rb_mKernel, "instance_of?", rb_obj_is_instance_of, 1);
rb_define_method(rb_mKernel, "kind_of?", rb_obj_is_kind_of, 1);
@@ -4379,11 +4465,11 @@ InitVM_Object(void)
rb_define_method(rb_cModule, "initialize_clone", rb_mod_initialize_clone, -1);
rb_define_method(rb_cModule, "instance_methods", rb_class_instance_methods, -1); /* in class.c */
rb_define_method(rb_cModule, "public_instance_methods",
- rb_class_public_instance_methods, -1); /* in class.c */
+ rb_class_public_instance_methods, -1); /* in class.c */
rb_define_method(rb_cModule, "protected_instance_methods",
- rb_class_protected_instance_methods, -1); /* in class.c */
+ rb_class_protected_instance_methods, -1); /* in class.c */
rb_define_method(rb_cModule, "private_instance_methods",
- rb_class_private_instance_methods, -1); /* in class.c */
+ rb_class_private_instance_methods, -1); /* in class.c */
rb_define_method(rb_cModule, "undefined_instance_methods",
rb_class_undefined_instance_methods, 0); /* in class.c */
@@ -4393,13 +4479,13 @@ InitVM_Object(void)
rb_define_method(rb_cModule, "const_defined?", rb_mod_const_defined, -1);
rb_define_method(rb_cModule, "const_source_location", rb_mod_const_source_location, -1);
rb_define_private_method(rb_cModule, "remove_const",
- rb_mod_remove_const, 1); /* in variable.c */
+ rb_mod_remove_const, 1); /* in variable.c */
rb_define_method(rb_cModule, "const_missing",
- rb_mod_const_missing, 1); /* in variable.c */
+ rb_mod_const_missing, 1); /* in variable.c */
rb_define_method(rb_cModule, "class_variables",
- rb_mod_class_variables, -1); /* in variable.c */
+ rb_mod_class_variables, -1); /* in variable.c */
rb_define_method(rb_cModule, "remove_class_variable",
- rb_mod_remove_cvar, 1); /* in variable.c */
+ rb_mod_remove_cvar, 1); /* in variable.c */
rb_define_method(rb_cModule, "class_variable_get", rb_mod_cvar_get, 1);
rb_define_method(rb_cModule, "class_variable_set", rb_mod_cvar_set, 2);
rb_define_method(rb_cModule, "class_variable_defined?", rb_mod_cvar_defined, 1);
@@ -4414,6 +4500,7 @@ InitVM_Object(void)
rb_define_method(rb_cClass, "initialize", rb_class_initialize, -1);
rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0);
rb_define_method(rb_cClass, "subclasses", rb_class_subclasses, 0); /* in class.c */
+ rb_define_method(rb_cClass, "attached_object", rb_class_attached_object, 0); /* in class.c */
rb_define_alloc_func(rb_cClass, rb_class_s_alloc);
rb_undef_method(rb_cClass, "extend_object");
rb_undef_method(rb_cClass, "append_features");
diff --git a/pack.c b/pack.c
index 1fbbd724d7..3bdae01362 100644
--- a/pack.c
+++ b/pack.c
@@ -147,8 +147,8 @@ associated_pointer(VALUE associates, const char *t)
const VALUE *p = RARRAY_CONST_PTR(associates);
const VALUE *pend = p + RARRAY_LEN(associates);
for (; p < pend; p++) {
- VALUE tmp = *p;
- if (RB_TYPE_P(tmp, T_STRING) && RSTRING_PTR(tmp) == t) return tmp;
+ VALUE tmp = *p;
+ if (RB_TYPE_P(tmp, T_STRING) && RSTRING_PTR(tmp) == t) return tmp;
}
rb_raise(rb_eArgError, "non associated pointer");
UNREACHABLE_RETURN(Qnil);
@@ -167,8 +167,8 @@ unknown_directive(const char *mode, char type, VALUE fmt)
snprintf(unknown, sizeof(unknown), "\\x%.2x", type & 0xff);
}
fmt = rb_str_quote_unprintable(fmt);
- rb_warning("unknown %s directive '%s' in '%"PRIsVALUE"'",
- mode, unknown, fmt);
+ rb_warn("unknown %s directive '%s' in '%"PRIsVALUE"'",
+ mode, unknown, fmt);
}
static float
@@ -208,16 +208,18 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer)
int integer_size, bigendian_p;
StringValue(fmt);
+ rb_must_asciicompat(fmt);
p = RSTRING_PTR(fmt);
pend = p + RSTRING_LEN(fmt);
if (NIL_P(buffer)) {
- res = rb_str_buf_new(0);
+ res = rb_str_buf_new(0);
}
else {
if (!RB_TYPE_P(buffer, T_STRING))
rb_raise(rb_eTypeError, "buffer must be String, not %s", rb_obj_classname(buffer));
- res = buffer;
+ rb_str_modify(buffer);
+ res = buffer;
}
idx = 0;
@@ -228,327 +230,311 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer)
#define NEXTFROM (MORE_ITEM ? RARRAY_AREF(ary, idx++) : TOO_FEW)
while (p < pend) {
- int explicit_endian = 0;
- if (RSTRING_PTR(fmt) + RSTRING_LEN(fmt) != pend) {
- rb_raise(rb_eRuntimeError, "format string modified");
- }
- type = *p++; /* get data type */
+ int explicit_endian = 0;
+ if (RSTRING_PTR(fmt) + RSTRING_LEN(fmt) != pend) {
+ rb_raise(rb_eRuntimeError, "format string modified");
+ }
+ type = *p++; /* get data type */
#ifdef NATINT_PACK
- natint = 0;
+ natint = 0;
#endif
- if (ISSPACE(type)) continue;
- if (type == '#') {
- while ((p < pend) && (*p != '\n')) {
- p++;
- }
- continue;
- }
+ if (ISSPACE(type)) continue;
+ if (type == '#') {
+ while ((p < pend) && (*p != '\n')) {
+ p++;
+ }
+ continue;
+ }
- {
+ {
modifiers:
- switch (*p) {
- case '_':
- case '!':
- if (strchr(natstr, type)) {
+ switch (*p) {
+ case '_':
+ case '!':
+ if (strchr(natstr, type)) {
#ifdef NATINT_PACK
- natint = 1;
+ natint = 1;
#endif
- p++;
- }
- else {
- rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
- }
- goto modifiers;
-
- case '<':
- case '>':
- if (!strchr(endstr, type)) {
- rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, endstr);
- }
- if (explicit_endian) {
- rb_raise(rb_eRangeError, "Can't use both '<' and '>'");
- }
- explicit_endian = *p++;
- goto modifiers;
- }
- }
-
- if (*p == '*') { /* set data length */
- len = strchr("@Xxu", type) ? 0
+ p++;
+ }
+ else {
+ rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
+ }
+ goto modifiers;
+
+ case '<':
+ case '>':
+ if (!strchr(endstr, type)) {
+ rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, endstr);
+ }
+ if (explicit_endian) {
+ rb_raise(rb_eRangeError, "Can't use both '<' and '>'");
+ }
+ explicit_endian = *p++;
+ goto modifiers;
+ }
+ }
+
+ if (*p == '*') { /* set data length */
+ len = strchr("@Xxu", type) ? 0
: strchr("PMm", type) ? 1
: RARRAY_LEN(ary) - idx;
- p++;
- }
- else if (ISDIGIT(*p)) {
- errno = 0;
- len = STRTOUL(p, (char**)&p, 10);
- if (errno) {
- rb_raise(rb_eRangeError, "pack length too big");
- }
- }
- else {
- len = 1;
- }
-
- switch (type) {
- case 'U':
- /* if encoding is US-ASCII, upgrade to UTF-8 */
- if (enc_info == 1) enc_info = 2;
- break;
- case 'm': case 'M': case 'u':
- /* keep US-ASCII (do nothing) */
- break;
- default:
- /* fall back to BINARY */
- enc_info = 0;
- break;
- }
- switch (type) {
- case 'A': case 'a': case 'Z':
- case 'B': case 'b':
- case 'H': case 'h':
- from = NEXTFROM;
- if (NIL_P(from)) {
- ptr = "";
- plen = 0;
- }
- else {
- StringValue(from);
- ptr = RSTRING_PTR(from);
- plen = RSTRING_LEN(from);
- }
-
- if (p[-1] == '*')
- len = plen;
-
- switch (type) {
- case 'a': /* arbitrary binary string (null padded) */
- case 'A': /* arbitrary binary string (ASCII space padded) */
- case 'Z': /* null terminated string */
- if (plen >= len) {
- rb_str_buf_cat(res, ptr, len);
- if (p[-1] == '*' && type == 'Z')
- rb_str_buf_cat(res, nul10, 1);
- }
- else {
- rb_str_buf_cat(res, ptr, plen);
- len -= plen;
- while (len >= 10) {
- rb_str_buf_cat(res, (type == 'A')?spc10:nul10, 10);
- len -= 10;
- }
- rb_str_buf_cat(res, (type == 'A')?spc10:nul10, len);
- }
- break;
+ p++;
+ }
+ else if (ISDIGIT(*p)) {
+ errno = 0;
+ len = STRTOUL(p, (char**)&p, 10);
+ if (errno) {
+ rb_raise(rb_eRangeError, "pack length too big");
+ }
+ }
+ else {
+ len = 1;
+ }
+
+ switch (type) {
+ case 'U':
+ /* if encoding is US-ASCII, upgrade to UTF-8 */
+ if (enc_info == 1) enc_info = 2;
+ break;
+ case 'm': case 'M': case 'u':
+ /* keep US-ASCII (do nothing) */
+ break;
+ default:
+ /* fall back to BINARY */
+ enc_info = 0;
+ break;
+ }
+ switch (type) {
+ case 'A': case 'a': case 'Z':
+ case 'B': case 'b':
+ case 'H': case 'h':
+ from = NEXTFROM;
+ if (NIL_P(from)) {
+ ptr = "";
+ plen = 0;
+ }
+ else {
+ StringValue(from);
+ ptr = RSTRING_PTR(from);
+ plen = RSTRING_LEN(from);
+ }
+
+ if (p[-1] == '*')
+ len = plen;
+
+ switch (type) {
+ case 'a': /* arbitrary binary string (null padded) */
+ case 'A': /* arbitrary binary string (ASCII space padded) */
+ case 'Z': /* null terminated string */
+ if (plen >= len) {
+ rb_str_buf_cat(res, ptr, len);
+ if (p[-1] == '*' && type == 'Z')
+ rb_str_buf_cat(res, nul10, 1);
+ }
+ else {
+ rb_str_buf_cat(res, ptr, plen);
+ len -= plen;
+ while (len >= 10) {
+ rb_str_buf_cat(res, (type == 'A')?spc10:nul10, 10);
+ len -= 10;
+ }
+ rb_str_buf_cat(res, (type == 'A')?spc10:nul10, len);
+ }
+ break;
#define castchar(from) (char)((from) & 0xff)
- case 'b': /* bit string (ascending) */
- {
- int byte = 0;
- long i, j = 0;
-
- if (len > plen) {
- j = (len - plen + 1)/2;
- len = plen;
- }
- for (i=0; i++ < len; ptr++) {
- if (*ptr & 1)
- byte |= 128;
- if (i & 7)
- byte >>= 1;
- else {
- char c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- byte = 0;
- }
- }
- if (len & 7) {
- char c;
- byte >>= 7 - (len & 7);
- c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- }
- len = j;
- goto grow;
- }
- break;
-
- case 'B': /* bit string (descending) */
- {
- int byte = 0;
- long i, j = 0;
-
- if (len > plen) {
- j = (len - plen + 1)/2;
- len = plen;
- }
- for (i=0; i++ < len; ptr++) {
- byte |= *ptr & 1;
- if (i & 7)
- byte <<= 1;
- else {
- char c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- byte = 0;
- }
- }
- if (len & 7) {
- char c;
- byte <<= 7 - (len & 7);
- c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- }
- len = j;
- goto grow;
- }
- break;
-
- case 'h': /* hex string (low nibble first) */
- {
- int byte = 0;
- long i, j = 0;
-
- if (len > plen) {
- j = (len + 1) / 2 - (plen + 1) / 2;
- len = plen;
- }
- for (i=0; i++ < len; ptr++) {
- if (ISALPHA(*ptr))
- byte |= (((*ptr & 15) + 9) & 15) << 4;
- else
- byte |= (*ptr & 15) << 4;
- if (i & 1)
- byte >>= 4;
- else {
- char c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- byte = 0;
- }
- }
- if (len & 1) {
- char c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- }
- len = j;
- goto grow;
- }
- break;
-
- case 'H': /* hex string (high nibble first) */
- {
- int byte = 0;
- long i, j = 0;
-
- if (len > plen) {
- j = (len + 1) / 2 - (plen + 1) / 2;
- len = plen;
- }
- for (i=0; i++ < len; ptr++) {
- if (ISALPHA(*ptr))
- byte |= ((*ptr & 15) + 9) & 15;
- else
- byte |= *ptr & 15;
- if (i & 1)
- byte <<= 4;
- else {
- char c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- byte = 0;
- }
- }
- if (len & 1) {
- char c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- }
- len = j;
- goto grow;
- }
- break;
- }
- break;
-
- case 'c': /* signed char */
- case 'C': /* unsigned char */
+ case 'b': /* bit string (ascending) */
+ {
+ int byte = 0;
+ long i, j = 0;
+
+ if (len > plen) {
+ j = (len - plen + 1)/2;
+ len = plen;
+ }
+ for (i=0; i++ < len; ptr++) {
+ if (*ptr & 1)
+ byte |= 128;
+ if (i & 7)
+ byte >>= 1;
+ else {
+ char c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 7) {
+ char c;
+ byte >>= 7 - (len & 7);
+ c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ }
+ len = j;
+ goto grow;
+ }
+ break;
+
+ case 'B': /* bit string (descending) */
+ {
+ int byte = 0;
+ long i, j = 0;
+
+ if (len > plen) {
+ j = (len - plen + 1)/2;
+ len = plen;
+ }
+ for (i=0; i++ < len; ptr++) {
+ byte |= *ptr & 1;
+ if (i & 7)
+ byte <<= 1;
+ else {
+ char c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 7) {
+ char c;
+ byte <<= 7 - (len & 7);
+ c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ }
+ len = j;
+ goto grow;
+ }
+ break;
+
+ case 'h': /* hex string (low nibble first) */
+ {
+ int byte = 0;
+ long i, j = 0;
+
+ if (len > plen) {
+ j = (len + 1) / 2 - (plen + 1) / 2;
+ len = plen;
+ }
+ for (i=0; i++ < len; ptr++) {
+ if (ISALPHA(*ptr))
+ byte |= (((*ptr & 15) + 9) & 15) << 4;
+ else
+ byte |= (*ptr & 15) << 4;
+ if (i & 1)
+ byte >>= 4;
+ else {
+ char c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 1) {
+ char c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ }
+ len = j;
+ goto grow;
+ }
+ break;
+
+ case 'H': /* hex string (high nibble first) */
+ {
+ int byte = 0;
+ long i, j = 0;
+
+ if (len > plen) {
+ j = (len + 1) / 2 - (plen + 1) / 2;
+ len = plen;
+ }
+ for (i=0; i++ < len; ptr++) {
+ if (ISALPHA(*ptr))
+ byte |= ((*ptr & 15) + 9) & 15;
+ else
+ byte |= *ptr & 15;
+ if (i & 1)
+ byte <<= 4;
+ else {
+ char c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 1) {
+ char c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ }
+ len = j;
+ goto grow;
+ }
+ break;
+ }
+ break;
+
+ case 'c': /* signed char */
+ case 'C': /* unsigned char */
integer_size = 1;
bigendian_p = BIGENDIAN_P(); /* not effective */
goto pack_integer;
- case 's': /* s for int16_t, s! for signed short */
- integer_size = NATINT_LEN(short, 2);
- bigendian_p = BIGENDIAN_P();
- goto pack_integer;
-
- case 'S': /* S for uint16_t, S! for unsigned short */
+ case 's': /* s for int16_t, s! for signed short */
+ case 'S': /* S for uint16_t, S! for unsigned short */
integer_size = NATINT_LEN(short, 2);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
- case 'i': /* i and i! for signed int */
- integer_size = (int)sizeof(int);
- bigendian_p = BIGENDIAN_P();
- goto pack_integer;
-
- case 'I': /* I and I! for unsigned int */
+ case 'i': /* i and i! for signed int */
+ case 'I': /* I and I! for unsigned int */
integer_size = (int)sizeof(int);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
- case 'l': /* l for int32_t, l! for signed long */
+ case 'l': /* l for int32_t, l! for signed long */
+ case 'L': /* L for uint32_t, L! for unsigned long */
integer_size = NATINT_LEN(long, 4);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
- case 'L': /* L for uint32_t, L! for unsigned long */
- integer_size = NATINT_LEN(long, 4);
+ case 'q': /* q for int64_t, q! for signed long long */
+ case 'Q': /* Q for uint64_t, Q! for unsigned long long */
+ integer_size = NATINT_LEN_Q;
bigendian_p = BIGENDIAN_P();
goto pack_integer;
- case 'q': /* q for int64_t, q! for signed long long */
- integer_size = NATINT_LEN_Q;
+ case 'j': /* j for intptr_t */
+ integer_size = sizeof(intptr_t);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
- case 'Q': /* Q for uint64_t, Q! for unsigned long long */
- integer_size = NATINT_LEN_Q;
+ case 'J': /* J for uintptr_t */
+ integer_size = sizeof(uintptr_t);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
- case 'j': /* j for intptr_t */
- integer_size = sizeof(intptr_t);
- bigendian_p = BIGENDIAN_P();
- goto pack_integer;
-
- case 'J': /* J for uintptr_t */
- integer_size = sizeof(uintptr_t);
- bigendian_p = BIGENDIAN_P();
- goto pack_integer;
-
- case 'n': /* 16 bit (2 bytes) integer (network byte-order) */
+ case 'n': /* 16 bit (2 bytes) integer (network byte-order) */
integer_size = 2;
bigendian_p = 1;
goto pack_integer;
- case 'N': /* 32 bit (4 bytes) integer (network byte-order) */
+ case 'N': /* 32 bit (4 bytes) integer (network byte-order) */
integer_size = 4;
bigendian_p = 1;
goto pack_integer;
- case 'v': /* 16 bit (2 bytes) integer (VAX byte-order) */
+ case 'v': /* 16 bit (2 bytes) integer (VAX byte-order) */
integer_size = 2;
bigendian_p = 0;
goto pack_integer;
- case 'V': /* 32 bit (4 bytes) integer (VAX byte-order) */
+ case 'V': /* 32 bit (4 bytes) integer (VAX byte-order) */
integer_size = 4;
bigendian_p = 0;
goto pack_integer;
pack_integer:
- if (explicit_endian) {
- bigendian_p = explicit_endian == '>';
- }
+ if (explicit_endian) {
+ bigendian_p = explicit_endian == '>';
+ }
if (integer_size > MAX_INTEGER_PACK_SIZE)
rb_bug("unexpected intger size for pack: %d", integer_size);
while (len-- > 0) {
@@ -560,192 +546,192 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer)
(bigendian_p ? INTEGER_PACK_BIG_ENDIAN : INTEGER_PACK_LITTLE_ENDIAN));
rb_str_buf_cat(res, intbuf, integer_size);
}
- break;
+ break;
- case 'f': /* single precision float in native format */
- case 'F': /* ditto */
- while (len-- > 0) {
- float f;
+ case 'f': /* single precision float in native format */
+ case 'F': /* ditto */
+ while (len-- > 0) {
+ float f;
- from = NEXTFROM;
+ from = NEXTFROM;
f = VALUE_to_float(from);
- rb_str_buf_cat(res, (char*)&f, sizeof(float));
- }
- break;
+ rb_str_buf_cat(res, (char*)&f, sizeof(float));
+ }
+ break;
- case 'e': /* single precision float in VAX byte-order */
- while (len-- > 0) {
- FLOAT_CONVWITH(tmp);
+ case 'e': /* single precision float in VAX byte-order */
+ while (len-- > 0) {
+ FLOAT_CONVWITH(tmp);
- from = NEXTFROM;
+ from = NEXTFROM;
tmp.f = VALUE_to_float(from);
- HTOVF(tmp);
- rb_str_buf_cat(res, tmp.buf, sizeof(float));
- }
- break;
-
- case 'E': /* double precision float in VAX byte-order */
- while (len-- > 0) {
- DOUBLE_CONVWITH(tmp);
- from = NEXTFROM;
- tmp.d = RFLOAT_VALUE(rb_to_float(from));
- HTOVD(tmp);
- rb_str_buf_cat(res, tmp.buf, sizeof(double));
- }
- break;
-
- case 'd': /* double precision float in native format */
- case 'D': /* ditto */
- while (len-- > 0) {
- double d;
-
- from = NEXTFROM;
- d = RFLOAT_VALUE(rb_to_float(from));
- rb_str_buf_cat(res, (char*)&d, sizeof(double));
- }
- break;
-
- case 'g': /* single precision float in network byte-order */
- while (len-- > 0) {
- FLOAT_CONVWITH(tmp);
- from = NEXTFROM;
+ HTOVF(tmp);
+ rb_str_buf_cat(res, tmp.buf, sizeof(float));
+ }
+ break;
+
+ case 'E': /* double precision float in VAX byte-order */
+ while (len-- > 0) {
+ DOUBLE_CONVWITH(tmp);
+ from = NEXTFROM;
+ tmp.d = RFLOAT_VALUE(rb_to_float(from));
+ HTOVD(tmp);
+ rb_str_buf_cat(res, tmp.buf, sizeof(double));
+ }
+ break;
+
+ case 'd': /* double precision float in native format */
+ case 'D': /* ditto */
+ while (len-- > 0) {
+ double d;
+
+ from = NEXTFROM;
+ d = RFLOAT_VALUE(rb_to_float(from));
+ rb_str_buf_cat(res, (char*)&d, sizeof(double));
+ }
+ break;
+
+ case 'g': /* single precision float in network byte-order */
+ while (len-- > 0) {
+ FLOAT_CONVWITH(tmp);
+ from = NEXTFROM;
tmp.f = VALUE_to_float(from);
- HTONF(tmp);
- rb_str_buf_cat(res, tmp.buf, sizeof(float));
- }
- break;
-
- case 'G': /* double precision float in network byte-order */
- while (len-- > 0) {
- DOUBLE_CONVWITH(tmp);
-
- from = NEXTFROM;
- tmp.d = RFLOAT_VALUE(rb_to_float(from));
- HTOND(tmp);
- rb_str_buf_cat(res, tmp.buf, sizeof(double));
- }
- break;
-
- case 'x': /* null byte */
- grow:
- while (len >= 10) {
- rb_str_buf_cat(res, nul10, 10);
- len -= 10;
- }
- rb_str_buf_cat(res, nul10, len);
- break;
-
- case 'X': /* back up byte */
- shrink:
- plen = RSTRING_LEN(res);
- if (plen < len)
- rb_raise(rb_eArgError, "X outside of string");
- rb_str_set_len(res, plen - len);
- break;
-
- case '@': /* null fill to absolute position */
- len -= RSTRING_LEN(res);
- if (len > 0) goto grow;
- len = -len;
- if (len > 0) goto shrink;
- break;
-
- case '%':
- rb_raise(rb_eArgError, "%% is not supported");
- break;
-
- case 'U': /* Unicode character */
- while (len-- > 0) {
- SIGNED_VALUE l;
- char buf[8];
- int le;
-
- from = NEXTFROM;
- from = rb_to_int(from);
- l = NUM2LONG(from);
- if (l < 0) {
- rb_raise(rb_eRangeError, "pack(U): value out of range");
- }
- le = rb_uv_to_utf8(buf, l);
- rb_str_buf_cat(res, (char*)buf, le);
- }
- break;
-
- case 'u': /* uuencoded string */
- case 'm': /* base64 encoded string */
- from = NEXTFROM;
- StringValue(from);
- ptr = RSTRING_PTR(from);
- plen = RSTRING_LEN(from);
-
- if (len == 0 && type == 'm') {
- encodes(res, ptr, plen, type, 0);
- ptr += plen;
- break;
- }
- if (len <= 2)
- len = 45;
- else if (len > 63 && type == 'u')
- len = 63;
- else
- len = len / 3 * 3;
- while (plen > 0) {
- long todo;
-
- if (plen > len)
- todo = len;
- else
- todo = plen;
- encodes(res, ptr, todo, type, 1);
- plen -= todo;
- ptr += todo;
- }
- break;
-
- case 'M': /* quoted-printable encoded string */
- from = rb_obj_as_string(NEXTFROM);
- if (len <= 1)
- len = 72;
- qpencode(res, from, len);
- break;
-
- case 'P': /* pointer to packed byte string */
- from = THISFROM;
- if (!NIL_P(from)) {
- StringValue(from);
- if (RSTRING_LEN(from) < len) {
- rb_raise(rb_eArgError, "too short buffer for P(%ld for %ld)",
- RSTRING_LEN(from), len);
- }
- }
- len = 1;
- /* FALL THROUGH */
- case 'p': /* pointer to string */
- while (len-- > 0) {
- char *t;
- from = NEXTFROM;
- if (NIL_P(from)) {
- t = 0;
- }
- else {
- t = StringValuePtr(from);
- }
- if (!associates) {
- associates = rb_ary_new();
- }
- rb_ary_push(associates, from);
- rb_str_buf_cat(res, (char*)&t, sizeof(char*));
- }
- break;
-
- case 'w': /* BER compressed integer */
- while (len-- > 0) {
- VALUE buf = rb_str_new(0, 0);
+ HTONF(tmp);
+ rb_str_buf_cat(res, tmp.buf, sizeof(float));
+ }
+ break;
+
+ case 'G': /* double precision float in network byte-order */
+ while (len-- > 0) {
+ DOUBLE_CONVWITH(tmp);
+
+ from = NEXTFROM;
+ tmp.d = RFLOAT_VALUE(rb_to_float(from));
+ HTOND(tmp);
+ rb_str_buf_cat(res, tmp.buf, sizeof(double));
+ }
+ break;
+
+ case 'x': /* null byte */
+ grow:
+ while (len >= 10) {
+ rb_str_buf_cat(res, nul10, 10);
+ len -= 10;
+ }
+ rb_str_buf_cat(res, nul10, len);
+ break;
+
+ case 'X': /* back up byte */
+ shrink:
+ plen = RSTRING_LEN(res);
+ if (plen < len)
+ rb_raise(rb_eArgError, "X outside of string");
+ rb_str_set_len(res, plen - len);
+ break;
+
+ case '@': /* null fill to absolute position */
+ len -= RSTRING_LEN(res);
+ if (len > 0) goto grow;
+ len = -len;
+ if (len > 0) goto shrink;
+ break;
+
+ case '%':
+ rb_raise(rb_eArgError, "%% is not supported");
+ break;
+
+ case 'U': /* Unicode character */
+ while (len-- > 0) {
+ SIGNED_VALUE l;
+ char buf[8];
+ int le;
+
+ from = NEXTFROM;
+ from = rb_to_int(from);
+ l = NUM2LONG(from);
+ if (l < 0) {
+ rb_raise(rb_eRangeError, "pack(U): value out of range");
+ }
+ le = rb_uv_to_utf8(buf, l);
+ rb_str_buf_cat(res, (char*)buf, le);
+ }
+ break;
+
+ case 'u': /* uuencoded string */
+ case 'm': /* base64 encoded string */
+ from = NEXTFROM;
+ StringValue(from);
+ ptr = RSTRING_PTR(from);
+ plen = RSTRING_LEN(from);
+
+ if (len == 0 && type == 'm') {
+ encodes(res, ptr, plen, type, 0);
+ ptr += plen;
+ break;
+ }
+ if (len <= 2)
+ len = 45;
+ else if (len > 63 && type == 'u')
+ len = 63;
+ else
+ len = len / 3 * 3;
+ while (plen > 0) {
+ long todo;
+
+ if (plen > len)
+ todo = len;
+ else
+ todo = plen;
+ encodes(res, ptr, todo, type, 1);
+ plen -= todo;
+ ptr += todo;
+ }
+ break;
+
+ case 'M': /* quoted-printable encoded string */
+ from = rb_obj_as_string(NEXTFROM);
+ if (len <= 1)
+ len = 72;
+ qpencode(res, from, len);
+ break;
+
+ case 'P': /* pointer to packed byte string */
+ from = THISFROM;
+ if (!NIL_P(from)) {
+ StringValue(from);
+ if (RSTRING_LEN(from) < len) {
+ rb_raise(rb_eArgError, "too short buffer for P(%ld for %ld)",
+ RSTRING_LEN(from), len);
+ }
+ }
+ len = 1;
+ /* FALL THROUGH */
+ case 'p': /* pointer to string */
+ while (len-- > 0) {
+ char *t;
+ from = NEXTFROM;
+ if (NIL_P(from)) {
+ t = 0;
+ }
+ else {
+ t = StringValuePtr(from);
+ }
+ if (!associates) {
+ associates = rb_ary_new();
+ }
+ rb_ary_push(associates, from);
+ rb_str_buf_cat(res, (char*)&t, sizeof(char*));
+ }
+ break;
+
+ case 'w': /* BER compressed integer */
+ while (len-- > 0) {
+ VALUE buf = rb_str_new(0, 0);
size_t numbytes;
int sign;
char *cp;
- from = NEXTFROM;
+ from = NEXTFROM;
from = rb_to_int(from);
numbytes = rb_absint_numwords(from, 7, NULL);
if (numbytes == 0)
@@ -767,29 +753,29 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer)
}
rb_str_buf_cat(res, RSTRING_PTR(buf), RSTRING_LEN(buf));
- }
- break;
+ }
+ break;
- default: {
+ default: {
unknown_directive("pack", type, fmt);
- break;
- }
- }
+ break;
+ }
+ }
}
if (associates) {
- str_associate(res, associates);
+ str_associate(res, associates);
}
switch (enc_info) {
case 1:
- ENCODING_CODERANGE_SET(res, rb_usascii_encindex(), ENC_CODERANGE_7BIT);
- break;
+ ENCODING_CODERANGE_SET(res, rb_usascii_encindex(), ENC_CODERANGE_7BIT);
+ break;
case 2:
- rb_enc_set_index(res, rb_utf8_encindex());
- break;
+ rb_enc_set_index(res, rb_utf8_encindex());
+ break;
default:
- /* do nothing, keep ASCII-8BIT */
- break;
+ /* do nothing, keep ASCII-8BIT */
+ break;
}
return res;
}
@@ -810,11 +796,11 @@ encodes(VALUE str, const char *s0, long len, int type, int tail_lf)
const unsigned char *s = (const unsigned char *)s0;
if (type == 'u') {
- buff[i++] = (char)len + ' ';
- padding = '`';
+ buff[i++] = (char)len + ' ';
+ padding = '`';
}
else {
- padding = '=';
+ padding = '=';
}
while (len >= input_unit) {
while (len >= input_unit && buff_size-i >= encoded_unit) {
@@ -832,16 +818,16 @@ encodes(VALUE str, const char *s0, long len, int type, int tail_lf)
}
if (len == 2) {
- buff[i++] = trans[077 & (*s >> 2)];
- buff[i++] = trans[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
- buff[i++] = trans[077 & (((s[1] << 2) & 074) | (('\0' >> 6) & 03))];
- buff[i++] = padding;
+ buff[i++] = trans[077 & (*s >> 2)];
+ buff[i++] = trans[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
+ buff[i++] = trans[077 & (((s[1] << 2) & 074) | (('\0' >> 6) & 03))];
+ buff[i++] = padding;
}
else if (len == 1) {
- buff[i++] = trans[077 & (*s >> 2)];
- buff[i++] = trans[077 & (((*s << 4) & 060) | (('\0' >> 4) & 017))];
- buff[i++] = padding;
- buff[i++] = padding;
+ buff[i++] = trans[077 & (*s >> 2)];
+ buff[i++] = trans[077 & (((*s << 4) & 060) | (('\0' >> 4) & 017))];
+ buff[i++] = padding;
+ buff[i++] = padding;
}
if (tail_lf) buff[i++] = '\n';
rb_str_buf_cat(str, buff, i);
@@ -860,46 +846,46 @@ qpencode(VALUE str, VALUE from, long len)
while (s < send) {
if ((*s > 126) ||
- (*s < 32 && *s != '\n' && *s != '\t') ||
- (*s == '=')) {
- buff[i++] = '=';
- buff[i++] = hex_table[*s >> 4];
- buff[i++] = hex_table[*s & 0x0f];
+ (*s < 32 && *s != '\n' && *s != '\t') ||
+ (*s == '=')) {
+ buff[i++] = '=';
+ buff[i++] = hex_table[*s >> 4];
+ buff[i++] = hex_table[*s & 0x0f];
n += 3;
prev = EOF;
}
- else if (*s == '\n') {
+ else if (*s == '\n') {
if (prev == ' ' || prev == '\t') {
- buff[i++] = '=';
- buff[i++] = *s;
+ buff[i++] = '=';
+ buff[i++] = *s;
}
- buff[i++] = *s;
+ buff[i++] = *s;
n = 0;
prev = *s;
}
- else {
- buff[i++] = *s;
+ else {
+ buff[i++] = *s;
n++;
prev = *s;
}
if (n > len) {
- buff[i++] = '=';
- buff[i++] = '\n';
+ buff[i++] = '=';
+ buff[i++] = '\n';
n = 0;
prev = '\n';
}
- if (i > 1024 - 5) {
- rb_str_buf_cat(str, buff, i);
- i = 0;
- }
- s++;
+ if (i > 1024 - 5) {
+ rb_str_buf_cat(str, buff, i);
+ i = 0;
+ }
+ s++;
}
if (n > 0) {
- buff[i++] = '=';
- buff[i++] = '\n';
+ buff[i++] = '=';
+ buff[i++] = '\n';
}
if (i > 0) {
- rb_str_buf_cat(str, buff, i);
+ rb_str_buf_cat(str, buff, i);
}
}
@@ -917,15 +903,15 @@ hex2num(char c)
tmp_len = 0; \
if (len > (long)((send-s)/(sz))) { \
if (!star) { \
- tmp_len = len-(send-s)/(sz); \
+ tmp_len = len-(send-s)/(sz); \
} \
- len = (send-s)/(sz); \
+ len = (send-s)/(sz); \
} \
} while (0)
#define PACK_ITEM_ADJUST() do { \
if (tmp_len > 0 && mode == UNPACK_ARRAY) \
- rb_ary_store(ary, RARRAY_LEN(ary)+tmp_len-1, Qnil); \
+ rb_ary_store(ary, RARRAY_LEN(ary)+tmp_len-1, Qnil); \
} while (0)
/* Workaround for Oracle Developer Studio (Oracle Solaris Studio)
@@ -938,13 +924,14 @@ hex2num(char c)
# define AVOID_CC_BUG
#endif
-/* unpack mode */
-#define UNPACK_ARRAY 0
-#define UNPACK_BLOCK 1
-#define UNPACK_1 2
+enum unpack_mode {
+ UNPACK_ARRAY,
+ UNPACK_BLOCK,
+ UNPACK_1
+};
static VALUE
-pack_unpack_internal(VALUE str, VALUE fmt, int mode, long offset)
+pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset)
{
#define hexdigits ruby_hexdigits
char *s, *send;
@@ -959,20 +946,21 @@ pack_unpack_internal(VALUE str, VALUE fmt, int mode, long offset)
#endif
int signed_p, integer_size, bigendian_p;
#define UNPACK_PUSH(item) do {\
- VALUE item_val = (item);\
- if ((mode) == UNPACK_BLOCK) {\
- rb_yield(item_val);\
- }\
- else if ((mode) == UNPACK_ARRAY) {\
- rb_ary_push(ary, item_val);\
- }\
- else /* if ((mode) == UNPACK_1) { */ {\
- return item_val; \
- }\
+ VALUE item_val = (item);\
+ if ((mode) == UNPACK_BLOCK) {\
+ rb_yield(item_val);\
+ }\
+ else if ((mode) == UNPACK_ARRAY) {\
+ rb_ary_push(ary, item_val);\
+ }\
+ else /* if ((mode) == UNPACK_1) { */ {\
+ return item_val; \
+ }\
} while (0)
StringValue(str);
StringValue(fmt);
+ rb_must_asciicompat(fmt);
if (offset < 0) rb_raise(rb_eArgError, "offset can't be negative");
len = RSTRING_LEN(str);
@@ -989,295 +977,295 @@ pack_unpack_internal(VALUE str, VALUE fmt, int mode, long offset)
ary = mode == UNPACK_ARRAY ? rb_ary_new() : Qnil;
while (p < pend) {
- int explicit_endian = 0;
- type = *p++;
+ int explicit_endian = 0;
+ type = *p++;
#ifdef NATINT_PACK
- natint = 0;
+ natint = 0;
#endif
- if (ISSPACE(type)) continue;
- if (type == '#') {
- while ((p < pend) && (*p != '\n')) {
- p++;
- }
- continue;
- }
+ if (ISSPACE(type)) continue;
+ if (type == '#') {
+ while ((p < pend) && (*p != '\n')) {
+ p++;
+ }
+ continue;
+ }
- star = 0;
- {
+ star = 0;
+ {
modifiers:
- switch (*p) {
- case '_':
- case '!':
+ switch (*p) {
+ case '_':
+ case '!':
- if (strchr(natstr, type)) {
+ if (strchr(natstr, type)) {
#ifdef NATINT_PACK
- natint = 1;
+ natint = 1;
#endif
- p++;
- }
- else {
- rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
- }
- goto modifiers;
-
- case '<':
- case '>':
- if (!strchr(endstr, type)) {
- rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, endstr);
- }
- if (explicit_endian) {
- rb_raise(rb_eRangeError, "Can't use both '<' and '>'");
- }
- explicit_endian = *p++;
- goto modifiers;
- }
- }
-
- if (p >= pend)
- len = 1;
- else if (*p == '*') {
- star = 1;
- len = send - s;
- p++;
- }
- else if (ISDIGIT(*p)) {
- errno = 0;
- len = STRTOUL(p, (char**)&p, 10);
- if (len < 0 || errno) {
- rb_raise(rb_eRangeError, "pack length too big");
- }
- }
- else {
- len = (type != '@');
- }
-
- switch (type) {
- case '%':
- rb_raise(rb_eArgError, "%% is not supported");
- break;
-
- case 'A':
- if (len > send - s) len = send - s;
- {
- long end = len;
- char *t = s + len - 1;
-
- while (t >= s) {
- if (*t != ' ' && *t != '\0') break;
- t--; len--;
- }
+ p++;
+ }
+ else {
+ rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
+ }
+ goto modifiers;
+
+ case '<':
+ case '>':
+ if (!strchr(endstr, type)) {
+ rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, endstr);
+ }
+ if (explicit_endian) {
+ rb_raise(rb_eRangeError, "Can't use both '<' and '>'");
+ }
+ explicit_endian = *p++;
+ goto modifiers;
+ }
+ }
+
+ if (p >= pend)
+ len = 1;
+ else if (*p == '*') {
+ star = 1;
+ len = send - s;
+ p++;
+ }
+ else if (ISDIGIT(*p)) {
+ errno = 0;
+ len = STRTOUL(p, (char**)&p, 10);
+ if (len < 0 || errno) {
+ rb_raise(rb_eRangeError, "pack length too big");
+ }
+ }
+ else {
+ len = (type != '@');
+ }
+
+ switch (type) {
+ case '%':
+ rb_raise(rb_eArgError, "%% is not supported");
+ break;
+
+ case 'A':
+ if (len > send - s) len = send - s;
+ {
+ long end = len;
+ char *t = s + len - 1;
+
+ while (t >= s) {
+ if (*t != ' ' && *t != '\0') break;
+ t--; len--;
+ }
UNPACK_PUSH(rb_str_new(s, len));
- s += end;
- }
- break;
+ s += end;
+ }
+ break;
- case 'Z':
- {
- char *t = s;
+ case 'Z':
+ {
+ char *t = s;
- if (len > send-s) len = send-s;
- while (t < s+len && *t) t++;
+ if (len > send-s) len = send-s;
+ while (t < s+len && *t) t++;
UNPACK_PUSH(rb_str_new(s, t-s));
- if (t < send) t++;
- s = star ? t : s+len;
- }
- break;
+ if (t < send) t++;
+ s = star ? t : s+len;
+ }
+ break;
- case 'a':
- if (len > send - s) len = send - s;
+ case 'a':
+ if (len > send - s) len = send - s;
UNPACK_PUSH(rb_str_new(s, len));
- s += len;
- break;
-
- case 'b':
- {
- VALUE bitstr;
- char *t;
- int bits;
- long i;
-
- if (p[-1] == '*' || len > (send - s) * 8)
- len = (send - s) * 8;
- bits = 0;
- bitstr = rb_usascii_str_new(0, len);
- t = RSTRING_PTR(bitstr);
- for (i=0; i<len; i++) {
- if (i & 7) bits >>= 1;
- else bits = (unsigned char)*s++;
- *t++ = (bits & 1) ? '1' : '0';
- }
- UNPACK_PUSH(bitstr);
- }
- break;
-
- case 'B':
- {
- VALUE bitstr;
- char *t;
- int bits;
- long i;
-
- if (p[-1] == '*' || len > (send - s) * 8)
- len = (send - s) * 8;
- bits = 0;
- bitstr = rb_usascii_str_new(0, len);
- t = RSTRING_PTR(bitstr);
- for (i=0; i<len; i++) {
- if (i & 7) bits <<= 1;
- else bits = (unsigned char)*s++;
- *t++ = (bits & 128) ? '1' : '0';
- }
- UNPACK_PUSH(bitstr);
- }
- break;
-
- case 'h':
- {
- VALUE bitstr;
- char *t;
- int bits;
- long i;
-
- if (p[-1] == '*' || len > (send - s) * 2)
- len = (send - s) * 2;
- bits = 0;
- bitstr = rb_usascii_str_new(0, len);
- t = RSTRING_PTR(bitstr);
- for (i=0; i<len; i++) {
- if (i & 1)
- bits >>= 4;
- else
- bits = (unsigned char)*s++;
- *t++ = hexdigits[bits & 15];
- }
- UNPACK_PUSH(bitstr);
- }
- break;
-
- case 'H':
- {
- VALUE bitstr;
- char *t;
- int bits;
- long i;
-
- if (p[-1] == '*' || len > (send - s) * 2)
- len = (send - s) * 2;
- bits = 0;
- bitstr = rb_usascii_str_new(0, len);
- t = RSTRING_PTR(bitstr);
- for (i=0; i<len; i++) {
- if (i & 1)
- bits <<= 4;
- else
- bits = (unsigned char)*s++;
- *t++ = hexdigits[(bits >> 4) & 15];
- }
- UNPACK_PUSH(bitstr);
- }
- break;
-
- case 'c':
- signed_p = 1;
- integer_size = 1;
- bigendian_p = BIGENDIAN_P(); /* not effective */
- goto unpack_integer;
-
- case 'C':
- signed_p = 0;
- integer_size = 1;
- bigendian_p = BIGENDIAN_P(); /* not effective */
- goto unpack_integer;
-
- case 's':
- signed_p = 1;
- integer_size = NATINT_LEN(short, 2);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'S':
- signed_p = 0;
- integer_size = NATINT_LEN(short, 2);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'i':
- signed_p = 1;
- integer_size = (int)sizeof(int);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'I':
- signed_p = 0;
- integer_size = (int)sizeof(int);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'l':
- signed_p = 1;
- integer_size = NATINT_LEN(long, 4);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'L':
- signed_p = 0;
- integer_size = NATINT_LEN(long, 4);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'q':
- signed_p = 1;
- integer_size = NATINT_LEN_Q;
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'Q':
- signed_p = 0;
- integer_size = NATINT_LEN_Q;
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'j':
- signed_p = 1;
- integer_size = sizeof(intptr_t);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'J':
- signed_p = 0;
- integer_size = sizeof(uintptr_t);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'n':
- signed_p = 0;
- integer_size = 2;
- bigendian_p = 1;
- goto unpack_integer;
-
- case 'N':
- signed_p = 0;
- integer_size = 4;
- bigendian_p = 1;
- goto unpack_integer;
-
- case 'v':
- signed_p = 0;
- integer_size = 2;
- bigendian_p = 0;
- goto unpack_integer;
-
- case 'V':
- signed_p = 0;
- integer_size = 4;
- bigendian_p = 0;
- goto unpack_integer;
-
- unpack_integer:
- if (explicit_endian) {
- bigendian_p = explicit_endian == '>';
- }
+ s += len;
+ break;
+
+ case 'b':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits;
+ long i;
+
+ if (p[-1] == '*' || len > (send - s) * 8)
+ len = (send - s) * 8;
+ bits = 0;
+ bitstr = rb_usascii_str_new(0, len);
+ t = RSTRING_PTR(bitstr);
+ for (i=0; i<len; i++) {
+ if (i & 7) bits >>= 1;
+ else bits = (unsigned char)*s++;
+ *t++ = (bits & 1) ? '1' : '0';
+ }
+ UNPACK_PUSH(bitstr);
+ }
+ break;
+
+ case 'B':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits;
+ long i;
+
+ if (p[-1] == '*' || len > (send - s) * 8)
+ len = (send - s) * 8;
+ bits = 0;
+ bitstr = rb_usascii_str_new(0, len);
+ t = RSTRING_PTR(bitstr);
+ for (i=0; i<len; i++) {
+ if (i & 7) bits <<= 1;
+ else bits = (unsigned char)*s++;
+ *t++ = (bits & 128) ? '1' : '0';
+ }
+ UNPACK_PUSH(bitstr);
+ }
+ break;
+
+ case 'h':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits;
+ long i;
+
+ if (p[-1] == '*' || len > (send - s) * 2)
+ len = (send - s) * 2;
+ bits = 0;
+ bitstr = rb_usascii_str_new(0, len);
+ t = RSTRING_PTR(bitstr);
+ for (i=0; i<len; i++) {
+ if (i & 1)
+ bits >>= 4;
+ else
+ bits = (unsigned char)*s++;
+ *t++ = hexdigits[bits & 15];
+ }
+ UNPACK_PUSH(bitstr);
+ }
+ break;
+
+ case 'H':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits;
+ long i;
+
+ if (p[-1] == '*' || len > (send - s) * 2)
+ len = (send - s) * 2;
+ bits = 0;
+ bitstr = rb_usascii_str_new(0, len);
+ t = RSTRING_PTR(bitstr);
+ for (i=0; i<len; i++) {
+ if (i & 1)
+ bits <<= 4;
+ else
+ bits = (unsigned char)*s++;
+ *t++ = hexdigits[(bits >> 4) & 15];
+ }
+ UNPACK_PUSH(bitstr);
+ }
+ break;
+
+ case 'c':
+ signed_p = 1;
+ integer_size = 1;
+ bigendian_p = BIGENDIAN_P(); /* not effective */
+ goto unpack_integer;
+
+ case 'C':
+ signed_p = 0;
+ integer_size = 1;
+ bigendian_p = BIGENDIAN_P(); /* not effective */
+ goto unpack_integer;
+
+ case 's':
+ signed_p = 1;
+ integer_size = NATINT_LEN(short, 2);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'S':
+ signed_p = 0;
+ integer_size = NATINT_LEN(short, 2);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'i':
+ signed_p = 1;
+ integer_size = (int)sizeof(int);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'I':
+ signed_p = 0;
+ integer_size = (int)sizeof(int);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'l':
+ signed_p = 1;
+ integer_size = NATINT_LEN(long, 4);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'L':
+ signed_p = 0;
+ integer_size = NATINT_LEN(long, 4);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'q':
+ signed_p = 1;
+ integer_size = NATINT_LEN_Q;
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'Q':
+ signed_p = 0;
+ integer_size = NATINT_LEN_Q;
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'j':
+ signed_p = 1;
+ integer_size = sizeof(intptr_t);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'J':
+ signed_p = 0;
+ integer_size = sizeof(uintptr_t);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'n':
+ signed_p = 0;
+ integer_size = 2;
+ bigendian_p = 1;
+ goto unpack_integer;
+
+ case 'N':
+ signed_p = 0;
+ integer_size = 4;
+ bigendian_p = 1;
+ goto unpack_integer;
+
+ case 'v':
+ signed_p = 0;
+ integer_size = 2;
+ bigendian_p = 0;
+ goto unpack_integer;
+
+ case 'V':
+ signed_p = 0;
+ integer_size = 4;
+ bigendian_p = 0;
+ goto unpack_integer;
+
+ unpack_integer:
+ if (explicit_endian) {
+ bigendian_p = explicit_endian == '>';
+ }
PACK_LENGTH_ADJUST_SIZE(integer_size);
while (len-- > 0) {
int flags = bigendian_p ? INTEGER_PACK_BIG_ENDIAN : INTEGER_PACK_LITTLE_ENDIAN;
@@ -1291,311 +1279,311 @@ pack_unpack_internal(VALUE str, VALUE fmt, int mode, long offset)
PACK_ITEM_ADJUST();
break;
- case 'f':
- case 'F':
- PACK_LENGTH_ADJUST_SIZE(sizeof(float));
- while (len-- > 0) {
- float tmp;
- UNPACK_FETCH(&tmp, float);
- UNPACK_PUSH(DBL2NUM((double)tmp));
- }
- PACK_ITEM_ADJUST();
- break;
-
- case 'e':
- PACK_LENGTH_ADJUST_SIZE(sizeof(float));
- while (len-- > 0) {
- FLOAT_CONVWITH(tmp);
- UNPACK_FETCH(tmp.buf, float);
- VTOHF(tmp);
- UNPACK_PUSH(DBL2NUM(tmp.f));
- }
- PACK_ITEM_ADJUST();
- break;
-
- case 'E':
- PACK_LENGTH_ADJUST_SIZE(sizeof(double));
- while (len-- > 0) {
- DOUBLE_CONVWITH(tmp);
- UNPACK_FETCH(tmp.buf, double);
- VTOHD(tmp);
- UNPACK_PUSH(DBL2NUM(tmp.d));
- }
- PACK_ITEM_ADJUST();
- break;
-
- case 'D':
- case 'd':
- PACK_LENGTH_ADJUST_SIZE(sizeof(double));
- while (len-- > 0) {
- double tmp;
- UNPACK_FETCH(&tmp, double);
- UNPACK_PUSH(DBL2NUM(tmp));
- }
- PACK_ITEM_ADJUST();
- break;
-
- case 'g':
- PACK_LENGTH_ADJUST_SIZE(sizeof(float));
- while (len-- > 0) {
- FLOAT_CONVWITH(tmp);
- UNPACK_FETCH(tmp.buf, float);
- NTOHF(tmp);
- UNPACK_PUSH(DBL2NUM(tmp.f));
- }
- PACK_ITEM_ADJUST();
- break;
-
- case 'G':
- PACK_LENGTH_ADJUST_SIZE(sizeof(double));
- while (len-- > 0) {
- DOUBLE_CONVWITH(tmp);
- UNPACK_FETCH(tmp.buf, double);
- NTOHD(tmp);
- UNPACK_PUSH(DBL2NUM(tmp.d));
- }
- PACK_ITEM_ADJUST();
- break;
-
- case 'U':
- if (len > send - s) len = send - s;
- while (len > 0 && s < send) {
- long alen = send - s;
- unsigned long l;
-
- l = utf8_to_uv(s, &alen);
- s += alen; len--;
- UNPACK_PUSH(ULONG2NUM(l));
- }
- break;
-
- case 'u':
- {
+ case 'f':
+ case 'F':
+ PACK_LENGTH_ADJUST_SIZE(sizeof(float));
+ while (len-- > 0) {
+ float tmp;
+ UNPACK_FETCH(&tmp, float);
+ UNPACK_PUSH(DBL2NUM((double)tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'e':
+ PACK_LENGTH_ADJUST_SIZE(sizeof(float));
+ while (len-- > 0) {
+ FLOAT_CONVWITH(tmp);
+ UNPACK_FETCH(tmp.buf, float);
+ VTOHF(tmp);
+ UNPACK_PUSH(DBL2NUM(tmp.f));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'E':
+ PACK_LENGTH_ADJUST_SIZE(sizeof(double));
+ while (len-- > 0) {
+ DOUBLE_CONVWITH(tmp);
+ UNPACK_FETCH(tmp.buf, double);
+ VTOHD(tmp);
+ UNPACK_PUSH(DBL2NUM(tmp.d));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'D':
+ case 'd':
+ PACK_LENGTH_ADJUST_SIZE(sizeof(double));
+ while (len-- > 0) {
+ double tmp;
+ UNPACK_FETCH(&tmp, double);
+ UNPACK_PUSH(DBL2NUM(tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'g':
+ PACK_LENGTH_ADJUST_SIZE(sizeof(float));
+ while (len-- > 0) {
+ FLOAT_CONVWITH(tmp);
+ UNPACK_FETCH(tmp.buf, float);
+ NTOHF(tmp);
+ UNPACK_PUSH(DBL2NUM(tmp.f));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'G':
+ PACK_LENGTH_ADJUST_SIZE(sizeof(double));
+ while (len-- > 0) {
+ DOUBLE_CONVWITH(tmp);
+ UNPACK_FETCH(tmp.buf, double);
+ NTOHD(tmp);
+ UNPACK_PUSH(DBL2NUM(tmp.d));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'U':
+ if (len > send - s) len = send - s;
+ while (len > 0 && s < send) {
+ long alen = send - s;
+ unsigned long l;
+
+ l = utf8_to_uv(s, &alen);
+ s += alen; len--;
+ UNPACK_PUSH(ULONG2NUM(l));
+ }
+ break;
+
+ case 'u':
+ {
VALUE buf = rb_str_new(0, (send - s)*3/4);
- char *ptr = RSTRING_PTR(buf);
- long total = 0;
-
- while (s < send && (unsigned char)*s > ' ' && (unsigned char)*s < 'a') {
- long a,b,c,d;
- char hunk[3];
-
- len = ((unsigned char)*s++ - ' ') & 077;
-
- total += len;
- if (total > RSTRING_LEN(buf)) {
- len -= total - RSTRING_LEN(buf);
- total = RSTRING_LEN(buf);
- }
-
- while (len > 0) {
- long mlen = len > 3 ? 3 : len;
-
- if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
- a = ((unsigned char)*s++ - ' ') & 077;
- else
- a = 0;
- if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
- b = ((unsigned char)*s++ - ' ') & 077;
- else
- b = 0;
- if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
- c = ((unsigned char)*s++ - ' ') & 077;
- else
- c = 0;
- if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
- d = ((unsigned char)*s++ - ' ') & 077;
- else
- d = 0;
- hunk[0] = (char)(a << 2 | b >> 4);
- hunk[1] = (char)(b << 4 | c >> 2);
- hunk[2] = (char)(c << 6 | d);
- memcpy(ptr, hunk, mlen);
- ptr += mlen;
- len -= mlen;
- }
- if (s < send && (unsigned char)*s != '\r' && *s != '\n')
- s++; /* possible checksum byte */
- if (s < send && *s == '\r') s++;
- if (s < send && *s == '\n') s++;
- }
-
- rb_str_set_len(buf, total);
- UNPACK_PUSH(buf);
- }
- break;
-
- case 'm':
- {
+ char *ptr = RSTRING_PTR(buf);
+ long total = 0;
+
+ while (s < send && (unsigned char)*s > ' ' && (unsigned char)*s < 'a') {
+ long a,b,c,d;
+ char hunk[3];
+
+ len = ((unsigned char)*s++ - ' ') & 077;
+
+ total += len;
+ if (total > RSTRING_LEN(buf)) {
+ len -= total - RSTRING_LEN(buf);
+ total = RSTRING_LEN(buf);
+ }
+
+ while (len > 0) {
+ long mlen = len > 3 ? 3 : len;
+
+ if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
+ a = ((unsigned char)*s++ - ' ') & 077;
+ else
+ a = 0;
+ if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
+ b = ((unsigned char)*s++ - ' ') & 077;
+ else
+ b = 0;
+ if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
+ c = ((unsigned char)*s++ - ' ') & 077;
+ else
+ c = 0;
+ if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
+ d = ((unsigned char)*s++ - ' ') & 077;
+ else
+ d = 0;
+ hunk[0] = (char)(a << 2 | b >> 4);
+ hunk[1] = (char)(b << 4 | c >> 2);
+ hunk[2] = (char)(c << 6 | d);
+ memcpy(ptr, hunk, mlen);
+ ptr += mlen;
+ len -= mlen;
+ }
+ if (s < send && (unsigned char)*s != '\r' && *s != '\n')
+ s++; /* possible checksum byte */
+ if (s < send && *s == '\r') s++;
+ if (s < send && *s == '\n') s++;
+ }
+
+ rb_str_set_len(buf, total);
+ UNPACK_PUSH(buf);
+ }
+ break;
+
+ case 'm':
+ {
VALUE buf = rb_str_new(0, (send - s + 3)*3/4); /* +3 is for skipping paddings */
- char *ptr = RSTRING_PTR(buf);
- int a = -1,b = -1,c = 0,d = 0;
- static signed char b64_xtable[256];
-
- if (b64_xtable['/'] <= 0) {
- int i;
-
- for (i = 0; i < 256; i++) {
- b64_xtable[i] = -1;
- }
- for (i = 0; i < 64; i++) {
- b64_xtable[(unsigned char)b64_table[i]] = (char)i;
- }
- }
- if (len == 0) {
- while (s < send) {
- a = b = c = d = -1;
- a = b64_xtable[(unsigned char)*s++];
- if (s >= send || a == -1) rb_raise(rb_eArgError, "invalid base64");
- b = b64_xtable[(unsigned char)*s++];
- if (s >= send || b == -1) rb_raise(rb_eArgError, "invalid base64");
- if (*s == '=') {
- if (s + 2 == send && *(s + 1) == '=') break;
- rb_raise(rb_eArgError, "invalid base64");
- }
- c = b64_xtable[(unsigned char)*s++];
- if (s >= send || c == -1) rb_raise(rb_eArgError, "invalid base64");
- if (s + 1 == send && *s == '=') break;
- d = b64_xtable[(unsigned char)*s++];
- if (d == -1) rb_raise(rb_eArgError, "invalid base64");
- *ptr++ = castchar(a << 2 | b >> 4);
- *ptr++ = castchar(b << 4 | c >> 2);
- *ptr++ = castchar(c << 6 | d);
- }
- if (c == -1) {
- *ptr++ = castchar(a << 2 | b >> 4);
- if (b & 0xf) rb_raise(rb_eArgError, "invalid base64");
- }
- else if (d == -1) {
- *ptr++ = castchar(a << 2 | b >> 4);
- *ptr++ = castchar(b << 4 | c >> 2);
- if (c & 0x3) rb_raise(rb_eArgError, "invalid base64");
- }
- }
- else {
- while (s < send) {
- a = b = c = d = -1;
- while ((a = b64_xtable[(unsigned char)*s]) == -1 && s < send) {s++;}
- if (s >= send) break;
- s++;
- while ((b = b64_xtable[(unsigned char)*s]) == -1 && s < send) {s++;}
- if (s >= send) break;
- s++;
- while ((c = b64_xtable[(unsigned char)*s]) == -1 && s < send) {if (*s == '=') break; s++;}
- if (*s == '=' || s >= send) break;
- s++;
- while ((d = b64_xtable[(unsigned char)*s]) == -1 && s < send) {if (*s == '=') break; s++;}
- if (*s == '=' || s >= send) break;
- s++;
- *ptr++ = castchar(a << 2 | b >> 4);
- *ptr++ = castchar(b << 4 | c >> 2);
- *ptr++ = castchar(c << 6 | d);
- a = -1;
- }
- if (a != -1 && b != -1) {
- if (c == -1)
- *ptr++ = castchar(a << 2 | b >> 4);
- else {
- *ptr++ = castchar(a << 2 | b >> 4);
- *ptr++ = castchar(b << 4 | c >> 2);
- }
- }
- }
- rb_str_set_len(buf, ptr - RSTRING_PTR(buf));
- UNPACK_PUSH(buf);
- }
- break;
-
- case 'M':
- {
+ char *ptr = RSTRING_PTR(buf);
+ int a = -1,b = -1,c = 0,d = 0;
+ static signed char b64_xtable[256];
+
+ if (b64_xtable['/'] <= 0) {
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ b64_xtable[i] = -1;
+ }
+ for (i = 0; i < 64; i++) {
+ b64_xtable[(unsigned char)b64_table[i]] = (char)i;
+ }
+ }
+ if (len == 0) {
+ while (s < send) {
+ a = b = c = d = -1;
+ a = b64_xtable[(unsigned char)*s++];
+ if (s >= send || a == -1) rb_raise(rb_eArgError, "invalid base64");
+ b = b64_xtable[(unsigned char)*s++];
+ if (s >= send || b == -1) rb_raise(rb_eArgError, "invalid base64");
+ if (*s == '=') {
+ if (s + 2 == send && *(s + 1) == '=') break;
+ rb_raise(rb_eArgError, "invalid base64");
+ }
+ c = b64_xtable[(unsigned char)*s++];
+ if (s >= send || c == -1) rb_raise(rb_eArgError, "invalid base64");
+ if (s + 1 == send && *s == '=') break;
+ d = b64_xtable[(unsigned char)*s++];
+ if (d == -1) rb_raise(rb_eArgError, "invalid base64");
+ *ptr++ = castchar(a << 2 | b >> 4);
+ *ptr++ = castchar(b << 4 | c >> 2);
+ *ptr++ = castchar(c << 6 | d);
+ }
+ if (c == -1) {
+ *ptr++ = castchar(a << 2 | b >> 4);
+ if (b & 0xf) rb_raise(rb_eArgError, "invalid base64");
+ }
+ else if (d == -1) {
+ *ptr++ = castchar(a << 2 | b >> 4);
+ *ptr++ = castchar(b << 4 | c >> 2);
+ if (c & 0x3) rb_raise(rb_eArgError, "invalid base64");
+ }
+ }
+ else {
+ while (s < send) {
+ a = b = c = d = -1;
+ while ((a = b64_xtable[(unsigned char)*s]) == -1 && s < send) {s++;}
+ if (s >= send) break;
+ s++;
+ while ((b = b64_xtable[(unsigned char)*s]) == -1 && s < send) {s++;}
+ if (s >= send) break;
+ s++;
+ while ((c = b64_xtable[(unsigned char)*s]) == -1 && s < send) {if (*s == '=') break; s++;}
+ if (*s == '=' || s >= send) break;
+ s++;
+ while ((d = b64_xtable[(unsigned char)*s]) == -1 && s < send) {if (*s == '=') break; s++;}
+ if (*s == '=' || s >= send) break;
+ s++;
+ *ptr++ = castchar(a << 2 | b >> 4);
+ *ptr++ = castchar(b << 4 | c >> 2);
+ *ptr++ = castchar(c << 6 | d);
+ a = -1;
+ }
+ if (a != -1 && b != -1) {
+ if (c == -1)
+ *ptr++ = castchar(a << 2 | b >> 4);
+ else {
+ *ptr++ = castchar(a << 2 | b >> 4);
+ *ptr++ = castchar(b << 4 | c >> 2);
+ }
+ }
+ }
+ rb_str_set_len(buf, ptr - RSTRING_PTR(buf));
+ UNPACK_PUSH(buf);
+ }
+ break;
+
+ case 'M':
+ {
VALUE buf = rb_str_new(0, send - s);
- char *ptr = RSTRING_PTR(buf), *ss = s;
- int csum = 0;
- int c1, c2;
-
- while (s < send) {
- if (*s == '=') {
- if (++s == send) break;
- if (s+1 < send && *s == '\r' && *(s+1) == '\n')
- s++;
- if (*s != '\n') {
- if ((c1 = hex2num(*s)) == -1) break;
- if (++s == send) break;
- if ((c2 = hex2num(*s)) == -1) break;
- csum |= *ptr++ = castchar(c1 << 4 | c2);
- }
- }
- else {
- csum |= *ptr++ = *s;
- }
- s++;
- ss = s;
- }
- rb_str_set_len(buf, ptr - RSTRING_PTR(buf));
- rb_str_buf_cat(buf, ss, send-ss);
- csum = ISASCII(csum) ? ENC_CODERANGE_7BIT : ENC_CODERANGE_VALID;
- ENCODING_CODERANGE_SET(buf, rb_ascii8bit_encindex(), csum);
- UNPACK_PUSH(buf);
- }
- break;
-
- case '@':
- if (len > RSTRING_LEN(str))
- rb_raise(rb_eArgError, "@ outside of string");
- s = RSTRING_PTR(str) + len;
- break;
-
- case 'X':
- if (len > s - RSTRING_PTR(str))
- rb_raise(rb_eArgError, "X outside of string");
- s -= len;
- break;
-
- case 'x':
- if (len > send - s)
- rb_raise(rb_eArgError, "x outside of string");
- s += len;
- break;
-
- case 'P':
- if (sizeof(char *) <= (size_t)(send - s)) {
- VALUE tmp = Qnil;
- char *t;
-
- UNPACK_FETCH(&t, char *);
- if (t) {
- if (!associates) associates = str_associated(str);
- tmp = associated_pointer(associates, t);
- if (len < RSTRING_LEN(tmp)) {
- tmp = rb_str_new(t, len);
- str_associate(tmp, associates);
- }
- }
- UNPACK_PUSH(tmp);
- }
- break;
-
- case 'p':
- if (len > (long)((send - s) / sizeof(char *)))
- len = (send - s) / sizeof(char *);
- while (len-- > 0) {
- if ((size_t)(send - s) < sizeof(char *))
- break;
- else {
- VALUE tmp = Qnil;
- char *t;
-
- UNPACK_FETCH(&t, char *);
- if (t) {
- if (!associates) associates = str_associated(str);
- tmp = associated_pointer(associates, t);
- }
- UNPACK_PUSH(tmp);
- }
- }
- break;
-
- case 'w':
- {
+ char *ptr = RSTRING_PTR(buf), *ss = s;
+ int csum = 0;
+ int c1, c2;
+
+ while (s < send) {
+ if (*s == '=') {
+ if (++s == send) break;
+ if (s+1 < send && *s == '\r' && *(s+1) == '\n')
+ s++;
+ if (*s != '\n') {
+ if ((c1 = hex2num(*s)) == -1) break;
+ if (++s == send) break;
+ if ((c2 = hex2num(*s)) == -1) break;
+ csum |= *ptr++ = castchar(c1 << 4 | c2);
+ }
+ }
+ else {
+ csum |= *ptr++ = *s;
+ }
+ s++;
+ ss = s;
+ }
+ rb_str_set_len(buf, ptr - RSTRING_PTR(buf));
+ rb_str_buf_cat(buf, ss, send-ss);
+ csum = ISASCII(csum) ? ENC_CODERANGE_7BIT : ENC_CODERANGE_VALID;
+ ENCODING_CODERANGE_SET(buf, rb_ascii8bit_encindex(), csum);
+ UNPACK_PUSH(buf);
+ }
+ break;
+
+ case '@':
+ if (len > RSTRING_LEN(str))
+ rb_raise(rb_eArgError, "@ outside of string");
+ s = RSTRING_PTR(str) + len;
+ break;
+
+ case 'X':
+ if (len > s - RSTRING_PTR(str))
+ rb_raise(rb_eArgError, "X outside of string");
+ s -= len;
+ break;
+
+ case 'x':
+ if (len > send - s)
+ rb_raise(rb_eArgError, "x outside of string");
+ s += len;
+ break;
+
+ case 'P':
+ if (sizeof(char *) <= (size_t)(send - s)) {
+ VALUE tmp = Qnil;
+ char *t;
+
+ UNPACK_FETCH(&t, char *);
+ if (t) {
+ if (!associates) associates = str_associated(str);
+ tmp = associated_pointer(associates, t);
+ if (len < RSTRING_LEN(tmp)) {
+ tmp = rb_str_new(t, len);
+ str_associate(tmp, associates);
+ }
+ }
+ UNPACK_PUSH(tmp);
+ }
+ break;
+
+ case 'p':
+ if (len > (long)((send - s) / sizeof(char *)))
+ len = (send - s) / sizeof(char *);
+ while (len-- > 0) {
+ if ((size_t)(send - s) < sizeof(char *))
+ break;
+ else {
+ VALUE tmp = Qnil;
+ char *t;
+
+ UNPACK_FETCH(&t, char *);
+ if (t) {
+ if (!associates) associates = str_associated(str);
+ tmp = associated_pointer(associates, t);
+ }
+ UNPACK_PUSH(tmp);
+ }
+ }
+ break;
+
+ case 'w':
+ {
char *s0 = s;
while (len > 0 && s < send) {
if (*s & 0x80) {
@@ -1608,13 +1596,13 @@ pack_unpack_internal(VALUE str, VALUE fmt, int mode, long offset)
s0 = s;
}
}
- }
- break;
+ }
+ break;
- default:
+ default:
unknown_directive("unpack", type, fmt);
- break;
- }
+ break;
+ }
}
return ary;
@@ -1623,7 +1611,7 @@ pack_unpack_internal(VALUE str, VALUE fmt, int mode, long offset)
static VALUE
pack_unpack(rb_execution_context_t *ec, VALUE str, VALUE fmt, VALUE offset)
{
- int mode = rb_block_given_p() ? UNPACK_BLOCK : UNPACK_ARRAY;
+ enum unpack_mode mode = rb_block_given_p() ? UNPACK_BLOCK : UNPACK_ARRAY;
return pack_unpack_internal(str, fmt, mode, RB_NUM2LONG(offset));
}
@@ -1637,43 +1625,43 @@ int
rb_uv_to_utf8(char buf[6], unsigned long uv)
{
if (uv <= 0x7f) {
- buf[0] = (char)uv;
- return 1;
+ buf[0] = (char)uv;
+ return 1;
}
if (uv <= 0x7ff) {
- buf[0] = castchar(((uv>>6)&0xff)|0xc0);
- buf[1] = castchar((uv&0x3f)|0x80);
- return 2;
+ buf[0] = castchar(((uv>>6)&0xff)|0xc0);
+ buf[1] = castchar((uv&0x3f)|0x80);
+ return 2;
}
if (uv <= 0xffff) {
- buf[0] = castchar(((uv>>12)&0xff)|0xe0);
- buf[1] = castchar(((uv>>6)&0x3f)|0x80);
- buf[2] = castchar((uv&0x3f)|0x80);
- return 3;
+ buf[0] = castchar(((uv>>12)&0xff)|0xe0);
+ buf[1] = castchar(((uv>>6)&0x3f)|0x80);
+ buf[2] = castchar((uv&0x3f)|0x80);
+ return 3;
}
if (uv <= 0x1fffff) {
- buf[0] = castchar(((uv>>18)&0xff)|0xf0);
- buf[1] = castchar(((uv>>12)&0x3f)|0x80);
- buf[2] = castchar(((uv>>6)&0x3f)|0x80);
- buf[3] = castchar((uv&0x3f)|0x80);
- return 4;
+ buf[0] = castchar(((uv>>18)&0xff)|0xf0);
+ buf[1] = castchar(((uv>>12)&0x3f)|0x80);
+ buf[2] = castchar(((uv>>6)&0x3f)|0x80);
+ buf[3] = castchar((uv&0x3f)|0x80);
+ return 4;
}
if (uv <= 0x3ffffff) {
- buf[0] = castchar(((uv>>24)&0xff)|0xf8);
- buf[1] = castchar(((uv>>18)&0x3f)|0x80);
- buf[2] = castchar(((uv>>12)&0x3f)|0x80);
- buf[3] = castchar(((uv>>6)&0x3f)|0x80);
- buf[4] = castchar((uv&0x3f)|0x80);
- return 5;
+ buf[0] = castchar(((uv>>24)&0xff)|0xf8);
+ buf[1] = castchar(((uv>>18)&0x3f)|0x80);
+ buf[2] = castchar(((uv>>12)&0x3f)|0x80);
+ buf[3] = castchar(((uv>>6)&0x3f)|0x80);
+ buf[4] = castchar((uv&0x3f)|0x80);
+ return 5;
}
if (uv <= 0x7fffffff) {
- buf[0] = castchar(((uv>>30)&0xff)|0xfc);
- buf[1] = castchar(((uv>>24)&0x3f)|0x80);
- buf[2] = castchar(((uv>>18)&0x3f)|0x80);
- buf[3] = castchar(((uv>>12)&0x3f)|0x80);
- buf[4] = castchar(((uv>>6)&0x3f)|0x80);
- buf[5] = castchar((uv&0x3f)|0x80);
- return 6;
+ buf[0] = castchar(((uv>>30)&0xff)|0xfc);
+ buf[1] = castchar(((uv>>24)&0x3f)|0x80);
+ buf[2] = castchar(((uv>>18)&0x3f)|0x80);
+ buf[3] = castchar(((uv>>12)&0x3f)|0x80);
+ buf[4] = castchar(((uv>>6)&0x3f)|0x80);
+ buf[5] = castchar((uv&0x3f)|0x80);
+ return 6;
}
rb_raise(rb_eRangeError, "pack(U): value out of range");
@@ -1698,12 +1686,12 @@ utf8_to_uv(const char *p, long *lenp)
long n;
if (!(uv & 0x80)) {
- *lenp = 1;
+ *lenp = 1;
return uv;
}
if (!(uv & 0x40)) {
- *lenp = 1;
- rb_raise(rb_eArgError, "malformed UTF-8 character");
+ *lenp = 1;
+ rb_raise(rb_eArgError, "malformed UTF-8 character");
}
if (!(uv & 0x20)) { n = 2; uv &= 0x1f; }
@@ -1712,30 +1700,30 @@ utf8_to_uv(const char *p, long *lenp)
else if (!(uv & 0x04)) { n = 5; uv &= 0x03; }
else if (!(uv & 0x02)) { n = 6; uv &= 0x01; }
else {
- *lenp = 1;
- rb_raise(rb_eArgError, "malformed UTF-8 character");
+ *lenp = 1;
+ rb_raise(rb_eArgError, "malformed UTF-8 character");
}
if (n > *lenp) {
- rb_raise(rb_eArgError, "malformed UTF-8 character (expected %ld bytes, given %ld bytes)",
- n, *lenp);
+ rb_raise(rb_eArgError, "malformed UTF-8 character (expected %ld bytes, given %ld bytes)",
+ n, *lenp);
}
*lenp = n--;
if (n != 0) {
- while (n--) {
- c = *p++ & 0xff;
- if ((c & 0xc0) != 0x80) {
- *lenp -= n + 1;
- rb_raise(rb_eArgError, "malformed UTF-8 character");
- }
- else {
- c &= 0x3f;
- uv = uv << 6 | c;
- }
- }
+ while (n--) {
+ c = *p++ & 0xff;
+ if ((c & 0xc0) != 0x80) {
+ *lenp -= n + 1;
+ rb_raise(rb_eArgError, "malformed UTF-8 character");
+ }
+ else {
+ c &= 0x3f;
+ uv = uv << 6 | c;
+ }
+ }
}
n = *lenp - 1;
if (uv < utf8_limits[n]) {
- rb_raise(rb_eArgError, "redundant UTF-8 sequence");
+ rb_raise(rb_eArgError, "redundant UTF-8 sequence");
}
return uv;
}
diff --git a/pack.rb b/pack.rb
index 0f5b75ba43..d505eaee35 100644
--- a/pack.rb
+++ b/pack.rb
@@ -1,143 +1,9 @@
class Array
# call-seq:
- # arr.pack( aTemplateString ) -> aBinaryString
- # arr.pack( aTemplateString, buffer: aBufferString ) -> aBufferString
+ # pack(template, buffer: nil) -> string
#
- # Packs the contents of <i>arr</i> into a binary sequence according to
- # the directives in <i>aTemplateString</i> (see the table below)
- # Directives ``A,'' ``a,'' and ``Z'' may be followed by a count,
- # which gives the width of the resulting field. The remaining
- # directives also may take a count, indicating the number of array
- # elements to convert. If the count is an asterisk
- # (``<code>*</code>''), all remaining array elements will be
- # converted. Any of the directives ``<code>sSiIlL</code>'' may be
- # followed by an underscore (``<code>_</code>'') or
- # exclamation mark (``<code>!</code>'') to use the underlying
- # platform's native size for the specified type; otherwise, they use a
- # platform-independent size. Spaces are ignored in the template
- # string. See also String#unpack.
- #
- # a = [ "a", "b", "c" ]
- # n = [ 65, 66, 67 ]
- # a.pack("A3A3A3") #=> "a b c "
- # a.pack("a3a3a3") #=> "a\000\000b\000\000c\000\000"
- # n.pack("ccc") #=> "ABC"
- #
- # If <i>aBufferString</i> is specified and its capacity is enough,
- # +pack+ uses it as the buffer and returns it.
- # When the offset is specified by the beginning of <i>aTemplateString</i>,
- # the result is filled after the offset.
- # If original contents of <i>aBufferString</i> exists and it's longer than
- # the offset, the rest of <i>offsetOfBuffer</i> are overwritten by the result.
- # If it's shorter, the gap is filled with ``<code>\0</code>''.
- #
- # # packed data is appended by default
- # [255].pack("C", buffer:"foo".b) #=> "foo\xFF"
- #
- # # "@0" (offset 0) specifies that packed data is filled from beginning.
- # # Also, original data after packed data is removed. ("oo" is removed.)
- # [255].pack("@0C", buffer:"foo".b) #=> "\xFF"
- #
- # # If the offset is bigger than the original length, \x00 is filled.
- # [255].pack("@5C", buffer:"foo".b) #=> "foo\x00\x00\xFF"
- #
- # Note that ``buffer:'' option does not guarantee not to allocate memory
- # in +pack+. If the capacity of <i>aBufferString</i> is not enough,
- # +pack+ allocates memory.
- #
- # Directives for +pack+.
- #
- # Integer | Array |
- # Directive | Element | Meaning
- # ----------------------------------------------------------------------------
- # C | Integer | 8-bit unsigned (unsigned char)
- # S | Integer | 16-bit unsigned, native endian (uint16_t)
- # L | Integer | 32-bit unsigned, native endian (uint32_t)
- # Q | Integer | 64-bit unsigned, native endian (uint64_t)
- # J | Integer | pointer width unsigned, native endian (uintptr_t)
- # | | (J is available since Ruby 2.3.)
- # | |
- # c | Integer | 8-bit signed (signed char)
- # s | Integer | 16-bit signed, native endian (int16_t)
- # l | Integer | 32-bit signed, native endian (int32_t)
- # q | Integer | 64-bit signed, native endian (int64_t)
- # j | Integer | pointer width signed, native endian (intptr_t)
- # | | (j is available since Ruby 2.3.)
- # | |
- # S_ S! | Integer | unsigned short, native endian
- # I I_ I! | Integer | unsigned int, native endian
- # L_ L! | Integer | unsigned long, native endian
- # Q_ Q! | Integer | unsigned long long, native endian (ArgumentError
- # | | if the platform has no long long type.)
- # | | (Q_ and Q! is available since Ruby 2.1.)
- # J! | Integer | uintptr_t, native endian (same with J)
- # | | (J! is available since Ruby 2.3.)
- # | |
- # s_ s! | Integer | signed short, native endian
- # i i_ i! | Integer | signed int, native endian
- # l_ l! | Integer | signed long, native endian
- # q_ q! | Integer | signed long long, native endian (ArgumentError
- # | | if the platform has no long long type.)
- # | | (q_ and q! is available since Ruby 2.1.)
- # j! | Integer | intptr_t, native endian (same with j)
- # | | (j! is available since Ruby 2.3.)
- # | |
- # S> s> S!> s!> | Integer | same as the directives without ">" except
- # L> l> L!> l!> | | big endian
- # I!> i!> | | (available since Ruby 1.9.3)
- # Q> q> Q!> q!> | | "S>" is the same as "n"
- # J> j> J!> j!> | | "L>" is the same as "N"
- # | |
- # S< s< S!< s!< | Integer | same as the directives without "<" except
- # L< l< L!< l!< | | little endian
- # I!< i!< | | (available since Ruby 1.9.3)
- # Q< q< Q!< q!< | | "S<" is the same as "v"
- # J< j< J!< j!< | | "L<" is the same as "V"
- # | |
- # n | Integer | 16-bit unsigned, network (big-endian) byte order
- # N | Integer | 32-bit unsigned, network (big-endian) byte order
- # v | Integer | 16-bit unsigned, VAX (little-endian) byte order
- # V | Integer | 32-bit unsigned, VAX (little-endian) byte order
- # | |
- # U | Integer | UTF-8 character
- # w | Integer | BER-compressed integer
- #
- # Float | Array |
- # Directive | Element | Meaning
- # ---------------------------------------------------------------------------
- # D d | Float | double-precision, native format
- # F f | Float | single-precision, native format
- # E | Float | double-precision, little-endian byte order
- # e | Float | single-precision, little-endian byte order
- # G | Float | double-precision, network (big-endian) byte order
- # g | Float | single-precision, network (big-endian) byte order
- #
- # String | Array |
- # Directive | Element | Meaning
- # ---------------------------------------------------------------------------
- # A | String | arbitrary binary string (space padded, count is width)
- # a | String | arbitrary binary string (null padded, count is width)
- # Z | String | same as ``a'', except that null is added with *
- # B | String | bit string (MSB first)
- # b | String | bit string (LSB first)
- # H | String | hex string (high nibble first)
- # h | String | hex string (low nibble first)
- # u | String | UU-encoded string
- # M | String | quoted printable, MIME encoding (see also RFC2045)
- # | | (text mode but input must use LF and output LF)
- # m | String | base64 encoded string (see RFC 2045)
- # | | (if count is 0, no line feed are added, see RFC 4648)
- # | | (count specifies input bytes between each LF,
- # | | rounded down to nearest multiple of 3)
- # P | String | pointer to a structure (fixed-length string)
- # p | String | pointer to a null-terminated string
- #
- # Misc. | Array |
- # Directive | Element | Meaning
- # ---------------------------------------------------------------------------
- # @ | --- | moves to absolute position
- # X | --- | back up a byte
- # x | --- | null byte
+ # Formats each element in +self+ into a binary string; returns that string.
+ # See {Packed Data}[rdoc-ref:packed_data.rdoc].
def pack(fmt, buffer: nil)
Primitive.pack_pack(fmt, buffer)
end
@@ -145,164 +11,20 @@ end
class String
# call-seq:
- # str.unpack(format) -> anArray
- # str.unpack(format, offset: anInteger) -> anArray
- #
- # Decodes <i>str</i> (which may contain binary data) according to the
- # format string, returning an array of each value extracted.
- # The format string consists of a sequence of single-character directives,
- # summarized in the table at the end of this entry.
- # Each directive may be followed
- # by a number, indicating the number of times to repeat with this
- # directive. An asterisk (``<code>*</code>'') will use up all
- # remaining elements. The directives <code>sSiIlL</code> may each be
- # followed by an underscore (``<code>_</code>'') or
- # exclamation mark (``<code>!</code>'') to use the underlying
- # platform's native size for the specified type; otherwise, it uses a
- # platform-independent consistent size. Spaces are ignored in the
- # format string.
- #
- # See also String#unpack1, Array#pack.
- #
- # "abc \0\0abc \0\0".unpack('A6Z6') #=> ["abc", "abc "]
- # "abc \0\0".unpack('a3a3') #=> ["abc", " \000\000"]
- # "abc \0abc \0".unpack('Z*Z*') #=> ["abc ", "abc "]
- # "aa".unpack('b8B8') #=> ["10000110", "01100001"]
- # "aaa".unpack('h2H2c') #=> ["16", "61", 97]
- # "\xfe\xff\xfe\xff".unpack('sS') #=> [-2, 65534]
- # "now=20is".unpack('M*') #=> ["now is"]
- # "whole".unpack('xax2aX2aX1aX2a') #=> ["h", "e", "l", "l", "o"]
- #
- # This table summarizes the various formats and the Ruby classes
- # returned by each.
+ # unpack(template, offset: 0) -> array
#
- # Integer | |
- # Directive | Returns | Meaning
- # ------------------------------------------------------------------
- # C | Integer | 8-bit unsigned (unsigned char)
- # S | Integer | 16-bit unsigned, native endian (uint16_t)
- # L | Integer | 32-bit unsigned, native endian (uint32_t)
- # Q | Integer | 64-bit unsigned, native endian (uint64_t)
- # J | Integer | pointer width unsigned, native endian (uintptr_t)
- # | |
- # c | Integer | 8-bit signed (signed char)
- # s | Integer | 16-bit signed, native endian (int16_t)
- # l | Integer | 32-bit signed, native endian (int32_t)
- # q | Integer | 64-bit signed, native endian (int64_t)
- # j | Integer | pointer width signed, native endian (intptr_t)
- # | |
- # S_ S! | Integer | unsigned short, native endian
- # I I_ I! | Integer | unsigned int, native endian
- # L_ L! | Integer | unsigned long, native endian
- # Q_ Q! | Integer | unsigned long long, native endian (ArgumentError
- # | | if the platform has no long long type.)
- # J! | Integer | uintptr_t, native endian (same with J)
- # | |
- # s_ s! | Integer | signed short, native endian
- # i i_ i! | Integer | signed int, native endian
- # l_ l! | Integer | signed long, native endian
- # q_ q! | Integer | signed long long, native endian (ArgumentError
- # | | if the platform has no long long type.)
- # j! | Integer | intptr_t, native endian (same with j)
- # | |
- # S> s> S!> s!> | Integer | same as the directives without ">" except
- # L> l> L!> l!> | | big endian
- # I!> i!> | |
- # Q> q> Q!> q!> | | "S>" is the same as "n"
- # J> j> J!> j!> | | "L>" is the same as "N"
- # | |
- # S< s< S!< s!< | Integer | same as the directives without "<" except
- # L< l< L!< l!< | | little endian
- # I!< i!< | |
- # Q< q< Q!< q!< | | "S<" is the same as "v"
- # J< j< J!< j!< | | "L<" is the same as "V"
- # | |
- # n | Integer | 16-bit unsigned, network (big-endian) byte order
- # N | Integer | 32-bit unsigned, network (big-endian) byte order
- # v | Integer | 16-bit unsigned, VAX (little-endian) byte order
- # V | Integer | 32-bit unsigned, VAX (little-endian) byte order
- # | |
- # U | Integer | UTF-8 character
- # w | Integer | BER-compressed integer (see Array#pack)
- #
- # Float | |
- # Directive | Returns | Meaning
- # -----------------------------------------------------------------
- # D d | Float | double-precision, native format
- # F f | Float | single-precision, native format
- # E | Float | double-precision, little-endian byte order
- # e | Float | single-precision, little-endian byte order
- # G | Float | double-precision, network (big-endian) byte order
- # g | Float | single-precision, network (big-endian) byte order
- #
- # String | |
- # Directive | Returns | Meaning
- # -----------------------------------------------------------------
- # A | String | arbitrary binary string (remove trailing nulls and ASCII spaces)
- # a | String | arbitrary binary string
- # Z | String | null-terminated string
- # B | String | bit string (MSB first)
- # b | String | bit string (LSB first)
- # H | String | hex string (high nibble first)
- # h | String | hex string (low nibble first)
- # u | String | UU-encoded string
- # M | String | quoted-printable, MIME encoding (see RFC2045)
- # m | String | base64 encoded string (RFC 2045) (default)
- # | | base64 encoded string (RFC 4648) if followed by 0
- # P | String | pointer to a structure (fixed-length string)
- # p | String | pointer to a null-terminated string
- #
- # Misc. | |
- # Directive | Returns | Meaning
- # -----------------------------------------------------------------
- # @ | --- | skip to the offset given by the length argument
- # X | --- | skip backward one byte
- # x | --- | skip forward one byte
- #
- # The keyword <i>offset</i> can be given to start the decoding after skipping
- # the specified amount of bytes:
- # "abc".unpack("C*") # => [97, 98, 99]
- # "abc".unpack("C*", offset: 2) # => [99]
- # "abc".unpack("C*", offset: 4) # => offset outside of string (ArgumentError)
- #
- # HISTORY
- #
- # * J, J! j, and j! are available since Ruby 2.3.
- # * Q_, Q!, q_, and q! are available since Ruby 2.1.
- # * I!<, i!<, I!>, and i!> are available since Ruby 1.9.3.
+ # Extracts data from +self+, forming objects that become the elements of a new array;
+ # returns that array.
+ # See {Packed Data}[rdoc-ref:packed_data.rdoc].
def unpack(fmt, offset: 0)
Primitive.pack_unpack(fmt, offset)
end
# call-seq:
- # str.unpack1(format) -> obj
- # str.unpack1(format, offset: anInteger) -> obj
- #
- # Decodes <i>str</i> (which may contain binary data) according to the
- # format string, returning the first value extracted.
- #
- # See also String#unpack, Array#pack.
- #
- # Contrast with String#unpack:
- #
- # "abc \0\0abc \0\0".unpack('A6Z6') #=> ["abc", "abc "]
- # "abc \0\0abc \0\0".unpack1('A6Z6') #=> "abc"
- #
- # In that case data would be lost but often it's the case that the array
- # only holds one value, especially when unpacking binary data. For instance:
- #
- # "\xff\x00\x00\x00".unpack("l") #=> [255]
- # "\xff\x00\x00\x00".unpack1("l") #=> 255
- #
- # Thus unpack1 is convenient, makes clear the intention and signals
- # the expected return value to those reading the code.
- #
- # The keyword <i>offset</i> can be given to start the decoding after skipping
- # the specified amount of bytes:
- # "abc".unpack1("C*") # => 97
- # "abc".unpack1("C*", offset: 2) # => 99
- # "abc".unpack1("C*", offset: 4) # => offset outside of string (ArgumentError)
+ # unpack1(template, offset: 0) -> object
#
+ # Like String#unpack, but unpacks and returns only the first extracted object.
+ # See {Packed Data}[rdoc-ref:packed_data.rdoc].
def unpack1(fmt, offset: 0)
Primitive.pack_unpack1(fmt, offset)
end
diff --git a/parse.y b/parse.y
index b4c3106b8c..1c808bd60e 100644
--- a/parse.y
+++ b/parse.y
@@ -9,6 +9,8 @@
**********************************************************************/
+%require "3.0"
+
%{
#if !YYPURE
@@ -32,6 +34,7 @@ struct lex_context;
#include "internal/compile.h"
#include "internal/compilers.h"
#include "internal/complex.h"
+#include "internal/encoding.h"
#include "internal/error.h"
#include "internal/hash.h"
#include "internal/imemo.h"
@@ -70,7 +73,7 @@ struct lex_context {
BITFIELD(enum shareability, shareable_constant_value, 2);
};
-#ifdef __GNUC__
+#if defined(__GNUC__) && !defined(__clang__)
// Suppress "parameter passing for argument of type 'struct
// lex_context' changed" notes. `struct lex_context` is file scope,
// and has no ABI compatibility issue.
@@ -122,7 +125,13 @@ RBIMPL_WARNING_POP()
#define RUBY_SET_YYLLOC_FROM_STRTERM_HEREDOC(Current) \
rb_parser_set_location_from_strterm_heredoc(p, &p->lex.strterm->u.heredoc, &(Current))
-#define RUBY_SET_YYLLOC_OF_NONE(Current) \
+#define RUBY_SET_YYLLOC_OF_DELAYED_TOKEN(Current) \
+ rb_parser_set_location_of_delayed_token(p, &(Current))
+#define RUBY_SET_YYLLOC_OF_HEREDOC_END(Current) \
+ rb_parser_set_location_of_heredoc_end(p, &(Current))
+#define RUBY_SET_YYLLOC_OF_DUMMY_END(Current) \
+ rb_parser_set_location_of_dummy_end(p, &(Current))
+#define RUBY_SET_YYLLOC_OF_NONE(Current) \
rb_parser_set_location_of_none(p, &(Current))
#define RUBY_SET_YYLLOC(Current) \
rb_parser_set_location(p, &(Current))
@@ -270,12 +279,12 @@ struct parser_params {
rb_imemo_tmpbuf_t *heap;
YYSTYPE *lval;
+ YYLTYPE *yylloc;
struct {
rb_strterm_t *strterm;
VALUE (*gets)(struct parser_params*,VALUE);
VALUE input;
- VALUE prevline;
VALUE lastline;
VALUE nextline;
const char *pbeg;
@@ -318,6 +327,14 @@ struct parser_params {
VALUE debug_buffer;
VALUE debug_output;
+ struct {
+ VALUE token;
+ int beg_line;
+ int beg_col;
+ int end_line;
+ int end_col;
+ } delayed;
+
ID cur_arg;
rb_ast_t *ast;
@@ -348,21 +365,23 @@ struct parser_params {
unsigned int do_chomp: 1;
unsigned int do_split: 1;
unsigned int keep_script_lines: 1;
+ unsigned int error_tolerant: 1;
+ unsigned int keep_tokens: 1;
NODE *eval_tree_begin;
NODE *eval_tree;
VALUE error_buffer;
VALUE debug_lines;
const struct rb_iseq_struct *parent_iseq;
+ /* store specific keyword locations to generate dummy end token */
+ VALUE end_expect_token_locations;
+ /* id for terms */
+ int token_id;
+ /* Array for term tokens */
+ VALUE tokens;
#else
/* Ripper only */
- struct {
- VALUE token;
- int line;
- int col;
- } delayed;
-
VALUE value;
VALUE result;
VALUE parsing_thread;
@@ -407,6 +426,214 @@ pop_pktbl(struct parser_params *p, st_table *tbl)
p->pktbl = tbl;
}
+#ifndef RIPPER
+static void flush_debug_buffer(struct parser_params *p, VALUE out, VALUE str);
+
+static void
+debug_end_expect_token_locations(struct parser_params *p, const char *name)
+{
+ if(p->debug) {
+ VALUE mesg = rb_sprintf("%s: ", name);
+ rb_str_catf(mesg, " %"PRIsVALUE"\n", p->end_expect_token_locations);
+ flush_debug_buffer(p, p->debug_output, mesg);
+ }
+}
+
+static void
+push_end_expect_token_locations(struct parser_params *p, const rb_code_position_t *pos)
+{
+ if(NIL_P(p->end_expect_token_locations)) return;
+ rb_ary_push(p->end_expect_token_locations, rb_ary_new_from_args(2, INT2NUM(pos->lineno), INT2NUM(pos->column)));
+ debug_end_expect_token_locations(p, "push_end_expect_token_locations");
+}
+
+static void
+pop_end_expect_token_locations(struct parser_params *p)
+{
+ if(NIL_P(p->end_expect_token_locations)) return;
+ rb_ary_pop(p->end_expect_token_locations);
+ debug_end_expect_token_locations(p, "pop_end_expect_token_locations");
+}
+
+static VALUE
+peek_end_expect_token_locations(struct parser_params *p)
+{
+ if(NIL_P(p->end_expect_token_locations)) return Qnil;
+ return rb_ary_last(0, 0, p->end_expect_token_locations);
+}
+
+static ID
+parser_token2id(enum yytokentype tok)
+{
+ switch ((int) tok) {
+#define TOKEN2ID(tok) case tok: return rb_intern(#tok);
+#define TOKEN2ID2(tok, name) case tok: return rb_intern(name);
+ TOKEN2ID2(' ', "words_sep")
+ TOKEN2ID2('!', "!")
+ TOKEN2ID2('%', "%");
+ TOKEN2ID2('&', "&");
+ TOKEN2ID2('*', "*");
+ TOKEN2ID2('+', "+");
+ TOKEN2ID2('-', "-");
+ TOKEN2ID2('/', "/");
+ TOKEN2ID2('<', "<");
+ TOKEN2ID2('=', "=");
+ TOKEN2ID2('>', ">");
+ TOKEN2ID2('?', "?");
+ TOKEN2ID2('^', "^");
+ TOKEN2ID2('|', "|");
+ TOKEN2ID2('~', "~");
+ TOKEN2ID2(':', ":");
+ TOKEN2ID2(',', ",");
+ TOKEN2ID2('.', ".");
+ TOKEN2ID2(';', ";");
+ TOKEN2ID2('`', "`");
+ TOKEN2ID2('\n', "nl");
+ TOKEN2ID2('{', "{");
+ TOKEN2ID2('}', "}");
+ TOKEN2ID2('[', "[");
+ TOKEN2ID2(']', "]");
+ TOKEN2ID2('(', "(");
+ TOKEN2ID2(')', ")");
+ TOKEN2ID(keyword_class);
+ TOKEN2ID(keyword_module);
+ TOKEN2ID(keyword_def);
+ TOKEN2ID(keyword_undef);
+ TOKEN2ID(keyword_begin);
+ TOKEN2ID(keyword_rescue);
+ TOKEN2ID(keyword_ensure);
+ TOKEN2ID(keyword_end);
+ TOKEN2ID(keyword_if);
+ TOKEN2ID(keyword_unless);
+ TOKEN2ID(keyword_then);
+ TOKEN2ID(keyword_elsif);
+ TOKEN2ID(keyword_else);
+ TOKEN2ID(keyword_case);
+ TOKEN2ID(keyword_when);
+ TOKEN2ID(keyword_while);
+ TOKEN2ID(keyword_until);
+ TOKEN2ID(keyword_for);
+ TOKEN2ID(keyword_break);
+ TOKEN2ID(keyword_next);
+ TOKEN2ID(keyword_redo);
+ TOKEN2ID(keyword_retry);
+ TOKEN2ID(keyword_in);
+ TOKEN2ID(keyword_do);
+ TOKEN2ID(keyword_do_cond);
+ TOKEN2ID(keyword_do_block);
+ TOKEN2ID(keyword_do_LAMBDA);
+ TOKEN2ID(keyword_return);
+ TOKEN2ID(keyword_yield);
+ TOKEN2ID(keyword_super);
+ TOKEN2ID(keyword_self);
+ TOKEN2ID(keyword_nil);
+ TOKEN2ID(keyword_true);
+ TOKEN2ID(keyword_false);
+ TOKEN2ID(keyword_and);
+ TOKEN2ID(keyword_or);
+ TOKEN2ID(keyword_not);
+ TOKEN2ID(modifier_if);
+ TOKEN2ID(modifier_unless);
+ TOKEN2ID(modifier_while);
+ TOKEN2ID(modifier_until);
+ TOKEN2ID(modifier_rescue);
+ TOKEN2ID(keyword_alias);
+ TOKEN2ID(keyword_defined);
+ TOKEN2ID(keyword_BEGIN);
+ TOKEN2ID(keyword_END);
+ TOKEN2ID(keyword__LINE__);
+ TOKEN2ID(keyword__FILE__);
+ TOKEN2ID(keyword__ENCODING__);
+ TOKEN2ID(tIDENTIFIER);
+ TOKEN2ID(tFID);
+ TOKEN2ID(tGVAR);
+ TOKEN2ID(tIVAR);
+ TOKEN2ID(tCONSTANT);
+ TOKEN2ID(tCVAR);
+ TOKEN2ID(tLABEL);
+ TOKEN2ID(tINTEGER);
+ TOKEN2ID(tFLOAT);
+ TOKEN2ID(tRATIONAL);
+ TOKEN2ID(tIMAGINARY);
+ TOKEN2ID(tCHAR);
+ TOKEN2ID(tNTH_REF);
+ TOKEN2ID(tBACK_REF);
+ TOKEN2ID(tSTRING_CONTENT);
+ TOKEN2ID(tREGEXP_END);
+ TOKEN2ID(tDUMNY_END);
+ TOKEN2ID(tSP);
+ TOKEN2ID(tUPLUS);
+ TOKEN2ID(tUMINUS);
+ TOKEN2ID(tPOW);
+ TOKEN2ID(tCMP);
+ TOKEN2ID(tEQ);
+ TOKEN2ID(tEQQ);
+ TOKEN2ID(tNEQ);
+ TOKEN2ID(tGEQ);
+ TOKEN2ID(tLEQ);
+ TOKEN2ID(tANDOP);
+ TOKEN2ID(tOROP);
+ TOKEN2ID(tMATCH);
+ TOKEN2ID(tNMATCH);
+ TOKEN2ID(tDOT2);
+ TOKEN2ID(tDOT3);
+ TOKEN2ID(tBDOT2);
+ TOKEN2ID(tBDOT3);
+ TOKEN2ID(tAREF);
+ TOKEN2ID(tASET);
+ TOKEN2ID(tLSHFT);
+ TOKEN2ID(tRSHFT);
+ TOKEN2ID(tANDDOT);
+ TOKEN2ID(tCOLON2);
+ TOKEN2ID(tCOLON3);
+ TOKEN2ID(tOP_ASGN);
+ TOKEN2ID(tASSOC);
+ TOKEN2ID(tLPAREN);
+ TOKEN2ID(tLPAREN_ARG);
+ TOKEN2ID(tRPAREN);
+ TOKEN2ID(tLBRACK);
+ TOKEN2ID(tLBRACE);
+ TOKEN2ID(tLBRACE_ARG);
+ TOKEN2ID(tSTAR);
+ TOKEN2ID(tDSTAR);
+ TOKEN2ID(tAMPER);
+ TOKEN2ID(tLAMBDA);
+ TOKEN2ID(tSYMBEG);
+ TOKEN2ID(tSTRING_BEG);
+ TOKEN2ID(tXSTRING_BEG);
+ TOKEN2ID(tREGEXP_BEG);
+ TOKEN2ID(tWORDS_BEG);
+ TOKEN2ID(tQWORDS_BEG);
+ TOKEN2ID(tSYMBOLS_BEG);
+ TOKEN2ID(tQSYMBOLS_BEG);
+ TOKEN2ID(tSTRING_END);
+ TOKEN2ID(tSTRING_DEND);
+ TOKEN2ID(tSTRING_DBEG);
+ TOKEN2ID(tSTRING_DVAR);
+ TOKEN2ID(tLAMBEG);
+ TOKEN2ID(tLABEL_END);
+ TOKEN2ID(tIGNORED_NL);
+ TOKEN2ID(tCOMMENT);
+ TOKEN2ID(tEMBDOC_BEG);
+ TOKEN2ID(tEMBDOC);
+ TOKEN2ID(tEMBDOC_END);
+ TOKEN2ID(tHEREDOC_BEG);
+ TOKEN2ID(tHEREDOC_END);
+ TOKEN2ID(k__END__);
+ TOKEN2ID(tLOWEST);
+ TOKEN2ID(tUMINUS_NUM);
+ TOKEN2ID(tLAST_TOKEN);
+#undef TOKEN2ID
+#undef TOKEN2ID2
+ }
+
+ rb_bug("parser_token2id: unknown token %d", tok);
+
+ UNREACHABLE_RETURN(0);
+}
+
+#endif
+
RBIMPL_ATTR_NONNULL((1, 2, 3))
static int parser_yyerror(struct parser_params*, const YYLTYPE *yylloc, const char*);
RBIMPL_ATTR_NONNULL((1, 2))
@@ -415,6 +642,9 @@ static int parser_yyerror0(struct parser_params*, const char*);
#define yyerror1(loc, msg) parser_yyerror(p, (loc), (msg))
#define yyerror(yylloc, p, msg) parser_yyerror(p, yylloc, msg)
#define token_flush(ptr) ((ptr)->lex.ptok = (ptr)->lex.pcur)
+#define lex_goto_eol(p) ((p)->lex.pcur = (p)->lex.pend)
+#define lex_eol_p(p) ((p)->lex.pcur >= (p)->lex.pend)
+#define lex_eol_n_p(p,n) ((p)->lex.pcur+(n) >= (p)->lex.pend)
static void token_info_setup(token_info *ptinfo, const char *ptr, const rb_code_location_t *loc);
static void token_info_push(struct parser_params*, const char *token, const rb_code_location_t *loc);
@@ -436,10 +666,6 @@ static void token_info_drop(struct parser_params *p, const char *token, rb_code_
#define lambda_beginning_p() (p->lex.lpar_beg == p->lex.paren_nest)
-#define ANON_BLOCK_ID '&'
-#define ANON_REST_ID '*'
-#define ANON_KEYWORD_REST_ID idPow
-
static enum yytokentype yylex(YYSTYPE*, YYLTYPE*, struct parser_params*);
#ifndef RIPPER
@@ -468,6 +694,11 @@ static NODE* node_newnode_with_locals(struct parser_params *, enum node_type, VA
static NODE* node_newnode(struct parser_params *, enum node_type, VALUE, VALUE, VALUE, const rb_code_location_t*);
#define rb_node_newnode(type, a1, a2, a3, loc) node_newnode(p, (type), (a1), (a2), (a3), (loc))
+/* Make a new temporal node, which should not be appeared in the
+ * result AST and does not have node_id and location. */
+static NODE* node_new_temporal(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2);
+#define NODE_NEW_TEMPORAL(t,a0,a1,a2) node_new_temporal(p, (t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2))
+
static NODE *nd_set_loc(NODE *nd, const YYLTYPE *loc);
static int
@@ -665,6 +896,9 @@ VALUE rb_parser_lex_state_name(enum lex_state_e state);
void rb_parser_show_bitstack(struct parser_params *, stack_type, const char *, int);
PRINTF_ARGS(void rb_parser_fatal(struct parser_params *p, const char *fmt, ...), 2, 3);
YYLTYPE *rb_parser_set_location_from_strterm_heredoc(struct parser_params *p, rb_strterm_heredoc_t *here, YYLTYPE *yylloc);
+YYLTYPE *rb_parser_set_location_of_delayed_token(struct parser_params *p, YYLTYPE *yylloc);
+YYLTYPE *rb_parser_set_location_of_heredoc_end(struct parser_params *p, YYLTYPE *yylloc);
+YYLTYPE *rb_parser_set_location_of_dummy_end(struct parser_params *p, YYLTYPE *yylloc);
YYLTYPE *rb_parser_set_location_of_none(struct parser_params *p, YYLTYPE *yylloc);
YYLTYPE *rb_parser_set_location(struct parser_params *p, YYLTYPE *yylloc);
RUBY_SYMBOL_EXPORT_END
@@ -712,12 +946,10 @@ static void numparam_pop(struct parser_params *p, NODE *prev_inner);
#endif
#define idFWD_REST '*'
-#ifdef RUBY3_KEYWORDS
#define idFWD_KWREST idPow /* Use simple "**", as tDSTAR is "**arg" */
-#else
-#define idFWD_KWREST 0
-#endif
#define idFWD_BLOCK '&'
+#define idFWD_ALL idDot3
+#define FORWARD_ARGS_WITH_RUBY2_KEYWORDS
#define RE_OPTION_ONCE (1<<16)
#define RE_OPTION_ENCODING_SHIFT 8
@@ -996,10 +1228,13 @@ rescued_expr(struct parser_params *p, NODE *arg, NODE *rescue,
static void
restore_defun(struct parser_params *p, NODE *name)
{
- YYSTYPE c = {.val = name->nd_cval};
+ NODE *save = name->nd_next;
+ YYSTYPE c = {.val = save->nd_cval};
p->cur_arg = name->nd_vid;
p->ctxt.in_def = c.ctxt.in_def;
p->ctxt.shareable_constant_value = c.ctxt.shareable_constant_value;
+ p->max_numparam = (int)save->nd_nth;
+ numparam_pop(p, save->nd_head);
}
static void
@@ -1015,6 +1250,8 @@ endless_method_name(struct parser_params *p, NODE *defn, const YYLTYPE *loc)
token_info_drop(p, "def", loc->beg_pos);
}
+#define debug_token_line(p, name, line) if (p->debug) rb_parser_printf(p, name ":%d (%d: %ld|%ld|%ld)\n", line, p->ruby_sourceline, p->lex.ptok - p->lex.pbeg, p->lex.pcur - p->lex.ptok, p->lex.pend - p->lex.pcur)
+
#ifndef RIPPER
# define Qnone 0
# define Qnull 0
@@ -1101,6 +1338,14 @@ static int looking_at_eol_p(struct parser_params *p);
%define parse.error verbose
%printer {
#ifndef RIPPER
+ if ($$) {
+ rb_parser_printf(p, "%s", ruby_node_name(nd_type($$)));
+ }
+#else
+#endif
+} <node>
+%printer {
+#ifndef RIPPER
rb_parser_printf(p, "%"PRIsVALUE, rb_id2str($$));
#else
rb_parser_printf(p, "%"PRIsVALUE, RNODE($$)->nd_rval);
@@ -1213,6 +1458,7 @@ static int looking_at_eol_p(struct parser_params *p);
%token <node> tBACK_REF "back reference"
%token <node> tSTRING_CONTENT "literal content"
%token <num> tREGEXP_END
+%token <num> tDUMNY_END "dummy end"
%type <node> singleton strings string string1 xstring regexp
%type <node> string_contents xstring_contents regexp_contents string_content
@@ -1305,6 +1551,9 @@ static int looking_at_eol_p(struct parser_params *p);
%token tSTRING_DEND "'}'"
%token tSTRING_DBEG tSTRING_DVAR tLAMBEG tLABEL_END
+%token tIGNORED_NL tCOMMENT tEMBDOC_BEG tEMBDOC tEMBDOC_END
+%token tHEREDOC_BEG tHEREDOC_END k__END__
+
/*
* precedence table
*/
@@ -1389,10 +1638,6 @@ top_stmts : none
/*% %*/
/*% ripper: stmts_add!($1, $3) %*/
}
- | error top_stmt
- {
- $$ = remove_begin($2);
- }
;
top_stmt : stmt
@@ -1462,10 +1707,6 @@ stmts : none
/*% %*/
/*% ripper: stmts_add!($1, $3) %*/
}
- | error stmt
- {
- $$ = remove_begin($2);
- }
;
stmt_or_begin : stmt
@@ -1618,6 +1859,12 @@ stmt : keyword_alias fitem {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
/*% ripper: massign!($1, $4) %*/
}
| expr
+ | error
+ {
+ /*%%%*/
+ $$ = NEW_ERROR(&@$);
+ /*% %*/
+ }
;
command_asgn : lhs '=' lex_ctxt command_rhs
@@ -1669,7 +1916,7 @@ command_asgn : lhs '=' lex_ctxt command_rhs
/*%%%*/
$$ = new_attr_op_assign(p, $1, ID2VAL(idCOLON2), $3, $4, $6, &@$);
/*% %*/
- /*% ripper: opassign!(field!($1, ID2VAL(idCOLON2), $3), $4, $6) %*/
+ /*% ripper: opassign!(field!($1, $2, $3), $4, $6) %*/
}
| defn_head f_opt_paren_args '=' command
{
@@ -1774,14 +2021,18 @@ expr : command_call
p->ctxt.in_kwarg = 1;
$<tbl>$ = push_pvtbl(p);
}
+ {
+ $<tbl>$ = push_pktbl(p);
+ }
p_top_expr_body
{
+ pop_pktbl(p, $<tbl>4);
pop_pvtbl(p, $<tbl>3);
p->ctxt.in_kwarg = $<ctxt>2.in_kwarg;
/*%%%*/
- $$ = NEW_CASE3($1, NEW_IN($4, 0, 0, &@4), &@$);
+ $$ = NEW_CASE3($1, NEW_IN($5, 0, 0, &@5), &@$);
/*% %*/
- /*% ripper: case!($1, in!($4, Qnil, Qnil)) %*/
+ /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/
}
| arg keyword_in
{
@@ -1792,34 +2043,43 @@ expr : command_call
p->ctxt.in_kwarg = 1;
$<tbl>$ = push_pvtbl(p);
}
+ {
+ $<tbl>$ = push_pktbl(p);
+ }
p_top_expr_body
{
+ pop_pktbl(p, $<tbl>4);
pop_pvtbl(p, $<tbl>3);
p->ctxt.in_kwarg = $<ctxt>2.in_kwarg;
/*%%%*/
- $$ = NEW_CASE3($1, NEW_IN($4, NEW_TRUE(&@4), NEW_FALSE(&@4), &@4), &@$);
+ $$ = NEW_CASE3($1, NEW_IN($5, NEW_TRUE(&@5), NEW_FALSE(&@5), &@5), &@$);
/*% %*/
- /*% ripper: case!($1, in!($4, Qnil, Qnil)) %*/
+ /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/
}
| arg %prec tLBRACE_ARG
;
-def_name : fname
- {
- ID fname = get_id($1);
- ID cur_arg = p->cur_arg;
- YYSTYPE c = {.ctxt = p->ctxt};
- numparam_name(p, fname);
- local_push(p, 0);
- p->cur_arg = 0;
- p->ctxt.in_def = 1;
- $<node>$ = NEW_NODE(NODE_SELF, /*vid*/cur_arg, /*mid*/fname, /*cval*/c.val, &@$);
- /*%%%*/
- /*%
+def_name : fname
+ {
+ ID fname = get_id($1);
+ ID cur_arg = p->cur_arg;
+ YYSTYPE c = {.ctxt = p->ctxt};
+ numparam_name(p, fname);
+ NODE *save =
+ NODE_NEW_TEMPORAL(NODE_SELF,
+ /*head*/numparam_push(p),
+ /*nth*/p->max_numparam,
+ /*cval*/c.val);
+ local_push(p, 0);
+ p->cur_arg = 0;
+ p->ctxt.in_def = 1;
+ $<node>$ = NEW_NODE(NODE_SELF, /*vid*/cur_arg, /*mid*/fname, /*args*/save, &@$);
+ /*%%%*/
+ /*%
$$ = NEW_RIPPER(fname, get_value($1), $$, &NULL_LOC);
%*/
- }
- ;
+ }
+ ;
defn_head : k_def def_name
{
@@ -1854,6 +2114,12 @@ expr_value : expr
value_expr($1);
$$ = $1;
}
+ | error
+ {
+ /*%%%*/
+ $$ = NEW_ERROR(&@$);
+ /*% %*/
+ }
;
expr_value_do : {COND_PUSH(1);} expr_value do {COND_POP();}
@@ -1935,14 +2201,14 @@ command : fcall command_args %prec tLOWEST
/*%%%*/
$$ = new_command_qcall(p, ID2VAL(idCOLON2), $1, $3, $4, Qnull, &@3, &@$);
/*% %*/
- /*% ripper: command_call!($1, ID2VAL(idCOLON2), $3, $4) %*/
+ /*% ripper: command_call!($1, $2, $3, $4) %*/
}
| primary_value tCOLON2 operation2 command_args cmd_brace_block
{
/*%%%*/
$$ = new_command_qcall(p, ID2VAL(idCOLON2), $1, $3, $4, $5, &@3, &@$);
/*% %*/
- /*% ripper: method_add_block!(command_call!($1, ID2VAL(idCOLON2), $3, $4), $5) %*/
+ /*% ripper: method_add_block!(command_call!($1, $2, $3, $4), $5) %*/
}
| keyword_super command_args
{
@@ -2222,7 +2488,7 @@ lhs : user_variable
/*%%%*/
$$ = attrset(p, $1, idCOLON2, $3, &@$);
/*% %*/
- /*% ripper: field!($1, ID2VAL(idCOLON2), $3) %*/
+ /*% ripper: field!($1, $2, $3) %*/
}
| primary_value call_op tCONSTANT
{
@@ -2413,7 +2679,7 @@ arg : lhs '=' lex_ctxt arg_rhs
/*%%%*/
$$ = new_attr_op_assign(p, $1, ID2VAL(idCOLON2), $3, $4, $6, &@$);
/*% %*/
- /*% ripper: opassign!(field!($1, ID2VAL(idCOLON2), $3), $4, $6) %*/
+ /*% ripper: opassign!(field!($1, $2, $3), $4, $6) %*/
}
| primary_value tCOLON2 tCONSTANT tOP_ASGN lex_ctxt arg_rhs
{
@@ -2862,11 +3128,11 @@ block_arg : tAMPER arg_value
}
| tAMPER
{
- if (!local_id(p, ANON_BLOCK_ID)) {
+ if (!local_id(p, idFWD_BLOCK)) {
compile_error(p, "no anonymous block parameter");
}
/*%%%*/
- $$ = NEW_BLOCK_PASS(NEW_LVAR(ANON_BLOCK_ID, &@1), &@$);
+ $$ = NEW_BLOCK_PASS(NEW_LVAR(idFWD_BLOCK, &@1), &@$);
/*% %*/
/*% ripper: Qnil %*/
}
@@ -2899,11 +3165,12 @@ args : arg_value
}
| tSTAR
{
- if (!local_id(p, ANON_REST_ID)) {
+ if (!local_id(p, idFWD_REST) ||
+ local_id(p, idFWD_ALL)) {
compile_error(p, "no anonymous rest parameter");
}
/*%%%*/
- $$ = NEW_SPLAT(NEW_LVAR(ANON_REST_ID, &@1), &@$);
+ $$ = NEW_SPLAT(NEW_LVAR(idFWD_REST, &@1), &@$);
/*% %*/
/*% ripper: args_add_star!(args_new!, Qnil) %*/
}
@@ -2923,11 +3190,12 @@ args : arg_value
}
| args ',' tSTAR
{
- if (!local_id(p, ANON_REST_ID)) {
+ if (!local_id(p, idFWD_REST) ||
+ local_id(p, idFWD_ALL)) {
compile_error(p, "no anonymous rest parameter");
}
/*%%%*/
- $$ = rest_arg_append(p, $1, NEW_LVAR(ANON_REST_ID, &@3), &@$);
+ $$ = rest_arg_append(p, $1, NEW_LVAR(idFWD_REST, &@3), &@$);
/*% %*/
/*% ripper: args_add_star!($1, Qnil) %*/
}
@@ -3298,28 +3566,38 @@ primary : literal
}
| defn_head
f_arglist
+ {
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
+ }
bodystmt
k_end
{
restore_defun(p, $<node>1->nd_defn);
/*%%%*/
- $$ = set_defun_body(p, $1, $2, $3, &@$);
+ $$ = set_defun_body(p, $1, $2, $4, &@$);
/*% %*/
- /*% ripper: def!(get_value($1), $2, $3) %*/
+ /*% ripper: def!(get_value($1), $2, $4) %*/
local_pop(p);
}
| defs_head
f_arglist
+ {
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
+ }
bodystmt
k_end
{
restore_defun(p, $<node>1->nd_defn);
/*%%%*/
- $$ = set_defun_body(p, $1, $2, $3, &@$);
+ $$ = set_defun_body(p, $1, $2, $4, &@$);
/*%
$1 = get_value($1);
%*/
- /*% ripper: defs!(AREF($1, 0), AREF($1, 1), AREF($1, 2), $2, $3) %*/
+ /*% ripper: defs!(AREF($1, 0), AREF($1, 1), AREF($1, 2), $2, $4) %*/
local_pop(p);
}
| keyword_break
@@ -3362,6 +3640,9 @@ primary_value : primary
k_begin : keyword_begin
{
token_info_push(p, "begin", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
@@ -3371,7 +3652,7 @@ k_if : keyword_if
token_info_push(p, "if", &@$);
if (p->token_info && p->token_info->nonspc &&
p->token_info->next && !strcmp(p->token_info->next->token, "else")) {
- const char *tok = p->lex.ptok;
+ const char *tok = p->lex.ptok - rb_strlen_lit("if");
const char *beg = p->lex.pbeg + p->token_info->next->beg.column;
beg += rb_strlen_lit("else");
while (beg < tok && ISSPACE(*beg)) beg++;
@@ -3379,36 +3660,54 @@ k_if : keyword_if
p->token_info->nonspc = 0;
}
}
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
k_unless : keyword_unless
{
token_info_push(p, "unless", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
k_while : keyword_while
{
token_info_push(p, "while", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
k_until : keyword_until
{
token_info_push(p, "until", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
k_case : keyword_case
{
token_info_push(p, "case", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
k_for : keyword_for
{
token_info_push(p, "for", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
@@ -3416,6 +3715,9 @@ k_class : keyword_class
{
token_info_push(p, "class", &@$);
$<ctxt>$ = p->ctxt;
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
@@ -3423,6 +3725,9 @@ k_module : keyword_module
{
token_info_push(p, "module", &@$);
$<ctxt>$ = p->ctxt;
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
@@ -3436,12 +3741,19 @@ k_def : keyword_def
k_do : keyword_do
{
token_info_push(p, "do", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
+
}
;
k_do_block : keyword_do_block
{
token_info_push(p, "do", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
@@ -3488,6 +3800,13 @@ k_elsif : keyword_elsif
k_end : keyword_end
{
token_info_pop(p, "end", &@$);
+ /*%%%*/
+ pop_end_expect_token_locations(p);
+ /*% %*/
+ }
+ | tDUMNY_END
+ {
+ compile_error(p, "syntax error, unexpected end-of-input");
}
;
@@ -3853,9 +4172,15 @@ lambda_body : tLAMBEG compstmt '}'
token_info_pop(p, "}", &@3);
$$ = $2;
}
- | keyword_do_LAMBDA bodystmt k_end
+ | keyword_do_LAMBDA
{
- $$ = $2;
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
+ }
+ bodystmt k_end
+ {
+ $$ = $3;
}
;
@@ -3929,14 +4254,14 @@ method_call : fcall paren_args
$$ = new_qcall(p, ID2VAL(idCOLON2), $1, $3, $4, &@3, &@$);
nd_set_line($$, @3.end_pos.lineno);
/*% %*/
- /*% ripper: method_add_arg!(call!($1, ID2VAL(idCOLON2), $3), $4) %*/
+ /*% ripper: method_add_arg!(call!($1, $2, $3), $4) %*/
}
| primary_value tCOLON2 operation3
{
/*%%%*/
$$ = new_qcall(p, ID2VAL(idCOLON2), $1, $3, Qnull, &@3, &@$);
/*% %*/
- /*% ripper: call!($1, ID2VAL(idCOLON2), $3) %*/
+ /*% ripper: call!($1, $2, $3) %*/
}
| primary_value call_op paren_args
{
@@ -3952,7 +4277,7 @@ method_call : fcall paren_args
$$ = new_qcall(p, ID2VAL(idCOLON2), $1, ID2VAL(idCall), $3, &@2, &@$);
nd_set_line($$, @2.end_pos.lineno);
/*% %*/
- /*% ripper: method_add_arg!(call!($1, ID2VAL(idCOLON2), ID2VAL(idCall)), $3) %*/
+ /*% ripper: method_add_arg!(call!($1, $2, ID2VAL(idCall)), $3) %*/
}
| keyword_super paren_args
{
@@ -4593,7 +4918,7 @@ p_var_ref : '^' tIDENTIFIER
}
;
-p_expr_ref : '^' tLPAREN expr_value ')'
+p_expr_ref : '^' tLPAREN expr_value rparen
{
/*%%%*/
$$ = NEW_BEGIN($3, &@$);
@@ -4633,7 +4958,16 @@ opt_rescue : k_rescue exc_list exc_var then
$$ = NEW_RESBODY($2,
$3 ? block_append(p, node_assign(p, $3, NEW_ERRINFO(&@3), NO_LEX_CTXT, &@3), $5) : $5,
$6, &@$);
- fixpos($$, $2?$2:$5);
+
+ if ($2) {
+ fixpos($$, $2);
+ }
+ else if ($3) {
+ fixpos($$, $3);
+ }
+ else {
+ fixpos($$, $5);
+ }
/*% %*/
/*% ripper: rescue!(escape_Qundef($2), escape_Qundef($3), escape_Qundef($5), escape_Qundef($6)) %*/
}
@@ -5035,9 +5369,7 @@ ssym : tSYMBEG sym
;
sym : fname
- | tIVAR
- | tGVAR
- | tCVAR
+ | nonlocal_var
;
dsym : tSYMBEG string_contents tSTRING_END
@@ -5073,10 +5405,8 @@ nonlocal_var : tIVAR
;
user_variable : tIDENTIFIER
- | tIVAR
- | tGVAR
| tCONSTANT
- | tCVAR
+ | nonlocal_var
;
keyword_variable: keyword_nil {$$ = KWD2EID(nil, $1);}
@@ -5206,6 +5536,9 @@ args_tail : f_kwarg ',' f_kwrest opt_f_block_arg
{
add_forwarding_args(p);
$$ = new_args_tail(p, Qnone, $1, ID2VAL(idFWD_BLOCK), &@1);
+ /*%%%*/
+ ($$->nd_ainfo)->forwarding = 1;
+ /*% %*/
}
;
@@ -5285,7 +5618,11 @@ f_args : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail
args_forward : tBDOT3
{
/*%%%*/
+#ifdef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
+ $$ = 0;
+#else
$$ = idFWD_KWREST;
+#endif
/*% %*/
/*% ripper: args_forward! %*/
}
@@ -5494,8 +5831,9 @@ f_kwrest : kwrest_mark tIDENTIFIER
}
| kwrest_mark
{
- arg_var(p, ANON_KEYWORD_REST_ID);
+ arg_var(p, idFWD_KWREST);
/*%%%*/
+ $$ = idFWD_KWREST;
/*% %*/
/*% ripper: kwrest_param!(Qnil) %*/
}
@@ -5569,8 +5907,9 @@ f_rest_arg : restarg_mark tIDENTIFIER
}
| restarg_mark
{
- arg_var(p, ANON_REST_ID);
+ arg_var(p, idFWD_REST);
/*%%%*/
+ $$ = idFWD_REST;
/*% %*/
/*% ripper: rest_param!(Qnil) %*/
}
@@ -5590,8 +5929,9 @@ f_block_arg : blkarg_mark tIDENTIFIER
}
| blkarg_mark
{
- arg_var(p, ANON_BLOCK_ID);
+ arg_var(p, idFWD_BLOCK);
/*%%%*/
+ $$ = idFWD_BLOCK;
/*% %*/
/*% ripper: blockarg!(Qnil) %*/
}
@@ -5725,12 +6065,13 @@ assoc : arg_value tASSOC arg_value
}
| tDSTAR
{
- if (!local_id(p, ANON_KEYWORD_REST_ID)) {
+ if (!local_id(p, idFWD_KWREST) ||
+ local_id(p, idFWD_ALL)) {
compile_error(p, "no anonymous keyword rest parameter");
}
/*%%%*/
$$ = list_append(p, NEW_LIST(0, &@$),
- NEW_LVAR(ANON_KEYWORD_REST_ID, &@$));
+ NEW_LVAR(idFWD_KWREST, &@$));
/*% %*/
/*% ripper: assoc_splat!(Qnil) %*/
}
@@ -5779,13 +6120,16 @@ rbracket : opt_nl ']'
rbrace : opt_nl '}'
;
-trailer : /* none */
- | '\n'
+trailer : opt_nl
| ','
;
term : ';' {yyerrok;token_flush(p);}
- | '\n' {token_flush(p);}
+ | '\n'
+ {
+ @$.end_pos = @$.beg_pos;
+ token_flush(p);
+ }
;
terms : term
@@ -5846,12 +6190,91 @@ ripper_yylval_id(struct parser_params *p, ID x)
#endif
#define set_yylval_noname() set_yylval_id(keyword_nil)
+#define has_delayed_token(p) (!NIL_P(p->delayed.token))
#ifndef RIPPER
#define literal_flush(p, ptr) ((p)->lex.ptok = (ptr))
-#define dispatch_scan_event(p, t) ((void)0)
-#define dispatch_delayed_token(p, t) ((void)0)
-#define has_delayed_token(p) (0)
+#define dispatch_scan_event(p, t) parser_dispatch_scan_event(p, t, __LINE__)
+
+static bool
+parser_has_token(struct parser_params *p)
+{
+ if (p->keep_tokens && (p->lex.pcur < p->lex.ptok)) rb_bug("lex.pcur < lex.ptok. (line: %d) %ld|%ld|%ld", p->ruby_sourceline, p->lex.ptok - p->lex.pbeg, p->lex.pcur - p->lex.ptok, p->lex.pend - p->lex.pcur);
+ return p->lex.pcur > p->lex.ptok;
+}
+
+static VALUE
+code_loc_to_ary(const rb_code_location_t *loc)
+{
+ VALUE ary = rb_ary_new_from_args(4,
+ INT2NUM(loc->beg_pos.lineno), INT2NUM(loc->beg_pos.column),
+ INT2NUM(loc->end_pos.lineno), INT2NUM(loc->end_pos.column));
+ rb_obj_freeze(ary);
+
+ return ary;
+}
+
+static void
+parser_append_tokens(struct parser_params *p, VALUE str, enum yytokentype t, int line)
+{
+ VALUE ary;
+ int token_id;
+
+ ary = rb_ary_new2(4);
+ token_id = p->token_id;
+ rb_ary_push(ary, INT2FIX(token_id));
+ rb_ary_push(ary, ID2SYM(parser_token2id(t)));
+ rb_ary_push(ary, str);
+ rb_ary_push(ary, code_loc_to_ary(p->yylloc));
+ rb_obj_freeze(ary);
+ rb_ary_push(p->tokens, ary);
+ p->token_id++;
+
+ if (p->debug) {
+ rb_parser_printf(p, "Append tokens (line: %d) %"PRIsVALUE"\n", line, ary);
+ }
+}
+
+static void
+parser_dispatch_scan_event(struct parser_params *p, enum yytokentype t, int line)
+{
+ debug_token_line(p, "parser_dispatch_scan_event", line);
+
+ if (!parser_has_token(p)) return;
+
+ RUBY_SET_YYLLOC(*p->yylloc);
+
+ if (p->keep_tokens) {
+ VALUE str = STR_NEW(p->lex.ptok, p->lex.pcur - p->lex.ptok);
+ parser_append_tokens(p, str, t, line);
+ }
+
+ token_flush(p);
+}
+
+#define dispatch_delayed_token(p, t) parser_dispatch_delayed_token(p, t, __LINE__)
+static void
+parser_dispatch_delayed_token(struct parser_params *p, enum yytokentype t, int line)
+{
+ int saved_line = p->ruby_sourceline;
+ const char *saved_tokp = p->lex.ptok;
+
+ debug_token_line(p, "parser_dispatch_delayed_token", line);
+
+ if (!has_delayed_token(p)) return;
+
+ RUBY_SET_YYLLOC_OF_DELAYED_TOKEN(*p->yylloc);
+
+ if (p->keep_tokens) {
+ p->ruby_sourceline = p->delayed.beg_line;
+ p->lex.ptok = p->lex.pbeg + p->delayed.beg_col;
+ parser_append_tokens(p, p->delayed.token, t, line);
+ p->ruby_sourceline = saved_line;
+ p->lex.ptok = saved_tokp;
+ }
+
+ p->delayed.token = Qnil;
+}
#else
#define literal_flush(p, ptr) ((void)(ptr))
@@ -5876,6 +6299,7 @@ ripper_scan_event_val(struct parser_params *p, enum yytokentype t)
{
VALUE str = STR_NEW(p->lex.ptok, p->lex.pcur - p->lex.ptok);
VALUE rval = ripper_dispatch1(p, ripper_token2eventid(t), str);
+ RUBY_SET_YYLLOC(*p->yylloc);
token_flush(p);
return rval;
}
@@ -5894,16 +6318,15 @@ ripper_dispatch_delayed_token(struct parser_params *p, enum yytokentype t)
int saved_line = p->ruby_sourceline;
const char *saved_tokp = p->lex.ptok;
- if (NIL_P(p->delayed.token)) return;
- p->ruby_sourceline = p->delayed.line;
- p->lex.ptok = p->lex.pbeg + p->delayed.col;
+ if (!has_delayed_token(p)) return;
+ p->ruby_sourceline = p->delayed.beg_line;
+ p->lex.ptok = p->lex.pbeg + p->delayed.beg_col;
add_mark_object(p, yylval_rval = ripper_dispatch1(p, ripper_token2eventid(t), p->delayed.token));
p->delayed.token = Qnil;
p->ruby_sourceline = saved_line;
p->lex.ptok = saved_tokp;
}
#define dispatch_delayed_token(p, t) ripper_dispatch_delayed_token(p, t)
-#define has_delayed_token(p) (!NIL_P(p->delayed.token))
#endif /* RIPPER */
static inline int
@@ -6374,14 +6797,15 @@ yycompile0(VALUE arg)
p->lex.strterm = 0;
p->lex.pcur = p->lex.pbeg = p->lex.pend = 0;
- p->lex.prevline = p->lex.lastline = p->lex.nextline = 0;
if (n || p->error_p) {
VALUE mesg = p->error_buffer;
if (!mesg) {
mesg = rb_class_new_instance(0, 0, rb_eSyntaxError);
}
- rb_set_errinfo(mesg);
- return FALSE;
+ if (!p->error_tolerant) {
+ rb_set_errinfo(mesg);
+ return FALSE;
+ }
}
tree = p->eval_tree;
if (!tree) {
@@ -6389,6 +6813,7 @@ yycompile0(VALUE arg)
}
else {
VALUE opt = p->compile_option;
+ VALUE tokens = p->tokens;
NODE *prelude;
NODE *body = parser_append_options(p, tree->nd_body);
if (!opt) opt = rb_obj_hide(rb_ident_hash_new());
@@ -6396,6 +6821,10 @@ yycompile0(VALUE arg)
prelude = block_append(p, p->eval_tree_begin, body);
tree->nd_body = prelude;
RB_OBJ_WRITE(p->ast, &p->ast->body.compile_option, opt);
+ if (p->keep_tokens) {
+ rb_obj_freeze(tokens);
+ rb_ast_set_tokens(p->ast, tokens);
+ }
}
p->ast->body.root = tree;
if (!p->ast->body.script_lines) p->ast->body.script_lines = INT2FIX(p->line_count);
@@ -6578,7 +7007,7 @@ parser_str_new(const char *ptr, long len, rb_encoding *enc, int func, rb_encodin
if (!(func & STR_FUNC_REGEXP) && rb_enc_asciicompat(enc)) {
if (is_ascii_string(str)) {
}
- else if (enc0 == rb_usascii_encoding() && enc != rb_utf8_encoding()) {
+ else if (rb_is_usascii_enc(enc0) && enc != rb_utf8_encoding()) {
rb_enc_associate(str, rb_ascii8bit_encoding());
}
}
@@ -6586,32 +7015,31 @@ parser_str_new(const char *ptr, long len, rb_encoding *enc, int func, rb_encodin
return str;
}
-#define lex_goto_eol(p) ((p)->lex.pcur = (p)->lex.pend)
-#define lex_eol_p(p) ((p)->lex.pcur >= (p)->lex.pend)
-#define lex_eol_n_p(p,n) ((p)->lex.pcur+(n) >= (p)->lex.pend)
#define peek(p,c) peek_n(p, (c), 0)
#define peek_n(p,c,n) (!lex_eol_n_p(p, n) && (c) == (unsigned char)(p)->lex.pcur[n])
#define peekc(p) peekc_n(p, 0)
#define peekc_n(p,n) (lex_eol_n_p(p, n) ? -1 : (unsigned char)(p)->lex.pcur[n])
-#ifdef RIPPER
static void
-add_delayed_token(struct parser_params *p, const char *tok, const char *end)
+add_delayed_token(struct parser_params *p, const char *tok, const char *end, int line)
{
+#ifndef RIPPER
+ debug_token_line(p, "add_delayed_token", line);
+#endif
+
if (tok < end) {
if (!has_delayed_token(p)) {
p->delayed.token = rb_str_buf_new(end - tok);
rb_enc_associate(p->delayed.token, p->enc);
- p->delayed.line = p->ruby_sourceline;
- p->delayed.col = rb_long2int(tok - p->lex.pbeg);
+ p->delayed.beg_line = p->ruby_sourceline;
+ p->delayed.beg_col = rb_long2int(tok - p->lex.pbeg);
}
rb_str_buf_cat(p->delayed.token, tok, end - tok);
+ p->delayed.end_line = p->ruby_sourceline;
+ p->delayed.end_col = rb_long2int(end - p->lex.pbeg);
p->lex.ptok = end;
}
}
-#else
-#define add_delayed_token(p, tok, end) ((void)(tok), (void)(end))
-#endif
static int
nextline(struct parser_params *p, int set_encoding)
@@ -6644,7 +7072,7 @@ nextline(struct parser_params *p, int set_encoding)
/* after here-document without terminator */
goto end_of_input;
}
- add_delayed_token(p, p->lex.ptok, p->lex.pend);
+ add_delayed_token(p, p->lex.ptok, p->lex.pend, __LINE__);
if (p->heredoc_end > 0) {
p->ruby_sourceline = p->heredoc_end;
p->heredoc_end = 0;
@@ -6653,7 +7081,6 @@ nextline(struct parser_params *p, int set_encoding)
p->lex.pbeg = p->lex.pcur = RSTRING_PTR(v);
p->lex.pend = p->lex.pcur + RSTRING_LEN(v);
token_flush(p);
- p->lex.prevline = p->lex.lastline;
p->lex.lastline = v;
return 0;
}
@@ -6806,20 +7233,22 @@ tokadd_codepoint(struct parser_params *p, rb_encoding **encp,
{
size_t numlen;
int codepoint = scan_hex(p->lex.pcur, wide ? p->lex.pend - p->lex.pcur : 4, &numlen);
- literal_flush(p, p->lex.pcur);
p->lex.pcur += numlen;
if (p->lex.strterm == NULL ||
(p->lex.strterm->flags & STRTERM_HEREDOC) ||
(p->lex.strterm->u.literal.u1.func != str_regexp)) {
if (wide ? (numlen == 0 || numlen > 6) : (numlen < 4)) {
+ literal_flush(p, p->lex.pcur);
yyerror0("invalid Unicode escape");
return wide && numlen > 0;
}
if (codepoint > 0x10ffff) {
+ literal_flush(p, p->lex.pcur);
yyerror0("invalid Unicode codepoint (too large)");
return wide;
}
if ((codepoint & 0xfffff800) == 0xd800) {
+ literal_flush(p, p->lex.pcur);
yyerror0("invalid Unicode codepoint");
return wide;
}
@@ -6844,6 +7273,18 @@ tokadd_codepoint(struct parser_params *p, rb_encoding **encp,
return TRUE;
}
+static int tokadd_mbchar(struct parser_params *p, int c);
+
+static int
+tokskip_mbchar(struct parser_params *p)
+{
+ int len = parser_precise_mbclen(p, p->lex.pcur-1);
+ if (len > 0) {
+ p->lex.pcur += len - 1;
+ }
+ return len;
+}
+
/* return value is for ?\u3042 */
static void
tokadd_utf8(struct parser_params *p, rb_encoding **encp,
@@ -6861,44 +7302,71 @@ tokadd_utf8(struct parser_params *p, rb_encoding **encp,
if (regexp_literal) { tokadd(p, '\\'); tokadd(p, 'u'); }
if (peek(p, open_brace)) { /* handle \u{...} form */
- const char *second = NULL;
- int c, last = nextc(p);
- if (p->lex.pcur >= p->lex.pend) goto unterminated;
- while (ISSPACE(c = *p->lex.pcur) && ++p->lex.pcur < p->lex.pend);
- while (c != close_brace) {
- if (c == term) goto unterminated;
- if (second == multiple_codepoints)
- second = p->lex.pcur;
- if (regexp_literal) tokadd(p, last);
- if (!tokadd_codepoint(p, encp, regexp_literal, TRUE)) {
- break;
- }
- while (ISSPACE(c = *p->lex.pcur)) {
- if (++p->lex.pcur >= p->lex.pend) goto unterminated;
- last = c;
- }
- if (term == -1 && !second)
- second = multiple_codepoints;
- }
+ if (regexp_literal && p->lex.strterm->u.literal.u1.func == str_regexp) {
+ /*
+ * Skip parsing validation code and copy bytes as-is until term or
+ * closing brace, in order to correctly handle extended regexps where
+ * invalid unicode escapes are allowed in comments. The regexp parser
+ * does its own validation and will catch any issues.
+ */
+ tokadd(p, open_brace);
+ while (++p->lex.pcur < p->lex.pend) {
+ int c = peekc(p);
+ if (c == close_brace) {
+ tokadd(p, c);
+ ++p->lex.pcur;
+ break;
+ }
+ else if (c == term) {
+ break;
+ }
+ if (c == '\\' && p->lex.pcur + 1 < p->lex.pend) {
+ tokadd(p, c);
+ c = *++p->lex.pcur;
+ }
+ tokadd_mbchar(p, c);
+ }
+ }
+ else {
+ const char *second = NULL;
+ int c, last = nextc(p);
+ if (p->lex.pcur >= p->lex.pend) goto unterminated;
+ while (ISSPACE(c = *p->lex.pcur) && ++p->lex.pcur < p->lex.pend);
+ while (c != close_brace) {
+ if (c == term) goto unterminated;
+ if (second == multiple_codepoints)
+ second = p->lex.pcur;
+ if (regexp_literal) tokadd(p, last);
+ if (!tokadd_codepoint(p, encp, regexp_literal, TRUE)) {
+ break;
+ }
+ while (ISSPACE(c = *p->lex.pcur)) {
+ if (++p->lex.pcur >= p->lex.pend) goto unterminated;
+ last = c;
+ }
+ if (term == -1 && !second)
+ second = multiple_codepoints;
+ }
- if (c != close_brace) {
- unterminated:
- token_flush(p);
- yyerror0("unterminated Unicode escape");
- return;
- }
- if (second && second != multiple_codepoints) {
- const char *pcur = p->lex.pcur;
- p->lex.pcur = second;
- dispatch_scan_event(p, tSTRING_CONTENT);
- token_flush(p);
- p->lex.pcur = pcur;
- yyerror0(multiple_codepoints);
- token_flush(p);
- }
+ if (c != close_brace) {
+ unterminated:
+ token_flush(p);
+ yyerror0("unterminated Unicode escape");
+ return;
+ }
+ if (second && second != multiple_codepoints) {
+ const char *pcur = p->lex.pcur;
+ p->lex.pcur = second;
+ dispatch_scan_event(p, tSTRING_CONTENT);
+ token_flush(p);
+ p->lex.pcur = pcur;
+ yyerror0(multiple_codepoints);
+ token_flush(p);
+ }
- if (regexp_literal) tokadd(p, close_brace);
- nextc(p);
+ if (regexp_literal) tokadd(p, close_brace);
+ nextc(p);
+ }
}
else { /* handle \uxxxx form */
if (!tokadd_codepoint(p, encp, regexp_literal, FALSE)) {
@@ -6973,7 +7441,11 @@ read_escape(struct parser_params *p, int flags, rb_encoding **encp)
}
return read_escape(p, flags|ESCAPE_META, encp) | 0x80;
}
- else if (c == -1 || !ISASCII(c)) goto eof;
+ else if (c == -1) goto eof;
+ else if (!ISASCII(c)) {
+ tokskip_mbchar(p);
+ goto eof;
+ }
else {
int c2 = escaped_control_code(c);
if (c2) {
@@ -7004,7 +7476,11 @@ read_escape(struct parser_params *p, int flags, rb_encoding **encp)
}
else if (c == '?')
return 0177;
- else if (c == -1 || !ISASCII(c)) goto eof;
+ else if (c == -1) goto eof;
+ else if (!ISASCII(c)) {
+ tokskip_mbchar(p);
+ goto eof;
+ }
else {
int c2 = escaped_control_code(c);
if (c2) {
@@ -7032,7 +7508,7 @@ read_escape(struct parser_params *p, int flags, rb_encoding **encp)
eof:
case -1:
yyerror0("Invalid escape character syntax");
- token_flush(p);
+ dispatch_scan_event(p, tSTRING_CONTENT);
return '\0';
default:
@@ -7201,6 +7677,10 @@ tokadd_string(struct parser_params *p,
{
int c;
bool erred = false;
+#ifdef RIPPER
+ const int heredoc_end = (p->heredoc_end ? p->heredoc_end + 1 : 0);
+ int top_of_line = FALSE;
+#endif
#define mixed_error(enc1, enc2) \
(void)(erred || (parser_mixed_error(p, enc1, enc2), erred = true))
@@ -7211,6 +7691,12 @@ tokadd_string(struct parser_params *p,
if (p->heredoc_indent > 0) {
parser_update_heredoc_indent(p, c);
}
+#ifdef RIPPER
+ if (top_of_line && heredoc_end == p->ruby_sourceline) {
+ pushback(p, c);
+ break;
+ }
+#endif
if (paren && c == paren) {
++*nest;
@@ -7223,14 +7709,13 @@ tokadd_string(struct parser_params *p,
--*nest;
}
else if ((func & STR_FUNC_EXPAND) && c == '#' && p->lex.pcur < p->lex.pend) {
- int c2 = *p->lex.pcur;
+ unsigned char c2 = *p->lex.pcur;
if (c2 == '$' || c2 == '@' || c2 == '{') {
pushback(p, c);
break;
}
}
else if (c == '\\') {
- literal_flush(p, p->lex.pcur - 1);
c = nextc(p);
switch (c) {
case '\n':
@@ -7337,6 +7822,9 @@ tokadd_string(struct parser_params *p,
}
}
tokadd(p, c);
+#ifdef RIPPER
+ top_of_line = (c == '\n');
+#endif
}
terminate:
if (*enc) *encp = *enc;
@@ -7375,7 +7863,21 @@ flush_string_content(struct parser_params *p, rb_encoding *enc)
yylval.val = content;
}
#else
-#define flush_string_content(p, enc) ((void)(enc))
+static void
+flush_string_content(struct parser_params *p, rb_encoding *enc)
+{
+ if (has_delayed_token(p)) {
+ ptrdiff_t len = p->lex.pcur - p->lex.ptok;
+ if (len > 0) {
+ rb_enc_str_buf_cat(p->delayed.token, p->lex.ptok, len, enc);
+ p->delayed.end_line = p->ruby_sourceline;
+ p->delayed.end_col = rb_long2int(p->lex.pcur - p->lex.pbeg);
+ }
+ dispatch_delayed_token(p, tSTRING_CONTENT);
+ p->lex.ptok = p->lex.pcur;
+ }
+ dispatch_scan_event(p, tSTRING_CONTENT);
+}
#endif
RUBY_FUNC_EXPORTED const unsigned int ruby_global_name_punct_bits[(0x7e - 0x20 + 31) / 32];
@@ -7494,14 +7996,14 @@ parse_string(struct parser_params *p, rb_strterm_literal_t *quote)
if (func & STR_FUNC_QWORDS) {
quote->u1.func |= STR_FUNC_TERM;
pushback(p, c); /* dispatch the term at tSTRING_END */
- add_delayed_token(p, p->lex.ptok, p->lex.pcur);
+ add_delayed_token(p, p->lex.ptok, p->lex.pcur, __LINE__);
return ' ';
}
return parser_string_term(p, func);
}
if (space) {
pushback(p, c);
- add_delayed_token(p, p->lex.ptok, p->lex.pcur);
+ add_delayed_token(p, p->lex.ptok, p->lex.pcur, __LINE__);
return ' ';
}
newtok(p);
@@ -7861,12 +8363,29 @@ dispatch_heredoc_end(struct parser_params *p)
dispatch_delayed_token(p, tSTRING_CONTENT);
str = STR_NEW(p->lex.ptok, p->lex.pend - p->lex.ptok);
ripper_dispatch1(p, ripper_token2eventid(tHEREDOC_END), str);
+ RUBY_SET_YYLLOC_FROM_STRTERM_HEREDOC(*p->yylloc);
lex_goto_eol(p);
token_flush(p);
}
#else
-#define dispatch_heredoc_end(p) ((void)0)
+#define dispatch_heredoc_end(p) parser_dispatch_heredoc_end(p, __LINE__)
+static void
+parser_dispatch_heredoc_end(struct parser_params *p, int line)
+{
+ if (has_delayed_token(p))
+ dispatch_delayed_token(p, tSTRING_CONTENT);
+
+ if (p->keep_tokens) {
+ VALUE str = STR_NEW(p->lex.ptok, p->lex.pend - p->lex.ptok);
+ RUBY_SET_YYLLOC_OF_HEREDOC_END(*p->yylloc);
+ parser_append_tokens(p, str, tHEREDOC_END, line);
+ }
+
+ RUBY_SET_YYLLOC_FROM_STRTERM_HEREDOC(*p->yylloc);
+ lex_goto_eol(p);
+ token_flush(p);
+}
#endif
static enum yytokentype
@@ -7896,7 +8415,7 @@ here_document(struct parser_params *p, rb_strterm_heredoc_t *here)
int cr = ENC_CODERANGE_UNKNOWN;
rb_str_coderange_scan_restartable(p->lex.ptok, p->lex.pcur, enc, &cr);
if (cr != ENC_CODERANGE_7BIT &&
- p->enc == rb_usascii_encoding() &&
+ rb_is_usascii_enc(p->enc) &&
enc != rb_utf8_encoding()) {
enc = rb_ascii8bit_encoding();
}
@@ -8825,7 +9344,7 @@ parse_qmark(struct parser_params *p, int space_seen)
enc = rb_utf8_encoding();
tokadd_utf8(p, &enc, -1, 0, 0);
}
- else if (!lex_eol_p(p) && !(c = *p->lex.pcur, ISASCII(c))) {
+ else if (!ISASCII(c = peekc(p))) {
nextc(p);
if (tokadd_mbchar(p, c) == -1) return 0;
}
@@ -9176,6 +9695,7 @@ parse_ident(struct parser_params *p, int c, int cmd_state)
int mb = ENC_CODERANGE_7BIT;
const enum lex_state_e last_state = p->lex.state;
ID ident;
+ int enforce_keyword_end = 0;
do {
if (!ISASCII(c)) mb = ENC_CODERANGE_UNKNOWN;
@@ -9205,7 +9725,34 @@ parse_ident(struct parser_params *p, int c, int cmd_state)
return tLABEL;
}
}
- if (mb == ENC_CODERANGE_7BIT && !IS_lex_state(EXPR_DOT)) {
+
+#ifndef RIPPER
+ if (!NIL_P(peek_end_expect_token_locations(p))) {
+ VALUE end_loc;
+ int lineno, column;
+ int beg_pos = (int)(p->lex.ptok - p->lex.pbeg);
+
+ end_loc = peek_end_expect_token_locations(p);
+ lineno = NUM2INT(rb_ary_entry(end_loc, 0));
+ column = NUM2INT(rb_ary_entry(end_loc, 1));
+
+ if (p->debug) {
+ rb_parser_printf(p, "enforce_keyword_end check. current: (%d, %d), peek: (%d, %d)\n",
+ p->ruby_sourceline, beg_pos, lineno, column);
+ }
+
+ if ((p->ruby_sourceline > lineno) && (beg_pos <= column)) {
+ const struct kwtable *kw;
+
+ if ((IS_lex_state(EXPR_DOT)) && (kw = rb_reserved_word(tok(p), toklen(p))) && (kw && kw->id[0] == keyword_end)) {
+ if (p->debug) rb_parser_printf(p, "enforce_keyword_end is enabled\n");
+ enforce_keyword_end = 1;
+ }
+ }
+ }
+#endif
+
+ if (mb == ENC_CODERANGE_7BIT && (!IS_lex_state(EXPR_DOT) || enforce_keyword_end)) {
const struct kwtable *kw;
/* See if it is a reserved word. */
@@ -9259,13 +9806,23 @@ parse_ident(struct parser_params *p, int c, int cmd_state)
ident = tokenize_ident(p, last_state);
if (result == tCONSTANT && is_local_id(ident)) result = tIDENTIFIER;
if (!IS_lex_state_for(last_state, EXPR_DOT|EXPR_FNAME) &&
- (result == tIDENTIFIER) && /* not EXPR_FNAME, not attrasgn */
- lvar_defined(p, ident)) {
- SET_LEX_STATE(EXPR_END|EXPR_LABEL);
+ (result == tIDENTIFIER) && /* not EXPR_FNAME, not attrasgn */
+ (lvar_defined(p, ident) || NUMPARAM_ID_P(ident))) {
+ SET_LEX_STATE(EXPR_END|EXPR_LABEL);
}
return result;
}
+static void
+warn_cr(struct parser_params *p)
+{
+ if (!p->cr_seen) {
+ p->cr_seen = TRUE;
+ /* carried over with p->lex.nextline for nextc() */
+ rb_warn0("encountered \\r in middle of line, treated as a mere space");
+ }
+}
+
static enum yytokentype
parser_yylex(struct parser_params *p)
{
@@ -9279,6 +9836,7 @@ parser_yylex(struct parser_params *p)
if (p->lex.strterm) {
if (p->lex.strterm->flags & STRTERM_HEREDOC) {
+ token_flush(p);
return here_document(p, &p->lex.strterm->u.heredoc);
}
else {
@@ -9289,33 +9847,41 @@ parser_yylex(struct parser_params *p)
cmd_state = p->command_start;
p->command_start = FALSE;
p->token_seen = TRUE;
- retry:
- last_state = p->lex.state;
#ifndef RIPPER
token_flush(p);
#endif
+ retry:
+ last_state = p->lex.state;
switch (c = nextc(p)) {
case '\0': /* NUL */
case '\004': /* ^D */
case '\032': /* ^Z */
case -1: /* end of script. */
+ p->eofp = 1;
+#ifndef RIPPER
+ if (!NIL_P(p->end_expect_token_locations) && RARRAY_LEN(p->end_expect_token_locations) > 0) {
+ pop_end_expect_token_locations(p);
+ RUBY_SET_YYLLOC_OF_DUMMY_END(*p->yylloc);
+ return tDUMNY_END;
+ }
+#endif
+ /* Set location for end-of-input because dispatch_scan_event is not called. */
+ RUBY_SET_YYLLOC(*p->yylloc);
return 0;
/* white spaces */
case '\r':
- if (!p->cr_seen) {
- p->cr_seen = TRUE;
- /* carried over with p->lex.nextline for nextc() */
- rb_warn0("encountered \\r in middle of line, treated as a mere space");
- }
+ warn_cr(p);
/* fall through */
case ' ': case '\t': case '\f':
case '\13': /* '\v' */
space_seen = 1;
-#ifdef RIPPER
while ((c = nextc(p))) {
switch (c) {
- case ' ': case '\t': case '\f': case '\r':
+ case '\r':
+ warn_cr(p);
+ /* fall through */
+ case ' ': case '\t': case '\f':
case '\13': /* '\v' */
break;
default:
@@ -9325,6 +9891,8 @@ parser_yylex(struct parser_params *p)
outofloop:
pushback(p, c);
dispatch_scan_event(p, tSP);
+#ifndef RIPPER
+ token_flush(p);
#endif
goto retry;
@@ -9362,7 +9930,10 @@ parser_yylex(struct parser_params *p)
break;
case '#':
pushback(p, c);
- if (space_seen) dispatch_scan_event(p, tSP);
+ if (space_seen) {
+ dispatch_scan_event(p, tSP);
+ token_flush(p);
+ }
goto retry;
case '&':
case '.': {
@@ -9377,18 +9948,10 @@ parser_yylex(struct parser_params *p)
p->ruby_sourceline--;
p->lex.nextline = p->lex.lastline;
case -1: /* EOF no decrement*/
-#ifndef RIPPER
- if (p->lex.prevline && !p->eofp) p->lex.lastline = p->lex.prevline;
- p->lex.pbeg = RSTRING_PTR(p->lex.lastline);
- p->lex.pend = p->lex.pcur = p->lex.pbeg + RSTRING_LEN(p->lex.lastline);
- pushback(p, 1); /* always pushback */
- p->lex.ptok = p->lex.pcur;
-#else
lex_goto_eol(p);
if (c != -1) {
p->lex.ptok = p->lex.pcur;
}
-#endif
goto normal_newline;
}
}
@@ -9986,12 +10549,9 @@ yylex(YYSTYPE *lval, YYLTYPE *yylloc, struct parser_params *p)
p->lval = lval;
lval->val = Qundef;
- t = parser_yylex(p);
+ p->yylloc = yylloc;
- if (p->lex.strterm && (p->lex.strterm->flags & STRTERM_HEREDOC))
- RUBY_SET_YYLLOC_FROM_STRTERM_HEREDOC(*yylloc);
- else
- RUBY_SET_YYLLOC(*yylloc);
+ t = parser_yylex(p);
if (has_delayed_token(p))
dispatch_delayed_token(p, t);
@@ -10004,11 +10564,18 @@ yylex(YYSTYPE *lval, YYLTYPE *yylloc, struct parser_params *p)
#define LVAR_USED ((ID)1 << (sizeof(ID) * CHAR_BIT - 1))
static NODE*
-node_newnode(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2, const rb_code_location_t *loc)
+node_new_temporal(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2)
{
NODE *n = rb_ast_newnode(p->ast, type);
rb_node_init(n, type, a0, a1, a2);
+ return n;
+}
+
+static NODE*
+node_newnode(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2, const rb_code_location_t *loc)
+{
+ NODE *n = node_new_temporal(p, type, a0, a1, a2);
nd_set_loc(n, loc);
nd_set_node_id(n, parser_get_node_id(p));
@@ -10544,13 +11111,7 @@ static NODE *
kwd_append(NODE *kwlist, NODE *kw)
{
if (kwlist) {
- NODE *kws = kwlist;
- kws->nd_loc.end_pos = kw->nd_loc.end_pos;
- while (kws->nd_next) {
- kws = kws->nd_next;
- kws->nd_loc.end_pos = kw->nd_loc.end_pos;
- }
- kws->nd_next = kw;
+ opt_arg_append(kwlist, kw);
}
return kwlist;
}
@@ -10686,7 +11247,7 @@ check_literal_when(struct parser_params *p, NODE *arg, const YYLTYPE *loc)
if (!arg || !p->case_labels) return;
lit = rb_node_case_when_optimizable_literal(arg);
- if (lit == Qundef) return;
+ if (UNDEF_P(lit)) return;
if (nd_type_p(arg, NODE_STR)) {
RB_OBJ_WRITTEN(p->ast, Qnil, arg->nd_lit = lit);
}
@@ -10889,6 +11450,34 @@ rb_parser_set_location_from_strterm_heredoc(struct parser_params *p, rb_strterm_
}
YYLTYPE *
+rb_parser_set_location_of_delayed_token(struct parser_params *p, YYLTYPE *yylloc)
+{
+ yylloc->beg_pos.lineno = p->delayed.beg_line;
+ yylloc->beg_pos.column = p->delayed.beg_col;
+ yylloc->end_pos.lineno = p->delayed.end_line;
+ yylloc->end_pos.column = p->delayed.end_col;
+
+ return yylloc;
+}
+
+YYLTYPE *
+rb_parser_set_location_of_heredoc_end(struct parser_params *p, YYLTYPE *yylloc)
+{
+ int sourceline = p->ruby_sourceline;
+ int beg_pos = (int)(p->lex.ptok - p->lex.pbeg);
+ int end_pos = (int)(p->lex.pend - p->lex.pbeg);
+ return rb_parser_set_pos(yylloc, sourceline, beg_pos, end_pos);
+}
+
+YYLTYPE *
+rb_parser_set_location_of_dummy_end(struct parser_params *p, YYLTYPE *yylloc)
+{
+ yylloc->end_pos = yylloc->beg_pos;
+
+ return yylloc;
+}
+
+YYLTYPE *
rb_parser_set_location_of_none(struct parser_params *p, YYLTYPE *yylloc)
{
int sourceline = p->ruby_sourceline;
@@ -11339,7 +11928,7 @@ shareable_literal_constant(struct parser_params *p, enum shareability shareable,
}
if (RTEST(lit)) {
VALUE e = shareable_literal_value(elt);
- if (e != Qundef) {
+ if (!UNDEF_P(e)) {
rb_ary_push(lit, e);
}
else {
@@ -11379,7 +11968,7 @@ shareable_literal_constant(struct parser_params *p, enum shareability shareable,
if (RTEST(lit)) {
VALUE k = shareable_literal_value(key);
VALUE v = shareable_literal_value(val);
- if (k != Qundef && v != Qundef) {
+ if (!UNDEF_P(k) && !UNDEF_P(v)) {
rb_hash_aset(lit, k, v);
}
else {
@@ -12042,7 +12631,7 @@ new_args(struct parser_params *p, NODE *pre_args, NODE *opt_args, ID rest_arg, N
int saved_line = p->ruby_sourceline;
struct rb_args_info *args = tail->nd_ainfo;
- if (args->block_arg == idFWD_BLOCK) {
+ if (args->forwarding) {
if (rest_arg) {
yyerror1(&tail->nd_loc, "... after rest argument");
return tail;
@@ -12061,7 +12650,11 @@ new_args(struct parser_params *p, NODE *pre_args, NODE *opt_args, ID rest_arg, N
args->opt_args = opt_args;
- args->ruby2_keywords = rest_arg == idFWD_REST;
+#ifdef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
+ args->ruby2_keywords = args->forwarding;
+#else
+ args->ruby2_keywords = 0;
+#endif
p->ruby_sourceline = saved_line;
nd_set_loc(tail, loc);
@@ -12621,26 +13214,43 @@ local_push(struct parser_params *p, int toplevel_scope)
}
static void
+vtable_chain_free(struct parser_params *p, struct vtable *table)
+{
+ while (!DVARS_TERMINAL_P(table)) {
+ struct vtable *cur_table = table;
+ table = cur_table->prev;
+ vtable_free(cur_table);
+ }
+}
+
+static void
+local_free(struct parser_params *p, struct local_vars *local)
+{
+ vtable_chain_free(p, local->used);
+
+# if WARN_PAST_SCOPE
+ vtable_chain_free(p, local->past);
+# endif
+
+ vtable_chain_free(p, local->args);
+ vtable_chain_free(p, local->vars);
+
+ ruby_sized_xfree(local, sizeof(struct local_vars));
+}
+
+static void
local_pop(struct parser_params *p)
{
struct local_vars *local = p->lvtbl->prev;
if (p->lvtbl->used) {
- warn_unused_var(p, p->lvtbl);
- vtable_free(p->lvtbl->used);
+ warn_unused_var(p, p->lvtbl);
}
-# if WARN_PAST_SCOPE
- while (p->lvtbl->past) {
- struct vtable *past = p->lvtbl->past;
- p->lvtbl->past = past->prev;
- vtable_free(past);
- }
-# endif
- vtable_free(p->lvtbl->args);
- vtable_free(p->lvtbl->vars);
+
+ local_free(p, p->lvtbl);
+ p->lvtbl = local;
+
CMDARG_POP();
COND_POP();
- ruby_sized_xfree(p->lvtbl, sizeof(*p->lvtbl));
- p->lvtbl = local;
}
#ifndef RIPPER
@@ -12745,11 +13355,7 @@ local_id(struct parser_params *p, ID id)
static int
check_forwarding_args(struct parser_params *p)
{
- if (local_id(p, idFWD_REST) &&
-#if idFWD_KWREST
- local_id(p, idFWD_KWREST) &&
-#endif
- local_id(p, idFWD_BLOCK)) return TRUE;
+ if (local_id(p, idFWD_ALL)) return TRUE;
compile_error(p, "unexpected ...");
return FALSE;
}
@@ -12758,24 +13364,25 @@ static void
add_forwarding_args(struct parser_params *p)
{
arg_var(p, idFWD_REST);
-#if idFWD_KWREST
+#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
arg_var(p, idFWD_KWREST);
#endif
arg_var(p, idFWD_BLOCK);
+ arg_var(p, idFWD_ALL);
}
#ifndef RIPPER
static NODE *
new_args_forward_call(struct parser_params *p, NODE *leading, const YYLTYPE *loc, const YYLTYPE *argsloc)
{
- NODE *splat = NEW_SPLAT(NEW_LVAR(idFWD_REST, loc), loc);
-#if idFWD_KWREST
+ NODE *rest = NEW_LVAR(idFWD_REST, loc);
+#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
NODE *kwrest = list_append(p, NEW_LIST(0, loc), NEW_LVAR(idFWD_KWREST, loc));
#endif
NODE *block = NEW_BLOCK_PASS(NEW_LVAR(idFWD_BLOCK, loc), loc);
- NODE *args = leading ? rest_arg_append(p, leading, splat, argsloc) : splat;
-#if idFWD_KWREST
- args = arg_append(p, splat, new_hash(p, kwrest, loc), loc);
+ NODE *args = leading ? rest_arg_append(p, leading, rest, argsloc) : NEW_SPLAT(rest, loc);
+#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
+ args = arg_append(p, args, new_hash(p, kwrest, loc), loc);
#endif
return arg_blk_pass(args, block);
}
@@ -12958,7 +13565,7 @@ rb_reg_fragment_setenc(struct parser_params* p, VALUE str, int options)
}
rb_enc_associate(str, rb_ascii8bit_encoding());
}
- else if (p->enc == rb_usascii_encoding()) {
+ else if (rb_is_usascii_enc(p->enc)) {
if (!is_ascii_string(str)) {
/* raise in re.c */
rb_enc_associate(str, rb_usascii_encoding());
@@ -13164,12 +13771,15 @@ parser_initialize(struct parser_params *p)
p->ruby_sourcefile_string = Qnil;
p->lex.lpar_beg = -1; /* make lambda_beginning_p() == FALSE at first */
p->node_id = 0;
-#ifdef RIPPER
p->delayed.token = Qnil;
+#ifdef RIPPER
p->result = Qnil;
p->parsing_thread = Qnil;
#else
p->error_buffer = Qfalse;
+ p->end_expect_token_locations = Qnil;
+ p->token_id = 0;
+ p->tokens = Qnil;
#endif
p->debug_buffer = Qnil;
p->debug_output = rb_ractor_stdout();
@@ -13187,19 +13797,20 @@ parser_mark(void *ptr)
struct parser_params *p = (struct parser_params*)ptr;
rb_gc_mark(p->lex.input);
- rb_gc_mark(p->lex.prevline);
rb_gc_mark(p->lex.lastline);
rb_gc_mark(p->lex.nextline);
rb_gc_mark(p->ruby_sourcefile_string);
rb_gc_mark((VALUE)p->lex.strterm);
rb_gc_mark((VALUE)p->ast);
rb_gc_mark(p->case_labels);
+ rb_gc_mark(p->delayed.token);
#ifndef RIPPER
rb_gc_mark(p->debug_lines);
rb_gc_mark(p->compile_option);
rb_gc_mark(p->error_buffer);
+ rb_gc_mark(p->end_expect_token_locations);
+ rb_gc_mark(p->tokens);
#else
- rb_gc_mark(p->delayed.token);
rb_gc_mark(p->value);
rb_gc_mark(p->result);
rb_gc_mark(p->parsing_thread);
@@ -13220,16 +13831,17 @@ parser_free(void *ptr)
if (p->tokenbuf) {
ruby_sized_xfree(p->tokenbuf, p->toksiz);
}
+
for (local = p->lvtbl; local; local = prev) {
- if (local->vars) xfree(local->vars);
- prev = local->prev;
- xfree(local);
+ prev = local->prev;
+ local_free(p, local);
}
+
{
- token_info *ptinfo;
- while ((ptinfo = p->token_info) != 0) {
- p->token_info = ptinfo->next;
- xfree(ptinfo);
+ token_info *ptinfo;
+ while ((ptinfo = p->token_info) != 0) {
+ p->token_info = ptinfo->next;
+ xfree(ptinfo);
}
}
xfree(ptr);
@@ -13302,6 +13914,27 @@ rb_parser_keep_script_lines(VALUE vparser)
TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
p->keep_script_lines = 1;
}
+
+void
+rb_parser_error_tolerant(VALUE vparser)
+{
+ struct parser_params *p;
+
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
+ p->error_tolerant = 1;
+ p->end_expect_token_locations = rb_ary_new();
+}
+
+void
+rb_parser_keep_tokens(VALUE vparser)
+{
+ struct parser_params *p;
+
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
+ p->keep_tokens = 1;
+ p->tokens = rb_ary_new();
+}
+
#endif
#ifdef RIPPER
@@ -13611,8 +14244,8 @@ ripper_validate_object(VALUE self, VALUE x)
{
if (x == Qfalse) return x;
if (x == Qtrue) return x;
- if (x == Qnil) return x;
- if (x == Qundef)
+ 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;
@@ -13723,7 +14356,7 @@ static VALUE
ripper_get_value(VALUE v)
{
NODE *nd;
- if (v == Qundef) return Qnil;
+ 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;
@@ -13984,7 +14617,7 @@ static VALUE
ripper_assert_Qundef(VALUE self, VALUE obj, VALUE msg)
{
StringValue(msg);
- if (obj == Qundef) {
+ if (UNDEF_P(obj)) {
rb_raise(rb_eArgError, "%"PRIsVALUE, msg);
}
return Qnil;
diff --git a/probes_helper.h b/probes_helper.h
index d2d0ebb6e2..0003e96dd8 100644
--- a/probes_helper.h
+++ b/probes_helper.h
@@ -19,13 +19,13 @@ MJIT_SYMBOL_EXPORT_END
#define RUBY_DTRACE_METHOD_HOOK(name, ec, klazz, id) \
do { \
if (UNLIKELY(RUBY_DTRACE_##name##_ENABLED())) { \
- struct ruby_dtrace_method_hook_args args; \
- if (rb_dtrace_setup(ec, klazz, id, &args)) { \
- RUBY_DTRACE_##name(args.classname, \
- args.methodname, \
- args.filename, \
- args.line_no); \
- } \
+ struct ruby_dtrace_method_hook_args args; \
+ if (rb_dtrace_setup(ec, klazz, id, &args)) { \
+ RUBY_DTRACE_##name(args.classname, \
+ args.methodname, \
+ args.filename, \
+ args.line_no); \
+ } \
} \
} while (0)
diff --git a/proc.c b/proc.c
index 9bb02a0114..bb0ad89851 100644
--- a/proc.c
+++ b/proc.c
@@ -15,6 +15,7 @@
#include "internal/class.h"
#include "internal/error.h"
#include "internal/eval.h"
+#include "internal/hash.h"
#include "internal/object.h"
#include "internal/proc.h"
#include "internal/symbol.h"
@@ -37,10 +38,13 @@ const rb_cref_t *rb_vm_cref_in_context(VALUE self, VALUE cbase);
struct METHOD {
const VALUE recv;
const VALUE klass;
+ /* needed for #super_method */
const VALUE iclass;
+ /* Different than me->owner only for ZSUPER methods.
+ This is error-prone but unavoidable unless ZSUPER methods are removed. */
+ const VALUE owner;
const rb_method_entry_t * const me;
/* for bound methods, `me' should be rb_callable_method_entry_t * */
- rb_method_visibility_t visibility;
};
VALUE rb_cUnboundMethod;
@@ -82,21 +86,21 @@ block_mark(const struct rb_block *block)
switch (vm_block_type(block)) {
case block_type_iseq:
case block_type_ifunc:
- {
- const struct rb_captured_block *captured = &block->as.captured;
- RUBY_MARK_MOVABLE_UNLESS_NULL(captured->self);
- RUBY_MARK_MOVABLE_UNLESS_NULL((VALUE)captured->code.val);
- if (captured->ep && captured->ep[VM_ENV_DATA_INDEX_ENV] != Qundef /* cfunc_proc_t */) {
+ {
+ const struct rb_captured_block *captured = &block->as.captured;
+ RUBY_MARK_MOVABLE_UNLESS_NULL(captured->self);
+ RUBY_MARK_MOVABLE_UNLESS_NULL((VALUE)captured->code.val);
+ if (captured->ep && !UNDEF_P(captured->ep[VM_ENV_DATA_INDEX_ENV]) /* cfunc_proc_t */) {
rb_gc_mark(VM_ENV_ENVVAL(captured->ep));
- }
- }
- break;
+ }
+ }
+ break;
case block_type_symbol:
- RUBY_MARK_MOVABLE_UNLESS_NULL(block->as.symbol);
- break;
+ RUBY_MARK_MOVABLE_UNLESS_NULL(block->as.symbol);
+ break;
case block_type_proc:
- RUBY_MARK_MOVABLE_UNLESS_NULL(block->as.proc);
- break;
+ RUBY_MARK_MOVABLE_UNLESS_NULL(block->as.proc);
+ break;
}
}
@@ -106,18 +110,18 @@ block_compact(struct rb_block *block)
switch (block->type) {
case block_type_iseq:
case block_type_ifunc:
- {
- struct rb_captured_block *captured = &block->as.captured;
+ {
+ struct rb_captured_block *captured = &block->as.captured;
captured->self = rb_gc_location(captured->self);
captured->code.val = rb_gc_location(captured->code.val);
- }
- break;
+ }
+ break;
case block_type_symbol:
block->as.symbol = rb_gc_location(block->as.symbol);
- break;
+ break;
case block_type_proc:
block->as.proc = rb_gc_location(block->as.proc);
- break;
+ break;
}
}
@@ -146,17 +150,17 @@ proc_memsize(const void *ptr)
{
const rb_proc_t *proc = ptr;
if (proc->block.as.captured.ep == ((const cfunc_proc_t *)ptr)->env+1)
- return sizeof(cfunc_proc_t);
+ return sizeof(cfunc_proc_t);
return sizeof(rb_proc_t);
}
static const rb_data_type_t proc_data_type = {
"proc",
{
- proc_mark,
- RUBY_TYPED_DEFAULT_FREE,
- proc_memsize,
- proc_compact,
+ proc_mark,
+ RUBY_TYPED_DEFAULT_FREE,
+ proc_memsize,
+ proc_compact,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};
@@ -334,10 +338,10 @@ binding_memsize(const void *ptr)
const rb_data_type_t ruby_binding_data_type = {
"binding",
{
- binding_mark,
- binding_free,
- binding_memsize,
- binding_compact,
+ binding_mark,
+ binding_free,
+ binding_memsize,
+ binding_compact,
},
0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
};
@@ -438,37 +442,37 @@ get_local_variable_ptr(const rb_env_t **envp, ID lid)
{
const rb_env_t *env = *envp;
do {
- if (!VM_ENV_FLAGS(env->ep, VM_FRAME_FLAG_CFRAME)) {
+ if (!VM_ENV_FLAGS(env->ep, VM_FRAME_FLAG_CFRAME)) {
if (VM_ENV_FLAGS(env->ep, VM_ENV_FLAG_ISOLATED)) {
return NULL;
}
const rb_iseq_t *iseq = env->iseq;
- unsigned int i;
+ unsigned int i;
- VM_ASSERT(rb_obj_is_iseq((VALUE)iseq));
+ VM_ASSERT(rb_obj_is_iseq((VALUE)iseq));
for (i=0; i<ISEQ_BODY(iseq)->local_table_size; i++) {
if (ISEQ_BODY(iseq)->local_table[i] == lid) {
if (ISEQ_BODY(iseq)->local_iseq == iseq &&
ISEQ_BODY(iseq)->param.flags.has_block &&
(unsigned int)ISEQ_BODY(iseq)->param.block_start == i) {
- const VALUE *ep = env->ep;
- if (!VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)) {
- RB_OBJ_WRITE(env, &env->env[i], rb_vm_bh_to_procval(GET_EC(), VM_ENV_BLOCK_HANDLER(ep)));
- VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM);
- }
- }
-
- *envp = env;
- return &env->env[i];
- }
- }
- }
- else {
- *envp = NULL;
- return NULL;
- }
+ const VALUE *ep = env->ep;
+ if (!VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)) {
+ RB_OBJ_WRITE(env, &env->env[i], rb_vm_bh_to_procval(GET_EC(), VM_ENV_BLOCK_HANDLER(ep)));
+ VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM);
+ }
+ }
+
+ *envp = env;
+ return &env->env[i];
+ }
+ }
+ }
+ else {
+ *envp = NULL;
+ return NULL;
+ }
} while ((env = rb_vm_env_prev_env(env)) != NULL);
*envp = NULL;
@@ -487,17 +491,17 @@ check_local_id(VALUE bindval, volatile VALUE *pname)
VALUE name = *pname;
if (lid) {
- if (!rb_is_local_id(lid)) {
- rb_name_err_raise("wrong local variable name `%1$s' for %2$s",
- bindval, ID2SYM(lid));
- }
+ if (!rb_is_local_id(lid)) {
+ rb_name_err_raise("wrong local variable name `%1$s' for %2$s",
+ bindval, ID2SYM(lid));
+ }
}
else {
- if (!rb_is_local_name(name)) {
- rb_name_err_raise("wrong local variable name `%1$s' for %2$s",
- bindval, name);
- }
- return 0;
+ if (!rb_is_local_name(name)) {
+ rb_name_err_raise("wrong local variable name `%1$s' for %2$s",
+ bindval, name);
+ }
+ return 0;
}
return lid;
}
@@ -610,9 +614,9 @@ bind_local_variable_set(VALUE bindval, VALUE sym, VALUE val)
GetBindingPtr(bindval, bind);
env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
if ((ptr = get_local_variable_ptr(&env, lid)) == NULL) {
- /* not found. create new env */
- ptr = rb_binding_add_dynavars(bindval, bind, 1, &lid);
- env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
+ /* not found. create new env */
+ ptr = rb_binding_add_dynavars(bindval, bind, 1, &lid);
+ env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
}
#if YJIT_STATS
@@ -727,25 +731,25 @@ struct vm_ifunc *
rb_vm_ifunc_new(rb_block_call_func_t func, const void *data, int min_argc, int max_argc)
{
union {
- struct vm_ifunc_argc argc;
- VALUE packed;
+ struct vm_ifunc_argc argc;
+ VALUE packed;
} arity;
if (min_argc < UNLIMITED_ARGUMENTS ||
#if SIZEOF_INT * 2 > SIZEOF_VALUE
- min_argc >= (int)(1U << (SIZEOF_VALUE * CHAR_BIT) / 2) ||
+ min_argc >= (int)(1U << (SIZEOF_VALUE * CHAR_BIT) / 2) ||
#endif
- 0) {
- rb_raise(rb_eRangeError, "minimum argument number out of range: %d",
- min_argc);
+ 0) {
+ rb_raise(rb_eRangeError, "minimum argument number out of range: %d",
+ min_argc);
}
if (max_argc < UNLIMITED_ARGUMENTS ||
#if SIZEOF_INT * 2 > SIZEOF_VALUE
- max_argc >= (int)(1U << (SIZEOF_VALUE * CHAR_BIT) / 2) ||
+ max_argc >= (int)(1U << (SIZEOF_VALUE * CHAR_BIT) / 2) ||
#endif
- 0) {
- rb_raise(rb_eRangeError, "maximum argument number out of range: %d",
- max_argc);
+ 0) {
+ rb_raise(rb_eRangeError, "maximum argument number out of range: %d",
+ max_argc);
}
arity.argc.min = min_argc;
arity.argc.max = max_argc;
@@ -784,26 +788,26 @@ proc_new(VALUE klass, int8_t is_lambda, int8_t kernel)
/* block is in cf */
switch (vm_block_handler_type(block_handler)) {
case block_handler_type_proc:
- procval = VM_BH_TO_PROC(block_handler);
+ procval = VM_BH_TO_PROC(block_handler);
- if (RBASIC_CLASS(procval) == klass) {
- return procval;
- }
- else {
- VALUE newprocval = rb_proc_dup(procval);
+ if (RBASIC_CLASS(procval) == klass) {
+ return procval;
+ }
+ else {
+ VALUE newprocval = rb_proc_dup(procval);
RBASIC_SET_CLASS(newprocval, klass);
- return newprocval;
- }
- break;
+ return newprocval;
+ }
+ break;
case block_handler_type_symbol:
- return (klass != rb_cProc) ?
- sym_proc_new(klass, VM_BH_TO_SYMBOL(block_handler)) :
- rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler));
- break;
+ return (klass != rb_cProc) ?
+ sym_proc_new(klass, VM_BH_TO_SYMBOL(block_handler)) :
+ rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler));
+ break;
case block_handler_type_ifunc:
- return rb_vm_make_proc_lambda(ec, VM_BH_TO_CAPT_BLOCK(block_handler), klass, is_lambda);
+ return rb_vm_make_proc_lambda(ec, VM_BH_TO_CAPT_BLOCK(block_handler), klass, is_lambda);
case block_handler_type_iseq:
{
const struct rb_captured_block *captured = VM_BH_TO_CAPT_BLOCK(block_handler);
@@ -972,8 +976,8 @@ static inline int
check_argc(long argc)
{
if (argc > INT_MAX || argc < 0) {
- rb_raise(rb_eArgError, "too many arguments (%lu)",
- (unsigned long)argc);
+ rb_raise(rb_eArgError, "too many arguments (%lu)",
+ (unsigned long)argc);
}
return (int)argc;
}
@@ -1091,20 +1095,20 @@ rb_vm_block_min_max_arity(const struct rb_block *block, int *max)
again:
switch (vm_block_type(block)) {
case block_type_iseq:
- return rb_iseq_min_max_arity(rb_iseq_check(block->as.captured.code.iseq), max);
+ return rb_iseq_min_max_arity(rb_iseq_check(block->as.captured.code.iseq), max);
case block_type_proc:
- block = vm_proc_block(block->as.proc);
- goto again;
+ block = vm_proc_block(block->as.proc);
+ goto again;
case block_type_ifunc:
- {
- const struct vm_ifunc *ifunc = block->as.captured.code.ifunc;
- if (IS_METHOD_PROC_IFUNC(ifunc)) {
- /* e.g. method(:foo).to_proc.arity */
- return method_min_max_arity((VALUE)ifunc->data, max);
- }
- *max = ifunc->argc.max;
- return ifunc->argc.min;
- }
+ {
+ const struct vm_ifunc *ifunc = block->as.captured.code.ifunc;
+ if (IS_METHOD_PROC_IFUNC(ifunc)) {
+ /* e.g. method(:foo).to_proc.arity */
+ return method_min_max_arity((VALUE)ifunc->data, max);
+ }
+ *max = ifunc->argc.max;
+ return ifunc->argc.min;
+ }
case block_type_symbol:
*max = UNLIMITED_ARGUMENTS;
return 1;
@@ -1142,20 +1146,20 @@ block_setup(struct rb_block *block, VALUE block_handler)
{
switch (vm_block_handler_type(block_handler)) {
case block_handler_type_iseq:
- block->type = block_type_iseq;
- block->as.captured = *VM_BH_TO_ISEQ_BLOCK(block_handler);
- break;
+ block->type = block_type_iseq;
+ block->as.captured = *VM_BH_TO_ISEQ_BLOCK(block_handler);
+ break;
case block_handler_type_ifunc:
- block->type = block_type_ifunc;
- block->as.captured = *VM_BH_TO_IFUNC_BLOCK(block_handler);
- break;
+ block->type = block_type_ifunc;
+ block->as.captured = *VM_BH_TO_IFUNC_BLOCK(block_handler);
+ break;
case block_handler_type_symbol:
- block->type = block_type_symbol;
- block->as.symbol = VM_BH_TO_SYMBOL(block_handler);
- break;
+ block->type = block_type_symbol;
+ block->as.symbol = VM_BH_TO_SYMBOL(block_handler);
+ break;
case block_handler_type_proc:
- block->type = block_type_proc;
- block->as.proc = VM_BH_TO_PROC(block_handler);
+ block->type = block_type_proc;
+ block->as.proc = VM_BH_TO_PROC(block_handler);
}
}
@@ -1169,7 +1173,7 @@ rb_block_pair_yield_optimizable(void)
struct rb_block block;
if (block_handler == VM_BLOCK_HANDLER_NONE) {
- rb_raise(rb_eArgError, "no block given");
+ rb_raise(rb_eArgError, "no block given");
}
block_setup(&block, block_handler);
@@ -1180,14 +1184,14 @@ rb_block_pair_yield_optimizable(void)
return 0;
case block_handler_type_proc:
- {
- VALUE procval = block_handler;
- rb_proc_t *proc;
- GetProcPtr(procval, proc);
+ {
+ VALUE procval = block_handler;
+ rb_proc_t *proc;
+ GetProcPtr(procval, proc);
if (proc->is_lambda) return 0;
if (min != max) return 0;
return min > 1;
- }
+ }
default:
return min > 1;
@@ -1204,21 +1208,21 @@ rb_block_arity(void)
struct rb_block block;
if (block_handler == VM_BLOCK_HANDLER_NONE) {
- rb_raise(rb_eArgError, "no block given");
+ rb_raise(rb_eArgError, "no block given");
}
block_setup(&block, block_handler);
switch (vm_block_type(&block)) {
case block_handler_type_symbol:
- return -1;
+ return -1;
case block_handler_type_proc:
return rb_proc_arity(block_handler);
default:
min = rb_vm_block_min_max_arity(&block, &max);
- return max != UNLIMITED_ARGUMENTS ? min : -min-1;
+ return max != UNLIMITED_ARGUMENTS ? min : -min-1;
}
}
@@ -1231,7 +1235,7 @@ rb_block_min_max_arity(int *max)
struct rb_block block;
if (block_handler == VM_BLOCK_HANDLER_NONE) {
- rb_raise(rb_eArgError, "no block given");
+ rb_raise(rb_eArgError, "no block given");
}
block_setup(&block, block_handler);
@@ -1250,23 +1254,23 @@ rb_proc_get_iseq(VALUE self, int *is_proc)
switch (vm_block_type(block)) {
case block_type_iseq:
- return rb_iseq_check(block->as.captured.code.iseq);
+ return rb_iseq_check(block->as.captured.code.iseq);
case block_type_proc:
- return rb_proc_get_iseq(block->as.proc, is_proc);
+ return rb_proc_get_iseq(block->as.proc, is_proc);
case block_type_ifunc:
- {
+ {
const struct vm_ifunc *ifunc = block->as.captured.code.ifunc;
- if (IS_METHOD_PROC_IFUNC(ifunc)) {
- /* method(:foo).to_proc */
- if (is_proc) *is_proc = 0;
- return rb_method_iseq((VALUE)ifunc->data);
- }
- else {
- return NULL;
- }
- }
+ if (IS_METHOD_PROC_IFUNC(ifunc)) {
+ /* method(:foo).to_proc */
+ if (is_proc) *is_proc = 0;
+ return rb_method_iseq((VALUE)ifunc->data);
+ }
+ else {
+ return NULL;
+ }
+ }
case block_type_symbol:
- return NULL;
+ return NULL;
}
VM_UNREACHABLE(rb_proc_get_iseq);
@@ -1364,7 +1368,7 @@ iseq_location(const rb_iseq_t *iseq)
if (!iseq) return Qnil;
rb_iseq_check(iseq);
loc[0] = rb_iseq_path(iseq);
- loc[1] = ISEQ_BODY(iseq)->location.first_lineno;
+ loc[1] = RB_INT2NUM(ISEQ_BODY(iseq)->location.first_lineno);
return rb_ary_new4(2, loc);
}
@@ -1399,11 +1403,11 @@ rb_unnamed_parameters(int arity)
a = rb_ary_new3(1, ID2SYM(req));
OBJ_FREEZE(a);
for (; n; --n) {
- rb_ary_push(param, a);
+ rb_ary_push(param, a);
}
if (arity < 0) {
- CONST_ID(rest, "rest");
- rb_ary_store(param, ~arity, rb_ary_new3(1, ID2SYM(rest)));
+ CONST_ID(rest, "rest");
+ rb_ary_store(param, ~arity, rb_ary_new3(1, ID2SYM(rest)));
}
return param;
}
@@ -1451,7 +1455,7 @@ rb_proc_parameters(int argc, VALUE *argv, VALUE self)
}
if (!iseq) {
- return rb_unnamed_parameters(rb_proc_arity(self));
+ return rb_unnamed_parameters(rb_proc_arity(self));
}
return rb_iseq_parameters(iseq, is_proc);
}
@@ -1461,8 +1465,24 @@ rb_hash_proc(st_index_t hash, VALUE prc)
{
rb_proc_t *proc;
GetProcPtr(prc, proc);
- hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.code.val);
- hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.self);
+
+ switch (vm_block_type(&proc->block)) {
+ case block_type_iseq:
+ hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.iseq->body);
+ break;
+ case block_type_ifunc:
+ hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.ifunc->func);
+ break;
+ case block_type_symbol:
+ hash = rb_st_hash_uint(hash, rb_any_hash(proc->block.as.symbol));
+ break;
+ case block_type_proc:
+ hash = rb_st_hash_uint(hash, rb_any_hash(proc->block.as.proc));
+ break;
+ default:
+ rb_bug("rb_hash_proc: unknown block type %d", vm_block_type(&proc->block));
+ }
+
return rb_hash_uint(hash, (st_index_t)proc->block.as.captured.ep);
}
@@ -1491,9 +1511,9 @@ rb_sym_to_proc(VALUE sym)
ID id;
if (!sym_proc_cache) {
- sym_proc_cache = rb_ary_tmp_new(SYM_PROC_CACHE_SIZE * 2);
- rb_gc_register_mark_object(sym_proc_cache);
- rb_ary_store(sym_proc_cache, SYM_PROC_CACHE_SIZE*2 - 1, Qnil);
+ sym_proc_cache = rb_ary_hidden_new(SYM_PROC_CACHE_SIZE * 2);
+ rb_gc_register_mark_object(sym_proc_cache);
+ rb_ary_store(sym_proc_cache, SYM_PROC_CACHE_SIZE*2 - 1, Qnil);
}
id = SYM2ID(sym);
@@ -1506,7 +1526,7 @@ rb_sym_to_proc(VALUE sym)
proc = sym_proc_new(rb_cProc, ID2SYM(id));
RARRAY_ASET(sym_proc_cache, index, sym);
RARRAY_ASET(sym_proc_cache, index + 1, proc);
- return proc;
+ return proc;
}
}
@@ -1538,22 +1558,22 @@ rb_block_to_s(VALUE self, const struct rb_block *block, const char *additional_i
again:
switch (vm_block_type(block)) {
case block_type_proc:
- block = vm_proc_block(block->as.proc);
- goto again;
+ block = vm_proc_block(block->as.proc);
+ goto again;
case block_type_iseq:
- {
- const rb_iseq_t *iseq = rb_iseq_check(block->as.captured.code.iseq);
+ {
+ const rb_iseq_t *iseq = rb_iseq_check(block->as.captured.code.iseq);
rb_str_catf(str, "%p %"PRIsVALUE":%d", (void *)self,
- rb_iseq_path(iseq),
- FIX2INT(ISEQ_BODY(iseq)->location.first_lineno));
- }
- break;
+ rb_iseq_path(iseq),
+ ISEQ_BODY(iseq)->location.first_lineno);
+ }
+ break;
case block_type_symbol:
- rb_str_catf(str, "%p(&%+"PRIsVALUE")", (void *)self, block->as.symbol);
- break;
+ rb_str_catf(str, "%p(&%+"PRIsVALUE")", (void *)self, block->as.symbol);
+ break;
case block_type_ifunc:
- rb_str_catf(str, "%p", (void *)block->as.captured.code.ifunc);
- break;
+ rb_str_catf(str, "%p", (void *)block->as.captured.code.ifunc);
+ break;
}
if (additional_info) rb_str_cat_cstr(str, additional_info);
@@ -1598,6 +1618,7 @@ bm_mark(void *ptr)
rb_gc_mark_movable(data->recv);
rb_gc_mark_movable(data->klass);
rb_gc_mark_movable(data->iclass);
+ rb_gc_mark_movable(data->owner);
rb_gc_mark_movable((VALUE)data->me);
}
@@ -1608,6 +1629,7 @@ bm_compact(void *ptr)
UPDATE_REFERENCE(data->recv);
UPDATE_REFERENCE(data->klass);
UPDATE_REFERENCE(data->iclass);
+ UPDATE_REFERENCE(data->owner);
UPDATE_TYPED_REFERENCE(rb_method_entry_t *, data->me);
}
@@ -1620,10 +1642,10 @@ bm_memsize(const void *ptr)
static const rb_data_type_t method_data_type = {
"method",
{
- bm_mark,
- RUBY_TYPED_DEFAULT_FREE,
- bm_memsize,
- bm_compact,
+ bm_mark,
+ RUBY_TYPED_DEFAULT_FREE,
+ bm_memsize,
+ bm_compact,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
@@ -1640,7 +1662,7 @@ respond_to_missing_p(VALUE klass, VALUE obj, VALUE sym, int scope)
/* TODO: merge with obj_respond_to() */
ID rmiss = idRespond_to_missing;
- if (obj == Qundef) return 0;
+ if (UNDEF_P(obj)) return 0;
if (rb_method_basic_definition_p(klass, rmiss)) return 0;
return RTEST(rb_funcall(obj, rmiss, 2, sym, RBOOL(!scope)));
}
@@ -1656,6 +1678,7 @@ mnew_missing(VALUE klass, VALUE obj, ID id, VALUE mclass)
RB_OBJ_WRITE(method, &data->recv, obj);
RB_OBJ_WRITE(method, &data->klass, klass);
+ RB_OBJ_WRITE(method, &data->owner, klass);
def = ZALLOC(rb_method_definition_t);
def->type = VM_METHOD_TYPE_MISSING;
@@ -1664,7 +1687,6 @@ mnew_missing(VALUE klass, VALUE obj, ID id, VALUE mclass)
me = rb_method_entry_create(id, klass, METHOD_VISI_UNDEF, def);
RB_OBJ_WRITE(method, &data->me, me);
- data->visibility = METHOD_ENTRY_VISI(me);
return method;
}
@@ -1680,56 +1702,63 @@ mnew_missing_by_name(VALUE klass, VALUE obj, VALUE *name, int scope, VALUE mclas
static VALUE
mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
- VALUE obj, ID id, VALUE mclass, int scope, int error)
+ VALUE obj, ID id, VALUE mclass, int scope, int error)
{
struct METHOD *data;
VALUE method;
+ const rb_method_entry_t *original_me = me;
rb_method_visibility_t visi = METHOD_VISI_UNDEF;
again:
if (UNDEFINED_METHOD_ENTRY_P(me)) {
- if (respond_to_missing_p(klass, obj, ID2SYM(id), scope)) {
- return mnew_missing(klass, obj, id, mclass);
- }
- if (!error) return Qnil;
- rb_print_undef(klass, id, METHOD_VISI_UNDEF);
+ if (respond_to_missing_p(klass, obj, ID2SYM(id), scope)) {
+ return mnew_missing(klass, obj, id, mclass);
+ }
+ if (!error) return Qnil;
+ rb_print_undef(klass, id, METHOD_VISI_UNDEF);
}
if (visi == METHOD_VISI_UNDEF) {
- visi = METHOD_ENTRY_VISI(me);
- RUBY_ASSERT(visi != METHOD_VISI_UNDEF); /* !UNDEFINED_METHOD_ENTRY_P(me) */
- if (scope && (visi != METHOD_VISI_PUBLIC)) {
- if (!error) return Qnil;
- rb_print_inaccessible(klass, id, visi);
- }
+ visi = METHOD_ENTRY_VISI(me);
+ RUBY_ASSERT(visi != METHOD_VISI_UNDEF); /* !UNDEFINED_METHOD_ENTRY_P(me) */
+ if (scope && (visi != METHOD_VISI_PUBLIC)) {
+ if (!error) return Qnil;
+ rb_print_inaccessible(klass, id, visi);
+ }
}
if (me->def->type == VM_METHOD_TYPE_ZSUPER) {
- if (me->defined_class) {
+ if (me->defined_class) {
VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->defined_class));
- id = me->def->original_id;
+ id = me->def->original_id;
me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass);
- }
- else {
+ }
+ else {
VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->owner));
- id = me->def->original_id;
- me = rb_method_entry_without_refinements(klass, id, &iclass);
- }
- goto again;
+ id = me->def->original_id;
+ me = rb_method_entry_without_refinements(klass, id, &iclass);
+ }
+ goto again;
}
method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
- RB_OBJ_WRITE(method, &data->recv, obj);
- RB_OBJ_WRITE(method, &data->klass, klass);
+ if (obj == Qundef) {
+ RB_OBJ_WRITE(method, &data->recv, Qundef);
+ RB_OBJ_WRITE(method, &data->klass, Qundef);
+ }
+ else {
+ RB_OBJ_WRITE(method, &data->recv, obj);
+ RB_OBJ_WRITE(method, &data->klass, klass);
+ }
RB_OBJ_WRITE(method, &data->iclass, iclass);
+ RB_OBJ_WRITE(method, &data->owner, original_me->owner);
RB_OBJ_WRITE(method, &data->me, me);
- data->visibility = visi;
return method;
}
static VALUE
mnew_from_me(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
- VALUE obj, ID id, VALUE mclass, int scope)
+ VALUE obj, ID id, VALUE mclass, int scope)
{
return mnew_internal(me, klass, iclass, obj, id, mclass, scope, TRUE);
}
@@ -1740,7 +1769,7 @@ mnew_callable(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
const rb_method_entry_t *me;
VALUE iclass = Qnil;
- ASSUME(obj != Qundef);
+ ASSUME(!UNDEF_P(obj));
me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass);
return mnew_from_me(me, klass, iclass, obj, id, mclass, scope);
}
@@ -1807,9 +1836,9 @@ method_eq(VALUE method, VALUE other)
VALUE klass1, klass2;
if (!rb_obj_is_method(other))
- return Qfalse;
+ return Qfalse;
if (CLASS_OF(method) != CLASS_OF(other))
- return Qfalse;
+ return Qfalse;
Check_TypedStruct(method, &method_data_type);
m1 = (struct METHOD *)DATA_PTR(method);
@@ -1819,11 +1848,10 @@ method_eq(VALUE method, VALUE other)
klass2 = method_entry_defined_class(m2->me);
if (!rb_method_entry_eq(m1->me, m2->me) ||
- klass1 != klass2 ||
- m1->visibility != m2->visibility ||
- m1->klass != m2->klass ||
- m1->recv != m2->recv) {
- return Qfalse;
+ klass1 != klass2 ||
+ m1->klass != m2->klass ||
+ m1->recv != m2->recv) {
+ return Qfalse;
}
return Qtrue;
@@ -1831,6 +1859,22 @@ method_eq(VALUE method, VALUE other)
/*
* call-seq:
+ * meth.eql?(other_meth) -> true or false
+ * meth == other_meth -> true or false
+ *
+ * Two unbound method objects are equal if they refer to the same
+ * method definition.
+ *
+ * Array.instance_method(:each_slice) == Enumerable.instance_method(:each_slice)
+ * #=> true
+ *
+ * Array.instance_method(:sum) == Enumerable.instance_method(:sum)
+ * #=> false, Array redefines the method for efficiency
+ */
+#define unbound_method_eq method_eq
+
+/*
+ * call-seq:
* meth.hash -> integer
*
* Returns a hash value corresponding to the method object.
@@ -1869,12 +1913,12 @@ method_unbind(VALUE obj)
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, orig);
method = TypedData_Make_Struct(rb_cUnboundMethod, struct METHOD,
- &method_data_type, data);
+ &method_data_type, data);
RB_OBJ_WRITE(method, &data->recv, Qundef);
- RB_OBJ_WRITE(method, &data->klass, orig->klass);
+ RB_OBJ_WRITE(method, &data->klass, Qundef);
RB_OBJ_WRITE(method, &data->iclass, orig->iclass);
+ RB_OBJ_WRITE(method, &data->owner, orig->me->owner);
RB_OBJ_WRITE(method, &data->me, rb_method_entry_clone(orig->me));
- data->visibility = orig->visibility;
return method;
}
@@ -1939,7 +1983,15 @@ method_original_name(VALUE obj)
* call-seq:
* meth.owner -> class_or_module
*
- * Returns the class or module that defines the method.
+ * Returns the class or module on which this method is defined.
+ * In other words,
+ *
+ * meth.owner.instance_methods(false).include?(meth.name) # => true
+ *
+ * holds as long as the method is not removed/undefined/replaced,
+ * (with private_instance_methods instead of instance_methods if the method
+ * is private).
+ *
* See also Method#receiver.
*
* (1..3).method(:map).owner #=> Enumerable
@@ -1950,7 +2002,7 @@ method_owner(VALUE obj)
{
struct METHOD *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
- return data->me->owner;
+ return data->owner;
}
void
@@ -1961,22 +2013,22 @@ rb_method_name_error(VALUE klass, VALUE str)
VALUE s = Qundef;
if (FL_TEST(c, FL_SINGLETON)) {
- VALUE obj = rb_ivar_get(klass, attached);
+ VALUE obj = rb_ivar_get(klass, attached);
- switch (BUILTIN_TYPE(obj)) {
- case T_MODULE:
- case T_CLASS:
- c = obj;
+ switch (BUILTIN_TYPE(obj)) {
+ case T_MODULE:
+ case T_CLASS:
+ c = obj;
break;
default:
- break;
- }
+ break;
+ }
}
else if (RB_TYPE_P(c, T_MODULE)) {
- s = MSG(" module");
+ s = MSG(" module");
}
- if (s == Qundef) {
- s = MSG(" class");
+ if (UNDEF_P(s)) {
+ s = MSG(" class");
}
rb_name_err_raise_str(s, c, str);
#undef MSG
@@ -1992,7 +2044,7 @@ obj_method(VALUE obj, VALUE vid, int scope)
if (!id) {
VALUE m = mnew_missing_by_name(klass, obj, &vid, scope, mclass);
if (m) return m;
- rb_method_name_error(klass, vid);
+ rb_method_name_error(klass, vid);
}
return mnew_callable(klass, obj, id, mclass, scope);
}
@@ -2153,7 +2205,7 @@ rb_mod_instance_method(VALUE mod, VALUE vid)
{
ID id = rb_check_id(&vid);
if (!id) {
- rb_method_name_error(mod, vid);
+ rb_method_name_error(mod, vid);
}
return mnew_unbound(mod, id, rb_cUnboundMethod, FALSE);
}
@@ -2170,7 +2222,7 @@ rb_mod_public_instance_method(VALUE mod, VALUE vid)
{
ID id = rb_check_id(&vid);
if (!id) {
- rb_method_name_error(mod, vid);
+ rb_method_name_error(mod, vid);
}
return mnew_unbound(mod, id, rb_cUnboundMethod, TRUE);
}
@@ -2187,57 +2239,57 @@ rb_mod_define_method_with_visibility(int argc, VALUE *argv, VALUE mod, const str
name = argv[0];
id = rb_check_id(&name);
if (argc == 1) {
- body = rb_block_lambda();
+ body = rb_block_lambda();
}
else {
- body = argv[1];
-
- if (rb_obj_is_method(body)) {
- is_method = TRUE;
- }
- else if (rb_obj_is_proc(body)) {
- is_method = FALSE;
- }
- else {
- rb_raise(rb_eTypeError,
- "wrong argument type %s (expected Proc/Method/UnboundMethod)",
- rb_obj_classname(body));
- }
+ body = argv[1];
+
+ if (rb_obj_is_method(body)) {
+ is_method = TRUE;
+ }
+ else if (rb_obj_is_proc(body)) {
+ is_method = FALSE;
+ }
+ else {
+ rb_raise(rb_eTypeError,
+ "wrong argument type %s (expected Proc/Method/UnboundMethod)",
+ rb_obj_classname(body));
+ }
}
if (!id) id = rb_to_id(name);
if (is_method) {
- struct METHOD *method = (struct METHOD *)DATA_PTR(body);
- if (method->me->owner != mod && !RB_TYPE_P(method->me->owner, T_MODULE) &&
- !RTEST(rb_class_inherited_p(mod, method->me->owner))) {
- if (FL_TEST(method->me->owner, FL_SINGLETON)) {
- rb_raise(rb_eTypeError,
- "can't bind singleton method to a different class");
- }
- else {
- rb_raise(rb_eTypeError,
- "bind argument must be a subclass of % "PRIsVALUE,
- method->me->owner);
- }
- }
- rb_method_entry_set(mod, id, method->me, scope_visi->method_visi);
- if (scope_visi->module_func) {
- rb_method_entry_set(rb_singleton_class(mod), id, method->me, METHOD_VISI_PUBLIC);
- }
- RB_GC_GUARD(body);
+ struct METHOD *method = (struct METHOD *)DATA_PTR(body);
+ if (method->me->owner != mod && !RB_TYPE_P(method->me->owner, T_MODULE) &&
+ !RTEST(rb_class_inherited_p(mod, method->me->owner))) {
+ if (FL_TEST(method->me->owner, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError,
+ "can't bind singleton method to a different class");
+ }
+ else {
+ rb_raise(rb_eTypeError,
+ "bind argument must be a subclass of % "PRIsVALUE,
+ method->me->owner);
+ }
+ }
+ rb_method_entry_set(mod, id, method->me, scope_visi->method_visi);
+ if (scope_visi->module_func) {
+ rb_method_entry_set(rb_singleton_class(mod), id, method->me, METHOD_VISI_PUBLIC);
+ }
+ RB_GC_GUARD(body);
}
else {
- VALUE procval = rb_proc_dup(body);
- if (vm_proc_iseq(procval) != NULL) {
- rb_proc_t *proc;
- GetProcPtr(procval, proc);
- proc->is_lambda = TRUE;
- proc->is_from_method = TRUE;
- }
- rb_add_method(mod, id, VM_METHOD_TYPE_BMETHOD, (void *)procval, scope_visi->method_visi);
- if (scope_visi->module_func) {
- rb_add_method(rb_singleton_class(mod), id, VM_METHOD_TYPE_BMETHOD, (void *)body, METHOD_VISI_PUBLIC);
- }
+ VALUE procval = rb_proc_dup(body);
+ if (vm_proc_iseq(procval) != NULL) {
+ rb_proc_t *proc;
+ GetProcPtr(procval, proc);
+ proc->is_lambda = TRUE;
+ proc->is_from_method = TRUE;
+ }
+ rb_add_method(mod, id, VM_METHOD_TYPE_BMETHOD, (void *)procval, scope_visi->method_visi);
+ if (scope_visi->module_func) {
+ rb_add_method(rb_singleton_class(mod), id, VM_METHOD_TYPE_BMETHOD, (void *)body, METHOD_VISI_PUBLIC);
+ }
}
return ID2SYM(id);
@@ -2347,17 +2399,7 @@ rb_obj_define_method(int argc, VALUE *argv, VALUE obj)
static VALUE
top_define_method(int argc, VALUE *argv, VALUE obj)
{
- rb_thread_t *th = GET_THREAD();
- VALUE klass;
-
- klass = th->top_wrapper;
- if (klass) {
- rb_warning("main.define_method in the wrapped load is effective only in wrapper module");
- }
- else {
- klass = rb_cObject;
- }
- return rb_mod_define_method(argc, argv, klass);
+ return rb_mod_define_method(argc, argv, rb_top_main_class("define_method"));
}
/*
@@ -2389,8 +2431,8 @@ method_clone(VALUE self)
RB_OBJ_WRITE(clone, &data->recv, orig->recv);
RB_OBJ_WRITE(clone, &data->klass, orig->klass);
RB_OBJ_WRITE(clone, &data->iclass, orig->iclass);
+ RB_OBJ_WRITE(clone, &data->owner, orig->owner);
RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me));
- data->visibility = orig->visibility;
return clone;
}
@@ -2440,8 +2482,7 @@ method_clone(VALUE self)
static VALUE
rb_method_call_pass_called_kw(int argc, const VALUE *argv, VALUE method)
{
- VALUE procval = rb_block_given_p() ? rb_block_proc() : Qnil;
- return rb_method_call_with_block_kw(argc, argv, method, procval, RB_PASS_CALLED_KEYWORDS);
+ return rb_method_call_kw(argc, argv, method, RB_PASS_CALLED_KEYWORDS);
}
VALUE
@@ -2481,8 +2522,8 @@ rb_method_call_with_block_kw(int argc, const VALUE *argv, VALUE method, VALUE pa
rb_execution_context_t *ec = GET_EC();
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
- if (data->recv == Qundef) {
- rb_raise(rb_eTypeError, "can't call unbound method; bind first");
+ if (UNDEF_P(data->recv)) {
+ rb_raise(rb_eTypeError, "can't call unbound method; bind first");
}
return call_method_data(ec, data, argc, argv, passed_procval, kw_splat);
}
@@ -2549,9 +2590,9 @@ rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE passe
*/
static void
-convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALUE *methclass_out, VALUE *klass_out, VALUE *iclass_out, const rb_method_entry_t **me_out)
+convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALUE *methclass_out, VALUE *klass_out, VALUE *iclass_out, const rb_method_entry_t **me_out, const bool clone)
{
- VALUE methclass = data->me->owner;
+ VALUE methclass = data->owner;
VALUE iclass = data->me->defined_class;
VALUE klass = CLASS_OF(recv);
@@ -2559,29 +2600,39 @@ convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALU
VALUE refined_class = rb_refinement_module_get_refined_class(methclass);
if (!NIL_P(refined_class)) methclass = refined_class;
}
- if (!RB_TYPE_P(methclass, T_MODULE) &&
- methclass != CLASS_OF(recv) && !rb_obj_is_kind_of(recv, methclass)) {
- if (FL_TEST(methclass, FL_SINGLETON)) {
- rb_raise(rb_eTypeError,
- "singleton method called for a different object");
- }
- else {
- rb_raise(rb_eTypeError, "bind argument must be an instance of % "PRIsVALUE,
- methclass);
- }
+ if (!RB_TYPE_P(methclass, T_MODULE) && !RTEST(rb_obj_is_kind_of(recv, methclass))) {
+ if (FL_TEST(methclass, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError,
+ "singleton method called for a different object");
+ }
+ else {
+ rb_raise(rb_eTypeError, "bind argument must be an instance of % "PRIsVALUE,
+ methclass);
+ }
}
- const rb_method_entry_t *me = rb_method_entry_clone(data->me);
+ const rb_method_entry_t *me;
+ if (clone) {
+ me = rb_method_entry_clone(data->me);
+ }
+ else {
+ me = data->me;
+ }
if (RB_TYPE_P(me->owner, T_MODULE)) {
- VALUE ic = rb_class_search_ancestor(klass, me->owner);
- if (ic) {
- klass = ic;
+ if (!clone) {
+ // if we didn't previously clone the method entry, then we need to clone it now
+ // because this branch manipulates it in rb_method_entry_complement_defined_class
+ me = rb_method_entry_clone(me);
+ }
+ VALUE ic = rb_class_search_ancestor(klass, me->owner);
+ if (ic) {
+ klass = ic;
iclass = ic;
- }
- else {
- klass = rb_include_class_new(methclass, klass);
- }
+ }
+ else {
+ klass = rb_include_class_new(methclass, klass);
+ }
me = (const rb_method_entry_t *) rb_method_entry_complement_defined_class(me, me->called_id, klass);
}
@@ -2633,15 +2684,15 @@ umethod_bind(VALUE method, VALUE recv)
const rb_method_entry_t *me;
const struct METHOD *data;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
- convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me);
+ convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me, true);
struct METHOD *bound;
method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound);
RB_OBJ_WRITE(method, &bound->recv, recv);
RB_OBJ_WRITE(method, &bound->klass, klass);
RB_OBJ_WRITE(method, &bound->iclass, iclass);
+ RB_OBJ_WRITE(method, &bound->owner, methclass);
RB_OBJ_WRITE(method, &bound->me, me);
- bound->visibility = data->visibility;
return method;
}
@@ -2676,8 +2727,8 @@ umethod_bind_call(int argc, VALUE *argv, VALUE method)
else {
VALUE methclass, klass, iclass;
const rb_method_entry_t *me;
- convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me);
- struct METHOD bound = { recv, klass, 0, me, METHOD_ENTRY_VISI(me) };
+ convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me, false);
+ struct METHOD bound = { recv, klass, 0, methclass, me };
return call_method_data(ec, &bound, argc, argv, passed_procval, RB_PASS_CALLED_KEYWORDS);
}
@@ -2695,56 +2746,56 @@ method_def_min_max_arity(const rb_method_definition_t *def, int *max)
if (!def) return *max = 0;
switch (def->type) {
case VM_METHOD_TYPE_CFUNC:
- if (def->body.cfunc.argc < 0) {
- *max = UNLIMITED_ARGUMENTS;
- return 0;
- }
- return *max = check_argc(def->body.cfunc.argc);
+ if (def->body.cfunc.argc < 0) {
+ *max = UNLIMITED_ARGUMENTS;
+ return 0;
+ }
+ return *max = check_argc(def->body.cfunc.argc);
case VM_METHOD_TYPE_ZSUPER:
- *max = UNLIMITED_ARGUMENTS;
- return 0;
+ *max = UNLIMITED_ARGUMENTS;
+ return 0;
case VM_METHOD_TYPE_ATTRSET:
- return *max = 1;
+ return *max = 1;
case VM_METHOD_TYPE_IVAR:
- return *max = 0;
+ return *max = 0;
case VM_METHOD_TYPE_ALIAS:
- def = def->body.alias.original_me->def;
- goto again;
+ def = def->body.alias.original_me->def;
+ goto again;
case VM_METHOD_TYPE_BMETHOD:
return rb_proc_min_max_arity(def->body.bmethod.proc, max);
case VM_METHOD_TYPE_ISEQ:
- return rb_iseq_min_max_arity(rb_iseq_check(def->body.iseq.iseqptr), max);
+ return rb_iseq_min_max_arity(rb_iseq_check(def->body.iseq.iseqptr), max);
case VM_METHOD_TYPE_UNDEF:
case VM_METHOD_TYPE_NOTIMPLEMENTED:
- return *max = 0;
+ return *max = 0;
case VM_METHOD_TYPE_MISSING:
- *max = UNLIMITED_ARGUMENTS;
- return 0;
+ *max = UNLIMITED_ARGUMENTS;
+ return 0;
case VM_METHOD_TYPE_OPTIMIZED: {
- switch (def->body.optimized.type) {
- case OPTIMIZED_METHOD_TYPE_SEND:
- *max = UNLIMITED_ARGUMENTS;
- return 0;
- case OPTIMIZED_METHOD_TYPE_CALL:
- *max = UNLIMITED_ARGUMENTS;
- return 0;
- case OPTIMIZED_METHOD_TYPE_BLOCK_CALL:
- *max = UNLIMITED_ARGUMENTS;
- return 0;
+ switch (def->body.optimized.type) {
+ case OPTIMIZED_METHOD_TYPE_SEND:
+ *max = UNLIMITED_ARGUMENTS;
+ return 0;
+ case OPTIMIZED_METHOD_TYPE_CALL:
+ *max = UNLIMITED_ARGUMENTS;
+ return 0;
+ case OPTIMIZED_METHOD_TYPE_BLOCK_CALL:
+ *max = UNLIMITED_ARGUMENTS;
+ return 0;
case OPTIMIZED_METHOD_TYPE_STRUCT_AREF:
*max = 0;
return 0;
case OPTIMIZED_METHOD_TYPE_STRUCT_ASET:
*max = 1;
return 1;
- default:
- break;
- }
- break;
+ default:
+ break;
+ }
+ break;
}
case VM_METHOD_TYPE_REFINED:
- *max = UNLIMITED_ARGUMENTS;
- return 0;
+ *max = UNLIMITED_ARGUMENTS;
+ return 0;
}
rb_bug("method_def_min_max_arity: invalid method entry type (%d)", def->type);
UNREACHABLE_RETURN(Qnil);
@@ -2828,10 +2879,10 @@ original_method_entry(VALUE mod, ID id)
const rb_method_entry_t *me;
while ((me = rb_method_entry(mod, id)) != 0) {
- const rb_method_definition_t *def = me->def;
- if (def->type != VM_METHOD_TYPE_ZSUPER) break;
- mod = RCLASS_SUPER(me->owner);
- id = def->original_id;
+ const rb_method_definition_t *def = me->def;
+ if (def->type != VM_METHOD_TYPE_ZSUPER) break;
+ mod = RCLASS_SUPER(me->owner);
+ id = def->original_id;
}
return me;
}
@@ -2888,11 +2939,11 @@ method_def_iseq(const rb_method_definition_t *def)
{
switch (def->type) {
case VM_METHOD_TYPE_ISEQ:
- return rb_iseq_check(def->body.iseq.iseqptr);
+ return rb_iseq_check(def->body.iseq.iseqptr);
case VM_METHOD_TYPE_BMETHOD:
return rb_proc_get_iseq(def->body.bmethod.proc, 0);
case VM_METHOD_TYPE_ALIAS:
- return method_def_iseq(def->body.alias.original_me->def);
+ return method_def_iseq(def->body.alias.original_me->def);
case VM_METHOD_TYPE_CFUNC:
case VM_METHOD_TYPE_ATTRSET:
case VM_METHOD_TYPE_IVAR:
@@ -2902,7 +2953,7 @@ method_def_iseq(const rb_method_definition_t *def)
case VM_METHOD_TYPE_OPTIMIZED:
case VM_METHOD_TYPE_MISSING:
case VM_METHOD_TYPE_REFINED:
- break;
+ break;
}
return NULL;
}
@@ -2921,12 +2972,12 @@ method_cref(VALUE method)
again:
switch (def->type) {
case VM_METHOD_TYPE_ISEQ:
- return def->body.iseq.cref;
+ return def->body.iseq.cref;
case VM_METHOD_TYPE_ALIAS:
- def = def->body.alias.original_me->def;
- goto again;
+ def = def->body.alias.original_me->def;
+ goto again;
default:
- return NULL;
+ return NULL;
}
}
@@ -2934,9 +2985,9 @@ static VALUE
method_def_location(const rb_method_definition_t *def)
{
if (def->type == VM_METHOD_TYPE_ATTRSET || def->type == VM_METHOD_TYPE_IVAR) {
- if (!def->body.attr.location)
- return Qnil;
- return rb_ary_dup(def->body.attr.location);
+ if (!def->body.attr.location)
+ return Qnil;
+ return rb_ary_dup(def->body.attr.location);
}
return iseq_location(method_def_iseq(def));
}
@@ -3107,33 +3158,37 @@ method_inspect(VALUE method)
}
if (data->me->def->type == VM_METHOD_TYPE_ALIAS) {
- defined_class = data->me->def->body.alias.original_me->owner;
+ defined_class = data->me->def->body.alias.original_me->owner;
}
else {
- defined_class = method_entry_defined_class(data->me);
+ defined_class = method_entry_defined_class(data->me);
}
if (RB_TYPE_P(defined_class, T_ICLASS)) {
- defined_class = RBASIC_CLASS(defined_class);
+ defined_class = RBASIC_CLASS(defined_class);
}
- if (FL_TEST(mklass, FL_SINGLETON)) {
- VALUE v = rb_ivar_get(mklass, attached);
-
- if (data->recv == Qundef) {
- rb_str_buf_append(str, rb_inspect(mklass));
- }
- else if (data->recv == v) {
- rb_str_buf_append(str, rb_inspect(v));
- sharp = ".";
- }
- else {
- rb_str_buf_append(str, rb_inspect(data->recv));
- rb_str_buf_cat2(str, "(");
- rb_str_buf_append(str, rb_inspect(v));
- rb_str_buf_cat2(str, ")");
- sharp = ".";
- }
+ if (data->recv == Qundef) {
+ // UnboundMethod
+ rb_str_buf_append(str, rb_inspect(defined_class));
+ }
+ else if (FL_TEST(mklass, FL_SINGLETON)) {
+ VALUE v = rb_ivar_get(mklass, attached);
+
+ if (UNDEF_P(data->recv)) {
+ rb_str_buf_append(str, rb_inspect(mklass));
+ }
+ else if (data->recv == v) {
+ rb_str_buf_append(str, rb_inspect(v));
+ sharp = ".";
+ }
+ else {
+ rb_str_buf_append(str, rb_inspect(data->recv));
+ rb_str_buf_cat2(str, "(");
+ rb_str_buf_append(str, rb_inspect(v));
+ rb_str_buf_cat2(str, ")");
+ sharp = ".";
+ }
}
else {
mklass = data->klass;
@@ -3145,16 +3200,16 @@ method_inspect(VALUE method)
} while (RB_TYPE_P(mklass, T_ICLASS));
}
}
- rb_str_buf_append(str, rb_inspect(mklass));
- if (defined_class != mklass) {
- rb_str_catf(str, "(% "PRIsVALUE")", defined_class);
- }
+ rb_str_buf_append(str, rb_inspect(mklass));
+ if (defined_class != mklass) {
+ rb_str_catf(str, "(% "PRIsVALUE")", defined_class);
+ }
}
rb_str_buf_cat2(str, sharp);
rb_str_append(str, rb_id2str(data->me->called_id));
if (data->me->called_id != data->me->def->original_id) {
- rb_str_catf(str, "(%"PRIsVALUE")",
- rb_id2str(data->me->def->original_id));
+ rb_str_catf(str, "(%"PRIsVALUE")",
+ rb_id2str(data->me->def->original_id));
}
if (data->me->def->type == VM_METHOD_TYPE_NOTIMPLEMENTED) {
rb_str_buf_cat2(str, " (not-implemented)");
@@ -3355,51 +3410,6 @@ method_super_method(VALUE method)
}
/*
- * call-seq:
- * meth.public? -> true or false
- *
- * Returns whether the method is public.
- */
-
-static VALUE
-method_public_p(VALUE method)
-{
- const struct METHOD *data;
- TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
- return RBOOL(data->visibility == METHOD_VISI_PUBLIC);
-}
-
-/*
- * call-seq:
- * meth.protected? -> true or false
- *
- * Returns whether the method is protected.
- */
-
-static VALUE
-method_protected_p(VALUE method)
-{
- const struct METHOD *data;
- TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
- return RBOOL(data->visibility == METHOD_VISI_PROTECTED);
-}
-
-/*
- * call-seq:
- * meth.private? -> true or false
- *
- * Returns whether the method is private.
- */
-
-static VALUE
-method_private_p(VALUE method)
-{
- const struct METHOD *data;
- TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
- return RBOOL(data->visibility == METHOD_VISI_PRIVATE);
-}
-
-/*
* call-seq:
* local_jump_error.exit_value -> obj
*
@@ -3438,7 +3448,7 @@ env_clone(const rb_env_t *env, const rb_cref_t *cref)
VM_ASSERT(VM_ENV_ESCAPED_P(env->ep));
if (cref == NULL) {
- cref = rb_vm_cref_new_toplevel();
+ cref = rb_vm_cref_new_toplevel();
}
new_body = ALLOC_N(VALUE, env->env_size);
@@ -3481,31 +3491,31 @@ proc_binding(VALUE self)
again:
switch (vm_block_type(block)) {
case block_type_iseq:
- iseq = block->as.captured.code.iseq;
- binding_self = block->as.captured.self;
- env = VM_ENV_ENVVAL_PTR(block->as.captured.ep);
- break;
+ iseq = block->as.captured.code.iseq;
+ binding_self = block->as.captured.self;
+ env = VM_ENV_ENVVAL_PTR(block->as.captured.ep);
+ break;
case block_type_proc:
- GetProcPtr(block->as.proc, proc);
- block = &proc->block;
- goto again;
+ GetProcPtr(block->as.proc, proc);
+ block = &proc->block;
+ goto again;
case block_type_ifunc:
- {
- const struct vm_ifunc *ifunc = block->as.captured.code.ifunc;
- if (IS_METHOD_PROC_IFUNC(ifunc)) {
- VALUE method = (VALUE)ifunc->data;
- VALUE name = rb_fstring_lit("<empty_iseq>");
- rb_iseq_t *empty;
- binding_self = method_receiver(method);
- iseq = rb_method_iseq(method);
- env = VM_ENV_ENVVAL_PTR(block->as.captured.ep);
- env = env_clone(env, method_cref(method));
- /* set empty iseq */
- empty = rb_iseq_new(NULL, name, name, Qnil, 0, ISEQ_TYPE_TOP);
- RB_OBJ_WRITE(env, &env->iseq, empty);
- break;
- }
- }
+ {
+ const struct vm_ifunc *ifunc = block->as.captured.code.ifunc;
+ if (IS_METHOD_PROC_IFUNC(ifunc)) {
+ VALUE method = (VALUE)ifunc->data;
+ VALUE name = rb_fstring_lit("<empty_iseq>");
+ rb_iseq_t *empty;
+ binding_self = method_receiver(method);
+ iseq = rb_method_iseq(method);
+ env = VM_ENV_ENVVAL_PTR(block->as.captured.ep);
+ env = env_clone(env, method_cref(method));
+ /* set empty iseq */
+ empty = rb_iseq_new(NULL, name, name, Qnil, 0, ISEQ_TYPE_TOP);
+ RB_OBJ_WRITE(env, &env->iseq, empty);
+ break;
+ }
+ }
/* FALLTHROUGH */
case block_type_symbol:
rb_raise(rb_eArgError, "Can't create Binding from C level Proc");
@@ -3520,14 +3530,14 @@ proc_binding(VALUE self)
RB_OBJ_WRITTEN(bindval, Qundef, VM_ENV_ENVVAL(env->ep));
if (iseq) {
- rb_iseq_check(iseq);
+ rb_iseq_check(iseq);
RB_OBJ_WRITE(bindval, &bind->pathobj, ISEQ_BODY(iseq)->location.pathobj);
- bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq));
+ bind->first_lineno = ISEQ_BODY(iseq)->location.first_lineno;
}
else {
- RB_OBJ_WRITE(bindval, &bind->pathobj,
- rb_iseq_pathobj_new(rb_fstring_lit("(binding)"), Qnil));
- bind->first_lineno = 1;
+ RB_OBJ_WRITE(bindval, &bind->pathobj,
+ rb_iseq_pathobj_new(rb_fstring_lit("(binding)"), Qnil));
+ bind->first_lineno = 1;
}
return bindval;
@@ -3565,10 +3575,10 @@ curry(RB_BLOCK_CALL_FUNC_ARGLIST(_, args))
if (RARRAY_LEN(passed) < FIX2INT(arity)) {
if (!NIL_P(blockarg)) {
- rb_warn("given block not used");
- }
- arity = make_curry_proc(proc, passed, arity);
- return arity;
+ rb_warn("given block not used");
+ }
+ arity = make_curry_proc(proc, passed, arity);
+ return arity;
}
else {
return rb_proc_call_with_block(proc, check_argc(RARRAY_LEN(passed)), RARRAY_CONST_PTR(passed), blockarg);
@@ -3628,13 +3638,13 @@ proc_curry(int argc, const VALUE *argv, VALUE self)
VALUE arity;
if (rb_check_arity(argc, 0, 1) == 0 || NIL_P(arity = argv[0])) {
- arity = INT2FIX(min_arity);
+ arity = INT2FIX(min_arity);
}
else {
- sarity = FIX2INT(arity);
- if (rb_proc_lambda_p(self)) {
- rb_check_arity(sarity, min_arity, max_arity);
- }
+ sarity = FIX2INT(arity);
+ if (rb_proc_lambda_p(self)) {
+ rb_check_arity(sarity, min_arity, max_arity);
+ }
}
return make_curry_proc(self, rb_ary_new(), arity);
@@ -4339,9 +4349,6 @@ Init_Proc(void)
rb_define_method(rb_cMethod, "source_location", rb_method_location, 0);
rb_define_method(rb_cMethod, "parameters", rb_method_parameters, 0);
rb_define_method(rb_cMethod, "super_method", method_super_method, 0);
- rb_define_method(rb_cMethod, "public?", method_public_p, 0);
- rb_define_method(rb_cMethod, "protected?", method_protected_p, 0);
- rb_define_method(rb_cMethod, "private?", method_private_p, 0);
rb_define_method(rb_mKernel, "method", rb_obj_method, 1);
rb_define_method(rb_mKernel, "public_method", rb_obj_public_method, 1);
rb_define_method(rb_mKernel, "singleton_method", rb_obj_singleton_method, 1);
@@ -4350,8 +4357,8 @@ Init_Proc(void)
rb_cUnboundMethod = rb_define_class("UnboundMethod", rb_cObject);
rb_undef_alloc_func(rb_cUnboundMethod);
rb_undef_method(CLASS_OF(rb_cUnboundMethod), "new");
- rb_define_method(rb_cUnboundMethod, "==", method_eq, 1);
- rb_define_method(rb_cUnboundMethod, "eql?", method_eq, 1);
+ rb_define_method(rb_cUnboundMethod, "==", unbound_method_eq, 1);
+ rb_define_method(rb_cUnboundMethod, "eql?", unbound_method_eq, 1);
rb_define_method(rb_cUnboundMethod, "hash", method_hash, 0);
rb_define_method(rb_cUnboundMethod, "clone", method_clone, 0);
rb_define_method(rb_cUnboundMethod, "arity", method_arity_m, 0);
@@ -4365,9 +4372,6 @@ Init_Proc(void)
rb_define_method(rb_cUnboundMethod, "source_location", rb_method_location, 0);
rb_define_method(rb_cUnboundMethod, "parameters", rb_method_parameters, 0);
rb_define_method(rb_cUnboundMethod, "super_method", method_super_method, 0);
- rb_define_method(rb_cUnboundMethod, "public?", method_public_p, 0);
- rb_define_method(rb_cUnboundMethod, "protected?", method_protected_p, 0);
- rb_define_method(rb_cUnboundMethod, "private?", method_private_p, 0);
/* Module#*_method */
rb_define_method(rb_cModule, "instance_method", rb_mod_instance_method, 1);
@@ -4378,7 +4382,7 @@ Init_Proc(void)
rb_define_method(rb_mKernel, "define_singleton_method", rb_obj_define_method, -1);
rb_define_private_method(rb_singleton_class(rb_vm_top_self()),
- "define_method", top_define_method, -1);
+ "define_method", top_define_method, -1);
}
/*
diff --git a/process.c b/process.c
index 57bfa808be..c317a4cc2c 100644
--- a/process.c
+++ b/process.c
@@ -760,31 +760,31 @@ static VALUE
pst_message_status(VALUE str, int status)
{
if (WIFSTOPPED(status)) {
- int stopsig = WSTOPSIG(status);
- const char *signame = ruby_signal_name(stopsig);
- if (signame) {
- rb_str_catf(str, " stopped SIG%s (signal %d)", signame, stopsig);
- }
- else {
- rb_str_catf(str, " stopped signal %d", stopsig);
- }
+ int stopsig = WSTOPSIG(status);
+ const char *signame = ruby_signal_name(stopsig);
+ if (signame) {
+ rb_str_catf(str, " stopped SIG%s (signal %d)", signame, stopsig);
+ }
+ else {
+ rb_str_catf(str, " stopped signal %d", stopsig);
+ }
}
if (WIFSIGNALED(status)) {
- int termsig = WTERMSIG(status);
- const char *signame = ruby_signal_name(termsig);
- if (signame) {
- rb_str_catf(str, " SIG%s (signal %d)", signame, termsig);
- }
- else {
- rb_str_catf(str, " signal %d", termsig);
- }
+ int termsig = WTERMSIG(status);
+ const char *signame = ruby_signal_name(termsig);
+ if (signame) {
+ rb_str_catf(str, " SIG%s (signal %d)", signame, termsig);
+ }
+ else {
+ rb_str_catf(str, " signal %d", termsig);
+ }
}
if (WIFEXITED(status)) {
- rb_str_catf(str, " exit %d", WEXITSTATUS(status));
+ rb_str_catf(str, " exit %d", WEXITSTATUS(status));
}
#ifdef WCOREDUMP
if (WCOREDUMP(status)) {
- rb_str_cat2(str, " (core dumped)");
+ rb_str_cat2(str, " (core dumped)");
}
#endif
return str;
@@ -939,7 +939,7 @@ pst_wstopsig(VALUE st)
int status = PST2INT(st);
if (WIFSTOPPED(status))
- return INT2NUM(WSTOPSIG(status));
+ return INT2NUM(WSTOPSIG(status));
return Qnil;
}
@@ -976,7 +976,7 @@ pst_wtermsig(VALUE st)
int status = PST2INT(st);
if (WIFSIGNALED(status))
- return INT2NUM(WTERMSIG(status));
+ return INT2NUM(WTERMSIG(status));
return Qnil;
}
@@ -1023,7 +1023,7 @@ pst_wexitstatus(VALUE st)
int status = PST2INT(st);
if (WIFEXITED(status))
- return INT2NUM(WEXITSTATUS(status));
+ return INT2NUM(WEXITSTATUS(status));
return Qnil;
}
@@ -1042,7 +1042,7 @@ pst_success_p(VALUE st)
int status = PST2INT(st);
if (!WIFEXITED(status))
- return Qnil;
+ return Qnil;
return RBOOL(WEXITSTATUS(status) == EXIT_SUCCESS);
}
@@ -1168,7 +1168,7 @@ rb_sigwait_fd_migrate(rb_vm_t *vm)
extern volatile unsigned int ruby_nocldwait; /* signal.c */
/* called by timer thread or thread which acquired sigwait_fd */
static void
-waitpid_each(struct ccan_list_head *head)
+waitpid_each(rb_vm_t *vm, struct ccan_list_head *head)
{
struct waitpid_state *w = 0, *next;
@@ -1178,6 +1178,18 @@ waitpid_each(struct ccan_list_head *head)
if (!ret) continue;
if (ret == -1) w->errnum = errno;
+ if (w->pid <= 0) {
+ /* when waiting for a group of processes, make sure a waiter for a
+ * specific pid is given that event in preference */
+ struct waitpid_state *w_inner = 0, *next_inner;
+ ccan_list_for_each_safe(&vm->waiting_pids, w_inner, next_inner, wnode) {
+ if (w_inner->pid == ret) {
+ /* signal this one instead */
+ w = w_inner;
+ }
+ }
+ }
+
w->ret = ret;
ccan_list_del_init(&w->wnode);
waitpid_signal(w);
@@ -1192,10 +1204,8 @@ ruby_waitpid_all(rb_vm_t *vm)
{
#if RUBY_SIGCHLD
rb_native_mutex_lock(&vm->waitpid_lock);
- waitpid_each(&vm->waiting_pids);
- if (ccan_list_empty(&vm->waiting_pids)) {
- waitpid_each(&vm->waiting_grps);
- }
+ waitpid_each(vm, &vm->waiting_pids);
+ waitpid_each(vm, &vm->waiting_grps);
/* emulate SA_NOCLDWAIT */
if (ccan_list_empty(&vm->waiting_pids) && ccan_list_empty(&vm->waiting_grps)) {
while (ruby_nocldwait && do_waitpid(-1, 0, WNOHANG) > 0)
@@ -1260,6 +1270,7 @@ waitpid_cleanup(VALUE x)
return Qfalse;
}
+#if RUBY_SIGCHLD
static void
waitpid_wait(struct waitpid_state *w)
{
@@ -1273,10 +1284,17 @@ waitpid_wait(struct waitpid_state *w)
*/
rb_native_mutex_lock(&vm->waitpid_lock);
- if (w->pid > 0 || ccan_list_empty(&vm->waiting_pids)) {
- w->ret = do_waitpid(w->pid, &w->status, w->options | WNOHANG);
+ if (w->options & WNOHANG && w->pid <= 0) {
+ /* In the case of WNOHANG wait for a group, make sure there isn't a zombie child
+ * whose PID we are directly waiting for in another call to waitpid. If there is,
+ * we will reap it via a call to waitpid($pid) with this call to waitpid_each. */
+ waitpid_each(vm, &vm->waiting_pids);
+ /* _now_ it's safe to call do_waitpid, without risk of stealing the wait from
+ * another directed call. */
}
+ w->ret = do_waitpid(w->pid, &w->status, w->options | WNOHANG);
+
if (w->ret) {
if (w->ret == -1) w->errnum = errno;
}
@@ -1298,6 +1316,7 @@ waitpid_wait(struct waitpid_state *w)
rb_ensure(waitpid_sleep, (VALUE)w, waitpid_cleanup, (VALUE)w);
}
}
+#endif
static void *
waitpid_blocking_no_SIGCHLD(void *x)
@@ -1332,7 +1351,7 @@ rb_process_status_wait(rb_pid_t pid, int flags)
if (!(flags & WNOHANG)) {
VALUE scheduler = rb_fiber_scheduler_current();
VALUE result = rb_fiber_scheduler_process_wait(scheduler, pid, flags);
- if (result != Qundef) return result;
+ if (!UNDEF_P(result)) return result;
}
struct waitpid_state waitpid_state;
@@ -1340,12 +1359,11 @@ rb_process_status_wait(rb_pid_t pid, int flags)
waitpid_state_init(&waitpid_state, pid, flags);
waitpid_state.ec = GET_EC();
- if (WAITPID_USE_SIGCHLD) {
- waitpid_wait(&waitpid_state);
- }
- else {
- waitpid_no_SIGCHLD(&waitpid_state);
- }
+#if WAITPID_USE_SIGCHLD
+ waitpid_wait(&waitpid_state);
+#else
+ waitpid_no_SIGCHLD(&waitpid_state);
+#endif
if (waitpid_state.ret == 0) return Qnil;
@@ -1426,7 +1444,7 @@ rb_waitpid(rb_pid_t pid, int *st, int flags)
VALUE status = rb_process_status_wait(pid, flags);
if (NIL_P(status)) return 0;
- struct rb_process_status *data = RTYPEDDATA_DATA(status);
+ struct rb_process_status *data = rb_check_typeddata(status, &rb_process_status_type);
pid = data->pid;
if (st) *st = data->status;
@@ -1591,14 +1609,14 @@ proc_waitall(VALUE _)
rb_last_status_clear();
for (pid = -1;;) {
- pid = rb_waitpid(-1, &status, 0);
- if (pid == -1) {
- int e = errno;
- if (e == ECHILD)
- break;
- rb_syserr_fail(e, 0);
- }
- rb_ary_push(result, rb_assoc_new(PIDT2NUM(pid), rb_last_status_get()));
+ pid = rb_waitpid(-1, &status, 0);
+ if (pid == -1) {
+ int e = errno;
+ if (e == ECHILD)
+ break;
+ rb_syserr_fail(e, 0);
+ }
+ rb_ary_push(result, rb_assoc_new(PIDT2NUM(pid), rb_last_status_get()));
}
return result;
}
@@ -1618,7 +1636,7 @@ detach_process_watcher(void *arg)
int status;
while ((cpid = rb_waitpid(pid, &status, 0)) == 0) {
- /* wait while alive */
+ /* wait while alive */
}
return rb_last_status_get();
}
@@ -1776,7 +1794,6 @@ before_fork_ruby(void)
static void
after_fork_ruby(void)
{
- rb_threadptr_pending_interrupt_clear(GET_THREAD());
after_exec();
}
#endif
@@ -1813,7 +1830,7 @@ proc_exec_cmd(const char *prog, VALUE argv_str, VALUE envp_str)
argv = ARGVSTR2ARGV(argv_str);
if (!prog) {
- return ENOENT;
+ return ENOENT;
}
#ifdef _WIN32
@@ -1839,7 +1856,7 @@ proc_exec_sh(const char *str, VALUE envp_str)
s = str;
while (*s == ' ' || *s == '\t' || *s == '\n')
- s++;
+ s++;
if (!*s) {
return ENOENT;
@@ -1955,19 +1972,19 @@ proc_spawn_cmd_internal(char **argv, char *prog)
rb_pid_t status;
if (!prog)
- prog = argv[0];
+ prog = argv[0];
prog = dln_find_exe_r(prog, 0, fbuf, sizeof(fbuf));
if (!prog)
- return -1;
+ return -1;
before_exec();
status = spawnv(P_NOWAIT, prog, (const char **)argv);
if (status == -1 && errno == ENOEXEC) {
- *argv = (char *)prog;
- *--argv = (char *)"sh";
- status = spawnv(P_NOWAIT, "/bin/sh", (const char **)argv);
- after_exec();
- if (status == -1) errno = ENOEXEC;
+ *argv = (char *)prog;
+ *--argv = (char *)"sh";
+ status = spawnv(P_NOWAIT, "/bin/sh", (const char **)argv);
+ after_exec();
+ if (status == -1) errno = ENOEXEC;
}
return status;
}
@@ -1980,13 +1997,13 @@ proc_spawn_cmd(char **argv, VALUE prog, struct rb_execarg *eargp)
if (argv[0]) {
#if defined(_WIN32)
- DWORD flags = 0;
- if (eargp->new_pgroup_given && eargp->new_pgroup_flag) {
- flags = CREATE_NEW_PROCESS_GROUP;
- }
- pid = rb_w32_uaspawn_flags(P_NOWAIT, prog ? RSTRING_PTR(prog) : 0, argv, flags);
+ DWORD flags = 0;
+ if (eargp->new_pgroup_given && eargp->new_pgroup_flag) {
+ flags = CREATE_NEW_PROCESS_GROUP;
+ }
+ pid = rb_w32_uaspawn_flags(P_NOWAIT, prog ? RSTRING_PTR(prog) : 0, argv, flags);
#else
- pid = proc_spawn_cmd_internal(argv, prog ? RSTRING_PTR(prog) : 0);
+ pid = proc_spawn_cmd_internal(argv, prog ? RSTRING_PTR(prog) : 0);
#endif
}
return pid;
@@ -2156,18 +2173,18 @@ check_exec_redirect(VALUE key, VALUE val, struct rb_execarg *eargp)
if (FIXNUM_P(key) && (FIX2INT(key) == 1 || FIX2INT(key) == 2))
flags = INT2NUM(O_WRONLY|O_CREAT|O_TRUNC);
else if (RB_TYPE_P(key, T_ARRAY)) {
- int i;
- for (i = 0; i < RARRAY_LEN(key); i++) {
+ int i;
+ for (i = 0; i < RARRAY_LEN(key); i++) {
VALUE v = RARRAY_AREF(key, i);
- VALUE fd = check_exec_redirect_fd(v, 1);
- if (FIX2INT(fd) != 1 && FIX2INT(fd) != 2) break;
- }
- if (i == RARRAY_LEN(key))
- flags = INT2NUM(O_WRONLY|O_CREAT|O_TRUNC);
- else
- flags = INT2NUM(O_RDONLY);
- }
- else
+ VALUE fd = check_exec_redirect_fd(v, 1);
+ if (FIX2INT(fd) != 1 && FIX2INT(fd) != 2) break;
+ }
+ if (i == RARRAY_LEN(key))
+ flags = INT2NUM(O_WRONLY|O_CREAT|O_TRUNC);
+ else
+ flags = INT2NUM(O_RDONLY);
+ }
+ else
flags = INT2NUM(O_RDONLY);
perm = INT2FIX(0644);
param = hide_obj(rb_ary_new3(4, hide_obj(EXPORT_DUP(path)),
@@ -2176,9 +2193,9 @@ check_exec_redirect(VALUE key, VALUE val, struct rb_execarg *eargp)
break;
default:
- tmp = val;
- val = rb_io_check_io(tmp);
- if (!NIL_P(val)) goto io;
+ tmp = val;
+ val = rb_io_check_io(tmp);
+ if (!NIL_P(val)) goto io;
rb_raise(rb_eArgError, "wrong exec redirect action");
}
@@ -2193,23 +2210,23 @@ rb_execarg_addopt_rlimit(struct rb_execarg *eargp, int rtype, VALUE val)
VALUE ary = eargp->rlimit_limits;
VALUE tmp, softlim, hardlim;
if (eargp->rlimit_limits == Qfalse)
- ary = eargp->rlimit_limits = hide_obj(rb_ary_new());
+ ary = eargp->rlimit_limits = hide_obj(rb_ary_new());
else
- ary = eargp->rlimit_limits;
+ ary = eargp->rlimit_limits;
tmp = rb_check_array_type(val);
if (!NIL_P(tmp)) {
- if (RARRAY_LEN(tmp) == 1)
- softlim = hardlim = rb_to_int(rb_ary_entry(tmp, 0));
- else if (RARRAY_LEN(tmp) == 2) {
- softlim = rb_to_int(rb_ary_entry(tmp, 0));
- hardlim = rb_to_int(rb_ary_entry(tmp, 1));
- }
- else {
- rb_raise(rb_eArgError, "wrong exec rlimit option");
- }
+ if (RARRAY_LEN(tmp) == 1)
+ softlim = hardlim = rb_to_int(rb_ary_entry(tmp, 0));
+ else if (RARRAY_LEN(tmp) == 2) {
+ softlim = rb_to_int(rb_ary_entry(tmp, 0));
+ hardlim = rb_to_int(rb_ary_entry(tmp, 1));
+ }
+ else {
+ rb_raise(rb_eArgError, "wrong exec rlimit option");
+ }
}
else {
- softlim = hardlim = rb_to_int(val);
+ softlim = hardlim = rb_to_int(val);
}
tmp = hide_obj(rb_ary_new3(3, INT2NUM(rtype), softlim, hardlim));
rb_ary_push(ary, tmp);
@@ -2280,12 +2297,12 @@ rb_execarg_addopt(VALUE execarg_obj, VALUE key, VALUE val)
rb_raise(rb_eArgError, "chdir option specified twice");
}
FilePathValue(val);
- val = rb_str_encode_ospath(val);
+ val = rb_str_encode_ospath(val);
eargp->chdir_given = 1;
eargp->chdir_dir = hide_obj(EXPORT_DUP(val));
}
else if (id == id_umask) {
- mode_t cmask = NUM2MODET(val);
+ mode_t cmask = NUM2MODET(val);
if (eargp->umask_given) {
rb_raise(rb_eArgError, "umask option specified twice");
}
@@ -2311,36 +2328,36 @@ rb_execarg_addopt(VALUE execarg_obj, VALUE key, VALUE val)
key = INT2FIX(2);
goto redirect;
}
- else if (id == id_uid) {
+ else if (id == id_uid) {
#ifdef HAVE_SETUID
- if (eargp->uid_given) {
- rb_raise(rb_eArgError, "uid option specified twice");
- }
- check_uid_switch();
- {
- eargp->uid = OBJ2UID(val);
- eargp->uid_given = 1;
- }
+ if (eargp->uid_given) {
+ rb_raise(rb_eArgError, "uid option specified twice");
+ }
+ check_uid_switch();
+ {
+ eargp->uid = OBJ2UID(val);
+ eargp->uid_given = 1;
+ }
#else
- rb_raise(rb_eNotImpError,
- "uid option is unimplemented on this machine");
+ rb_raise(rb_eNotImpError,
+ "uid option is unimplemented on this machine");
#endif
- }
- else if (id == id_gid) {
+ }
+ else if (id == id_gid) {
#ifdef HAVE_SETGID
- if (eargp->gid_given) {
- rb_raise(rb_eArgError, "gid option specified twice");
- }
- check_gid_switch();
- {
- eargp->gid = OBJ2GID(val);
- eargp->gid_given = 1;
- }
+ if (eargp->gid_given) {
+ rb_raise(rb_eArgError, "gid option specified twice");
+ }
+ check_gid_switch();
+ {
+ eargp->gid = OBJ2GID(val);
+ eargp->gid_given = 1;
+ }
#else
- rb_raise(rb_eNotImpError,
- "gid option is unimplemented on this machine");
+ rb_raise(rb_eNotImpError,
+ "gid option is unimplemented on this machine");
#endif
- }
+ }
else if (id == id_exception) {
if (eargp->exception_given) {
rb_raise(rb_eArgError, "exception option specified twice");
@@ -2349,7 +2366,7 @@ rb_execarg_addopt(VALUE execarg_obj, VALUE key, VALUE val)
eargp->exception = TO_BOOL(val, "exception");
}
else {
- return ST_STOP;
+ return ST_STOP;
}
break;
@@ -2361,7 +2378,7 @@ redirect:
break;
default:
- return ST_STOP;
+ return ST_STOP;
}
RB_GC_GUARD(execarg_obj);
@@ -2375,10 +2392,10 @@ check_exec_options_i(st_data_t st_key, st_data_t st_val, st_data_t arg)
VALUE val = (VALUE)st_val;
VALUE execarg_obj = (VALUE)arg;
if (rb_execarg_addopt(execarg_obj, key, val) != ST_CONTINUE) {
- if (SYMBOL_P(key))
- rb_raise(rb_eArgError, "wrong exec option symbol: % "PRIsVALUE,
- key);
- rb_raise(rb_eArgError, "wrong exec option");
+ if (SYMBOL_P(key))
+ rb_raise(rb_eArgError, "wrong exec option symbol: % "PRIsVALUE,
+ key);
+ rb_raise(rb_eArgError, "wrong exec option");
}
return ST_CONTINUE;
}
@@ -2391,9 +2408,9 @@ check_exec_options_i_extract(st_data_t st_key, st_data_t st_val, st_data_t arg)
VALUE *args = (VALUE *)arg;
VALUE execarg_obj = args[0];
if (rb_execarg_addopt(execarg_obj, key, val) != ST_CONTINUE) {
- VALUE nonopts = args[1];
- if (NIL_P(nonopts)) args[1] = nonopts = rb_hash_new();
- rb_hash_aset(nonopts, key, val);
+ VALUE nonopts = args[1];
+ if (NIL_P(nonopts)) args[1] = nonopts = rb_hash_new();
+ rb_hash_aset(nonopts, key, val);
}
return ST_CONTINUE;
}
@@ -2521,7 +2538,7 @@ check_exec_env_i(st_data_t st_key, st_data_t st_val, st_data_t arg)
if (!NIL_P(val)) val = EXPORT_STR(val);
if (ENVMATCH(k, PATH_ENV)) {
- *path = val;
+ *path = val;
}
rb_ary_push(env, hide_obj(rb_assoc_new(key, val)));
@@ -2552,19 +2569,19 @@ rb_check_argv(int argc, VALUE *argv)
prog = 0;
tmp = rb_check_array_type(argv[0]);
if (!NIL_P(tmp)) {
- if (RARRAY_LEN(tmp) != 2) {
- rb_raise(rb_eArgError, "wrong first argument");
- }
- prog = RARRAY_AREF(tmp, 0);
- argv[0] = RARRAY_AREF(tmp, 1);
- SafeStringValue(prog);
- StringValueCStr(prog);
- prog = rb_str_new_frozen(prog);
+ if (RARRAY_LEN(tmp) != 2) {
+ rb_raise(rb_eArgError, "wrong first argument");
+ }
+ prog = RARRAY_AREF(tmp, 0);
+ argv[0] = RARRAY_AREF(tmp, 1);
+ SafeStringValue(prog);
+ StringValueCStr(prog);
+ prog = rb_str_new_frozen(prog);
}
for (i = 0; i < argc; i++) {
- SafeStringValue(argv[i]);
- argv[i] = rb_str_new_frozen(argv[i]);
- StringValueCStr(argv[i]);
+ SafeStringValue(argv[i]);
+ argv[i] = rb_str_new_frozen(argv[i]);
+ StringValueCStr(argv[i]);
}
return prog;
}
@@ -2576,7 +2593,7 @@ check_hash(VALUE obj)
switch (RB_BUILTIN_TYPE(obj)) {
case T_STRING:
case T_ARRAY:
- return Qnil;
+ return Qnil;
default:
break;
}
@@ -2656,39 +2673,39 @@ rb_exec_fillarg(VALUE prog, int argc, VALUE *argv, VALUE env, VALUE opthash, VAL
#ifndef _WIN32
if (eargp->use_shell) {
- static const char posix_sh_cmds[][9] = {
- "!", /* reserved */
- ".", /* special built-in */
- ":", /* special built-in */
- "break", /* special built-in */
- "case", /* reserved */
- "continue", /* special built-in */
- "do", /* reserved */
- "done", /* reserved */
- "elif", /* reserved */
- "else", /* reserved */
- "esac", /* reserved */
- "eval", /* special built-in */
- "exec", /* special built-in */
- "exit", /* special built-in */
- "export", /* special built-in */
- "fi", /* reserved */
- "for", /* reserved */
- "if", /* reserved */
- "in", /* reserved */
- "readonly", /* special built-in */
- "return", /* special built-in */
- "set", /* special built-in */
- "shift", /* special built-in */
- "then", /* reserved */
- "times", /* special built-in */
- "trap", /* special built-in */
- "unset", /* special built-in */
- "until", /* reserved */
- "while", /* reserved */
- };
- const char *p;
- struct string_part first = {0, 0};
+ static const char posix_sh_cmds[][9] = {
+ "!", /* reserved */
+ ".", /* special built-in */
+ ":", /* special built-in */
+ "break", /* special built-in */
+ "case", /* reserved */
+ "continue", /* special built-in */
+ "do", /* reserved */
+ "done", /* reserved */
+ "elif", /* reserved */
+ "else", /* reserved */
+ "esac", /* reserved */
+ "eval", /* special built-in */
+ "exec", /* special built-in */
+ "exit", /* special built-in */
+ "export", /* special built-in */
+ "fi", /* reserved */
+ "for", /* reserved */
+ "if", /* reserved */
+ "in", /* reserved */
+ "readonly", /* special built-in */
+ "return", /* special built-in */
+ "set", /* special built-in */
+ "shift", /* special built-in */
+ "then", /* reserved */
+ "times", /* special built-in */
+ "trap", /* special built-in */
+ "unset", /* special built-in */
+ "until", /* reserved */
+ "while", /* reserved */
+ };
+ const char *p;
+ struct string_part first = {0, 0};
int has_meta = 0;
/*
* meta characters:
@@ -2715,32 +2732,32 @@ rb_exec_fillarg(VALUE prog, int argc, VALUE *argv, VALUE env, VALUE opthash, VAL
* % (used in Parameter Expansion)
*/
for (p = RSTRING_PTR(prog); *p; p++) {
- if (*p == ' ' || *p == '\t') {
- if (first.ptr && !first.len) first.len = p - first.ptr;
- }
- else {
- if (!first.ptr) first.ptr = p;
- }
+ if (*p == ' ' || *p == '\t') {
+ if (first.ptr && !first.len) first.len = p - first.ptr;
+ }
+ else {
+ if (!first.ptr) first.ptr = p;
+ }
if (!has_meta && strchr("*?{}[]<>()~&|\\$;'`\"\n#", *p))
has_meta = 1;
- if (!first.len) {
- if (*p == '=') {
- has_meta = 1;
- }
- else if (*p == '/') {
- first.len = 0x100; /* longer than any posix_sh_cmds */
- }
- }
- if (has_meta)
+ if (!first.len) {
+ if (*p == '=') {
+ has_meta = 1;
+ }
+ else if (*p == '/') {
+ first.len = 0x100; /* longer than any posix_sh_cmds */
+ }
+ }
+ if (has_meta)
break;
}
- if (!has_meta && first.ptr) {
- if (!first.len) first.len = p - first.ptr;
- if (first.len > 0 && first.len <= sizeof(posix_sh_cmds[0]) &&
- bsearch(&first, posix_sh_cmds, numberof(posix_sh_cmds), sizeof(posix_sh_cmds[0]), compare_posix_sh))
- has_meta = 1;
- }
- if (!has_meta) {
+ if (!has_meta && first.ptr) {
+ if (!first.len) first.len = p - first.ptr;
+ if (first.len > 0 && first.len <= sizeof(posix_sh_cmds[0]) &&
+ bsearch(&first, posix_sh_cmds, numberof(posix_sh_cmds), sizeof(posix_sh_cmds[0]), compare_posix_sh))
+ has_meta = 1;
+ }
+ if (!has_meta) {
/* avoid shell since no shell meta character found. */
eargp->use_shell = 0;
}
@@ -2752,7 +2769,7 @@ rb_exec_fillarg(VALUE prog, int argc, VALUE *argv, VALUE env, VALUE opthash, VAL
while (*p == ' ' || *p == '\t')
p++;
if (*p) {
- const char *w = p;
+ const char *w = p;
while (*p && *p != ' ' && *p != '\t')
p++;
rb_str_buf_cat(argv_buf, w, p-w);
@@ -2768,15 +2785,15 @@ rb_exec_fillarg(VALUE prog, int argc, VALUE *argv, VALUE env, VALUE opthash, VAL
#endif
if (!eargp->use_shell) {
- const char *abspath;
- const char *path_env = 0;
- if (RTEST(eargp->path_env)) path_env = RSTRING_PTR(eargp->path_env);
- abspath = dln_find_exe_r(RSTRING_PTR(eargp->invoke.cmd.command_name),
- path_env, fbuf, sizeof(fbuf));
- if (abspath)
- eargp->invoke.cmd.command_abspath = rb_str_new_cstr(abspath);
- else
- eargp->invoke.cmd.command_abspath = Qnil;
+ const char *abspath;
+ const char *path_env = 0;
+ if (RTEST(eargp->path_env)) path_env = RSTRING_PTR(eargp->path_env);
+ abspath = dln_find_exe_r(RSTRING_PTR(eargp->invoke.cmd.command_name),
+ path_env, fbuf, sizeof(fbuf));
+ if (abspath)
+ eargp->invoke.cmd.command_abspath = rb_str_new_cstr(abspath);
+ else
+ eargp->invoke.cmd.command_abspath = Qnil;
}
if (!eargp->use_shell && !eargp->invoke.cmd.argv_buf) {
@@ -2785,13 +2802,13 @@ rb_exec_fillarg(VALUE prog, int argc, VALUE *argv, VALUE env, VALUE opthash, VAL
argv_buf = rb_str_buf_new(0);
hide_obj(argv_buf);
for (i = 0; i < argc; i++) {
- VALUE arg = argv[i];
- const char *s = StringValueCStr(arg);
+ VALUE arg = argv[i];
+ const char *s = StringValueCStr(arg);
#ifdef DEFAULT_PROCESS_ENCODING
- arg = EXPORT_STR(arg);
- s = RSTRING_PTR(arg);
+ arg = EXPORT_STR(arg);
+ s = RSTRING_PTR(arg);
#endif
- rb_str_buf_cat(argv_buf, s, RSTRING_LEN(arg) + 1); /* include '\0' */
+ rb_str_buf_cat(argv_buf, s, RSTRING_LEN(arg) + 1); /* include '\0' */
}
eargp->invoke.cmd.argv_buf = argv_buf;
}
@@ -2972,20 +2989,20 @@ rb_execarg_parent_start1(VALUE execarg_obj)
}
hide_obj(envtbl);
if (envopts != Qfalse) {
- st_table *stenv = RHASH_TBL_RAW(envtbl);
+ st_table *stenv = RHASH_TBL_RAW(envtbl);
long i;
for (i = 0; i < RARRAY_LEN(envopts); i++) {
VALUE pair = RARRAY_AREF(envopts, i);
VALUE key = RARRAY_AREF(pair, 0);
VALUE val = RARRAY_AREF(pair, 1);
if (NIL_P(val)) {
- st_data_t stkey = (st_data_t)key;
- st_delete(stenv, &stkey, NULL);
+ st_data_t stkey = (st_data_t)key;
+ st_delete(stenv, &stkey, NULL);
}
else {
- st_insert(stenv, (st_data_t)key, (st_data_t)val);
- RB_OBJ_WRITTEN(envtbl, Qundef, key);
- RB_OBJ_WRITTEN(envtbl, Qundef, val);
+ st_insert(stenv, (st_data_t)key, (st_data_t)val);
+ RB_OBJ_WRITTEN(envtbl, Qundef, key);
+ RB_OBJ_WRITTEN(envtbl, Qundef, val);
}
}
}
@@ -3070,7 +3087,7 @@ rb_exec_fail(struct rb_execarg *eargp, int err, const char *errmsg)
{
if (!errmsg || !*errmsg) return;
if (strcmp(errmsg, "chdir") == 0) {
- rb_sys_fail_str(eargp->chdir_dir);
+ rb_sys_fail_str(eargp->chdir_dir);
}
rb_sys_fail(errmsg);
}
@@ -3144,9 +3161,10 @@ NORETURN(static VALUE f_exec(int c, const VALUE *a, VALUE _));
* If the string from the first form (<code>exec("command")</code>) follows
* these simple rules:
*
- * * no meta characters
- * * not starting with shell reserved word or special built-in
- * * Ruby invokes the command directly without shell
+ * * no meta characters,
+ * * not starting with shell reserved word or special built-in,
+ *
+ * Ruby invokes the command directly without shell.
*
* You can force shell invocation by adding ";" to the string (because ";" is
* a meta character).
@@ -3226,10 +3244,10 @@ save_redirect_fd(int fd, struct rb_execarg *sargp, char *errmsg, size_t errmsg_b
newary = hide_obj(rb_ary_new());
sargp->fd_dup2 = newary;
}
- cloexec = fd_get_cloexec(fd, errmsg, errmsg_buflen);
- redirection = hide_obj(rb_assoc_new(INT2FIX(fd), INT2FIX(save_fd)));
- if (cloexec) rb_ary_push(redirection, Qtrue);
- rb_ary_push(newary, redirection);
+ cloexec = fd_get_cloexec(fd, errmsg, errmsg_buflen);
+ redirection = hide_obj(rb_assoc_new(INT2FIX(fd), INT2FIX(save_fd)));
+ if (cloexec) rb_ary_push(redirection, Qtrue);
+ rb_ary_push(newary, redirection);
newary = sargp->fd_close;
if (newary == Qfalse) {
@@ -3386,10 +3404,10 @@ run_exec_dup2(VALUE ary, VALUE tmpbuf, struct rb_execarg *sargp, char *errmsg, s
ERRMSG("dup2");
goto fail;
}
- if (pairs[j].cloexec &&
- fd_set_cloexec(pairs[j].newfd, errmsg, errmsg_buflen)) {
- goto fail;
- }
+ if (pairs[j].cloexec &&
+ fd_set_cloexec(pairs[j].newfd, errmsg, errmsg_buflen)) {
+ goto fail;
+ }
rb_update_max_fd(pairs[j].newfd); /* async-signal-safe but don't need to call it in a child process. */
pairs[j].oldfd = -1;
j = pairs[j].older_index;
@@ -3697,18 +3715,18 @@ rb_execarg_run_options(const struct rb_execarg *eargp, struct rb_execarg *sargp,
#ifdef HAVE_SETGID
if (eargp->gid_given) {
- if (setgid(eargp->gid) < 0) {
- ERRMSG("setgid");
- return -1;
- }
+ if (setgid(eargp->gid) < 0) {
+ ERRMSG("setgid");
+ return -1;
+ }
}
#endif
#ifdef HAVE_SETUID
if (eargp->uid_given) {
- if (setuid(eargp->uid) < 0) {
- ERRMSG("setuid");
- return -1;
- }
+ if (setuid(eargp->uid) < 0) {
+ ERRMSG("setuid");
+ return -1;
+ }
}
#endif
@@ -3746,17 +3764,17 @@ exec_async_signal_safe(const struct rb_execarg *eargp, char *errmsg, size_t errm
int err;
if (rb_execarg_run_options(eargp, sargp, errmsg, errmsg_buflen) < 0) { /* hopefully async-signal-safe */
- return errno;
+ return errno;
}
if (eargp->use_shell) {
- err = proc_exec_sh(RSTRING_PTR(eargp->invoke.sh.shell_script), eargp->envp_str); /* async-signal-safe */
+ err = proc_exec_sh(RSTRING_PTR(eargp->invoke.sh.shell_script), eargp->envp_str); /* async-signal-safe */
}
else {
- char *abspath = NULL;
- if (!NIL_P(eargp->invoke.cmd.command_abspath))
- abspath = RSTRING_PTR(eargp->invoke.cmd.command_abspath);
- err = proc_exec_cmd(abspath, eargp->invoke.cmd.argv_str, eargp->envp_str); /* async-signal-safe */
+ char *abspath = NULL;
+ if (!NIL_P(eargp->invoke.cmd.command_abspath))
+ abspath = RSTRING_PTR(eargp->invoke.cmd.command_abspath);
+ err = proc_exec_cmd(abspath, eargp->invoke.cmd.argv_str, eargp->envp_str); /* async-signal-safe */
}
#if !defined(HAVE_WORKING_FORK)
rb_execarg_run_options(sargp, NULL, errmsg, errmsg_buflen);
@@ -3773,16 +3791,12 @@ rb_exec_atfork(void* arg, char *errmsg, size_t errmsg_buflen)
return rb_exec_async_signal_safe(arg, errmsg, errmsg_buflen); /* hopefully async-signal-safe */
}
-#if SIZEOF_INT == SIZEOF_LONG
-#define proc_syswait (VALUE (*)(VALUE))rb_syswait
-#else
static VALUE
proc_syswait(VALUE pid)
{
- rb_syswait((int)pid);
+ rb_syswait((rb_pid_t)pid);
return Qnil;
}
-#endif
static int
move_fds_to_avoid_crash(int *fdp, int n, VALUE fds)
@@ -3865,18 +3879,18 @@ handle_fork_error(int err, struct rb_process_status *status, int *ep, volatile i
break;
}
if (ep) {
- close(ep[0]);
- close(ep[1]);
- errno = err;
+ close(ep[0]);
+ close(ep[1]);
+ errno = err;
}
if (state && !status) rb_jump_tag(state);
return -1;
}
#define prefork() ( \
- rb_io_flush(rb_stdout), \
- rb_io_flush(rb_stderr) \
- )
+ rb_io_flush(rb_stdout), \
+ rb_io_flush(rb_stderr) \
+ )
/*
* Forks child process, and returns the process ID in the parent
@@ -3910,7 +3924,7 @@ write_retry(int fd, const void *buf, size_t len)
ssize_t w;
do {
- w = write(fd, buf, len);
+ w = write(fd, buf, len);
} while (w < 0 && errno == EINTR);
return w;
@@ -3928,7 +3942,7 @@ read_retry(int fd, void *buf, size_t len)
}
do {
- r = read(fd, buf, len);
+ r = read(fd, buf, len);
} while (r < 0 && errno == EINTR);
return r;
@@ -3981,7 +3995,7 @@ getresuid(rb_uid_t *ruid, rb_uid_t *euid, rb_uid_t *suid)
*euid = geteuid();
ret = getuidx(ID_SAVED);
if (ret == (rb_uid_t)-1)
- return -1;
+ return -1;
*suid = ret;
return 0;
}
@@ -3999,7 +4013,7 @@ getresgid(rb_gid_t *rgid, rb_gid_t *egid, rb_gid_t *sgid)
*egid = getegid();
ret = getgidx(ID_SAVED);
if (ret == (rb_gid_t)-1)
- return -1;
+ return -1;
*sgid = ret;
return 0;
}
@@ -4026,7 +4040,7 @@ has_privilege(void)
#if defined HAVE_ISSETUGID
if (issetugid())
- return 1;
+ return 1;
#endif
#ifdef HAVE_GETRESUID
@@ -4087,7 +4101,7 @@ disable_child_handler_before_fork(struct child_handler_disabler_state *old)
ret = pthread_sigmask(SIG_SETMASK, &all, &old->sigmask); /* not async-signal-safe */
if (ret != 0) {
- rb_syserr_fail(ret, "pthread_sigmask");
+ rb_syserr_fail(ret, "pthread_sigmask");
}
#else
# pragma GCC warning "pthread_sigmask on fork is not available. potentially dangerous"
@@ -4102,7 +4116,7 @@ disable_child_handler_fork_parent(struct child_handler_disabler_state *old)
ret = pthread_sigmask(SIG_SETMASK, &old->sigmask, NULL); /* not async-signal-safe */
if (ret != 0) {
- rb_syserr_fail(ret, "pthread_sigmask");
+ rb_syserr_fail(ret, "pthread_sigmask");
}
#else
# pragma GCC warning "pthread_sigmask on fork is not available. potentially dangerous"
@@ -4117,24 +4131,24 @@ disable_child_handler_fork_child(struct child_handler_disabler_state *old, char
int ret;
for (sig = 1; sig < NSIG; sig++) {
- sig_t handler = signal(sig, SIG_DFL);
-
- if (handler == SIG_ERR && errno == EINVAL) {
- continue; /* Ignore invalid signal number */
- }
- if (handler == SIG_ERR) {
- ERRMSG("signal to obtain old action");
- return -1;
- }
+ sig_t handler = signal(sig, SIG_DFL);
+
+ if (handler == SIG_ERR && errno == EINVAL) {
+ continue; /* Ignore invalid signal number */
+ }
+ if (handler == SIG_ERR) {
+ ERRMSG("signal to obtain old action");
+ return -1;
+ }
#ifdef SIGPIPE
- if (sig == SIGPIPE) {
- continue;
- }
+ if (sig == SIGPIPE) {
+ continue;
+ }
#endif
- /* it will be reset to SIG_DFL at execve time, instead */
- if (handler == SIG_IGN) {
- signal(sig, SIG_IGN);
- }
+ /* it will be reset to SIG_DFL at execve time, instead */
+ if (handler == SIG_IGN) {
+ signal(sig, SIG_IGN);
+ }
}
/* non-Ruby child process, ensure cmake can see SIGCHLD */
@@ -4199,15 +4213,39 @@ retry_fork_async_signal_safe(struct rb_process_status *status, int *ep,
}
rb_native_mutex_unlock(waitpid_lock);
}
- disable_child_handler_fork_parent(&old);
+ disable_child_handler_fork_parent(&old);
if (0 < pid) /* fork succeed, parent process */
return pid;
/* fork failed */
- if (handle_fork_error(err, status, ep, &try_gc))
+ if (handle_fork_error(err, status, ep, &try_gc))
return -1;
}
}
+#if USE_MJIT
+// This is used to create MJIT's child Ruby process
+pid_t
+rb_mjit_fork(void)
+{
+ struct child_handler_disabler_state old;
+ rb_vm_t *vm = GET_VM();
+ prefork();
+ disable_child_handler_before_fork(&old);
+ before_fork_ruby();
+
+ rb_native_mutex_lock(&vm->waitpid_lock);
+ pid_t pid = rb_fork();
+ if (pid > 0) mjit_add_waiting_pid(vm, pid);
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+
+ after_fork_ruby();
+ disable_child_handler_fork_parent(&old);
+ if (pid == 0) rb_thread_atfork();
+
+ return pid;
+}
+#endif
+
static rb_pid_t
fork_check_err(struct rb_process_status *status, int (*chfunc)(void*, char *, size_t), void *charg,
VALUE fds, char *errmsg, size_t errmsg_buflen,
@@ -4332,12 +4370,30 @@ rb_fork_ruby(int *status)
return pid;
}
+static rb_pid_t
+proc_fork_pid(void)
+{
+ rb_pid_t pid = rb_fork_ruby(NULL);
+
+ if (pid == -1) {
+ rb_sys_fail("fork(2)");
+ }
+
+ return pid;
+}
+
rb_pid_t
rb_call_proc__fork(void)
{
- VALUE pid = rb_funcall(rb_mProcess, rb_intern("_fork"), 0);
-
- return NUM2PIDT(pid);
+ ID id__fork;
+ CONST_ID(id__fork, "_fork");
+ if (rb_method_basic_definition_p(CLASS_OF(rb_mProcess), id__fork)) {
+ return proc_fork_pid();
+ }
+ else {
+ VALUE pid = rb_funcall(rb_mProcess, id__fork, 0);
+ return NUM2PIDT(pid);
+ }
}
#endif
@@ -4353,16 +4409,18 @@ rb_call_proc__fork(void)
* This method is not for casual code but for application monitoring
* libraries. You can add custom code before and after fork events
* by overriding this method.
+ *
+ * Note: Process.daemon may be implemented using fork(2) BUT does not go
+ * through this method.
+ * Thus, depending on your reason to hook into this method, you
+ * may also want to hook into that one.
+ * See {this issue}[https://bugs.ruby-lang.org/issues/18911] for a
+ * more detailed discussion of this.
*/
VALUE
rb_proc__fork(VALUE _obj)
{
- rb_pid_t pid = rb_fork_ruby(NULL);
-
- if (pid == -1) {
- rb_sys_fail("fork(2)");
- }
-
+ rb_pid_t pid = proc_fork_pid();
return PIDT2NUM(pid);
}
@@ -4399,12 +4457,12 @@ rb_f_fork(VALUE obj)
pid = rb_call_proc__fork();
if (pid == 0) {
- if (rb_block_given_p()) {
- int status;
- rb_protect(rb_yield, Qundef, &status);
- ruby_stop(status);
- }
- return Qnil;
+ if (rb_block_given_p()) {
+ int status;
+ rb_protect(rb_yield, Qundef, &status);
+ ruby_stop(status);
+ }
+ return Qnil;
}
return PIDT2NUM(pid);
@@ -4421,18 +4479,18 @@ exit_status_code(VALUE status)
switch (status) {
case Qtrue:
- istatus = EXIT_SUCCESS;
- break;
+ istatus = EXIT_SUCCESS;
+ break;
case Qfalse:
- istatus = EXIT_FAILURE;
- break;
+ istatus = EXIT_FAILURE;
+ break;
default:
- istatus = NUM2INT(status);
+ istatus = NUM2INT(status);
#if EXIT_SUCCESS != 0
- if (istatus == 0)
- istatus = EXIT_SUCCESS;
+ if (istatus == 0)
+ istatus = EXIT_SUCCESS;
#endif
- break;
+ break;
}
return istatus;
}
@@ -4455,10 +4513,10 @@ rb_f_exit_bang(int argc, VALUE *argv, VALUE obj)
int istatus;
if (rb_check_arity(argc, 0, 1) == 1) {
- istatus = exit_status_code(argv[0]);
+ istatus = exit_status_code(argv[0]);
}
else {
- istatus = EXIT_FAILURE;
+ istatus = EXIT_FAILURE;
}
_exit(istatus);
@@ -4469,11 +4527,11 @@ void
rb_exit(int status)
{
if (GET_EC()->tag) {
- VALUE args[2];
+ VALUE args[2];
- args[0] = INT2NUM(status);
- args[1] = rb_str_new2("exit");
- rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit));
+ args[0] = INT2NUM(status);
+ args[1] = rb_str_new2("exit");
+ rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit));
}
ruby_stop(status);
}
@@ -4548,21 +4606,21 @@ rb_f_abort(int argc, const VALUE *argv)
{
rb_check_arity(argc, 0, 1);
if (argc == 0) {
- rb_execution_context_t *ec = GET_EC();
+ rb_execution_context_t *ec = GET_EC();
VALUE errinfo = rb_ec_get_errinfo(ec);
- if (!NIL_P(errinfo)) {
- rb_ec_error_print(ec, errinfo);
- }
- rb_exit(EXIT_FAILURE);
+ if (!NIL_P(errinfo)) {
+ rb_ec_error_print(ec, errinfo);
+ }
+ rb_exit(EXIT_FAILURE);
}
else {
- VALUE args[2];
+ VALUE args[2];
- args[1] = args[0] = argv[0];
- StringValue(args[0]);
- rb_io_puts(1, args, rb_ractor_stderr());
- args[0] = INT2NUM(EXIT_FAILURE);
- rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit));
+ args[1] = args[0] = argv[0];
+ StringValue(args[0]);
+ rb_io_puts(1, args, rb_ractor_stderr());
+ args[0] = INT2NUM(EXIT_FAILURE);
+ rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit));
}
UNREACHABLE_RETURN(Qnil);
@@ -4602,18 +4660,18 @@ rb_execarg_commandline(const struct rb_execarg *eargp, VALUE *prog)
{
VALUE cmd = *prog;
if (eargp && !eargp->use_shell) {
- VALUE str = eargp->invoke.cmd.argv_str;
- VALUE buf = eargp->invoke.cmd.argv_buf;
- char *p, **argv = ARGVSTR2ARGV(str);
- long i, argc = ARGVSTR2ARGC(str);
- const char *start = RSTRING_PTR(buf);
- cmd = rb_str_new(start, RSTRING_LEN(buf));
- p = RSTRING_PTR(cmd);
- for (i = 1; i < argc; ++i) {
- p[argv[i] - start - 1] = ' ';
- }
- *prog = cmd;
- return p;
+ VALUE str = eargp->invoke.cmd.argv_str;
+ VALUE buf = eargp->invoke.cmd.argv_buf;
+ char *p, **argv = ARGVSTR2ARGV(str);
+ long i, argc = ARGVSTR2ARGC(str);
+ const char *start = RSTRING_PTR(buf);
+ cmd = rb_str_new(start, RSTRING_LEN(buf));
+ p = RSTRING_PTR(cmd);
+ for (i = 1; i < argc; ++i) {
+ p[argv[i] - start - 1] = ' ';
+ }
+ *prog = cmd;
+ return p;
}
return StringValueCStr(*prog);
}
@@ -4686,7 +4744,7 @@ do_spawn_process(VALUE arg)
struct spawn_args *argp = (struct spawn_args *)arg;
rb_execarg_parent_start1(argp->execarg);
return (VALUE)rb_spawn_process(DATA_PTR(argp->execarg),
- argp->errmsg.ptr, argp->errmsg.buflen);
+ argp->errmsg.ptr, argp->errmsg.buflen);
}
static rb_pid_t
@@ -4707,7 +4765,7 @@ rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen)
args.errmsg.ptr = errmsg;
args.errmsg.buflen = errmsg_buflen;
return (rb_pid_t)rb_ensure(do_spawn_process, (VALUE)&args,
- execarg_parent_end, execarg_obj);
+ execarg_parent_end, execarg_obj);
}
static rb_pid_t
@@ -4802,7 +4860,8 @@ rb_f_system(int argc, VALUE *argv, VALUE _)
if (pid > 0) {
VALUE status = rb_process_status_wait(pid, 0);
- struct rb_process_status *data = RTYPEDDATA_DATA(status);
+
+ struct rb_process_status *data = rb_check_typeddata(status, &rb_process_status_type);
// Set the last status:
rb_obj_freeze(status);
@@ -5131,10 +5190,10 @@ rb_f_spawn(int argc, VALUE *argv, VALUE _)
pid = rb_execarg_spawn(execarg_obj, errmsg, sizeof(errmsg));
if (pid == -1) {
- int err = errno;
- rb_exec_fail(eargp, err, errmsg);
- RB_GC_GUARD(execarg_obj);
- rb_syserr_fail_str(err, fail_str);
+ int err = errno;
+ rb_exec_fail(eargp, err, errmsg);
+ RB_GC_GUARD(execarg_obj);
+ rb_syserr_fail_str(err, fail_str);
}
#if defined(HAVE_WORKING_FORK) || defined(HAVE_SPAWNV)
return PIDT2NUM(pid);
@@ -5315,7 +5374,7 @@ proc_getsid(int argc, VALUE *argv, VALUE _)
rb_pid_t pid = 0;
if (rb_check_arity(argc, 0, 1) == 1 && !NIL_P(argv[0]))
- pid = NUM2PIDT(argv[0]);
+ pid = NUM2PIDT(argv[0]);
sid = getsid(pid);
if (sid < 0) rb_sys_fail(0);
@@ -5373,8 +5432,8 @@ ruby_setsid(void)
if ((fd = rb_cloexec_open("/dev/tty", O_RDWR, 0)) >= 0) {
rb_update_max_fd(fd);
- ioctl(fd, TIOCNOTTY, NULL);
- close(fd);
+ ioctl(fd, TIOCNOTTY, NULL);
+ close(fd);
}
return pid;
}
@@ -5443,7 +5502,7 @@ proc_setpriority(VALUE obj, VALUE which, VALUE who, VALUE prio)
iprio = NUM2INT(prio);
if (setpriority(iwhich, iwho, iprio) < 0)
- rb_sys_fail(0);
+ rb_sys_fail(0);
return INT2FIX(0);
}
#else
@@ -5508,6 +5567,9 @@ rlimit_resource_name2int(const char *name, long len, int casetype)
#ifdef RLIMIT_NPROC
RESCHECK(NPROC);
#endif
+#ifdef RLIMIT_NPTS
+ RESCHECK(NPTS);
+#endif
#ifdef RLIMIT_NICE
RESCHECK(NICE);
#endif
@@ -5583,7 +5645,7 @@ rlimit_type_by_sym(VALUE key)
enum {prefix_len = sizeof(prefix)-1};
if (len > prefix_len && strncmp(prefix, rname, prefix_len) == 0) {
- rtype = rlimit_type_by_lname(rname + prefix_len, len - prefix_len);
+ rtype = rlimit_type_by_lname(rname + prefix_len, len - prefix_len);
}
RB_GC_GUARD(key);
@@ -5600,9 +5662,9 @@ rlimit_resource_type(VALUE rtype)
switch (TYPE(rtype)) {
case T_SYMBOL:
- v = rb_sym2str(rtype);
- name = RSTRING_PTR(v);
- len = RSTRING_LEN(v);
+ v = rb_sym2str(rtype);
+ name = RSTRING_PTR(v);
+ len = RSTRING_LEN(v);
break;
default:
@@ -5611,7 +5673,7 @@ rlimit_resource_type(VALUE rtype)
rtype = v;
case T_STRING:
name = StringValueCStr(rtype);
- len = RSTRING_LEN(rtype);
+ len = RSTRING_LEN(rtype);
break;
}
/* fall through */
@@ -5638,8 +5700,8 @@ rlimit_resource_value(VALUE rval)
switch (TYPE(rval)) {
case T_SYMBOL:
- v = rb_sym2str(rval);
- name = RSTRING_PTR(v);
+ v = rb_sym2str(rval);
+ name = RSTRING_PTR(v);
break;
default:
@@ -5699,7 +5761,7 @@ proc_getrlimit(VALUE obj, VALUE resource)
struct rlimit rlim;
if (getrlimit(rlimit_resource_type(resource), &rlim) < 0) {
- rb_sys_fail("getrlimit");
+ rb_sys_fail("getrlimit");
}
return rb_assoc_new(RLIM2NUM(rlim.rlim_cur), RLIM2NUM(rlim.rlim_max));
}
@@ -5736,6 +5798,7 @@ proc_getrlimit(VALUE obj, VALUE resource)
* [NICE] ceiling on process's nice(2) value (number) (GNU/Linux)
* [NOFILE] file descriptors (number) (SUSv3)
* [NPROC] number of processes for the user (number) (4.4BSD, GNU/Linux)
+ * [NPTS] number of pseudo terminals (number) (FreeBSD)
* [RSS] resident memory size (bytes) (4.2BSD, GNU/Linux)
* [RTPRIO] ceiling on the process's real-time priority (number) (GNU/Linux)
* [RTTIME] CPU time for real-time process (us) (GNU/Linux)
@@ -5775,7 +5838,7 @@ proc_setrlimit(int argc, VALUE *argv, VALUE obj)
rlim.rlim_max = rlimit_resource_value(rlim_max);
if (setrlimit(rlimit_resource_type(resource), &rlim) < 0) {
- rb_sys_fail("setrlimit");
+ rb_sys_fail("setrlimit");
}
return Qnil;
}
@@ -5788,7 +5851,7 @@ static void
check_uid_switch(void)
{
if (under_uid_switch) {
- rb_raise(rb_eRuntimeError, "can't handle UID while evaluating block given to Process::UID.switch method");
+ rb_raise(rb_eRuntimeError, "can't handle UID while evaluating block given to Process::UID.switch method");
}
}
@@ -5797,7 +5860,7 @@ static void
check_gid_switch(void)
{
if (under_gid_switch) {
- rb_raise(rb_eRuntimeError, "can't handle GID while evaluating block given to Process::UID.switch method");
+ rb_raise(rb_eRuntimeError, "can't handle GID while evaluating block given to Process::UID.switch method");
}
}
@@ -6056,7 +6119,7 @@ rb_getpwdiruid(void)
static rb_uid_t
obj2uid(VALUE id
# ifdef USE_GETPWNAM_R
- , VALUE *getpw_tmp
+ , VALUE *getpw_tmp
# endif
)
{
@@ -6064,46 +6127,46 @@ obj2uid(VALUE id
VALUE tmp;
if (FIXNUM_P(id) || NIL_P(tmp = rb_check_string_type(id))) {
- uid = NUM2UIDT(id);
+ uid = NUM2UIDT(id);
}
else {
- const char *usrname = StringValueCStr(id);
- struct passwd *pwptr;
+ const char *usrname = StringValueCStr(id);
+ struct passwd *pwptr;
#ifdef USE_GETPWNAM_R
- struct passwd pwbuf;
- char *getpw_buf;
- long getpw_buf_len;
- int e;
- if (!*getpw_tmp) {
- getpw_buf_len = GETPW_R_SIZE_INIT;
- if (getpw_buf_len < 0) getpw_buf_len = GETPW_R_SIZE_DEFAULT;
- *getpw_tmp = rb_str_tmp_new(getpw_buf_len);
- }
- getpw_buf = RSTRING_PTR(*getpw_tmp);
- getpw_buf_len = rb_str_capacity(*getpw_tmp);
- rb_str_set_len(*getpw_tmp, getpw_buf_len);
- errno = 0;
- while ((e = getpwnam_r(usrname, &pwbuf, getpw_buf, getpw_buf_len, &pwptr)) != 0) {
- if (e != ERANGE || getpw_buf_len >= GETPW_R_SIZE_LIMIT) {
- rb_str_resize(*getpw_tmp, 0);
- rb_syserr_fail(e, "getpwnam_r");
- }
- rb_str_modify_expand(*getpw_tmp, getpw_buf_len);
- getpw_buf = RSTRING_PTR(*getpw_tmp);
- getpw_buf_len = rb_str_capacity(*getpw_tmp);
- }
+ struct passwd pwbuf;
+ char *getpw_buf;
+ long getpw_buf_len;
+ int e;
+ if (!*getpw_tmp) {
+ getpw_buf_len = GETPW_R_SIZE_INIT;
+ if (getpw_buf_len < 0) getpw_buf_len = GETPW_R_SIZE_DEFAULT;
+ *getpw_tmp = rb_str_tmp_new(getpw_buf_len);
+ }
+ getpw_buf = RSTRING_PTR(*getpw_tmp);
+ getpw_buf_len = rb_str_capacity(*getpw_tmp);
+ rb_str_set_len(*getpw_tmp, getpw_buf_len);
+ errno = 0;
+ while ((e = getpwnam_r(usrname, &pwbuf, getpw_buf, getpw_buf_len, &pwptr)) != 0) {
+ if (e != ERANGE || getpw_buf_len >= GETPW_R_SIZE_LIMIT) {
+ rb_str_resize(*getpw_tmp, 0);
+ rb_syserr_fail(e, "getpwnam_r");
+ }
+ rb_str_modify_expand(*getpw_tmp, getpw_buf_len);
+ getpw_buf = RSTRING_PTR(*getpw_tmp);
+ getpw_buf_len = rb_str_capacity(*getpw_tmp);
+ }
#else
- pwptr = getpwnam(usrname);
+ pwptr = getpwnam(usrname);
#endif
- if (!pwptr) {
+ if (!pwptr) {
#ifndef USE_GETPWNAM_R
- endpwent();
+ endpwent();
#endif
rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, id);
- }
- uid = pwptr->pw_uid;
+ }
+ uid = pwptr->pw_uid;
#ifndef USE_GETPWNAM_R
- endpwent();
+ endpwent();
#endif
}
return uid;
@@ -6133,7 +6196,7 @@ p_uid_from_name(VALUE self, VALUE id)
static rb_gid_t
obj2gid(VALUE id
# ifdef USE_GETGRNAM_R
- , VALUE *getgr_tmp
+ , VALUE *getgr_tmp
# endif
)
{
@@ -6141,48 +6204,48 @@ obj2gid(VALUE id
VALUE tmp;
if (FIXNUM_P(id) || NIL_P(tmp = rb_check_string_type(id))) {
- gid = NUM2GIDT(id);
+ gid = NUM2GIDT(id);
}
else {
- const char *grpname = StringValueCStr(id);
- struct group *grptr;
+ const char *grpname = StringValueCStr(id);
+ struct group *grptr;
#ifdef USE_GETGRNAM_R
- struct group grbuf;
- char *getgr_buf;
- long getgr_buf_len;
- int e;
- if (!*getgr_tmp) {
- getgr_buf_len = GETGR_R_SIZE_INIT;
- if (getgr_buf_len < 0) getgr_buf_len = GETGR_R_SIZE_DEFAULT;
- *getgr_tmp = rb_str_tmp_new(getgr_buf_len);
- }
- getgr_buf = RSTRING_PTR(*getgr_tmp);
- getgr_buf_len = rb_str_capacity(*getgr_tmp);
- rb_str_set_len(*getgr_tmp, getgr_buf_len);
- errno = 0;
- while ((e = getgrnam_r(grpname, &grbuf, getgr_buf, getgr_buf_len, &grptr)) != 0) {
- if (e != ERANGE || getgr_buf_len >= GETGR_R_SIZE_LIMIT) {
- rb_str_resize(*getgr_tmp, 0);
- rb_syserr_fail(e, "getgrnam_r");
- }
- rb_str_modify_expand(*getgr_tmp, getgr_buf_len);
- getgr_buf = RSTRING_PTR(*getgr_tmp);
- getgr_buf_len = rb_str_capacity(*getgr_tmp);
- }
+ struct group grbuf;
+ char *getgr_buf;
+ long getgr_buf_len;
+ int e;
+ if (!*getgr_tmp) {
+ getgr_buf_len = GETGR_R_SIZE_INIT;
+ if (getgr_buf_len < 0) getgr_buf_len = GETGR_R_SIZE_DEFAULT;
+ *getgr_tmp = rb_str_tmp_new(getgr_buf_len);
+ }
+ getgr_buf = RSTRING_PTR(*getgr_tmp);
+ getgr_buf_len = rb_str_capacity(*getgr_tmp);
+ rb_str_set_len(*getgr_tmp, getgr_buf_len);
+ errno = 0;
+ while ((e = getgrnam_r(grpname, &grbuf, getgr_buf, getgr_buf_len, &grptr)) != 0) {
+ if (e != ERANGE || getgr_buf_len >= GETGR_R_SIZE_LIMIT) {
+ rb_str_resize(*getgr_tmp, 0);
+ rb_syserr_fail(e, "getgrnam_r");
+ }
+ rb_str_modify_expand(*getgr_tmp, getgr_buf_len);
+ getgr_buf = RSTRING_PTR(*getgr_tmp);
+ getgr_buf_len = rb_str_capacity(*getgr_tmp);
+ }
#elif defined(HAVE_GETGRNAM)
- grptr = getgrnam(grpname);
+ grptr = getgrnam(grpname);
#else
- grptr = NULL;
+ grptr = NULL;
#endif
- if (!grptr) {
+ if (!grptr) {
#if !defined(USE_GETGRNAM_R) && defined(HAVE_ENDGRENT)
- endgrent();
+ endgrent();
#endif
rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, id);
- }
- gid = grptr->gr_gid;
+ }
+ gid = grptr->gr_gid;
#if !defined(USE_GETGRNAM_R) && defined(HAVE_ENDGRENT)
- endgrent();
+ endgrent();
#endif
}
return gid;
@@ -6377,12 +6440,12 @@ proc_setuid(VALUE obj, VALUE id)
if (setruid(uid) < 0) rb_sys_fail(0);
#elif defined HAVE_SETUID
{
- if (geteuid() == uid) {
- if (setuid(uid) < 0) rb_sys_fail(0);
- }
- else {
- rb_notimplement();
- }
+ if (geteuid() == uid) {
+ if (setuid(uid) < 0) rb_sys_fail(0);
+ }
+ else {
+ rb_notimplement();
+ }
}
#endif
return id;
@@ -6409,11 +6472,11 @@ int
setreuid(rb_uid_t ruid, rb_uid_t euid)
{
if (ruid != (rb_uid_t)-1 && ruid != getuid()) {
- if (euid == (rb_uid_t)-1) euid = geteuid();
- if (setuid(ruid) < 0) return -1;
+ if (euid == (rb_uid_t)-1) euid = geteuid();
+ if (setuid(ruid) < 0) return -1;
}
if (euid != (rb_uid_t)-1 && euid != geteuid()) {
- if (seteuid(euid) < 0) return -1;
+ if (seteuid(euid) < 0) return -1;
}
return 0;
}
@@ -6443,144 +6506,144 @@ p_uid_change_privilege(VALUE obj, VALUE id)
if (geteuid() == 0) { /* root-user */
#if defined(HAVE_SETRESUID)
- if (setresuid(uid, uid, uid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
+ if (setresuid(uid, uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
#elif defined(HAVE_SETUID)
- if (setuid(uid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
+ if (setuid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
- if (getuid() == uid) {
- if (SAVED_USER_ID == uid) {
- if (setreuid(-1, uid) < 0) rb_sys_fail(0);
- }
- else {
- if (uid == 0) { /* (r,e,s) == (root, root, x) */
- if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
- if (setreuid(SAVED_USER_ID, 0) < 0) rb_sys_fail(0);
- SAVED_USER_ID = 0; /* (r,e,s) == (x, root, root) */
- if (setreuid(uid, uid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
- }
- else {
- if (setreuid(0, -1) < 0) rb_sys_fail(0);
- SAVED_USER_ID = 0;
- if (setreuid(uid, uid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
- }
- }
- }
- else {
- if (setreuid(uid, uid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
- }
+ if (getuid() == uid) {
+ if (SAVED_USER_ID == uid) {
+ if (setreuid(-1, uid) < 0) rb_sys_fail(0);
+ }
+ else {
+ if (uid == 0) { /* (r,e,s) == (root, root, x) */
+ if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
+ if (setreuid(SAVED_USER_ID, 0) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = 0; /* (r,e,s) == (x, root, root) */
+ if (setreuid(uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+ else {
+ if (setreuid(0, -1) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = 0;
+ if (setreuid(uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+ }
+ }
+ else {
+ if (setreuid(uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
- if (getuid() == uid) {
- if (SAVED_USER_ID == uid) {
- if (seteuid(uid) < 0) rb_sys_fail(0);
- }
- else {
- if (uid == 0) {
- if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
- SAVED_USER_ID = 0;
- if (setruid(0) < 0) rb_sys_fail(0);
- }
- else {
- if (setruid(0) < 0) rb_sys_fail(0);
- SAVED_USER_ID = 0;
- if (seteuid(uid) < 0) rb_sys_fail(0);
- if (setruid(uid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
- }
- }
- }
- else {
- if (seteuid(uid) < 0) rb_sys_fail(0);
- if (setruid(uid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
- }
+ if (getuid() == uid) {
+ if (SAVED_USER_ID == uid) {
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ }
+ else {
+ if (uid == 0) {
+ if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = 0;
+ if (setruid(0) < 0) rb_sys_fail(0);
+ }
+ else {
+ if (setruid(0) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = 0;
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+ }
+ }
+ else {
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
#else
- (void)uid;
- rb_notimplement();
+ (void)uid;
+ rb_notimplement();
#endif
}
else { /* unprivileged user */
#if defined(HAVE_SETRESUID)
- if (setresuid((getuid() == uid)? (rb_uid_t)-1: uid,
- (geteuid() == uid)? (rb_uid_t)-1: uid,
- (SAVED_USER_ID == uid)? (rb_uid_t)-1: uid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
+ if (setresuid((getuid() == uid)? (rb_uid_t)-1: uid,
+ (geteuid() == uid)? (rb_uid_t)-1: uid,
+ (SAVED_USER_ID == uid)? (rb_uid_t)-1: uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
- if (SAVED_USER_ID == uid) {
- if (setreuid((getuid() == uid)? (rb_uid_t)-1: uid,
- (geteuid() == uid)? (rb_uid_t)-1: uid) < 0)
- rb_sys_fail(0);
- }
- else if (getuid() != uid) {
- if (setreuid(uid, (geteuid() == uid)? (rb_uid_t)-1: uid) < 0)
- rb_sys_fail(0);
- SAVED_USER_ID = uid;
- }
- else if (/* getuid() == uid && */ geteuid() != uid) {
- if (setreuid(geteuid(), uid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
- if (setreuid(uid, -1) < 0) rb_sys_fail(0);
- }
- else { /* getuid() == uid && geteuid() == uid */
- if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
- if (setreuid(SAVED_USER_ID, uid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
- if (setreuid(uid, -1) < 0) rb_sys_fail(0);
- }
+ if (SAVED_USER_ID == uid) {
+ if (setreuid((getuid() == uid)? (rb_uid_t)-1: uid,
+ (geteuid() == uid)? (rb_uid_t)-1: uid) < 0)
+ rb_sys_fail(0);
+ }
+ else if (getuid() != uid) {
+ if (setreuid(uid, (geteuid() == uid)? (rb_uid_t)-1: uid) < 0)
+ rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+ else if (/* getuid() == uid && */ geteuid() != uid) {
+ if (setreuid(geteuid(), uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ if (setreuid(uid, -1) < 0) rb_sys_fail(0);
+ }
+ else { /* getuid() == uid && geteuid() == uid */
+ if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
+ if (setreuid(SAVED_USER_ID, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ if (setreuid(uid, -1) < 0) rb_sys_fail(0);
+ }
#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
- if (SAVED_USER_ID == uid) {
- if (geteuid() != uid && seteuid(uid) < 0) rb_sys_fail(0);
- if (getuid() != uid && setruid(uid) < 0) rb_sys_fail(0);
- }
- else if (/* SAVED_USER_ID != uid && */ geteuid() == uid) {
- if (getuid() != uid) {
- if (setruid(uid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
- }
- else {
- if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
- if (setruid(uid) < 0) rb_sys_fail(0);
- }
- }
- else if (/* geteuid() != uid && */ getuid() == uid) {
- if (seteuid(uid) < 0) rb_sys_fail(0);
- if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
- if (setruid(uid) < 0) rb_sys_fail(0);
- }
- else {
- rb_syserr_fail(EPERM, 0);
- }
+ if (SAVED_USER_ID == uid) {
+ if (geteuid() != uid && seteuid(uid) < 0) rb_sys_fail(0);
+ if (getuid() != uid && setruid(uid) < 0) rb_sys_fail(0);
+ }
+ else if (/* SAVED_USER_ID != uid && */ geteuid() == uid) {
+ if (getuid() != uid) {
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+ else {
+ if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ }
+ }
+ else if (/* geteuid() != uid && */ getuid() == uid) {
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ }
+ else {
+ rb_syserr_fail(EPERM, 0);
+ }
#elif defined HAVE_44BSD_SETUID
- if (getuid() == uid) {
- /* (r,e,s)==(uid,?,?) ==> (uid,uid,uid) */
- if (setuid(uid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = uid;
- }
- else {
- rb_syserr_fail(EPERM, 0);
- }
+ if (getuid() == uid) {
+ /* (r,e,s)==(uid,?,?) ==> (uid,uid,uid) */
+ if (setuid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+ else {
+ rb_syserr_fail(EPERM, 0);
+ }
#elif defined HAVE_SETEUID
- if (getuid() == uid && SAVED_USER_ID == uid) {
- if (seteuid(uid) < 0) rb_sys_fail(0);
- }
- else {
- rb_syserr_fail(EPERM, 0);
- }
+ if (getuid() == uid && SAVED_USER_ID == uid) {
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ }
+ else {
+ rb_syserr_fail(EPERM, 0);
+ }
#elif defined HAVE_SETUID
- if (getuid() == uid && SAVED_USER_ID == uid) {
- if (setuid(uid) < 0) rb_sys_fail(0);
- }
- else {
- rb_syserr_fail(EPERM, 0);
- }
+ if (getuid() == uid && SAVED_USER_ID == uid) {
+ if (setuid(uid) < 0) rb_sys_fail(0);
+ }
+ else {
+ rb_syserr_fail(EPERM, 0);
+ }
#else
- rb_notimplement();
+ rb_notimplement();
#endif
}
return id;
@@ -6774,12 +6837,12 @@ proc_setgid(VALUE obj, VALUE id)
if (setrgid(gid) < 0) rb_sys_fail(0);
#elif defined HAVE_SETGID
{
- if (getegid() == gid) {
- if (setgid(gid) < 0) rb_sys_fail(0);
- }
- else {
- rb_notimplement();
- }
+ if (getegid() == gid) {
+ if (setgid(gid) < 0) rb_sys_fail(0);
+ }
+ else {
+ rb_notimplement();
+ }
}
#endif
return GIDT2NUM(gid);
@@ -6825,9 +6888,9 @@ static int
maxgroups(void)
{
if (_maxgroups < 0) {
- _maxgroups = get_sc_ngroups_max();
- if (_maxgroups < 0)
- _maxgroups = RB_MAX_GROUPS;
+ _maxgroups = get_sc_ngroups_max();
+ if (_maxgroups < 0)
+ _maxgroups = RB_MAX_GROUPS;
}
return _maxgroups;
@@ -6871,17 +6934,17 @@ proc_getgroups(VALUE obj)
ngroups = getgroups(0, NULL);
if (ngroups == -1)
- rb_sys_fail(0);
+ rb_sys_fail(0);
groups = ALLOCV_N(rb_gid_t, tmp, ngroups);
ngroups = getgroups(ngroups, groups);
if (ngroups == -1)
- rb_sys_fail(0);
+ rb_sys_fail(0);
ary = rb_ary_new();
for (i = 0; i < ngroups; i++)
- rb_ary_push(ary, GIDT2NUM(groups[i]));
+ rb_ary_push(ary, GIDT2NUM(groups[i]));
ALLOCV_END(tmp);
@@ -6918,19 +6981,19 @@ proc_setgroups(VALUE obj, VALUE ary)
ngroups = RARRAY_LENINT(ary);
if (ngroups > maxgroups())
- rb_raise(rb_eArgError, "too many groups, %d max", maxgroups());
+ rb_raise(rb_eArgError, "too many groups, %d max", maxgroups());
groups = ALLOCV_N(rb_gid_t, tmp, ngroups);
for (i = 0; i < ngroups; i++) {
- VALUE g = RARRAY_AREF(ary, i);
+ VALUE g = RARRAY_AREF(ary, i);
- groups[i] = OBJ2GID1(g);
+ groups[i] = OBJ2GID1(g);
}
FINISH_GETGRNAM;
if (setgroups(ngroups, groups) == -1) /* ngroups <= maxgroups */
- rb_sys_fail(0);
+ rb_sys_fail(0);
ALLOCV_END(tmp);
@@ -6963,7 +7026,7 @@ static VALUE
proc_initgroups(VALUE obj, VALUE uname, VALUE base_grp)
{
if (initgroups(StringValueCStr(uname), OBJ2GID(base_grp)) != 0) {
- rb_sys_fail(0);
+ rb_sys_fail(0);
}
return proc_getgroups(obj);
}
@@ -7007,13 +7070,13 @@ proc_setmaxgroups(VALUE obj, VALUE val)
int ngroups_max = get_sc_ngroups_max();
if (ngroups <= 0)
- rb_raise(rb_eArgError, "maxgroups %d should be positive", ngroups);
+ rb_raise(rb_eArgError, "maxgroups %d should be positive", ngroups);
if (ngroups > RB_MAX_GROUPS)
- ngroups = RB_MAX_GROUPS;
+ ngroups = RB_MAX_GROUPS;
if (ngroups_max > 0 && ngroups > ngroups_max)
- ngroups = ngroups_max;
+ ngroups = ngroups_max;
_maxgroups = ngroups;
@@ -7069,30 +7132,25 @@ rb_daemon(int nochdir, int noclose)
#else
int n;
-#define fork_daemon() \
- switch (rb_fork_ruby(NULL)) { \
- case -1: return -1; \
- case 0: break; \
- default: _exit(EXIT_SUCCESS); \
+ switch (rb_fork_ruby(NULL)) {
+ case -1: return -1;
+ case 0: break;
+ default: _exit(EXIT_SUCCESS);
}
- fork_daemon();
-
- if (setsid() < 0) return -1;
-
- /* must not be process-leader */
- fork_daemon();
+ /* ignore EPERM which means already being process-leader */
+ if (setsid() < 0) (void)0;
if (!nochdir)
- err = chdir("/");
+ err = chdir("/");
if (!noclose && (n = rb_cloexec_open("/dev/null", O_RDWR, 0)) != -1) {
rb_update_max_fd(n);
- (void)dup2(n, 0);
- (void)dup2(n, 1);
- (void)dup2(n, 2);
- if (n > 2)
- (void)close (n);
+ (void)dup2(n, 0);
+ (void)dup2(n, 1);
+ (void)dup2(n, 2);
+ if (n > 2)
+ (void)close (n);
}
#endif
return err;
@@ -7118,11 +7176,11 @@ int
setregid(rb_gid_t rgid, rb_gid_t egid)
{
if (rgid != (rb_gid_t)-1 && rgid != getgid()) {
- if (egid == (rb_gid_t)-1) egid = getegid();
- if (setgid(rgid) < 0) return -1;
+ if (egid == (rb_gid_t)-1) egid = getegid();
+ if (setgid(rgid) < 0) return -1;
}
if (egid != (rb_gid_t)-1 && egid != getegid()) {
- if (setegid(egid) < 0) return -1;
+ if (setegid(egid) < 0) return -1;
}
return 0;
}
@@ -7152,145 +7210,145 @@ p_gid_change_privilege(VALUE obj, VALUE id)
if (geteuid() == 0) { /* root-user */
#if defined(HAVE_SETRESGID)
- if (setresgid(gid, gid, gid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
+ if (setresgid(gid, gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
#elif defined HAVE_SETGID
- if (setgid(gid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
+ if (setgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
- if (getgid() == gid) {
- if (SAVED_GROUP_ID == gid) {
- if (setregid(-1, gid) < 0) rb_sys_fail(0);
- }
- else {
- if (gid == 0) { /* (r,e,s) == (root, y, x) */
- if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
- if (setregid(SAVED_GROUP_ID, 0) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = 0; /* (r,e,s) == (x, root, root) */
- if (setregid(gid, gid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
- }
- else { /* (r,e,s) == (z, y, x) */
- if (setregid(0, 0) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = 0;
- if (setregid(gid, gid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
- }
- }
- }
- else {
- if (setregid(gid, gid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
- }
+ if (getgid() == gid) {
+ if (SAVED_GROUP_ID == gid) {
+ if (setregid(-1, gid) < 0) rb_sys_fail(0);
+ }
+ else {
+ if (gid == 0) { /* (r,e,s) == (root, y, x) */
+ if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ if (setregid(SAVED_GROUP_ID, 0) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = 0; /* (r,e,s) == (x, root, root) */
+ if (setregid(gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+ else { /* (r,e,s) == (z, y, x) */
+ if (setregid(0, 0) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = 0;
+ if (setregid(gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+ }
+ }
+ else {
+ if (setregid(gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
#elif defined(HAVE_SETRGID) && defined (HAVE_SETEGID)
- if (getgid() == gid) {
- if (SAVED_GROUP_ID == gid) {
- if (setegid(gid) < 0) rb_sys_fail(0);
- }
- else {
- if (gid == 0) {
- if (setegid(gid) < 0) rb_sys_fail(0);
- if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = 0;
- if (setrgid(0) < 0) rb_sys_fail(0);
- }
- else {
- if (setrgid(0) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = 0;
- if (setegid(gid) < 0) rb_sys_fail(0);
- if (setrgid(gid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
- }
- }
- }
- else {
- if (setegid(gid) < 0) rb_sys_fail(0);
- if (setrgid(gid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
- }
+ if (getgid() == gid) {
+ if (SAVED_GROUP_ID == gid) {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ }
+ else {
+ if (gid == 0) {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = 0;
+ if (setrgid(0) < 0) rb_sys_fail(0);
+ }
+ else {
+ if (setrgid(0) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = 0;
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+ }
+ }
+ else {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
#else
- rb_notimplement();
+ rb_notimplement();
#endif
}
else { /* unprivileged user */
#if defined(HAVE_SETRESGID)
- if (setresgid((getgid() == gid)? (rb_gid_t)-1: gid,
- (getegid() == gid)? (rb_gid_t)-1: gid,
- (SAVED_GROUP_ID == gid)? (rb_gid_t)-1: gid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
+ if (setresgid((getgid() == gid)? (rb_gid_t)-1: gid,
+ (getegid() == gid)? (rb_gid_t)-1: gid,
+ (SAVED_GROUP_ID == gid)? (rb_gid_t)-1: gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
- if (SAVED_GROUP_ID == gid) {
- if (setregid((getgid() == gid)? (rb_uid_t)-1: gid,
- (getegid() == gid)? (rb_uid_t)-1: gid) < 0)
- rb_sys_fail(0);
- }
- else if (getgid() != gid) {
- if (setregid(gid, (getegid() == gid)? (rb_uid_t)-1: gid) < 0)
- rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
- }
- else if (/* getgid() == gid && */ getegid() != gid) {
- if (setregid(getegid(), gid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
- if (setregid(gid, -1) < 0) rb_sys_fail(0);
- }
- else { /* getgid() == gid && getegid() == gid */
- if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
- if (setregid(SAVED_GROUP_ID, gid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
- if (setregid(gid, -1) < 0) rb_sys_fail(0);
- }
+ if (SAVED_GROUP_ID == gid) {
+ if (setregid((getgid() == gid)? (rb_uid_t)-1: gid,
+ (getegid() == gid)? (rb_uid_t)-1: gid) < 0)
+ rb_sys_fail(0);
+ }
+ else if (getgid() != gid) {
+ if (setregid(gid, (getegid() == gid)? (rb_uid_t)-1: gid) < 0)
+ rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+ else if (/* getgid() == gid && */ getegid() != gid) {
+ if (setregid(getegid(), gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ if (setregid(gid, -1) < 0) rb_sys_fail(0);
+ }
+ else { /* getgid() == gid && getegid() == gid */
+ if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ if (setregid(SAVED_GROUP_ID, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ if (setregid(gid, -1) < 0) rb_sys_fail(0);
+ }
#elif defined(HAVE_SETRGID) && defined(HAVE_SETEGID)
- if (SAVED_GROUP_ID == gid) {
- if (getegid() != gid && setegid(gid) < 0) rb_sys_fail(0);
- if (getgid() != gid && setrgid(gid) < 0) rb_sys_fail(0);
- }
- else if (/* SAVED_GROUP_ID != gid && */ getegid() == gid) {
- if (getgid() != gid) {
- if (setrgid(gid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
- }
- else {
- if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
- if (setrgid(gid) < 0) rb_sys_fail(0);
- }
- }
- else if (/* getegid() != gid && */ getgid() == gid) {
- if (setegid(gid) < 0) rb_sys_fail(0);
- if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
- if (setrgid(gid) < 0) rb_sys_fail(0);
- }
- else {
- rb_syserr_fail(EPERM, 0);
- }
+ if (SAVED_GROUP_ID == gid) {
+ if (getegid() != gid && setegid(gid) < 0) rb_sys_fail(0);
+ if (getgid() != gid && setrgid(gid) < 0) rb_sys_fail(0);
+ }
+ else if (/* SAVED_GROUP_ID != gid && */ getegid() == gid) {
+ if (getgid() != gid) {
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+ else {
+ if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ }
+ }
+ else if (/* getegid() != gid && */ getgid() == gid) {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ }
+ else {
+ rb_syserr_fail(EPERM, 0);
+ }
#elif defined HAVE_44BSD_SETGID
- if (getgid() == gid) {
- /* (r,e,s)==(gid,?,?) ==> (gid,gid,gid) */
- if (setgid(gid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = gid;
- }
- else {
- rb_syserr_fail(EPERM, 0);
- }
+ if (getgid() == gid) {
+ /* (r,e,s)==(gid,?,?) ==> (gid,gid,gid) */
+ if (setgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+ else {
+ rb_syserr_fail(EPERM, 0);
+ }
#elif defined HAVE_SETEGID
- if (getgid() == gid && SAVED_GROUP_ID == gid) {
- if (setegid(gid) < 0) rb_sys_fail(0);
- }
- else {
- rb_syserr_fail(EPERM, 0);
- }
+ if (getgid() == gid && SAVED_GROUP_ID == gid) {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ }
+ else {
+ rb_syserr_fail(EPERM, 0);
+ }
#elif defined HAVE_SETGID
- if (getgid() == gid && SAVED_GROUP_ID == gid) {
- if (setgid(gid) < 0) rb_sys_fail(0);
- }
- else {
- rb_syserr_fail(EPERM, 0);
- }
+ if (getgid() == gid && SAVED_GROUP_ID == gid) {
+ if (setgid(gid) < 0) rb_sys_fail(0);
+ }
+ else {
+ rb_syserr_fail(EPERM, 0);
+ }
#else
- (void)gid;
- rb_notimplement();
+ (void)gid;
+ rb_notimplement();
#endif
}
return id;
@@ -7327,10 +7385,10 @@ proc_seteuid(rb_uid_t uid)
if (seteuid(uid) < 0) rb_sys_fail(0);
#elif defined HAVE_SETUID
if (uid == getuid()) {
- if (setuid(uid) < 0) rb_sys_fail(0);
+ if (setuid(uid) < 0) rb_sys_fail(0);
}
else {
- rb_notimplement();
+ rb_notimplement();
}
#else
rb_notimplement();
@@ -7373,18 +7431,18 @@ rb_seteuid_core(rb_uid_t euid)
#if defined(HAVE_SETRESUID)
if (uid != euid) {
- if (setresuid(-1,euid,euid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = euid;
+ if (setresuid(-1,euid,euid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = euid;
}
else {
- if (setresuid(-1,euid,-1) < 0) rb_sys_fail(0);
+ if (setresuid(-1,euid,-1) < 0) rb_sys_fail(0);
}
#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
if (setreuid(-1, euid) < 0) rb_sys_fail(0);
if (uid != euid) {
- if (setreuid(euid,uid) < 0) rb_sys_fail(0);
- if (setreuid(uid,euid) < 0) rb_sys_fail(0);
- SAVED_USER_ID = euid;
+ if (setreuid(euid,uid) < 0) rb_sys_fail(0);
+ if (setreuid(uid,euid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = euid;
}
#elif defined HAVE_SETEUID
if (seteuid(euid) < 0) rb_sys_fail(0);
@@ -7470,10 +7528,10 @@ proc_setegid(VALUE obj, VALUE egid)
if (setegid(gid) < 0) rb_sys_fail(0);
#elif defined HAVE_SETGID
if (gid == getgid()) {
- if (setgid(gid) < 0) rb_sys_fail(0);
+ if (setgid(gid) < 0) rb_sys_fail(0);
}
else {
- rb_notimplement();
+ rb_notimplement();
}
#else
rb_notimplement();
@@ -7503,18 +7561,18 @@ rb_setegid_core(rb_gid_t egid)
#if defined(HAVE_SETRESGID)
if (gid != egid) {
- if (setresgid(-1,egid,egid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = egid;
+ if (setresgid(-1,egid,egid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = egid;
}
else {
- if (setresgid(-1,egid,-1) < 0) rb_sys_fail(0);
+ if (setresgid(-1,egid,-1) < 0) rb_sys_fail(0);
}
#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
if (setregid(-1, egid) < 0) rb_sys_fail(0);
if (gid != egid) {
- if (setregid(egid,gid) < 0) rb_sys_fail(0);
- if (setregid(gid,egid) < 0) rb_sys_fail(0);
- SAVED_GROUP_ID = egid;
+ if (setregid(egid,gid) < 0) rb_sys_fail(0);
+ if (setregid(gid,egid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = egid;
}
#elif defined HAVE_SETEGID
if (setegid(egid) < 0) rb_sys_fail(0);
@@ -7730,27 +7788,27 @@ p_uid_switch(VALUE obj)
euid = geteuid();
if (uid != euid) {
- proc_seteuid(uid);
- if (rb_block_given_p()) {
- under_uid_switch = 1;
- return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, SAVED_USER_ID);
- }
- else {
- return UIDT2NUM(euid);
- }
+ proc_seteuid(uid);
+ if (rb_block_given_p()) {
+ under_uid_switch = 1;
+ return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, SAVED_USER_ID);
+ }
+ else {
+ return UIDT2NUM(euid);
+ }
}
else if (euid != SAVED_USER_ID) {
- proc_seteuid(SAVED_USER_ID);
- if (rb_block_given_p()) {
- under_uid_switch = 1;
- return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, euid);
- }
- else {
- return UIDT2NUM(uid);
- }
+ proc_seteuid(SAVED_USER_ID);
+ if (rb_block_given_p()) {
+ under_uid_switch = 1;
+ return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, euid);
+ }
+ else {
+ return UIDT2NUM(uid);
+ }
}
else {
- rb_syserr_fail(EPERM, 0);
+ rb_syserr_fail(EPERM, 0);
}
UNREACHABLE_RETURN(Qnil);
@@ -7774,15 +7832,15 @@ p_uid_switch(VALUE obj)
euid = geteuid();
if (uid == euid) {
- rb_syserr_fail(EPERM, 0);
+ rb_syserr_fail(EPERM, 0);
}
p_uid_exchange(obj);
if (rb_block_given_p()) {
- under_uid_switch = 1;
- return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, obj);
+ under_uid_switch = 1;
+ return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, obj);
}
else {
- return UIDT2NUM(euid);
+ return UIDT2NUM(euid);
}
}
#endif
@@ -7844,27 +7902,27 @@ p_gid_switch(VALUE obj)
egid = getegid();
if (gid != egid) {
- proc_setegid(obj, GIDT2NUM(gid));
- if (rb_block_given_p()) {
- under_gid_switch = 1;
- return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, SAVED_GROUP_ID);
- }
- else {
- return GIDT2NUM(egid);
- }
+ proc_setegid(obj, GIDT2NUM(gid));
+ if (rb_block_given_p()) {
+ under_gid_switch = 1;
+ return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, SAVED_GROUP_ID);
+ }
+ else {
+ return GIDT2NUM(egid);
+ }
}
else if (egid != SAVED_GROUP_ID) {
- proc_setegid(obj, GIDT2NUM(SAVED_GROUP_ID));
- if (rb_block_given_p()) {
- under_gid_switch = 1;
- return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, egid);
- }
- else {
- return GIDT2NUM(gid);
- }
+ proc_setegid(obj, GIDT2NUM(SAVED_GROUP_ID));
+ if (rb_block_given_p()) {
+ under_gid_switch = 1;
+ return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, egid);
+ }
+ else {
+ return GIDT2NUM(gid);
+ }
}
else {
- rb_syserr_fail(EPERM, 0);
+ rb_syserr_fail(EPERM, 0);
}
UNREACHABLE_RETURN(Qnil);
@@ -7888,15 +7946,15 @@ p_gid_switch(VALUE obj)
egid = getegid();
if (gid == egid) {
- rb_syserr_fail(EPERM, 0);
+ rb_syserr_fail(EPERM, 0);
}
p_gid_exchange(obj);
if (rb_block_given_p()) {
- under_gid_switch = 1;
- return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, obj);
+ under_gid_switch = 1;
+ return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, obj);
}
else {
- return GIDT2NUM(egid);
+ return GIDT2NUM(egid);
}
}
#endif
@@ -7937,7 +7995,7 @@ rb_proc_times(VALUE obj)
struct rusage usage_s, usage_c;
if (getrusage(RUSAGE_SELF, &usage_s) != 0 || getrusage(RUSAGE_CHILDREN, &usage_c) != 0)
- rb_sys_fail("getrusage");
+ rb_sys_fail("getrusage");
utime = DBL2NUM((double)usage_s.ru_utime.tv_sec + (double)usage_s.ru_utime.tv_usec/1e6);
stime = DBL2NUM((double)usage_s.ru_stime.tv_sec + (double)usage_s.ru_stime.tv_usec/1e6);
cutime = DBL2NUM((double)usage_c.ru_utime.tv_sec + (double)usage_c.ru_utime.tv_usec/1e6);
@@ -8321,7 +8379,9 @@ rb_clock_gettime(int argc, VALUE *argv, VALUE _)
VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
VALUE clk_id = argv[0];
+#ifdef HAVE_CLOCK_GETTIME
clockid_t c;
+#endif
if (SYMBOL_P(clk_id)) {
#ifdef CLOCK_REALTIME
@@ -8463,7 +8523,7 @@ rb_clock_gettime(int argc, VALUE *argv, VALUE _)
#ifdef __APPLE__
if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
- const mach_timebase_info_data_t *info = get_mach_timebase_info();
+ const mach_timebase_info_data_t *info = get_mach_timebase_info();
uint64_t t = mach_absolute_time();
tt.count = (int32_t)(t % 1000000000);
tt.giga_count = t / 1000000000;
@@ -8547,7 +8607,9 @@ rb_clock_getres(int argc, VALUE *argv, VALUE _)
timetick_int_t denominators[2];
int num_numerators = 0;
int num_denominators = 0;
+#ifdef HAVE_CLOCK_GETRES
clockid_t c;
+#endif
VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
VALUE clk_id = argv[0];
@@ -8637,7 +8699,7 @@ rb_clock_getres(int argc, VALUE *argv, VALUE _)
#ifdef RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
- const mach_timebase_info_data_t *info = get_mach_timebase_info();
+ const mach_timebase_info_data_t *info = get_mach_timebase_info();
tt.count = 1;
tt.giga_count = 0;
numerators[num_numerators++] = info->numer;
@@ -8687,7 +8749,7 @@ get_PROCESS_ID(ID _x, VALUE *_y)
/*
* call-seq:
- * Process.kill(signal, pid, ...) -> integer
+ * Process.kill(signal, pid, *pids) -> integer
*
* Sends the given signal to the specified process id(s) if _pid_ is positive.
* If _pid_ is zero, _signal_ is sent to all processes whose group ID is equal
@@ -8851,20 +8913,20 @@ InitVM_process(void)
{
VALUE inf = RLIM2NUM(RLIM_INFINITY);
#ifdef RLIM_SAVED_MAX
- {
- VALUE v = RLIM_INFINITY == RLIM_SAVED_MAX ? inf : RLIM2NUM(RLIM_SAVED_MAX);
- /* see Process.setrlimit */
- rb_define_const(rb_mProcess, "RLIM_SAVED_MAX", v);
- }
+ {
+ VALUE v = RLIM_INFINITY == RLIM_SAVED_MAX ? inf : RLIM2NUM(RLIM_SAVED_MAX);
+ /* see Process.setrlimit */
+ rb_define_const(rb_mProcess, "RLIM_SAVED_MAX", v);
+ }
#endif
- /* see Process.setrlimit */
+ /* see Process.setrlimit */
rb_define_const(rb_mProcess, "RLIM_INFINITY", inf);
#ifdef RLIM_SAVED_CUR
- {
- VALUE v = RLIM_INFINITY == RLIM_SAVED_CUR ? inf : RLIM2NUM(RLIM_SAVED_CUR);
- /* see Process.setrlimit */
- rb_define_const(rb_mProcess, "RLIM_SAVED_CUR", v);
- }
+ {
+ VALUE v = RLIM_INFINITY == RLIM_SAVED_CUR ? inf : RLIM2NUM(RLIM_SAVED_CUR);
+ /* see Process.setrlimit */
+ rb_define_const(rb_mProcess, "RLIM_SAVED_CUR", v);
+ }
#endif
}
#ifdef RLIMIT_AS
@@ -8940,6 +9002,14 @@ InitVM_process(void)
*/
rb_define_const(rb_mProcess, "RLIMIT_NPROC", INT2FIX(RLIMIT_NPROC));
#endif
+#ifdef RLIMIT_NPTS
+ /* The maximum number of pseudo-terminals that can be created for the
+ * real user ID of the calling process.
+ *
+ * see the system getrlimit(2) manual for details.
+ */
+ rb_define_const(rb_mProcess, "RLIMIT_NPTS", INT2FIX(RLIMIT_NPTS));
+#endif
#ifdef RLIMIT_RSS
/* Specifies the limit (in pages) of the process's resident set.
*
diff --git a/ractor.c b/ractor.c
index ab993eb94e..8fb563fa11 100644
--- a/ractor.c
+++ b/ractor.c
@@ -17,6 +17,7 @@
#include "gc.h"
#include "transient_heap.h"
#include "yjit.h"
+#include "mjit.h"
VALUE rb_cRactor;
@@ -74,7 +75,9 @@ static void
ractor_lock_self(rb_ractor_t *cr, const char *file, int line)
{
VM_ASSERT(cr == GET_RACTOR());
+#if RACTOR_CHECK_MODE > 0
VM_ASSERT(cr->sync.locked_by != cr->pub.self);
+#endif
ractor_lock(cr, file, line);
}
@@ -94,7 +97,9 @@ static void
ractor_unlock_self(rb_ractor_t *cr, const char *file, int line)
{
VM_ASSERT(cr == GET_RACTOR());
+#if RACTOR_CHECK_MODE > 0
VM_ASSERT(cr->sync.locked_by == cr->pub.self);
+#endif
ractor_unlock(cr, file, line);
}
@@ -263,7 +268,7 @@ static const rb_data_type_t ractor_data_type = {
"ractor",
{
ractor_mark,
- ractor_free,
+ ractor_free,
ractor_memsize,
NULL, // update
},
@@ -493,13 +498,13 @@ ractor_try_receive(rb_execution_context_t *ec, rb_ractor_t *r)
}
static bool
-ractor_sleeping_by(const rb_ractor_t *r, enum ractor_wait_status wait_status)
+ractor_sleeping_by(const rb_ractor_t *r, enum rb_ractor_wait_status wait_status)
{
return (r->sync.wait.status & wait_status) && r->sync.wait.wakeup_status == wakeup_none;
}
static bool
-ractor_wakeup(rb_ractor_t *r, enum ractor_wait_status wait_status, enum ractor_wakeup_status wakeup_status)
+ractor_wakeup(rb_ractor_t *r, enum rb_ractor_wait_status wait_status, enum rb_ractor_wakeup_status wakeup_status)
{
ASSERT_ractor_locking(r);
@@ -547,7 +552,7 @@ ractor_sleep_interrupt(void *ptr)
#if USE_RUBY_DEBUG_LOG
static const char *
-wait_status_str(enum ractor_wait_status wait_status)
+wait_status_str(enum rb_ractor_wait_status wait_status)
{
switch ((int)wait_status) {
case wait_none: return "none";
@@ -563,7 +568,7 @@ wait_status_str(enum ractor_wait_status wait_status)
}
static const char *
-wakeup_status_str(enum ractor_wakeup_status wakeup_status)
+wakeup_status_str(enum rb_ractor_wakeup_status wakeup_status)
{
switch (wakeup_status) {
case wakeup_none: return "none";
@@ -724,7 +729,7 @@ ractor_receive(rb_execution_context_t *ec, rb_ractor_t *cr)
VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
VALUE v;
- while ((v = ractor_try_receive(ec, cr)) == Qundef) {
+ while (UNDEF_P(v = ractor_try_receive(ec, cr))) {
ractor_receive_wait(ec, cr);
}
@@ -871,7 +876,7 @@ ractor_receive_if(rb_execution_context_t *ec, VALUE crv, VALUE b)
}
RACTOR_UNLOCK_SELF(cr);
- if (v != Qundef) {
+ if (!UNDEF_P(v)) {
struct receive_block_data data = {
.cr = cr,
.rq = rq,
@@ -883,7 +888,7 @@ ractor_receive_if(rb_execution_context_t *ec, VALUE crv, VALUE b)
VALUE result = rb_ensure(receive_if_body, (VALUE)&data,
receive_if_ensure, (VALUE)&data);
- if (result != Qundef) return result;
+ if (!UNDEF_P(result)) return result;
index++;
}
@@ -1035,7 +1040,7 @@ ractor_try_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_b
VM_ASSERT(r->sync.wait.taken_basket.type == basket_type_none);
if (basket->type == basket_type_move) {
- enum ractor_wait_status prev_wait_status = r->sync.wait.status;
+ enum rb_ractor_wait_status prev_wait_status = r->sync.wait.status;
r->sync.wait.status = wait_moving;
RACTOR_UNLOCK(r);
@@ -1090,8 +1095,8 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL
VALUE ret = Qundef;
int i;
bool interrupted = false;
- enum ractor_wait_status wait_status = 0;
- bool yield_p = (yielded_value != Qundef) ? true : false;
+ enum rb_ractor_wait_status wait_status = 0;
+ bool yield_p = !UNDEF_P(yielded_value) ? true : false;
const int alen = rs_len + (yield_p ? 1 : 0);
struct ractor_select_action {
@@ -1148,7 +1153,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL
case ractor_select_action_take:
rv = actions[i].v;
v = ractor_try_take(ec, RACTOR_PTR(rv));
- if (v != Qundef) {
+ if (!UNDEF_P(v)) {
*ret_r = rv;
ret = v;
goto cleanup;
@@ -1156,7 +1161,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL
break;
case ractor_select_action_receive:
v = ractor_try_receive(ec, cr);
- if (v != Qundef) {
+ if (!UNDEF_P(v)) {
*ret_r = ID2SYM(rb_intern("receive"));
ret = v;
goto cleanup;
@@ -1264,7 +1269,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL
}
// check results
- enum ractor_wakeup_status wakeup_status = cr->sync.wait.wakeup_status;
+ enum rb_ractor_wakeup_status wakeup_status = cr->sync.wait.wakeup_status;
cr->sync.wait.wakeup_status = wakeup_none;
switch (wakeup_status) {
@@ -1320,7 +1325,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL
goto restart;
}
- VM_ASSERT(ret != Qundef);
+ VM_ASSERT(!UNDEF_P(ret));
return ret;
}
@@ -1434,12 +1439,6 @@ cancel_single_ractor_mode(void)
}
ruby_single_main_ractor = NULL;
-
- if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
- rb_category_warn(RB_WARN_CATEGORY_EXPERIMENTAL,
- "Ractor is experimental, and the behavior may change in future versions of Ruby! "
- "Also there are many implementation issues.");
- }
}
static void
@@ -1514,7 +1513,7 @@ rb_ractor_main_alloc(void)
{
rb_ractor_t *r = ruby_mimmalloc(sizeof(rb_ractor_t));
if (r == NULL) {
- fprintf(stderr, "[FATAL] failed to allocate memory for main ractor\n");
+ fprintf(stderr, "[FATAL] failed to allocate memory for main ractor\n");
exit(EXIT_FAILURE);
}
MEMZERO(r, rb_ractor_t, 1);
@@ -1608,6 +1607,7 @@ ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VAL
r->debug = cr->debug;
rb_yjit_before_ractor_spawn();
+ rb_mjit_before_ractor_spawn();
rb_thread_create_ractor(r, args, block);
RB_GC_GUARD(rv);
@@ -2250,6 +2250,19 @@ obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
return ST_CONTINUE;
}
+static int
+obj_hash_iv_traverse_i(st_data_t key, st_data_t val, st_data_t ptr)
+{
+ struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
+
+ if (obj_traverse_i((VALUE)val, d->data)) {
+ d->stop = true;
+ return ST_STOP;
+ }
+
+ return ST_CONTINUE;
+}
+
static void
obj_traverse_reachable_i(VALUE obj, void *ptr)
{
@@ -2291,7 +2304,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
for (uint32_t i = 0; i < ivtbl->numiv; i++) {
VALUE val = ivtbl->ivptr[i];
- if (val != Qundef && obj_traverse_i(val, data)) return 1;
+ if (!UNDEF_P(val) && obj_traverse_i(val, data)) return 1;
}
}
@@ -2308,12 +2321,22 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
case T_OBJECT:
{
- uint32_t len = ROBJECT_NUMIV(obj);
- VALUE *ptr = ROBJECT_IVPTR(obj);
+ if (rb_shape_obj_too_complex(obj)) {
+ struct obj_traverse_callback_data d = {
+ .stop = false,
+ .data = data,
+ };
+ rb_st_foreach(ROBJECT_IV_HASH(obj), obj_hash_iv_traverse_i, (st_data_t)&d);
+ if (d.stop) return 1;
+ }
+ else {
+ uint32_t len = ROBJECT_IV_COUNT(obj);
+ VALUE *ptr = ROBJECT_IVPTR(obj);
- for (uint32_t i=0; i<len; i++) {
- VALUE val = ptr[i];
- if (val != Qundef && obj_traverse_i(val, data)) return 1;
+ for (uint32_t i=0; i<len; i++) {
+ VALUE val = ptr[i];
+ if (!UNDEF_P(val) && obj_traverse_i(val, data)) return 1;
+ }
}
}
break;
@@ -2658,6 +2681,30 @@ obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr, int e
return ST_CONTINUE;
}
+static int
+obj_iv_hash_traverse_replace_foreach_i(st_data_t _key, st_data_t _val, st_data_t _data, int _x)
+{
+ return ST_REPLACE;
+}
+
+static int
+obj_iv_hash_traverse_replace_i(st_data_t * _key, st_data_t * val, st_data_t ptr, int exists)
+{
+ struct obj_traverse_replace_callback_data *d = (struct obj_traverse_replace_callback_data *)ptr;
+ struct obj_traverse_replace_data *data = d->data;
+
+ if (obj_traverse_replace_i(*(VALUE *)val, data)) {
+ d->stop = true;
+ return ST_STOP;
+ }
+ else if (*(VALUE *)val != data->replacement) {
+ VALUE v = *(VALUE *)val = data->replacement;
+ RB_OBJ_WRITTEN(d->src, Qundef, v);
+ }
+
+ return ST_CONTINUE;
+}
+
static struct st_table *
obj_traverse_replace_rec(struct obj_traverse_replace_data *data)
{
@@ -2681,7 +2728,7 @@ obj_refer_only_shareables_p_i(VALUE obj, void *ptr)
int *pcnt = (int *)ptr;
if (!rb_ractor_shareable_p(obj)) {
- pcnt++;
+ *pcnt++;
}
}
@@ -2737,7 +2784,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
struct gen_ivtbl *ivtbl;
rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
for (uint32_t i = 0; i < ivtbl->numiv; i++) {
- if (ivtbl->ivptr[i] != Qundef) {
+ if (!UNDEF_P(ivtbl->ivptr[i])) {
CHECK_AND_REPLACE(ivtbl->ivptr[i]);
}
}
@@ -2758,16 +2805,31 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
case T_OBJECT:
{
+ if (rb_shape_obj_too_complex(obj)) {
+ st_table * table = ROBJECT_IV_HASH(obj);
+ struct obj_traverse_replace_callback_data d = {
+ .stop = false,
+ .data = data,
+ .src = obj,
+ };
+ rb_st_foreach_with_replace(
+ table,
+ obj_iv_hash_traverse_replace_foreach_i,
+ obj_iv_hash_traverse_replace_i,
+ (st_data_t)&d);
+ }
+ else {
#if USE_TRANSIENT_HEAP
- if (data->move) rb_obj_transient_heap_evacuate(obj, TRUE);
+ if (data->move) rb_obj_transient_heap_evacuate(obj, TRUE);
#endif
- uint32_t len = ROBJECT_NUMIV(obj);
- VALUE *ptr = ROBJECT_IVPTR(obj);
+ uint32_t len = ROBJECT_IV_COUNT(obj);
+ VALUE *ptr = ROBJECT_IVPTR(obj);
- for (uint32_t i=0; i<len; i++) {
- if (ptr[i] != Qundef) {
- CHECK_AND_REPLACE(ptr[i]);
+ for (uint32_t i=0; i<len; i++) {
+ if (!UNDEF_P(ptr[i])) {
+ CHECK_AND_REPLACE(ptr[i]);
+ }
}
}
}
@@ -2971,7 +3033,7 @@ static VALUE
ractor_move(VALUE obj)
{
VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave, true);
- if (val != Qundef) {
+ if (!UNDEF_P(val)) {
return val;
}
else {
@@ -3002,7 +3064,7 @@ static VALUE
ractor_copy(VALUE obj)
{
VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave, false);
- if (val != Qundef) {
+ if (!UNDEF_P(val)) {
return val;
}
else {
@@ -3134,7 +3196,7 @@ static bool
ractor_local_ref(rb_ractor_local_key_t key, void **pret)
{
if (rb_ractor_main_p()) {
- if ((VALUE)key->main_cache != Qundef) {
+ if (!UNDEF_P((VALUE)key->main_cache)) {
*pret = key->main_cache;
return true;
}
diff --git a/ractor.rb b/ractor.rb
index ef36b2937f..1031fe499b 100644
--- a/ractor.rb
+++ b/ractor.rb
@@ -1,4 +1,4 @@
-# Ractor is a Actor-model abstraction for Ruby that provides thread-safe parallel execution.
+# Ractor is an Actor-model abstraction for Ruby that provides thread-safe parallel execution.
#
# Ractor.new can make a new Ractor, and it will run in parallel.
#
@@ -262,6 +262,10 @@ class Ractor
def self.new(*args, name: nil, &block)
b = block # TODO: builtin bug
raise ArgumentError, "must be called with a block" unless block
+ if __builtin_cexpr!("RBOOL(ruby_single_main_ractor)")
+ warn("Ractor is experimental, and the behavior may change in future versions of Ruby! " \
+ "Also there are many implementation issues.", uplevel: 0, category: :experimental)
+ end
loc = caller_locations(1, 1).first
loc = "#{loc.path}:#{loc.lineno}"
__builtin_ractor_create(loc, name, args, b)
@@ -699,7 +703,7 @@ class Ractor
def inspect
loc = __builtin_cexpr! %q{ RACTOR_PTR(self)->loc }
name = __builtin_cexpr! %q{ RACTOR_PTR(self)->name }
- id = __builtin_cexpr! %q{ INT2FIX(rb_ractor_id(RACTOR_PTR(self))) }
+ id = __builtin_cexpr! %q{ UINT2NUM(rb_ractor_id(RACTOR_PTR(self))) }
status = __builtin_cexpr! %q{
rb_str_new2(ractor_status_str(RACTOR_PTR(self)->status_))
}
diff --git a/ractor_core.h b/ractor_core.h
index dd1b73b331..968c12d291 100644
--- a/ractor_core.h
+++ b/ractor_core.h
@@ -5,7 +5,7 @@
#include "vm_debug.h"
#ifndef RACTOR_CHECK_MODE
-#define RACTOR_CHECK_MODE (0 || VM_CHECK_MODE || RUBY_DEBUG)
+#define RACTOR_CHECK_MODE (VM_CHECK_MODE || RUBY_DEBUG) && (SIZEOF_UINT64_T == SIZEOF_VALUE)
#endif
enum rb_ractor_basket_type {
@@ -40,6 +40,24 @@ struct rb_ractor_waiting_list {
rb_ractor_t **ractors;
};
+enum rb_ractor_wait_status {
+ wait_none = 0x00,
+ wait_receiving = 0x01,
+ wait_taking = 0x02,
+ wait_yielding = 0x04,
+ wait_moving = 0x08,
+};
+
+enum rb_ractor_wakeup_status {
+ wakeup_none,
+ wakeup_by_send,
+ wakeup_by_yield,
+ wakeup_by_take,
+ wakeup_by_close,
+ wakeup_by_interrupt,
+ wakeup_by_retry,
+};
+
struct rb_ractor_sync {
// ractor lock
rb_nativethread_lock_t lock;
@@ -56,29 +74,34 @@ struct rb_ractor_sync {
bool outgoing_port_closed;
struct ractor_wait {
- enum ractor_wait_status {
- wait_none = 0x00,
- wait_receiving = 0x01,
- wait_taking = 0x02,
- wait_yielding = 0x04,
- wait_moving = 0x08,
- } status;
-
- enum ractor_wakeup_status {
- wakeup_none,
- wakeup_by_send,
- wakeup_by_yield,
- wakeup_by_take,
- wakeup_by_close,
- wakeup_by_interrupt,
- wakeup_by_retry,
- } wakeup_status;
-
+ enum rb_ractor_wait_status status;
+ enum rb_ractor_wakeup_status wakeup_status;
struct rb_ractor_basket yielded_basket;
struct rb_ractor_basket taken_basket;
} wait;
};
+// created
+// | ready to run
+// ====================== inserted to vm->ractor
+// v
+// blocking <---+ all threads are blocking
+// | |
+// v |
+// running -----+
+// | all threads are terminated.
+// ====================== removed from vm->ractor
+// v
+// terminated
+//
+// status is protected by VM lock (global state)
+enum ractor_status {
+ ractor_created,
+ ractor_running,
+ ractor_blocking,
+ ractor_terminated,
+};
+
struct rb_ractor_struct {
struct rb_ractor_pub pub;
@@ -104,27 +127,7 @@ struct rb_ractor_struct {
VALUE name;
VALUE loc;
- // created
- // | ready to run
- // ====================== inserted to vm->ractor
- // v
- // blocking <---+ all threads are blocking
- // | |
- // v |
- // running -----+
- // | all threads are terminated.
- // ====================== removed from vm->ractor
- // v
- // terminated
- //
- // status is protected by VM lock (global state)
-
- enum ractor_status {
- ractor_created,
- ractor_running,
- ractor_blocking,
- ractor_terminated,
- } status_;
+ enum ractor_status status_;
struct ccan_list_node vmlr_node;
@@ -270,7 +273,7 @@ rb_ractor_set_current_ec_(rb_ractor_t *cr, rb_execution_context_t *ec, const cha
#else
native_tls_set(ruby_current_ec_key, ec);
#endif
- RUBY_DEBUG_LOG2(file, line, "ec:%p->%p", cr->threads.running_ec, ec);
+ RUBY_DEBUG_LOG2(file, line, "ec:%p->%p", (void *)cr->threads.running_ec, (void *)ec);
VM_ASSERT(cr->threads.running_ec != ec);
cr->threads.running_ec = ec;
}
@@ -285,13 +288,14 @@ rb_ractor_id(const rb_ractor_t *r)
}
#if RACTOR_CHECK_MODE > 0
+# define RACTOR_BELONGING_ID(obj) (*(uint32_t *)(((uintptr_t)(obj)) + rb_gc_obj_slot_size(obj)))
+
uint32_t rb_ractor_current_id(void);
static inline void
rb_ractor_setup_belonging_to(VALUE obj, uint32_t rid)
{
- VALUE flags = RBASIC(obj)->flags & 0xffffffff; // 4B
- RBASIC(obj)->flags = flags | ((VALUE)rid << 32);
+ RACTOR_BELONGING_ID(obj) = rid;
}
static inline void
@@ -307,7 +311,7 @@ rb_ractor_belonging(VALUE obj)
return 0;
}
else {
- return RBASIC(obj)->flags >> 32;
+ return RACTOR_BELONGING_ID(obj);
}
}
diff --git a/random.c b/random.c
index 30329ed45c..4b5b7ab6c4 100644
--- a/random.c
+++ b/random.c
@@ -144,7 +144,7 @@ static rb_random_mt_t *
rand_mt_start(rb_random_mt_t *r)
{
if (!genrand_initialized(&r->mt)) {
- r->base.seed = rand_init(&random_mt_if, &r->base, random_seed(Qundef));
+ r->base.seed = rand_init(&random_mt_if, &r->base, random_seed(Qundef));
}
return r;
}
@@ -216,7 +216,7 @@ int_pair_to_real_inclusive(uint32_t a, uint32_t b)
r = (double)(uint64_t)((x * m) >> 64);
#elif defined HAVE_UINT64_T && !MSC_VERSION_BEFORE(1300)
uint64_t x = ((uint64_t)a << dig_u) +
- (((uint64_t)b + (a >> dig_u)) >> dig_r64);
+ (((uint64_t)b + (a >> dig_u)) >> dig_r64);
r = (double)x;
#else
/* shift then add to get rid of overflow */
@@ -252,9 +252,9 @@ random_memsize(const void *ptr)
const rb_data_type_t rb_random_data_type = {
"random",
{
- random_mark,
- random_free,
- random_memsize,
+ random_mark,
+ random_free,
+ random_memsize,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
@@ -271,9 +271,9 @@ random_mt_memsize(const void *ptr)
static const rb_data_type_t random_mt_type = {
"random/MT",
{
- random_mt_mark,
- random_mt_free,
- random_mt_memsize,
+ random_mt_mark,
+ random_mt_free,
+ random_mt_memsize,
},
&rb_random_data_type,
(void *)&random_mt_if,
@@ -302,7 +302,7 @@ static rb_random_t *
try_get_rnd(VALUE obj)
{
if (obj == rb_cRandom) {
- return rand_start(default_rand());
+ return rand_start(default_rand());
}
if (!rb_typeddata_is_kind_of(obj, &rb_random_data_type)) return NULL;
if (RTYPEDDATA_TYPE(obj) == &random_mt_type)
@@ -319,7 +319,7 @@ static const rb_random_interface_t *
try_rand_if(VALUE obj, rb_random_t *rnd)
{
if (rnd == &default_rand()->base) {
- return &random_mt_if;
+ return &random_mt_if;
}
return rb_rand_if(obj);
}
@@ -371,11 +371,14 @@ rand_init(const rb_random_interface_t *rng, rb_random_t *rnd, VALUE seed)
INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER);
if (sign < 0)
sign = -sign;
- if (len > 1) {
+ if (len <= 1) {
+ rng->init_int32(rnd, len ? buf[0] : 0);
+ }
+ else {
if (sign != 2 && buf[len-1] == 1) /* remove leading-zero-guard */
len--;
+ rng->init(rnd, buf, len);
}
- rng->init(rnd, buf, len);
explicit_bzero(buf, len * sizeof(*buf));
ALLOCV_END(buf0);
return seed;
@@ -400,6 +403,15 @@ random_init(int argc, VALUE *argv, VALUE obj)
rb_raise(rb_eTypeError, "undefined random interface: %s",
RTYPEDDATA_TYPE(obj)->wrap_struct_name);
}
+
+ unsigned int major = rng->version.major;
+ unsigned int minor = rng->version.minor;
+ if (major != RUBY_RANDOM_INTERFACE_VERSION_MAJOR) {
+ rb_raise(rb_eTypeError, "Random interface version "
+ STRINGIZE(RUBY_RANDOM_INTERFACE_VERSION_MAJOR) "."
+ STRINGIZE(RUBY_RANDOM_INTERFACE_VERSION_MINOR) " "
+ "expected: %d.%d", major, minor);
+ }
argc = rb_check_arity(argc, 0, 1);
rb_check_frozen(obj);
if (argc == 0) {
@@ -426,12 +438,12 @@ fill_random_bytes_urandom(void *seed, size_t size)
{
unsigned char *p = (unsigned char *)seed;
while (size) {
- size_t len = size < MAX_SEED_LEN_PER_READ ? size : MAX_SEED_LEN_PER_READ;
- if (getentropy(p, len) != 0) {
+ size_t len = size < MAX_SEED_LEN_PER_READ ? size : MAX_SEED_LEN_PER_READ;
+ if (getentropy(p, len) != 0) {
return -1;
- }
- p += len;
- size -= len;
+ }
+ p += len;
+ size -= len;
}
return 0;
}
@@ -446,12 +458,12 @@ fill_random_bytes_urandom(void *seed, size_t size)
*/
int fd = rb_cloexec_open("/dev/urandom",
# ifdef O_NONBLOCK
- O_NONBLOCK|
+ O_NONBLOCK|
# endif
# ifdef O_NOCTTY
- O_NOCTTY|
+ O_NOCTTY|
# endif
- O_RDONLY, 0);
+ O_RDONLY, 0);
struct stat statbuf;
ssize_t ret = 0;
size_t offset = 0;
@@ -459,14 +471,14 @@ fill_random_bytes_urandom(void *seed, size_t size)
if (fd < 0) return -1;
rb_update_max_fd(fd);
if (fstat(fd, &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) {
- do {
- ret = read(fd, ((char*)seed) + offset, size - offset);
- if (ret < 0) {
- close(fd);
- return -1;
- }
- offset += (size_t)ret;
- } while (offset < size);
+ do {
+ ret = read(fd, ((char*)seed) + offset, size - offset);
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+ offset += (size_t)ret;
+ } while (offset < size);
}
close(fd);
return 0;
@@ -487,28 +499,35 @@ fill_random_bytes_urandom(void *seed, size_t size)
#if 0
#elif defined MAC_OS_X_VERSION_10_7 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7
-# if defined MAC_OS_X_VERSION_10_10 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10
+# if defined(USE_COMMON_RANDOM)
+# elif defined MAC_OS_X_VERSION_10_10 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10
+# define USE_COMMON_RANDOM 1
+# else
+# define USE_COMMON_RANDOM 0
+# endif
+# if USE_COMMON_RANDOM
# include <CommonCrypto/CommonCryptoError.h> /* for old Xcode */
# include <CommonCrypto/CommonRandom.h>
-# define USE_COMMON_RANDOM 1
# else
# include <Security/SecRandom.h>
-# define USE_COMMON_RANDOM 0
# endif
static int
fill_random_bytes_syscall(void *seed, size_t size, int unused)
{
#if USE_COMMON_RANDOM
- int failed = CCRandomGenerateBytes(seed, size) != kCCSuccess;
+ CCRNGStatus status = CCRandomGenerateBytes(seed, size);
+ int failed = status != kCCSuccess;
#else
- int failed = SecRandomCopyBytes(kSecRandomDefault, size, seed) != errSecSuccess;
+ int status = SecRandomCopyBytes(kSecRandomDefault, size, seed);
+ int failed = status != errSecSuccess;
#endif
if (failed) {
# if 0
# if USE_COMMON_RANDOM
/* How to get the error message? */
+ fprintf(stderr, "CCRandomGenerateBytes failed: %d\n", status);
# else
CFStringRef s = SecCopyErrorMessageString(status, NULL);
const char *m = s ? CFStringGetCStringPtr(s, kCFStringEncodingUTF8) : NULL;
@@ -553,7 +572,7 @@ release_crypt(void *p)
HCRYPTPROV *ptr = p;
HCRYPTPROV prov = (HCRYPTPROV)ATOMIC_SIZE_EXCHANGE(*ptr, INVALID_HCRYPTPROV);
if (prov && prov != INVALID_HCRYPTPROV) {
- CryptReleaseContext(prov, 0);
+ CryptReleaseContext(prov, 0);
}
}
@@ -563,23 +582,23 @@ fill_random_bytes_crypt(void *seed, size_t size)
static HCRYPTPROV perm_prov;
HCRYPTPROV prov = perm_prov, old_prov;
if (!prov) {
- if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
- prov = INVALID_HCRYPTPROV;
- }
- old_prov = (HCRYPTPROV)ATOMIC_SIZE_CAS(perm_prov, 0, prov);
- if (LIKELY(!old_prov)) { /* no other threads acquired */
- if (prov != INVALID_HCRYPTPROV) {
+ if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+ prov = INVALID_HCRYPTPROV;
+ }
+ old_prov = (HCRYPTPROV)ATOMIC_SIZE_CAS(perm_prov, 0, prov);
+ if (LIKELY(!old_prov)) { /* no other threads acquired */
+ if (prov != INVALID_HCRYPTPROV) {
#undef RUBY_UNTYPED_DATA_WARNING
#define RUBY_UNTYPED_DATA_WARNING 0
- rb_gc_register_mark_object(Data_Wrap_Struct(0, 0, release_crypt, &perm_prov));
- }
- }
- else { /* another thread acquired */
- if (prov != INVALID_HCRYPTPROV) {
- CryptReleaseContext(prov, 0);
- }
- prov = old_prov;
- }
+ rb_gc_register_mark_object(Data_Wrap_Struct(0, 0, release_crypt, &perm_prov));
+ }
+ }
+ else { /* another thread acquired */
+ if (prov != INVALID_HCRYPTPROV) {
+ CryptReleaseContext(prov, 0);
+ }
+ prov = old_prov;
+ }
}
if (prov == INVALID_HCRYPTPROV) return -1;
while (size > 0) {
@@ -619,20 +638,20 @@ fill_random_bytes_syscall(void *seed, size_t size, int need_secure)
{
static rb_atomic_t try_syscall = 1;
if (try_syscall) {
- size_t offset = 0;
- int flags = 0;
- if (!need_secure)
- flags = GRND_NONBLOCK;
- do {
- errno = 0;
+ size_t offset = 0;
+ int flags = 0;
+ if (!need_secure)
+ flags = GRND_NONBLOCK;
+ do {
+ errno = 0;
ssize_t ret = getrandom(((char*)seed) + offset, size - offset, flags);
- if (ret == -1) {
- ATOMIC_SET(try_syscall, 0);
- return -1;
- }
- offset += (size_t)ret;
- } while (offset < size);
- return 0;
+ if (ret == -1) {
+ ATOMIC_SET(try_syscall, 0);
+ return -1;
+ }
+ offset += (size_t)ret;
+ } while (offset < size);
+ return 0;
}
return -1;
}
@@ -745,7 +764,7 @@ random_raw_seed(VALUE self, VALUE size)
VALUE buf = rb_str_new(0, n);
if (n == 0) return buf;
if (fill_random_bytes(RSTRING_PTR(buf), n, TRUE))
- rb_raise(rb_eRuntimeError, "failed to get urandom");
+ rb_raise(rb_eRuntimeError, "failed to get urandom");
return buf;
}
@@ -857,16 +876,16 @@ rand_mt_load(VALUE obj, VALUE dump)
left = RARRAY_AREF(dump, 1);
case 1:
state = RARRAY_AREF(dump, 0);
- break;
+ break;
default:
- rb_raise(rb_eArgError, "wrong dump data");
+ rb_raise(rb_eArgError, "wrong dump data");
}
rb_integer_pack(state, mt->state, numberof(mt->state),
sizeof(*mt->state), 0,
INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER);
x = NUM2ULONG(left);
if (x > numberof(mt->state)) {
- rb_raise(rb_eArgError, "wrong value");
+ rb_raise(rb_eArgError, "wrong value");
}
mt->left = (unsigned int)x;
mt->next = mt->state + numberof(mt->state) - x + 1;
@@ -876,15 +895,17 @@ rand_mt_load(VALUE obj, VALUE dump)
}
static void
+rand_mt_init_int32(rb_random_t *rnd, uint32_t data)
+{
+ struct MT *mt = &((rb_random_mt_t *)rnd)->mt;
+ init_genrand(mt, data);
+}
+
+static void
rand_mt_init(rb_random_t *rnd, const uint32_t *buf, size_t len)
{
struct MT *mt = &((rb_random_mt_t *)rnd)->mt;
- if (len <= 1) {
- init_genrand(mt, len ? buf[0] : 0);
- }
- else {
- init_by_array(mt, buf, (int)len);
- }
+ init_by_array(mt, buf, (int)len);
}
static unsigned int
@@ -933,7 +954,7 @@ rb_f_srand(int argc, VALUE *argv, VALUE obj)
seed = random_seed(obj);
}
else {
- seed = rb_to_int(argv[0]);
+ seed = rb_to_int(argv[0]);
}
old = r->base.seed;
rand_init(&random_mt_if, &r->base, seed);
@@ -1056,9 +1077,9 @@ obj_random_bytes(VALUE obj, void *p, long n)
Check_Type(v, T_STRING);
l = RSTRING_LEN(v);
if (l < n)
- rb_raise(rb_eRangeError, "random data too short %ld", l);
+ rb_raise(rb_eRangeError, "random data too short %ld", l);
else if (l > n)
- rb_raise(rb_eRangeError, "random data too long %ld", l);
+ rb_raise(rb_eRangeError, "random data too long %ld", l);
if (p) memcpy(p, RSTRING_PTR(v), n);
return v;
}
@@ -1074,9 +1095,9 @@ rb_random_int32(VALUE obj)
{
rb_random_t *rnd = try_get_rnd(obj);
if (!rnd) {
- uint32_t x;
- obj_random_bytes(obj, &x, sizeof(x));
- return (unsigned int)x;
+ uint32_t x;
+ obj_random_bytes(obj, &x, sizeof(x));
+ return (unsigned int)x;
}
return random_int32(try_rand_if(obj, rnd), rnd);
}
@@ -1087,10 +1108,10 @@ random_real(VALUE obj, rb_random_t *rnd, int excl)
uint32_t a, b;
if (!rnd) {
- uint32_t x[2] = {0, 0};
- obj_random_bytes(obj, x, sizeof(x));
- a = x[0];
- b = x[1];
+ uint32_t x[2] = {0, 0};
+ obj_random_bytes(obj, x, sizeof(x));
+ a = x[0];
+ b = x[1];
}
else {
const rb_random_interface_t *rng = try_rand_if(obj, rnd);
@@ -1105,10 +1126,10 @@ double
rb_int_pair_to_real(uint32_t a, uint32_t b, int excl)
{
if (excl) {
- return int_pair_to_real_exclusive(a, b);
+ return int_pair_to_real_exclusive(a, b);
}
else {
- return int_pair_to_real_inclusive(a, b);
+ return int_pair_to_real_inclusive(a, b);
}
}
@@ -1117,15 +1138,15 @@ rb_random_real(VALUE obj)
{
rb_random_t *rnd = try_get_rnd(obj);
if (!rnd) {
- VALUE v = rb_funcallv(obj, id_rand, 0, 0);
- double d = NUM2DBL(v);
- if (d < 0.0) {
- rb_raise(rb_eRangeError, "random number too small %g", d);
- }
- else if (d >= 1.0) {
- rb_raise(rb_eRangeError, "random number too big %g", d);
- }
- return d;
+ VALUE v = rb_funcallv(obj, id_rand, 0, 0);
+ double d = NUM2DBL(v);
+ if (d < 0.0) {
+ rb_raise(rb_eRangeError, "random number too small %g", d);
+ }
+ else if (d >= 1.0) {
+ rb_raise(rb_eRangeError, "random number too big %g", d);
+ }
+ return d;
}
return random_real(obj, rnd, TRUE);
}
@@ -1137,7 +1158,7 @@ ulong_to_num_plus_1(unsigned long n)
return ULL2NUM((LONG_LONG)n+1);
#else
if (n >= ULONG_MAX) {
- return rb_big_plus(ULONG2NUM(n), INT2FIX(1));
+ return rb_big_plus(ULONG2NUM(n), INT2FIX(1));
}
return ULONG2NUM(n+1);
#endif
@@ -1148,26 +1169,26 @@ random_ulong_limited(VALUE obj, rb_random_t *rnd, unsigned long limit)
{
if (!limit) return 0;
if (!rnd) {
- const int w = sizeof(limit) * CHAR_BIT - nlz_long(limit);
- const int n = w > 32 ? sizeof(unsigned long) : sizeof(uint32_t);
- const unsigned long mask = ~(~0UL << w);
- const unsigned long full =
- (size_t)n >= sizeof(unsigned long) ? ~0UL :
- ~(~0UL << n * CHAR_BIT);
- unsigned long val, bits = 0, rest = 0;
- do {
- if (mask & ~rest) {
- union {uint32_t u32; unsigned long ul;} buf;
- obj_random_bytes(obj, &buf, n);
- rest = full;
- bits = (n == sizeof(uint32_t)) ? buf.u32 : buf.ul;
- }
- val = bits;
- bits >>= w;
- rest >>= w;
- val &= mask;
- } while (limit < val);
- return val;
+ const int w = sizeof(limit) * CHAR_BIT - nlz_long(limit);
+ const int n = w > 32 ? sizeof(unsigned long) : sizeof(uint32_t);
+ const unsigned long mask = ~(~0UL << w);
+ const unsigned long full =
+ (size_t)n >= sizeof(unsigned long) ? ~0UL :
+ ~(~0UL << n * CHAR_BIT);
+ unsigned long val, bits = 0, rest = 0;
+ do {
+ if (mask & ~rest) {
+ union {uint32_t u32; unsigned long ul;} buf;
+ obj_random_bytes(obj, &buf, n);
+ rest = full;
+ bits = (n == sizeof(uint32_t)) ? buf.u32 : buf.ul;
+ }
+ val = bits;
+ bits >>= w;
+ rest >>= w;
+ val &= mask;
+ } while (limit < val);
+ return val;
}
return limited_rand(try_rand_if(obj, rnd), rnd, limit);
}
@@ -1177,16 +1198,16 @@ rb_random_ulong_limited(VALUE obj, unsigned long limit)
{
rb_random_t *rnd = try_get_rnd(obj);
if (!rnd) {
- VALUE lim = ulong_to_num_plus_1(limit);
- VALUE v = rb_to_int(rb_funcallv_public(obj, id_rand, 1, &lim));
- unsigned long r = NUM2ULONG(v);
- if (rb_num_negative_p(v)) {
- rb_raise(rb_eRangeError, "random number too small %ld", r);
- }
- if (r > limit) {
- rb_raise(rb_eRangeError, "random number too big %ld", r);
- }
- return r;
+ VALUE lim = ulong_to_num_plus_1(limit);
+ VALUE v = rb_to_int(rb_funcallv_public(obj, id_rand, 1, &lim));
+ unsigned long r = NUM2ULONG(v);
+ if (rb_num_negative_p(v)) {
+ rb_raise(rb_eRangeError, "random number too small %ld", r);
+ }
+ if (r > limit) {
+ rb_raise(rb_eRangeError, "random number too big %ld", r);
+ }
+ return r;
}
return limited_rand(try_rand_if(obj, rnd), rnd, limit);
}
@@ -1195,27 +1216,27 @@ static VALUE
random_ulong_limited_big(VALUE obj, rb_random_t *rnd, VALUE vmax)
{
if (!rnd) {
- VALUE v, vtmp;
- size_t i, nlz, len = rb_absint_numwords(vmax, 32, &nlz);
- uint32_t *tmp = ALLOCV_N(uint32_t, vtmp, len * 2);
- uint32_t mask = (uint32_t)~0 >> nlz;
- uint32_t *lim_array = tmp;
- uint32_t *rnd_array = tmp + len;
- int flag = INTEGER_PACK_MSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER;
- rb_integer_pack(vmax, lim_array, len, sizeof(uint32_t), 0, flag);
+ VALUE v, vtmp;
+ size_t i, nlz, len = rb_absint_numwords(vmax, 32, &nlz);
+ uint32_t *tmp = ALLOCV_N(uint32_t, vtmp, len * 2);
+ uint32_t mask = (uint32_t)~0 >> nlz;
+ uint32_t *lim_array = tmp;
+ uint32_t *rnd_array = tmp + len;
+ int flag = INTEGER_PACK_MSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER;
+ rb_integer_pack(vmax, lim_array, len, sizeof(uint32_t), 0, flag);
retry:
- obj_random_bytes(obj, rnd_array, len * sizeof(uint32_t));
- rnd_array[0] &= mask;
- for (i = 0; i < len; ++i) {
- if (lim_array[i] < rnd_array[i])
- goto retry;
- if (rnd_array[i] < lim_array[i])
- break;
- }
- v = rb_integer_unpack(rnd_array, len, sizeof(uint32_t), 0, flag);
- ALLOCV_END(vtmp);
- return v;
+ obj_random_bytes(obj, rnd_array, len * sizeof(uint32_t));
+ rnd_array[0] &= mask;
+ for (i = 0; i < len; ++i) {
+ if (lim_array[i] < rnd_array[i])
+ goto retry;
+ if (rnd_array[i] < lim_array[i])
+ break;
+ }
+ v = rb_integer_unpack(rnd_array, len, sizeof(uint32_t), 0, flag);
+ ALLOCV_END(vtmp);
+ return v;
}
return limited_big_rand(try_rand_if(obj, rnd), rnd, vmax);
}
@@ -1255,18 +1276,18 @@ rb_rand_bytes_int32(rb_random_get_int32_func *get_int32,
unsigned int r, i;
for (; n >= SIZEOF_INT32; n -= SIZEOF_INT32) {
r = get_int32(rnd);
- i = SIZEOF_INT32;
- do {
- *ptr++ = (char)r;
- r >>= CHAR_BIT;
+ i = SIZEOF_INT32;
+ do {
+ *ptr++ = (char)r;
+ r >>= CHAR_BIT;
} while (--i);
}
if (n > 0) {
r = get_int32(rnd);
- do {
- *ptr++ = (char)r;
- r >>= CHAR_BIT;
- } while (--n);
+ do {
+ *ptr++ = (char)r;
+ r >>= CHAR_BIT;
+ } while (--n);
}
}
@@ -1275,7 +1296,7 @@ rb_random_bytes(VALUE obj, long n)
{
rb_random_t *rnd = try_get_rnd(obj);
if (!rnd) {
- return obj_random_bytes(obj, NULL, n);
+ return obj_random_bytes(obj, NULL, n);
}
return rand_bytes(try_rand_if(obj, rnd), rnd, n);
}
@@ -1335,32 +1356,32 @@ rand_int(VALUE obj, rb_random_t *rnd, VALUE vmax, int restrictive)
unsigned long r;
if (FIXNUM_P(vmax)) {
- long max = FIX2LONG(vmax);
- if (!max) return Qnil;
- if (max < 0) {
- if (restrictive) return Qnil;
- max = -max;
- }
- r = random_ulong_limited(obj, rnd, (unsigned long)max - 1);
- return ULONG2NUM(r);
+ long max = FIX2LONG(vmax);
+ if (!max) return Qnil;
+ if (max < 0) {
+ if (restrictive) return Qnil;
+ max = -max;
+ }
+ r = random_ulong_limited(obj, rnd, (unsigned long)max - 1);
+ return ULONG2NUM(r);
}
else {
- VALUE ret;
- if (rb_bigzero_p(vmax)) return Qnil;
- if (!BIGNUM_SIGN(vmax)) {
- if (restrictive) return Qnil;
+ VALUE ret;
+ if (rb_bigzero_p(vmax)) return Qnil;
+ if (!BIGNUM_SIGN(vmax)) {
+ if (restrictive) return Qnil;
vmax = rb_big_uminus(vmax);
- }
- vmax = rb_big_minus(vmax, INT2FIX(1));
- if (FIXNUM_P(vmax)) {
- long max = FIX2LONG(vmax);
- if (max == -1) return Qnil;
- r = random_ulong_limited(obj, rnd, max);
- return LONG2NUM(r);
- }
- ret = random_ulong_limited_big(obj, rnd, vmax);
- RB_GC_GUARD(vmax);
- return ret;
+ }
+ vmax = rb_big_minus(vmax, INT2FIX(1));
+ if (FIXNUM_P(vmax)) {
+ long max = FIX2LONG(vmax);
+ if (max == -1) return Qnil;
+ r = random_ulong_limited(obj, rnd, max);
+ return LONG2NUM(r);
+ }
+ ret = random_ulong_limited_big(obj, rnd, vmax);
+ RB_GC_GUARD(vmax);
+ return ret;
}
}
@@ -1383,10 +1404,10 @@ check_random_number(VALUE v, const VALUE *argv)
{
switch (v) {
case Qfalse:
- (void)NUM2LONG(argv[0]);
- break;
+ (void)NUM2LONG(argv[0]);
+ break;
case Qnil:
- invalid_argument(argv[0]);
+ invalid_argument(argv[0]);
}
return v;
}
@@ -1396,7 +1417,7 @@ float_value(VALUE v)
{
double x = RFLOAT_VALUE(v);
if (!isfinite(x)) {
- domain_error();
+ domain_error();
}
return x;
}
@@ -1408,71 +1429,71 @@ rand_range(VALUE obj, rb_random_t* rnd, VALUE range)
int excl = 0;
if ((v = vmax = range_values(range, &beg, &end, &excl)) == Qfalse)
- return Qfalse;
+ return Qfalse;
if (NIL_P(v)) domain_error();
if (!RB_FLOAT_TYPE_P(vmax) && (v = rb_check_to_int(vmax), !NIL_P(v))) {
- long max;
- vmax = v;
- v = Qnil;
+ long max;
+ vmax = v;
+ v = Qnil;
fixnum:
- if (FIXNUM_P(vmax)) {
- if ((max = FIX2LONG(vmax) - excl) >= 0) {
- unsigned long r = random_ulong_limited(obj, rnd, (unsigned long)max);
- v = ULONG2NUM(r);
- }
- }
- else if (BUILTIN_TYPE(vmax) == T_BIGNUM && BIGNUM_SIGN(vmax) && !rb_bigzero_p(vmax)) {
- vmax = excl ? rb_big_minus(vmax, INT2FIX(1)) : rb_big_norm(vmax);
- if (FIXNUM_P(vmax)) {
- excl = 0;
- goto fixnum;
- }
- v = random_ulong_limited_big(obj, rnd, vmax);
- }
+ if (FIXNUM_P(vmax)) {
+ if ((max = FIX2LONG(vmax) - excl) >= 0) {
+ unsigned long r = random_ulong_limited(obj, rnd, (unsigned long)max);
+ v = ULONG2NUM(r);
+ }
+ }
+ else if (BUILTIN_TYPE(vmax) == T_BIGNUM && BIGNUM_SIGN(vmax) && !rb_bigzero_p(vmax)) {
+ vmax = excl ? rb_big_minus(vmax, INT2FIX(1)) : rb_big_norm(vmax);
+ if (FIXNUM_P(vmax)) {
+ excl = 0;
+ goto fixnum;
+ }
+ v = random_ulong_limited_big(obj, rnd, vmax);
+ }
}
else if (v = rb_check_to_float(vmax), !NIL_P(v)) {
- int scale = 1;
- double max = RFLOAT_VALUE(v), mid = 0.5, r;
- if (isinf(max)) {
- double min = float_value(rb_to_float(beg)) / 2.0;
- max = float_value(rb_to_float(end)) / 2.0;
- scale = 2;
- mid = max + min;
- max -= min;
- }
- else if (isnan(max)) {
- domain_error();
- }
- v = Qnil;
- if (max > 0.0) {
- r = random_real(obj, rnd, excl);
- if (scale > 1) {
- return rb_float_new(+(+(+(r - 0.5) * max) * scale) + mid);
- }
- v = rb_float_new(r * max);
- }
- else if (max == 0.0 && !excl) {
- v = rb_float_new(0.0);
- }
+ int scale = 1;
+ double max = RFLOAT_VALUE(v), mid = 0.5, r;
+ if (isinf(max)) {
+ double min = float_value(rb_to_float(beg)) / 2.0;
+ max = float_value(rb_to_float(end)) / 2.0;
+ scale = 2;
+ mid = max + min;
+ max -= min;
+ }
+ else if (isnan(max)) {
+ domain_error();
+ }
+ v = Qnil;
+ if (max > 0.0) {
+ r = random_real(obj, rnd, excl);
+ if (scale > 1) {
+ return rb_float_new(+(+(+(r - 0.5) * max) * scale) + mid);
+ }
+ v = rb_float_new(r * max);
+ }
+ else if (max == 0.0 && !excl) {
+ v = rb_float_new(0.0);
+ }
}
if (FIXNUM_P(beg) && FIXNUM_P(v)) {
- long x = FIX2LONG(beg) + FIX2LONG(v);
- return LONG2NUM(x);
+ long x = FIX2LONG(beg) + FIX2LONG(v);
+ return LONG2NUM(x);
}
switch (TYPE(v)) {
case T_NIL:
- break;
+ break;
case T_BIGNUM:
- return rb_big_plus(v, beg);
+ return rb_big_plus(v, beg);
case T_FLOAT: {
- VALUE f = rb_check_to_float(beg);
- if (!NIL_P(f)) {
- return DBL2NUM(RFLOAT_VALUE(v) + RFLOAT_VALUE(f));
- }
+ VALUE f = rb_check_to_float(beg);
+ if (!NIL_P(f)) {
+ return DBL2NUM(RFLOAT_VALUE(v) + RFLOAT_VALUE(f));
+ }
}
default:
- return rb_funcallv(beg, id_plus, 1, &v);
+ return rb_funcallv(beg, id_plus, 1, &v);
}
return v;
@@ -1524,25 +1545,25 @@ rand_random(int argc, VALUE *argv, VALUE obj, rb_random_t *rnd)
VALUE vmax, v;
if (rb_check_arity(argc, 0, 1) == 0) {
- return rb_float_new(random_real(obj, rnd, TRUE));
+ return rb_float_new(random_real(obj, rnd, TRUE));
}
vmax = argv[0];
if (NIL_P(vmax)) return Qnil;
if (!RB_FLOAT_TYPE_P(vmax)) {
- v = rb_check_to_int(vmax);
- if (!NIL_P(v)) return rand_int(obj, rnd, v, 1);
+ v = rb_check_to_int(vmax);
+ if (!NIL_P(v)) return rand_int(obj, rnd, v, 1);
}
v = rb_check_to_float(vmax);
if (!NIL_P(v)) {
- const double max = float_value(v);
- if (max < 0.0) {
- return Qnil;
- }
- else {
- double r = random_real(obj, rnd, TRUE);
- if (max > 0.0) r *= max;
- return rb_float_new(r);
- }
+ const double max = float_value(v);
+ if (max < 0.0) {
+ return Qnil;
+ }
+ else {
+ double r = random_real(obj, rnd, TRUE);
+ if (max > 0.0) r *= max;
+ return rb_float_new(r);
+ }
}
return rand_range(obj, rnd, vmax);
}
@@ -1645,12 +1666,12 @@ rb_f_rand(int argc, VALUE *argv, VALUE obj)
if (rb_check_arity(argc, 0, 1) && !NIL_P(vmax = argv[0])) {
VALUE v = rand_range(obj, rnd, vmax);
- if (v != Qfalse) return v;
- vmax = rb_to_int(vmax);
- if (vmax != INT2FIX(0)) {
+ if (v != Qfalse) return v;
+ vmax = rb_to_int(vmax);
+ if (vmax != INT2FIX(0)) {
v = rand_int(obj, rnd, vmax, 0);
- if (!NIL_P(v)) return v;
- }
+ if (!NIL_P(v)) return v;
+ }
}
return DBL2NUM(random_real(obj, rnd, TRUE));
}
@@ -1706,7 +1727,7 @@ init_hash_salt(struct MT *mt)
int i;
for (i = 0; i < numberof(hash_salt.u32); ++i)
- hash_salt.u32[i] = genrand_int32(mt);
+ hash_salt.u32[i] = genrand_int32(mt);
}
NO_SANITIZE("unsigned-integer-overflow", extern st_index_t rb_hash_start(st_index_t h));
@@ -1782,6 +1803,9 @@ rb_reset_random_seed(void)
* PRNGs are currently implemented as a modified Mersenne Twister with a period
* of 2**19937-1. As this algorithm is _not_ for cryptographical use, you must
* use SecureRandom for security purpose, instead of this PRNG.
+ *
+ * See also Random::Formatter module that adds convenience methods to generate
+ * various forms of random data.
*/
void
@@ -1826,7 +1850,7 @@ InitVM_Random(void)
rb_define_private_method(CLASS_OF(rb_cRandom), "left", random_s_left, 0);
{
- /*
+ /*
* Generate a random number in the given range as Random does
*
* prng.random_number #=> 0.5816771641321361
@@ -1836,11 +1860,11 @@ InitVM_Random(void)
* prng.rand(1000) #=> 485
* prng.rand(1..6) #=> 3
*/
- VALUE m = rb_define_module_under(rb_cRandom, "Formatter");
- rb_include_module(base, m);
- rb_extend_object(base, m);
- rb_define_method(m, "random_number", rand_random_number, -1);
- rb_define_method(m, "rand", rand_random_number, -1);
+ VALUE m = rb_define_module_under(rb_cRandom, "Formatter");
+ rb_include_module(base, m);
+ rb_extend_object(base, m);
+ rb_define_method(m, "random_number", rand_random_number, -1);
+ rb_define_method(m, "rand", rand_random_number, -1);
}
default_rand_key = rb_ractor_local_storage_ptr_newkey(&default_rand_key_storage_type);
diff --git a/range.c b/range.c
index e45e405f5a..dbc37c1410 100644
--- a/range.c
+++ b/range.c
@@ -47,11 +47,11 @@ static void
range_init(VALUE range, VALUE beg, VALUE end, VALUE exclude_end)
{
if ((!FIXNUM_P(beg) || !FIXNUM_P(end)) && !NIL_P(beg) && !NIL_P(end)) {
- VALUE v;
+ VALUE v;
- v = rb_funcall(beg, id_cmp, 1, end);
- if (NIL_P(v))
- rb_raise(rb_eArgError, "bad value for range");
+ v = rb_funcall(beg, id_cmp, 1, end);
+ if (NIL_P(v))
+ rb_raise(rb_eArgError, "bad value for range");
}
RANGE_SET_EXCL(range, exclude_end);
@@ -78,7 +78,7 @@ range_modify(VALUE range)
rb_check_frozen(range);
/* Ranges are immutable, so that they should be initialized only once. */
if (RANGE_EXCL(range) != Qnil) {
- rb_name_err_raise("`initialize' called twice", range, ID2SYM(idInitialize));
+ rb_name_err_raise("`initialize' called twice", range, ID2SYM(idInitialize));
}
}
@@ -140,9 +140,9 @@ recursive_equal(VALUE range, VALUE obj, int recur)
{
if (recur) return Qtrue; /* Subtle! */
if (!rb_equal(RANGE_BEG(range), RANGE_BEG(obj)))
- return Qfalse;
+ return Qfalse;
if (!rb_equal(RANGE_END(range), RANGE_END(obj)))
- return Qfalse;
+ return Qfalse;
return RBOOL(EXCL(range) == EXCL(obj));
}
@@ -183,9 +183,9 @@ static VALUE
range_eq(VALUE range, VALUE obj)
{
if (range == obj)
- return Qtrue;
+ return Qtrue;
if (!rb_obj_is_kind_of(obj, rb_cRange))
- return Qfalse;
+ return Qfalse;
return rb_exec_recursive_paired(recursive_equal, range, obj, obj);
}
@@ -201,7 +201,7 @@ r_less(VALUE a, VALUE b)
VALUE r = rb_funcall(a, id_cmp, 1, b);
if (NIL_P(r))
- return INT_MAX;
+ return INT_MAX;
return rb_cmpint(r, a, b);
}
@@ -210,9 +210,9 @@ recursive_eql(VALUE range, VALUE obj, int recur)
{
if (recur) return Qtrue; /* Subtle! */
if (!rb_eql(RANGE_BEG(range), RANGE_BEG(obj)))
- return Qfalse;
+ return Qfalse;
if (!rb_eql(RANGE_END(range), RANGE_END(obj)))
- return Qfalse;
+ return Qfalse;
return RBOOL(EXCL(range) == EXCL(obj));
}
@@ -251,9 +251,9 @@ static VALUE
range_eql(VALUE range, VALUE obj)
{
if (range == obj)
- return Qtrue;
+ return Qtrue;
if (!rb_obj_is_kind_of(obj, rb_cRange))
- return Qfalse;
+ return Qfalse;
return rb_exec_recursive_paired(recursive_eql, range, obj, obj);
}
@@ -294,17 +294,17 @@ range_each_func(VALUE range, int (*func)(VALUE, VALUE), VALUE arg)
VALUE v = b;
if (EXCL(range)) {
- while (r_less(v, e) < 0) {
- if ((*func)(v, arg)) break;
- v = rb_funcallv(v, id_succ, 0, 0);
- }
+ while (r_less(v, e) < 0) {
+ if ((*func)(v, arg)) break;
+ v = rb_funcallv(v, id_succ, 0, 0);
+ }
}
else {
- while ((c = r_less(v, e)) <= 0) {
- if ((*func)(v, arg)) break;
- if (!c) break;
- v = rb_funcallv(v, id_succ, 0, 0);
- }
+ while ((c = r_less(v, e)) <= 0) {
+ if ((*func)(v, arg)) break;
+ if (!c) break;
+ v = rb_funcallv(v, id_succ, 0, 0);
+ }
}
}
@@ -314,10 +314,10 @@ step_i_iter(VALUE arg)
VALUE *iter = (VALUE *)arg;
if (FIXNUM_P(iter[0])) {
- iter[0] -= INT2FIX(1) & ~FIXNUM_FLAG;
+ iter[0] -= INT2FIX(1) & ~FIXNUM_FLAG;
}
else {
- iter[0] = rb_funcall(iter[0], '-', 1, INT2FIX(1));
+ iter[0] = rb_funcall(iter[0], '-', 1, INT2FIX(1));
}
if (iter[0] != INT2FIX(0)) return false;
iter[0] = iter[1];
@@ -328,7 +328,7 @@ static int
sym_step_i(VALUE i, VALUE arg)
{
if (step_i_iter(arg)) {
- rb_yield(rb_str_intern(i));
+ rb_yield(rb_str_intern(i));
}
return 0;
}
@@ -337,7 +337,7 @@ static int
step_i(VALUE i, VALUE arg)
{
if (step_i_iter(arg)) {
- rb_yield(i);
+ rb_yield(i);
}
return 0;
}
@@ -356,7 +356,7 @@ linear_object_p(VALUE obj)
switch (BUILTIN_TYPE(obj)) {
case T_FLOAT:
case T_BIGNUM:
- return TRUE;
+ return TRUE;
default:
break;
}
@@ -371,14 +371,14 @@ check_step_domain(VALUE step)
VALUE zero = INT2FIX(0);
int cmp;
if (!rb_obj_is_kind_of(step, rb_cNumeric)) {
- step = rb_to_int(step);
+ step = rb_to_int(step);
}
cmp = rb_cmpint(rb_funcallv(step, idCmp, 1, &zero), step, zero);
if (cmp < 0) {
- rb_raise(rb_eArgError, "step can't be negative");
+ rb_raise(rb_eArgError, "step can't be negative");
}
else if (cmp == 0) {
- rb_raise(rb_eArgError, "step can't be 0");
+ rb_raise(rb_eArgError, "step can't be 0");
}
return step;
}
@@ -389,11 +389,11 @@ range_step_size(VALUE range, VALUE args, VALUE eobj)
VALUE b = RANGE_BEG(range), e = RANGE_END(range);
VALUE step = INT2FIX(1);
if (args) {
- step = check_step_domain(RARRAY_AREF(args, 0));
+ step = check_step_domain(RARRAY_AREF(args, 0));
}
if (rb_obj_is_kind_of(b, rb_cNumeric) && rb_obj_is_kind_of(e, rb_cNumeric)) {
- return ruby_num_interval_step_size(b, e, step, EXCL(range));
+ return ruby_num_interval_step_size(b, e, step, EXCL(range));
}
return Qnil;
}
@@ -466,74 +466,78 @@ range_step(int argc, VALUE *argv, VALUE range)
VALUE iter[2] = {INT2FIX(1), step};
if (FIXNUM_P(b) && NIL_P(e) && FIXNUM_P(step)) {
- long i = FIX2LONG(b), unit = FIX2LONG(step);
- do {
- rb_yield(LONG2FIX(i));
- i += unit; /* FIXABLE+FIXABLE never overflow */
- } while (FIXABLE(i));
- b = LONG2NUM(i);
+ long i = FIX2LONG(b), unit = FIX2LONG(step);
+ do {
+ rb_yield(LONG2FIX(i));
+ i += unit; /* FIXABLE+FIXABLE never overflow */
+ } while (FIXABLE(i));
+ b = LONG2NUM(i);
- for (;; b = rb_big_plus(b, step))
- rb_yield(b);
+ for (;; b = rb_big_plus(b, step))
+ rb_yield(b);
}
else if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(step)) { /* fixnums are special */
- long end = FIX2LONG(e);
- long i, unit = FIX2LONG(step);
-
- if (!EXCL(range))
- end += 1;
- i = FIX2LONG(b);
- while (i < end) {
- rb_yield(LONG2NUM(i));
- if (i + unit < i) break;
- i += unit;
- }
+ long end = FIX2LONG(e);
+ long i, unit = FIX2LONG(step);
+
+ if (!EXCL(range))
+ end += 1;
+ i = FIX2LONG(b);
+ while (i < end) {
+ rb_yield(LONG2NUM(i));
+ if (i + unit < i) break;
+ i += unit;
+ }
}
else if (SYMBOL_P(b) && (NIL_P(e) || SYMBOL_P(e))) { /* symbols are special */
- b = rb_sym2str(b);
- if (NIL_P(e)) {
- rb_str_upto_endless_each(b, sym_step_i, (VALUE)iter);
- }
- else {
- rb_str_upto_each(b, rb_sym2str(e), EXCL(range), sym_step_i, (VALUE)iter);
- }
+ b = rb_sym2str(b);
+ if (NIL_P(e)) {
+ rb_str_upto_endless_each(b, sym_step_i, (VALUE)iter);
+ }
+ else {
+ rb_str_upto_each(b, rb_sym2str(e), EXCL(range), sym_step_i, (VALUE)iter);
+ }
}
else if (ruby_float_step(b, e, step, EXCL(range), TRUE)) {
- /* done */
+ /* done */
}
else if (rb_obj_is_kind_of(b, rb_cNumeric) ||
- !NIL_P(rb_check_to_integer(b, "to_int")) ||
- !NIL_P(rb_check_to_integer(e, "to_int"))) {
- ID op = EXCL(range) ? '<' : idLE;
- VALUE v = b;
- int i = 0;
-
- while (NIL_P(e) || RTEST(rb_funcall(v, op, 1, e))) {
- rb_yield(v);
- i++;
- v = rb_funcall(b, '+', 1, rb_funcall(INT2NUM(i), '*', 1, step));
- }
+ !NIL_P(rb_check_to_integer(b, "to_int")) ||
+ !NIL_P(rb_check_to_integer(e, "to_int"))) {
+ ID op = EXCL(range) ? '<' : idLE;
+ VALUE v = b;
+ int i = 0;
+
+ while (NIL_P(e) || RTEST(rb_funcall(v, op, 1, e))) {
+ rb_yield(v);
+ i++;
+ v = rb_funcall(b, '+', 1, rb_funcall(INT2NUM(i), '*', 1, step));
+ }
}
else {
- tmp = rb_check_string_type(b);
-
- if (!NIL_P(tmp)) {
- b = tmp;
- if (NIL_P(e)) {
- rb_str_upto_endless_each(b, step_i, (VALUE)iter);
- }
- else {
- rb_str_upto_each(b, e, EXCL(range), step_i, (VALUE)iter);
- }
- }
- else {
- if (!discrete_object_p(b)) {
- rb_raise(rb_eTypeError, "can't iterate from %s",
- rb_obj_classname(b));
- }
- range_each_func(range, step_i, (VALUE)iter);
- }
+ tmp = rb_check_string_type(b);
+
+ if (!NIL_P(tmp)) {
+ b = tmp;
+ if (NIL_P(e)) {
+ rb_str_upto_endless_each(b, step_i, (VALUE)iter);
+ }
+ else {
+ rb_str_upto_each(b, e, EXCL(range), step_i, (VALUE)iter);
+ }
+ }
+ else {
+ if (!discrete_object_p(b)) {
+ rb_raise(rb_eTypeError, "can't iterate from %s",
+ rb_obj_classname(b));
+ }
+ if (!NIL_P(e))
+ range_each_func(range, step_i, (VALUE)iter);
+ else
+ for (;; b = rb_funcallv(b, id_succ, 0, 0))
+ step_i(b, (VALUE)iter);
+ }
}
return range;
}
@@ -582,12 +586,12 @@ int64_as_double_to_num(int64_t i)
{
union int64_double convert;
if (i < 0) {
- convert.i = -i;
- return DBL2NUM(-convert.d);
+ convert.i = -i;
+ return DBL2NUM(-convert.d);
}
else {
- convert.i = i;
- return DBL2NUM(convert.d);
+ convert.i = i;
+ return DBL2NUM(convert.d);
}
}
@@ -607,7 +611,7 @@ is_integer_p(VALUE v)
VALUE is_int;
CONST_ID(id_integer_p, "integer?");
is_int = rb_check_funcall(v, id_integer_p, 0, 0);
- return RTEST(is_int) && is_int != Qundef;
+ return RTEST(is_int) && !UNDEF_P(is_int);
}
static VALUE
@@ -618,29 +622,29 @@ bsearch_integer_range(VALUE beg, VALUE end, int excl)
#define BSEARCH_CHECK(expr) \
do { \
- VALUE val = (expr); \
- VALUE v = rb_yield(val); \
- if (FIXNUM_P(v)) { \
- if (v == INT2FIX(0)) return val; \
- smaller = (SIGNED_VALUE)v < 0; \
- } \
- else if (v == Qtrue) { \
- satisfied = val; \
- smaller = 1; \
- } \
- else if (!RTEST(v)) { \
- smaller = 0; \
- } \
- else if (rb_obj_is_kind_of(v, rb_cNumeric)) { \
- int cmp = rb_cmpint(rb_funcall(v, id_cmp, 1, INT2FIX(0)), v, INT2FIX(0)); \
- if (!cmp) return val; \
- smaller = cmp < 0; \
- } \
- else { \
- rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE \
- " (must be numeric, true, false or nil)", \
- rb_obj_class(v)); \
- } \
+ VALUE val = (expr); \
+ VALUE v = rb_yield(val); \
+ if (FIXNUM_P(v)) { \
+ if (v == INT2FIX(0)) return val; \
+ smaller = (SIGNED_VALUE)v < 0; \
+ } \
+ else if (v == Qtrue) { \
+ satisfied = val; \
+ smaller = 1; \
+ } \
+ else if (!RTEST(v)) { \
+ smaller = 0; \
+ } \
+ else if (rb_obj_is_kind_of(v, rb_cNumeric)) { \
+ int cmp = rb_cmpint(rb_funcall(v, id_cmp, 1, INT2FIX(0)), v, INT2FIX(0)); \
+ if (!cmp) return val; \
+ smaller = cmp < 0; \
+ } \
+ else { \
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE \
+ " (must be numeric, true, false or nil)", \
+ rb_obj_class(v)); \
+ } \
} while (0)
VALUE low = rb_to_int(beg);
@@ -653,18 +657,18 @@ bsearch_integer_range(VALUE beg, VALUE end, int excl)
org_high = high;
while (rb_cmpint(rb_funcall(low, id_cmp, 1, high), low, high) < 0) {
- mid = rb_funcall(rb_funcall(high, '+', 1, low), id_div, 1, INT2FIX(2));
- BSEARCH_CHECK(mid);
- if (smaller) {
- high = mid;
- }
- else {
- low = rb_funcall(mid, '+', 1, INT2FIX(1));
- }
+ mid = rb_funcall(rb_funcall(high, '+', 1, low), id_div, 1, INT2FIX(2));
+ BSEARCH_CHECK(mid);
+ if (smaller) {
+ high = mid;
+ }
+ else {
+ low = rb_funcall(mid, '+', 1, INT2FIX(1));
+ }
}
if (rb_equal(low, org_high)) {
- BSEARCH_CHECK(low);
- if (!smaller) return Qnil;
+ BSEARCH_CHECK(low);
+ if (!smaller) return Qnil;
}
return satisfied;
}
@@ -701,25 +705,25 @@ range_bsearch(VALUE range)
#define BSEARCH(conv) \
do { \
- RETURN_ENUMERATOR(range, 0, 0); \
- if (EXCL(range)) high--; \
- org_high = high; \
- while (low < high) { \
- mid = ((high < 0) == (low < 0)) ? low + ((high - low) / 2) \
- : (low < -high) ? -((-1 - low - high)/2 + 1) : (low + high) / 2; \
- BSEARCH_CHECK(conv(mid)); \
- if (smaller) { \
- high = mid; \
- } \
- else { \
- low = mid + 1; \
- } \
- } \
- if (low == org_high) { \
- BSEARCH_CHECK(conv(low)); \
- if (!smaller) return Qnil; \
- } \
- return satisfied; \
+ RETURN_ENUMERATOR(range, 0, 0); \
+ if (EXCL(range)) high--; \
+ org_high = high; \
+ while (low < high) { \
+ mid = ((high < 0) == (low < 0)) ? low + ((high - low) / 2) \
+ : (low < -high) ? -((-1 - low - high)/2 + 1) : (low + high) / 2; \
+ BSEARCH_CHECK(conv(mid)); \
+ if (smaller) { \
+ high = mid; \
+ } \
+ else { \
+ low = mid + 1; \
+ } \
+ } \
+ if (low == org_high) { \
+ BSEARCH_CHECK(conv(low)); \
+ if (!smaller) return Qnil; \
+ } \
+ return satisfied; \
} while (0)
@@ -727,49 +731,49 @@ range_bsearch(VALUE range)
end = RANGE_END(range);
if (FIXNUM_P(beg) && FIXNUM_P(end)) {
- long low = FIX2LONG(beg);
- long high = FIX2LONG(end);
- long mid, org_high;
- BSEARCH(INT2FIX);
+ long low = FIX2LONG(beg);
+ long high = FIX2LONG(end);
+ long mid, org_high;
+ BSEARCH(INT2FIX);
}
#if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T)
else if (RB_FLOAT_TYPE_P(beg) || RB_FLOAT_TYPE_P(end)) {
- int64_t low = double_as_int64(NIL_P(beg) ? -HUGE_VAL : RFLOAT_VALUE(rb_Float(beg)));
- int64_t high = double_as_int64(NIL_P(end) ? HUGE_VAL : RFLOAT_VALUE(rb_Float(end)));
- int64_t mid, org_high;
- BSEARCH(int64_as_double_to_num);
+ int64_t low = double_as_int64(NIL_P(beg) ? -HUGE_VAL : RFLOAT_VALUE(rb_Float(beg)));
+ int64_t high = double_as_int64(NIL_P(end) ? HUGE_VAL : RFLOAT_VALUE(rb_Float(end)));
+ int64_t mid, org_high;
+ BSEARCH(int64_as_double_to_num);
}
#endif
else if (is_integer_p(beg) && is_integer_p(end)) {
- RETURN_ENUMERATOR(range, 0, 0);
- return bsearch_integer_range(beg, end, EXCL(range));
+ RETURN_ENUMERATOR(range, 0, 0);
+ return bsearch_integer_range(beg, end, EXCL(range));
}
else if (is_integer_p(beg) && NIL_P(end)) {
- VALUE diff = LONG2FIX(1);
- RETURN_ENUMERATOR(range, 0, 0);
- while (1) {
- VALUE mid = rb_funcall(beg, '+', 1, diff);
- BSEARCH_CHECK(mid);
- if (smaller) {
- return bsearch_integer_range(beg, mid, 0);
- }
- diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
- }
+ VALUE diff = LONG2FIX(1);
+ RETURN_ENUMERATOR(range, 0, 0);
+ while (1) {
+ VALUE mid = rb_funcall(beg, '+', 1, diff);
+ BSEARCH_CHECK(mid);
+ if (smaller) {
+ return bsearch_integer_range(beg, mid, 0);
+ }
+ diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
+ }
}
else if (NIL_P(beg) && is_integer_p(end)) {
- VALUE diff = LONG2FIX(-1);
- RETURN_ENUMERATOR(range, 0, 0);
- while (1) {
- VALUE mid = rb_funcall(end, '+', 1, diff);
- BSEARCH_CHECK(mid);
- if (!smaller) {
- return bsearch_integer_range(mid, end, 0);
- }
- diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
- }
+ VALUE diff = LONG2FIX(-1);
+ RETURN_ENUMERATOR(range, 0, 0);
+ while (1) {
+ VALUE mid = rb_funcall(end, '+', 1, diff);
+ BSEARCH_CHECK(mid);
+ if (!smaller) {
+ return bsearch_integer_range(mid, end, 0);
+ }
+ diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
+ }
}
else {
- rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg));
+ rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg));
}
return range;
}
@@ -809,14 +813,16 @@ range_size(VALUE range)
VALUE b = RANGE_BEG(range), e = RANGE_END(range);
if (rb_obj_is_kind_of(b, rb_cNumeric)) {
if (rb_obj_is_kind_of(e, rb_cNumeric)) {
- return ruby_num_interval_step_size(b, e, INT2FIX(1), EXCL(range));
+ return ruby_num_interval_step_size(b, e, INT2FIX(1), EXCL(range));
}
if (NIL_P(e)) {
return DBL2NUM(HUGE_VAL);
}
}
else if (NIL_P(b)) {
- return DBL2NUM(HUGE_VAL);
+ if (rb_obj_is_kind_of(e, rb_cNumeric)) {
+ return DBL2NUM(HUGE_VAL);
+ }
}
return Qnil;
@@ -840,7 +846,7 @@ static VALUE
range_to_a(VALUE range)
{
if (NIL_P(RANGE_END(range))) {
- rb_raise(rb_eRangeError, "cannot convert endless range to an array");
+ rb_raise(rb_eRangeError, "cannot convert endless range to an array");
}
return rb_call_super(0, 0);
}
@@ -918,78 +924,78 @@ range_each(VALUE range)
return range_each_fixnum_loop(beg, end, range);
}
else if (RB_INTEGER_TYPE_P(beg) && (NIL_P(end) || RB_INTEGER_TYPE_P(end))) {
- if (SPECIAL_CONST_P(end) || RBIGNUM_POSITIVE_P(end)) { /* end >= FIXNUM_MIN */
- if (!FIXNUM_P(beg)) {
- if (RBIGNUM_NEGATIVE_P(beg)) {
- do {
- rb_yield(beg);
- } while (!FIXNUM_P(beg = rb_big_plus(beg, INT2FIX(1))));
+ if (SPECIAL_CONST_P(end) || RBIGNUM_POSITIVE_P(end)) { /* end >= FIXNUM_MIN */
+ if (!FIXNUM_P(beg)) {
+ if (RBIGNUM_NEGATIVE_P(beg)) {
+ do {
+ rb_yield(beg);
+ } while (!FIXNUM_P(beg = rb_big_plus(beg, INT2FIX(1))));
if (NIL_P(end)) range_each_fixnum_endless(beg);
if (FIXNUM_P(end)) return range_each_fixnum_loop(beg, end, range);
- }
- else {
+ }
+ else {
if (NIL_P(end)) range_each_bignum_endless(beg);
- if (FIXNUM_P(end)) return range;
- }
- }
- if (FIXNUM_P(beg)) {
- i = FIX2LONG(beg);
- do {
- rb_yield(LONG2FIX(i));
- } while (POSFIXABLE(++i));
- beg = LONG2NUM(i);
- }
- ASSUME(!FIXNUM_P(beg));
- ASSUME(!SPECIAL_CONST_P(end));
- }
- if (!FIXNUM_P(beg) && RBIGNUM_SIGN(beg) == RBIGNUM_SIGN(end)) {
- if (EXCL(range)) {
- while (rb_big_cmp(beg, end) == INT2FIX(-1)) {
- rb_yield(beg);
- beg = rb_big_plus(beg, INT2FIX(1));
- }
- }
- else {
- VALUE c;
- while ((c = rb_big_cmp(beg, end)) != INT2FIX(1)) {
- rb_yield(beg);
- if (c == INT2FIX(0)) break;
- beg = rb_big_plus(beg, INT2FIX(1));
- }
- }
- }
+ if (FIXNUM_P(end)) return range;
+ }
+ }
+ if (FIXNUM_P(beg)) {
+ i = FIX2LONG(beg);
+ do {
+ rb_yield(LONG2FIX(i));
+ } while (POSFIXABLE(++i));
+ beg = LONG2NUM(i);
+ }
+ ASSUME(!FIXNUM_P(beg));
+ ASSUME(!SPECIAL_CONST_P(end));
+ }
+ if (!FIXNUM_P(beg) && RBIGNUM_SIGN(beg) == RBIGNUM_SIGN(end)) {
+ if (EXCL(range)) {
+ while (rb_big_cmp(beg, end) == INT2FIX(-1)) {
+ rb_yield(beg);
+ beg = rb_big_plus(beg, INT2FIX(1));
+ }
+ }
+ else {
+ VALUE c;
+ while ((c = rb_big_cmp(beg, end)) != INT2FIX(1)) {
+ rb_yield(beg);
+ if (c == INT2FIX(0)) break;
+ beg = rb_big_plus(beg, INT2FIX(1));
+ }
+ }
+ }
}
else if (SYMBOL_P(beg) && (NIL_P(end) || SYMBOL_P(end))) { /* symbols are special */
- beg = rb_sym2str(beg);
- if (NIL_P(end)) {
- rb_str_upto_endless_each(beg, sym_each_i, 0);
- }
- else {
- rb_str_upto_each(beg, rb_sym2str(end), EXCL(range), sym_each_i, 0);
- }
+ beg = rb_sym2str(beg);
+ if (NIL_P(end)) {
+ rb_str_upto_endless_each(beg, sym_each_i, 0);
+ }
+ else {
+ rb_str_upto_each(beg, rb_sym2str(end), EXCL(range), sym_each_i, 0);
+ }
}
else {
- VALUE tmp = rb_check_string_type(beg);
-
- if (!NIL_P(tmp)) {
- if (!NIL_P(end)) {
- rb_str_upto_each(tmp, end, EXCL(range), each_i, 0);
- }
- else {
- rb_str_upto_endless_each(tmp, each_i, 0);
- }
- }
- else {
- if (!discrete_object_p(beg)) {
- rb_raise(rb_eTypeError, "can't iterate from %s",
- rb_obj_classname(beg));
- }
- if (!NIL_P(end))
- range_each_func(range, each_i, 0);
- else
- for (;; beg = rb_funcallv(beg, id_succ, 0, 0))
- rb_yield(beg);
- }
+ VALUE tmp = rb_check_string_type(beg);
+
+ if (!NIL_P(tmp)) {
+ if (!NIL_P(end)) {
+ rb_str_upto_each(tmp, end, EXCL(range), each_i, 0);
+ }
+ else {
+ rb_str_upto_endless_each(tmp, each_i, 0);
+ }
+ }
+ else {
+ if (!discrete_object_p(beg)) {
+ rb_raise(rb_eTypeError, "can't iterate from %s",
+ rb_obj_classname(beg));
+ }
+ if (!NIL_P(end))
+ range_each_func(range, each_i, 0);
+ else
+ for (;; beg = rb_funcallv(beg, id_succ, 0, 0))
+ rb_yield(beg);
+ }
}
return range;
}
@@ -1041,7 +1047,7 @@ first_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, cbarg))
long n = NUM2LONG(ary[0]);
if (n <= 0) {
- rb_iter_break();
+ rb_iter_break();
}
rb_ary_push(ary[1], i);
n--;
@@ -1107,10 +1113,6 @@ rb_int_range_last(int argc, VALUE *argv, VALUE range)
x = EXCL(range);
len_1 = rb_int_minus(e, b);
- if (FIXNUM_ZERO_P(len_1) || rb_num_negative_p(len_1)) {
- return rb_ary_new_capa(0);
- }
-
if (x) {
e = rb_int_minus(e, ONE);
len = len_1;
@@ -1119,6 +1121,10 @@ rb_int_range_last(int argc, VALUE *argv, VALUE range)
len = rb_int_plus(len_1, ONE);
}
+ if (FIXNUM_ZERO_P(len) || rb_num_negative_p(len)) {
+ return rb_ary_new_capa(0);
+ }
+
rb_scan_args(argc, argv, "1", &nv);
n = NUM2LONG(nv);
if (n < 0) {
@@ -1282,27 +1288,26 @@ static VALUE
range_min(int argc, VALUE *argv, VALUE range)
{
if (NIL_P(RANGE_BEG(range))) {
- rb_raise(rb_eRangeError, "cannot get the minimum of beginless range");
+ rb_raise(rb_eRangeError, "cannot get the minimum of beginless range");
}
if (rb_block_given_p()) {
if (NIL_P(RANGE_END(range))) {
rb_raise(rb_eRangeError, "cannot get the minimum of endless range with custom comparison method");
}
- return rb_call_super(argc, argv);
+ return rb_call_super(argc, argv);
}
else if (argc != 0) {
- return range_first(argc, argv, range);
+ return range_first(argc, argv, range);
}
else {
- struct cmp_opt_data cmp_opt = { 0, 0 };
- VALUE b = RANGE_BEG(range);
- VALUE e = RANGE_END(range);
- int c = NIL_P(e) ? -1 : OPTIMIZED_CMP(b, e, cmp_opt);
+ VALUE b = RANGE_BEG(range);
+ VALUE e = RANGE_END(range);
+ int c = NIL_P(e) ? -1 : OPTIMIZED_CMP(b, e);
- if (c > 0 || (c == 0 && EXCL(range)))
- return Qnil;
- return b;
+ if (c > 0 || (c == 0 && EXCL(range)))
+ return Qnil;
+ return b;
}
}
@@ -1394,7 +1399,7 @@ range_max(int argc, VALUE *argv, VALUE range)
int nm = FIXNUM_P(e) || rb_obj_is_kind_of(e, rb_cNumeric);
if (NIL_P(RANGE_END(range))) {
- rb_raise(rb_eRangeError, "cannot get the maximum of endless range");
+ rb_raise(rb_eRangeError, "cannot get the maximum of endless range");
}
VALUE b = RANGE_BEG(range);
@@ -1406,8 +1411,7 @@ range_max(int argc, VALUE *argv, VALUE range)
return rb_call_super(argc, argv);
}
else {
- struct cmp_opt_data cmp_opt = { 0, 0 };
- int c = NIL_P(b) ? -1 : OPTIMIZED_CMP(b, e, cmp_opt);
+ int c = NIL_P(b) ? -1 : OPTIMIZED_CMP(b, e);
if (c > 0)
return Qnil;
@@ -1493,22 +1497,22 @@ rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp)
int excl;
if (rb_obj_is_kind_of(range, rb_cRange)) {
- b = RANGE_BEG(range);
- e = RANGE_END(range);
- excl = EXCL(range);
+ b = RANGE_BEG(range);
+ e = RANGE_END(range);
+ excl = EXCL(range);
}
else if (RTEST(rb_obj_is_kind_of(range, rb_cArithSeq))) {
return (int)Qfalse;
}
else {
- VALUE x;
- b = rb_check_funcall(range, id_beg, 0, 0);
- if (b == Qundef) return (int)Qfalse;
- e = rb_check_funcall(range, id_end, 0, 0);
- if (e == Qundef) return (int)Qfalse;
- x = rb_check_funcall(range, rb_intern("exclude_end?"), 0, 0);
- if (x == Qundef) return (int)Qfalse;
- excl = RTEST(x);
+ VALUE x;
+ b = rb_check_funcall(range, id_beg, 0, 0);
+ if (UNDEF_P(b)) return (int)Qfalse;
+ e = rb_check_funcall(range, id_end, 0, 0);
+ if (UNDEF_P(e)) return (int)Qfalse;
+ x = rb_check_funcall(range, rb_intern("exclude_end?"), 0, 0);
+ if (UNDEF_P(x)) return (int)Qfalse;
+ excl = RTEST(x);
}
*begp = b;
*endp = e;
@@ -1632,7 +1636,7 @@ inspect_range(VALUE range, VALUE dummy, int recur)
VALUE str, str2 = Qundef;
if (recur) {
- return rb_str_new2(EXCL(range) ? "(... ... ...)" : "(... .. ...)");
+ return rb_str_new2(EXCL(range) ? "(... ... ...)" : "(... .. ...)");
}
if (!NIL_P(RANGE_BEG(range)) || NIL_P(RANGE_END(range))) {
str = rb_str_dup(rb_inspect(RANGE_BEG(range)));
@@ -1644,7 +1648,7 @@ inspect_range(VALUE range, VALUE dummy, int recur)
if (NIL_P(RANGE_BEG(range)) || !NIL_P(RANGE_END(range))) {
str2 = rb_inspect(RANGE_END(range));
}
- if (str2 != Qundef) rb_str_append(str, str2);
+ if (!UNDEF_P(str2)) rb_str_append(str, str2);
return str;
}
@@ -1677,7 +1681,9 @@ range_inspect(VALUE range)
return rb_exec_recursive(inspect_range, range, 0);
}
-static VALUE range_include_internal(VALUE range, VALUE val, int string_use_cover);
+static VALUE range_include_internal(VALUE range, VALUE val);
+static VALUE range_string_cover_internal(VALUE range, VALUE val);
+VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive);
/*
* call-seq:
@@ -1721,8 +1727,8 @@ static VALUE range_include_internal(VALUE range, VALUE val, int string_use_cover
static VALUE
range_eqq(VALUE range, VALUE val)
{
- VALUE ret = range_include_internal(range, val, 1);
- if (ret != Qundef) return ret;
+ VALUE ret = range_string_cover_internal(range, val);
+ if (!UNDEF_P(ret)) return ret;
return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val);
}
@@ -1761,49 +1767,102 @@ range_eqq(VALUE range, VALUE val)
static VALUE
range_include(VALUE range, VALUE val)
{
- VALUE ret = range_include_internal(range, val, 0);
- if (ret != Qundef) return ret;
+ VALUE ret = range_include_internal(range, val);
+ if (!UNDEF_P(ret)) return ret;
return rb_call_super(1, &val);
}
+static inline bool
+range_integer_edge_p(VALUE beg, VALUE end)
+{
+ return (!NIL_P(rb_check_to_integer(beg, "to_int")) ||
+ !NIL_P(rb_check_to_integer(end, "to_int")));
+}
+
+static inline bool
+range_string_edge_p(VALUE beg, VALUE end)
+{
+ return RB_TYPE_P(beg, T_STRING) || RB_TYPE_P(end, T_STRING);
+}
+
+static inline bool
+range_string_range_p(VALUE beg, VALUE end)
+{
+ return RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING);
+}
+
+static inline VALUE
+range_include_fallback(VALUE beg, VALUE end, VALUE val)
+{
+ if (NIL_P(beg) && NIL_P(end)) {
+ if (linear_object_p(val)) return Qtrue;
+ }
+
+ if (NIL_P(beg) || NIL_P(end)) {
+ rb_raise(rb_eTypeError, "cannot determine inclusion in beginless/endless ranges");
+ }
+
+ return Qundef;
+}
+
static VALUE
-range_include_internal(VALUE range, VALUE val, int string_use_cover)
+range_string_cover_internal(VALUE range, VALUE val)
{
VALUE beg = RANGE_BEG(range);
VALUE end = RANGE_END(range);
int nv = FIXNUM_P(beg) || FIXNUM_P(end) ||
- linear_object_p(beg) || linear_object_p(end);
+ linear_object_p(beg) || linear_object_p(end);
- if (nv ||
- !NIL_P(rb_check_to_integer(beg, "to_int")) ||
- !NIL_P(rb_check_to_integer(end, "to_int"))) {
- return r_cover_p(range, beg, end, val);
+ if (nv || range_integer_edge_p(beg, end)) {
+ return r_cover_p(range, beg, end, val);
}
- else if (RB_TYPE_P(beg, T_STRING) || RB_TYPE_P(end, T_STRING)) {
- if (RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING)) {
- if (string_use_cover) {
- return r_cover_p(range, beg, end, val);
- }
- else {
- VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive);
- return rb_str_include_range_p(beg, end, val, RANGE_EXCL(range));
- }
+ else if (range_string_edge_p(beg, end)) {
+ if (range_string_range_p(beg, end)) {
+ return r_cover_p(range, beg, end, val);
}
- else if (NIL_P(beg)) {
- VALUE r = rb_funcall(val, id_cmp, 1, end);
- if (NIL_P(r)) return Qfalse;
+ if (NIL_P(beg)) {
+unbounded_begin:;
+ VALUE r = rb_funcall(val, id_cmp, 1, end);
+ if (NIL_P(r)) return Qfalse;
if (RANGE_EXCL(range)) {
return RBOOL(rb_cmpint(r, val, end) < 0);
}
return RBOOL(rb_cmpint(r, val, end) <= 0);
}
- else if (NIL_P(end)) {
- VALUE r = rb_funcall(beg, id_cmp, 1, val);
- if (NIL_P(r)) return Qfalse;
+ else if (NIL_P(end)) {
+unbounded_end:;
+ VALUE r = rb_funcall(beg, id_cmp, 1, val);
+ if (NIL_P(r)) return Qfalse;
return RBOOL(rb_cmpint(r, beg, val) <= 0);
- }
+ }
}
- return Qundef;
+
+ if (!NIL_P(beg) && NIL_P(end)) {
+ goto unbounded_end;
+ }
+ if (NIL_P(beg) && !NIL_P(end)) {
+ goto unbounded_begin;
+ }
+
+ return range_include_fallback(beg, end, val);
+}
+
+static VALUE
+range_include_internal(VALUE range, VALUE val)
+{
+ VALUE beg = RANGE_BEG(range);
+ VALUE end = RANGE_END(range);
+ int nv = FIXNUM_P(beg) || FIXNUM_P(end) ||
+ linear_object_p(beg) || linear_object_p(end);
+
+ if (nv || range_integer_edge_p(beg, end)) {
+ return r_cover_p(range, beg, end, val);
+ }
+ else if (range_string_range_p(beg, end)) {
+ return rb_str_include_range_p(beg, end, val, RANGE_EXCL(range));
+ }
+
+ return range_include_fallback(beg, end, val);
}
static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
@@ -1888,48 +1947,48 @@ static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
* - An internal call to <tt><=></tt> returns +nil+;
* that is, the operands are not comparable.
*
- * Beginless ranges cover all values of the same type before the end,
- * excluding the end for exclusive ranges. Beginless ranges cover
- * ranges that end before the end of the beginless range, or at the
- * end of the beginless range for inclusive ranges.
- *
- * (..2).cover?(1) # => true
- * (..2).cover?(2) # => true
- * (..2).cover?(3) # => false
- * (...2).cover?(2) # => false
- * (..2).cover?("2") # => false
- * (..2).cover?(..2) # => true
- * (..2).cover?(...2) # => true
- * (..2).cover?(.."2") # => false
- * (...2).cover?(..2) # => false
- *
- * Endless ranges cover all values of the same type after the
- * beginning. Endless exclusive ranges do not cover endless
- * inclusive ranges.
- *
- * (2..).cover?(1) # => false
- * (2..).cover?(3) # => true
- * (2...).cover?(3) # => true
- * (2..).cover?(2) # => true
- * (2..).cover?("2") # => false
- * (2..).cover?(2..) # => true
- * (2..).cover?(2...) # => true
- * (2..).cover?("2"..) # => false
- * (2...).cover?(2..) # => false
- * (2...).cover?(3...) # => true
- * (2...).cover?(3..) # => false
- * (3..).cover?(2..) # => false
- *
- * Ranges that are both beginless and endless cover all values and
- * ranges, and return true for all arguments, with the exception that
- * beginless and endless exclusive ranges do not cover endless
- * inclusive ranges.
- *
- * (nil...).cover?(Object.new) # => true
- * (nil...).cover?(nil...) # => true
- * (nil..).cover?(nil...) # => true
- * (nil...).cover?(nil..) # => false
- * (nil...).cover?(1..) # => false
+ * Beginless ranges cover all values of the same type before the end,
+ * excluding the end for exclusive ranges. Beginless ranges cover
+ * ranges that end before the end of the beginless range, or at the
+ * end of the beginless range for inclusive ranges.
+ *
+ * (..2).cover?(1) # => true
+ * (..2).cover?(2) # => true
+ * (..2).cover?(3) # => false
+ * (...2).cover?(2) # => false
+ * (..2).cover?("2") # => false
+ * (..2).cover?(..2) # => true
+ * (..2).cover?(...2) # => true
+ * (..2).cover?(.."2") # => false
+ * (...2).cover?(..2) # => false
+ *
+ * Endless ranges cover all values of the same type after the
+ * beginning. Endless exclusive ranges do not cover endless
+ * inclusive ranges.
+ *
+ * (2..).cover?(1) # => false
+ * (2..).cover?(3) # => true
+ * (2...).cover?(3) # => true
+ * (2..).cover?(2) # => true
+ * (2..).cover?("2") # => false
+ * (2..).cover?(2..) # => true
+ * (2..).cover?(2...) # => true
+ * (2..).cover?("2"..) # => false
+ * (2...).cover?(2..) # => false
+ * (2...).cover?(3...) # => true
+ * (2...).cover?(3..) # => false
+ * (3..).cover?(2..) # => false
+ *
+ * Ranges that are both beginless and endless cover all values and
+ * ranges, and return true for all arguments, with the exception that
+ * beginless and endless exclusive ranges do not cover endless
+ * inclusive ranges.
+ *
+ * (nil...).cover?(Object.new) # => true
+ * (nil...).cover?(nil...) # => true
+ * (nil..).cover?(nil...) # => true
+ * (nil...).cover?(nil..) # => false
+ * (nil...).cover?(1..) # => false
*
* Related: Range#include?.
*
@@ -2000,9 +2059,9 @@ static VALUE
r_cover_p(VALUE range, VALUE beg, VALUE end, VALUE val)
{
if (NIL_P(beg) || r_less(beg, val) <= 0) {
- int excl = EXCL(range);
- if (NIL_P(end) || r_less(val, end) <= -excl)
- return Qtrue;
+ int excl = EXCL(range);
+ if (NIL_P(end) || r_less(val, end) <= -excl)
+ return Qtrue;
}
return Qfalse;
}
@@ -2032,7 +2091,7 @@ range_loader(VALUE range, VALUE obj)
end = rb_ivar_get(obj, id_end);
excl = rb_ivar_get(obj, id_excl);
if (!NIL_P(excl)) {
- range_init(range, beg, end, RBOOL(RTEST(excl)));
+ range_init(range, beg, end, RBOOL(RTEST(excl)));
}
return range;
}
diff --git a/rational.c b/rational.c
index 093de5fa5b..dfe2ad74eb 100644
--- a/rational.c
+++ b/rational.c
@@ -62,9 +62,9 @@ inline static VALUE
f_add(VALUE x, VALUE y)
{
if (FIXNUM_ZERO_P(y))
- return x;
+ return x;
if (FIXNUM_ZERO_P(x))
- return y;
+ return y;
if (RB_INTEGER_TYPE_P(x))
return rb_int_plus(x, y);
return rb_funcall(x, '+', 1, y);
@@ -74,9 +74,9 @@ inline static VALUE
f_div(VALUE x, VALUE y)
{
if (y == ONE)
- return x;
+ return x;
if (RB_INTEGER_TYPE_P(x))
- return rb_int_div(x, y);
+ return rb_int_div(x, y);
return rb_funcall(x, '/', 1, y);
}
@@ -84,7 +84,7 @@ inline static int
f_lt_p(VALUE x, VALUE y)
{
if (FIXNUM_P(x) && FIXNUM_P(y))
- return (SIGNED_VALUE)x < (SIGNED_VALUE)y;
+ return (SIGNED_VALUE)x < (SIGNED_VALUE)y;
if (RB_INTEGER_TYPE_P(x)) {
VALUE r = rb_int_cmp(x, y);
if (!NIL_P(r)) return rb_int_negative_p(r);
@@ -107,13 +107,13 @@ inline static VALUE
f_mul(VALUE x, VALUE y)
{
if (FIXNUM_ZERO_P(y) && RB_INTEGER_TYPE_P(x))
- return ZERO;
+ return ZERO;
if (y == ONE) return x;
if (FIXNUM_ZERO_P(x) && RB_INTEGER_TYPE_P(y))
- return ZERO;
+ return ZERO;
if (x == ONE) return y;
else if (RB_INTEGER_TYPE_P(x))
- return rb_int_mul(x, y);
+ return rb_int_mul(x, y);
return rb_funcall(x, '*', 1, y);
}
@@ -121,7 +121,7 @@ inline static VALUE
f_sub(VALUE x, VALUE y)
{
if (FIXNUM_P(y) && FIXNUM_ZERO_P(y))
- return x;
+ return x;
return rb_funcall(x, '-', 1, y);
}
@@ -129,7 +129,7 @@ inline static VALUE
f_abs(VALUE x)
{
if (RB_INTEGER_TYPE_P(x))
- return rb_int_abs(x);
+ return rb_int_abs(x);
return rb_funcall(x, id_abs, 0);
}
@@ -144,7 +144,7 @@ inline static VALUE
f_to_i(VALUE x)
{
if (RB_TYPE_P(x, T_STRING))
- return rb_str_to_inum(x, 10, 0);
+ return rb_str_to_inum(x, 10, 0);
return rb_funcall(x, id_to_i, 0);
}
@@ -152,7 +152,7 @@ inline static int
f_eqeq_p(VALUE x, VALUE y)
{
if (FIXNUM_P(x) && FIXNUM_P(y))
- return x == y;
+ return x == y;
if (RB_INTEGER_TYPE_P(x))
return RTEST(rb_int_equal(x, y));
return (int)rb_equal(x, y);
@@ -162,7 +162,7 @@ inline static VALUE
f_idiv(VALUE x, VALUE y)
{
if (RB_INTEGER_TYPE_P(x))
- return rb_int_idiv(x, y);
+ return rb_int_idiv(x, y);
return rb_funcall(x, id_idiv, 1, y);
}
@@ -172,12 +172,12 @@ inline static int
f_zero_p(VALUE x)
{
if (RB_INTEGER_TYPE_P(x)) {
- return FIXNUM_ZERO_P(x);
+ return FIXNUM_ZERO_P(x);
}
else if (RB_TYPE_P(x, T_RATIONAL)) {
- VALUE num = RRATIONAL(x)->num;
+ VALUE num = RRATIONAL(x)->num;
- return FIXNUM_ZERO_P(num);
+ return FIXNUM_ZERO_P(num);
}
return (int)rb_equal(x, ZERO);
}
@@ -188,13 +188,13 @@ inline static int
f_one_p(VALUE x)
{
if (RB_INTEGER_TYPE_P(x)) {
- return x == LONG2FIX(1);
+ return x == LONG2FIX(1);
}
else if (RB_TYPE_P(x, T_RATIONAL)) {
- VALUE num = RRATIONAL(x)->num;
- VALUE den = RRATIONAL(x)->den;
+ VALUE num = RRATIONAL(x)->num;
+ VALUE den = RRATIONAL(x)->den;
- return num == LONG2FIX(1) && den == LONG2FIX(1);
+ return num == LONG2FIX(1) && den == LONG2FIX(1);
}
return (int)rb_equal(x, ONE);
}
@@ -203,16 +203,16 @@ inline static int
f_minus_one_p(VALUE x)
{
if (RB_INTEGER_TYPE_P(x)) {
- return x == LONG2FIX(-1);
+ return x == LONG2FIX(-1);
}
else if (RB_BIGNUM_TYPE_P(x)) {
- return Qfalse;
+ return Qfalse;
}
else if (RB_TYPE_P(x, T_RATIONAL)) {
- VALUE num = RRATIONAL(x)->num;
- VALUE den = RRATIONAL(x)->den;
+ VALUE num = RRATIONAL(x)->num;
+ VALUE den = RRATIONAL(x)->den;
- return num == LONG2FIX(-1) && den == LONG2FIX(1);
+ return num == LONG2FIX(-1) && den == LONG2FIX(1);
}
return (int)rb_equal(x, INT2FIX(-1));
}
@@ -295,35 +295,35 @@ i_gcd(long x, long y)
int shift;
if (x < 0)
- x = -x;
+ x = -x;
if (y < 0)
- y = -y;
+ y = -y;
if (x == 0)
- return y;
+ return y;
if (y == 0)
- return x;
+ return x;
u = (unsigned long)x;
v = (unsigned long)y;
for (shift = 0; ((u | v) & 1) == 0; ++shift) {
- u >>= 1;
- v >>= 1;
+ u >>= 1;
+ v >>= 1;
}
while ((u & 1) == 0)
- u >>= 1;
+ u >>= 1;
do {
- while ((v & 1) == 0)
- v >>= 1;
-
- if (u > v) {
- t = v;
- v = u;
- u = t;
- }
- v = v - u;
+ while ((v & 1) == 0)
+ v >>= 1;
+
+ if (u > v) {
+ t = v;
+ v = u;
+ u = t;
+ }
+ v = v - u;
} while (v != 0);
return (long)(u << shift);
@@ -335,28 +335,28 @@ f_gcd_normal(VALUE x, VALUE y)
VALUE z;
if (FIXNUM_P(x) && FIXNUM_P(y))
- return LONG2NUM(i_gcd(FIX2LONG(x), FIX2LONG(y)));
+ return LONG2NUM(i_gcd(FIX2LONG(x), FIX2LONG(y)));
if (INT_NEGATIVE_P(x))
- x = rb_int_uminus(x);
+ x = rb_int_uminus(x);
if (INT_NEGATIVE_P(y))
- y = rb_int_uminus(y);
+ y = rb_int_uminus(y);
if (INT_ZERO_P(x))
- return y;
+ return y;
if (INT_ZERO_P(y))
- return x;
+ return x;
for (;;) {
- if (FIXNUM_P(x)) {
- if (FIXNUM_ZERO_P(x))
- return y;
- if (FIXNUM_P(y))
- return LONG2NUM(i_gcd(FIX2LONG(x), FIX2LONG(y)));
- }
- z = x;
- x = rb_int_modulo(y, x);
- y = z;
+ if (FIXNUM_P(x)) {
+ if (FIXNUM_ZERO_P(x))
+ return y;
+ if (FIXNUM_P(y))
+ return LONG2NUM(i_gcd(FIX2LONG(x), FIX2LONG(y)));
+ }
+ z = x;
+ x = rb_int_modulo(y, x);
+ y = z;
}
/* NOTREACHED */
}
@@ -389,8 +389,8 @@ f_gcd(VALUE x, VALUE y)
{
VALUE r = f_gcd_orig(x, y);
if (f_nonzero_p(r)) {
- assert(f_zero_p(f_mod(x, r)));
- assert(f_zero_p(f_mod(y, r)));
+ assert(f_zero_p(f_mod(x, r)));
+ assert(f_zero_p(f_mod(y, r)));
}
return r;
}
@@ -400,7 +400,7 @@ inline static VALUE
f_lcm(VALUE x, VALUE y)
{
if (INT_ZERO_P(x) || INT_ZERO_P(y))
- return ZERO;
+ return ZERO;
return f_abs(f_mul(f_div(x, f_gcd(x, y)), y));
}
@@ -438,8 +438,8 @@ inline static void
nurat_int_check(VALUE num)
{
if (!RB_INTEGER_TYPE_P(num)) {
- if (!k_numeric_p(num) || !f_integer_p(num))
- rb_raise(rb_eTypeError, "not an integer");
+ if (!k_numeric_p(num) || !f_integer_p(num))
+ rb_raise(rb_eTypeError, "not an integer");
}
}
@@ -448,7 +448,7 @@ nurat_int_value(VALUE num)
{
nurat_int_check(num);
if (!k_integer_p(num))
- num = f_to_i(num);
+ num = f_to_i(num);
return num;
}
@@ -625,14 +625,14 @@ f_imul(long a, long b)
VALUE r;
if (a == 0 || b == 0)
- return ZERO;
+ return ZERO;
else if (a == 1)
- return LONG2NUM(b);
+ return LONG2NUM(b);
else if (b == 1)
- return LONG2NUM(a);
+ return LONG2NUM(a);
if (MUL_OVERFLOW_LONG_P(a, b))
- r = rb_big_mul(rb_int2big(a), rb_int2big(b));
+ r = rb_big_mul(rb_int2big(a), rb_int2big(b));
else
r = LONG2NUM(a * b);
return r;
@@ -656,46 +656,46 @@ f_addsub(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k)
VALUE num, den;
if (FIXNUM_P(anum) && FIXNUM_P(aden) &&
- FIXNUM_P(bnum) && FIXNUM_P(bden)) {
- long an = FIX2LONG(anum);
- long ad = FIX2LONG(aden);
- long bn = FIX2LONG(bnum);
- long bd = FIX2LONG(bden);
- long ig = i_gcd(ad, bd);
-
- VALUE g = LONG2NUM(ig);
- VALUE a = f_imul(an, bd / ig);
- VALUE b = f_imul(bn, ad / ig);
- VALUE c;
-
- if (k == '+')
- c = rb_int_plus(a, b);
- else
- c = rb_int_minus(a, b);
-
- b = rb_int_idiv(aden, g);
- g = f_gcd(c, g);
- num = rb_int_idiv(c, g);
- a = rb_int_idiv(bden, g);
- den = rb_int_mul(a, b);
+ FIXNUM_P(bnum) && FIXNUM_P(bden)) {
+ long an = FIX2LONG(anum);
+ long ad = FIX2LONG(aden);
+ long bn = FIX2LONG(bnum);
+ long bd = FIX2LONG(bden);
+ long ig = i_gcd(ad, bd);
+
+ VALUE g = LONG2NUM(ig);
+ VALUE a = f_imul(an, bd / ig);
+ VALUE b = f_imul(bn, ad / ig);
+ VALUE c;
+
+ if (k == '+')
+ c = rb_int_plus(a, b);
+ else
+ c = rb_int_minus(a, b);
+
+ b = rb_int_idiv(aden, g);
+ g = f_gcd(c, g);
+ num = rb_int_idiv(c, g);
+ a = rb_int_idiv(bden, g);
+ den = rb_int_mul(a, b);
}
else if (RB_INTEGER_TYPE_P(anum) && RB_INTEGER_TYPE_P(aden) &&
RB_INTEGER_TYPE_P(bnum) && RB_INTEGER_TYPE_P(bden)) {
- VALUE g = f_gcd(aden, bden);
- VALUE a = rb_int_mul(anum, rb_int_idiv(bden, g));
- VALUE b = rb_int_mul(bnum, rb_int_idiv(aden, g));
- VALUE c;
-
- if (k == '+')
- c = rb_int_plus(a, b);
- else
- c = rb_int_minus(a, b);
-
- b = rb_int_idiv(aden, g);
- g = f_gcd(c, g);
- num = rb_int_idiv(c, g);
- a = rb_int_idiv(bden, g);
- den = rb_int_mul(a, b);
+ VALUE g = f_gcd(aden, bden);
+ VALUE a = rb_int_mul(anum, rb_int_idiv(bden, g));
+ VALUE b = rb_int_mul(bnum, rb_int_idiv(aden, g));
+ VALUE c;
+
+ if (k == '+')
+ c = rb_int_plus(a, b);
+ else
+ c = rb_int_minus(a, b);
+
+ b = rb_int_idiv(aden, g);
+ g = f_gcd(c, g);
+ num = rb_int_idiv(c, g);
+ a = rb_int_idiv(bden, g);
+ den = rb_int_mul(a, b);
}
else {
double a = NUM2DBL(anum) / NUM2DBL(aden);
@@ -723,28 +723,28 @@ VALUE
rb_rational_plus(VALUE self, VALUE other)
{
if (RB_INTEGER_TYPE_P(other)) {
- {
- get_dat1(self);
+ {
+ get_dat1(self);
- return f_rational_new_no_reduce2(CLASS_OF(self),
- rb_int_plus(dat->num, rb_int_mul(other, dat->den)),
- dat->den);
- }
+ return f_rational_new_no_reduce2(CLASS_OF(self),
+ rb_int_plus(dat->num, rb_int_mul(other, dat->den)),
+ dat->den);
+ }
}
else if (RB_FLOAT_TYPE_P(other)) {
- return DBL2NUM(nurat_to_double(self) + RFLOAT_VALUE(other));
+ return DBL2NUM(nurat_to_double(self) + RFLOAT_VALUE(other));
}
else if (RB_TYPE_P(other, T_RATIONAL)) {
- {
- get_dat2(self, other);
+ {
+ get_dat2(self, other);
- return f_addsub(self,
- adat->num, adat->den,
- bdat->num, bdat->den, '+');
- }
+ return f_addsub(self,
+ adat->num, adat->den,
+ bdat->num, bdat->den, '+');
+ }
}
else {
- return rb_num_coerce_bin(self, other, '+');
+ return rb_num_coerce_bin(self, other, '+');
}
}
@@ -764,28 +764,28 @@ VALUE
rb_rational_minus(VALUE self, VALUE other)
{
if (RB_INTEGER_TYPE_P(other)) {
- {
- get_dat1(self);
+ {
+ get_dat1(self);
- return f_rational_new_no_reduce2(CLASS_OF(self),
- rb_int_minus(dat->num, rb_int_mul(other, dat->den)),
- dat->den);
- }
+ return f_rational_new_no_reduce2(CLASS_OF(self),
+ rb_int_minus(dat->num, rb_int_mul(other, dat->den)),
+ dat->den);
+ }
}
else if (RB_FLOAT_TYPE_P(other)) {
- return DBL2NUM(nurat_to_double(self) - RFLOAT_VALUE(other));
+ return DBL2NUM(nurat_to_double(self) - RFLOAT_VALUE(other));
}
else if (RB_TYPE_P(other, T_RATIONAL)) {
- {
- get_dat2(self, other);
+ {
+ get_dat2(self, other);
- return f_addsub(self,
- adat->num, adat->den,
- bdat->num, bdat->den, '-');
- }
+ return f_addsub(self,
+ adat->num, adat->den,
+ bdat->num, bdat->den, '-');
+ }
}
else {
- return rb_num_coerce_bin(self, other, '-');
+ return rb_num_coerce_bin(self, other, '-');
}
}
@@ -811,35 +811,35 @@ f_muldiv(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k)
assert(RB_INTEGER_TYPE_P(bden));
if (k == '/') {
- VALUE t;
+ VALUE t;
- if (INT_NEGATIVE_P(bnum)) {
- anum = rb_int_uminus(anum);
- bnum = rb_int_uminus(bnum);
- }
- t = bnum;
- bnum = bden;
- bden = t;
+ if (INT_NEGATIVE_P(bnum)) {
+ anum = rb_int_uminus(anum);
+ bnum = rb_int_uminus(bnum);
+ }
+ t = bnum;
+ bnum = bden;
+ bden = t;
}
if (FIXNUM_P(anum) && FIXNUM_P(aden) &&
- FIXNUM_P(bnum) && FIXNUM_P(bden)) {
- long an = FIX2LONG(anum);
- long ad = FIX2LONG(aden);
- long bn = FIX2LONG(bnum);
- long bd = FIX2LONG(bden);
- long g1 = i_gcd(an, bd);
- long g2 = i_gcd(ad, bn);
+ FIXNUM_P(bnum) && FIXNUM_P(bden)) {
+ long an = FIX2LONG(anum);
+ long ad = FIX2LONG(aden);
+ long bn = FIX2LONG(bnum);
+ long bd = FIX2LONG(bden);
+ long g1 = i_gcd(an, bd);
+ long g2 = i_gcd(ad, bn);
- num = f_imul(an / g1, bn / g2);
- den = f_imul(ad / g2, bd / g1);
+ num = f_imul(an / g1, bn / g2);
+ den = f_imul(ad / g2, bd / g1);
}
else {
- VALUE g1 = f_gcd(anum, bden);
- VALUE g2 = f_gcd(aden, bnum);
+ VALUE g1 = f_gcd(anum, bden);
+ VALUE g2 = f_gcd(aden, bnum);
- num = rb_int_mul(rb_int_idiv(anum, g1), rb_int_idiv(bnum, g2));
- den = rb_int_mul(rb_int_idiv(aden, g2), rb_int_idiv(bden, g1));
+ num = rb_int_mul(rb_int_idiv(anum, g1), rb_int_idiv(bnum, g2));
+ den = rb_int_mul(rb_int_idiv(aden, g2), rb_int_idiv(bden, g1));
}
return f_rational_new_no_reduce2(CLASS_OF(self), num, den);
}
@@ -860,28 +860,28 @@ VALUE
rb_rational_mul(VALUE self, VALUE other)
{
if (RB_INTEGER_TYPE_P(other)) {
- {
- get_dat1(self);
+ {
+ get_dat1(self);
- return f_muldiv(self,
- dat->num, dat->den,
- other, ONE, '*');
- }
+ return f_muldiv(self,
+ dat->num, dat->den,
+ other, ONE, '*');
+ }
}
else if (RB_FLOAT_TYPE_P(other)) {
- return DBL2NUM(nurat_to_double(self) * RFLOAT_VALUE(other));
+ return DBL2NUM(nurat_to_double(self) * RFLOAT_VALUE(other));
}
else if (RB_TYPE_P(other, T_RATIONAL)) {
- {
- get_dat2(self, other);
+ {
+ get_dat2(self, other);
- return f_muldiv(self,
- adat->num, adat->den,
- bdat->num, bdat->den, '*');
- }
+ return f_muldiv(self,
+ adat->num, adat->den,
+ bdat->num, bdat->den, '*');
+ }
}
else {
- return rb_num_coerce_bin(self, other, '*');
+ return rb_num_coerce_bin(self, other, '*');
}
}
@@ -902,37 +902,37 @@ VALUE
rb_rational_div(VALUE self, VALUE other)
{
if (RB_INTEGER_TYPE_P(other)) {
- if (f_zero_p(other))
+ if (f_zero_p(other))
rb_num_zerodiv();
- {
- get_dat1(self);
+ {
+ get_dat1(self);
- return f_muldiv(self,
- dat->num, dat->den,
- other, ONE, '/');
- }
+ return f_muldiv(self,
+ dat->num, dat->den,
+ other, ONE, '/');
+ }
}
else if (RB_FLOAT_TYPE_P(other)) {
VALUE v = nurat_to_f(self);
return rb_flo_div_flo(v, other);
}
else if (RB_TYPE_P(other, T_RATIONAL)) {
- if (f_zero_p(other))
+ if (f_zero_p(other))
rb_num_zerodiv();
- {
- get_dat2(self, other);
+ {
+ get_dat2(self, other);
- if (f_one_p(self))
- return f_rational_new_no_reduce2(CLASS_OF(self),
- bdat->den, bdat->num);
+ if (f_one_p(self))
+ return f_rational_new_no_reduce2(CLASS_OF(self),
+ bdat->den, bdat->num);
- return f_muldiv(self,
- adat->num, adat->den,
- bdat->num, bdat->den, '/');
- }
+ return f_muldiv(self,
+ adat->num, adat->den,
+ bdat->num, bdat->den, '/');
+ }
}
else {
- return rb_num_coerce_bin(self, other, '/');
+ return rb_num_coerce_bin(self, other, '/');
}
}
@@ -953,12 +953,12 @@ nurat_fdiv(VALUE self, VALUE other)
if (f_zero_p(other))
return rb_rational_div(self, rb_float_new(0.0));
if (FIXNUM_P(other) && other == LONG2FIX(1))
- return nurat_to_f(self);
+ return nurat_to_f(self);
div = rb_rational_div(self, other);
if (RB_TYPE_P(div, T_RATIONAL))
- return nurat_to_f(div);
+ return nurat_to_f(div);
if (RB_FLOAT_TYPE_P(div))
- return div;
+ return div;
return rb_funcall(div, idTo_f, 0);
}
@@ -979,76 +979,76 @@ VALUE
rb_rational_pow(VALUE self, VALUE other)
{
if (k_numeric_p(other) && k_exact_zero_p(other))
- return f_rational_new_bang1(CLASS_OF(self), ONE);
+ return f_rational_new_bang1(CLASS_OF(self), ONE);
if (k_rational_p(other)) {
- get_dat1(other);
+ get_dat1(other);
- if (f_one_p(dat->den))
- other = dat->num; /* c14n */
+ if (f_one_p(dat->den))
+ other = dat->num; /* c14n */
}
/* Deal with special cases of 0**n and 1**n */
if (k_numeric_p(other) && k_exact_p(other)) {
- get_dat1(self);
- if (f_one_p(dat->den)) {
- if (f_one_p(dat->num)) {
- return f_rational_new_bang1(CLASS_OF(self), ONE);
- }
- else if (f_minus_one_p(dat->num) && RB_INTEGER_TYPE_P(other)) {
- return f_rational_new_bang1(CLASS_OF(self), INT2FIX(rb_int_odd_p(other) ? -1 : 1));
- }
- else if (INT_ZERO_P(dat->num)) {
- if (rb_num_negative_p(other)) {
+ get_dat1(self);
+ if (f_one_p(dat->den)) {
+ if (f_one_p(dat->num)) {
+ return f_rational_new_bang1(CLASS_OF(self), ONE);
+ }
+ else if (f_minus_one_p(dat->num) && RB_INTEGER_TYPE_P(other)) {
+ return f_rational_new_bang1(CLASS_OF(self), INT2FIX(rb_int_odd_p(other) ? -1 : 1));
+ }
+ else if (INT_ZERO_P(dat->num)) {
+ if (rb_num_negative_p(other)) {
rb_num_zerodiv();
- }
- else {
- return f_rational_new_bang1(CLASS_OF(self), ZERO);
- }
- }
- }
+ }
+ else {
+ return f_rational_new_bang1(CLASS_OF(self), ZERO);
+ }
+ }
+ }
}
/* General case */
if (FIXNUM_P(other)) {
- {
- VALUE num, den;
+ {
+ VALUE num, den;
- get_dat1(self);
+ get_dat1(self);
if (INT_POSITIVE_P(other)) {
- num = rb_int_pow(dat->num, other);
- den = rb_int_pow(dat->den, other);
+ num = rb_int_pow(dat->num, other);
+ den = rb_int_pow(dat->den, other);
}
else if (INT_NEGATIVE_P(other)) {
- num = rb_int_pow(dat->den, rb_int_uminus(other));
- den = rb_int_pow(dat->num, rb_int_uminus(other));
+ num = rb_int_pow(dat->den, rb_int_uminus(other));
+ den = rb_int_pow(dat->num, rb_int_uminus(other));
}
else {
- num = ONE;
- den = ONE;
- }
- if (RB_FLOAT_TYPE_P(num)) { /* infinity due to overflow */
- if (RB_FLOAT_TYPE_P(den))
- return DBL2NUM(nan(""));
- return num;
- }
- if (RB_FLOAT_TYPE_P(den)) { /* infinity due to overflow */
- num = ZERO;
- den = ONE;
- }
- return f_rational_new2(CLASS_OF(self), num, den);
- }
+ num = ONE;
+ den = ONE;
+ }
+ if (RB_FLOAT_TYPE_P(num)) { /* infinity due to overflow */
+ if (RB_FLOAT_TYPE_P(den))
+ return DBL2NUM(nan(""));
+ return num;
+ }
+ if (RB_FLOAT_TYPE_P(den)) { /* infinity due to overflow */
+ num = ZERO;
+ den = ONE;
+ }
+ return f_rational_new2(CLASS_OF(self), num, den);
+ }
}
else if (RB_BIGNUM_TYPE_P(other)) {
- rb_warn("in a**b, b may be too big");
- return rb_float_pow(nurat_to_f(self), other);
+ rb_warn("in a**b, b may be too big");
+ return rb_float_pow(nurat_to_f(self), other);
}
else if (RB_FLOAT_TYPE_P(other) || RB_TYPE_P(other, T_RATIONAL)) {
- return rb_float_pow(nurat_to_f(self), other);
+ return rb_float_pow(nurat_to_f(self), other);
}
else {
- return rb_num_coerce_bin(self, other, idPow);
+ return rb_num_coerce_bin(self, other, idPow);
}
}
#define nurat_expt rb_rational_pow
@@ -1076,38 +1076,38 @@ rb_rational_cmp(VALUE self, VALUE other)
switch (TYPE(other)) {
case T_FIXNUM:
case T_BIGNUM:
- {
- get_dat1(self);
+ {
+ get_dat1(self);
- if (dat->den == LONG2FIX(1))
- return rb_int_cmp(dat->num, other); /* c14n */
- other = f_rational_new_bang1(CLASS_OF(self), other);
+ if (dat->den == LONG2FIX(1))
+ return rb_int_cmp(dat->num, other); /* c14n */
+ other = f_rational_new_bang1(CLASS_OF(self), other);
/* FALLTHROUGH */
- }
+ }
case T_RATIONAL:
- {
- VALUE num1, num2;
-
- get_dat2(self, other);
-
- if (FIXNUM_P(adat->num) && FIXNUM_P(adat->den) &&
- FIXNUM_P(bdat->num) && FIXNUM_P(bdat->den)) {
- num1 = f_imul(FIX2LONG(adat->num), FIX2LONG(bdat->den));
- num2 = f_imul(FIX2LONG(bdat->num), FIX2LONG(adat->den));
- }
- else {
- num1 = rb_int_mul(adat->num, bdat->den);
- num2 = rb_int_mul(bdat->num, adat->den);
- }
- return rb_int_cmp(rb_int_minus(num1, num2), ZERO);
- }
+ {
+ VALUE num1, num2;
+
+ get_dat2(self, other);
+
+ if (FIXNUM_P(adat->num) && FIXNUM_P(adat->den) &&
+ FIXNUM_P(bdat->num) && FIXNUM_P(bdat->den)) {
+ num1 = f_imul(FIX2LONG(adat->num), FIX2LONG(bdat->den));
+ num2 = f_imul(FIX2LONG(bdat->num), FIX2LONG(adat->den));
+ }
+ else {
+ num1 = rb_int_mul(adat->num, bdat->den);
+ num2 = rb_int_mul(bdat->num, adat->den);
+ }
+ return rb_int_cmp(rb_int_minus(num1, num2), ZERO);
+ }
case T_FLOAT:
return rb_dbl_cmp(nurat_to_double(self), RFLOAT_VALUE(other));
default:
- return rb_num_coerce_cmp(self, other, idCmp);
+ return rb_num_coerce_cmp(self, other, idCmp);
}
}
@@ -1130,37 +1130,37 @@ nurat_eqeq_p(VALUE self, VALUE other)
get_dat1(self);
if (RB_INTEGER_TYPE_P(dat->num) && RB_INTEGER_TYPE_P(dat->den)) {
- if (INT_ZERO_P(dat->num) && INT_ZERO_P(other))
- return Qtrue;
-
- if (!FIXNUM_P(dat->den))
- return Qfalse;
- if (FIX2LONG(dat->den) != 1)
- return Qfalse;
- return rb_int_equal(dat->num, other);
- }
+ if (INT_ZERO_P(dat->num) && INT_ZERO_P(other))
+ return Qtrue;
+
+ if (!FIXNUM_P(dat->den))
+ return Qfalse;
+ if (FIX2LONG(dat->den) != 1)
+ return Qfalse;
+ return rb_int_equal(dat->num, other);
+ }
else {
const double d = nurat_to_double(self);
return RBOOL(FIXNUM_ZERO_P(rb_dbl_cmp(d, NUM2DBL(other))));
}
}
else if (RB_FLOAT_TYPE_P(other)) {
- const double d = nurat_to_double(self);
- return RBOOL(FIXNUM_ZERO_P(rb_dbl_cmp(d, RFLOAT_VALUE(other))));
+ const double d = nurat_to_double(self);
+ return RBOOL(FIXNUM_ZERO_P(rb_dbl_cmp(d, RFLOAT_VALUE(other))));
}
else if (RB_TYPE_P(other, T_RATIONAL)) {
- {
- get_dat2(self, other);
+ {
+ get_dat2(self, other);
- if (INT_ZERO_P(adat->num) && INT_ZERO_P(bdat->num))
- return Qtrue;
+ if (INT_ZERO_P(adat->num) && INT_ZERO_P(bdat->num))
+ return Qtrue;
- return RBOOL(rb_int_equal(adat->num, bdat->num) &&
- rb_int_equal(adat->den, bdat->den));
- }
+ return RBOOL(rb_int_equal(adat->num, bdat->num) &&
+ rb_int_equal(adat->den, bdat->den));
+ }
}
else {
- return rb_equal(other, self);
+ return rb_equal(other, self);
}
}
@@ -1169,17 +1169,17 @@ static VALUE
nurat_coerce(VALUE self, VALUE other)
{
if (RB_INTEGER_TYPE_P(other)) {
- return rb_assoc_new(f_rational_new_bang1(CLASS_OF(self), other), self);
+ return rb_assoc_new(f_rational_new_bang1(CLASS_OF(self), other), self);
}
else if (RB_FLOAT_TYPE_P(other)) {
return rb_assoc_new(other, nurat_to_f(self));
}
else if (RB_TYPE_P(other, T_RATIONAL)) {
- return rb_assoc_new(other, self);
+ return rb_assoc_new(other, self);
}
else if (RB_TYPE_P(other, T_COMPLEX)) {
- if (!k_exact_zero_p(RCOMPLEX(other)->imag))
- return rb_assoc_new(other, rb_Complex(self, INT2FIX(0)));
+ if (!k_exact_zero_p(RCOMPLEX(other)->imag))
+ return rb_assoc_new(other, rb_Complex(self, INT2FIX(0)));
other = RCOMPLEX(other)->real;
if (RB_FLOAT_TYPE_P(other)) {
other = float_to_r(other);
@@ -1192,7 +1192,7 @@ nurat_coerce(VALUE self, VALUE other)
}
rb_raise(rb_eTypeError, "%s can't be coerced into %s",
- rb_obj_classname(other), rb_obj_classname(self));
+ rb_obj_classname(other), rb_obj_classname(self));
return Qnil;
}
@@ -1279,7 +1279,7 @@ nurat_truncate(VALUE self)
{
get_dat1(self);
if (INT_NEGATIVE_P(dat->num))
- return rb_int_uminus(rb_int_idiv(rb_int_uminus(dat->num), dat->den));
+ return rb_int_uminus(rb_int_idiv(rb_int_uminus(dat->num), dat->den));
return rb_int_idiv(dat->num, dat->den);
}
@@ -1295,14 +1295,14 @@ nurat_round_half_up(VALUE self)
neg = INT_NEGATIVE_P(num);
if (neg)
- num = rb_int_uminus(num);
+ num = rb_int_uminus(num);
num = rb_int_plus(rb_int_mul(num, TWO), den);
den = rb_int_mul(den, TWO);
num = rb_int_idiv(num, den);
if (neg)
- num = rb_int_uminus(num);
+ num = rb_int_uminus(num);
return num;
}
@@ -1319,7 +1319,7 @@ nurat_round_half_down(VALUE self)
neg = INT_NEGATIVE_P(num);
if (neg)
- num = rb_int_uminus(num);
+ num = rb_int_uminus(num);
num = rb_int_plus(rb_int_mul(num, TWO), den);
num = rb_int_minus(num, ONE);
@@ -1327,7 +1327,7 @@ nurat_round_half_down(VALUE self)
num = rb_int_idiv(num, den);
if (neg)
- num = rb_int_uminus(num);
+ num = rb_int_uminus(num);
return num;
}
@@ -1344,17 +1344,17 @@ nurat_round_half_even(VALUE self)
neg = INT_NEGATIVE_P(num);
if (neg)
- num = rb_int_uminus(num);
+ num = rb_int_uminus(num);
num = rb_int_plus(rb_int_mul(num, TWO), den);
den = rb_int_mul(den, TWO);
qr = rb_int_divmod(num, den);
num = RARRAY_AREF(qr, 0);
if (INT_ZERO_P(RARRAY_AREF(qr, 1)))
- num = rb_int_and(num, LONG2FIX(((int)~1)));
+ num = rb_int_and(num, LONG2FIX(((int)~1)));
if (neg)
- num = rb_int_uminus(num);
+ num = rb_int_uminus(num);
return num;
}
@@ -1365,24 +1365,24 @@ f_round_common(int argc, VALUE *argv, VALUE self, VALUE (*func)(VALUE))
VALUE n, b, s;
if (rb_check_arity(argc, 0, 1) == 0)
- return (*func)(self);
+ return (*func)(self);
n = argv[0];
if (!k_integer_p(n))
- rb_raise(rb_eTypeError, "not an integer");
+ rb_raise(rb_eTypeError, "not an integer");
b = f_expt10(n);
s = rb_rational_mul(self, b);
if (k_float_p(s)) {
- if (INT_NEGATIVE_P(n))
- return ZERO;
- return self;
+ if (INT_NEGATIVE_P(n))
+ return ZERO;
+ return self;
}
if (!k_rational_p(s)) {
- s = f_rational_new_bang1(CLASS_OF(self), s);
+ s = f_rational_new_bang1(CLASS_OF(self), s);
}
s = (*func)(s);
@@ -1390,7 +1390,7 @@ f_round_common(int argc, VALUE *argv, VALUE self, VALUE (*func)(VALUE))
s = rb_rational_div(f_rational_new_bang1(CLASS_OF(self), s), b);
if (RB_TYPE_P(s, T_RATIONAL) && FIX2INT(rb_int_cmp(n, ONE)) < 0)
- s = nurat_truncate(s);
+ s = nurat_truncate(s);
return s;
}
@@ -1540,7 +1540,7 @@ nurat_round_n(int argc, VALUE *argv, VALUE self)
VALUE opt;
enum ruby_num_rounding_mode mode = (
argc = rb_scan_args(argc, argv, "*:", NULL, &opt),
- rb_num_get_rounding_option(opt));
+ rb_num_get_rounding_option(opt));
VALUE (*round_func)(VALUE) = ROUND_FUNC(mode, nurat_round);
return f_round_common(argc, argv, self, round_func);
}
@@ -1689,19 +1689,19 @@ nurat_rationalize_internal(VALUE a, VALUE b, VALUE *p, VALUE *q)
q1 = ZERO;
while (1) {
- c = f_ceil(a);
- if (f_lt_p(c, b))
- break;
- k = f_sub(c, ONE);
- p2 = f_add(f_mul(k, p1), p0);
- q2 = f_add(f_mul(k, q1), q0);
- t = f_reciprocal(f_sub(b, k));
- b = f_reciprocal(f_sub(a, k));
- a = t;
- p0 = p1;
- q0 = q1;
- p1 = p2;
- q1 = q2;
+ c = f_ceil(a);
+ if (f_lt_p(c, b))
+ break;
+ k = f_sub(c, ONE);
+ p2 = f_add(f_mul(k, p1), p0);
+ q2 = f_add(f_mul(k, q1), q0);
+ t = f_reciprocal(f_sub(b, k));
+ b = f_reciprocal(f_sub(a, k));
+ a = t;
+ p0 = p1;
+ q0 = q1;
+ p1 = p2;
+ q1 = q2;
}
*p = f_add(f_mul(c, p1), p0);
*q = f_add(f_mul(c, q1), q0);
@@ -1729,7 +1729,7 @@ nurat_rationalize(int argc, VALUE *argv, VALUE self)
get_dat1(self);
if (rb_check_arity(argc, 0, 1) == 0)
- return self;
+ return self;
e = f_abs(argv[0]);
@@ -1741,7 +1741,7 @@ nurat_rationalize(int argc, VALUE *argv, VALUE self)
b = FIXNUM_ZERO_P(e) ? rat : rb_rational_plus(rat, e);
if (f_eqeq_p(a, b))
- return self;
+ return self;
nurat_rationalize_internal(a, b, &p, &q);
if (rat != self) {
@@ -1874,7 +1874,7 @@ nurat_marshal_load(VALUE self, VALUE a)
Check_Type(a, T_ARRAY);
if (RARRAY_LEN(a) != 2)
- rb_raise(rb_eArgError, "marshaled rational must have an array whose length is 2 but %ld", RARRAY_LEN(a));
+ rb_raise(rb_eArgError, "marshaled rational must have an array whose length is 2 but %ld", RARRAY_LEN(a));
num = RARRAY_AREF(a, 0);
den = RARRAY_AREF(a, 1);
@@ -2061,30 +2061,6 @@ rb_rational_canonicalize(VALUE x)
/*
* call-seq:
- * int.numerator -> self
- *
- * Returns self.
- */
-static VALUE
-integer_numerator(VALUE self)
-{
- return self;
-}
-
-/*
- * call-seq:
- * int.denominator -> 1
- *
- * Returns 1.
- */
-static VALUE
-integer_denominator(VALUE self)
-{
- return INT2FIX(1);
-}
-
-/*
- * call-seq:
* flo.numerator -> integer
*
* Returns the numerator. The result is machine dependent.
@@ -2101,7 +2077,7 @@ rb_float_numerator(VALUE self)
double d = RFLOAT_VALUE(self);
VALUE r;
if (!isfinite(d))
- return self;
+ return self;
r = float_to_r(self);
return nurat_numerator(r);
}
@@ -2121,7 +2097,7 @@ rb_float_denominator(VALUE self)
double d = RFLOAT_VALUE(self);
VALUE r;
if (!isfinite(d))
- return INT2FIX(1);
+ return INT2FIX(1);
r = float_to_r(self);
return nurat_denominator(r);
}
@@ -2229,7 +2205,7 @@ float_to_r(VALUE self)
#else
f = rb_int_mul(f, rb_int_pow(INT2FIX(FLT_RADIX), n));
if (RB_TYPE_P(f, T_RATIONAL))
- return f;
+ return f;
return rb_rational_new1(f);
#endif
}
@@ -2327,8 +2303,8 @@ read_sign(const char **s, const char *const e)
int sign = '?';
if (*s < e && issign(**s)) {
- sign = **s;
- (*s)++;
+ sign = **s;
+ (*s)++;
}
return sign;
}
@@ -2343,11 +2319,11 @@ static VALUE
negate_num(VALUE num)
{
if (FIXNUM_P(num)) {
- return rb_int_uminus(num);
+ return rb_int_uminus(num);
}
else {
- BIGNUM_NEGATE(num);
- return rb_big_norm(num);
+ BIGNUM_NEGATE(num);
+ return rb_big_norm(num);
}
}
@@ -2361,51 +2337,51 @@ read_num(const char **s, const char *const end, VALUE *num, VALUE *nexp)
*nexp = ZERO;
*num = ZERO;
if (*s < end && **s != '.') {
- n = rb_int_parse_cstr(*s, end-*s, &e, NULL,
- 10, RB_INT_PARSE_UNDERSCORE);
- if (NIL_P(n))
- return 0;
- *s = e;
- *num = n;
- ok = 1;
+ n = rb_int_parse_cstr(*s, end-*s, &e, NULL,
+ 10, RB_INT_PARSE_UNDERSCORE);
+ if (NIL_P(n))
+ return 0;
+ *s = e;
+ *num = n;
+ ok = 1;
}
if (*s < end && **s == '.') {
- size_t count = 0;
-
- (*s)++;
- fp = rb_int_parse_cstr(*s, end-*s, &e, &count,
- 10, RB_INT_PARSE_UNDERSCORE);
- if (NIL_P(fp))
- return 1;
- *s = e;
- {
+ size_t count = 0;
+
+ (*s)++;
+ fp = rb_int_parse_cstr(*s, end-*s, &e, &count,
+ 10, RB_INT_PARSE_UNDERSCORE);
+ if (NIL_P(fp))
+ return 1;
+ *s = e;
+ {
VALUE l = f_expt10(*nexp = SIZET2NUM(count));
- n = n == ZERO ? fp : rb_int_plus(rb_int_mul(*num, l), fp);
- *num = n;
- fn = SIZET2NUM(count);
- }
- ok = 1;
+ n = n == ZERO ? fp : rb_int_plus(rb_int_mul(*num, l), fp);
+ *num = n;
+ fn = SIZET2NUM(count);
+ }
+ ok = 1;
}
if (ok && *s + 1 < end && islettere(**s)) {
- (*s)++;
- expsign = read_sign(s, end);
- exp = rb_int_parse_cstr(*s, end-*s, &e, NULL,
- 10, RB_INT_PARSE_UNDERSCORE);
- if (NIL_P(exp))
- return 1;
- *s = e;
- if (exp != ZERO) {
- if (expsign == '-') {
- if (fn != ZERO) exp = rb_int_plus(exp, fn);
- }
- else {
- if (fn != ZERO) exp = rb_int_minus(exp, fn);
+ (*s)++;
+ expsign = read_sign(s, end);
+ exp = rb_int_parse_cstr(*s, end-*s, &e, NULL,
+ 10, RB_INT_PARSE_UNDERSCORE);
+ if (NIL_P(exp))
+ return 1;
+ *s = e;
+ if (exp != ZERO) {
+ if (expsign == '-') {
+ if (fn != ZERO) exp = rb_int_plus(exp, fn);
+ }
+ else {
+ if (fn != ZERO) exp = rb_int_minus(exp, fn);
exp = negate_num(exp);
- }
+ }
*nexp = exp;
- }
+ }
}
return ok;
@@ -2415,7 +2391,7 @@ inline static const char *
skip_ws(const char *s, const char *e)
{
while (s < e && isspace((unsigned char)*s))
- ++s;
+ ++s;
return s;
}
@@ -2429,30 +2405,30 @@ parse_rat(const char *s, const char *const e, int strict, int raise)
sign = read_sign(&s, e);
if (!read_num(&s, e, &num, &nexp)) {
- if (strict) return Qnil;
- return nurat_s_alloc(rb_cRational);
+ if (strict) return Qnil;
+ return nurat_s_alloc(rb_cRational);
}
den = ONE;
if (s < e && *s == '/') {
- s++;
+ s++;
if (!read_num(&s, e, &den, &dexp)) {
- if (strict) return Qnil;
+ if (strict) return Qnil;
den = ONE;
- }
- else if (den == ZERO) {
+ }
+ else if (den == ZERO) {
if (!raise) return Qnil;
- rb_num_zerodiv();
- }
- else if (strict && skip_ws(s, e) != e) {
- return Qnil;
- }
- else {
+ rb_num_zerodiv();
+ }
+ else if (strict && skip_ws(s, e) != e) {
+ return Qnil;
+ }
+ else {
nexp = rb_int_minus(nexp, dexp);
- nurat_reduce(&num, &den);
- }
+ nurat_reduce(&num, &den);
+ }
}
else if (strict && skip_ws(s, e) != e) {
- return Qnil;
+ return Qnil;
}
if (nexp != ZERO) {
@@ -2483,7 +2459,7 @@ parse_rat(const char *s, const char *const e, int strict, int raise)
}
if (sign == '-') {
- num = negate_num(num);
+ num = negate_num(num);
}
return rb_rational_raw(num, den);
@@ -2548,7 +2524,7 @@ string_to_r(VALUE self)
num = parse_rat(RSTRING_PTR(self), RSTRING_END(self), 0, TRUE);
if (RB_FLOAT_TYPE_P(num) && !FLOAT_ZERO_P(num))
- rb_raise(rb_eFloatDomainError, "Infinity");
+ rb_raise(rb_eFloatDomainError, "Infinity");
return num;
}
@@ -2560,7 +2536,7 @@ rb_cstr_to_rat(const char *s, int strict) /* for complex's internal */
num = parse_rat(s, s + strlen(s), strict, TRUE);
if (RB_FLOAT_TYPE_P(num) && !FLOAT_ZERO_P(num))
- rb_raise(rb_eFloatDomainError, "Infinity");
+ rb_raise(rb_eFloatDomainError, "Infinity");
return num;
}
@@ -2576,7 +2552,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise)
VALUE a1 = numv, a2 = denv;
int state;
- assert(a1 != Qundef);
+ assert(!UNDEF_P(a1));
if (NIL_P(a1) || NIL_P(a2)) {
if (!raise) return Qnil;
@@ -2627,7 +2603,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise)
a2 = string_to_r_strict(a2, raise);
if (!raise && NIL_P(a2)) return Qnil;
}
- else if (a2 != Qundef && !rb_respond_to(a2, idTo_r)) {
+ else if (!UNDEF_P(a2) && !rb_respond_to(a2, idTo_r)) {
VALUE tmp = rb_protect(rb_check_to_int, a2, NULL);
rb_set_errinfo(Qnil);
if (!NIL_P(tmp)) {
@@ -2636,11 +2612,11 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise)
}
if (RB_TYPE_P(a1, T_RATIONAL)) {
- if (a2 == Qundef || (k_exact_one_p(a2)))
+ if (UNDEF_P(a2) || (k_exact_one_p(a2)))
return a1;
}
- if (a2 == Qundef) {
+ if (UNDEF_P(a2)) {
if (!RB_INTEGER_TYPE_P(a1)) {
if (!raise) {
VALUE result = rb_protect(to_rational, a1, NULL);
@@ -2690,7 +2666,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise)
a1 = nurat_int_value(a1);
- if (a2 == Qundef) {
+ if (UNDEF_P(a2)) {
a2 = ONE;
}
else if (!k_integer_p(a2) && !raise) {
@@ -2832,9 +2808,6 @@ Init_Rational(void)
rb_define_method(rb_cNumeric, "denominator", numeric_denominator, 0);
rb_define_method(rb_cNumeric, "quo", rb_numeric_quo, 1);
- rb_define_method(rb_cInteger, "numerator", integer_numerator, 0);
- rb_define_method(rb_cInteger, "denominator", integer_denominator, 0);
-
rb_define_method(rb_cFloat, "numerator", rb_float_numerator, 0);
rb_define_method(rb_cFloat, "denominator", rb_float_denominator, 0);
diff --git a/re.c b/re.c
index 0f0eba0552..f357d38c63 100644
--- a/re.c
+++ b/re.c
@@ -16,6 +16,7 @@
#include "encindex.h"
#include "hrtime.h"
#include "internal.h"
+#include "internal/encoding.h"
#include "internal/hash.h"
#include "internal/imemo.h"
#include "internal/re.h"
@@ -94,8 +95,8 @@ rb_memcicmp(const void *x, const void *y, long len)
int tmp;
while (len--) {
- if ((tmp = casetable[(unsigned)*p1++] - casetable[(unsigned)*p2++]))
- return tmp;
+ if ((tmp = casetable[(unsigned)*p1++] - casetable[(unsigned)*p2++]))
+ return tmp;
}
return 0;
}
@@ -107,9 +108,9 @@ rb_memsearch_ss(const unsigned char *xs, long m, const unsigned char *ys, long n
const unsigned char *y;
if ((y = memmem(ys, n, xs, m)) != NULL)
- return y - ys;
+ return y - ys;
else
- return -1;
+ return -1;
}
#else
static inline long
@@ -121,26 +122,26 @@ rb_memsearch_ss(const unsigned char *xs, long m, const unsigned char *ys, long n
VALUE hx, hy, mask = VALUE_MAX >> ((SIZEOF_VALUE - m) * CHAR_BIT);
if (m > SIZEOF_VALUE)
- rb_bug("!!too long pattern string!!");
+ rb_bug("!!too long pattern string!!");
if (!(y = memchr(y, *x, n - m + 1)))
- return -1;
+ return -1;
/* Prepare hash value */
for (hx = *x++, hy = *y++; x < xe; ++x, ++y) {
- hx <<= CHAR_BIT;
- hy <<= CHAR_BIT;
- hx |= *x;
- hy |= *y;
+ hx <<= CHAR_BIT;
+ hy <<= CHAR_BIT;
+ hx |= *x;
+ hy |= *y;
}
/* Searching */
while (hx != hy) {
- if (y == ye)
- return -1;
- hy <<= CHAR_BIT;
- hy |= *y;
- hy &= mask;
- y++;
+ if (y == ye)
+ return -1;
+ hy <<= CHAR_BIT;
+ hy |= *y;
+ hy &= mask;
+ y++;
}
return y - ys - m;
}
@@ -155,13 +156,13 @@ rb_memsearch_qs(const unsigned char *xs, long m, const unsigned char *ys, long n
/* Preprocessing */
for (i = 0; i < 256; ++i)
- qstable[i] = m + 1;
+ qstable[i] = m + 1;
for (; x < xe; ++x)
- qstable[*x] = xe - x;
+ qstable[*x] = xe - x;
/* Searching */
for (; y + m <= ys + n; y += *(qstable + y[m])) {
- if (*xs == *y && memcmp(xs, y, m) == 0)
- return y - ys;
+ if (*xs == *y && memcmp(xs, y, m) == 0)
+ return y - ys;
}
return -1;
}
@@ -172,28 +173,28 @@ rb_memsearch_qs_utf8_hash(const unsigned char *x)
register const unsigned int mix = 8353;
register unsigned int h = *x;
if (h < 0xC0) {
- return h + 256;
+ return h + 256;
}
else if (h < 0xE0) {
- h *= mix;
- h += x[1];
+ h *= mix;
+ h += x[1];
}
else if (h < 0xF0) {
- h *= mix;
- h += x[1];
- h *= mix;
- h += x[2];
+ h *= mix;
+ h += x[1];
+ h *= mix;
+ h += x[2];
}
else if (h < 0xF5) {
- h *= mix;
- h += x[1];
- h *= mix;
- h += x[2];
- h *= mix;
- h += x[3];
+ h *= mix;
+ h += x[1];
+ h *= mix;
+ h += x[2];
+ h *= mix;
+ h += x[3];
}
else {
- return h + 256;
+ return h + 256;
}
return (unsigned char)h;
}
@@ -207,43 +208,41 @@ rb_memsearch_qs_utf8(const unsigned char *xs, long m, const unsigned char *ys, l
/* Preprocessing */
for (i = 0; i < 512; ++i) {
- qstable[i] = m + 1;
+ qstable[i] = m + 1;
}
for (; x < xe; ++x) {
- qstable[rb_memsearch_qs_utf8_hash(x)] = xe - x;
+ qstable[rb_memsearch_qs_utf8_hash(x)] = xe - x;
}
/* Searching */
for (; y + m <= ys + n; y += qstable[rb_memsearch_qs_utf8_hash(y+m)]) {
- if (*xs == *y && memcmp(xs, y, m) == 0)
- return y - ys;
+ if (*xs == *y && memcmp(xs, y, m) == 0)
+ return y - ys;
}
return -1;
}
static inline long
-rb_memsearch_wchar(const unsigned char *xs, long m, const unsigned char *ys, long n)
+rb_memsearch_with_char_size(const unsigned char *xs, long m, const unsigned char *ys, long n, int char_size)
{
const unsigned char *x = xs, x0 = *xs, *y = ys;
- enum {char_size = 2};
for (n -= m; n >= 0; n -= char_size, y += char_size) {
- if (x0 == *y && memcmp(x+1, y+1, m-1) == 0)
- return y - ys;
+ if (x0 == *y && memcmp(x+1, y+1, m-1) == 0)
+ return y - ys;
}
return -1;
}
static inline long
-rb_memsearch_qchar(const unsigned char *xs, long m, const unsigned char *ys, long n)
+rb_memsearch_wchar(const unsigned char *xs, long m, const unsigned char *ys, long n)
{
- const unsigned char *x = xs, x0 = *xs, *y = ys;
- enum {char_size = 4};
+ return rb_memsearch_with_char_size(xs, m, ys, n, 2);
+}
- for (n -= m; n >= 0; n -= char_size, y += char_size) {
- if (x0 == *y && memcmp(x+1, y+1, m-1) == 0)
- return y - ys;
- }
- return -1;
+static inline long
+rb_memsearch_qchar(const unsigned char *xs, long m, const unsigned char *ys, long n)
+{
+ return rb_memsearch_with_char_size(xs, m, ys, n, 4);
}
long
@@ -253,32 +252,32 @@ rb_memsearch(const void *x0, long m, const void *y0, long n, rb_encoding *enc)
if (m > n) return -1;
else if (m == n) {
- return memcmp(x0, y0, m) == 0 ? 0 : -1;
+ return memcmp(x0, y0, m) == 0 ? 0 : -1;
}
else if (m < 1) {
- return 0;
+ return 0;
}
else if (m == 1) {
- const unsigned char *ys = memchr(y, *x, n);
+ const unsigned char *ys = memchr(y, *x, n);
- if (ys)
- return ys - y;
- else
- return -1;
+ if (ys)
+ return ys - y;
+ else
+ return -1;
}
else if (LIKELY(rb_enc_mbminlen(enc) == 1)) {
- if (m <= SIZEOF_VALUE) {
- return rb_memsearch_ss(x0, m, y0, n);
- }
- else if (enc == rb_utf8_encoding()){
- return rb_memsearch_qs_utf8(x0, m, y0, n);
- }
+ if (m <= SIZEOF_VALUE) {
+ return rb_memsearch_ss(x0, m, y0, n);
+ }
+ else if (enc == rb_utf8_encoding()){
+ return rb_memsearch_qs_utf8(x0, m, y0, n);
+ }
}
else if (LIKELY(rb_enc_mbminlen(enc) == 2)) {
- return rb_memsearch_wchar(x0, m, y0, n);
+ return rb_memsearch_wchar(x0, m, y0, n);
}
else if (LIKELY(rb_enc_mbminlen(enc) == 4)) {
- return rb_memsearch_qchar(x0, m, y0, n);
+ return rb_memsearch_qchar(x0, m, y0, n);
}
return rb_memsearch_qs(x0, m, y0, n);
}
@@ -300,17 +299,17 @@ char_to_option(int c)
switch (c) {
case 'i':
- val = ONIG_OPTION_IGNORECASE;
- break;
+ val = ONIG_OPTION_IGNORECASE;
+ break;
case 'x':
- val = ONIG_OPTION_EXTEND;
- break;
+ val = ONIG_OPTION_EXTEND;
+ break;
case 'm':
- val = ONIG_OPTION_MULTILINE;
- break;
+ val = ONIG_OPTION_MULTILINE;
+ break;
default:
- val = 0;
- break;
+ val = 0;
+ break;
}
return val;
}
@@ -338,17 +337,17 @@ rb_char_to_option_kcode(int c, int *option, int *kcode)
*kcode = rb_ascii8bit_encindex();
return (*option = ARG_ENCODING_NONE);
case 'e':
- *kcode = ENCINDEX_EUC_JP;
- break;
+ *kcode = ENCINDEX_EUC_JP;
+ break;
case 's':
- *kcode = ENCINDEX_Windows_31J;
- break;
+ *kcode = ENCINDEX_Windows_31J;
+ break;
case 'u':
- *kcode = rb_utf8_encindex();
- break;
+ *kcode = rb_utf8_encindex();
+ break;
default:
- *kcode = -1;
- return (*option = char_to_option(c));
+ *kcode = -1;
+ return (*option = char_to_option(c));
}
*option = ARG_ENCODING_FIXED;
return 1;
@@ -358,13 +357,13 @@ static void
rb_reg_check(VALUE re)
{
if (!RREGEXP_PTR(re) || !RREGEXP_SRC(re) || !RREGEXP_SRC_PTR(re)) {
- rb_raise(rb_eTypeError, "uninitialized Regexp");
+ rb_raise(rb_eTypeError, "uninitialized Regexp");
}
}
static void
rb_reg_expr_str(VALUE str, const char *s, long len,
- rb_encoding *enc, rb_encoding *resenc, int term)
+ rb_encoding *enc, rb_encoding *resenc, int term)
{
const char *p, *pend;
int cr = ENC_CODERANGE_UNKNOWN;
@@ -374,80 +373,80 @@ rb_reg_expr_str(VALUE str, const char *s, long len,
p = s; pend = p + len;
rb_str_coderange_scan_restartable(p, pend, enc, &cr);
if (rb_enc_asciicompat(enc) && ENC_CODERANGE_CLEAN_P(cr)) {
- while (p < pend) {
- c = rb_enc_ascget(p, pend, &clen, enc);
- if (c == -1) {
- if (enc == resenc) {
- p += mbclen(p, pend, enc);
- }
- else {
- need_escape = 1;
- break;
- }
- }
- else if (c != term && rb_enc_isprint(c, enc)) {
- p += clen;
- }
- else {
- need_escape = 1;
- break;
- }
- }
+ while (p < pend) {
+ c = rb_enc_ascget(p, pend, &clen, enc);
+ if (c == -1) {
+ if (enc == resenc) {
+ p += mbclen(p, pend, enc);
+ }
+ else {
+ need_escape = 1;
+ break;
+ }
+ }
+ else if (c != term && rb_enc_isprint(c, enc)) {
+ p += clen;
+ }
+ else {
+ need_escape = 1;
+ break;
+ }
+ }
}
else {
- need_escape = 1;
+ need_escape = 1;
}
if (!need_escape) {
- rb_str_buf_cat(str, s, len);
+ rb_str_buf_cat(str, s, len);
}
else {
- int unicode_p = rb_enc_unicode_p(enc);
- p = s;
- while (p<pend) {
+ int unicode_p = rb_enc_unicode_p(enc);
+ p = s;
+ while (p<pend) {
c = rb_enc_ascget(p, pend, &clen, enc);
- if (c == '\\' && p+clen < pend) {
- int n = clen + mbclen(p+clen, pend, enc);
- rb_str_buf_cat(str, p, n);
- p += n;
- continue;
- }
- else if (c == -1) {
- clen = rb_enc_precise_mbclen(p, pend, enc);
- if (!MBCLEN_CHARFOUND_P(clen)) {
- c = (unsigned char)*p;
- clen = 1;
- goto hex;
- }
- if (resenc) {
- unsigned int c = rb_enc_mbc_to_codepoint(p, pend, enc);
- rb_str_buf_cat_escaped_char(str, c, unicode_p);
- }
- else {
- clen = MBCLEN_CHARFOUND_LEN(clen);
- rb_str_buf_cat(str, p, clen);
- }
- }
- else if (c == term) {
- char c = '\\';
- rb_str_buf_cat(str, &c, 1);
- rb_str_buf_cat(str, p, clen);
- }
- else if (rb_enc_isprint(c, enc)) {
- rb_str_buf_cat(str, p, clen);
- }
- else if (!rb_enc_isspace(c, enc)) {
- char b[8];
-
- hex:
- snprintf(b, sizeof(b), "\\x%02X", c);
- rb_str_buf_cat(str, b, 4);
- }
- else {
- rb_str_buf_cat(str, p, clen);
- }
- p += clen;
- }
+ if (c == '\\' && p+clen < pend) {
+ int n = clen + mbclen(p+clen, pend, enc);
+ rb_str_buf_cat(str, p, n);
+ p += n;
+ continue;
+ }
+ else if (c == -1) {
+ clen = rb_enc_precise_mbclen(p, pend, enc);
+ if (!MBCLEN_CHARFOUND_P(clen)) {
+ c = (unsigned char)*p;
+ clen = 1;
+ goto hex;
+ }
+ if (resenc) {
+ unsigned int c = rb_enc_mbc_to_codepoint(p, pend, enc);
+ rb_str_buf_cat_escaped_char(str, c, unicode_p);
+ }
+ else {
+ clen = MBCLEN_CHARFOUND_LEN(clen);
+ rb_str_buf_cat(str, p, clen);
+ }
+ }
+ else if (c == term) {
+ char c = '\\';
+ rb_str_buf_cat(str, &c, 1);
+ rb_str_buf_cat(str, p, clen);
+ }
+ else if (rb_enc_isprint(c, enc)) {
+ rb_str_buf_cat(str, p, clen);
+ }
+ else if (!rb_enc_isspace(c, enc)) {
+ char b[8];
+
+ hex:
+ snprintf(b, sizeof(b), "\\x%02X", c);
+ rb_str_buf_cat(str, b, 4);
+ }
+ else {
+ rb_str_buf_cat(str, p, clen);
+ }
+ p += clen;
+ }
}
}
@@ -460,20 +459,20 @@ rb_reg_desc(const char *s, long len, VALUE re)
if (resenc == NULL) resenc = rb_default_external_encoding();
if (re && rb_enc_asciicompat(enc)) {
- rb_enc_copy(str, re);
+ rb_enc_copy(str, re);
}
else {
- rb_enc_associate(str, rb_usascii_encoding());
+ rb_enc_associate(str, rb_usascii_encoding());
}
rb_reg_expr_str(str, s, len, enc, resenc, '/');
rb_str_buf_cat2(str, "/");
if (re) {
- char opts[OPTBUF_SIZE];
- rb_reg_check(re);
- if (*option_to_str(opts, RREGEXP_PTR(re)->options))
- rb_str_buf_cat2(str, opts);
- if (RBASIC(re)->flags & REG_ENCODING_NONE)
- rb_str_buf_cat2(str, "n");
+ char opts[OPTBUF_SIZE];
+ rb_reg_check(re);
+ if (*option_to_str(opts, RREGEXP_PTR(re)->options))
+ rb_str_buf_cat2(str, opts);
+ if (RBASIC(re)->flags & REG_ENCODING_NONE)
+ rb_str_buf_cat2(str, "n");
}
return str;
}
@@ -581,10 +580,10 @@ rb_reg_str_with_term(VALUE re, int term)
len = RREGEXP_SRC_LEN(re);
again:
if (len >= 4 && ptr[0] == '(' && ptr[1] == '?') {
- int err = 1;
- ptr += 2;
- if ((len -= 2) > 0) {
- do {
+ int err = 1;
+ ptr += 2;
+ if ((len -= 2) > 0) {
+ do {
opt = char_to_option((int )*ptr);
if (opt != 0) {
options |= opt;
@@ -592,13 +591,13 @@ rb_reg_str_with_term(VALUE re, int term)
else {
break;
}
- ++ptr;
- } while (--len > 0);
- }
- if (len > 1 && *ptr == '-') {
- ++ptr;
- --len;
- do {
+ ++ptr;
+ } while (--len > 0);
+ }
+ if (len > 1 && *ptr == '-') {
+ ++ptr;
+ --len;
+ do {
opt = char_to_option((int )*ptr);
if (opt != 0) {
options &= ~opt;
@@ -606,65 +605,65 @@ rb_reg_str_with_term(VALUE re, int term)
else {
break;
}
- ++ptr;
- } while (--len > 0);
- }
- if (*ptr == ')') {
- --len;
- ++ptr;
- goto again;
- }
- if (*ptr == ':' && ptr[len-1] == ')') {
- Regexp *rp;
- VALUE verbose = ruby_verbose;
- ruby_verbose = Qfalse;
-
- ++ptr;
- len -= 2;
+ ++ptr;
+ } while (--len > 0);
+ }
+ if (*ptr == ')') {
+ --len;
+ ++ptr;
+ goto again;
+ }
+ if (*ptr == ':' && ptr[len-1] == ')') {
+ Regexp *rp;
+ VALUE verbose = ruby_verbose;
+ ruby_verbose = Qfalse;
+
+ ++ptr;
+ len -= 2;
err = onig_new(&rp, ptr, ptr + len, options,
- enc, OnigDefaultSyntax, NULL);
- onig_free(rp);
- ruby_verbose = verbose;
- }
- if (err) {
- options = RREGEXP_PTR(re)->options;
- ptr = (UChar*)RREGEXP_SRC_PTR(re);
- len = RREGEXP_SRC_LEN(re);
- }
+ enc, OnigDefaultSyntax, NULL);
+ onig_free(rp);
+ ruby_verbose = verbose;
+ }
+ if (err) {
+ options = RREGEXP_PTR(re)->options;
+ ptr = (UChar*)RREGEXP_SRC_PTR(re);
+ len = RREGEXP_SRC_LEN(re);
+ }
}
if (*option_to_str(optbuf, options)) rb_str_buf_cat2(str, optbuf);
if ((options & embeddable) != embeddable) {
- optbuf[0] = '-';
- option_to_str(optbuf + 1, ~options);
- rb_str_buf_cat2(str, optbuf);
+ optbuf[0] = '-';
+ option_to_str(optbuf + 1, ~options);
+ rb_str_buf_cat2(str, optbuf);
}
rb_str_buf_cat2(str, ":");
if (rb_enc_asciicompat(enc)) {
- rb_reg_expr_str(str, (char*)ptr, len, enc, NULL, term);
- rb_str_buf_cat2(str, ")");
+ rb_reg_expr_str(str, (char*)ptr, len, enc, NULL, term);
+ rb_str_buf_cat2(str, ")");
}
else {
- const char *s, *e;
- char *paren;
- ptrdiff_t n;
- rb_str_buf_cat2(str, ")");
- rb_enc_associate(str, rb_usascii_encoding());
- str = rb_str_encode(str, rb_enc_from_encoding(enc), 0, Qnil);
-
- /* backup encoded ")" to paren */
- s = RSTRING_PTR(str);
- e = RSTRING_END(str);
- s = rb_enc_left_char_head(s, e-1, e, enc);
- n = e - s;
- paren = ALLOCA_N(char, n);
- memcpy(paren, s, n);
- rb_str_resize(str, RSTRING_LEN(str) - n);
-
- rb_reg_expr_str(str, (char*)ptr, len, enc, NULL, term);
- rb_str_buf_cat(str, paren, n);
+ const char *s, *e;
+ char *paren;
+ ptrdiff_t n;
+ rb_str_buf_cat2(str, ")");
+ rb_enc_associate(str, rb_usascii_encoding());
+ str = rb_str_encode(str, rb_enc_from_encoding(enc), 0, Qnil);
+
+ /* backup encoded ")" to paren */
+ s = RSTRING_PTR(str);
+ e = RSTRING_END(str);
+ s = rb_enc_left_char_head(s, e-1, e, enc);
+ n = e - s;
+ paren = ALLOCA_N(char, n);
+ memcpy(paren, s, n);
+ rb_str_resize(str, RSTRING_LEN(str) - n);
+
+ rb_reg_expr_str(str, (char*)ptr, len, enc, NULL, term);
+ rb_str_buf_cat(str, paren, n);
}
rb_enc_copy(str, re);
@@ -710,7 +709,7 @@ static VALUE
rb_reg_error_desc(VALUE str, int options, const char *err)
{
return rb_enc_reg_error_desc(RSTRING_PTR(str), RSTRING_LEN(str),
- rb_enc_get(str), options, err);
+ rb_enc_get(str), options, err);
}
NORETURN(static void rb_reg_raise_str(VALUE str, int options, const char *err));
@@ -864,8 +863,8 @@ rb_reg_named_captures(VALUE re)
static int
onig_new_with_source(regex_t** reg, const UChar* pattern, const UChar* pattern_end,
- OnigOptionType option, OnigEncoding enc, const OnigSyntaxType* syntax,
- OnigErrorInfo* einfo, const char *sourcefile, int sourceline)
+ OnigOptionType option, OnigEncoding enc, const OnigSyntaxType* syntax,
+ OnigErrorInfo* einfo, const char *sourcefile, int sourceline)
{
int r;
@@ -878,15 +877,15 @@ onig_new_with_source(regex_t** reg, const UChar* pattern, const UChar* pattern_e
r = onig_compile_ruby(*reg, pattern, pattern_end, einfo, sourcefile, sourceline);
if (r) {
err:
- onig_free(*reg);
- *reg = NULL;
+ onig_free(*reg);
+ *reg = NULL;
}
return r;
}
static Regexp*
make_regexp(const char *s, long len, rb_encoding *enc, int flags, onig_errmsg_buffer err,
- const char *sourcefile, int sourceline)
+ const char *sourcefile, int sourceline)
{
Regexp *rp;
int r;
@@ -900,10 +899,10 @@ make_regexp(const char *s, long len, rb_encoding *enc, int flags, onig_errmsg_bu
*/
r = onig_new_with_source(&rp, (UChar*)s, (UChar*)(s + len), flags,
- enc, OnigDefaultSyntax, &einfo, sourcefile, sourceline);
+ enc, OnigDefaultSyntax, &einfo, sourcefile, sourceline);
if (r) {
- onig_error_code_to_str((UChar*)err, r, &einfo);
- return 0;
+ onig_error_code_to_str((UChar*)err, r, &einfo);
+ return 0;
}
return rp;
}
@@ -1068,12 +1067,13 @@ update_char_offset(VALUE match)
}
}
-static void
+static VALUE
match_check(VALUE match)
{
if (!RMATCH(match)->regexp) {
- rb_raise(rb_eTypeError, "uninitialized MatchData");
+ rb_raise(rb_eTypeError, "uninitialized MatchData");
}
+ return match;
}
/* :nodoc: */
@@ -1089,7 +1089,7 @@ match_init_copy(VALUE obj, VALUE orig)
rm = RMATCH(obj)->rmatch;
if (rb_reg_region_copy(&rm->regs, RMATCH_REGS(orig)))
- rb_memerror();
+ rb_memerror();
if (RMATCH(orig)->rmatch->char_offset_num_allocated) {
if (rm->char_offset_num_allocated < rm->regs.num_regs) {
@@ -1098,7 +1098,7 @@ match_init_copy(VALUE obj, VALUE orig)
}
MEMCPY(rm->char_offset, RMATCH(orig)->rmatch->char_offset,
struct rmatch_offset, rm->regs.num_regs);
- RB_GC_GUARD(orig);
+ RB_GC_GUARD(orig);
}
return obj;
@@ -1123,9 +1123,9 @@ match_regexp(VALUE match)
match_check(match);
regexp = RMATCH(match)->regexp;
if (NIL_P(regexp)) {
- VALUE str = rb_reg_nth_match(0, match);
- regexp = rb_reg_regcomp(rb_reg_quote(str));
- RMATCH(match)->regexp = regexp;
+ VALUE str = rb_reg_nth_match(0, match);
+ regexp = rb_reg_regcomp(rb_reg_quote(str));
+ RMATCH(match)->regexp = regexp;
}
return regexp;
}
@@ -1156,7 +1156,7 @@ match_names(VALUE match)
{
match_check(match);
if (NIL_P(RMATCH(match)->regexp))
- return rb_ary_new_capa(0);
+ return rb_ary_new_capa(0);
return rb_reg_names(RMATCH(match)->regexp);
}
@@ -1188,14 +1188,14 @@ static void
name_to_backref_error(VALUE name)
{
rb_raise(rb_eIndexError, "undefined group name reference: % "PRIsVALUE,
- name);
+ name);
}
static void
backref_number_check(struct re_registers *regs, int i)
{
if (i < 0 || regs->num_regs <= i)
- rb_raise(rb_eIndexError, "index %d out of matches", i);
+ rb_raise(rb_eIndexError, "index %d out of matches", i);
}
static int
@@ -1209,10 +1209,10 @@ match_backref_number(VALUE match, VALUE backref)
match_check(match);
if (SYMBOL_P(backref)) {
- backref = rb_sym2str(backref);
+ backref = rb_sym2str(backref);
}
else if (!RB_TYPE_P(backref, T_STRING)) {
- return NUM2INT(backref);
+ return NUM2INT(backref);
}
name = StringValueCStr(backref);
@@ -1250,7 +1250,7 @@ match_offset(VALUE match, VALUE n)
backref_number_check(regs, i);
if (BEG(i) < 0)
- return rb_assoc_new(Qnil, Qnil);
+ return rb_assoc_new(Qnil, Qnil);
update_char_offset(match);
return rb_assoc_new(LONG2NUM(RMATCH(match)->rmatch->char_offset[i].beg),
@@ -1309,7 +1309,7 @@ match_begin(VALUE match, VALUE n)
backref_number_check(regs, i);
if (BEG(i) < 0)
- return Qnil;
+ return Qnil;
update_char_offset(match);
return LONG2NUM(RMATCH(match)->rmatch->char_offset[i].beg);
@@ -1335,7 +1335,7 @@ match_end(VALUE match, VALUE n)
backref_number_check(regs, i);
if (BEG(i) < 0)
- return Qnil;
+ return Qnil;
update_char_offset(match);
return LONG2NUM(RMATCH(match)->rmatch->char_offset[i].end);
@@ -1377,7 +1377,7 @@ match_nth(VALUE match, VALUE n)
long start = BEG(i), end = END(i);
if (start < 0)
- return Qnil;
+ return Qnil;
return rb_str_subseq(RMATCH(match)->str, start, end - start);
}
@@ -1421,11 +1421,11 @@ match_nth_length(VALUE match, VALUE n)
backref_number_check(regs, i);
if (BEG(i) < 0)
- return Qnil;
+ return Qnil;
update_char_offset(match);
const struct rmatch_offset *const ofs =
- &RMATCH(match)->rmatch->char_offset[i];
+ &RMATCH(match)->rmatch->char_offset[i];
return LONG2NUM(ofs->end - ofs->beg);
}
@@ -1461,11 +1461,11 @@ rb_match_nth_defined(int nth, VALUE match)
regs = RMATCH_REGS(match);
if (!regs) return FALSE;
if (nth >= regs->num_regs) {
- return FALSE;
+ return FALSE;
}
if (nth < 0) {
- nth += regs->num_regs;
- if (nth <= 0) return FALSE;
+ nth += regs->num_regs;
+ if (nth <= 0) return FALSE;
}
return (BEG(nth) != -1);
}
@@ -1489,7 +1489,7 @@ rb_backref_set_string(VALUE string, long pos, long len)
{
VALUE match = rb_backref_get();
if (NIL_P(match) || FL_TEST(match, MATCH_BUSY)) {
- match = match_alloc(rb_cMatch);
+ match = match_alloc(rb_cMatch);
}
match_set_string(match, string, pos, len);
rb_backref_set(match);
@@ -1540,9 +1540,9 @@ static void
reg_enc_error(VALUE re, VALUE str)
{
rb_raise(rb_eEncCompatError,
- "incompatible encoding regexp match (%s regexp with %s string)",
- rb_enc_name(rb_enc_get(re)),
- rb_enc_name(rb_enc_get(str)));
+ "incompatible encoding regexp match (%s regexp with %s string)",
+ rb_enc_name(rb_enc_get(re)),
+ rb_enc_name(rb_enc_get(str)));
}
static inline int
@@ -1550,7 +1550,7 @@ str_coderange(VALUE str)
{
int cr = ENC_CODERANGE(str);
if (cr == ENC_CODERANGE_UNKNOWN) {
- cr = rb_enc_str_coderange(str);
+ cr = rb_enc_str_coderange(str);
}
return cr;
}
@@ -1572,24 +1572,24 @@ rb_reg_prepare_enc(VALUE re, VALUE str, int warn)
if (RREGEXP_PTR(re)->enc == enc) {
}
else if (cr == ENC_CODERANGE_7BIT &&
- RREGEXP_PTR(re)->enc == rb_usascii_encoding()) {
- enc = RREGEXP_PTR(re)->enc;
+ RREGEXP_PTR(re)->enc == rb_usascii_encoding()) {
+ enc = RREGEXP_PTR(re)->enc;
}
else if (!rb_enc_asciicompat(enc)) {
- reg_enc_error(re, str);
+ reg_enc_error(re, str);
}
else if (rb_reg_fixed_encoding_p(re)) {
if ((!rb_enc_asciicompat(RREGEXP_PTR(re)->enc) ||
- cr != ENC_CODERANGE_7BIT)) {
- reg_enc_error(re, str);
- }
- enc = RREGEXP_PTR(re)->enc;
+ cr != ENC_CODERANGE_7BIT)) {
+ reg_enc_error(re, str);
+ }
+ enc = RREGEXP_PTR(re)->enc;
}
else if (warn && (RBASIC(re)->flags & REG_ENCODING_NONE) &&
- enc != rb_ascii8bit_encoding() &&
- cr != ENC_CODERANGE_7BIT) {
- rb_warn("historical binary regexp match /.../n against %s string",
- rb_enc_name(enc));
+ enc != rb_ascii8bit_encoding() &&
+ cr != ENC_CODERANGE_7BIT) {
+ rb_warn("historical binary regexp match /.../n against %s string",
+ rb_enc_name(enc));
}
return enc;
}
@@ -1612,11 +1612,11 @@ rb_reg_prepare_re0(VALUE re, VALUE str, onig_errmsg_buffer err)
pattern = RREGEXP_SRC_PTR(re);
unescaped = rb_reg_preprocess(
- pattern, pattern + RREGEXP_SRC_LEN(re), enc,
+ pattern, pattern + RREGEXP_SRC_LEN(re), enc,
&fixed_enc, err, 0);
if (NIL_P(unescaped)) {
- rb_raise(rb_eArgError, "regexp preprocess failed: %s", err);
+ rb_raise(rb_eArgError, "regexp preprocess failed: %s", err);
}
// inherit the timeout settings
@@ -1626,11 +1626,11 @@ rb_reg_prepare_re0(VALUE re, VALUE str, onig_errmsg_buffer err)
long len;
RSTRING_GETMEM(unescaped, ptr, len);
r = onig_new(&reg, (UChar *)ptr, (UChar *)(ptr + len),
- reg->options, enc,
- OnigDefaultSyntax, &einfo);
+ reg->options, enc,
+ OnigDefaultSyntax, &einfo);
if (r) {
- onig_error_code_to_str((UChar*)err, r, &einfo);
- rb_reg_raise(pattern, RREGEXP_SRC_LEN(re), err, re);
+ onig_error_code_to_str((UChar*)err, r, &einfo);
+ rb_reg_raise(pattern, RREGEXP_SRC_LEN(re), err, re);
}
reg->timelimit = timelimit;
@@ -1656,22 +1656,22 @@ rb_reg_adjust_startpos(VALUE re, VALUE str, long pos, int reverse)
enc = rb_reg_prepare_enc(re, str, 0);
if (reverse) {
- range = -pos;
+ range = -pos;
}
else {
- range = RSTRING_LEN(str) - pos;
+ range = RSTRING_LEN(str) - pos;
}
if (pos > 0 && ONIGENC_MBC_MAXLEN(enc) != 1 && pos < RSTRING_LEN(str)) {
- string = (UChar*)RSTRING_PTR(str);
+ string = (UChar*)RSTRING_PTR(str);
- if (range > 0) {
- p = onigenc_get_right_adjust_char_head(enc, string, string + pos, string + RSTRING_LEN(str));
- }
- else {
- p = ONIGENC_LEFT_ADJUST_CHAR_HEAD(enc, string, string + pos, string + RSTRING_LEN(str));
- }
- return p - string;
+ if (range > 0) {
+ p = onigenc_get_right_adjust_char_head(enc, string, string + pos, string + RSTRING_LEN(str));
+ }
+ else {
+ p = ONIGENC_LEFT_ADJUST_CHAR_HEAD(enc, string, string + pos, string + RSTRING_LEN(str));
+ }
+ return p - string;
}
return pos;
@@ -1693,8 +1693,8 @@ rb_reg_search_set_match(VALUE re, VALUE str, long pos, int reverse, int set_back
RSTRING_GETMEM(str, start, len);
range = start;
if (pos > len || pos < 0) {
- rb_backref_set(Qnil);
- return -1;
+ rb_backref_set(Qnil);
+ return -1;
}
reg = rb_reg_prepare_re0(re, str, err);
@@ -1703,44 +1703,49 @@ rb_reg_search_set_match(VALUE re, VALUE str, long pos, int reverse, int set_back
MEMZERO(regs, struct re_registers, 1);
if (!reverse) {
- range += len;
+ range += len;
}
result = onig_search(reg,
- (UChar*)start,
- ((UChar*)(start + len)),
- ((UChar*)(start + pos)),
- ((UChar*)range),
- regs, ONIG_OPTION_NONE);
+ (UChar*)start,
+ ((UChar*)(start + len)),
+ ((UChar*)(start + pos)),
+ ((UChar*)range),
+ regs, ONIG_OPTION_NONE);
if (!tmpreg) RREGEXP(re)->usecnt--;
if (tmpreg) {
- if (RREGEXP(re)->usecnt) {
- onig_free(reg);
- }
- else {
- onig_free(RREGEXP_PTR(re));
- RREGEXP_PTR(re) = reg;
- }
+ if (RREGEXP(re)->usecnt) {
+ onig_free(reg);
+ }
+ else {
+ onig_free(RREGEXP_PTR(re));
+ RREGEXP_PTR(re) = reg;
+ }
}
if (result < 0) {
- if (regs == &regi)
- onig_region_free(regs, 0);
- if (result == ONIG_MISMATCH) {
- rb_backref_set(Qnil);
- return result;
- }
- else {
- onig_error_code_to_str((UChar*)err, (int)result);
- rb_reg_raise(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), err, re);
- }
+ if (regs == &regi)
+ onig_region_free(regs, 0);
+ if (result == ONIG_MISMATCH) {
+ rb_backref_set(Qnil);
+ return result;
+ }
+ else {
+ onig_error_code_to_str((UChar*)err, (int)result);
+ rb_reg_raise(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), err, re);
+ }
}
match = match_alloc(rb_cMatch);
- int copy_err = rb_reg_region_copy(RMATCH_REGS(match), regs);
- onig_region_free(regs, 0);
- if (copy_err) rb_memerror();
+ memcpy(RMATCH_REGS(match), regs, sizeof(struct re_registers));
if (set_backref_str) {
- RMATCH(match)->str = rb_str_new4(str);
+ RMATCH(match)->str = rb_str_new4(str);
+ }
+ else {
+ /* Note that a MatchData object with RMATCH(match)->str == 0 is incomplete!
+ * We need to hide the object from ObjectSpace.each_object.
+ * https://bugs.ruby-lang.org/issues/19159
+ */
+ rb_obj_hide(match);
}
RMATCH(match)->regexp = re;
@@ -1778,53 +1783,53 @@ rb_reg_start_with_p(VALUE re, VALUE str)
match = rb_backref_get();
if (!NIL_P(match)) {
- if (FL_TEST(match, MATCH_BUSY)) {
- match = Qnil;
- }
- else {
- regs = RMATCH_REGS(match);
- }
+ if (FL_TEST(match, MATCH_BUSY)) {
+ match = Qnil;
+ }
+ else {
+ regs = RMATCH_REGS(match);
+ }
}
if (NIL_P(match)) {
- MEMZERO(regs, struct re_registers, 1);
+ MEMZERO(regs, struct re_registers, 1);
}
const char *ptr;
long len;
RSTRING_GETMEM(str, ptr, len);
result = onig_match(reg,
- (UChar*)(ptr),
- ((UChar*)(ptr + len)),
- (UChar*)(ptr),
- regs, ONIG_OPTION_NONE);
+ (UChar*)(ptr),
+ ((UChar*)(ptr + len)),
+ (UChar*)(ptr),
+ regs, ONIG_OPTION_NONE);
if (!tmpreg) RREGEXP(re)->usecnt--;
if (tmpreg) {
- if (RREGEXP(re)->usecnt) {
- onig_free(reg);
- }
- else {
- onig_free(RREGEXP_PTR(re));
- RREGEXP_PTR(re) = reg;
- }
+ if (RREGEXP(re)->usecnt) {
+ onig_free(reg);
+ }
+ else {
+ onig_free(RREGEXP_PTR(re));
+ RREGEXP_PTR(re) = reg;
+ }
}
if (result < 0) {
- if (regs == &regi)
- onig_region_free(regs, 0);
- if (result == ONIG_MISMATCH) {
- rb_backref_set(Qnil);
- return false;
- }
- else {
- onig_error_code_to_str((UChar*)err, (int)result);
- rb_reg_raise(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), err, re);
- }
+ if (regs == &regi)
+ onig_region_free(regs, 0);
+ if (result == ONIG_MISMATCH) {
+ rb_backref_set(Qnil);
+ return false;
+ }
+ else {
+ onig_error_code_to_str((UChar*)err, (int)result);
+ rb_reg_raise(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), err, re);
+ }
}
if (NIL_P(match)) {
- int err;
- match = match_alloc(rb_cMatch);
- err = rb_reg_region_copy(RMATCH_REGS(match), regs);
- onig_region_free(regs, 0);
- if (err) rb_memerror();
+ int err;
+ match = match_alloc(rb_cMatch);
+ err = rb_reg_region_copy(RMATCH_REGS(match), regs);
+ onig_region_free(regs, 0);
+ if (err) rb_memerror();
}
RMATCH(match)->str = rb_str_new4(str);
@@ -1843,11 +1848,11 @@ rb_reg_nth_defined(int nth, VALUE match)
match_check(match);
regs = RMATCH_REGS(match);
if (nth >= regs->num_regs) {
- return Qnil;
+ return Qnil;
}
if (nth < 0) {
- nth += regs->num_regs;
- if (nth <= 0) return Qnil;
+ nth += regs->num_regs;
+ if (nth <= 0) return Qnil;
}
return RBOOL(BEG(nth) != -1);
}
@@ -1863,11 +1868,11 @@ rb_reg_nth_match(int nth, VALUE match)
match_check(match);
regs = RMATCH_REGS(match);
if (nth >= regs->num_regs) {
- return Qnil;
+ return Qnil;
}
if (nth < 0) {
- nth += regs->num_regs;
- if (nth <= 0) return Qnil;
+ nth += regs->num_regs;
+ if (nth <= 0) return Qnil;
}
start = BEG(nth);
if (start == -1) return Qnil;
@@ -1963,7 +1968,7 @@ rb_reg_match_last(VALUE match)
if (BEG(0) == -1) return Qnil;
for (i=regs->num_regs-1; BEG(i) == -1 && i > 0; i--)
- ;
+ ;
if (i == 0) return Qnil;
return rb_reg_nth_match(i, match);
}
@@ -2006,13 +2011,13 @@ match_array(VALUE match, int start)
target = RMATCH(match)->str;
for (i=start; i<regs->num_regs; i++) {
- if (regs->beg[i] == -1) {
- rb_ary_push(ary, Qnil);
- }
- else {
- VALUE str = rb_str_subseq(target, regs->beg[i], regs->end[i]-regs->beg[i]);
- rb_ary_push(ary, str);
- }
+ if (regs->beg[i] == -1) {
+ rb_ary_push(ary, Qnil);
+ }
+ else {
+ VALUE str = rb_str_subseq(target, regs->beg[i], regs->end[i]-regs->beg[i]);
+ rb_ary_push(ary, str);
+ }
}
return ary;
}
@@ -2065,7 +2070,7 @@ name_to_backref_number(struct re_registers *regs, VALUE regexp, const char* name
{
if (NIL_P(regexp)) return -1;
return onig_name_to_backref_number(RREGEXP_PTR(regexp),
- (const unsigned char *)name, (const unsigned char *)name_end, regs);
+ (const unsigned char *)name, (const unsigned char *)name_end, regs);
}
#define NAME_TO_NUMBER(regs, re, name, name_ptr, name_end) \
@@ -2079,15 +2084,15 @@ namev_to_backref_number(struct re_registers *regs, VALUE re, VALUE name)
int num;
if (SYMBOL_P(name)) {
- name = rb_sym2str(name);
+ name = rb_sym2str(name);
}
else if (!RB_TYPE_P(name, T_STRING)) {
- return -1;
+ return -1;
}
num = NAME_TO_NUMBER(regs, re, name,
- RSTRING_PTR(name), RSTRING_END(name));
+ RSTRING_PTR(name), RSTRING_END(name));
if (num < 1) {
- name_to_backref_error(name);
+ name_to_backref_error(name);
}
return num;
}
@@ -2101,10 +2106,10 @@ match_ary_subseq(VALUE match, long beg, long len, VALUE result)
if (len == 0) return result;
for (j = beg; j < end; j++) {
- rb_ary_push(result, rb_reg_nth_match((int)j, match));
+ rb_ary_push(result, rb_reg_nth_match((int)j, match));
}
if (beg + len > j) {
- rb_ary_resize(result, RARRAY_LEN(result) + (beg + len) - j);
+ rb_ary_resize(result, RARRAY_LEN(result) + (beg + len) - j);
}
return result;
}
@@ -2118,13 +2123,13 @@ match_ary_aref(VALUE match, VALUE idx, VALUE result)
/* check if idx is Range */
switch (rb_range_beg_len(idx, &beg, &len, (long)num_regs, !NIL_P(result))) {
case Qfalse:
- if (NIL_P(result)) return rb_reg_nth_match(NUM2INT(idx), match);
- rb_ary_push(result, rb_reg_nth_match(NUM2INT(idx), match));
- return result;
+ if (NIL_P(result)) return rb_reg_nth_match(NUM2INT(idx), match);
+ rb_ary_push(result, rb_reg_nth_match(NUM2INT(idx), match));
+ return result;
case Qnil:
- return Qnil;
+ return Qnil;
default:
- return match_ary_subseq(match, beg, len, result);
+ return match_ary_subseq(match, beg, len, result);
}
}
@@ -2164,37 +2169,37 @@ match_aref(int argc, VALUE *argv, VALUE match)
rb_scan_args(argc, argv, "11", &idx, &length);
if (NIL_P(length)) {
- if (FIXNUM_P(idx)) {
- return rb_reg_nth_match(FIX2INT(idx), match);
- }
- else {
- int num = namev_to_backref_number(RMATCH_REGS(match), RMATCH(match)->regexp, idx);
- if (num >= 0) {
- return rb_reg_nth_match(num, match);
- }
- else {
- return match_ary_aref(match, idx, Qnil);
- }
- }
+ if (FIXNUM_P(idx)) {
+ return rb_reg_nth_match(FIX2INT(idx), match);
+ }
+ else {
+ int num = namev_to_backref_number(RMATCH_REGS(match), RMATCH(match)->regexp, idx);
+ if (num >= 0) {
+ return rb_reg_nth_match(num, match);
+ }
+ else {
+ return match_ary_aref(match, idx, Qnil);
+ }
+ }
}
else {
- long beg = NUM2LONG(idx);
- long len = NUM2LONG(length);
- long num_regs = RMATCH_REGS(match)->num_regs;
- if (len < 0) {
- return Qnil;
- }
- if (beg < 0) {
- beg += num_regs;
- if (beg < 0) return Qnil;
- }
- else if (beg > num_regs) {
- return Qnil;
- }
- if (beg+len > num_regs) {
- len = num_regs - beg;
- }
- return match_ary_subseq(match, beg, len, Qnil);
+ long beg = NUM2LONG(idx);
+ long len = NUM2LONG(length);
+ long num_regs = RMATCH_REGS(match)->num_regs;
+ if (len < 0) {
+ return Qnil;
+ }
+ if (beg < 0) {
+ beg += num_regs;
+ if (beg < 0) return Qnil;
+ }
+ else if (beg > num_regs) {
+ return Qnil;
+ }
+ if (beg+len > num_regs) {
+ len = num_regs - beg;
+ }
+ return match_ary_subseq(match, beg, len, Qnil);
}
}
@@ -2234,18 +2239,18 @@ match_values_at(int argc, VALUE *argv, VALUE match)
result = rb_ary_new2(argc);
for (i=0; i<argc; i++) {
- if (FIXNUM_P(argv[i])) {
- rb_ary_push(result, rb_reg_nth_match(FIX2INT(argv[i]), match));
- }
- else {
- int num = namev_to_backref_number(RMATCH_REGS(match), RMATCH(match)->regexp, argv[i]);
- if (num >= 0) {
- rb_ary_push(result, rb_reg_nth_match(num, match));
- }
- else {
- match_ary_aref(match, argv[i], result);
- }
- }
+ if (FIXNUM_P(argv[i])) {
+ rb_ary_push(result, rb_reg_nth_match(FIX2INT(argv[i]), match));
+ }
+ else {
+ int num = namev_to_backref_number(RMATCH_REGS(match), RMATCH(match)->regexp, argv[i]);
+ if (num >= 0) {
+ rb_ary_push(result, rb_reg_nth_match(num, match));
+ }
+ else {
+ match_ary_aref(match, argv[i], result);
+ }
+ }
}
return result;
}
@@ -2272,36 +2277,42 @@ match_values_at(int argc, VALUE *argv, VALUE match)
static VALUE
match_to_s(VALUE match)
{
- VALUE str = rb_reg_last_match(match);
+ VALUE str = rb_reg_last_match(match_check(match));
- match_check(match);
if (NIL_P(str)) str = rb_str_new(0,0);
return str;
}
static int
match_named_captures_iter(const OnigUChar *name, const OnigUChar *name_end,
- int back_num, int *back_refs, OnigRegex regex, void *arg) {
+ int back_num, int *back_refs, OnigRegex regex, void *arg)
+{
struct MEMO *memo = MEMO_CAST(arg);
VALUE hash = memo->v1;
VALUE match = memo->v2;
+ long symbolize = memo->u3.state;
VALUE key = rb_enc_str_new((const char *)name, name_end-name, regex->enc);
+
+ if (symbolize > 0) {
+ key = rb_str_intern(key);
+ }
+
VALUE value;
int i;
int found = 0;
for (i = 0; i < back_num; i++) {
- value = rb_reg_nth_match(back_refs[i], match);
- if (RTEST(value)) {
- rb_hash_aset(hash, key, value);
- found = 1;
- }
+ value = rb_reg_nth_match(back_refs[i], match);
+ if (RTEST(value)) {
+ rb_hash_aset(hash, key, value);
+ found = 1;
+ }
}
if (found == 0) {
- rb_hash_aset(hash, key, Qnil);
+ rb_hash_aset(hash, key, Qnil);
}
return 0;
@@ -2340,7 +2351,7 @@ match_named_captures(VALUE match)
match_check(match);
if (NIL_P(RMATCH(match)->regexp))
- return rb_hash_new();
+ return rb_hash_new();
hash = rb_hash_new();
memo = MEMO_NEW(hash, match, 0);
@@ -2352,6 +2363,75 @@ match_named_captures(VALUE match)
/*
* call-seq:
+ * deconstruct_keys(array_of_names) -> hash
+ *
+ * Returns a hash of the named captures for the given names.
+ *
+ * m = /(?<hours>\d{2}):(?<minutes>\d{2}):(?<seconds>\d{2})/.match("18:37:22")
+ * m.deconstruct_keys([:hours, :minutes]) # => {:hours => "18", :minutes => "37"}
+ * m.deconstruct_keys(nil) # => {:hours => "18", :minutes => "37", :seconds => "22"}
+ *
+ * Returns an empty hash of no named captures were defined:
+ *
+ * m = /(\d{2}):(\d{2}):(\d{2})/.match("18:37:22")
+ * m.deconstruct_keys(nil) # => {}
+ *
+ */
+static VALUE
+match_deconstruct_keys(VALUE match, VALUE keys)
+{
+ VALUE h;
+ long i;
+
+ match_check(match);
+
+ if (NIL_P(RMATCH(match)->regexp)) {
+ return rb_hash_new_with_size(0);
+ }
+
+ if (NIL_P(keys)) {
+ h = rb_hash_new_with_size(onig_number_of_names(RREGEXP_PTR(RMATCH(match)->regexp)));
+
+ struct MEMO *memo;
+ memo = MEMO_NEW(h, match, 1);
+
+ onig_foreach_name(RREGEXP_PTR(RMATCH(match)->regexp), match_named_captures_iter, (void*)memo);
+
+ return h;
+ }
+
+ Check_Type(keys, T_ARRAY);
+
+ if (onig_number_of_names(RREGEXP_PTR(RMATCH(match)->regexp)) < RARRAY_LEN(keys)) {
+ return rb_hash_new_with_size(0);
+ }
+
+ h = rb_hash_new_with_size(RARRAY_LEN(keys));
+
+ for (i=0; i<RARRAY_LEN(keys); i++) {
+ VALUE key = RARRAY_AREF(keys, i);
+ VALUE name;
+
+ Check_Type(key, T_SYMBOL);
+
+ name = rb_sym2str(key);
+
+ int num = NAME_TO_NUMBER(RMATCH_REGS(match), RMATCH(match)->regexp, RMATCH(match)->regexp,
+ RSTRING_PTR(name), RSTRING_END(name));
+
+ if (num >= 0) {
+ rb_hash_aset(h, key, rb_reg_nth_match(num, match));
+ }
+ else {
+ return h;
+ }
+ }
+
+ return h;
+}
+
+/*
+ * call-seq:
* string -> string
*
* Returns the target string if it was frozen;
@@ -2427,7 +2507,7 @@ match_inspect(VALUE match)
}
else if (NIL_P(regexp)) {
return rb_sprintf("#<%"PRIsVALUE": %"PRIsVALUE">",
- cname, rb_reg_nth_match(0, match));
+ cname, rb_reg_nth_match(0, match));
}
names = ALLOCA_N(struct backref_name_tag, num_regs);
@@ -2721,14 +2801,18 @@ unescape_unicode_bmp(const char **pp, const char *end,
}
static int
-unescape_nonascii(const char *p, const char *end, rb_encoding *enc,
+unescape_nonascii0(const char **pp, const char *end, rb_encoding *enc,
VALUE buf, rb_encoding **encp, int *has_property,
- onig_errmsg_buffer err, int options)
+ onig_errmsg_buffer err, int options, int recurse)
{
+ const char *p = *pp;
unsigned char c;
char smallbuf[2];
int in_char_class = 0;
+ int parens = 1; /* ignored unless recurse is true */
+ int extended_mode = options & ONIG_OPTION_EXTEND;
+begin_scan:
while (p < end) {
int chlen = rb_enc_precise_mbclen(p, end, enc);
if (!MBCLEN_CHARFOUND_P(chlen)) {
@@ -2761,7 +2845,7 @@ unescape_nonascii(const char *p, const char *end, rb_encoding *enc,
goto invalid_multibyte;
}
if ((chlen = MBCLEN_CHARFOUND_LEN(chlen)) > 1) {
- /* include the previous backslash */
+ /* include the previous backslash */
--p;
++chlen;
goto multibyte;
@@ -2787,17 +2871,17 @@ unescape_nonascii(const char *p, const char *end, rb_encoding *enc,
case 'C': /* \C-X, \C-\M-X */
case 'M': /* \M-X, \M-\C-X, \M-\cX */
p = p-2;
- if (enc == rb_usascii_encoding()) {
- const char *pbeg = p;
+ if (rb_is_usascii_enc(enc)) {
+ const char *pbeg = p;
int byte = read_escaped_byte(&p, end, err);
if (byte == -1) return -1;
c = byte;
- rb_str_buf_cat(buf, pbeg, p-pbeg);
- }
- else {
- if (unescape_escaped_nonascii(&p, end, enc, buf, encp, err) != 0)
- return -1;
- }
+ rb_str_buf_cat(buf, pbeg, p-pbeg);
+ }
+ else {
+ if (unescape_escaped_nonascii(&p, end, enc, buf, encp, err) != 0)
+ return -1;
+ }
break;
case 'u':
@@ -2840,9 +2924,13 @@ escape_asis:
break;
case '#':
- if ((options & ONIG_OPTION_EXTEND) && !in_char_class) {
+ if (extended_mode && !in_char_class) {
/* consume and ignore comment in extended regexp */
- while ((p < end) && ((c = *p++) != '\n'));
+ while ((p < end) && ((c = *p++) != '\n')) {
+ if ((c & 0x80) && !*encp && enc == rb_utf8_encoding()) {
+ *encp = enc;
+ }
+ }
break;
}
rb_str_buf_cat(buf, (char *)&c, 1);
@@ -2857,51 +2945,141 @@ escape_asis:
}
rb_str_buf_cat(buf, (char *)&c, 1);
break;
+ case ')':
+ rb_str_buf_cat(buf, (char *)&c, 1);
+ if (!in_char_class && recurse) {
+ if (--parens == 0) {
+ *pp = p;
+ return 0;
+ }
+ }
+ break;
case '(':
- if (!in_char_class && p + 1 < end && *p == '?' && *(p+1) == '#') {
- /* (?# is comment inside any regexp, and content inside should be ignored */
- const char *orig_p = p;
- int cont = 1;
-
- while (cont && (p < end)) {
- switch (c = *p++) {
- default:
- if (!(c & 0x80)) break;
- --p;
- /* fallthrough */
- case '\\':
- chlen = rb_enc_precise_mbclen(p, end, enc);
- if (!MBCLEN_CHARFOUND_P(chlen)) {
- goto invalid_multibyte;
+ if (!in_char_class && p + 1 < end && *p == '?') {
+ if (*(p+1) == '#') {
+ /* (?# is comment inside any regexp, and content inside should be ignored */
+ const char *orig_p = p;
+ int cont = 1;
+
+ while (cont && (p < end)) {
+ switch (c = *p++) {
+ default:
+ if (!(c & 0x80)) break;
+ if (!*encp && enc == rb_utf8_encoding()) {
+ *encp = enc;
+ }
+ --p;
+ /* fallthrough */
+ case '\\':
+ chlen = rb_enc_precise_mbclen(p, end, enc);
+ if (!MBCLEN_CHARFOUND_P(chlen)) {
+ goto invalid_multibyte;
+ }
+ p += MBCLEN_CHARFOUND_LEN(chlen);
+ break;
+ case ')':
+ cont = 0;
+ break;
}
- p += MBCLEN_CHARFOUND_LEN(chlen);
- break;
- case ')':
- cont = 0;
- break;
}
+
+ if (cont) {
+ /* unterminated (?#, rewind so it is syntax error */
+ p = orig_p;
+ c = '(';
+ rb_str_buf_cat(buf, (char *)&c, 1);
+ }
+ break;
}
+ else {
+ /* potential change of extended option */
+ int invert = 0;
+ int local_extend = 0;
+ const char *s;
+
+ if (recurse) {
+ parens++;
+ }
- if (cont) {
- /* unterminated (?#, rewind so it is syntax error */
- p = orig_p;
- c = '(';
- rb_str_buf_cat(buf, (char *)&c, 1);
+ for(s = p+1; s < end; s++) {
+ switch(*s) {
+ case 'x':
+ local_extend = invert ? -1 : 1;
+ break;
+ case '-':
+ invert = 1;
+ break;
+ case ':':
+ case ')':
+ if (local_extend == 0 ||
+ (local_extend == -1 && !extended_mode) ||
+ (local_extend == 1 && extended_mode)) {
+ /* no changes to extended flag */
+ goto fallthrough;
+ }
+
+ if (*s == ':') {
+ /* change extended flag until ')' */
+ int local_options = options;
+ if (local_extend == 1) {
+ local_options |= ONIG_OPTION_EXTEND;
+ }
+ else {
+ local_options &= ~ONIG_OPTION_EXTEND;
+ }
+
+ rb_str_buf_cat(buf, (char *)&c, 1);
+ int ret = unescape_nonascii0(&p, end, enc, buf, encp,
+ has_property, err,
+ local_options, 1);
+ if (ret < 0) return ret;
+ goto begin_scan;
+ }
+ else {
+ /* change extended flag for rest of expression */
+ extended_mode = local_extend == 1;
+ goto fallthrough;
+ }
+ case 'i':
+ case 'm':
+ case 'a':
+ case 'd':
+ case 'u':
+ /* other option flags, ignored during scanning */
+ break;
+ default:
+ /* other character, no extended flag change*/
+ goto fallthrough;
+ }
+ }
}
}
- else {
- rb_str_buf_cat(buf, (char *)&c, 1);
+ else if (!in_char_class && recurse) {
+ parens++;
}
- break;
+ /* FALLTHROUGH */
default:
+fallthrough:
rb_str_buf_cat(buf, (char *)&c, 1);
break;
}
}
+ if (recurse) {
+ *pp = p;
+ }
return 0;
}
+static int
+unescape_nonascii(const char *p, const char *end, rb_encoding *enc,
+ VALUE buf, rb_encoding **encp, int *has_property,
+ onig_errmsg_buffer err, int options)
+{
+ return unescape_nonascii0(&p, end, enc, buf, encp, has_property,
+ err, options, 0);
+}
+
static VALUE
rb_reg_preprocess(const char *p, const char *end, rb_encoding *enc,
rb_encoding **fixed_enc, onig_errmsg_buffer err, int options)
@@ -2950,7 +3128,7 @@ rb_reg_check_preprocess(VALUE str)
RB_GC_GUARD(str);
if (NIL_P(buf)) {
- return rb_reg_error_desc(str, 0, err);
+ return rb_reg_error_desc(str, 0, err);
}
return Qnil;
}
@@ -2975,14 +3153,14 @@ rb_reg_preprocess_dregexp(VALUE ary, int options)
char *p, *end;
rb_encoding *src_enc;
- src_enc = rb_enc_get(str);
- if (options & ARG_ENCODING_NONE &&
- src_enc != ascii8bit) {
- if (str_coderange(str) != ENC_CODERANGE_7BIT)
- rb_raise(rb_eRegexpError, "/.../n has a non escaped non ASCII character in non ASCII-8BIT script");
- else
- src_enc = ascii8bit;
- }
+ src_enc = rb_enc_get(str);
+ if (options & ARG_ENCODING_NONE &&
+ src_enc != ascii8bit) {
+ if (str_coderange(str) != ENC_CODERANGE_7BIT)
+ rb_raise(rb_eRegexpError, "/.../n has a non escaped non ASCII character in non ASCII-8BIT script");
+ else
+ src_enc = ascii8bit;
+ }
StringValue(str);
p = RSTRING_PTR(str);
@@ -3015,8 +3193,8 @@ rb_reg_preprocess_dregexp(VALUE ary, int options)
static int
rb_reg_initialize(VALUE obj, const char *s, long len, rb_encoding *enc,
- int options, onig_errmsg_buffer err,
- const char *sourcefile, int sourceline)
+ int options, onig_errmsg_buffer err,
+ const char *sourcefile, int sourceline)
{
struct RRegexp *re = RREGEXP(obj);
VALUE unescaped;
@@ -3025,14 +3203,14 @@ rb_reg_initialize(VALUE obj, const char *s, long len, rb_encoding *enc,
rb_check_frozen(obj);
if (FL_TEST(obj, REG_LITERAL))
- rb_raise(rb_eSecurityError, "can't modify literal regexp");
+ rb_raise(rb_eSecurityError, "can't modify literal regexp");
if (re->ptr)
rb_raise(rb_eTypeError, "already initialized regexp");
re->ptr = 0;
if (rb_enc_dummy_p(enc)) {
- errcpy(err, "can't make regexp with dummy encoding");
- return -1;
+ errcpy(err, "can't make regexp with dummy encoding");
+ return -1;
}
unescaped = rb_reg_preprocess(s, s+len, enc, &fixed_enc, err, options);
@@ -3040,15 +3218,15 @@ rb_reg_initialize(VALUE obj, const char *s, long len, rb_encoding *enc,
return -1;
if (fixed_enc) {
- if ((fixed_enc != enc && (options & ARG_ENCODING_FIXED)) ||
+ if ((fixed_enc != enc && (options & ARG_ENCODING_FIXED)) ||
(fixed_enc != a_enc && (options & ARG_ENCODING_NONE))) {
- errcpy(err, "incompatible character encoding");
- return -1;
- }
+ errcpy(err, "incompatible character encoding");
+ return -1;
+ }
if (fixed_enc != a_enc) {
- options |= ARG_ENCODING_FIXED;
- enc = fixed_enc;
- }
+ options |= ARG_ENCODING_FIXED;
+ enc = fixed_enc;
+ }
}
else if (!(options & ARG_ENCODING_FIXED)) {
enc = rb_usascii_encoding();
@@ -3056,15 +3234,15 @@ rb_reg_initialize(VALUE obj, const char *s, long len, rb_encoding *enc,
rb_enc_associate((VALUE)re, enc);
if ((options & ARG_ENCODING_FIXED) || fixed_enc) {
- re->basic.flags |= KCODE_FIXED;
+ re->basic.flags |= KCODE_FIXED;
}
if (options & ARG_ENCODING_NONE) {
re->basic.flags |= REG_ENCODING_NONE;
}
re->ptr = make_regexp(RSTRING_PTR(unescaped), RSTRING_LEN(unescaped), enc,
- options & ARG_REG_OPTION_MASK, err,
- sourcefile, sourceline);
+ options & ARG_REG_OPTION_MASK, err,
+ sourcefile, sourceline);
if (!re->ptr) return -1;
RB_GC_GUARD(unescaped);
return 0;
@@ -3075,14 +3253,14 @@ reg_set_source(VALUE reg, VALUE str, rb_encoding *enc)
{
rb_encoding *regenc = rb_enc_get(reg);
if (regenc != enc) {
- str = rb_enc_associate(rb_str_dup(str), enc = regenc);
+ str = rb_enc_associate(rb_str_dup(str), enc = regenc);
}
RB_OBJ_WRITE(reg, &RREGEXP(reg)->src, rb_fstring(str));
}
static int
rb_reg_initialize_str(VALUE obj, VALUE str, int options, onig_errmsg_buffer err,
- const char *sourcefile, int sourceline)
+ const char *sourcefile, int sourceline)
{
int ret;
rb_encoding *str_enc = rb_enc_get(str), *enc = str_enc;
@@ -3097,7 +3275,7 @@ rb_reg_initialize_str(VALUE obj, VALUE str, int options, onig_errmsg_buffer err,
}
}
ret = rb_reg_initialize(obj, RSTRING_PTR(str), RSTRING_LEN(str), enc,
- options, err, sourcefile, sourceline);
+ options, err, sourcefile, sourceline);
if (ret == 0) reg_set_source(obj, str, str_enc);
return ret;
}
@@ -3132,7 +3310,7 @@ rb_reg_init_str(VALUE re, VALUE s, int options)
onig_errmsg_buffer err = "";
if (rb_reg_initialize_str(re, s, options, err, NULL, 0) != 0) {
- rb_reg_raise_str(s, options, err);
+ rb_reg_raise_str(s, options, err);
}
return re;
@@ -3144,8 +3322,8 @@ rb_reg_init_str_enc(VALUE re, VALUE s, rb_encoding *enc, int options)
onig_errmsg_buffer err = "";
if (rb_reg_initialize(re, RSTRING_PTR(s), RSTRING_LEN(s),
- enc, options, err, NULL, 0) != 0) {
- rb_reg_raise_str(s, options, err);
+ enc, options, err, NULL, 0) != 0) {
+ rb_reg_raise_str(s, options, err);
}
reg_set_source(re, s, enc);
@@ -3167,7 +3345,7 @@ rb_enc_reg_new(const char *s, long len, rb_encoding *enc, int options)
onig_errmsg_buffer err = "";
if (rb_reg_initialize(re, s, len, enc, options, err, NULL, 0) != 0) {
- rb_enc_reg_raise(s, len, enc, options, err);
+ rb_enc_reg_raise(s, len, enc, options, err);
}
RB_OBJ_WRITE(re, &RREGEXP(re)->src, rb_fstring(rb_enc_str_new(s, len, enc)));
@@ -3188,8 +3366,8 @@ rb_reg_compile(VALUE str, int options, const char *sourcefile, int sourceline)
if (!str) str = rb_str_new(0,0);
if (rb_reg_initialize_str(re, str, options, err, sourcefile, sourceline) != 0) {
- rb_set_errinfo(rb_reg_error_desc(str, options, err));
- return Qnil;
+ rb_set_errinfo(rb_reg_error_desc(str, options, err));
+ return Qnil;
}
FL_SET(re, REG_LITERAL);
rb_obj_freeze(re);
@@ -3202,9 +3380,9 @@ VALUE
rb_reg_regcomp(VALUE str)
{
if (reg_cache && RREGEXP_SRC_LEN(reg_cache) == RSTRING_LEN(str)
- && ENCODING_GET(reg_cache) == ENCODING_GET(str)
- && memcmp(RREGEXP_SRC_PTR(reg_cache), RSTRING_PTR(str), RSTRING_LEN(str)) == 0)
- return reg_cache;
+ && ENCODING_GET(reg_cache) == ENCODING_GET(str)
+ && memcmp(RREGEXP_SRC_PTR(reg_cache), RSTRING_PTR(str), RSTRING_LEN(str)) == 0)
+ return reg_cache;
return reg_cache = rb_reg_new_str(str, 0);
}
@@ -3330,7 +3508,7 @@ static VALUE
reg_operand(VALUE s, int check)
{
if (SYMBOL_P(s)) {
- return rb_sym2str(s);
+ return rb_sym2str(s);
}
else if (RB_TYPE_P(s, T_STRING)) {
return s;
@@ -3346,19 +3524,19 @@ reg_match_pos(VALUE re, VALUE *strp, long pos, VALUE* set_match)
VALUE str = *strp;
if (NIL_P(str)) {
- rb_backref_set(Qnil);
- return -1;
+ rb_backref_set(Qnil);
+ return -1;
}
*strp = str = reg_operand(str, TRUE);
if (pos != 0) {
- if (pos < 0) {
- VALUE l = rb_str_length(str);
- pos += NUM2INT(l);
- if (pos < 0) {
- return pos;
- }
- }
- pos = rb_str_offset(str, pos);
+ if (pos < 0) {
+ VALUE l = rb_str_length(str);
+ pos += NUM2INT(l);
+ if (pos < 0) {
+ return pos;
+ }
+ }
+ pos = rb_str_offset(str, pos);
}
return rb_reg_search_set_match(re, str, pos, 0, 1, set_match);
}
@@ -3455,8 +3633,8 @@ rb_reg_eqq(VALUE re, VALUE str)
str = reg_operand(str, FALSE);
if (NIL_P(str)) {
- rb_backref_set(Qnil);
- return Qfalse;
+ rb_backref_set(Qnil);
+ return Qfalse;
}
start = rb_reg_search(re, str, 0, 0);
return RBOOL(start >= 0);
@@ -3481,13 +3659,13 @@ rb_reg_match2(VALUE re)
VALUE line = rb_lastline_get();
if (!RB_TYPE_P(line, T_STRING)) {
- rb_backref_set(Qnil);
- return Qnil;
+ rb_backref_set(Qnil);
+ return Qnil;
}
start = rb_reg_search(re, line, 0, 0);
if (start < 0) {
- return Qnil;
+ return Qnil;
}
start = rb_str_sublen(line, start);
return LONG2FIX(start);
@@ -3501,13 +3679,18 @@ rb_reg_match2(VALUE re)
*
* With no block given, returns the MatchData object
* that describes the match, if any, or +nil+ if none;
- * the search begins at the given byte +offset+ in +self+:
+ * the search begins at the given character +offset+ in +string+:
*
* /abra/.match('abracadabra') # => #<MatchData "abra">
* /abra/.match('abracadabra', 4) # => #<MatchData "abra">
* /abra/.match('abracadabra', 8) # => nil
* /abra/.match('abracadabra', 800) # => nil
*
+ * string = "\u{5d0 5d1 5e8 5d0}cadabra"
+ * /abra/.match(string, 7) #=> #<MatchData "abra">
+ * /abra/.match(string, 8) #=> nil
+ * /abra/.match(string.b, 8) #=> #<MatchData "abra">
+ *
* With a block given, calls the block if and only if a match is found;
* returns the block's value:
*
@@ -3537,20 +3720,20 @@ rb_reg_match_m(int argc, VALUE *argv, VALUE re)
long pos;
if (rb_scan_args(argc, argv, "11", &str, &initpos) == 2) {
- pos = NUM2LONG(initpos);
+ pos = NUM2LONG(initpos);
}
else {
- pos = 0;
+ pos = 0;
}
pos = reg_match_pos(re, &str, pos, &result);
if (pos < 0) {
- rb_backref_set(Qnil);
- return Qnil;
+ rb_backref_set(Qnil);
+ return Qnil;
}
rb_match_busy(result);
if (!NIL_P(result) && rb_block_given_p()) {
- return rb_yield(result);
+ return rb_yield(result);
}
return result;
}
@@ -3590,16 +3773,16 @@ rb_reg_match_p(VALUE re, VALUE str, long pos)
if (NIL_P(str)) return Qfalse;
str = SYMBOL_P(str) ? rb_sym2str(str) : StringValue(str);
if (pos) {
- if (pos < 0) {
- pos += NUM2LONG(rb_str_length(str));
- if (pos < 0) return Qfalse;
- }
- if (pos > 0) {
- long len = 1;
- const char *beg = rb_str_subpos(str, pos, &len);
- if (!beg) return Qfalse;
- pos = beg - RSTRING_PTR(str);
- }
+ if (pos < 0) {
+ pos += NUM2LONG(rb_str_length(str));
+ if (pos < 0) return Qfalse;
+ }
+ if (pos > 0) {
+ long len = 1;
+ const char *beg = rb_str_subpos(str, pos, &len);
+ if (!beg) return Qfalse;
+ pos = beg - RSTRING_PTR(str);
+ }
}
reg = rb_reg_prepare_re0(re, str, err);
tmpreg = reg != RREGEXP_PTR(re);
@@ -3607,25 +3790,25 @@ rb_reg_match_p(VALUE re, VALUE str, long pos)
start = ((UChar*)RSTRING_PTR(str));
end = start + RSTRING_LEN(str);
result = onig_search(reg, start, end, start + pos, end,
- NULL, ONIG_OPTION_NONE);
+ NULL, ONIG_OPTION_NONE);
if (!tmpreg) RREGEXP(re)->usecnt--;
if (tmpreg) {
- if (RREGEXP(re)->usecnt) {
- onig_free(reg);
- }
- else {
- onig_free(RREGEXP_PTR(re));
- RREGEXP_PTR(re) = reg;
- }
+ if (RREGEXP(re)->usecnt) {
+ onig_free(reg);
+ }
+ else {
+ onig_free(RREGEXP_PTR(re));
+ RREGEXP_PTR(re) = reg;
+ }
}
if (result < 0) {
- if (result == ONIG_MISMATCH) {
- return Qfalse;
- }
- else {
- onig_error_code_to_str((UChar*)err, (int)result);
- rb_reg_raise(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), err, re);
- }
+ if (result == ONIG_MISMATCH) {
+ return Qfalse;
+ }
+ else {
+ onig_error_code_to_str((UChar*)err, (int)result);
+ rb_reg_raise(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), err, re);
+ }
}
return Qtrue;
}
@@ -3646,18 +3829,39 @@ str_to_option(VALUE str)
if (NIL_P(str)) return -1;
RSTRING_GETMEM(str, ptr, len);
for (long i = 0; i < len; ++i) {
- int f = char_to_option(ptr[i]);
- if (!f) {
- rb_raise(rb_eArgError, "unknown regexp option: %"PRIsVALUE, str);
- }
- flag |= f;
+ int f = char_to_option(ptr[i]);
+ if (!f) {
+ rb_raise(rb_eArgError, "unknown regexp option: %"PRIsVALUE, str);
+ }
+ flag |= f;
}
return flag;
}
+static void
+set_timeout(rb_hrtime_t *hrt, VALUE timeout)
+{
+ double timeout_d = NIL_P(timeout) ? 0.0 : NUM2DBL(timeout);
+ if (!NIL_P(timeout) && timeout_d <= 0) {
+ rb_raise(rb_eArgError, "invalid timeout: %"PRIsVALUE, timeout);
+ }
+ double2hrtime(hrt, timeout_d);
+}
+
+struct reg_init_args {
+ VALUE str;
+ VALUE timeout;
+ rb_encoding *enc;
+ int flags;
+};
+
+static VALUE reg_extract_args(int argc, VALUE *argv, struct reg_init_args *args);
+static VALUE reg_init_args(VALUE self, VALUE str, rb_encoding *enc, int flags);
+void rb_warn_deprecated_to_remove(const char *removal, const char *fmt, const char *suggest, ...);
+
/*
* call-seq:
- * Regexp.new(string, options = 0, n_flag = nil, timeout: nil) -> regexp
+ * Regexp.new(string, options = 0, timeout: nil) -> regexp
* Regexp.new(regexp, timeout: nil) -> regexp
*
* With argument +string+ given, returns a new regexp with the given string
@@ -3675,27 +3879,23 @@ str_to_option(VALUE str)
* Regexp.new('foo', 'im') # => /foo/im
*
* - The logical OR of one or more of the constants
- * Regexp::EXTENDED, Regexp::IGNORECASE, and Regexp::MULTILINE:
+ * Regexp::EXTENDED, Regexp::IGNORECASE, Regexp::MULTILINE, and
+ * Regexp::NOENCODING:
*
* Regexp.new('foo', Regexp::IGNORECASE) # => /foo/i
* Regexp.new('foo', Regexp::EXTENDED) # => /foo/x
* Regexp.new('foo', Regexp::MULTILINE) # => /foo/m
+ * Regexp.new('foo', Regexp::NOENCODING) # => /foo/n
* flags = Regexp::IGNORECASE | Regexp::EXTENDED | Regexp::MULTILINE
* Regexp.new('foo', flags) # => /foo/mix
*
* - +nil+ or +false+, which is ignored.
*
- * If optional argument +n_flag+ if it is a string starts with
- * <code>'n'</code> or <code>'N'</code>, the encoding of +string+ is
- * ignored and the new regexp encoding is fixed to +ASCII-8BIT+ or
- * +US-ASCII+, by its content.
- *
- * Regexp.new('foo', nil, 'n') # => /foo/n
- * Regexp.new("\u3042", nil, 'n') # => /\xE3\x81\x82/n
- *
* If optional keyword argument +timeout+ is given,
* its float value overrides the timeout interval for the class,
* Regexp.timeout.
+ * If +nil+ is passed as +timeout, it uses the timeout interval
+ * for the class, Regexp.timeout.
*
* With argument +regexp+ given, returns a new regexp. The source,
* options, timeout are the same as +regexp+. +options+ and +n_flag+
@@ -3716,65 +3916,79 @@ str_to_option(VALUE str)
static VALUE
rb_reg_initialize_m(int argc, VALUE *argv, VALUE self)
{
+ struct reg_init_args args;
+
+ reg_extract_args(argc, argv, &args);
+ reg_init_args(self, args.str, args.enc, args.flags);
+
+ set_timeout(&RREGEXP_PTR(self)->timelimit, args.timeout);
+
+ return self;
+}
+
+static VALUE
+reg_extract_args(int argc, VALUE *argv, struct reg_init_args *args)
+{
int flags = 0;
- VALUE str;
rb_encoding *enc = 0;
+ VALUE str, src, opts = Qundef, n_flag = Qundef, kwargs;
+ VALUE re = Qnil;
- VALUE src, opts = Qundef, n_flag = Qundef, kwargs, timeout = Qnil;
-
- rb_scan_args(argc, argv, "12:", &src, &opts, &n_flag, &kwargs);
+ argc = rb_scan_args(argc, argv, "12:", &src, &opts, &n_flag, &kwargs);
+ args->timeout = Qnil;
if (!NIL_P(kwargs)) {
- static ID keywords[1];
- if (!keywords[0]) {
- keywords[0] = rb_intern_const("timeout");
- }
- rb_get_kwargs(kwargs, keywords, 0, 1, &timeout);
+ static ID keywords[1];
+ if (!keywords[0]) {
+ keywords[0] = rb_intern_const("timeout");
+ }
+ rb_get_kwargs(kwargs, keywords, 0, 1, &args->timeout);
+ }
+
+ if (argc == 3) {
+ rb_warn_deprecated_to_remove("3.3", "3rd argument to Regexp.new", "2nd argument");
}
if (RB_TYPE_P(src, T_REGEXP)) {
- VALUE re = src;
+ re = src;
- if (opts != Qnil) {
- rb_warn("flags ignored");
- }
- rb_reg_check(re);
- flags = rb_reg_options(re);
- str = RREGEXP_SRC(re);
+ if (!NIL_P(opts)) {
+ rb_warn("flags ignored");
+ }
+ rb_reg_check(re);
+ flags = rb_reg_options(re);
+ str = RREGEXP_SRC(re);
}
else {
- if (opts != Qundef) {
- int f;
- if (FIXNUM_P(opts)) flags = FIX2INT(opts);
- else if ((f = str_to_option(opts)) >= 0) flags = f;
- else if (!NIL_P(opts) && rb_bool_expected(opts, "ignorecase", FALSE))
- flags = ONIG_OPTION_IGNORECASE;
- }
- if (n_flag != Qundef && !NIL_P(n_flag)) {
- char *kcode = StringValuePtr(n_flag);
- if (kcode[0] == 'n' || kcode[0] == 'N') {
- enc = rb_ascii8bit_encoding();
- flags |= ARG_ENCODING_NONE;
- }
- else {
- rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "encoding option is ignored - %s", kcode);
- }
- }
- str = StringValue(src);
+ if (!UNDEF_P(opts)) {
+ int f;
+ if (FIXNUM_P(opts)) flags = FIX2INT(opts);
+ else if ((f = str_to_option(opts)) >= 0) flags = f;
+ else if (!NIL_P(opts) && rb_bool_expected(opts, "ignorecase", FALSE))
+ flags = ONIG_OPTION_IGNORECASE;
+ }
+ if (!NIL_OR_UNDEF_P(n_flag)) {
+ char *kcode = StringValuePtr(n_flag);
+ if (kcode[0] == 'n' || kcode[0] == 'N') {
+ enc = rb_ascii8bit_encoding();
+ flags |= ARG_ENCODING_NONE;
+ }
+ }
+ str = StringValue(src);
}
+ args->str = str;
+ args->enc = enc;
+ args->flags = flags;
+ return re;
+}
+
+static VALUE
+reg_init_args(VALUE self, VALUE str, rb_encoding *enc, int flags)
+{
if (enc && rb_enc_get(str) != enc)
- rb_reg_init_str_enc(self, str, enc, flags);
+ rb_reg_init_str_enc(self, str, enc, flags);
else
- rb_reg_init_str(self, str, flags);
-
- regex_t *reg = RREGEXP_PTR(self);
-
- {
- double limit = NIL_P(timeout) ? 0.0 : NUM2DBL(timeout);
- if (limit < 0) limit = 0;
- double2hrtime(&reg->timelimit, limit);
- }
-
+ rb_reg_init_str(self, str, flags);
return self;
}
@@ -3791,19 +4005,19 @@ rb_reg_quote(VALUE str)
send = s + RSTRING_LEN(str);
while (s < send) {
c = rb_enc_ascget(s, send, &clen, enc);
- if (c == -1) {
+ if (c == -1) {
s += mbclen(s, send, enc);
- continue;
- }
- switch (c) {
- case '[': case ']': case '{': case '}':
- case '(': case ')': case '|': case '-':
- case '*': case '.': case '\\':
- case '?': case '+': case '^': case '$':
- case ' ': case '#':
- case '\t': case '\f': case '\v': case '\n': case '\r':
- goto meta_found;
- }
+ continue;
+ }
+ switch (c) {
+ case '[': case ']': case '{': case '}':
+ case '(': case ')': case '|': case '-':
+ case '*': case '.': case '\\':
+ case '?': case '+': case '^': case '$':
+ case ' ': case '#':
+ case '\t': case '\f': case '\v': case '\n': case '\r':
+ goto meta_found;
+ }
s += clen;
}
tmp = rb_str_new3(str);
@@ -3828,47 +4042,47 @@ rb_reg_quote(VALUE str)
while (s < send) {
c = rb_enc_ascget(s, send, &clen, enc);
- if (c == -1) {
- int n = mbclen(s, send, enc);
+ if (c == -1) {
+ int n = mbclen(s, send, enc);
- while (n--)
- *t++ = *s++;
- continue;
- }
+ while (n--)
+ *t++ = *s++;
+ continue;
+ }
s += clen;
- switch (c) {
- case '[': case ']': case '{': case '}':
- case '(': case ')': case '|': case '-':
- case '*': case '.': case '\\':
- case '?': case '+': case '^': case '$':
- case '#':
+ switch (c) {
+ case '[': case ']': case '{': case '}':
+ case '(': case ')': case '|': case '-':
+ case '*': case '.': case '\\':
+ case '?': case '+': case '^': case '$':
+ case '#':
t += rb_enc_mbcput('\\', t, enc);
- break;
- case ' ':
+ break;
+ case ' ':
t += rb_enc_mbcput('\\', t, enc);
t += rb_enc_mbcput(' ', t, enc);
- continue;
- case '\t':
+ continue;
+ case '\t':
t += rb_enc_mbcput('\\', t, enc);
t += rb_enc_mbcput('t', t, enc);
- continue;
- case '\n':
+ continue;
+ case '\n':
t += rb_enc_mbcput('\\', t, enc);
t += rb_enc_mbcput('n', t, enc);
- continue;
- case '\r':
+ continue;
+ case '\r':
t += rb_enc_mbcput('\\', t, enc);
t += rb_enc_mbcput('r', t, enc);
- continue;
- case '\f':
+ continue;
+ case '\f':
t += rb_enc_mbcput('\\', t, enc);
t += rb_enc_mbcput('f', t, enc);
- continue;
- case '\v':
+ continue;
+ case '\v':
t += rb_enc_mbcput('\\', t, enc);
t += rb_enc_mbcput('v', t, enc);
- continue;
- }
+ continue;
+ }
t += rb_enc_mbcput(c, t, enc);
}
rb_str_resize(tmp, t - RSTRING_PTR(tmp));
@@ -3964,23 +4178,23 @@ rb_reg_s_union(VALUE self, VALUE args0)
}
}
else {
- int i;
- VALUE source = rb_str_buf_new(0);
- rb_encoding *result_enc;
+ int i;
+ VALUE source = rb_str_buf_new(0);
+ rb_encoding *result_enc;
int has_asciionly = 0;
rb_encoding *has_ascii_compat_fixed = 0;
rb_encoding *has_ascii_incompat = 0;
- for (i = 0; i < argc; i++) {
- volatile VALUE v;
- VALUE e = rb_ary_entry(args0, i);
+ for (i = 0; i < argc; i++) {
+ volatile VALUE v;
+ VALUE e = rb_ary_entry(args0, i);
- if (0 < i)
- rb_str_buf_cat_ascii(source, "|");
+ if (0 < i)
+ rb_str_buf_cat_ascii(source, "|");
- v = rb_check_regexp_type(e);
- if (!NIL_P(v)) {
+ v = rb_check_regexp_type(e);
+ if (!NIL_P(v)) {
rb_encoding *enc = rb_enc_get(v);
if (!rb_enc_asciicompat(enc)) {
if (!has_ascii_incompat)
@@ -3999,9 +4213,9 @@ rb_reg_s_union(VALUE self, VALUE args0)
else {
has_asciionly = 1;
}
- v = rb_reg_str_with_term(v, -1);
- }
- else {
+ v = rb_reg_str_with_term(v, -1);
+ }
+ else {
rb_encoding *enc;
StringValue(e);
enc = rb_enc_get(e);
@@ -4022,8 +4236,8 @@ rb_reg_s_union(VALUE self, VALUE args0)
rb_raise(rb_eArgError, "incompatible encodings: %s and %s",
rb_enc_name(has_ascii_compat_fixed), rb_enc_name(enc));
}
- v = rb_reg_s_quote(Qnil, e);
- }
+ v = rb_reg_s_quote(Qnil, e);
+ }
if (has_ascii_incompat) {
if (has_asciionly) {
rb_raise(rb_eArgError, "ASCII incompatible encoding: %s",
@@ -4038,8 +4252,8 @@ rb_reg_s_union(VALUE self, VALUE args0)
if (i == 0) {
rb_enc_copy(source, v);
}
- rb_str_append(source, v);
- }
+ rb_str_append(source, v);
+ }
if (has_ascii_incompat) {
result_enc = has_ascii_incompat;
@@ -4101,6 +4315,40 @@ rb_reg_s_union_m(VALUE self, VALUE args)
return rb_reg_s_union(self, args);
}
+/*
+ * call-seq:
+ * Regexp.linear_time?(re)
+ * Regexp.linear_time?(string, options = 0)
+ *
+ * Returns +true+ if matching against <tt>re</tt> can be
+ * done in linear time to the input string.
+ *
+ * Regexp.linear_time?(/re/) # => true
+ *
+ * Note that this is a property of the ruby interpreter, not of the argument
+ * regular expression. Identical regexp can or cannot run in linear time
+ * depending on your ruby binary. Neither forward nor backward compatibility
+ * is guaranteed about the return value of this method. Our current algorithm
+ * is (*1) but this is subject to change in the future. Alternative
+ * implementations can also behave differently. They might always return
+ * false for everything.
+ *
+ * (*1): https://doi.org/10.1109/SP40001.2021.00032
+ *
+ */
+static VALUE
+rb_reg_s_linear_time_p(int argc, VALUE *argv, VALUE self)
+{
+ struct reg_init_args args;
+ VALUE re = reg_extract_args(argc, argv, &args);
+
+ if (NIL_P(re)) {
+ re = reg_init_args(rb_reg_alloc(), args.str, args.enc, args.flags);
+ }
+
+ return RBOOL(onig_check_linear_time(RREGEXP_PTR(re)));
+}
+
/* :nodoc: */
static VALUE
rb_reg_init_copy(VALUE copy, VALUE re)
@@ -4128,42 +4376,42 @@ rb_reg_regsub(VALUE str, VALUE src, struct re_registers *regs, VALUE regexp)
while (s < e) {
int c = ASCGET(s, e, &clen);
- char *ss;
+ char *ss;
- if (c == -1) {
- s += mbclen(s, e, str_enc);
- continue;
- }
- ss = s;
+ if (c == -1) {
+ s += mbclen(s, e, str_enc);
+ continue;
+ }
+ ss = s;
s += clen;
- if (c != '\\' || s == e) continue;
+ if (c != '\\' || s == e) continue;
- if (!val) {
- val = rb_str_buf_new(ss-p);
- }
+ if (!val) {
+ val = rb_str_buf_new(ss-p);
+ }
rb_enc_str_buf_cat(val, p, ss-p, str_enc);
c = ASCGET(s, e, &clen);
if (c == -1) {
s += mbclen(s, e, str_enc);
- rb_enc_str_buf_cat(val, ss, s-ss, str_enc);
+ rb_enc_str_buf_cat(val, ss, s-ss, str_enc);
p = s;
- continue;
+ continue;
}
s += clen;
- p = s;
- switch (c) {
- case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
+ p = s;
+ switch (c) {
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
if (!NIL_P(regexp) && onig_noname_group_capture_is_active(RREGEXP_PTR(regexp))) {
no = c - '0';
}
else {
continue;
}
- break;
+ break;
case 'k':
if (s < e && ASCGET(s, e, &clen) == '<') {
@@ -4176,11 +4424,11 @@ rb_reg_regsub(VALUE str, VALUE src, struct re_registers *regs, VALUE regexp)
name_end += c == -1 ? mbclen(name_end, e, str_enc) : clen;
}
if (name_end < e) {
- VALUE n = rb_str_subseq(str, (long)(name - RSTRING_PTR(str)),
- (long)(name_end - name));
- if ((no = NAME_TO_NUMBER(regs, regexp, n, name, name_end)) < 1) {
- name_to_backref_error(n);
- }
+ VALUE n = rb_str_subseq(str, (long)(name - RSTRING_PTR(str)),
+ (long)(name_end - name));
+ if ((no = NAME_TO_NUMBER(regs, regexp, n, name, name_end)) < 1) {
+ name_to_backref_error(n);
+ }
p = s = name_end + clen;
break;
}
@@ -4193,38 +4441,38 @@ rb_reg_regsub(VALUE str, VALUE src, struct re_registers *regs, VALUE regexp)
continue;
case '0':
- case '&':
- no = 0;
- break;
+ case '&':
+ no = 0;
+ break;
- case '`':
- rb_enc_str_buf_cat(val, RSTRING_PTR(src), BEG(0), src_enc);
- continue;
+ case '`':
+ rb_enc_str_buf_cat(val, RSTRING_PTR(src), BEG(0), src_enc);
+ continue;
- case '\'':
- rb_enc_str_buf_cat(val, RSTRING_PTR(src)+END(0), RSTRING_LEN(src)-END(0), src_enc);
- continue;
+ case '\'':
+ rb_enc_str_buf_cat(val, RSTRING_PTR(src)+END(0), RSTRING_LEN(src)-END(0), src_enc);
+ continue;
- case '+':
- no = regs->num_regs-1;
- while (BEG(no) == -1 && no > 0) no--;
- if (no == 0) continue;
- break;
+ case '+':
+ no = regs->num_regs-1;
+ while (BEG(no) == -1 && no > 0) no--;
+ if (no == 0) continue;
+ break;
- case '\\':
- rb_enc_str_buf_cat(val, s-clen, clen, str_enc);
- continue;
+ case '\\':
+ rb_enc_str_buf_cat(val, s-clen, clen, str_enc);
+ continue;
- default:
- rb_enc_str_buf_cat(val, ss, s-ss, str_enc);
- continue;
- }
+ default:
+ rb_enc_str_buf_cat(val, ss, s-ss, str_enc);
+ continue;
+ }
- if (no >= 0) {
- if (no >= regs->num_regs) continue;
- if (BEG(no) == -1) continue;
- rb_enc_str_buf_cat(val, RSTRING_PTR(src)+BEG(no), END(no)-BEG(no), src_enc);
- }
+ if (no >= 0) {
+ if (no >= regs->num_regs) continue;
+ if (BEG(no) == -1) continue;
+ rb_enc_str_buf_cat(val, RSTRING_PTR(src)+BEG(no), END(no)-BEG(no), src_enc);
+ }
}
if (!val) return str;
@@ -4268,7 +4516,7 @@ static void
match_setter(VALUE val, ID _x, VALUE *_y)
{
if (!NIL_P(val)) {
- Check_Type(val, T_MATCH);
+ Check_Type(val, T_MATCH);
}
rb_backref_set(val);
}
@@ -4319,7 +4567,7 @@ rb_reg_s_last_match(int argc, VALUE *argv, VALUE _)
int n;
if (NIL_P(match)) return Qnil;
n = match_backref_number(match, argv[0]);
- return rb_reg_nth_match(n, match);
+ return rb_reg_nth_match(n, match);
}
return match_getter();
}
@@ -4396,16 +4644,13 @@ rb_reg_s_timeout_get(VALUE dummy)
*/
static VALUE
-rb_reg_s_timeout_set(VALUE dummy, VALUE limit)
+rb_reg_s_timeout_set(VALUE dummy, VALUE timeout)
{
- double timeout = NIL_P(limit) ? 0.0 : NUM2DBL(limit);
-
rb_ractor_ensure_main_ractor("can not access Regexp.timeout from non-main Ractors");
- if (timeout < 0) timeout = 0;
- double2hrtime(&rb_reg_match_time_limit, timeout);
+ set_timeout(&rb_reg_match_time_limit, timeout);
- return limit;
+ return timeout;
}
/*
@@ -4475,12 +4720,13 @@ Init_Regexp(void)
rb_cRegexp = rb_define_class("Regexp", rb_cObject);
rb_define_alloc_func(rb_cRegexp, rb_reg_s_alloc);
- rb_define_singleton_method(rb_cRegexp, "compile", rb_class_new_instance, -1);
+ rb_define_singleton_method(rb_cRegexp, "compile", rb_class_new_instance_pass_kw, -1);
rb_define_singleton_method(rb_cRegexp, "quote", rb_reg_s_quote, 1);
rb_define_singleton_method(rb_cRegexp, "escape", rb_reg_s_quote, 1);
rb_define_singleton_method(rb_cRegexp, "union", rb_reg_s_union_m, -2);
rb_define_singleton_method(rb_cRegexp, "last_match", rb_reg_s_last_match, -1);
rb_define_singleton_method(rb_cRegexp, "try_convert", rb_reg_s_try_convert, 1);
+ rb_define_singleton_method(rb_cRegexp, "linear_time?", rb_reg_s_linear_time_p, -1);
rb_define_method(rb_cRegexp, "initialize", rb_reg_initialize_m, -1);
rb_define_method(rb_cRegexp, "initialize_copy", rb_reg_init_copy, 1);
@@ -4539,7 +4785,9 @@ Init_Regexp(void)
rb_define_method(rb_cMatch, "to_a", match_to_a, 0);
rb_define_method(rb_cMatch, "[]", match_aref, -1);
rb_define_method(rb_cMatch, "captures", match_captures, 0);
+ rb_define_alias(rb_cMatch, "deconstruct", "captures");
rb_define_method(rb_cMatch, "named_captures", match_named_captures, 0);
+ rb_define_method(rb_cMatch, "deconstruct_keys", match_deconstruct_keys, 1);
rb_define_method(rb_cMatch, "values_at", match_values_at, -1);
rb_define_method(rb_cMatch, "pre_match", rb_reg_match_pre, 0);
rb_define_method(rb_cMatch, "post_match", rb_reg_match_post, 0);
diff --git a/regcomp.c b/regcomp.c
index 94640639d8..be85d85f93 100644
--- a/regcomp.c
+++ b/regcomp.c
@@ -341,7 +341,7 @@ static int
select_str_opcode(int mb_len, OnigDistance byte_len, int ignore_case)
{
int op;
- OnigDistance str_len = (byte_len + mb_len - 1) / mb_len;
+ OnigDistance str_len = roomof(byte_len, mb_len);
if (ignore_case) {
switch (str_len) {
diff --git a/regenc.c b/regenc.c
index 16d62fdf40..eb523e1ae5 100644
--- a/regenc.c
+++ b/regenc.c
@@ -52,6 +52,21 @@ onigenc_set_default_encoding(OnigEncoding enc)
}
extern int
+onigenc_mbclen(const OnigUChar* p,const OnigUChar* e, OnigEncoding enc)
+{
+ int ret = ONIGENC_PRECISE_MBC_ENC_LEN(enc, p, e);
+ if (ONIGENC_MBCLEN_CHARFOUND_P(ret)) {
+ ret = ONIGENC_MBCLEN_CHARFOUND_LEN(ret);
+ if (p + ret > e) ret = (int)(e - p); // just for case
+ return ret;
+ }
+ else if (ONIGENC_MBCLEN_NEEDMORE_P(ret)) {
+ return (int)(e - p);
+ }
+ return p < e ? 1 : 0;
+}
+
+extern int
onigenc_mbclen_approximate(const OnigUChar* p,const OnigUChar* e, OnigEncoding enc)
{
int ret = ONIGENC_PRECISE_MBC_ENC_LEN(enc, p, e);
diff --git a/regenc.h b/regenc.h
index 8c4ff0483b..4b4d21a715 100644
--- a/regenc.h
+++ b/regenc.h
@@ -91,7 +91,7 @@ typedef struct {
#define ONIG_CHECK_NULL_RETURN(p) if (ONIG_IS_NULL(p)) return NULL
#define ONIG_CHECK_NULL_RETURN_VAL(p,val) if (ONIG_IS_NULL(p)) return (val)
-#define enclen(enc,p,e) ((enc->max_enc_len == enc->min_enc_len) ? enc->min_enc_len : ONIGENC_MBC_ENC_LEN(enc,p,e))
+#define enclen(enc,p,e) ((enc->max_enc_len == enc->min_enc_len) ? (p < e ? enc->min_enc_len : 0) : ONIGENC_MBC_ENC_LEN(enc,p,e))
/* character types bit flag */
#define BIT_CTYPE_NEWLINE (1<< ONIGENC_CTYPE_NEWLINE)
@@ -118,6 +118,11 @@ typedef struct {
typedef struct {
short int len;
+#if defined(__has_attribute)
+# if __has_attribute(nonstring)
+ __attribute__((nonstring))
+# endif
+#endif
const UChar name[6];
int ctype;
} PosixBracketEntryType;
@@ -125,10 +130,9 @@ typedef struct {
#define POSIX_BRACKET_ENTRY_INIT(name, ctype) \
{(short int )(sizeof(name) - 1), name, (ctype)}
-#ifndef numberof
-# define numberof(array) (int )(sizeof(array) / sizeof((array)[0]))
-#endif
-
+#define numberof(array) ((int)(sizeof(array) / sizeof((array)[0])))
+#define roomof(x, y) (((x) + (y) - 1) / (y))
+#define type_roomof(x, y) roomof(sizeof(x), sizeof(y))
#define USE_CRNL_AS_LINE_TERMINATOR
#define USE_UNICODE_PROPERTIES
diff --git a/regexec.c b/regexec.c
index c77d48b1d9..47fc88c9c2 100644
--- a/regexec.c
+++ b/regexec.c
@@ -231,6 +231,488 @@ onig_get_capture_tree(OnigRegion* region)
}
#endif /* USE_CAPTURE_HISTORY */
+#ifdef USE_CACHE_MATCH_OPT
+
+/* count number of jump-like opcodes for allocation of cache memory. */
+static OnigPosition
+count_num_cache_opcode(regex_t* reg, long* num, long* table_size)
+{
+ UChar* p = reg->p;
+ UChar* pend = p + reg->used;
+ LengthType len;
+ MemNumType mem;
+ MemNumType current_mem = -1;
+ long current_mem_num = 0;
+ OnigEncoding enc = reg->enc;
+
+ *num = 0;
+ *table_size = 0;
+
+ while (p < pend) {
+ switch (*p++) {
+ case OP_FINISH:
+ case OP_END:
+ break;
+
+ case OP_EXACT1: p++; break;
+ case OP_EXACT2: p += 2; break;
+ case OP_EXACT3: p += 3; break;
+ case OP_EXACT4: p += 4; break;
+ case OP_EXACT5: p += 5; break;
+ case OP_EXACTN:
+ GET_LENGTH_INC(len, p); p += len; break;
+ case OP_EXACTMB2N1: p += 2; break;
+ case OP_EXACTMB2N2: p += 4; break;
+ case OP_EXACTMB2N3: p += 6; break;
+ case OP_EXACTMB2N:
+ GET_LENGTH_INC(len, p); p += len * 2; break;
+ case OP_EXACTMB3N:
+ GET_LENGTH_INC(len, p); p += len * 3; break;
+ case OP_EXACTMBN:
+ {
+ int mb_len;
+ GET_LENGTH_INC(mb_len, p);
+ GET_LENGTH_INC(len, p);
+ p += mb_len * len;
+ }
+ break;
+
+ case OP_EXACT1_IC:
+ len = enclen(enc, p, pend); p += len; break;
+ case OP_EXACTN_IC:
+ GET_LENGTH_INC(len, p); p += len; break;
+
+ case OP_CCLASS:
+ case OP_CCLASS_NOT:
+ p += SIZE_BITSET; break;
+ case OP_CCLASS_MB:
+ case OP_CCLASS_MB_NOT:
+ GET_LENGTH_INC(len, p); p += len; break;
+ case OP_CCLASS_MIX:
+ case OP_CCLASS_MIX_NOT:
+ p += SIZE_BITSET;
+ GET_LENGTH_INC(len, p);
+ p += len;
+ break;
+
+ case OP_ANYCHAR:
+ case OP_ANYCHAR_ML:
+ break;
+ case OP_ANYCHAR_STAR:
+ case OP_ANYCHAR_ML_STAR:
+ *num += 1; *table_size += 1; break;
+ case OP_ANYCHAR_STAR_PEEK_NEXT:
+ case OP_ANYCHAR_ML_STAR_PEEK_NEXT:
+ p++; *num += 1; *table_size += 1; break;
+
+ case OP_WORD:
+ case OP_NOT_WORD:
+ case OP_WORD_BOUND:
+ case OP_NOT_WORD_BOUND:
+ case OP_WORD_BEGIN:
+ case OP_WORD_END:
+ break;
+
+ case OP_ASCII_WORD:
+ case OP_NOT_ASCII_WORD:
+ case OP_ASCII_WORD_BOUND:
+ case OP_NOT_ASCII_WORD_BOUND:
+ case OP_ASCII_WORD_BEGIN:
+ case OP_ASCII_WORD_END:
+ break;
+
+ case OP_BEGIN_BUF:
+ case OP_END_BUF:
+ case OP_BEGIN_LINE:
+ case OP_END_LINE:
+ case OP_SEMI_END_BUF:
+ case OP_BEGIN_POSITION:
+ break;
+
+ case OP_BACKREF1:
+ case OP_BACKREF2:
+ case OP_BACKREFN:
+ case OP_BACKREFN_IC:
+ case OP_BACKREF_MULTI:
+ case OP_BACKREF_MULTI_IC:
+ case OP_BACKREF_WITH_LEVEL:
+ goto fail;
+
+ case OP_MEMORY_START:
+ case OP_MEMORY_START_PUSH:
+ case OP_MEMORY_END_PUSH:
+ case OP_MEMORY_END_PUSH_REC:
+ case OP_MEMORY_END:
+ case OP_MEMORY_END_REC:
+ p += SIZE_MEMNUM; break;
+
+ case OP_KEEP:
+ break;
+
+ case OP_FAIL:
+ break;
+ case OP_JUMP:
+ p += SIZE_RELADDR;
+ break;
+ case OP_PUSH:
+ p += SIZE_RELADDR;
+ *num += 1;
+ *table_size += 1;
+ break;
+ case OP_POP:
+ break;
+ case OP_PUSH_OR_JUMP_EXACT1:
+ case OP_PUSH_IF_PEEK_NEXT:
+ p += SIZE_RELADDR + 1; *num += 1; *table_size += 1; break;
+ case OP_REPEAT:
+ case OP_REPEAT_NG:
+ if (current_mem != -1) {
+ // A nested OP_REPEAT is not yet supported.
+ goto fail;
+ }
+ GET_MEMNUM_INC(mem, p);
+ p += SIZE_RELADDR;
+ if (reg->repeat_range[mem].lower == 0) {
+ *num += 1;
+ *table_size += 1;
+ }
+ reg->repeat_range[mem].base_num = *num;
+ current_mem = mem;
+ current_mem_num = *num;
+ break;
+ case OP_REPEAT_INC:
+ case OP_REPEAT_INC_NG:
+ GET_MEMNUM_INC(mem, p);
+ if (mem != current_mem) {
+ // A lone or invalid OP_REPEAT_INC is found.
+ goto fail;
+ }
+ {
+ long inner_num = *num - current_mem_num;
+ OnigRepeatRange *repeat_range = &reg->repeat_range[mem];
+ repeat_range->inner_num = inner_num;
+ *num -= inner_num;
+ *num += inner_num * repeat_range->lower + (inner_num + 1) * (repeat_range->upper == 0x7fffffff ? 1 : repeat_range->upper - repeat_range->lower);
+ if (repeat_range->lower < repeat_range->upper) {
+ *table_size += 1;
+ }
+ current_mem = -1;
+ current_mem_num = 0;
+ }
+ break;
+ case OP_REPEAT_INC_SG:
+ case OP_REPEAT_INC_NG_SG:
+ // TODO: Support nested OP_REPEAT.
+ goto fail;
+ case OP_NULL_CHECK_START:
+ case OP_NULL_CHECK_END:
+ case OP_NULL_CHECK_END_MEMST:
+ case OP_NULL_CHECK_END_MEMST_PUSH:
+ p += SIZE_MEMNUM; break;
+
+ case OP_PUSH_POS:
+ case OP_POP_POS:
+ case OP_PUSH_POS_NOT:
+ case OP_FAIL_POS:
+ case OP_PUSH_STOP_BT:
+ case OP_POP_STOP_BT:
+ case OP_LOOK_BEHIND:
+ case OP_PUSH_LOOK_BEHIND_NOT:
+ case OP_FAIL_LOOK_BEHIND_NOT:
+ case OP_PUSH_ABSENT_POS:
+ case OP_ABSENT_END:
+ case OP_ABSENT:
+ goto fail;
+
+ case OP_CALL:
+ case OP_RETURN:
+ goto fail;
+
+ case OP_CONDITION:
+ goto fail;
+
+ case OP_STATE_CHECK_PUSH:
+ case OP_STATE_CHECK_PUSH_OR_JUMP:
+ case OP_STATE_CHECK:
+ case OP_STATE_CHECK_ANYCHAR_STAR:
+ case OP_STATE_CHECK_ANYCHAR_ML_STAR:
+ goto fail;
+
+ case OP_SET_OPTION_PUSH:
+ case OP_SET_OPTION:
+ p += SIZE_OPTION;
+ break;
+
+ default:
+ goto bytecode_error;
+ }
+ }
+
+ return 0;
+
+fail:
+ *num = NUM_CACHE_OPCODE_FAIL;
+ return 0;
+
+bytecode_error:
+ return ONIGERR_UNDEFINED_BYTECODE;
+}
+
+static OnigPosition
+init_cache_index_table(regex_t* reg, OnigCacheIndex *table)
+{
+ UChar* pbegin;
+ UChar* p = reg->p;
+ UChar* pend = p + reg->used;
+ LengthType len;
+ MemNumType mem;
+ MemNumType current_mem = -1;
+ long num = 0;
+ long current_mem_num = 0;
+ OnigEncoding enc = reg->enc;
+
+ while (p < pend) {
+ pbegin = p;
+ switch (*p++) {
+ case OP_FINISH:
+ case OP_END:
+ break;
+
+ case OP_EXACT1: p++; break;
+ case OP_EXACT2: p += 2; break;
+ case OP_EXACT3: p += 3; break;
+ case OP_EXACT4: p += 4; break;
+ case OP_EXACT5: p += 5; break;
+ case OP_EXACTN:
+ GET_LENGTH_INC(len, p); p += len; break;
+ case OP_EXACTMB2N1: p += 2; break;
+ case OP_EXACTMB2N2: p += 4; break;
+ case OP_EXACTMB2N3: p += 6; break;
+ case OP_EXACTMB2N:
+ GET_LENGTH_INC(len, p); p += len * 2; break;
+ case OP_EXACTMB3N:
+ GET_LENGTH_INC(len, p); p += len * 3; break;
+ case OP_EXACTMBN:
+ {
+ int mb_len;
+ GET_LENGTH_INC(mb_len, p);
+ GET_LENGTH_INC(len, p);
+ p += mb_len * len;
+ }
+ break;
+
+ case OP_EXACT1_IC:
+ len = enclen(enc, p, pend); p += len; break;
+ case OP_EXACTN_IC:
+ GET_LENGTH_INC(len, p); p += len; break;
+
+ case OP_CCLASS:
+ case OP_CCLASS_NOT:
+ p += SIZE_BITSET; break;
+ case OP_CCLASS_MB:
+ case OP_CCLASS_MB_NOT:
+ GET_LENGTH_INC(len, p); p += len; break;
+ case OP_CCLASS_MIX:
+ case OP_CCLASS_MIX_NOT:
+ p += SIZE_BITSET;
+ GET_LENGTH_INC(len, p);
+ p += len;
+ break;
+
+ case OP_ANYCHAR:
+ case OP_ANYCHAR_ML:
+ break;
+ case OP_ANYCHAR_STAR:
+ case OP_ANYCHAR_ML_STAR:
+ table->addr = pbegin;
+ table->num = num - current_mem_num;
+ table->outer_repeat = current_mem;
+ num++;
+ table++;
+ break;
+ case OP_ANYCHAR_STAR_PEEK_NEXT:
+ case OP_ANYCHAR_ML_STAR_PEEK_NEXT:
+ p++;
+ table->addr = pbegin;
+ table->num = num - current_mem_num;
+ table->outer_repeat = current_mem;
+ num++;
+ table++;
+ break;
+
+ case OP_WORD:
+ case OP_NOT_WORD:
+ case OP_WORD_BOUND:
+ case OP_NOT_WORD_BOUND:
+ case OP_WORD_BEGIN:
+ case OP_WORD_END:
+ break;
+
+ case OP_ASCII_WORD:
+ case OP_NOT_ASCII_WORD:
+ case OP_ASCII_WORD_BOUND:
+ case OP_NOT_ASCII_WORD_BOUND:
+ case OP_ASCII_WORD_BEGIN:
+ case OP_ASCII_WORD_END:
+ break;
+
+ case OP_BEGIN_BUF:
+ case OP_END_BUF:
+ case OP_BEGIN_LINE:
+ case OP_END_LINE:
+ case OP_SEMI_END_BUF:
+ case OP_BEGIN_POSITION:
+ break;
+
+ case OP_BACKREF1:
+ case OP_BACKREF2:
+ case OP_BACKREFN:
+ case OP_BACKREFN_IC:
+ case OP_BACKREF_MULTI:
+ case OP_BACKREF_MULTI_IC:
+ case OP_BACKREF_WITH_LEVEL:
+ goto unexpected_bytecode_error;
+
+ case OP_MEMORY_START:
+ case OP_MEMORY_START_PUSH:
+ case OP_MEMORY_END_PUSH:
+ case OP_MEMORY_END_PUSH_REC:
+ case OP_MEMORY_END:
+ case OP_MEMORY_END_REC:
+ p += SIZE_MEMNUM; break;
+
+ case OP_KEEP:
+ break;
+
+ case OP_FAIL:
+ break;
+ case OP_JUMP:
+ p += SIZE_RELADDR;
+ break;
+ case OP_PUSH:
+ p += SIZE_RELADDR;
+ table->addr = pbegin;
+ table->num = num - current_mem_num;
+ table->outer_repeat = current_mem;
+ num++;
+ table++;
+ break;
+ case OP_POP:
+ break;
+ case OP_PUSH_OR_JUMP_EXACT1:
+ case OP_PUSH_IF_PEEK_NEXT:
+ p += SIZE_RELADDR + 1;
+ table->addr = pbegin;
+ table->num = num - current_mem_num;
+ table->outer_repeat = current_mem;
+ num++;
+ table++;
+ break;
+ case OP_REPEAT:
+ case OP_REPEAT_NG:
+ GET_MEMNUM_INC(mem, p);
+ p += SIZE_RELADDR;
+ if (reg->repeat_range[mem].lower == 0) {
+ table->addr = pbegin;
+ table->num = num - current_mem_num;
+ table->outer_repeat = -1;
+ num++;
+ table++;
+ }
+ current_mem = mem;
+ current_mem_num = num;
+ break;
+ case OP_REPEAT_INC:
+ case OP_REPEAT_INC_NG:
+ GET_MEMNUM_INC(mem, p);
+ {
+ long inner_num = num - current_mem_num;
+ OnigRepeatRange *repeat_range = &reg->repeat_range[mem];
+ if (repeat_range->lower < repeat_range->upper) {
+ table->addr = pbegin;
+ table->num = num - current_mem_num;
+ table->outer_repeat = mem;
+ table++;
+ }
+ num -= inner_num;
+ num += inner_num * repeat_range->lower + (inner_num + 1) * (repeat_range->upper == 0x7fffffff ? 1 : repeat_range->upper - repeat_range->lower);
+ current_mem = -1;
+ current_mem_num = 0;
+ }
+ break;
+ case OP_REPEAT_INC_SG:
+ case OP_REPEAT_INC_NG_SG:
+ // TODO: support OP_REPEAT opcodes.
+ goto unexpected_bytecode_error;
+ case OP_NULL_CHECK_START:
+ case OP_NULL_CHECK_END:
+ case OP_NULL_CHECK_END_MEMST:
+ case OP_NULL_CHECK_END_MEMST_PUSH:
+ p += SIZE_MEMNUM; break;
+
+ case OP_PUSH_POS:
+ case OP_POP_POS:
+ case OP_PUSH_POS_NOT:
+ case OP_FAIL_POS:
+ case OP_PUSH_STOP_BT:
+ case OP_POP_STOP_BT:
+ case OP_LOOK_BEHIND:
+ case OP_PUSH_LOOK_BEHIND_NOT:
+ case OP_FAIL_LOOK_BEHIND_NOT:
+ case OP_PUSH_ABSENT_POS:
+ case OP_ABSENT_END:
+ case OP_ABSENT:
+ goto unexpected_bytecode_error;
+
+ case OP_CALL:
+ case OP_RETURN:
+ goto unexpected_bytecode_error;
+
+ case OP_CONDITION:
+ goto unexpected_bytecode_error;
+
+ case OP_STATE_CHECK_PUSH:
+ case OP_STATE_CHECK_PUSH_OR_JUMP:
+ case OP_STATE_CHECK:
+ case OP_STATE_CHECK_ANYCHAR_STAR:
+ case OP_STATE_CHECK_ANYCHAR_ML_STAR:
+ goto unexpected_bytecode_error;
+
+ case OP_SET_OPTION_PUSH:
+ case OP_SET_OPTION:
+ p += SIZE_OPTION;
+ break;
+
+ default:
+ goto bytecode_error;
+ }
+ }
+
+ return 0;
+
+unexpected_bytecode_error:
+ return ONIGERR_UNEXPECTED_BYTECODE;
+
+bytecode_error:
+ return ONIGERR_UNDEFINED_BYTECODE;
+}
+#else /* USE_MATCH_CACHE */
+static OnigPosition
+count_num_cache_opcode(regex_t* reg, long* num, long* table_size)
+{
+ *num = NUM_CACHE_OPCODE_FAIL;
+ return 0;
+}
+#endif
+
+extern int
+onig_check_linear_time(OnigRegexType* reg)
+{
+ long num = 0, table_size = 0;
+ count_num_cache_opcode(reg, &num, &table_size);
+ return num != NUM_CACHE_OPCODE_FAIL;
+}
+
extern void
onig_region_clear(OnigRegion* region)
{
@@ -413,6 +895,24 @@ onig_region_copy(OnigRegion* to, const OnigRegion* from)
#define STK_MASK_TO_VOID_TARGET 0x10ff
#define STK_MASK_MEM_END_OR_MARK 0x8000 /* MEM_END or MEM_END_MARK */
+#ifdef USE_CACHE_MATCH_OPT
+#define MATCH_ARG_INIT_CACHE_MATCH_OPT(msa) do {\
+ (msa).enable_cache_match_opt = 0;\
+ (msa).num_fail = 0;\
+ (msa).num_cache_opcode = NUM_CACHE_OPCODE_UNINIT;\
+ (msa).num_cache_table = 0;\
+ (msa).cache_index_table = (OnigCacheIndex *)0;\
+ (msa).match_cache = (uint8_t *)0;\
+} while(0)
+#define MATCH_ARG_FREE_CACHE_MATCH_OPT(msa) do {\
+ if ((msa).cache_index_table) xfree((msa).cache_index_table);\
+ if ((msa).match_cache) xfree((msa).match_cache);\
+} while(0)
+#else
+#define MATCH_ARG_INIT_CACHE_MATCH_OPT(msa)
+#define MATCH_ARG_FREE_CACHE_MATCH_OPT(msa)
+#endif
+
#ifdef USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
# define MATCH_ARG_INIT(msa, arg_option, arg_region, arg_start, arg_gpos) do {\
(msa).stack_p = (void* )0;\
@@ -423,6 +923,7 @@ onig_region_copy(OnigRegion* to, const OnigRegion* from)
(msa).best_len = ONIG_MISMATCH;\
(msa).counter = 0;\
(msa).end_time = 0;\
+ MATCH_ARG_INIT_CACHE_MATCH_OPT(msa);\
} while(0)
#else
# define MATCH_ARG_INIT(msa, arg_option, arg_region, arg_start, arg_gpos) do {\
@@ -433,6 +934,7 @@ onig_region_copy(OnigRegion* to, const OnigRegion* from)
(msa).gpos = (arg_gpos);\
(msa).counter = 0;\
(msa).end_time = 0;\
+ MATCH_ARG_INIT_CACHE_MATCH_OPT(msa);\
} while(0)
#endif
@@ -471,9 +973,13 @@ onig_region_copy(OnigRegion* to, const OnigRegion* from)
if ((msa).state_check_buff_size >= STATE_CHECK_BUFF_MALLOC_THRESHOLD_SIZE) { \
if ((msa).state_check_buff) xfree((msa).state_check_buff);\
}\
+ MATCH_ARG_FREE_CACHE_MATCH_OPT(msa);\
} while(0)
#else /* USE_COMBINATION_EXPLOSION_CHECK */
-# define MATCH_ARG_FREE(msa) if ((msa).stack_p) xfree((msa).stack_p)
+# define MATCH_ARG_FREE(msa) do {\
+ if ((msa).stack_p) xfree((msa).stack_p);\
+ MATCH_ARG_FREE_CACHE_MATCH_OPT(msa);\
+} while (0)
#endif /* USE_COMBINATION_EXPLOSION_CHECK */
@@ -595,6 +1101,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_TYPE(stack_type) do {\
STACK_ENSURE(1);\
stk->type = (stack_type);\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
STACK_INC;\
} while(0)
@@ -664,6 +1171,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
# define STACK_PUSH(stack_type,pat,s,sprev,keep) do {\
STACK_ENSURE(1);\
stk->type = (stack_type);\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.state.pcode = (pat);\
stk->u.state.pstr = (s);\
stk->u.state.pstr_prev = (sprev);\
@@ -673,6 +1181,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
# define STACK_PUSH_ENSURED(stack_type,pat) do {\
stk->type = (stack_type);\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.state.pcode = (pat);\
STACK_INC;\
} while(0)
@@ -686,9 +1195,133 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_LOOK_BEHIND_NOT(pat,s,sprev,keep) \
STACK_PUSH(STK_LOOK_BEHIND_NOT,pat,s,sprev,keep)
+#ifdef USE_CACHE_MATCH_OPT
+
+#define DO_CACHE_MATCH_OPT(reg,stk,repeat_stk,enable,p,num_cache_table,num_cache_size,table,pos,match_cache) do {\
+ if (enable) {\
+ long cache_index = find_cache_index_table((reg), (stk), (repeat_stk), (table), (num_cache_table), (p));\
+ if (cache_index >= 0) {\
+ long key = (num_cache_size) * (long)(pos) + cache_index;\
+ long index = key >> 3;\
+ long mask = 1 << (key & 7);\
+ if ((match_cache)[index] & mask) {\
+ goto fail;\
+ }\
+ (match_cache)[index] |= mask;\
+ }\
+ }\
+} while (0)
+
+static long
+find_cache_index_table(regex_t* reg, OnigStackType *stk, OnigStackIndex *repeat_stk, OnigCacheIndex* table, long num_cache_table, UChar* p)
+{
+ long l = 0, r = num_cache_table - 1, m = 0;
+ OnigCacheIndex* item;
+ OnigRepeatRange* range;
+ OnigStackType *stkp;
+ int count = 0;
+ int is_inc = *p == OP_REPEAT_INC || *p == OP_REPEAT_INC_NG;
+
+ while (l <= r) {
+ m = (l + r) / 2;
+ if (table[m].addr == p) break;
+ if (table[m].addr < p) l = m + 1;
+ else r = m - 1;
+ }
+
+ if (!(0 <= m && m < num_cache_table && table[m].addr == p)) {
+ return -1;
+ }
+
+ item = &table[m];
+ if (item->outer_repeat == -1) {
+ return item->num;
+ }
+
+ range = &reg->repeat_range[item->outer_repeat];
+
+ stkp = &stk[repeat_stk[item->outer_repeat]];
+ count = is_inc ? stkp->u.repeat.count - 1 : stkp->u.repeat.count;
+
+ if (count < range->lower) {
+ return range->base_num + range->inner_num * count + item->num;
+ }
+
+ if (range->upper == 0x7fffffff) {
+ return range->base_num + range->inner_num * (range->lower - (is_inc ? 1 : 0)) + (is_inc ? 0 : 1) + item->num;
+ }
+
+ return range->base_num + range->inner_num * (range->lower - 1) + (range->inner_num + 1) * (count - range->lower + 1) + item->num;
+}
+
+static void
+reset_match_cache(regex_t* reg, UChar* pbegin, UChar* pend, long pos, uint8_t* match_cache, OnigCacheIndex *table, long num_cache_size, long num_cache_table)
+{
+ long l = 0, r = num_cache_table - 1, m1 = 0, m2 = 0;
+ int is_inc = *pend == OP_REPEAT_INC || *pend == OP_REPEAT_INC_NG;
+ OnigCacheIndex *item1, *item2;
+ long k1, k2, base;
+
+ while (l <= r) {
+ m1 = (l + r) / 2;
+ if (table[m1].addr == pbegin) break;
+ if (table[m1].addr < pbegin) l = m1 + 1;
+ else r = m1 - 1;
+ }
+
+ l = 0, r = num_cache_table - 1;
+ while (l <= r) {
+ m2 = (l + r) / 2;
+ if (table[m2].addr == pend) break;
+ if (table[m2].addr < pend) l = m2 + 1;
+ else r = m2 - 1;
+ }
+
+ if (table[m1].addr < pbegin && m1 + 1 < num_cache_table) m1++;
+ if (table[m2].addr > pend && m2 - 1 > 0) m2--;
+
+ item1 = &table[m1];
+ item2 = &table[m2];
+
+ if (item1->outer_repeat < 0) k1 = item1->num;
+ else k1 = reg->repeat_range[item1->outer_repeat].base_num + item1->num;
+
+ if (item2->outer_repeat < 0) k2 = item2->num;
+ else {
+ OnigRepeatRange *range = &reg->repeat_range[item2->outer_repeat];
+ if (range->upper == 0x7fffffff) k2 = range->base_num + range->inner_num * range->lower + (is_inc ? 0 : 1) + item2->num;
+ else k2 = range->base_num + range->inner_num * range->lower + (range->inner_num + 1) * (range->upper - range->lower - (is_inc ? 1 : 0)) + item2->num;
+ }
+
+ base = pos * num_cache_size;
+ k1 += base;
+ k2 += base;
+
+ if ((k1 >> 3) == (k2 >> 3)) {
+ match_cache[k1 >> 3] &= (((1 << (8 - (k2 & 7) - 1)) - 1) << ((k2 & 7) + 1)) | ((1 << (k1 & 7)) - 1);
+ } else {
+ long i = k1 >> 3;
+ if (k1 & 7) {
+ match_cache[k1 >> 3] &= (1 << ((k1 & 7) - 1)) - 1;
+ i++;
+ }
+ if (i < (k2 >> 3)) {
+ xmemset(&match_cache[i], 0, (k2 >> 3) - i);
+ if (k2 & 7) {
+ match_cache[k2 >> 3] &= (((1 << (8 - (k2 & 7) - 1)) - 1) << ((k2 & 7) + 1));
+ }
+ }
+ }
+}
+
+#else
+#define DO_CACHE_MATCH_OPT(reg,stk,repeat_stk,enable,p,num_cache_table,num_cache_size,table,pos,match_cache)
+#endif /* USE_CACHE_MATCH_OPT */
+
#define STACK_PUSH_REPEAT(id, pat) do {\
STACK_ENSURE(1);\
stk->type = STK_REPEAT;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.repeat.num = (id);\
stk->u.repeat.pcode = (pat);\
stk->u.repeat.count = 0;\
@@ -698,6 +1331,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_REPEAT_INC(sindex) do {\
STACK_ENSURE(1);\
stk->type = STK_REPEAT_INC;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.repeat_inc.si = (sindex);\
STACK_INC;\
} while(0)
@@ -705,6 +1339,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_MEM_START(mnum, s) do {\
STACK_ENSURE(1);\
stk->type = STK_MEM_START;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.mem.num = (mnum);\
stk->u.mem.pstr = (s);\
stk->u.mem.start = mem_start_stk[mnum];\
@@ -717,6 +1352,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_MEM_END(mnum, s) do {\
STACK_ENSURE(1);\
stk->type = STK_MEM_END;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.mem.num = (mnum);\
stk->u.mem.pstr = (s);\
stk->u.mem.start = mem_start_stk[mnum];\
@@ -728,6 +1364,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_MEM_END_MARK(mnum) do {\
STACK_ENSURE(1);\
stk->type = STK_MEM_END_MARK;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.mem.num = (mnum);\
STACK_INC;\
} while(0)
@@ -769,6 +1406,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_NULL_CHECK_START(cnum, s) do {\
STACK_ENSURE(1);\
stk->type = STK_NULL_CHECK_START;\
+ stk->null_check = (OnigStackIndex)(stk - stk_base);\
stk->u.null_check.num = (cnum);\
stk->u.null_check.pstr = (s);\
STACK_INC;\
@@ -777,6 +1415,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_NULL_CHECK_END(cnum) do {\
STACK_ENSURE(1);\
stk->type = STK_NULL_CHECK_END;\
+ stk->null_check = (OnigStackIndex)(stk - stk_base);\
stk->u.null_check.num = (cnum);\
STACK_INC;\
} while(0)
@@ -784,6 +1423,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_CALL_FRAME(pat) do {\
STACK_ENSURE(1);\
stk->type = STK_CALL_FRAME;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.call_frame.ret_addr = (pat);\
STACK_INC;\
} while(0)
@@ -791,12 +1431,14 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_RETURN do {\
STACK_ENSURE(1);\
stk->type = STK_RETURN;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
STACK_INC;\
} while(0)
#define STACK_PUSH_ABSENT_POS(start, end) do {\
STACK_ENSURE(1);\
stk->type = STK_ABSENT_POS;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.absent_pos.abs_pstr = (start);\
stk->u.absent_pos.end_pstr = (end);\
STACK_INC;\
@@ -960,7 +1602,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
} while(0)
#define STACK_NULL_CHECK(isnull,id,s) do {\
- OnigStackType* k = stk;\
+ OnigStackType* k = STACK_AT((stk-1)->null_check)+1;\
while (1) {\
k--;\
STACK_BASE_CHECK(k, "STACK_NULL_CHECK"); \
@@ -975,7 +1617,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_NULL_CHECK_REC(isnull,id,s) do {\
int level = 0;\
- OnigStackType* k = stk;\
+ OnigStackType* k = STACK_AT((stk-1)->null_check)+1;\
while (1) {\
k--;\
STACK_BASE_CHECK(k, "STACK_NULL_CHECK_REC"); \
@@ -994,8 +1636,8 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
}\
} while(0)
-#define STACK_NULL_CHECK_MEMST(isnull,id,s,reg) do {\
- OnigStackType* k = stk;\
+#define STACK_NULL_CHECK_MEMST(isnull,ischange,id,s,reg) do {\
+ OnigStackType* k = STACK_AT((stk-1)->null_check)+1;\
while (1) {\
k--;\
STACK_BASE_CHECK(k, "STACK_NULL_CHECK_MEMST"); \
@@ -1011,14 +1653,14 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
while (k < stk) {\
if (k->type == STK_MEM_START) {\
if (k->u.mem.end == INVALID_STACK_INDEX) {\
- (isnull) = 0; break;\
+ (isnull) = 0; (ischange) = 1; break;\
}\
if (BIT_STATUS_AT(reg->bt_mem_end, k->u.mem.num))\
endp = STACK_AT(k->u.mem.end)->u.mem.pstr;\
else\
endp = (UChar* )k->u.mem.end;\
if (STACK_AT(k->u.mem.start)->u.mem.pstr != endp) {\
- (isnull) = 0; break;\
+ (isnull) = 0; (ischange) = 1; break;\
}\
else if (endp != s) {\
(isnull) = -1; /* empty, but position changed */ \
@@ -1035,7 +1677,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_NULL_CHECK_MEMST_REC(isnull,id,s,reg) do {\
int level = 0;\
- OnigStackType* k = stk;\
+ OnigStackType* k = STACK_AT((stk-1)->null_check)+1;\
while (1) {\
k--;\
STACK_BASE_CHECK(k, "STACK_NULL_CHECK_MEMST_REC"); \
@@ -1190,6 +1832,19 @@ static int string_cmp_ic(OnigEncoding enc, int case_fold_flag,
# define ABSENT_END_POS end
#endif /* USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE */
+int onigenc_mbclen_approximate(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc);
+
+static inline int
+enclen_approx(OnigEncoding enc, const OnigUChar* p, const OnigUChar* e)
+{
+ if (enc->max_enc_len == enc->min_enc_len) {
+ return (p < e ? enc->min_enc_len : 0);
+ }
+ else {
+ return onigenc_mbclen_approximate(p, e, enc);
+ }
+}
+
#ifdef USE_CAPTURE_HISTORY
static int
@@ -1237,7 +1892,8 @@ make_capture_history_tree(OnigCaptureTreeNode* node, OnigStackType** kp,
#endif /* USE_CAPTURE_HISTORY */
#ifdef USE_BACKREF_WITH_LEVEL
-static int mem_is_in_memp(int mem, int num, UChar* memp)
+static int
+mem_is_in_memp(int mem, int num, UChar* memp)
{
int i;
MemNumType m;
@@ -1448,6 +2104,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
OnigCaseFoldType case_fold_flag = reg->case_fold_flag;
UChar *s, *q, *sbegin;
UChar *p = reg->p;
+ UChar *pbegin = p;
UChar *pkeep;
char *alloca_base;
char *xmalloc_base = NULL;
@@ -1469,7 +2126,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
# define CASE(x) L_##x: sbegin = s; OPCODE_EXEC_HOOK;
# define DEFAULT L_DEFAULT:
# define NEXT sprev = sbegin; JUMP
-# define JUMP RB_GNUC_EXTENSION_BLOCK(goto *oplabels[*p++])
+# define JUMP pbegin = p; RB_GNUC_EXTENSION_BLOCK(goto *oplabels[*p++])
RB_GNUC_EXTENSION static const void *oplabels[] = {
&&L_OP_FINISH, /* matching process terminator (no more alternative) */
@@ -1645,6 +2302,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
# define VM_LOOP \
while (1) { \
OPCODE_EXEC_HOOK; \
+ pbegin = p; \
sbegin = s; \
switch (*p++) {
# define VM_LOOP_END } sprev = sbegin; }
@@ -2037,7 +2695,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
int mb_len;
DATA_ENSURE(1);
- mb_len = enclen(encode, s, end);
+ mb_len = enclen_approx(encode, s, end);
DATA_ENSURE(mb_len);
ss = s;
s += mb_len;
@@ -2142,7 +2800,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR) MOP_IN(OP_ANYCHAR);
DATA_ENSURE(1);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
s += n;
@@ -2151,7 +2809,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR_ML) MOP_IN(OP_ANYCHAR_ML);
DATA_ENSURE(1);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
s += n;
MOP_OUT;
@@ -2159,8 +2817,9 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR_STAR) MOP_IN(OP_ANYCHAR_STAR);
while (DATA_ENSURE_CHECK1) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
STACK_PUSH_ALT(p, s, sprev, pkeep);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
sprev = s;
@@ -2171,8 +2830,9 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR_ML_STAR) MOP_IN(OP_ANYCHAR_ML_STAR);
while (DATA_ENSURE_CHECK1) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
STACK_PUSH_ALT(p, s, sprev, pkeep);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
if (n > 1) {
DATA_ENSURE(n);
sprev = s;
@@ -2188,10 +2848,17 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR_STAR_PEEK_NEXT) MOP_IN(OP_ANYCHAR_STAR_PEEK_NEXT);
while (DATA_ENSURE_CHECK1) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, end - s, msa->match_cache);
if (*p == *s) {
STACK_PUSH_ALT(p + 1, s, sprev, pkeep);
+ } else {
+ /* We need to increment num_fail here, for invoking a cache optimization correctly. */
+ /* Actually, the matching will be failed if we use `OP_ANYCHAR_STAR` simply in this case.*/
+#ifdef USE_CACHE_MATCH_OPT
+ msa->num_fail++;
+#endif
}
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
sprev = s;
@@ -2203,10 +2870,17 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR_ML_STAR_PEEK_NEXT)MOP_IN(OP_ANYCHAR_ML_STAR_PEEK_NEXT);
while (DATA_ENSURE_CHECK1) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
if (*p == *s) {
STACK_PUSH_ALT(p + 1, s, sprev, pkeep);
+ } else {
+ /* We need to increment num_fail here, for invoking a cache optimization correctly. */
+ /* Actually, the matching will be failed if we use `OP_ANYCHAR_STAR_ML` simply in this case.*/
+#ifdef USE_CACHE_MATCH_OPT
+ msa->num_fail++;
+#endif
}
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
if (n > 1) {
DATA_ENSURE(n);
sprev = s;
@@ -2229,7 +2903,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
if (scv) goto fail;
STACK_PUSH_ALT_WITH_STATE_CHECK(p, s, sprev, mem, pkeep);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
sprev = s;
@@ -2247,7 +2921,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
if (scv) goto fail;
STACK_PUSH_ALT_WITH_STATE_CHECK(p, s, sprev, mem, pkeep);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
if (n > 1) {
DATA_ENSURE(n);
sprev = s;
@@ -2534,8 +3208,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_MEMORY_END_PUSH_REC) MOP_IN(OP_MEMORY_END_PUSH_REC);
GET_MEMNUM_INC(mem, p);
STACK_GET_MEM_START(mem, stkp); /* should be before push mem-end. */
- STACK_PUSH_MEM_END(mem, s);
mem_start_stk[mem] = GET_STACK_INDEX(stkp);
+ STACK_PUSH_MEM_END(mem, s);
MOP_OUT;
JUMP;
@@ -2589,7 +3263,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
DATA_ENSURE(n);
sprev = s;
STRING_CMP(pstart, s, n);
- while (sprev + (len = enclen(encode, sprev, end)) < s)
+ while (sprev + (len = enclen_approx(encode, sprev, end)) < s)
sprev += len;
MOP_OUT;
@@ -2619,8 +3293,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
n = pend - pstart;
DATA_ENSURE(n);
sprev = s;
- STRING_CMP_IC(case_fold_flag, pstart, &s, (int)n, end);
- while (sprev + (len = enclen(encode, sprev, end)) < s)
+ STRING_CMP_IC(case_fold_flag, pstart, &s, n, end);
+ while (sprev + (len = enclen_approx(encode, sprev, end)) < s)
sprev += len;
MOP_OUT;
@@ -2655,7 +3329,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
STRING_CMP_VALUE(pstart, swork, n, is_fail);
if (is_fail) continue;
s = swork;
- while (sprev + (len = enclen(encode, sprev, end)) < s)
+ while (sprev + (len = enclen_approx(encode, sprev, end)) < s)
sprev += len;
p += (SIZE_MEMNUM * (tlen - i - 1));
@@ -2790,9 +3464,10 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_NULL_CHECK_END_MEMST) MOP_IN(OP_NULL_CHECK_END_MEMST);
{
int isnull;
+ int ischanged = 0; // set 1 when a loop is empty but memory status is changed.
GET_MEMNUM_INC(mem, p); /* mem: null check id */
- STACK_NULL_CHECK_MEMST(isnull, mem, s, reg);
+ STACK_NULL_CHECK_MEMST(isnull, ischanged, mem, s, reg);
if (isnull) {
# ifdef ONIG_DEBUG_MATCH
fprintf(stderr, "NULL_CHECK_END_MEMST: skip id:%d, s:%"PRIuPTR" (%p)\n",
@@ -2801,6 +3476,29 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
if (isnull == -1) goto fail;
goto null_check_found;
}
+# ifdef USE_CACHE_MATCH_OPT
+ if (ischanged && msa->enable_cache_match_opt) {
+ RelAddrType rel;
+ OnigUChar *addr;
+ MemNumType mem;
+ UChar* tmp = p;
+ switch (*tmp++) {
+ case OP_JUMP:
+ case OP_PUSH:
+ GET_RELADDR_INC(rel, tmp);
+ addr = tmp + rel;
+ break;
+ case OP_REPEAT_INC:
+ case OP_REPEAT_INC_NG:
+ GET_MEMNUM_INC(mem, tmp);
+ addr = STACK_AT(repeat_stk[mem])->u.repeat.pcode;
+ break;
+ default:
+ goto unexpected_bytecode_error;
+ }
+ reset_match_cache(reg, addr, pbegin, (long)(s - str), msa->match_cache, msa->cache_index_table, msa->num_cache_opcode, msa->num_cache_table);
+ }
+# endif
}
MOP_OUT;
JUMP;
@@ -2843,6 +3541,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_PUSH) MOP_IN(OP_PUSH);
GET_RELADDR_INC(addr, p);
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
STACK_PUSH_ALT(p + addr, s, sprev, pkeep);
MOP_OUT;
JUMP;
@@ -2883,6 +3582,11 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_POP) MOP_IN(OP_POP);
STACK_POP_ONE;
+ /* We need to increment num_fail here, for invoking a cache optimization correctly, */
+ /* because Onigmo makes a loop, which is pairwise disjoint to the following set, as atomic. */
+#ifdef USE_CACHE_MATCH_OPT
+ msa->num_fail++;
+#endif
MOP_OUT;
JUMP;
@@ -2891,6 +3595,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
GET_RELADDR_INC(addr, p);
if (*p == *s && DATA_ENSURE_CHECK1) {
p++;
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
STACK_PUSH_ALT(p + addr, s, sprev, pkeep);
MOP_OUT;
JUMP;
@@ -2904,6 +3609,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
GET_RELADDR_INC(addr, p);
if (*p == *s) {
p++;
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
STACK_PUSH_ALT(p + addr, s, sprev, pkeep);
MOP_OUT;
JUMP;
@@ -2922,6 +3628,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
STACK_PUSH_REPEAT(mem, p);
if (reg->repeat_range[mem].lower == 0) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, end - s, msa->match_cache);
STACK_PUSH_ALT(p + addr, s, sprev, pkeep);
}
}
@@ -2938,6 +3645,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
STACK_PUSH_REPEAT(mem, p);
if (reg->repeat_range[mem].lower == 0) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
STACK_PUSH_ALT(p, s, sprev, pkeep);
p += addr;
}
@@ -2956,6 +3664,9 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
/* end of repeat. Nothing to do. */
}
else if (stkp->u.repeat.count >= reg->repeat_range[mem].lower) {
+ if (*pbegin == OP_REPEAT_INC) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
+ }
STACK_PUSH_ALT(p, s, sprev, pkeep);
p = STACK_AT(si)->u.repeat.pcode; /* Don't use stkp after PUSH. */
}
@@ -2986,6 +3697,9 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
UChar* pcode = stkp->u.repeat.pcode;
STACK_PUSH_REPEAT_INC(si);
+ if (*pbegin == OP_REPEAT_INC_NG) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
+ }
STACK_PUSH_ALT(pcode, s, sprev, pkeep);
}
else {
@@ -3106,6 +3820,11 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
DATA_ENSURE(0);
p += addr;
}
+ else if (s == end) {
+ /* At the end of the string, just match with it */
+ DATA_ENSURE(0);
+ p += addr;
+ }
else {
STACK_PUSH_ALT(p + addr, s, sprev, pkeep); /* Push possible point. */
n = enclen(encode, s, end);
@@ -3173,6 +3892,49 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
sprev = stk->u.state.pstr_prev;
pkeep = stk->u.state.pkeep;
+#ifdef USE_CACHE_MATCH_OPT
+ if (++msa->num_fail >= (long)(end - str) + 1 && msa->num_cache_opcode == NUM_CACHE_OPCODE_UNINIT) {
+ msa->enable_cache_match_opt = 1;
+ if (msa->num_cache_opcode == NUM_CACHE_OPCODE_UNINIT) {
+ OnigPosition r = count_num_cache_opcode(reg, &msa->num_cache_opcode, &msa->num_cache_table);
+ if (r < 0) goto bytecode_error;
+ }
+ if (msa->num_cache_opcode == NUM_CACHE_OPCODE_FAIL || msa->num_cache_opcode == 0) {
+ msa->enable_cache_match_opt = 0;
+ goto fail_match_cache_opt;
+ }
+ if (msa->cache_index_table == NULL) {
+ OnigCacheIndex *table = (OnigCacheIndex *)xmalloc(msa->num_cache_table * sizeof(OnigCacheIndex));
+ if (table == NULL) {
+ return ONIGERR_MEMORY;
+ }
+ OnigPosition r = init_cache_index_table(reg, table);
+ if (r < 0) {
+ if (r == ONIGERR_UNEXPECTED_BYTECODE) goto unexpected_bytecode_error;
+ else goto bytecode_error;
+ }
+ msa->cache_index_table = table;
+ }
+ size_t len = (end - str) + 1;
+ size_t match_cache_size8 = (size_t)msa->num_cache_opcode * len;
+ /* overflow check */
+ if (match_cache_size8 / len != (size_t)msa->num_cache_opcode) {
+ return ONIGERR_MEMORY;
+ }
+ /* Currently, int is used for the key of match_cache */
+ if (match_cache_size8 >= LONG_MAX_LIMIT) {
+ return ONIGERR_MEMORY;
+ }
+ size_t match_cache_size = (match_cache_size8 >> 3) + (match_cache_size8 & 7 ? 1 : 0);
+ msa->match_cache = (uint8_t*)xmalloc(match_cache_size * sizeof(uint8_t));
+ if (msa->match_cache == NULL) {
+ return ONIGERR_MEMORY;
+ }
+ xmemset(msa->match_cache, 0, match_cache_size * sizeof(uint8_t));
+ }
+ fail_match_cache_opt:
+#endif
+
#ifdef USE_COMBINATION_EXPLOSION_CHECK
if (stk->u.state.state_check != 0) {
stk->type = STK_STATE_CHECK_MARK;
@@ -3909,12 +4671,17 @@ forward_search_range(regex_t* reg, const UChar* str, const UChar* end, UChar* s,
UChar* range, UChar** low, UChar** high, UChar** low_prev)
{
UChar *p, *pprev = (UChar* )NULL;
+ size_t input_len = end - str;
#ifdef ONIG_DEBUG_SEARCH
fprintf(stderr, "forward_search_range: str: %"PRIuPTR" (%p), end: %"PRIuPTR" (%p), s: %"PRIuPTR" (%p), range: %"PRIuPTR" (%p)\n",
(uintptr_t )str, str, (uintptr_t )end, end, (uintptr_t )s, s, (uintptr_t )range, range);
#endif
+ if (reg->dmin > input_len) {
+ return 0;
+ }
+
p = s;
if (reg->dmin > 0) {
if (ONIGENC_IS_SINGLEBYTE(reg->enc)) {
@@ -4051,6 +4818,11 @@ backward_search_range(regex_t* reg, const UChar* str, const UChar* end,
UChar** low, UChar** high)
{
UChar *p;
+ size_t input_len = end - str;
+
+ if (reg->dmin > input_len) {
+ return 0;
+ }
range += reg->dmin;
p = s;
diff --git a/regint.h b/regint.h
index 6c88f278c1..cee766ad6e 100644
--- a/regint.h
+++ b/regint.h
@@ -41,6 +41,14 @@
/* for byte-code statistical data. */
/* #define ONIG_DEBUG_STATISTICS */
+/* enable matching optimization by using cache. */
+#define USE_CACHE_MATCH_OPT
+
+#ifdef USE_CACHE_MATCH_OPT
+# define NUM_CACHE_OPCODE_FAIL -1
+# define NUM_CACHE_OPCODE_UNINIT -2
+#endif
+
#if defined(ONIG_DEBUG_PARSE_TREE) || defined(ONIG_DEBUG_MATCH) || \
defined(ONIG_DEBUG_SEARCH) || defined(ONIG_DEBUG_COMPILE) || \
defined(ONIG_DEBUG_STATISTICS) || defined(ONIG_DEBUG_MEMLEAK)
@@ -49,10 +57,11 @@
# endif
#endif
+/* __POWERPC__ added to accommodate Darwin case. */
#ifndef UNALIGNED_WORD_ACCESS
# if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || \
- defined(__powerpc64__) || defined(__aarch64__) || \
+ defined(__powerpc64__) || defined(__POWERPC__) || defined(__aarch64__) || \
defined(__mc68020__)
# define UNALIGNED_WORD_ACCESS 1
# else
@@ -310,9 +319,13 @@ RUBY_SYMBOL_EXPORT_BEGIN
#define ONIG_LAST_CODE_POINT (~((OnigCodePoint )0))
+#define PLATFORM_GET_INC_ARGUMENTS_ASSERT(val, type) \
+ ((void)sizeof(char[2 * (sizeof(val) == sizeof(type)) - 1]))
+
#ifdef PLATFORM_UNALIGNED_WORD_ACCESS
# define PLATFORM_GET_INC(val,p,type) do{\
+ PLATFORM_GET_INC_ARGUMENTS_ASSERT(val, type);\
val = *(type* )p;\
(p) += sizeof(type);\
} while(0)
@@ -320,7 +333,10 @@ RUBY_SYMBOL_EXPORT_BEGIN
#else
# define PLATFORM_GET_INC(val,p,type) do{\
- xmemcpy(&val, (p), sizeof(type));\
+ PLATFORM_GET_INC_ARGUMENTS_ASSERT(val, type);\
+ type platform_get_value;\
+ xmemcpy(&platform_get_value, (p), sizeof(type));\
+ val = platform_get_value;\
(p) += sizeof(type);\
} while(0)
@@ -378,6 +394,7 @@ typedef unsigned int BitStatusType;
#define INT_MAX_LIMIT ((1UL << (SIZEOF_INT * 8 - 1)) - 1)
+#define LONG_MAX_LIMIT ((1UL << (SIZEOF_LONG * 8 - 1)) - 1)
#define DIGITVAL(code) ((code) - '0')
#define ODIGITVAL(code) DIGITVAL(code)
@@ -819,6 +836,7 @@ typedef intptr_t OnigStackIndex;
typedef struct _OnigStackType {
unsigned int type;
+ OnigStackIndex null_check;
union {
struct {
UChar *pcode; /* byte code position */
@@ -862,6 +880,14 @@ typedef struct _OnigStackType {
} u;
} OnigStackType;
+#ifdef USE_CACHE_MATCH_OPT
+typedef struct {
+ UChar *addr;
+ long num;
+ int outer_repeat;
+} OnigCacheIndex;
+#endif
+
typedef struct {
void* stack_p;
size_t stack_n;
@@ -884,6 +910,14 @@ typedef struct {
#else
uint64_t end_time;
#endif
+#ifdef USE_CACHE_MATCH_OPT
+ long num_fail;
+ int enable_cache_match_opt;
+ long num_cache_opcode;
+ long num_cache_table;
+ OnigCacheIndex* cache_index_table;
+ uint8_t* match_cache;
+#endif
} OnigMatchArg;
diff --git a/regparse.c b/regparse.c
index 4ebd5f1c46..33df0e06c7 100644
--- a/regparse.c
+++ b/regparse.c
@@ -5977,7 +5977,8 @@ node_extended_grapheme_cluster(Node** np, ScanEnv* env)
R_ERR(add_code_range(&(cc->mbuf), env, 0x000A, 0x000A)); /* CR */
R_ERR(add_code_range(&(cc->mbuf), env, 0x000D, 0x000D)); /* LF */
R_ERR(not_code_range_buf(env->enc, cc->mbuf, &inverted_buf, env));
- cc->mbuf = inverted_buf; /* TODO: check what to do with buffer before inversion */
+ bbuf_free(cc->mbuf);
+ cc->mbuf = inverted_buf;
env->warnings_flag &= dup_not_warned; /* TODO: fix false warning */
}
diff --git a/ruby-runner.c b/ruby-runner.c
index b756c219fa..e1a53d5236 100644
--- a/ruby-runner.c
+++ b/ruby-runner.c
@@ -24,29 +24,29 @@ insert_env_path(const char *envname, const char *paths, size_t size, int prepend
size_t n = 0;
if (env) {
- while ((c = *env) == PATH_SEP) ++env;
- n = strlen(env);
- while (n > 0 && env[n-1] == PATH_SEP) --n;
+ while ((c = *env) == PATH_SEP) ++env;
+ n = strlen(env);
+ while (n > 0 && env[n-1] == PATH_SEP) --n;
}
if (c) {
- char *e = malloc(size+n+1);
- size_t pos = 0;
- if (prepend) {
- memcpy(e, paths, pos = size-1);
- e[pos++] = PATH_SEP;
- }
- memcpy(e+pos, env, n);
- pos += n;
- if (!prepend) {
- e[pos++] = PATH_SEP;
- memcpy(e+pos, paths, size-1);
- pos += size-1;
- }
- e[pos] = '\0';
- env = e;
+ char *e = malloc(size+n+1);
+ size_t pos = 0;
+ if (prepend) {
+ memcpy(e, paths, pos = size-1);
+ e[pos++] = PATH_SEP;
+ }
+ memcpy(e+pos, env, n);
+ pos += n;
+ if (!prepend) {
+ e[pos++] = PATH_SEP;
+ memcpy(e+pos, paths, size-1);
+ pos += size-1;
+ }
+ e[pos] = '\0';
+ env = e;
}
else {
- env = paths;
+ env = paths;
}
setenv(envname, env, 1);
}
@@ -58,12 +58,12 @@ main(int argc, char **argv)
static const char builddir[] = BUILDDIR;
static const char rubypath[] = BUILDDIR"/"STRINGIZE(RUBY_INSTALL_NAME);
static const char rubylib[] =
- ABS_SRCDIR"/lib"
- PATH_SEPARATOR
- EXTOUT_DIR"/common"
- PATH_SEPARATOR
- EXTOUT_DIR"/"ARCH
- ;
+ ABS_SRCDIR"/lib"
+ PATH_SEPARATOR
+ EXTOUT_DIR"/common"
+ PATH_SEPARATOR
+ EXTOUT_DIR"/"ARCH
+ ;
#ifndef LOAD_RELATIVE
static const char mjit_build_dir[] = BUILDDIR"/mjit_build_dir."SOEXT;
struct stat stbuf;
@@ -84,9 +84,9 @@ main(int argc, char **argv)
if (!(p = strrchr(arg0, '/'))) p = arg0; else p++;
if (strlen(p) < namesize - 1) {
- argv[0] = malloc(p - arg0 + namesize);
- memcpy(argv[0], arg0, p - arg0);
- p = argv[0] + (p - arg0);
+ argv[0] = malloc(p - arg0 + namesize);
+ memcpy(argv[0], arg0, p - arg0);
+ p = argv[0] + (p - arg0);
}
memcpy(p, rubyname, namesize);
diff --git a/ruby.c b/ruby.c
index 311e92a235..09c81db86c 100644
--- a/ruby.c
+++ b/ruby.c
@@ -22,7 +22,7 @@
# include <sys/cygwin.h>
#endif
-#if defined(LOAD_RELATIVE) && defined(HAVE_DLADDR)
+#if (defined(LOAD_RELATIVE) || defined(__MACH__)) && defined(HAVE_DLADDR)
# include <dlfcn.h>
#endif
@@ -44,6 +44,7 @@
#include "eval_intern.h"
#include "internal.h"
#include "internal/cmdlineopt.h"
+#include "internal/cont.h"
#include "internal/error.h"
#include "internal/file.h"
#include "internal/inits.h"
@@ -61,6 +62,10 @@
#include "ruby/version.h"
#include "ruby/internal/error.h"
+#define singlebit_only_p(x) !((x) & ((x)-1))
+STATIC_ASSERT(Qnil_1bit_from_Qfalse, singlebit_only_p(Qnil^Qfalse));
+STATIC_ASSERT(Qundef_1bit_from_Qnil, singlebit_only_p(Qundef^Qnil));
+
#ifndef MAXPATHLEN
# define MAXPATHLEN 1024
#endif
@@ -68,7 +73,7 @@
# define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
#endif
-void Init_ruby_description(void);
+void Init_ruby_description(ruby_cmdline_options_t *opt);
#ifndef HAVE_STDLIB_H
char *getenv();
@@ -94,6 +99,8 @@ void rb_warning_category_update(unsigned int mask, unsigned int bits);
SEP \
X(did_you_mean) \
SEP \
+ X(syntax_suggest) \
+ SEP \
X(rubyopt) \
SEP \
X(frozen_string_literal) \
@@ -111,7 +118,7 @@ void rb_warning_category_update(unsigned int mask, unsigned int bits);
enum feature_flag_bits {
EACH_FEATURES(DEFINE_FEATURE, COMMA),
feature_debug_flag_first,
-#if defined(MJIT_FORCE_ENABLE) || !YJIT_BUILD
+#if defined(MJIT_FORCE_ENABLE) || !USE_YJIT
DEFINE_FEATURE(jit) = feature_mjit,
#else
DEFINE_FEATURE(jit) = feature_yjit,
@@ -152,10 +159,14 @@ enum feature_flag_bits {
/* END OF DUMPS */
enum dump_flag_bits {
dump_version_v,
+ dump_error_tolerant,
EACH_DUMPS(DEFINE_DUMP, COMMA),
+ dump_error_tolerant_bits = (DUMP_BIT(yydebug) |
+ DUMP_BIT(parsetree) |
+ DUMP_BIT(parsetree_with_comment)),
dump_exit_bits = (DUMP_BIT(yydebug) | DUMP_BIT(syntax) |
- DUMP_BIT(parsetree) | DUMP_BIT(parsetree_with_comment) |
- DUMP_BIT(insns) | DUMP_BIT(insns_without_opt))
+ DUMP_BIT(parsetree) | DUMP_BIT(parsetree_with_comment) |
+ DUMP_BIT(insns) | DUMP_BIT(insns_without_opt))
};
static inline void
@@ -179,18 +190,18 @@ static void init_ids(ruby_cmdline_options_t *);
enum {
COMPILATION_FEATURES = (
- 0
- | FEATURE_BIT(frozen_string_literal)
- | FEATURE_BIT(debug_frozen_string_literal)
- ),
+ 0
+ | FEATURE_BIT(frozen_string_literal)
+ | FEATURE_BIT(debug_frozen_string_literal)
+ ),
DEFAULT_FEATURES = (
- (FEATURE_BIT(debug_flag_first)-1)
+ (FEATURE_BIT(debug_flag_first)-1)
#if DISABLE_RUBYGEMS
- & ~FEATURE_BIT(gems)
+ & ~FEATURE_BIT(gems)
#endif
- & ~FEATURE_BIT(frozen_string_literal)
+ & ~FEATURE_BIT(frozen_string_literal)
& ~feature_jit_mask
- )
+ )
};
static ruby_cmdline_options_t *
@@ -212,7 +223,7 @@ cmdline_options_init(ruby_cmdline_options_t *opt)
}
static rb_ast_t *load_file(VALUE parser, VALUE fname, VALUE f, int script,
- ruby_cmdline_options_t *opt);
+ ruby_cmdline_options_t *opt);
static VALUE open_load_file(VALUE fname_v, int *xflag);
static void forbid_setid(const char *, const ruby_cmdline_options_t *);
#define forbid_setid(s) forbid_setid((s), opt)
@@ -234,10 +245,10 @@ show_usage_line(const char *str, unsigned int namelen, unsigned int secondlen, i
const char *se = highlight ? esc_reset : esc_none;
const int wrap = help && namelen + secondlen - 1 > w;
printf(" %s%.*s%-*.*s%s%-*s%s\n", sb, namelen-1, str,
- (wrap ? 0 : w - namelen + 1),
- (help ? secondlen-1 : 0), str + namelen, se,
- (wrap ? w + 3 : 0), (wrap ? "\n" : ""),
- str + namelen + secondlen);
+ (wrap ? 0 : w - namelen + 1),
+ (help ? secondlen-1 : 0), str + namelen, se,
+ (wrap ? w + 3 : 0), (wrap ? "\n" : ""),
+ str + namelen + secondlen);
}
static void
@@ -248,71 +259,72 @@ usage(const char *name, int help, int highlight, int columns)
#define M(shortopt, longopt, desc) RUBY_OPT_MESSAGE(shortopt, longopt, desc)
-#if YJIT_BUILD
+#if USE_YJIT
# define PLATFORM_JIT_OPTION "--yjit"
#else
-# define PLATFORM_JIT_OPTION "--mjit"
+# define PLATFORM_JIT_OPTION "--mjit (experimental)"
#endif
static const struct ruby_opt_message usage_msg[] = {
- M("-0[octal]", "", "specify record separator (\\0, if no argument)"),
- M("-a", "", "autosplit mode with -n or -p (splits $_ into $F)"),
- M("-c", "", "check syntax only"),
- M("-Cdirectory", "", "cd to directory before executing your script"),
- M("-d", ", --debug", "set debugging flags (set $DEBUG to true)"),
- M("-e 'command'", "", "one line of script. Several -e's allowed. Omit [programfile]"),
- M("-Eex[:in]", ", --encoding=ex[:in]", "specify the default external and internal character encodings"),
- M("-Fpattern", "", "split() pattern for autosplit (-a)"),
- M("-i[extension]", "", "edit ARGV files in place (make backup if extension supplied)"),
- M("-Idirectory", "", "specify $LOAD_PATH directory (may be used more than once)"),
- M("-l", "", "enable line ending processing"),
- M("-n", "", "assume 'while gets(); ... end' loop around your script"),
- M("-p", "", "assume loop like -n but print line also like sed"),
- M("-rlibrary", "", "require the library before executing your script"),
- M("-s", "", "enable some switch parsing for switches after script name"),
- M("-S", "", "look for the script using PATH environment variable"),
- M("-v", "", "print the version number, then turn on verbose mode"),
- M("-w", "", "turn warnings on for your script"),
- M("-W[level=2|:category]", "", "set warning level; 0=silence, 1=medium, 2=verbose"),
- M("-x[directory]", "", "strip off text before #!ruby line and perhaps cd to directory"),
- M("--jit", "", "enable JIT for the platform, same as " PLATFORM_JIT_OPTION " (experimental)"),
+ M("-0[octal]", "", "specify record separator (\\0, if no argument)"),
+ M("-a", "", "autosplit mode with -n or -p (splits $_ into $F)"),
+ M("-c", "", "check syntax only"),
+ M("-Cdirectory", "", "cd to directory before executing your script"),
+ M("-d", ", --debug", "set debugging flags (set $DEBUG to true)"),
+ M("-e 'command'", "", "one line of script. Several -e's allowed. Omit [programfile]"),
+ M("-Eex[:in]", ", --encoding=ex[:in]", "specify the default external and internal character encodings"),
+ M("-Fpattern", "", "split() pattern for autosplit (-a)"),
+ M("-i[extension]", "", "edit ARGV files in place (make backup if extension supplied)"),
+ M("-Idirectory", "", "specify $LOAD_PATH directory (may be used more than once)"),
+ M("-l", "", "enable line ending processing"),
+ M("-n", "", "assume 'while gets(); ... end' loop around your script"),
+ M("-p", "", "assume loop like -n but print line also like sed"),
+ M("-rlibrary", "", "require the library before executing your script"),
+ M("-s", "", "enable some switch parsing for switches after script name"),
+ M("-S", "", "look for the script using PATH environment variable"),
+ M("-v", "", "print the version number, then turn on verbose mode"),
+ M("-w", "", "turn warnings on for your script"),
+ M("-W[level=2|:category]", "", "set warning level; 0=silence, 1=medium, 2=verbose"),
+ M("-x[directory]", "", "strip off text before #!ruby line and perhaps cd to directory"),
+ M("--jit", "", "enable JIT for the platform, same as " PLATFORM_JIT_OPTION),
#if USE_MJIT
M("--mjit", "", "enable C compiler-based JIT compiler (experimental)"),
#endif
-#if YJIT_BUILD
- M("--yjit", "", "enable in-process JIT compiler (experimental)"),
+#if USE_YJIT
+ M("--yjit", "", "enable in-process JIT compiler"),
#endif
- M("-h", "", "show this message, --help for more info"),
+ M("-h", "", "show this message, --help for more info"),
};
static const struct ruby_opt_message help_msg[] = {
- M("--copyright", "", "print the copyright"),
- M("--dump={insns|parsetree|...}[,...]", "",
+ M("--copyright", "", "print the copyright"),
+ M("--dump={insns|parsetree|...}[,...]", "",
"dump debug information. see below for available dump list"),
- M("--enable={jit|rubyopt|...}[,...]", ", --disable={jit|rubyopt|...}[,...]",
- "enable or disable features. see below for available features"),
- M("--external-encoding=encoding", ", --internal-encoding=encoding",
- "specify the default external or internal character encoding"),
- M("--backtrace-limit=num", "", "limit the maximum length of backtrace"),
- M("--verbose", "", "turn on verbose mode and disable script from stdin"),
- M("--version", "", "print the version number, then exit"),
- M("--help", "", "show this message, -h for short message"),
+ M("--enable={jit|rubyopt|...}[,...]", ", --disable={jit|rubyopt|...}[,...]",
+ "enable or disable features. see below for available features"),
+ M("--external-encoding=encoding", ", --internal-encoding=encoding",
+ "specify the default external or internal character encoding"),
+ M("--backtrace-limit=num", "", "limit the maximum length of backtrace"),
+ M("--verbose", "", "turn on verbose mode and disable script from stdin"),
+ M("--version", "", "print the version number, then exit"),
+ M("--help", "", "show this message, -h for short message"),
};
static const struct ruby_opt_message dumps[] = {
- M("insns", "", "instruction sequences"),
- M("insns_without_opt", "", "instruction sequences compiled with no optimization"),
- M("yydebug", "", "yydebug of yacc parser generator"),
- M("parsetree", "", "AST"),
- M("parsetree_with_comment", "", "AST with comments"),
+ M("insns", "", "instruction sequences"),
+ M("insns_without_opt", "", "instruction sequences compiled with no optimization"),
+ M("yydebug(+error-tolerant)", "", "yydebug of yacc parser generator"),
+ M("parsetree(+error-tolerant)","", "AST"),
+ M("parsetree_with_comment(+error-tolerant)", "", "AST with comments"),
};
static const struct ruby_opt_message features[] = {
- M("gems", "", "rubygems (only for debugging, default: "DEFAULT_RUBYGEMS_ENABLED")"),
- M("error_highlight", "", "error_highlight (default: "DEFAULT_RUBYGEMS_ENABLED")"),
- M("did_you_mean", "", "did_you_mean (default: "DEFAULT_RUBYGEMS_ENABLED")"),
- M("rubyopt", "", "RUBYOPT environment variable (default: enabled)"),
- M("frozen-string-literal", "", "freeze all string literals (default: disabled)"),
+ M("gems", "", "rubygems (only for debugging, default: "DEFAULT_RUBYGEMS_ENABLED")"),
+ M("error_highlight", "", "error_highlight (default: "DEFAULT_RUBYGEMS_ENABLED")"),
+ M("did_you_mean", "", "did_you_mean (default: "DEFAULT_RUBYGEMS_ENABLED")"),
+ M("syntax_suggest", "", "syntax_suggest (default: "DEFAULT_RUBYGEMS_ENABLED")"),
+ M("rubyopt", "", "RUBYOPT environment variable (default: enabled)"),
+ M("frozen-string-literal", "", "freeze all string literals (default: disabled)"),
#if USE_MJIT
M("mjit", "", "C compiler-based JIT compiler (default: disabled)"),
#endif
-#if YJIT_BUILD
+#if USE_YJIT
M("yjit", "", "in-process JIT compiler (default: disabled)"),
#endif
};
@@ -323,12 +335,10 @@ usage(const char *name, int help, int highlight, int columns)
#if USE_MJIT
extern const struct ruby_opt_message mjit_option_messages[];
#endif
-#if YJIT_BUILD
+#if USE_YJIT
static const struct ruby_opt_message yjit_options[] = {
-#if YJIT_STATS
M("--yjit-stats", "", "Enable collecting YJIT statistics"),
-#endif
- M("--yjit-exec-mem-size=num", "", "Size of executable memory block in MiB (default: 256)"),
+ M("--yjit-exec-mem-size=num", "", "Size of executable memory block in MiB (default: 64)"),
M("--yjit-call-threshold=num", "", "Number of calls to trigger JIT (default: 10)"),
M("--yjit-max-versions=num", "", "Maximum number of versions per basic block (default: 4)"),
M("--yjit-greedy-versioning", "", "Greedy versioning mode (default: disabled)"),
@@ -343,30 +353,30 @@ usage(const char *name, int help, int highlight, int columns)
printf("%sUsage:%s %s [switches] [--] [programfile] [arguments]\n", sb, se, name);
for (i = 0; i < num; ++i)
- SHOW(usage_msg[i]);
+ SHOW(usage_msg[i]);
if (!help) return;
if (highlight) sb = esc_standout;
for (i = 0; i < numberof(help_msg); ++i)
- SHOW(help_msg[i]);
+ SHOW(help_msg[i]);
printf("%s""Dump List:%s\n", sb, se);
for (i = 0; i < numberof(dumps); ++i)
- SHOW(dumps[i]);
+ SHOW(dumps[i]);
printf("%s""Features:%s\n", sb, se);
for (i = 0; i < numberof(features); ++i)
- SHOW(features[i]);
+ SHOW(features[i]);
printf("%s""Warning categories:%s\n", sb, se);
for (i = 0; i < numberof(warn_categories); ++i)
- SHOW(warn_categories[i]);
+ SHOW(warn_categories[i]);
#if USE_MJIT
printf("%s""MJIT options (experimental):%s\n", sb, se);
for (i = 0; mjit_option_messages[i].str; ++i)
- SHOW(mjit_option_messages[i]);
+ SHOW(mjit_option_messages[i]);
#endif
-#if YJIT_BUILD
- printf("%s""YJIT options (experimental):%s\n", sb, se);
+#if USE_YJIT
+ printf("%s""YJIT options:%s\n", sb, se);
for (i = 0; i < numberof(yjit_options); ++i)
SHOW(yjit_options[i]);
#endif
@@ -383,12 +393,12 @@ push_include(const char *path, VALUE (*filter)(VALUE))
p = path;
while (*p) {
- while (*p == sep)
- p++;
- if (!*p) break;
- for (s = p; *s && *s != sep; s = CharNext(s));
- rb_ary_push(load_path, (*filter)(rubylib_path_new(p, s - p)));
- p = s;
+ while (*p == sep)
+ p++;
+ if (!*p) break;
+ for (s = p; *s && *s != sep; s = CharNext(s));
+ rb_ary_push(load_path, (*filter)(rubylib_path_new(p, s - p)));
+ p = s;
}
}
@@ -402,33 +412,33 @@ push_include_cygwin(const char *path, VALUE (*filter)(VALUE))
p = path;
while (*p) {
- unsigned int len;
- while (*p == ';')
- p++;
- if (!*p) break;
- for (s = p; *s && *s != ';'; s = CharNext(s));
- len = s - p;
- if (*s) {
- if (!buf) {
- buf = rb_str_new(p, len);
- p = RSTRING_PTR(buf);
- }
- else {
- rb_str_resize(buf, len);
- p = strncpy(RSTRING_PTR(buf), p, len);
- }
- }
+ unsigned int len;
+ while (*p == ';')
+ p++;
+ if (!*p) break;
+ for (s = p; *s && *s != ';'; s = CharNext(s));
+ len = s - p;
+ if (*s) {
+ if (!buf) {
+ buf = rb_str_new(p, len);
+ p = RSTRING_PTR(buf);
+ }
+ else {
+ rb_str_resize(buf, len);
+ p = strncpy(RSTRING_PTR(buf), p, len);
+ }
+ }
#ifdef HAVE_CYGWIN_CONV_PATH
#define CONV_TO_POSIX_PATH(p, lib) \
- cygwin_conv_path(CCP_WIN_A_TO_POSIX|CCP_RELATIVE, (p), (lib), sizeof(lib))
+ cygwin_conv_path(CCP_WIN_A_TO_POSIX|CCP_RELATIVE, (p), (lib), sizeof(lib))
#else
# error no cygwin_conv_path
#endif
- if (CONV_TO_POSIX_PATH(p, rubylib) == 0)
- p = rubylib;
- push_include(p, filter);
- if (!*s) break;
- p = s + 1;
+ if (CONV_TO_POSIX_PATH(p, rubylib) == 0)
+ p = rubylib;
+ push_include(p, filter);
+ if (!*s) break;
+ p = s + 1;
}
}
@@ -439,7 +449,7 @@ void
ruby_push_include(const char *path, VALUE (*filter)(VALUE))
{
if (path == 0)
- return;
+ return;
push_include(path, filter);
}
@@ -466,9 +476,9 @@ expand_include_path(VALUE path)
{
char *p = RSTRING_PTR(path);
if (!p)
- return path;
+ return path;
if (*p == '.' && p[1] == '/')
- return path;
+ return path;
return rb_file_expand_path(path, Qnil);
}
@@ -486,7 +496,7 @@ BOOL WINAPI
DllMain(HINSTANCE dll, DWORD reason, LPVOID reserved)
{
if (reason == DLL_PROCESS_ATTACH)
- libruby = dll;
+ libruby = dll;
return TRUE;
}
@@ -500,14 +510,16 @@ static inline void
translit_char_bin(char *p, int from, int to)
{
while (*p) {
- if ((unsigned char)*p == from)
- *p = to;
- p++;
+ if ((unsigned char)*p == from)
+ *p = to;
+ p++;
}
}
#endif
#ifdef _WIN32
+# undef chdir
+# define chdir rb_w32_uchdir
# define UTF8_PATH 1
#endif
@@ -525,8 +537,8 @@ static VALUE
str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
{
return rb_str_conv_enc_opts(str, from, to,
- ECONV_UNDEF_REPLACE|ECONV_INVALID_REPLACE,
- Qnil);
+ ECONV_UNDEF_REPLACE|ECONV_INVALID_REPLACE,
+ Qnil);
}
#else
# define str_conv_enc(str, from, to) (str)
@@ -534,7 +546,7 @@ str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
void ruby_init_loadpath(void);
-#if defined(LOAD_RELATIVE)
+#if defined(LOAD_RELATIVE) || defined(__MACH__)
static VALUE
runtime_libruby_path(void)
{
@@ -551,33 +563,33 @@ runtime_libruby_path(void)
char *libpath;
while (wlibpath = (WCHAR *)RSTRING_PTR(wsopath),
- ret = GetModuleFileNameW(libruby, wlibpath, len),
- (ret == len))
+ ret = GetModuleFileNameW(libruby, wlibpath, len),
+ (ret == len))
{
- rb_str_modify_expand(wsopath, len*sizeof(WCHAR));
- rb_str_set_len(wsopath, (len += len)*sizeof(WCHAR));
+ rb_str_modify_expand(wsopath, len*sizeof(WCHAR));
+ rb_str_set_len(wsopath, (len += len)*sizeof(WCHAR));
}
if (!ret || ret > len) rb_fatal("failed to get module file name");
#if defined __CYGWIN__
{
- const int win_to_posix = CCP_WIN_W_TO_POSIX | CCP_RELATIVE;
- size_t newsize = cygwin_conv_path(win_to_posix, wlibpath, 0, 0);
- if (!newsize) rb_fatal("failed to convert module path to cygwin");
- path = rb_str_new(0, newsize);
- libpath = RSTRING_PTR(path);
- if (cygwin_conv_path(win_to_posix, wlibpath, libpath, newsize)) {
- rb_str_resize(path, 0);
- }
+ const int win_to_posix = CCP_WIN_W_TO_POSIX | CCP_RELATIVE;
+ size_t newsize = cygwin_conv_path(win_to_posix, wlibpath, 0, 0);
+ if (!newsize) rb_fatal("failed to convert module path to cygwin");
+ path = rb_str_new(0, newsize);
+ libpath = RSTRING_PTR(path);
+ if (cygwin_conv_path(win_to_posix, wlibpath, libpath, newsize)) {
+ rb_str_resize(path, 0);
+ }
}
#else
{
- DWORD i;
- for (len = ret, i = 0; i < len; ++i) {
- if (wlibpath[i] == L'\\') {
- wlibpath[i] = L'/';
- ret = i+1; /* chop after the last separator */
- }
- }
+ DWORD i;
+ for (len = ret, i = 0; i < len; ++i) {
+ if (wlibpath[i] == L'\\') {
+ wlibpath[i] = L'/';
+ ret = i+1; /* chop after the last separator */
+ }
+ }
}
len = WideCharToMultiByte(CP_UTF8, 0, wlibpath, ret, NULL, 0, NULL, NULL);
path = rb_utf8_str_new(0, len);
@@ -592,17 +604,17 @@ runtime_libruby_path(void)
const void* addr = (void *)(VALUE)expand_include_path;
if (!dladdr((void *)addr, &dli)) {
- return rb_str_new(0, 0);
+ return rb_str_new(0, 0);
}
#ifdef __linux__
else if (origarg.argc > 0 && origarg.argv && dli.dli_fname == origarg.argv[0]) {
- fname = rb_str_new_cstr("/proc/self/exe");
- path = rb_readlink(fname, NULL);
+ fname = rb_str_new_cstr("/proc/self/exe");
+ path = rb_readlink(fname, NULL);
}
#endif
else {
- fname = rb_str_new_cstr(dli.dli_fname);
- path = rb_realpath_internal(Qnil, fname, 1);
+ fname = rb_str_new_cstr(dli.dli_fname);
+ path = rb_realpath_internal(Qnil, fname, 1);
}
rb_str_resize(fname, 0);
return path;
@@ -615,6 +627,10 @@ runtime_libruby_path(void)
#define INITIAL_LOAD_PATH_MARK rb_intern_const("@gem_prelude_index")
VALUE ruby_archlibdir_path, ruby_prefix_path;
+#if defined(__MACH__)
+// A path to libruby.dylib itself or where it's statically linked to.
+VALUE rb_libruby_selfpath;
+#endif
void
ruby_init_loadpath(void)
@@ -622,6 +638,20 @@ ruby_init_loadpath(void)
VALUE load_path, archlibdir = 0;
ID id_initial_load_path_mark;
const char *paths = ruby_initial_load_paths;
+#if defined(LOAD_RELATIVE) || defined(__MACH__)
+ VALUE libruby_path = runtime_libruby_path();
+# if defined(__MACH__)
+ VALUE selfpath = libruby_path;
+# if defined(LOAD_RELATIVE)
+ selfpath = rb_str_dup(selfpath);
+# endif
+ rb_obj_hide(selfpath);
+ OBJ_FREEZE_RAW(selfpath);
+ rb_gc_register_address(&rb_libruby_selfpath);
+ rb_libruby_selfpath = selfpath;
+# endif
+#endif
+
#if defined LOAD_RELATIVE
#if !defined ENABLE_MULTIARCH
# define RUBY_ARCH_PATH ""
@@ -635,49 +665,49 @@ ruby_init_loadpath(void)
size_t baselen;
const char *p;
- sopath = runtime_libruby_path();
+ sopath = libruby_path;
libpath = RSTRING_PTR(sopath);
p = strrchr(libpath, '/');
if (p) {
- static const char libdir[] = "/"
+ static const char libdir[] = "/"
#ifdef LIBDIR_BASENAME
- LIBDIR_BASENAME
+ LIBDIR_BASENAME
#else
- "lib"
+ "lib"
#endif
- RUBY_ARCH_PATH;
- const ptrdiff_t libdir_len = (ptrdiff_t)sizeof(libdir)
- - rb_strlen_lit(RUBY_ARCH_PATH) - 1;
- static const char bindir[] = "/bin";
- const ptrdiff_t bindir_len = (ptrdiff_t)sizeof(bindir) - 1;
+ RUBY_ARCH_PATH;
+ const ptrdiff_t libdir_len = (ptrdiff_t)sizeof(libdir)
+ - rb_strlen_lit(RUBY_ARCH_PATH) - 1;
+ static const char bindir[] = "/bin";
+ const ptrdiff_t bindir_len = (ptrdiff_t)sizeof(bindir) - 1;
- const char *p2 = NULL;
+ const char *p2 = NULL;
#ifdef ENABLE_MULTIARCH
multiarch:
#endif
- if (p - libpath >= bindir_len && !STRNCASECMP(p - bindir_len, bindir, bindir_len)) {
- p -= bindir_len;
- archlibdir = rb_str_subseq(sopath, 0, p - libpath);
- rb_str_cat_cstr(archlibdir, libdir);
- OBJ_FREEZE_RAW(archlibdir);
- }
- else if (p - libpath >= libdir_len && !strncmp(p - libdir_len, libdir, libdir_len)) {
- archlibdir = rb_str_subseq(sopath, 0, (p2 ? p2 : p) - libpath);
- OBJ_FREEZE_RAW(archlibdir);
- p -= libdir_len;
- }
+ if (p - libpath >= bindir_len && !STRNCASECMP(p - bindir_len, bindir, bindir_len)) {
+ p -= bindir_len;
+ archlibdir = rb_str_subseq(sopath, 0, p - libpath);
+ rb_str_cat_cstr(archlibdir, libdir);
+ OBJ_FREEZE_RAW(archlibdir);
+ }
+ else if (p - libpath >= libdir_len && !strncmp(p - libdir_len, libdir, libdir_len)) {
+ archlibdir = rb_str_subseq(sopath, 0, (p2 ? p2 : p) - libpath);
+ OBJ_FREEZE_RAW(archlibdir);
+ p -= libdir_len;
+ }
#ifdef ENABLE_MULTIARCH
- else if (p2) {
- p = p2;
- }
- else {
- p2 = p;
- p = rb_enc_path_last_separator(libpath, p, rb_ascii8bit_encoding());
- if (p) goto multiarch;
- p = p2;
- }
+ else if (p2) {
+ p = p2;
+ }
+ else {
+ p2 = p;
+ p = rb_enc_path_last_separator(libpath, p, rb_ascii8bit_encoding());
+ if (p) goto multiarch;
+ p = p2;
+ }
#endif
baselen = p - libpath;
}
@@ -707,11 +737,11 @@ ruby_init_loadpath(void)
id_initial_load_path_mark = INITIAL_LOAD_PATH_MARK;
while (*paths) {
- size_t len = strlen(paths);
- VALUE path = RUBY_RELATIVE(paths, len);
- rb_ivar_set(path, id_initial_load_path_mark, path);
- rb_ary_push(load_path, path);
- paths += len + 1;
+ size_t len = strlen(paths);
+ VALUE path = RUBY_RELATIVE(paths, len);
+ rb_ivar_set(path, id_initial_load_path_mark, path);
+ rb_ary_push(load_path, path);
+ paths += len + 1;
}
rb_const_set(rb_cObject, rb_intern_const("TMP_RUBY_PREFIX"), ruby_prefix_path);
@@ -725,7 +755,7 @@ add_modules(VALUE *req_list, const char *mod)
VALUE feature;
if (!list) {
- *req_list = list = rb_ary_tmp_new(0);
+ *req_list = list = rb_ary_hidden_new(0);
}
feature = rb_str_cat_cstr(rb_str_tmp_new(0), mod);
rb_ary_push(list, feature);
@@ -741,11 +771,11 @@ require_libraries(VALUE *req_list)
CONST_ID(require, "require");
while (list && RARRAY_LEN(list) > 0) {
- VALUE feature = rb_ary_shift(list);
- rb_enc_associate(feature, extenc);
- RBASIC_SET_CLASS_RAW(feature, rb_cString);
- OBJ_FREEZE(feature);
- rb_funcallv(self, require, 1, &feature);
+ VALUE feature = rb_ary_shift(list);
+ rb_enc_associate(feature, extenc);
+ RBASIC_SET_CLASS_RAW(feature, rb_cString);
+ OBJ_FREEZE(feature);
+ rb_funcallv(self, require, 1, &feature);
}
*req_list = 0;
}
@@ -760,63 +790,63 @@ static void
process_sflag(int *sflag)
{
if (*sflag > 0) {
- long n;
- const VALUE *args;
- VALUE argv = rb_argv;
-
- n = RARRAY_LEN(argv);
- args = RARRAY_CONST_PTR(argv);
- while (n > 0) {
- VALUE v = *args++;
- char *s = StringValuePtr(v);
- char *p;
- int hyphen = FALSE;
-
- if (s[0] != '-')
- break;
- n--;
- if (s[1] == '-' && s[2] == '\0')
- break;
-
- v = Qtrue;
- /* check if valid name before replacing - with _ */
- for (p = s + 1; *p; p++) {
- if (*p == '=') {
- *p++ = '\0';
- v = rb_str_new2(p);
- break;
- }
- if (*p == '-') {
- hyphen = TRUE;
- }
- else if (*p != '_' && !ISALNUM(*p)) {
- VALUE name_error[2];
- name_error[0] =
- rb_str_new2("invalid name for global variable - ");
- if (!(p = strchr(p, '='))) {
- rb_str_cat2(name_error[0], s);
- }
- else {
- rb_str_cat(name_error[0], s, p - s);
- }
- name_error[1] = args[-1];
- rb_exc_raise(rb_class_new_instance(2, name_error, rb_eNameError));
- }
- }
- s[0] = '$';
- if (hyphen) {
- for (p = s + 1; *p; ++p) {
- if (*p == '-')
- *p = '_';
- }
- }
- rb_gv_set(s, v);
- }
- n = RARRAY_LEN(argv) - n;
- while (n--) {
- rb_ary_shift(argv);
- }
- *sflag = -1;
+ long n;
+ const VALUE *args;
+ VALUE argv = rb_argv;
+
+ n = RARRAY_LEN(argv);
+ args = RARRAY_CONST_PTR(argv);
+ while (n > 0) {
+ VALUE v = *args++;
+ char *s = StringValuePtr(v);
+ char *p;
+ int hyphen = FALSE;
+
+ if (s[0] != '-')
+ break;
+ n--;
+ if (s[1] == '-' && s[2] == '\0')
+ break;
+
+ v = Qtrue;
+ /* check if valid name before replacing - with _ */
+ for (p = s + 1; *p; p++) {
+ if (*p == '=') {
+ *p++ = '\0';
+ v = rb_str_new2(p);
+ break;
+ }
+ if (*p == '-') {
+ hyphen = TRUE;
+ }
+ else if (*p != '_' && !ISALNUM(*p)) {
+ VALUE name_error[2];
+ name_error[0] =
+ rb_str_new2("invalid name for global variable - ");
+ if (!(p = strchr(p, '='))) {
+ rb_str_cat2(name_error[0], s);
+ }
+ else {
+ rb_str_cat(name_error[0], s, p - s);
+ }
+ name_error[1] = args[-1];
+ rb_exc_raise(rb_class_new_instance(2, name_error, rb_eNameError));
+ }
+ }
+ s[0] = '$';
+ if (hyphen) {
+ for (p = s + 1; *p; ++p) {
+ if (*p == '-')
+ *p = '_';
+ }
+ }
+ rb_gv_set(s, v);
+ }
+ n = RARRAY_LEN(argv) - n;
+ while (n--) {
+ rb_ary_shift(argv);
+ }
+ *sflag = -1;
}
}
@@ -842,12 +872,12 @@ moreswitches(const char *s, ruby_cmdline_options_t *opt, int envopt)
ap = 0;
rb_str_cat(argary, (char *)&ap, sizeof(ap));
while (*p) {
- ap = p;
- rb_str_cat(argary, (char *)&ap, sizeof(ap));
- while (*p && !ISSPACE(*p)) ++p;
- if (!*p) break;
- *p++ = '\0';
- while (ISSPACE(*p)) ++p;
+ ap = p;
+ rb_str_cat(argary, (char *)&ap, sizeof(ap));
+ while (*p && !ISSPACE(*p)) ++p;
+ if (!*p) break;
+ *p++ = '\0';
+ while (ISSPACE(*p)) ++p;
}
argc = RSTRING_LEN(argary) / sizeof(ap);
ap = 0;
@@ -856,14 +886,14 @@ moreswitches(const char *s, ruby_cmdline_options_t *opt, int envopt)
MEMMOVE(argv, RSTRING_PTR(argary), char *, argc);
while ((i = proc_options(argc, argv, opt, envopt)) > 1 && envopt && (argc -= i) > 0) {
- argv += i;
- if (**argv != '-') {
- *--*argv = '-';
- }
- if ((*argv)[1]) {
- ++argc;
- --argv;
- }
+ argv += i;
+ if (**argv != '-') {
+ *--*argv = '-';
+ }
+ if ((*argv)[1]) {
+ ++argc;
+ --argv;
+ }
}
ruby_xfree(ptr);
@@ -877,15 +907,17 @@ name_match_p(const char *name, const char *str, size_t len)
{
if (len == 0) return 0;
while (1) {
- while (TOLOWER(*str) == *name) {
- if (!--len || !*++str) return 1;
- ++name;
- }
- if (*str != '-' && *str != '_') return 0;
- while (ISALNUM(*name)) name++;
- if (*name != '-' && *name != '_') return 0;
- ++name;
- ++str;
+ while (TOLOWER(*str) == *name) {
+ if (!--len) return 1;
+ ++name;
+ ++str;
+ }
+ if (*str != '-' && *str != '_') return 0;
+ while (ISALNUM(*name)) name++;
+ if (*name != '-' && *name != '_') return 0;
+ ++name;
+ ++str;
+ if (--len == 0) return 1;
}
}
@@ -894,14 +926,14 @@ name_match_p(const char *name, const char *str, size_t len)
#define UNSET_WHEN(name, bit, str, len) \
if (NAME_MATCH_P((name), (str), (len))) { \
- *(unsigned int *)arg &= ~(bit); \
- return; \
+ *(unsigned int *)arg &= ~(bit); \
+ return; \
}
#define SET_WHEN(name, bit, str, len) \
if (NAME_MATCH_P((name), (str), (len))) { \
- *(unsigned int *)arg |= (bit); \
- return; \
+ *(unsigned int *)arg |= (bit); \
+ return; \
}
#define LITERAL_NAME_ELEMENT(name) #name
@@ -934,22 +966,22 @@ feature_option(const char *str, int len, void *arg, const unsigned int enable)
#if AMBIGUOUS_FEATURE_NAMES
if (matched == 1) goto found;
if (matched > 1) {
- VALUE mesg = rb_sprintf("ambiguous feature: `%.*s' (", len, str);
+ VALUE mesg = rb_sprintf("ambiguous feature: `%.*s' (", len, str);
#define ADD_FEATURE_NAME(bit) \
- if (FEATURE_BIT(bit) & set) { \
- rb_str_cat_cstr(mesg, #bit); \
- if (--matched) rb_str_cat_cstr(mesg, ", "); \
- }
- EACH_FEATURES(ADD_FEATURE_NAME, ;);
- rb_str_cat_cstr(mesg, ")");
- rb_exc_raise(rb_exc_new_str(rb_eRuntimeError, mesg));
+ if (FEATURE_BIT(bit) & set) { \
+ rb_str_cat_cstr(mesg, #bit); \
+ if (--matched) rb_str_cat_cstr(mesg, ", "); \
+ }
+ EACH_FEATURES(ADD_FEATURE_NAME, ;);
+ rb_str_cat_cstr(mesg, ")");
+ rb_exc_raise(rb_exc_new_str(rb_eRuntimeError, mesg));
#undef ADD_FEATURE_NAME
}
#else
(void)set;
#endif
rb_warn("unknown argument for --%s: `%.*s'",
- enable ? "enable" : "disable", len, str);
+ enable ? "enable" : "disable", len, str);
rb_warn("features are [%.*s].", (int)strlen(list), list);
return;
@@ -991,11 +1023,49 @@ debug_option(const char *str, int len, void *arg)
rb_warn("debug features are [%.*s].", (int)strlen(list), list);
}
+static int
+memtermspn(const char *str, char term, int len)
+{
+ RUBY_ASSERT(len >= 0);
+ if (len <= 0) return 0;
+ const char *next = memchr(str, term, len);
+ return next ? (int)(next - str) : len;
+}
+
+static const char additional_opt_sep = '+';
+
+static unsigned int
+dump_additional_option(const char *str, int len, unsigned int bits, const char *name)
+{
+ int w;
+ for (; len-- > 0 && *str++ == additional_opt_sep; len -= w, str += w) {
+ w = memtermspn(str, additional_opt_sep, len);
+#define SET_ADDITIONAL(bit) if (NAME_MATCH_P(#bit, str, w)) { \
+ if (bits & DUMP_BIT(bit)) \
+ rb_warn("duplicate option to dump %s: `%.*s'", name, w, str); \
+ bits |= DUMP_BIT(bit); \
+ continue; \
+ }
+ if (dump_error_tolerant_bits & bits) {
+ SET_ADDITIONAL(error_tolerant);
+ }
+ rb_warn("don't know how to dump %s with `%.*s'", name, w, str);
+ }
+ return bits;
+}
+
static void
dump_option(const char *str, int len, void *arg)
{
static const char list[] = EACH_DUMPS(LITERAL_NAME_ELEMENT, ", ");
-#define SET_WHEN_DUMP(bit) SET_WHEN(#bit, DUMP_BIT(bit), str, len)
+ int w = memtermspn(str, additional_opt_sep, len);
+
+#define SET_WHEN_DUMP(bit) \
+ if (NAME_MATCH_P(#bit, (str), (w))) { \
+ *(unsigned int *)arg |= \
+ dump_additional_option(str + w, len - w, DUMP_BIT(bit), #bit); \
+ return; \
+ }
EACH_DUMPS(SET_WHEN_DUMP, ;);
rb_warn("don't know how to dump `%.*s',", len, str);
rb_warn("but only [%.*s].", (int)strlen(list), list);
@@ -1010,9 +1080,9 @@ set_option_encoding_once(const char *type, VALUE *name, const char *e, long elen
ename = rb_str_new(e, elen);
if (*name &&
- rb_funcall(ename, rb_intern("casecmp"), 1, *name) != INT2FIX(0)) {
- rb_raise(rb_eRuntimeError,
- "%s already set to %"PRIsVALUE, type, *name);
+ rb_funcall(ename, rb_intern("casecmp"), 1, *name) != INT2FIX(0)) {
+ rb_raise(rb_eRuntimeError,
+ "%s already set to %"PRIsVALUE, type, *name);
}
*name = ename;
}
@@ -1029,7 +1099,7 @@ set_option_encoding_once(const char *type, VALUE *name, const char *e, long elen
#define yjit_opt_match_arg(s, l, name) \
opt_match(s, l, name) && (*(s) && *(s+1) ? 1 : (rb_raise(rb_eRuntimeError, "--yjit-" name " needs an argument"), 0))
-#if YJIT_BUILD
+#if USE_YJIT
static bool
setup_yjit_options(const char *s)
{
@@ -1057,61 +1127,61 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
int warning = opt->warning;
if (argc <= 0 || !argv)
- return 0;
+ return 0;
for (argc--, argv++; argc > 0; argc--, argv++) {
- const char *const arg = argv[0];
- if (!arg || arg[0] != '-' || !arg[1])
- break;
+ const char *const arg = argv[0];
+ if (!arg || arg[0] != '-' || !arg[1])
+ break;
- s = arg + 1;
+ s = arg + 1;
reswitch:
- switch (*s) {
- case 'a':
- if (envopt) goto noenvopt;
- opt->do_split = TRUE;
- s++;
- goto reswitch;
-
- case 'p':
- if (envopt) goto noenvopt;
- opt->do_print = TRUE;
- /* through */
- case 'n':
- if (envopt) goto noenvopt;
- opt->do_loop = TRUE;
- s++;
- goto reswitch;
-
- case 'd':
- ruby_debug = Qtrue;
- ruby_verbose = Qtrue;
- s++;
- goto reswitch;
-
- case 'y':
- if (envopt) goto noenvopt;
- opt->dump |= DUMP_BIT(yydebug);
- s++;
- goto reswitch;
-
- case 'v':
- if (opt->verbose) {
- s++;
- goto reswitch;
- }
- opt->dump |= DUMP_BIT(version_v);
- opt->verbose = 1;
- case 'w':
- if (!opt->warning) {
- warning = 1;
- ruby_verbose = Qtrue;
- }
- FEATURE_SET(opt->warn, RB_WARN_CATEGORY_ALL_BITS);
- s++;
- goto reswitch;
-
- case 'W':
+ switch (*s) {
+ case 'a':
+ if (envopt) goto noenvopt;
+ opt->do_split = TRUE;
+ s++;
+ goto reswitch;
+
+ case 'p':
+ if (envopt) goto noenvopt;
+ opt->do_print = TRUE;
+ /* through */
+ case 'n':
+ if (envopt) goto noenvopt;
+ opt->do_loop = TRUE;
+ s++;
+ goto reswitch;
+
+ case 'd':
+ ruby_debug = Qtrue;
+ ruby_verbose = Qtrue;
+ s++;
+ goto reswitch;
+
+ case 'y':
+ if (envopt) goto noenvopt;
+ opt->dump |= DUMP_BIT(yydebug);
+ s++;
+ goto reswitch;
+
+ case 'v':
+ if (opt->verbose) {
+ s++;
+ goto reswitch;
+ }
+ opt->dump |= DUMP_BIT(version_v);
+ opt->verbose = 1;
+ case 'w':
+ if (!opt->warning) {
+ warning = 1;
+ ruby_verbose = Qtrue;
+ }
+ FEATURE_SET(opt->warn, RB_WARN_CATEGORY_ALL_BITS);
+ s++;
+ goto reswitch;
+
+ case 'W':
if (s[1] == ':') {
unsigned int bits = 0;
static const char no_prefix[] = "no-";
@@ -1130,293 +1200,293 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
if (bits) FEATURE_SET_TO(opt->warn, bits, enable ? bits : 0);
break;
}
- {
- size_t numlen;
- int v = 2; /* -W as -W2 */
+ {
+ size_t numlen;
+ int v = 2; /* -W as -W2 */
- if (*++s) {
- v = scan_oct(s, 1, &numlen);
- if (numlen == 0)
+ if (*++s) {
+ v = scan_oct(s, 1, &numlen);
+ if (numlen == 0)
v = 2;
- s += numlen;
- }
- if (!opt->warning) {
- switch (v) {
- case 0:
- ruby_verbose = Qnil;
- break;
- case 1:
- ruby_verbose = Qfalse;
- break;
- default:
- ruby_verbose = Qtrue;
- break;
- }
- }
- warning = 1;
- switch (v) {
- case 0:
- FEATURE_SET_TO(opt->warn, RB_WARN_CATEGORY_ALL_BITS, 0);
- break;
- case 1:
- FEATURE_SET_TO(opt->warn, 1U << RB_WARN_CATEGORY_DEPRECATED, 0);
- break;
- default:
- FEATURE_SET(opt->warn, RB_WARN_CATEGORY_ALL_BITS);
- break;
- }
- }
- goto reswitch;
-
- case 'c':
- if (envopt) goto noenvopt;
- opt->dump |= DUMP_BIT(syntax);
- s++;
- goto reswitch;
-
- case 's':
- if (envopt) goto noenvopt;
- forbid_setid("-s");
- if (!opt->sflag) opt->sflag = 1;
- s++;
- goto reswitch;
-
- case 'h':
- if (envopt) goto noenvopt;
- opt->dump |= DUMP_BIT(usage);
- goto switch_end;
-
- case 'l':
- if (envopt) goto noenvopt;
- opt->do_line = TRUE;
- rb_output_rs = rb_rs;
- s++;
- goto reswitch;
-
- case 'S':
- if (envopt) goto noenvopt;
- forbid_setid("-S");
- opt->do_search = TRUE;
- s++;
- goto reswitch;
-
- case 'e':
- if (envopt) goto noenvopt;
- forbid_setid("-e");
- if (!*++s) {
- if (!--argc)
- rb_raise(rb_eRuntimeError, "no code specified for -e");
- s = *++argv;
- }
- if (!opt->e_script) {
- opt->e_script = rb_str_new(0, 0);
- if (opt->script == 0)
- opt->script = "-e";
- }
- rb_str_cat2(opt->e_script, s);
- rb_str_cat2(opt->e_script, "\n");
- break;
-
- case 'r':
- forbid_setid("-r");
- if (*++s) {
- add_modules(&opt->req_list, s);
- }
- else if (argc > 1) {
- add_modules(&opt->req_list, argv[1]);
- argc--, argv++;
- }
- break;
-
- case 'i':
- if (envopt) goto noenvopt;
- forbid_setid("-i");
- ruby_set_inplace_mode(s + 1);
- break;
-
- case 'x':
- if (envopt) goto noenvopt;
- forbid_setid("-x");
- opt->xflag = TRUE;
- s++;
- if (*s && chdir(s) < 0) {
- rb_fatal("Can't chdir to %s", s);
- }
- break;
-
- case 'C':
- case 'X':
- if (envopt) goto noenvopt;
- if (!*++s && (!--argc || !(s = *++argv) || !*s)) {
- rb_fatal("Can't chdir");
- }
- if (chdir(s) < 0) {
- rb_fatal("Can't chdir to %s", s);
- }
- break;
-
- case 'F':
- if (envopt) goto noenvopt;
- if (*++s) {
- rb_fs = rb_reg_new(s, strlen(s), 0);
- }
- break;
-
- case 'E':
- if (!*++s && (!--argc || !(s = *++argv))) {
- rb_raise(rb_eRuntimeError, "missing argument for -E");
- }
- goto encoding;
-
- case 'U':
- set_internal_encoding_once(opt, "UTF-8", 0);
- ++s;
- goto reswitch;
-
- case 'K':
- if (*++s) {
- const char *enc_name = 0;
- switch (*s) {
- case 'E': case 'e':
- enc_name = "EUC-JP";
- break;
- case 'S': case 's':
- enc_name = "Windows-31J";
- break;
- case 'U': case 'u':
- enc_name = "UTF-8";
- break;
- case 'N': case 'n': case 'A': case 'a':
- enc_name = "ASCII-8BIT";
- break;
- }
- if (enc_name) {
- opt->src.enc.name = rb_str_new2(enc_name);
- if (!opt->ext.enc.name)
- opt->ext.enc.name = opt->src.enc.name;
- }
- s++;
- }
- goto reswitch;
-
- case 'I':
- forbid_setid("-I");
- if (*++s)
- ruby_incpush_expand(s);
- else if (argc > 1) {
- ruby_incpush_expand(argv[1]);
- argc--, argv++;
- }
- break;
-
- case '0':
- if (envopt) goto noenvopt;
- {
- size_t numlen;
- int v;
- char c;
-
- v = scan_oct(s, 4, &numlen);
- s += numlen;
- if (v > 0377)
- rb_rs = Qnil;
- else if (v == 0 && numlen >= 2) {
- rb_rs = rb_str_new2("");
- }
- else {
- c = v & 0xff;
- rb_rs = rb_str_new(&c, 1);
- }
- }
- goto reswitch;
-
- case '-':
- if (!s[1] || (s[1] == '\r' && !s[2])) {
- argc--, argv++;
- goto switch_end;
- }
- s++;
+ s += numlen;
+ }
+ if (!opt->warning) {
+ switch (v) {
+ case 0:
+ ruby_verbose = Qnil;
+ break;
+ case 1:
+ ruby_verbose = Qfalse;
+ break;
+ default:
+ ruby_verbose = Qtrue;
+ break;
+ }
+ }
+ warning = 1;
+ switch (v) {
+ case 0:
+ FEATURE_SET_TO(opt->warn, RB_WARN_CATEGORY_ALL_BITS, 0);
+ break;
+ case 1:
+ FEATURE_SET_TO(opt->warn, 1U << RB_WARN_CATEGORY_DEPRECATED, 0);
+ break;
+ default:
+ FEATURE_SET(opt->warn, RB_WARN_CATEGORY_ALL_BITS);
+ break;
+ }
+ }
+ goto reswitch;
+
+ case 'c':
+ if (envopt) goto noenvopt;
+ opt->dump |= DUMP_BIT(syntax);
+ s++;
+ goto reswitch;
+
+ case 's':
+ if (envopt) goto noenvopt;
+ forbid_setid("-s");
+ if (!opt->sflag) opt->sflag = 1;
+ s++;
+ goto reswitch;
+
+ case 'h':
+ if (envopt) goto noenvopt;
+ opt->dump |= DUMP_BIT(usage);
+ goto switch_end;
+
+ case 'l':
+ if (envopt) goto noenvopt;
+ opt->do_line = TRUE;
+ rb_output_rs = rb_rs;
+ s++;
+ goto reswitch;
+
+ case 'S':
+ if (envopt) goto noenvopt;
+ forbid_setid("-S");
+ opt->do_search = TRUE;
+ s++;
+ goto reswitch;
+
+ case 'e':
+ if (envopt) goto noenvopt;
+ forbid_setid("-e");
+ if (!*++s) {
+ if (!--argc)
+ rb_raise(rb_eRuntimeError, "no code specified for -e");
+ s = *++argv;
+ }
+ if (!opt->e_script) {
+ opt->e_script = rb_str_new(0, 0);
+ if (opt->script == 0)
+ opt->script = "-e";
+ }
+ rb_str_cat2(opt->e_script, s);
+ rb_str_cat2(opt->e_script, "\n");
+ break;
+
+ case 'r':
+ forbid_setid("-r");
+ if (*++s) {
+ add_modules(&opt->req_list, s);
+ }
+ else if (argc > 1) {
+ add_modules(&opt->req_list, argv[1]);
+ argc--, argv++;
+ }
+ break;
+
+ case 'i':
+ if (envopt) goto noenvopt;
+ forbid_setid("-i");
+ ruby_set_inplace_mode(s + 1);
+ break;
+
+ case 'x':
+ if (envopt) goto noenvopt;
+ forbid_setid("-x");
+ opt->xflag = TRUE;
+ s++;
+ if (*s && chdir(s) < 0) {
+ rb_fatal("Can't chdir to %s", s);
+ }
+ break;
+
+ case 'C':
+ case 'X':
+ if (envopt) goto noenvopt;
+ if (!*++s && (!--argc || !(s = *++argv) || !*s)) {
+ rb_fatal("Can't chdir");
+ }
+ if (chdir(s) < 0) {
+ rb_fatal("Can't chdir to %s", s);
+ }
+ break;
+
+ case 'F':
+ if (envopt) goto noenvopt;
+ if (*++s) {
+ rb_fs = rb_reg_new(s, strlen(s), 0);
+ }
+ break;
+
+ case 'E':
+ if (!*++s && (!--argc || !(s = *++argv))) {
+ rb_raise(rb_eRuntimeError, "missing argument for -E");
+ }
+ goto encoding;
+
+ case 'U':
+ set_internal_encoding_once(opt, "UTF-8", 0);
+ ++s;
+ goto reswitch;
+
+ case 'K':
+ if (*++s) {
+ const char *enc_name = 0;
+ switch (*s) {
+ case 'E': case 'e':
+ enc_name = "EUC-JP";
+ break;
+ case 'S': case 's':
+ enc_name = "Windows-31J";
+ break;
+ case 'U': case 'u':
+ enc_name = "UTF-8";
+ break;
+ case 'N': case 'n': case 'A': case 'a':
+ enc_name = "ASCII-8BIT";
+ break;
+ }
+ if (enc_name) {
+ opt->src.enc.name = rb_str_new2(enc_name);
+ if (!opt->ext.enc.name)
+ opt->ext.enc.name = opt->src.enc.name;
+ }
+ s++;
+ }
+ goto reswitch;
+
+ case 'I':
+ forbid_setid("-I");
+ if (*++s)
+ ruby_incpush_expand(s);
+ else if (argc > 1) {
+ ruby_incpush_expand(argv[1]);
+ argc--, argv++;
+ }
+ break;
+
+ case '0':
+ if (envopt) goto noenvopt;
+ {
+ size_t numlen;
+ int v;
+ char c;
+
+ v = scan_oct(s, 4, &numlen);
+ s += numlen;
+ if (v > 0377)
+ rb_rs = Qnil;
+ else if (v == 0 && numlen >= 2) {
+ rb_rs = rb_str_new2("");
+ }
+ else {
+ c = v & 0xff;
+ rb_rs = rb_str_new(&c, 1);
+ }
+ }
+ goto reswitch;
+
+ case '-':
+ if (!s[1] || (s[1] == '\r' && !s[2])) {
+ argc--, argv++;
+ goto switch_end;
+ }
+ s++;
# define is_option_end(c, allow_hyphen) \
- (!(c) || ((allow_hyphen) && (c) == '-') || (c) == '=')
+ (!(c) || ((allow_hyphen) && (c) == '-') || (c) == '=')
# define check_envopt(name, allow_envopt) \
- (((allow_envopt) || !envopt) ? (void)0 : \
- rb_raise(rb_eRuntimeError, "invalid switch in RUBYOPT: --" name))
+ (((allow_envopt) || !envopt) ? (void)0 : \
+ rb_raise(rb_eRuntimeError, "invalid switch in RUBYOPT: --" name))
# define need_argument(name, s, needs_arg, next_arg) \
- ((*(s) ? !*++(s) : (next_arg) && (!argc || !((s) = argv[1]) || (--argc, ++argv, 0))) && (needs_arg) ? \
- rb_raise(rb_eRuntimeError, "missing argument for --" name) \
- : (void)0)
+ ((*(s) ? !*++(s) : (next_arg) && (!argc || !((s) = argv[1]) || (--argc, ++argv, 0))) && (needs_arg) ? \
+ rb_raise(rb_eRuntimeError, "missing argument for --" name) \
+ : (void)0)
# define is_option_with_arg(name, allow_hyphen, allow_envopt) \
- is_option_with_optarg(name, allow_hyphen, allow_envopt, Qtrue, Qtrue)
+ is_option_with_optarg(name, allow_hyphen, allow_envopt, Qtrue, Qtrue)
# define is_option_with_optarg(name, allow_hyphen, allow_envopt, needs_arg, next_arg) \
- (strncmp((name), s, n = sizeof(name) - 1) == 0 && is_option_end(s[n], (allow_hyphen)) && \
+ (strncmp((name), s, n = sizeof(name) - 1) == 0 && is_option_end(s[n], (allow_hyphen)) && \
(s[n] != '-' || s[n+1]) ? \
- (check_envopt(name, (allow_envopt)), s += n, \
- need_argument(name, s, needs_arg, next_arg), 1) : 0)
-
- if (strcmp("copyright", s) == 0) {
- if (envopt) goto noenvopt_long;
- opt->dump |= DUMP_BIT(copyright);
- }
- else if (is_option_with_optarg("debug", Qtrue, Qtrue, Qfalse, Qfalse)) {
- if (s && *s) {
- ruby_each_words(s, debug_option, &opt->features);
- }
- else {
- ruby_debug = Qtrue;
- ruby_verbose = Qtrue;
- }
+ (check_envopt(name, (allow_envopt)), s += n, \
+ need_argument(name, s, needs_arg, next_arg), 1) : 0)
+
+ if (strcmp("copyright", s) == 0) {
+ if (envopt) goto noenvopt_long;
+ opt->dump |= DUMP_BIT(copyright);
}
- else if (is_option_with_arg("enable", Qtrue, Qtrue)) {
- ruby_each_words(s, enable_option, &opt->features);
- }
- else if (is_option_with_arg("disable", Qtrue, Qtrue)) {
- ruby_each_words(s, disable_option, &opt->features);
- }
- else if (is_option_with_arg("encoding", Qfalse, Qtrue)) {
- char *p;
- encoding:
- do {
+ else if (is_option_with_optarg("debug", Qtrue, Qtrue, Qfalse, Qfalse)) {
+ if (s && *s) {
+ ruby_each_words(s, debug_option, &opt->features);
+ }
+ else {
+ ruby_debug = Qtrue;
+ ruby_verbose = Qtrue;
+ }
+ }
+ else if (is_option_with_arg("enable", Qtrue, Qtrue)) {
+ ruby_each_words(s, enable_option, &opt->features);
+ }
+ else if (is_option_with_arg("disable", Qtrue, Qtrue)) {
+ ruby_each_words(s, disable_option, &opt->features);
+ }
+ else if (is_option_with_arg("encoding", Qfalse, Qtrue)) {
+ char *p;
+ encoding:
+ do {
# define set_encoding_part(type) \
- if (!(p = strchr(s, ':'))) { \
- set_##type##_encoding_once(opt, s, 0); \
- break; \
- } \
- else if (p > s) { \
- set_##type##_encoding_once(opt, s, p-s); \
- }
- set_encoding_part(external);
- if (!*(s = ++p)) break;
- set_encoding_part(internal);
- if (!*(s = ++p)) break;
+ if (!(p = strchr(s, ':'))) { \
+ set_##type##_encoding_once(opt, s, 0); \
+ break; \
+ } \
+ else if (p > s) { \
+ set_##type##_encoding_once(opt, s, p-s); \
+ }
+ set_encoding_part(external);
+ if (!*(s = ++p)) break;
+ set_encoding_part(internal);
+ if (!*(s = ++p)) break;
#if defined ALLOW_DEFAULT_SOURCE_ENCODING && ALLOW_DEFAULT_SOURCE_ENCODING
- set_encoding_part(source);
- if (!*(s = ++p)) break;
+ set_encoding_part(source);
+ if (!*(s = ++p)) break;
#endif
- rb_raise(rb_eRuntimeError, "extra argument for %s: %s",
- (arg[1] == '-' ? "--encoding" : "-E"), s);
+ rb_raise(rb_eRuntimeError, "extra argument for %s: %s",
+ (arg[1] == '-' ? "--encoding" : "-E"), s);
# undef set_encoding_part
- } while (0);
- }
- else if (is_option_with_arg("internal-encoding", Qfalse, Qtrue)) {
- set_internal_encoding_once(opt, s, 0);
- }
- else if (is_option_with_arg("external-encoding", Qfalse, Qtrue)) {
- set_external_encoding_once(opt, s, 0);
- }
+ } while (0);
+ }
+ else if (is_option_with_arg("internal-encoding", Qfalse, Qtrue)) {
+ set_internal_encoding_once(opt, s, 0);
+ }
+ else if (is_option_with_arg("external-encoding", Qfalse, Qtrue)) {
+ set_external_encoding_once(opt, s, 0);
+ }
#if defined ALLOW_DEFAULT_SOURCE_ENCODING && ALLOW_DEFAULT_SOURCE_ENCODING
- else if (is_option_with_arg("source-encoding", Qfalse, Qtrue)) {
- set_source_encoding_once(opt, s, 0);
- }
-#endif
- else if (strcmp("version", s) == 0) {
- if (envopt) goto noenvopt_long;
- opt->dump |= DUMP_BIT(version);
- }
- else if (strcmp("verbose", s) == 0) {
- opt->verbose = 1;
- ruby_verbose = Qtrue;
- }
+ else if (is_option_with_arg("source-encoding", Qfalse, Qtrue)) {
+ set_source_encoding_once(opt, s, 0);
+ }
+#endif
+ else if (strcmp("version", s) == 0) {
+ if (envopt) goto noenvopt_long;
+ opt->dump |= DUMP_BIT(version);
+ }
+ else if (strcmp("verbose", s) == 0) {
+ opt->verbose = 1;
+ ruby_verbose = Qtrue;
+ }
else if (strcmp("jit", s) == 0) {
#if !USE_MJIT
rb_warn("Ruby was built without JIT support");
@@ -1434,66 +1504,67 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
#endif
}
else if (is_option_with_optarg("yjit", '-', true, false, false)) {
-#if YJIT_BUILD
+#if USE_YJIT
FEATURE_SET(opt->features, FEATURE_BIT(yjit));
setup_yjit_options(s);
#else
- rb_warn("Ruby was built without YJIT support");
+ rb_warn("Ruby was built without YJIT support."
+ " You may need to install rustc to build Ruby with YJIT.");
#endif
}
- else if (strcmp("yydebug", s) == 0) {
- if (envopt) goto noenvopt_long;
- opt->dump |= DUMP_BIT(yydebug);
- }
- else if (is_option_with_arg("dump", Qfalse, Qfalse)) {
- ruby_each_words(s, dump_option, &opt->dump);
- }
- else if (strcmp("help", s) == 0) {
- if (envopt) goto noenvopt_long;
- opt->dump |= DUMP_BIT(help);
- goto switch_end;
- }
+ else if (strcmp("yydebug", s) == 0) {
+ if (envopt) goto noenvopt_long;
+ opt->dump |= DUMP_BIT(yydebug);
+ }
+ else if (is_option_with_arg("dump", Qfalse, Qfalse)) {
+ ruby_each_words(s, dump_option, &opt->dump);
+ }
+ else if (strcmp("help", s) == 0) {
+ if (envopt) goto noenvopt_long;
+ opt->dump |= DUMP_BIT(help);
+ goto switch_end;
+ }
else if (is_option_with_arg("backtrace-limit", Qfalse, Qfalse)) {
char *e;
long n = strtol(s, &e, 10);
if (errno == ERANGE || n < 0 || *e) rb_raise(rb_eRuntimeError, "wrong limit for backtrace length");
rb_backtrace_length_limit = n;
}
- else {
- rb_raise(rb_eRuntimeError,
- "invalid option --%s (-h will show valid options)", s);
- }
- break;
-
- case '\r':
- if (!s[1])
- break;
-
- default:
- {
+ else {
+ rb_raise(rb_eRuntimeError,
+ "invalid option --%s (-h will show valid options)", s);
+ }
+ break;
+
+ case '\r':
+ if (!s[1])
+ break;
+
+ default:
+ {
rb_raise(rb_eRuntimeError,
- "invalid option -%c (-h will show valid options)",
+ "invalid option -%c (-h will show valid options)",
(int)(unsigned char)*s);
- }
- goto switch_end;
+ }
+ goto switch_end;
- noenvopt:
- /* "EIdvwWrKU" only */
- rb_raise(rb_eRuntimeError, "invalid switch in RUBYOPT: -%c", *s);
- break;
+ noenvopt:
+ /* "EIdvwWrKU" only */
+ rb_raise(rb_eRuntimeError, "invalid switch in RUBYOPT: -%c", *s);
+ break;
- noenvopt_long:
- rb_raise(rb_eRuntimeError, "invalid switch in RUBYOPT: --%s", s);
- break;
+ noenvopt_long:
+ rb_raise(rb_eRuntimeError, "invalid switch in RUBYOPT: --%s", s);
+ break;
- case 0:
- break;
+ case 0:
+ break;
# undef is_option_end
# undef check_envopt
# undef need_argument
# undef is_option_with_arg
# undef is_option_with_optarg
- }
+ }
}
switch_end:
@@ -1517,7 +1588,8 @@ void rb_call_builtin_inits(void);
#if RBIMPL_HAS_ATTRIBUTE(weak)
__attribute__((weak))
#endif
-void Init_extra_exts(void)
+void
+Init_extra_exts(void)
{
}
@@ -1534,15 +1606,39 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
if (opt->features.set & FEATURE_BIT(did_you_mean)) {
rb_define_module("DidYouMean");
}
+ if (opt->features.set & FEATURE_BIT(syntax_suggest)) {
+ rb_define_module("SyntaxSuggest");
+ }
}
rb_warning_category_update(opt->warn.mask, opt->warn.set);
+#if USE_MJIT
+ // rb_call_builtin_inits depends on RubyVM::MJIT.enabled?
+ if (opt->mjit.on)
+ mjit_enabled = true;
+#endif
+
Init_ext(); /* load statically linked extensions before rubygems */
Init_extra_exts();
rb_call_builtin_inits();
ruby_init_prelude();
+ // Initialize JITs after prelude because JITing prelude is typically not optimal.
+#if USE_MJIT
+ // Also, mjit_init is safe only after rb_call_builtin_inits() defines RubyVM::MJIT::Compiler.
+ if (opt->mjit.on)
+ mjit_init(&opt->mjit);
+#endif
+#if USE_YJIT
+ if (opt->yjit)
+ rb_yjit_init();
+#endif
+ // rb_threadptr_root_fiber_setup for the initial thread is called before rb_yjit_enabled_p()
+ // or mjit_enabled becomes true, meaning jit_cont_new is skipped for the initial root fiber.
+ // Therefore we need to call this again here to set the initial root fiber's jit_cont.
+ rb_jit_cont_init(); // must be after mjit_enabled = true and rb_yjit_init()
+
ruby_set_script_name(opt->script_name);
require_libraries(&opt->req_list);
}
@@ -1554,10 +1650,10 @@ opt_enc_index(VALUE enc_name)
int i = rb_enc_find_index(s);
if (i < 0) {
- rb_raise(rb_eRuntimeError, "unknown encoding name - %s", s);
+ rb_raise(rb_eRuntimeError, "unknown encoding name - %s", s);
}
else if (rb_enc_dummy_p(rb_enc_from_index(i))) {
- rb_raise(rb_eRuntimeError, "dummy encoding is not acceptable - %s ", s);
+ rb_raise(rb_eRuntimeError, "dummy encoding is not acceptable - %s ", s);
}
return i;
}
@@ -1589,8 +1685,8 @@ uscore_get(void)
line = rb_lastline_get();
if (!RB_TYPE_P(line, T_STRING)) {
- rb_raise(rb_eTypeError, "$_ value need to be String (%s given)",
- NIL_P(line) ? "nil" : rb_obj_classname(line));
+ rb_raise(rb_eTypeError, "$_ value need to be String (%s given)",
+ NIL_P(line) ? "nil" : rb_obj_classname(line));
}
return line;
}
@@ -1704,6 +1800,26 @@ copy_str(VALUE str, rb_encoding *enc, bool intern)
return rb_enc_interned_str(RSTRING_PTR(str), RSTRING_LEN(str), enc);
}
+#if USE_YJIT
+// Check that an environment variable is set to a truthy value
+static bool
+env_var_truthy(const char *name)
+{
+ const char *value = getenv(name);
+
+ if (!value)
+ return false;
+ if (strcmp(value, "1") == 0)
+ return true;
+ if (strcmp(value, "true") == 0)
+ return true;
+ if (strcmp(value, "yes") == 0)
+ return true;
+
+ return false;
+}
+#endif
+
static VALUE
process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
{
@@ -1725,10 +1841,10 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
if (opt->dump & (DUMP_BIT(usage)|DUMP_BIT(help))) {
int tty = isatty(1);
- const char *const progname =
- (argc > 0 && argv && argv[0] ? argv[0] :
- origarg.argc > 0 && origarg.argv && origarg.argv[0] ? origarg.argv[0] :
- ruby_engine);
+ const char *const progname =
+ (argc > 0 && argv && argv[0] ? argv[0] :
+ origarg.argc > 0 && origarg.argv && origarg.argv[0] ? origarg.argv[0] :
+ ruby_engine);
int columns = 0;
if ((opt->dump & DUMP_BIT(help)) && tty) {
const char *pager_env = getenv("RUBY_PAGER");
@@ -1779,28 +1895,28 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
#endif
}
}
- usage(progname, (opt->dump & DUMP_BIT(help)), tty, columns);
- return Qtrue;
+ usage(progname, (opt->dump & DUMP_BIT(help)), tty, columns);
+ return Qtrue;
}
argc -= i;
argv += i;
if (FEATURE_SET_P(opt->features, rubyopt) && (s = getenv("RUBYOPT"))) {
- VALUE src_enc_name = opt->src.enc.name;
- VALUE ext_enc_name = opt->ext.enc.name;
- VALUE int_enc_name = opt->intern.enc.name;
+ VALUE src_enc_name = opt->src.enc.name;
+ VALUE ext_enc_name = opt->ext.enc.name;
+ VALUE int_enc_name = opt->intern.enc.name;
ruby_features_t feat = opt->features;
ruby_features_t warn = opt->warn;
- opt->src.enc.name = opt->ext.enc.name = opt->intern.enc.name = 0;
- moreswitches(s, opt, 1);
- if (src_enc_name)
- opt->src.enc.name = src_enc_name;
- if (ext_enc_name)
- opt->ext.enc.name = ext_enc_name;
- if (int_enc_name)
- opt->intern.enc.name = int_enc_name;
+ opt->src.enc.name = opt->ext.enc.name = opt->intern.enc.name = 0;
+ moreswitches(s, opt, 1);
+ if (src_enc_name)
+ opt->src.enc.name = src_enc_name;
+ if (ext_enc_name)
+ opt->ext.enc.name = ext_enc_name;
+ if (int_enc_name)
+ opt->intern.enc.name = int_enc_name;
FEATURE_SET_RESTORE(opt->features, feat);
FEATURE_SET_RESTORE(opt->warn, warn);
}
@@ -1812,8 +1928,8 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
rb_warning("-K is specified; it is for 1.8 compatibility and may cause odd behavior");
if (!(FEATURE_SET_BITS(opt->features) & feature_jit_mask)) {
-#if YJIT_BUILD
- if (!FEATURE_USED_P(opt->features, yjit) && getenv("RUBY_YJIT_ENABLE")) {
+#if USE_YJIT
+ if (!FEATURE_USED_P(opt->features, yjit) && env_var_truthy("RUBY_YJIT_ENABLE")) {
FEATURE_SET(opt->features, FEATURE_BIT(yjit));
}
#endif
@@ -1825,57 +1941,54 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
#if USE_MJIT
if (FEATURE_SET_P(opt->features, mjit)) {
- opt->mjit.on = TRUE; /* set mjit.on for ruby_show_version() API and check to call mjit_init() */
+ opt->mjit.on = true; // set opt->mjit.on for Init_ruby_description() and calling mjit_init()
}
#endif
-#if YJIT_BUILD
+#if USE_YJIT
if (FEATURE_SET_P(opt->features, yjit)) {
- rb_yjit_init();
+ opt->yjit = true; // set opt->yjit for Init_ruby_description() and calling rb_yjit_init()
}
#endif
-#if USE_MJIT
- mjit_opts.on = opt->mjit.on; /* used by Init_ruby_description(). mjit_init() still can't be called here. */
-#endif
- Init_ruby_description();
+ Init_ruby_description(opt);
if (opt->dump & (DUMP_BIT(version) | DUMP_BIT(version_v))) {
- ruby_show_version();
- if (opt->dump & DUMP_BIT(version)) return Qtrue;
+ ruby_show_version();
+ if (opt->dump & DUMP_BIT(version)) return Qtrue;
}
if (opt->dump & DUMP_BIT(copyright)) {
- ruby_show_copyright();
- return Qtrue;
+ ruby_show_copyright();
+ return Qtrue;
}
if (!opt->e_script) {
- if (argc <= 0) { /* no more args */
- if (opt->verbose)
- return Qtrue;
- opt->script = "-";
- }
- else {
- opt->script = argv[0];
- if (!opt->script || opt->script[0] == '\0') {
- opt->script = "-";
- }
- else if (opt->do_search) {
- const char *path = getenv("RUBYPATH");
-
- opt->script = 0;
- if (path) {
- opt->script = dln_find_file_r(argv[0], path, fbuf, sizeof(fbuf));
- }
- if (!opt->script) {
- opt->script = dln_find_file_r(argv[0], getenv(PATH_ENV), fbuf, sizeof(fbuf));
- }
- if (!opt->script)
- opt->script = argv[0];
- }
- argc--;
- argv++;
- }
- if (opt->script[0] == '-' && !opt->script[1]) {
- forbid_setid("program input from stdin");
- }
+ if (argc <= 0) { /* no more args */
+ if (opt->verbose)
+ return Qtrue;
+ opt->script = "-";
+ }
+ else {
+ opt->script = argv[0];
+ if (!opt->script || opt->script[0] == '\0') {
+ opt->script = "-";
+ }
+ else if (opt->do_search) {
+ const char *path = getenv("RUBYPATH");
+
+ opt->script = 0;
+ if (path) {
+ opt->script = dln_find_file_r(argv[0], path, fbuf, sizeof(fbuf));
+ }
+ if (!opt->script) {
+ opt->script = dln_find_file_r(argv[0], getenv(PATH_ENV), fbuf, sizeof(fbuf));
+ }
+ if (!opt->script)
+ opt->script = argv[0];
+ }
+ argc--;
+ argv++;
+ }
+ if (opt->script[0] == '-' && !opt->script[1]) {
+ forbid_setid("program input from stdin");
+ }
}
opt->script_name = rb_str_new_cstr(opt->script);
@@ -1890,78 +2003,75 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
ruby_gc_set_params();
ruby_init_loadpath();
-#if USE_MJIT
- if (opt->mjit.on)
- /* Using TMP_RUBY_PREFIX created by ruby_init_loadpath(). */
- mjit_init(&opt->mjit);
-#endif
-
Init_enc();
lenc = rb_locale_encoding();
rb_enc_associate(rb_progname, lenc);
rb_obj_freeze(rb_progname);
parser = rb_parser_new();
if (opt->dump & DUMP_BIT(yydebug)) {
- rb_parser_set_yydebug(parser, Qtrue);
+ rb_parser_set_yydebug(parser, Qtrue);
+ }
+ if (opt->dump & DUMP_BIT(error_tolerant)) {
+ rb_parser_error_tolerant(parser);
}
if (opt->ext.enc.name != 0) {
- opt->ext.enc.index = opt_enc_index(opt->ext.enc.name);
+ opt->ext.enc.index = opt_enc_index(opt->ext.enc.name);
}
if (opt->intern.enc.name != 0) {
- opt->intern.enc.index = opt_enc_index(opt->intern.enc.name);
+ opt->intern.enc.index = opt_enc_index(opt->intern.enc.name);
}
if (opt->src.enc.name != 0) {
- opt->src.enc.index = opt_enc_index(opt->src.enc.name);
- src_encoding_index = opt->src.enc.index;
+ opt->src.enc.index = opt_enc_index(opt->src.enc.name);
+ src_encoding_index = opt->src.enc.index;
}
if (opt->ext.enc.index >= 0) {
- enc = rb_enc_from_index(opt->ext.enc.index);
+ enc = rb_enc_from_index(opt->ext.enc.index);
}
else {
- enc = IF_UTF8_PATH(uenc, lenc);
+ enc = IF_UTF8_PATH(uenc, lenc);
}
rb_enc_set_default_external(rb_enc_from_encoding(enc));
if (opt->intern.enc.index >= 0) {
- enc = rb_enc_from_index(opt->intern.enc.index);
- rb_enc_set_default_internal(rb_enc_from_encoding(enc));
- opt->intern.enc.index = -1;
+ enc = rb_enc_from_index(opt->intern.enc.index);
+ rb_enc_set_default_internal(rb_enc_from_encoding(enc));
+ opt->intern.enc.index = -1;
#if UTF8_PATH
- ienc = enc;
+ ienc = enc;
#endif
}
script_name = opt->script_name;
rb_enc_associate(opt->script_name, IF_UTF8_PATH(uenc, lenc));
#if UTF8_PATH
if (uenc != lenc) {
- opt->script_name = str_conv_enc(opt->script_name, uenc, lenc);
- opt->script = RSTRING_PTR(opt->script_name);
+ opt->script_name = str_conv_enc(opt->script_name, uenc, lenc);
+ opt->script = RSTRING_PTR(opt->script_name);
}
#endif
rb_obj_freeze(opt->script_name);
if (IF_UTF8_PATH(uenc != lenc, 1)) {
- long i;
+ long i;
VALUE load_path = vm->load_path;
- const ID id_initial_load_path_mark = INITIAL_LOAD_PATH_MARK;
+ const ID id_initial_load_path_mark = INITIAL_LOAD_PATH_MARK;
int modifiable = FALSE;
rb_get_expanded_load_path();
- for (i = 0; i < RARRAY_LEN(load_path); ++i) {
- VALUE path = RARRAY_AREF(load_path, i);
- int mark = rb_attr_get(path, id_initial_load_path_mark) == path;
+ for (i = 0; i < RARRAY_LEN(load_path); ++i) {
+ VALUE path = RARRAY_AREF(load_path, i);
+ int mark = rb_attr_get(path, id_initial_load_path_mark) == path;
#if UTF8_PATH
- VALUE newpath = rb_str_conv_enc(path, uenc, lenc);
- if (newpath == path) continue;
- path = newpath;
+ VALUE newpath = rb_str_conv_enc(path, uenc, lenc);
+ if (newpath == path) continue;
+ path = newpath;
#else
- if (!(path = copy_str(path, lenc, !mark))) continue;
+ if (!(path = copy_str(path, lenc, !mark))) continue;
#endif
- if (mark) rb_ivar_set(path, id_initial_load_path_mark, path);
+ if (mark) rb_ivar_set(path, id_initial_load_path_mark, path);
if (!modifiable) {
rb_ary_modify(load_path);
modifiable = TRUE;
}
- RARRAY_ASET(load_path, i, path);
- }
+ RARRAY_ASET(load_path, i, path);
+ }
if (modifiable) {
rb_ary_replace(vm->load_path_snapshot, load_path);
}
@@ -1970,13 +2080,13 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
VALUE loaded_features = vm->loaded_features;
bool modified = false;
for (long i = loaded_before_enc; i < RARRAY_LEN(loaded_features); ++i) {
- VALUE path = RARRAY_AREF(loaded_features, i);
+ VALUE path = RARRAY_AREF(loaded_features, i);
if (!(path = copy_str(path, IF_UTF8_PATH(uenc, lenc), true))) continue;
if (!modified) {
rb_ary_modify(loaded_features);
modified = true;
}
- RARRAY_ASET(loaded_features, i, path);
+ RARRAY_ASET(loaded_features, i, path);
}
if (modified) {
rb_ary_replace(vm->loaded_features_snapshot, loaded_features);
@@ -1984,130 +2094,131 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
}
if (opt->features.mask & COMPILATION_FEATURES) {
- VALUE option = rb_hash_new();
+ VALUE option = rb_hash_new();
#define SET_COMPILE_OPTION(h, o, name) \
- rb_hash_aset((h), ID2SYM(rb_intern_const(#name)), \
+ rb_hash_aset((h), ID2SYM(rb_intern_const(#name)), \
RBOOL(FEATURE_SET_P(o->features, name)))
- SET_COMPILE_OPTION(option, opt, frozen_string_literal);
- SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
- rb_funcallv(rb_cISeq, rb_intern_const("compile_option="), 1, &option);
+ SET_COMPILE_OPTION(option, opt, frozen_string_literal);
+ SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
+ rb_funcallv(rb_cISeq, rb_intern_const("compile_option="), 1, &option);
#undef SET_COMPILE_OPTION
}
ruby_set_argv(argc, argv);
process_sflag(&opt->sflag);
- rb_parser_set_context(parser, 0, TRUE);
-
if (opt->e_script) {
- VALUE progname = rb_progname;
- rb_encoding *eenc;
- if (opt->src.enc.index >= 0) {
- eenc = rb_enc_from_index(opt->src.enc.index);
- }
- else {
- eenc = lenc;
+ VALUE progname = rb_progname;
+ rb_encoding *eenc;
+ rb_parser_set_context(parser, 0, TRUE);
+
+ if (opt->src.enc.index >= 0) {
+ eenc = rb_enc_from_index(opt->src.enc.index);
+ }
+ else {
+ eenc = lenc;
#if UTF8_PATH
- if (ienc) eenc = ienc;
+ if (ienc) eenc = ienc;
#endif
- }
+ }
#if UTF8_PATH
- if (eenc != uenc) {
- opt->e_script = str_conv_enc(opt->e_script, uenc, eenc);
- }
+ if (eenc != uenc) {
+ opt->e_script = str_conv_enc(opt->e_script, uenc, eenc);
+ }
#endif
- rb_enc_associate(opt->e_script, eenc);
+ rb_enc_associate(opt->e_script, eenc);
ruby_opt_init(opt);
ruby_set_script_name(progname);
- rb_parser_set_options(parser, opt->do_print, opt->do_loop,
- opt->do_line, opt->do_split);
- ast = rb_parser_compile_string(parser, opt->script, opt->e_script, 1);
+ rb_parser_set_options(parser, opt->do_print, opt->do_loop,
+ opt->do_line, opt->do_split);
+ ast = rb_parser_compile_string(parser, opt->script, opt->e_script, 1);
}
else {
- VALUE f;
- f = open_load_file(script_name, &opt->xflag);
- ast = load_file(parser, opt->script_name, f, 1, opt);
+ VALUE f;
+ f = open_load_file(script_name, &opt->xflag);
+ rb_parser_set_context(parser, 0, f == rb_stdin);
+ ast = load_file(parser, opt->script_name, f, 1, opt);
}
ruby_set_script_name(opt->script_name);
if (dump & DUMP_BIT(yydebug)) {
- dump &= ~DUMP_BIT(yydebug);
- if (!dump) return Qtrue;
+ dump &= ~DUMP_BIT(yydebug);
+ if (!dump) return Qtrue;
}
if (opt->ext.enc.index >= 0) {
- enc = rb_enc_from_index(opt->ext.enc.index);
+ enc = rb_enc_from_index(opt->ext.enc.index);
}
else {
- enc = IF_UTF8_PATH(uenc, lenc);
+ enc = IF_UTF8_PATH(uenc, lenc);
}
rb_enc_set_default_external(rb_enc_from_encoding(enc));
if (opt->intern.enc.index >= 0) {
- /* Set in the shebang line */
- enc = rb_enc_from_index(opt->intern.enc.index);
- rb_enc_set_default_internal(rb_enc_from_encoding(enc));
+ /* Set in the shebang line */
+ enc = rb_enc_from_index(opt->intern.enc.index);
+ rb_enc_set_default_internal(rb_enc_from_encoding(enc));
}
else if (!rb_default_internal_encoding())
- /* Freeze default_internal */
- rb_enc_set_default_internal(Qnil);
+ /* Freeze default_internal */
+ rb_enc_set_default_internal(Qnil);
rb_stdio_set_default_encoding();
if (!ast->body.root) {
- rb_ast_dispose(ast);
- return Qfalse;
+ rb_ast_dispose(ast);
+ return Qfalse;
}
process_sflag(&opt->sflag);
opt->xflag = 0;
if (dump & DUMP_BIT(syntax)) {
- printf("Syntax OK\n");
- dump &= ~DUMP_BIT(syntax);
- if (!dump) return Qtrue;
+ printf("Syntax OK\n");
+ dump &= ~DUMP_BIT(syntax);
+ if (!dump) return Qtrue;
}
if (opt->do_loop) {
- rb_define_global_function("sub", rb_f_sub, -1);
- rb_define_global_function("gsub", rb_f_gsub, -1);
- rb_define_global_function("chop", rb_f_chop, 0);
- rb_define_global_function("chomp", rb_f_chomp, -1);
+ rb_define_global_function("sub", rb_f_sub, -1);
+ rb_define_global_function("gsub", rb_f_gsub, -1);
+ rb_define_global_function("chop", rb_f_chop, 0);
+ rb_define_global_function("chomp", rb_f_chomp, -1);
}
if (dump & (DUMP_BIT(parsetree)|DUMP_BIT(parsetree_with_comment))) {
- rb_io_write(rb_stdout, rb_parser_dump_tree(ast->body.root, dump & DUMP_BIT(parsetree_with_comment)));
- rb_io_flush(rb_stdout);
- dump &= ~DUMP_BIT(parsetree)&~DUMP_BIT(parsetree_with_comment);
- if (!dump) {
- rb_ast_dispose(ast);
- return Qtrue;
- }
+ rb_io_write(rb_stdout, rb_parser_dump_tree(ast->body.root, dump & DUMP_BIT(parsetree_with_comment)));
+ rb_io_flush(rb_stdout);
+ dump &= ~DUMP_BIT(parsetree)&~DUMP_BIT(parsetree_with_comment);
+ if (!dump) {
+ rb_ast_dispose(ast);
+ return Qtrue;
+ }
}
{
- VALUE path = Qnil;
- if (!opt->e_script && strcmp(opt->script, "-")) {
- path = rb_realpath_internal(Qnil, script_name, 1);
+ VALUE path = Qnil;
+ if (!opt->e_script && strcmp(opt->script, "-")) {
+ path = rb_realpath_internal(Qnil, script_name, 1);
#if UTF8_PATH
- if (uenc != lenc) {
- path = str_conv_enc(path, uenc, lenc);
- }
+ if (uenc != lenc) {
+ path = str_conv_enc(path, uenc, lenc);
+ }
#endif
- if (!ENCODING_GET(path)) { /* ASCII-8BIT */
- rb_enc_copy(path, opt->script_name);
- }
- }
+ if (!ENCODING_GET(path)) { /* ASCII-8BIT */
+ rb_enc_copy(path, opt->script_name);
+ }
+ }
rb_binding_t *toplevel_binding;
GetBindingPtr(rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING")),
toplevel_binding);
const struct rb_block *base_block = toplevel_context(toplevel_binding);
- iseq = rb_iseq_new_main(&ast->body, opt->script_name, path, vm_block_iseq(base_block), !(dump & DUMP_BIT(insns_without_opt)));
- rb_ast_dispose(ast);
+ iseq = rb_iseq_new_main(&ast->body, opt->script_name, path, vm_block_iseq(base_block), !(dump & DUMP_BIT(insns_without_opt)));
+ rb_ast_dispose(ast);
}
if (dump & (DUMP_BIT(insns) | DUMP_BIT(insns_without_opt))) {
- rb_io_write(rb_stdout, rb_iseq_disasm((const rb_iseq_t *)iseq));
- rb_io_flush(rb_stdout);
- dump &= ~DUMP_BIT(insns);
- if (!dump) return Qtrue;
+ rb_io_write(rb_stdout, rb_iseq_disasm((const rb_iseq_t *)iseq));
+ rb_io_flush(rb_stdout);
+ dump &= ~DUMP_BIT(insns);
+ if (!dump) return Qtrue;
}
if (opt->dump & dump_exit_bits) return Qtrue;
@@ -2144,7 +2255,7 @@ static void
warn_cr_in_shebang(const char *str, long len)
{
if (str[len-1] == '\n' && str[len-2] == '\r') {
- rb_warn("shebang line ending with \\r may cause problems");
+ rb_warn("shebang line ending with \\r may cause problems");
}
}
#else
@@ -2177,115 +2288,115 @@ load_file_internal(VALUE argp_v)
CONST_ID(set_encoding, "set_encoding");
if (script) {
- VALUE c = 1; /* something not nil */
- VALUE line;
- char *p, *str;
- long len;
- int no_src_enc = !opt->src.enc.name;
- int no_ext_enc = !opt->ext.enc.name;
- int no_int_enc = !opt->intern.enc.name;
-
- enc = rb_ascii8bit_encoding();
- rb_funcall(f, set_encoding, 1, rb_enc_from_encoding(enc));
-
- if (opt->xflag) {
- line_start--;
- search_shebang:
- while (!NIL_P(line = rb_io_gets(f))) {
- line_start++;
- RSTRING_GETMEM(line, str, len);
- if (len > 2 && str[0] == '#' && str[1] == '!') {
- if (line_start == 1) warn_cr_in_shebang(str, len);
- if ((p = strstr(str+2, ruby_engine)) != 0) {
- goto start_read;
- }
- }
- }
- rb_loaderror("no Ruby script found in input");
- }
-
- c = rb_io_getbyte(f);
- if (c == INT2FIX('#')) {
- c = rb_io_getbyte(f);
+ VALUE c = 1; /* something not nil */
+ VALUE line;
+ char *p, *str;
+ long len;
+ int no_src_enc = !opt->src.enc.name;
+ int no_ext_enc = !opt->ext.enc.name;
+ int no_int_enc = !opt->intern.enc.name;
+
+ enc = rb_ascii8bit_encoding();
+ rb_funcall(f, set_encoding, 1, rb_enc_from_encoding(enc));
+
+ if (opt->xflag) {
+ line_start--;
+ search_shebang:
+ while (!NIL_P(line = rb_io_gets(f))) {
+ line_start++;
+ RSTRING_GETMEM(line, str, len);
+ if (len > 2 && str[0] == '#' && str[1] == '!') {
+ if (line_start == 1) warn_cr_in_shebang(str, len);
+ if ((p = strstr(str+2, ruby_engine)) != 0) {
+ goto start_read;
+ }
+ }
+ }
+ rb_loaderror("no Ruby script found in input");
+ }
+
+ c = rb_io_getbyte(f);
+ if (c == INT2FIX('#')) {
+ c = rb_io_getbyte(f);
if (c == INT2FIX('!') && !NIL_P(line = rb_io_gets(f))) {
- RSTRING_GETMEM(line, str, len);
- warn_cr_in_shebang(str, len);
- if ((p = strstr(str, ruby_engine)) == 0) {
- /* not ruby script, assume -x flag */
- goto search_shebang;
- }
-
- start_read:
- str += len - 1;
- if (*str == '\n') *str-- = '\0';
- if (*str == '\r') *str-- = '\0';
- /* ruby_engine should not contain a space */
- if ((p = strstr(p, " -")) != 0) {
- opt->warning = 0;
- moreswitches(p + 1, opt, 0);
- }
-
- /* push back shebang for pragma may exist in next line */
- rb_io_ungetbyte(f, rb_str_new2("!\n"));
- }
- else if (!NIL_P(c)) {
- rb_io_ungetbyte(f, c);
- }
- rb_io_ungetbyte(f, INT2FIX('#'));
- if (no_src_enc && opt->src.enc.name) {
- opt->src.enc.index = opt_enc_index(opt->src.enc.name);
- src_encoding_index = opt->src.enc.index;
- }
- if (no_ext_enc && opt->ext.enc.name) {
- opt->ext.enc.index = opt_enc_index(opt->ext.enc.name);
- }
- if (no_int_enc && opt->intern.enc.name) {
- opt->intern.enc.index = opt_enc_index(opt->intern.enc.name);
- }
- }
- else if (!NIL_P(c)) {
- rb_io_ungetbyte(f, c);
- }
+ RSTRING_GETMEM(line, str, len);
+ warn_cr_in_shebang(str, len);
+ if ((p = strstr(str, ruby_engine)) == 0) {
+ /* not ruby script, assume -x flag */
+ goto search_shebang;
+ }
+
+ start_read:
+ str += len - 1;
+ if (*str == '\n') *str-- = '\0';
+ if (*str == '\r') *str-- = '\0';
+ /* ruby_engine should not contain a space */
+ if ((p = strstr(p, " -")) != 0) {
+ opt->warning = 0;
+ moreswitches(p + 1, opt, 0);
+ }
+
+ /* push back shebang for pragma may exist in next line */
+ rb_io_ungetbyte(f, rb_str_new2("!\n"));
+ }
+ else if (!NIL_P(c)) {
+ rb_io_ungetbyte(f, c);
+ }
+ rb_io_ungetbyte(f, INT2FIX('#'));
+ if (no_src_enc && opt->src.enc.name) {
+ opt->src.enc.index = opt_enc_index(opt->src.enc.name);
+ src_encoding_index = opt->src.enc.index;
+ }
+ if (no_ext_enc && opt->ext.enc.name) {
+ opt->ext.enc.index = opt_enc_index(opt->ext.enc.name);
+ }
+ if (no_int_enc && opt->intern.enc.name) {
+ opt->intern.enc.index = opt_enc_index(opt->intern.enc.name);
+ }
+ }
+ else if (!NIL_P(c)) {
+ rb_io_ungetbyte(f, c);
+ }
if (NIL_P(c)) {
- argp->f = f = Qnil;
- }
+ argp->f = f = Qnil;
+ }
rb_reset_argf_lineno(0);
ruby_opt_init(opt);
}
if (opt->src.enc.index >= 0) {
- enc = rb_enc_from_index(opt->src.enc.index);
+ enc = rb_enc_from_index(opt->src.enc.index);
}
else if (f == rb_stdin) {
- enc = rb_locale_encoding();
+ enc = rb_locale_encoding();
}
else {
- enc = rb_utf8_encoding();
+ enc = rb_utf8_encoding();
}
rb_parser_set_options(parser, opt->do_print, opt->do_loop,
- opt->do_line, opt->do_split);
+ opt->do_line, opt->do_split);
if (NIL_P(f)) {
- f = rb_str_new(0, 0);
- rb_enc_associate(f, enc);
- return (VALUE)rb_parser_compile_string_path(parser, orig_fname, f, line_start);
+ f = rb_str_new(0, 0);
+ rb_enc_associate(f, enc);
+ return (VALUE)rb_parser_compile_string_path(parser, orig_fname, f, line_start);
}
rb_funcall(f, set_encoding, 2, rb_enc_from_encoding(enc), rb_str_new_cstr("-"));
ast = rb_parser_compile_file_path(parser, orig_fname, f, line_start);
rb_funcall(f, set_encoding, 1, rb_parser_encoding(parser));
if (script && rb_parser_end_seen_p(parser)) {
- /*
- * DATA is a File that contains the data section of the executed file.
- * To create a data section use <tt>__END__</tt>:
- *
- * $ cat t.rb
- * puts DATA.gets
- * __END__
- * hello world!
- *
- * $ ruby t.rb
- * hello world!
- */
- rb_define_global_const("DATA", f);
- argp->f = Qnil;
+ /*
+ * DATA is a File that contains the data section of the executed file.
+ * To create a data section use <tt>__END__</tt>:
+ *
+ * $ cat t.rb
+ * puts DATA.gets
+ * __END__
+ * hello world!
+ *
+ * $ ruby t.rb
+ * hello world!
+ */
+ rb_define_global_const("DATA", f);
+ argp->f = Qnil;
}
return (VALUE)ast;
}
@@ -2314,72 +2425,72 @@ static VALUE
open_load_file(VALUE fname_v, int *xflag)
{
const char *fname = (fname_v = rb_str_encode_ospath(fname_v),
- StringValueCStr(fname_v));
+ StringValueCStr(fname_v));
long flen = RSTRING_LEN(fname_v);
VALUE f;
int e;
if (flen == 1 && fname[0] == '-') {
- f = rb_stdin;
+ f = rb_stdin;
}
else {
- int fd;
- /* open(2) may block if fname is point to FIFO and it's empty. Let's
- use O_NONBLOCK. */
- const int MODE_TO_LOAD = O_RDONLY | (
+ int fd;
+ /* open(2) may block if fname is point to FIFO and it's empty. Let's
+ use O_NONBLOCK. */
+ const int MODE_TO_LOAD = O_RDONLY | (
#if defined O_NONBLOCK && HAVE_FCNTL
- /* TODO: fix conflicting O_NONBLOCK in ruby/win32.h */
- !(O_NONBLOCK & O_ACCMODE) ? O_NONBLOCK :
+ /* TODO: fix conflicting O_NONBLOCK in ruby/win32.h */
+ !(O_NONBLOCK & O_ACCMODE) ? O_NONBLOCK :
#endif
#if defined O_NDELAY && HAVE_FCNTL
!(O_NDELAY & O_ACCMODE) ? O_NDELAY :
#endif
0);
- int mode = MODE_TO_LOAD;
+ int mode = MODE_TO_LOAD;
#if defined DOSISH || defined __CYGWIN__
# define isdirsep(x) ((x) == '/' || (x) == '\\')
- {
- static const char exeext[] = ".exe";
- enum {extlen = sizeof(exeext)-1};
- if (flen > extlen && !isdirsep(fname[flen-extlen-1]) &&
- STRNCASECMP(fname+flen-extlen, exeext, extlen) == 0) {
- mode |= O_BINARY;
- *xflag = 1;
- }
- }
-#endif
-
- if ((fd = rb_cloexec_open(fname, mode, 0)) < 0) {
- e = errno;
- if (!rb_gc_for_fd(e)) {
- rb_load_fail(fname_v, strerror(e));
- }
- if ((fd = rb_cloexec_open(fname, mode, 0)) < 0) {
- rb_load_fail(fname_v, strerror(errno));
- }
- }
- rb_update_max_fd(fd);
-
- if (MODE_TO_LOAD != O_RDONLY && (e = disable_nonblock(fd)) != 0) {
- (void)close(fd);
- rb_load_fail(fname_v, strerror(e));
- }
-
- e = ruby_is_fd_loadable(fd);
- if (!e) {
- e = errno;
- (void)close(fd);
- rb_load_fail(fname_v, strerror(e));
- }
-
- f = rb_io_fdopen(fd, mode, fname);
- if (e < 0) {
- /*
- We need to wait if FIFO is empty. It's FIFO's semantics.
- rb_thread_wait_fd() release GVL. So, it's safe.
- */
- rb_io_wait(f, RB_INT2NUM(RUBY_IO_READABLE), Qnil);
- }
+ {
+ static const char exeext[] = ".exe";
+ enum {extlen = sizeof(exeext)-1};
+ if (flen > extlen && !isdirsep(fname[flen-extlen-1]) &&
+ STRNCASECMP(fname+flen-extlen, exeext, extlen) == 0) {
+ mode |= O_BINARY;
+ *xflag = 1;
+ }
+ }
+#endif
+
+ if ((fd = rb_cloexec_open(fname, mode, 0)) < 0) {
+ e = errno;
+ if (!rb_gc_for_fd(e)) {
+ rb_load_fail(fname_v, strerror(e));
+ }
+ if ((fd = rb_cloexec_open(fname, mode, 0)) < 0) {
+ rb_load_fail(fname_v, strerror(errno));
+ }
+ }
+ rb_update_max_fd(fd);
+
+ if (MODE_TO_LOAD != O_RDONLY && (e = disable_nonblock(fd)) != 0) {
+ (void)close(fd);
+ rb_load_fail(fname_v, strerror(e));
+ }
+
+ e = ruby_is_fd_loadable(fd);
+ if (!e) {
+ e = errno;
+ (void)close(fd);
+ rb_load_fail(fname_v, strerror(e));
+ }
+
+ f = rb_io_fdopen(fd, mode, fname);
+ if (e < 0) {
+ /*
+ We need to wait if FIFO is empty. It's FIFO's semantics.
+ rb_thread_wait_fd() release GVL. So, it's safe.
+ */
+ rb_io_wait(f, RB_INT2NUM(RUBY_IO_READABLE), Qnil);
+ }
}
return f;
}
@@ -2391,7 +2502,7 @@ restore_load_file(VALUE arg)
VALUE f = argp->f;
if (!NIL_P(f) && f != rb_stdin) {
- rb_io_close(f);
+ rb_io_close(f);
}
return Qnil;
}
@@ -2406,7 +2517,7 @@ load_file(VALUE parser, VALUE fname, VALUE f, int script, ruby_cmdline_options_t
arg.opt = opt;
arg.f = f;
return (rb_ast_t *)rb_ensure(load_file_internal, (VALUE)&arg,
- restore_load_file, (VALUE)&arg);
+ restore_load_file, (VALUE)&arg);
}
void *
@@ -2484,7 +2595,7 @@ static void
set_arg0(VALUE val, ID id, VALUE *_)
{
if (origarg.argv == 0)
- rb_raise(rb_eRuntimeError, "$0 not initialized");
+ rb_raise(rb_eRuntimeError, "$0 not initialized");
rb_progname = rb_str_new_frozen(ruby_setproctitle(val));
}
@@ -2505,8 +2616,8 @@ void
ruby_script(const char *name)
{
if (name) {
- rb_orig_progname = rb_progname = external_str_new_cstr(name);
- rb_vm_set_progname(rb_progname);
+ rb_orig_progname = rb_progname = external_str_new_cstr(name);
+ rb_vm_set_progname(rb_progname);
}
}
@@ -2562,13 +2673,13 @@ opt_W_getter(ID id, VALUE *dmy)
switch (v) {
case Qnil:
- return INT2FIX(0);
+ return INT2FIX(0);
case Qfalse:
- return INT2FIX(1);
+ return INT2FIX(1);
case Qtrue:
- return INT2FIX(2);
+ return INT2FIX(2);
default:
- return Qnil;
+ return Qnil;
}
}
@@ -2624,10 +2735,10 @@ ruby_set_argv(int argc, char **argv)
rb_ary_clear(av);
for (i = 0; i < argc; i++) {
- VALUE arg = external_str_new_cstr(argv[i]);
+ VALUE arg = external_str_new_cstr(argv[i]);
- OBJ_FREEZE(arg);
- rb_ary_push(av, arg);
+ OBJ_FREEZE(arg);
+ rb_ary_push(av, arg);
}
}
@@ -2639,8 +2750,8 @@ ruby_process_options(int argc, char **argv)
const char *script_name = (argc > 0 && argv[0]) ? argv[0] : ruby_engine;
if (!origarg.argv || origarg.argc <= 0) {
- origarg.argc = argc;
- origarg.argv = argv;
+ origarg.argc = argc;
+ origarg.argv = argv;
}
ruby_script(script_name); /* for the time being */
rb_argv0 = rb_str_new4(rb_progname);
@@ -2691,8 +2802,8 @@ ruby_sysinit(int *argc, char ***argv)
rb_w32_sysinit(argc, argv);
#endif
if (*argc >= 0 && *argv) {
- origarg.argc = *argc;
- origarg.argv = *argv;
+ origarg.argc = *argc;
+ origarg.argv = *argv;
}
fill_standard_fds();
}
diff --git a/rubystub.c b/rubystub.c
index 1b10cdd4c4..75aeca1869 100644
--- a/rubystub.c
+++ b/rubystub.c
@@ -1,4 +1,5 @@
#include "internal.h"
+#include "internal/missing.h"
#if defined HAVE_DLADDR
#include <dlfcn.h>
#endif
@@ -23,23 +24,23 @@ stub_options(int argc, char **argv)
* use argv[0] as is */
#elif defined __linux__
{
- char selfexe[MAXPATHLEN];
- ssize_t len = readlink("/proc/self/exe", selfexe, sizeof(selfexe));
- if (len < 0) {
- perror("readlink(\"/proc/self/exe\")");
- return NULL;
- }
- selfexe[len] = '\0';
- cmd = selfexe;
+ char selfexe[MAXPATHLEN];
+ ssize_t len = readlink("/proc/self/exe", selfexe, sizeof(selfexe));
+ if (len < 0) {
+ perror("readlink(\"/proc/self/exe\")");
+ return NULL;
+ }
+ selfexe[len] = '\0';
+ cmd = selfexe;
}
#elif defined HAVE_DLADDR
{
- Dl_info dli;
- if (!dladdr(stub_options, &dli)) {
- perror("dladdr");
- return NULL;
- }
- cmd = (char *)dli.dli_fname;
+ Dl_info dli;
+ if (!dladdr(stub_options, &dli)) {
+ perror("dladdr");
+ return NULL;
+ }
+ cmd = (char *)dli.dli_fname;
}
#endif
diff --git a/sample/coverage.rb b/sample/coverage.rb
index 8e8d6167e2..42ba89fd50 100644
--- a/sample/coverage.rb
+++ b/sample/coverage.rb
@@ -49,7 +49,7 @@ at_exit do
end
end
- open(cfile, "w") do |out|
+ File.open(cfile, "w") do |out|
covs.zip(sources, pcovs).each_with_index do |(cov, line, pcov), idx|
cov += pcov || 0 if cov
cov = (cov ? (cov == 0 ? "#####" : cov.to_s) : "-").rjust(9)
diff --git a/sample/from.rb b/sample/from.rb
index db1299c869..0e5a08de5f 100644
--- a/sample/from.rb
+++ b/sample/from.rb
@@ -62,7 +62,7 @@ def from_main
if File.exist?(file)
atime = File.atime(file)
mtime = File.mtime(file)
- open(file, "r") do |f|
+ File.open(file, "r") do |f|
until f.eof?
header = {}
f.each_line do |line|
diff --git a/sample/mine.rb b/sample/mine.rb
index a841d1a60a..77e0204bf6 100755
--- a/sample/mine.rb
+++ b/sample/mine.rb
@@ -1,6 +1,8 @@
#! /usr/bin/ruby -Ku
# -*- coding: utf-8 -*-
+require 'io/console'
+
class Board
def clr
print "\e[2J"
@@ -143,8 +145,8 @@ class Board
end
bd=Board.new(10,10,10)
-system("stty raw -echo")
-begin
+
+IO.console.raw do
loop do
case STDIN.getc
when ?n # new game
@@ -170,7 +172,5 @@ begin
bd.reset
end
end
-ensure
- system("stty -raw echo")
end
print "\n"
diff --git a/sample/mpart.rb b/sample/mpart.rb
index a88eba0ef6..eeb895d3de 100644
--- a/sample/mpart.rb
+++ b/sample/mpart.rb
@@ -2,11 +2,29 @@
# split into multi part
# usage: mpart.rb [-nnn] file..
+class MPart < File
+ def self.new(basename, extname, part, parts)
+ super(sprintf("%s.%s%02d", basename, extname, part), "w").
+ begin_mpart(basename, part, parts)
+ end
+
+ def begin_mpart(basename, part, parts)
+ printf("%s part%02d/%02d\n", basename, part, parts)
+ write("BEGIN--cut here--cut here\n")
+ self
+ end
+
+ def close
+ write("END--cut here--cut here\n")
+ super
+ end
+end
+
lines = 1000
if (ARGV[0] =~ /^-(\d+)$/ )
- lines = $1.to_i;
- ARGV.shift;
+ lines = $1.to_i
+ ARGV.shift
end
basename = ARGV[0]
@@ -14,31 +32,23 @@ extname = "part"
part = 1
line = 0
+ofp = nil
fline = 0
-for i in ifp = open(basename)
- fline = fline + 1
-end
-ifp.close
+File.foreach(basename) {fline += 1}
parts = fline / lines + 1
-for i in ifp = open(basename)
+File.foreach(basename) do |i|
if line == 0
- ofp = open(sprintf("%s.%s%02d", basename, extname, part), "w")
- printf(ofp, "%s part%02d/%02d\n", basename, part, parts)
- ofp.write("BEGIN--cut here--cut here\n")
+ ofp = MPart.new(basename, extname, part, parts)
end
ofp.write(i)
- line = line + 1
- if line >= lines and !ifp.eof?
- ofp.write("END--cut here--cut here\n")
+ line += 1
+ if line >= lines
ofp.close
- part = part + 1
+ part += 1
line = 0
end
end
-ofp.write("END--cut here--cut here\n")
ofp.close
-
-ifp.close
diff --git a/sample/trick2018/02-mame/entry.rb b/sample/trick2018/02-mame/entry.rb
index cc4ef9cbc4..ced791aa3d 100644
--- a/sample/trick2018/02-mame/entry.rb
+++ b/sample/trick2018/02-mame/entry.rb
@@ -1,11 +1,11 @@
'';eval(r=%q(->z{r="'';eval(r=\
-%q(#{r}))[%q`#{z}`]";i=-040;30.
+%q(#{r}))[%q`#{z}`]";i=-040;31.
times{|n|(15+n%2*15-n/2).times{
r<<r[i+=(1.-n&2)*(32-n%2*31)]}}
i=r[524,0]=?\0;eval(r[479..-1])
c['"']}))[%q`GFEDCBA"+"[e\"'"'t
kE*;;\";" TRICK2018 ";tb,;{{r
-2E0$ob[us@*0)[90,336])_#i\n}s#i
+2E0$ob[us@*0)[90,336])#_i\n}s#i
0H}>["t]];};o[1,?\n*8];ex"-}eac
1Hl<1[-1]*2*t=n%2];o[14-n,0)mvk
8M$<4,?\n];15.times{|n|;o[35ie2
diff --git a/sample/trick2022/01-tompng/Gemfile b/sample/trick2022/01-tompng/Gemfile
new file mode 100644
index 0000000000..982b9de67f
--- /dev/null
+++ b/sample/trick2022/01-tompng/Gemfile
@@ -0,0 +1,2 @@
+source 'https://rubygems.org'
+gem 'matrix'
diff --git a/sample/trick2022/01-tompng/Gemfile.lock b/sample/trick2022/01-tompng/Gemfile.lock
new file mode 100644
index 0000000000..8bb3c69025
--- /dev/null
+++ b/sample/trick2022/01-tompng/Gemfile.lock
@@ -0,0 +1,13 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ matrix (0.4.2)
+
+PLATFORMS
+ x86_64-darwin-20
+
+DEPENDENCIES
+ matrix
+
+BUNDLED WITH
+ 2.3.3
diff --git a/sample/trick2022/01-tompng/authors.markdown b/sample/trick2022/01-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2022/01-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2022/01-tompng/entry.rb b/sample/trick2022/01-tompng/entry.rb
new file mode 100644
index 0000000000..97beacc684
--- /dev/null
+++ b/sample/trick2022/01-tompng/entry.rb
@@ -0,0 +1,40 @@
+ eval((s=%~c=(0..35
+ ).map{s[2*_1+1]}*'';class$Inte
+ ger;def$quXinclude(Math ;spXo(a)=self*
+ a.pow(87X=h=32.chr;g=PI/480;ls=(sp*31X,89)%89;
+ def$abX+'eval((s=%'+(n=? .next)+s*88.chr+[nXs()=[a
+ =self%X+'.split(',sp*25+'?'+88.chr+');(0..36).mapX89,89-
+ a].miX{s[2*_1].split}',sp*31+".join.tr('$',$/)))"]*$/)Xn;end
+ ;reqX.split$/;trap(:INT){puts;exit};q=->t,i{a,y=((t+i*99)Xuire
+ 'matrX%960). ivmod(80);[(a*(7+i)+i*23)%79+(y+a)/(5+i%4)%2,39Xix';1
+ 5.tiX-y/2]};p=->t,u{a=->b,c{(0..5).sum{(u%2-1)*E**(t*(b+c*_1)*gXmes{
+ |i,*X.i+ i*u+=5+sin(u*u))}};x,z=a[5,3]. 5,3].rect;x+=y.Xv|z=
+ *?!Xi a[19,4];z+=w;r=(4+(x.abs+z.i).ab };t=(0..959).fX..?
+ W,?Xind{|t|(0..29).all?{x,y=q[t,_1];(x 2||h=ls[y][x]X[,*
+ ?]..X[/[^!-}]/]}};h=($**h+h).chr;eval( []} ->(x,yX?};a
+ =(0X,a,b){x=x*36+39.5;y=19.5-y*18;b*=1 |i|((yX..1
+ 34)X-b).ceil..y+b).map{|j|((x-i)/a+(y j)/ .times{X.ma
+ p{zXx,z=p[t,_1];l=u```=0;while``````(l<1)``; u+```=0 ;d=x-y;X.in
+dex(Xl+=(d.abs+(z-w``)``.i).ab``s*1.``1 ;x``,z=y``,w;o[v``=``x.r d.imag/Xc[i+
+15*Xd.abs*l*sin(2*``l-t``*g*80``-_1) l*(``1-l)/``6,a``=l*( -l)**2*0.X_1]
+)};X7,a*2]&&o[v,z,``0.0``3,l**`` ``times``{|i``|(8+i).times{|Xw=*
+MatXj|o[sin(i)/2+````` ```sin( `/2.0`````````)*j/200,j*0.0Xrix
+[*(X5-1,0.02,0.1]``}} ``. q[t,``_1];m``[y][x]= };i=-X0..
+44).X1;$><<(['%%','[H .map{|j|(0..79).map{|k|x=(Xmap{
+ |i,X -39.5)/35.8;y=( i+=1;m[j][k]?h:c[i]):ls[j]X*b|
+ v<<X[k];}*''}*$/<<0) 1)%9 te"`")#qv.jSaL{=;q(Q}4fXa.z
+ ip(Xjs(:#tK`Jm))FKO /A9(2'%iorvf7 eEa0uV xv+Q@qUU](L@&Py .1v'X0..
+ ).suXydSEH{-GI|-5(,z G5evpq,[b50 D[ t {on,I?VStS`?G@LoqFCXm{|j
+ ,k|Xj1.QnxKz!mH%o# )b2Seut,]! 48 lBieJGi 5jeNPD#b}H3X-(p
+ =(iXaVz#8*+US,hgF 5#6]y-` 4hy HN hF75WjD!0IxJ$sX+k)
+ .powX+UP"cNUE9- G< tHvV;Ib <-s U T ? vlE xylg=x#X(i+k
+ ,88)XV9u$9lKb9 @C do7+-w >l { v9 { P l ga%]AK<e&'X+1)*
+ (j||(X4ifK/6S+ k} @@*a} 6rS xn"Q[M 8 `|g>$#BrjXb<<p;
+ 0))}XtbDp'Kc t2 Dat9C s C rL+ g,j]Tf B< eMI+zzkWX;b}]
+ .lup.XtVP<ak IM E/+)B jwv uB (Twqed D* dyf_dT7Xsolve
+ (v);13Xn:8 #_ RiSTO, [Fk m O]O#"+ a_ cT_.X5.time
+ s{c[i+X e5 T`FBEC q*f 2 o@{a<eUG aW PX15*_1]
+ =z[a[_1]X z_@`nll 7F1 2 [=^uS0z^ 6X||w.shif
+ t]}};eval(Xfg K#R N bp-E_Xc)~.split(
+ ?X);(0..36).map{s[2*_1].split}
+ .join.tr('$',$/)))
diff --git a/sample/trick2022/01-tompng/remarks.markdown b/sample/trick2022/01-tompng/remarks.markdown
new file mode 100644
index 0000000000..70601908b7
--- /dev/null
+++ b/sample/trick2022/01-tompng/remarks.markdown
@@ -0,0 +1,51 @@
+### Remarks
+
+Just run it with no argument:
+
+ ruby entry.rb
+
+Or run it with one non-ascii half-width character argument:
+
+ ruby entry.rb ⬮
+ ruby entry.rb 𓆡
+
+I confirmed the following implementations/platforms:
+
+* ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
+* ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin20]
+
+### Description
+
+This program is an aquatic quine.
+Some characters in the code are overwritten with `" "`, but this program can restore the missing parts.
+Every frame of this animation is an executable ruby program that let fishes start swimming again from their current position.
+
+### Internals
+
+#### Error Correction
+
+Error correction is performed for each block of length 135.
+It consists of 89 kinds of characters(`[*('!'..'W'), '[', *(']'..'}')]`) and satisfies the following constraint.
+
+```
+matrix(size: 45x135) * block_vector(size: 135) % 89 == zero_vector(size: 45)
+```
+
+To restore the missing characters in the block, we need to solve a linear equation problem in modulo 89.
+This can be achieved by using bundled gem 'matrix' and overwriting some methods.
+
+```ruby
+require 'matrix'
+matrix = Matrix[[3, 1, 4], [1, 5, 9], [2, 6, 5]]
+class Integer
+ def quo(x) = self * x.pow(87, 89) % 89 # Fermat's little theorem. 89 is a prime number.
+ def abs() = [self % 89, 89 - self % 89].min # To avoid division by multiple of 89.
+end
+answer = matrix.lup.solve([1, 2, 3]) #=> Vector[24, 42, 83]
+(matrix * answer).map { _1 % 89 } #=> Vector[1, 2, 3]
+```
+
+#### Resuming Animation
+
+The entire animation of this fish tank is a loop of 960 frames.
+This program uses position of the floating bubbles to detect current frame number from the executed source code.
diff --git a/sample/trick2022/02-tompng/authors.markdown b/sample/trick2022/02-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2022/02-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2022/02-tompng/entry.rb b/sample/trick2022/02-tompng/entry.rb
new file mode 100644
index 0000000000..2e2e2bcf74
--- /dev/null
+++ b/sample/trick2022/02-tompng/entry.rb
@@ -0,0 +1,32 @@
+ q=->{!sleep _1/1e2};p=(
+ c=0..2).map{[_1/9r ,0,5**_1.i/3,1,0]}
+ require'socket';puts'op' "en http://localhost:#{(
+ w=TCPServer.new$*[0]||0).addr[1]}";Thread.new{q[2];f=[-1
+ ]*s=3;t=Time.now.to_f;p.select!{0<_1[3]=[_1[3]+_1[4]/8.0,1
+ ].min};9.times{h=p.map{[2**(_1*t.i)/_4**0.5/(1+Math.sin(2*t-
+ 9*_1%2)**32/16),_2+_4*( _3-_2)]};r=[s*3/2,84].min;g=->{x,y=
+(s*(1+_1+1i)/2).rect;x<0 ||x>=s-1||y<0||y>=s-1?0:((l=f[y+1])[
+x+1]*(a=x%1)+(1-a)*l[x] )*(b=y%1)+(1-b)*((l=f[y])[x+1]*a+(1-
+a)*l[x])};f=(1..r).map {|y|(1..r).map{|x|z=1.5+1.5i-3.0*(y
+.i+x)/r;[h.sum{g[_1.*z +_2]}*0.9,1].min}};s=r};c=f.flatten
+redo};loop{s=w.accept ; Thread.new{r=s.gets;h='HTTP/1.1 '+
+"200 OK\r\nContent-" 'T' "ype:text/html\r\n\r\n";r['/ ']?s.
+ <<(h+'<style>ifram' 'e{' 'opacity:0;height:0;}input{wid'+
+ 'th:252px;}</styl' 'e>' '<form target="i"><input src="'+
+ "g#{rand}\" type" '="im' 'age"><iframe name="i"></ifra'+
+ 'me></form>'):r ['/g'] ?(h[/:.+l/]=?:'image/gif';s<<
+ h+'GIF8' '7a'+[84,
+ 84,246,0,*(0..383).map {15*_1. /(383r)**(3-_1%
+ 3)*17}].pack('v3c*'); loop{ s<<[67434785,5,
+ 44,84,84,7,c.map{_1* 127} .each_slice(126
+ ).map{[127,128,*_1 ] .pack'c*'}*'',
+ 1,129].pack('V3x' 'v2na*c2x');q[
+ 5];q.[]1while(r ==r=c)}):(x,y,
+ z=r.scan(/\d+/).map{_1.to_f/
+ 126-1};z&&p<<[rand-0.5,(
+ z=x+y.i)*1.5,z/(z.
+ abs+0.9),0,-p[
+ -3][4]=-1]
+ s.<<h);s
+ .close
+ }}
diff --git a/sample/trick2022/02-tompng/remarks.markdown b/sample/trick2022/02-tompng/remarks.markdown
new file mode 100644
index 0000000000..3b2d3fd84b
--- /dev/null
+++ b/sample/trick2022/02-tompng/remarks.markdown
@@ -0,0 +1,32 @@
+### Remarks
+
+1. Run it with `ruby entry.rb 8080`
+
+2. Open "http://localhost:8080"
+
+3. Click on the screen and interact with it
+
+I confirmed the following implementations/platforms:
+
+* Ruby Version
+ * ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin20]
+* Browser
+ * Chrome(macOS, Android)
+ * Firefox(macOS)
+ * Edge(macOS)
+
+### Description
+
+This program is an HTTP server that provides a fractal creature playground.
+You can see the heartbeat of a mysterious fractal creature. Clicking on the screen will change the shape of the creature.
+Surprisingly, this interactive webpage is built without JavaScript.
+
+### Internals
+
+Fractal: Iterated function system
+Rendering from server: Streaming animated GIF
+Sending click event to server: `<input type="image" src="streaming.gif">` with `<form target="invisible_iframe">`
+
+### Limitations
+
+Does not work on Safari and iOS.
diff --git a/sample/trick2022/03-mame/authors.markdown b/sample/trick2022/03-mame/authors.markdown
new file mode 100644
index 0000000000..0e420fdf5d
--- /dev/null
+++ b/sample/trick2022/03-mame/authors.markdown
@@ -0,0 +1,3 @@
+* Yusuke Endoh
+ * mame@ruby-lang.org
+ * cctld: jp
diff --git a/sample/trick2022/03-mame/entry.rb b/sample/trick2022/03-mame/entry.rb
new file mode 100644
index 0000000000..f24595dfa9
--- /dev/null
+++ b/sample/trick2022/03-mame/entry.rb
@@ -0,0 +1,27 @@
+2022;"#
+
+.chars} {puts'TRICK+2022'
+ \ { ;
+#';$><< b
+ ?!
+};#{s=' ' # 0
+! s[0]? ( b=$<.read ;'
+} ub( ,''}
+';a= ''<<32
+b.lines {puts( ?.. *(
+b.size) .gsub(/./) {
+b.sub!( /^#$`\K(\S)
+ /x,a)?$1:a }
+ .rstrip)}):
+ ( [ 12,1,12,11].
+cycle { | i | t = ( s *
+
+10<<
+10)*
+10+ %(\e[A)*
+10
+10. times{t[i*
+ _1 ] = 'TRICK+2022'[
+ _1 ] };$><<t
+ sleep 1})
+ }"
diff --git a/sample/trick2022/03-mame/remarks.markdown b/sample/trick2022/03-mame/remarks.markdown
new file mode 100644
index 0000000000..c38279f016
--- /dev/null
+++ b/sample/trick2022/03-mame/remarks.markdown
@@ -0,0 +1,96 @@
+Execute the program normally.
+
+```
+$ ruby entry.rb
+```
+
+It shakes a string.
+
+... Wait! This is not all.
+
+Next, please apply "leftward gravity" to each letter in the file.
+IOW, if there is a space to the left of a letter, move it to the left.
+Here, you may want to use the following command.
+
+```
+$ sed "s/ //g" entry.rb | tee up.rb
+```
+
+This program applies "upward gravity" to each letter in an input text.
+The following demo will help you understand what this means.
+
+```
+$ cat test.txt
+$ ruby up.rb test.txt
+```
+
+Now, here's where we come in.
+Please apply "upward gravity" to entry.rb.
+
+```
+$ ruby up.rb entry.rb | tee left.rb
+```
+
+I think that you already noticed that.
+This program applies "leftward gravity" to an input text.
+
+```
+$ cat test.txt
+$ ruby left.rb test.txt
+```
+
+`sed` is no longer required to create `up.rb`; just use `left.rb`.
+
+```
+$ ruby left.rb entry.rb > up.rb
+```
+
+We've come to the final stage.
+Please apply `left.rb` to `left.rb`.
+
+```
+$ ruby left.rb left.rb | tee horizontal.rb
+$ ruby horizontal.rb
+```
+
+Of course, it is also possible to apply `up.rb` to `up.rb`.
+
+```
+$ ruby up.rb up.rb | tee vertical.rb
+$ ruby vertical.rb
+```
+
+Can you tell how they work? Enjoy analyzing!
+
+
+
+---
+Code reading tips (spoiler)
+
+Some code fragments are highly reused between the programs.
+For example, note that this program has one code fragment to input a text
+(`b=$>.read`); `up.rb` and `left.rb` share and use this code fragment.
+Also, `horizontal.rb` and `vertical.rb` share the fragment `puts'TRICK+2022'`.
+Sometimes letters in very distant places are reused all over the place.
+
+Can you tell how it detects if it is already aligned or not yet?
+Here is a simplified version of the gimmick to switch behavior when
+left-aligned:
+
+```
+"\ #{puts('not left-aligned yet')}
+ # {puts('left-aligned')}"
+```
+
+And for top-aligned:
+
+```
+"#
+xx{puts('top-aligned')}
+x#{puts('not top-aligned yet')}
+"
+```
+
+It is also necessary to detect "top-left-aligned" and "left-top-aligned".
+I made tons of subtle adjustments and trial-and-error to create the program.
+I no longer know precisely how it works.
diff --git a/sample/trick2022/03-mame/test.txt b/sample/trick2022/03-mame/test.txt
new file mode 100644
index 0000000000..18802153a9
--- /dev/null
+++ b/sample/trick2022/03-mame/test.txt
@@ -0,0 +1,13 @@
+T
+ R
+ I
+ C
+ K
+ |
+ |
+ |
+ |
+ 2
+ 0
+ 2
+ 2
diff --git a/sample/trick2022/README.md b/sample/trick2022/README.md
new file mode 100644
index 0000000000..3b2af6f86b
--- /dev/null
+++ b/sample/trick2022/README.md
@@ -0,0 +1,14 @@
+This directory contains the award-winning entries of
+the 4th Transcendental Ruby Imbroglio Contest for rubyKaigi (TRICK 2022).
+
+THESE ARE BAD EXAMPLES! You must NOT use them as a sample code.
+
+* 01-tompng/entry.rb: "Best fishbowl" -- Tomoya Ishida (tompng)
+* 02-tompng/entry.rb: "Most interactive code" -- Tomoya Ishida (tompng)
+* 03-mame/entry.rb: "Most anti-gravity" -- Yusuke Endoh
+
+These files are licensed under MIT license.
+
+For the contest outline and other winning entries, see:
+
+https://github.com/tric/trick2022
diff --git a/sample/uumerge.rb b/sample/uumerge.rb
index 2576bcb864..1b81582c24 100644
--- a/sample/uumerge.rb
+++ b/sample/uumerge.rb
@@ -15,7 +15,7 @@ while line = gets()
if out_stdout
out = STDOUT
else
- out = open($file, "w") if $file != ""
+ out = File.open($file, "w") if $file != ""
end
out.binmode
break
diff --git a/scheduler.c b/scheduler.c
index 06658356b1..4be18e1799 100644
--- a/scheduler.c
+++ b/scheduler.c
@@ -28,10 +28,63 @@ static ID id_process_wait;
static ID id_io_read, id_io_pread;
static ID id_io_write, id_io_pwrite;
static ID id_io_wait;
+static ID id_io_select;
static ID id_io_close;
static ID id_address_resolve;
+static ID id_fiber_schedule;
+
+/*
+ * Document-class: Fiber::Scheduler
+ *
+ * This is not an existing class, but documentation of the interface that Scheduler
+ * object should comply to in order to be used as argument to Fiber.scheduler and handle non-blocking
+ * fibers. See also the "Non-blocking fibers" section in Fiber class docs for explanations
+ * of some concepts.
+ *
+ * Scheduler's behavior and usage are expected to be as follows:
+ *
+ * * When the execution in the non-blocking Fiber reaches some blocking operation (like
+ * sleep, wait for a process, or a non-ready I/O), it calls some of the scheduler's
+ * hook methods, listed below.
+ * * Scheduler somehow registers what the current fiber is waiting on, and yields control
+ * to other fibers with Fiber.yield (so the fiber would be suspended while expecting its
+ * wait to end, and other fibers in the same thread can perform)
+ * * At the end of the current thread execution, the scheduler's method #scheduler_close is called
+ * * The scheduler runs into a wait loop, checking all the blocked fibers (which it has
+ * registered on hook calls) and resuming them when the awaited resource is ready
+ * (e.g. I/O ready or sleep time elapsed).
+ *
+ * This way concurrent execution will be achieved transparently for every
+ * individual Fiber's code.
+ *
+ * Scheduler implementations are provided by gems, like
+ * Async[https://github.com/socketry/async].
+ *
+ * Hook methods are:
+ *
+ * * #io_wait, #io_read, #io_write, #io_pread, #io_pwrite, and #io_select, #io_close
+ * * #process_wait
+ * * #kernel_sleep
+ * * #timeout_after
+ * * #address_resolve
+ * * #block and #unblock
+ * * (the list is expanded as Ruby developers make more methods having non-blocking calls)
+ *
+ * When not specified otherwise, the hook implementations are mandatory: if they are not
+ * implemented, the methods trying to call hook will fail. To provide backward compatibility,
+ * in the future hooks will be optional (if they are not implemented, due to the scheduler
+ * being created for the older Ruby version, the code which needs this hook will not fail,
+ * and will just behave in a blocking fashion).
+ *
+ * It is also strongly recommended that the scheduler implements the #fiber method, which is
+ * delegated to by Fiber.schedule.
+ *
+ * Sample _toy_ implementation of the scheduler can be found in Ruby's code, in
+ * <tt>test/fiber/scheduler.rb</tt>
+ *
+ */
void
Init_Fiber_Scheduler(void)
{
@@ -51,9 +104,30 @@ Init_Fiber_Scheduler(void)
id_io_pwrite = rb_intern_const("io_pwrite");
id_io_wait = rb_intern_const("io_wait");
+ id_io_select = rb_intern_const("io_select");
id_io_close = rb_intern_const("io_close");
id_address_resolve = rb_intern_const("address_resolve");
+
+ id_fiber_schedule = rb_intern_const("fiber");
+
+#if 0 /* for RDoc */
+ rb_cFiberScheduler = rb_define_class_under(rb_cFiber, "Scheduler", rb_cObject);
+ rb_define_method(rb_cFiberScheduler, "close", rb_fiber_scheduler_close, 0);
+ rb_define_method(rb_cFiberScheduler, "process_wait", rb_fiber_scheduler_process_wait, 2);
+ rb_define_method(rb_cFiberScheduler, "io_wait", rb_fiber_scheduler_io_wait, 3);
+ rb_define_method(rb_cFiberScheduler, "io_read", rb_fiber_scheduler_io_read, 4);
+ rb_define_method(rb_cFiberScheduler, "io_write", rb_fiber_scheduler_io_write, 4);
+ rb_define_method(rb_cFiberScheduler, "io_pread", rb_fiber_scheduler_io_pread, 5);
+ rb_define_method(rb_cFiberScheduler, "io_pwrite", rb_fiber_scheduler_io_pwrite, 5);
+ rb_define_method(rb_cFiberScheduler, "io_select", rb_fiber_scheduler_io_select, 4);
+ rb_define_method(rb_cFiberScheduler, "kernel_sleep", rb_fiber_scheduler_kernel_sleep, 1);
+ rb_define_method(rb_cFiberScheduler, "address_resolve", rb_fiber_scheduler_address_resolve, 1);
+ rb_define_method(rb_cFiberScheduler, "timeout_after", rb_fiber_scheduler_timeout_after, 3);
+ rb_define_method(rb_cFiberScheduler, "block", rb_fiber_scheduler_block, 2);
+ rb_define_method(rb_cFiberScheduler, "unblock", rb_fiber_scheduler_unblock, 2);
+ rb_define_method(rb_cFiberScheduler, "fiber", rb_fiber_scheduler, -2);
+#endif
}
VALUE
@@ -87,6 +161,21 @@ verify_interface(VALUE scheduler)
}
}
+static VALUE
+fiber_scheduler_close(VALUE scheduler)
+{
+ return rb_fiber_scheduler_close(scheduler);
+}
+
+static VALUE
+fiber_scheduler_close_ensure(VALUE _thread)
+{
+ rb_thread_t *thread = (rb_thread_t*)_thread;
+ thread->scheduler = Qnil;
+
+ return Qnil;
+}
+
VALUE
rb_fiber_scheduler_set(VALUE scheduler)
{
@@ -99,9 +188,13 @@ rb_fiber_scheduler_set(VALUE scheduler)
verify_interface(scheduler);
}
- // We invoke Scheduler#close when setting it to something else, to ensure the previous scheduler runs to completion before changing the scheduler. That way, we do not need to consider interactions, e.g., of a Fiber from the previous scheduler with the new scheduler.
+ // We invoke Scheduler#close when setting it to something else, to ensure
+ // the previous scheduler runs to completion before changing the scheduler.
+ // That way, we do not need to consider interactions, e.g., of a Fiber from
+ // the previous scheduler with the new scheduler.
if (thread->scheduler != Qnil) {
- rb_fiber_scheduler_close(thread->scheduler);
+ // rb_fiber_scheduler_close(thread->scheduler);
+ rb_ensure(fiber_scheduler_close, thread->scheduler, fiber_scheduler_close_ensure, (VALUE)thread);
}
thread->scheduler = scheduler;
@@ -133,6 +226,16 @@ VALUE rb_fiber_scheduler_current_for_thread(VALUE thread)
return rb_fiber_scheduler_current_for_threadptr(rb_thread_ptr(thread));
}
+/*
+ *
+ * Document-method: Fiber::Scheduler#close
+ *
+ * Called when the current thread exits. The scheduler is expected to implement this
+ * method in order to allow all waiting fibers to finalize their execution.
+ *
+ * The suggested pattern is to implement the main event loop in the #close method.
+ *
+ */
VALUE
rb_fiber_scheduler_close(VALUE scheduler)
{
@@ -140,11 +243,17 @@ rb_fiber_scheduler_close(VALUE scheduler)
VALUE result;
+ // The reason for calling `scheduler_close` before calling `close` is for
+ // legacy schedulers which implement `close` and expect the user to call
+ // it. Subsequently, that method would call `Fiber.set_scheduler(nil)`
+ // which should call `scheduler_close`. If it were to call `close`, it
+ // would create an infinite loop.
+
result = rb_check_funcall(scheduler, id_scheduler_close, 0, NULL);
- if (result != Qundef) return result;
+ if (!UNDEF_P(result)) return result;
result = rb_check_funcall(scheduler, id_close, 0, NULL);
- if (result != Qundef) return result;
+ if (!UNDEF_P(result)) return result;
return Qnil;
}
@@ -159,6 +268,17 @@ rb_fiber_scheduler_make_timeout(struct timeval *timeout)
return Qnil;
}
+/*
+ * Document-method: Fiber::Scheduler#kernel_sleep
+ * call-seq: kernel_sleep(duration = nil)
+ *
+ * Invoked by Kernel#sleep and Mutex#sleep and is expected to provide
+ * an implementation of sleeping in a non-blocking way. Implementation might
+ * register the current fiber in some list of "which fiber wait until what
+ * moment", call Fiber.yield to pass control, and then in #close resume
+ * the fibers whose wait period has elapsed.
+ *
+ */
VALUE
rb_fiber_scheduler_kernel_sleep(VALUE scheduler, VALUE timeout)
{
@@ -172,6 +292,34 @@ rb_fiber_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE * argv)
}
#if 0
+/*
+ * Document-method: Fiber::Scheduler#timeout_after
+ * call-seq: timeout_after(duration, exception_class, *exception_arguments, &block) -> result of block
+ *
+ * Invoked by Timeout.timeout to execute the given +block+ within the given
+ * +duration+. It can also be invoked directly by the scheduler or user code.
+ *
+ * Attempt to limit the execution time of a given +block+ to the given
+ * +duration+ if possible. When a non-blocking operation causes the +block+'s
+ * execution time to exceed the specified +duration+, that non-blocking
+ * operation should be interrupted by raising the specified +exception_class+
+ * constructed with the given +exception_arguments+.
+ *
+ * General execution timeouts are often considered risky. This implementation
+ * will only interrupt non-blocking operations. This is by design because it's
+ * expected that non-blocking operations can fail for a variety of
+ * unpredictable reasons, so applications should already be robust in handling
+ * these conditions and by implication timeouts.
+ *
+ * However, as a result of this design, if the +block+ does not invoke any
+ * non-blocking operations, it will be impossible to interrupt it. If you
+ * desire to provide predictable points for timeouts, consider adding
+ * +sleep(0)+.
+ *
+ * If the block is executed successfully, its result will be returned.
+ *
+ * The exception will typically be raised using Fiber#raise.
+ */
VALUE
rb_fiber_scheduler_timeout_after(VALUE scheduler, VALUE timeout, VALUE exception, VALUE message)
{
@@ -189,6 +337,24 @@ rb_fiber_scheduler_timeout_afterv(VALUE scheduler, int argc, VALUE * argv)
}
#endif
+/*
+ * Document-method: Fiber::Scheduler#process_wait
+ * call-seq: process_wait(pid, flags)
+ *
+ * Invoked by Process::Status.wait in order to wait for a specified process.
+ * See that method description for arguments description.
+ *
+ * Suggested minimal implementation:
+ *
+ * Thread.new do
+ * Process::Status.wait(pid, flags)
+ * end.value
+ *
+ * This hook is optional: if it is not present in the current scheduler,
+ * Process::Status.wait will behave as a blocking method.
+ *
+ * Expected to return a Process::Status instance.
+ */
VALUE
rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags)
{
@@ -199,20 +365,74 @@ rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags)
return rb_check_funcall(scheduler, id_process_wait, 2, arguments);
}
+/*
+ * Document-method: Fiber::Scheduler#block
+ * call-seq: block(blocker, timeout = nil)
+ *
+ * Invoked by methods like Thread.join, and by Mutex, to signify that current
+ * Fiber is blocked until further notice (e.g. #unblock) or until +timeout+ has
+ * elapsed.
+ *
+ * +blocker+ is what we are waiting on, informational only (for debugging and
+ * logging). There are no guarantee about its value.
+ *
+ * Expected to return boolean, specifying whether the blocking operation was
+ * successful or not.
+ */
VALUE
rb_fiber_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout)
{
return rb_funcall(scheduler, id_block, 2, blocker, timeout);
}
+/*
+ * Document-method: Fiber::Scheduler#unblock
+ * call-seq: unblock(blocker, fiber)
+ *
+ * Invoked to wake up Fiber previously blocked with #block (for example, Mutex#lock
+ * calls #block and Mutex#unlock calls #unblock). The scheduler should use
+ * the +fiber+ parameter to understand which fiber is unblocked.
+ *
+ * +blocker+ is what was awaited for, but it is informational only (for debugging
+ * and logging), and it is not guaranteed to be the same value as the +blocker+ for
+ * #block.
+ *
+ */
VALUE
rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber)
{
VM_ASSERT(rb_obj_is_fiber(fiber));
- return rb_funcall(scheduler, id_unblock, 2, blocker, fiber);
+ // `rb_fiber_scheduler_unblock` can be called from points where `errno` is expected to be preserved. Therefore, we should save and restore it. For example `io_binwrite` calls `rb_fiber_scheduler_unblock` and if `errno` is reset to 0 by user code, it will break the error handling in `io_write`.
+ // If we explicitly preserve `errno` in `io_binwrite` and other similar functions (e.g. by returning it), this code is no longer needed. I hope in the future we will be able to remove it.
+ int saved_errno = errno;
+
+ VALUE result = rb_funcall(scheduler, id_unblock, 2, blocker, fiber);
+
+ errno = saved_errno;
+
+ return result;
}
+/*
+ * Document-method: Fiber::Scheduler#io_wait
+ * call-seq: io_wait(io, events, timeout)
+ *
+ * Invoked by IO#wait, IO#wait_readable, IO#wait_writable to ask whether the
+ * specified descriptor is ready for specified events within
+ * the specified +timeout+.
+ *
+ * +events+ is a bit mask of <tt>IO::READABLE</tt>, <tt>IO::WRITABLE</tt>, and
+ * <tt>IO::PRIORITY</tt>.
+ *
+ * Suggested implementation should register which Fiber is waiting for which
+ * resources and immediately calling Fiber.yield to pass control to other
+ * fibers. Then, in the #close method, the scheduler might dispatch all the
+ * I/O resources to fibers waiting for it.
+ *
+ * Expected to return the subset of events that are ready immediately.
+ *
+ */
VALUE
rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeout)
{
@@ -222,53 +442,151 @@ rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeou
VALUE
rb_fiber_scheduler_io_wait_readable(VALUE scheduler, VALUE io)
{
- return rb_fiber_scheduler_io_wait(scheduler, io, RB_UINT2NUM(RUBY_IO_READABLE), Qnil);
+ return rb_fiber_scheduler_io_wait(scheduler, io, RB_UINT2NUM(RUBY_IO_READABLE), rb_io_timeout(io));
}
VALUE
rb_fiber_scheduler_io_wait_writable(VALUE scheduler, VALUE io)
{
- return rb_fiber_scheduler_io_wait(scheduler, io, RB_UINT2NUM(RUBY_IO_WRITABLE), Qnil);
+ return rb_fiber_scheduler_io_wait(scheduler, io, RB_UINT2NUM(RUBY_IO_WRITABLE), rb_io_timeout(io));
+}
+
+/*
+ * Document-method: Fiber::Scheduler#io_select
+ * call-seq: io_select(readables, writables, exceptables, timeout)
+ *
+ * Invoked by IO.select to ask whether the specified descriptors are ready for
+ * specified events within the specified +timeout+.
+ *
+ * Expected to return the 3-tuple of Array of IOs that are ready.
+ *
+ */
+VALUE rb_fiber_scheduler_io_select(VALUE scheduler, VALUE readables, VALUE writables, VALUE exceptables, VALUE timeout)
+{
+ VALUE arguments[] = {
+ readables, writables, exceptables, timeout
+ };
+
+ return rb_fiber_scheduler_io_selectv(scheduler, 4, arguments);
+}
+
+VALUE rb_fiber_scheduler_io_selectv(VALUE scheduler, int argc, VALUE *argv)
+{
+ // I wondered about extracting argv, and checking if there is only a single
+ // IO instance, and instead calling `io_wait`. However, it would require a
+ // decent amount of work and it would be hard to preserve the exact
+ // semantics of IO.select.
+
+ return rb_check_funcall(scheduler, id_io_select, argc, argv);
}
+/*
+ * Document-method: Fiber::Scheduler#io_read
+ * call-seq: io_read(io, buffer, minimum_length) -> read length or -errno
+ *
+ * Invoked by IO#read or IO#Buffer.read to read +length+ bytes from +io+ into a
+ * specified +buffer+ (see IO::Buffer).
+ *
+ * The +minimum_length+ argument is the "minimum length to be read". If the IO
+ * buffer size is 8KiB, but the +length+ is +1024+ (1KiB), up to 8KiB might be
+ * read, but at least 1KiB will be. Generally, the only case where less data
+ * than +length+ will be read is if there is an error reading the data.
+ *
+ * Specifying a +length+ of 0 is valid and means try reading at least once and
+ * return any available data.
+ *
+ * Suggested implementation should try to read from +io+ in a non-blocking
+ * manner and call #io_wait if the +io+ is not ready (which will yield control
+ * to other fibers).
+ *
+ * See IO::Buffer for an interface available to return data.
+ *
+ * Expected to return number of bytes read, or, in case of an error,
+ * <tt>-errno</tt> (negated number corresponding to system's error code).
+ *
+ * The method should be considered _experimental_.
+ */
VALUE
-rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length)
+rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset)
{
VALUE arguments[] = {
- io, buffer, SIZET2NUM(length)
+ io, buffer, SIZET2NUM(length), SIZET2NUM(offset)
};
- return rb_check_funcall(scheduler, id_io_read, 3, arguments);
+ return rb_check_funcall(scheduler, id_io_read, 4, arguments);
}
+
+/*
+ * Document-method: Fiber::Scheduler#io_read
+ * call-seq: io_pread(io, buffer, from, length, offset) -> read length or -errno
+ *
+ * Invoked by IO::Buffer#pread. See that method for description of arguments.
+ *
+ */
VALUE
-rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset)
+rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset)
{
VALUE arguments[] = {
- io, buffer, SIZET2NUM(length), OFFT2NUM(offset)
+ io, buffer, OFFT2NUM(from), SIZET2NUM(length), SIZET2NUM(offset)
};
- return rb_check_funcall(scheduler, id_io_pread, 4, arguments);
+ return rb_check_funcall(scheduler, id_io_pread, 5, arguments);
}
+/*
+ * Document-method: Scheduler#io_write
+ * call-seq: io_write(io, buffer, minimum_length) -> written length or -errno
+ *
+ * Invoked by IO#write or IO::Buffer#write to write +length+ bytes to +io+ from
+ * from a specified +buffer+ (see IO::Buffer).
+ *
+ * The +minimum_length+ argument is the "minimum length to be written". If the
+ * IO buffer size is 8KiB, but the +length+ specified is 1024 (1KiB), at most
+ * 8KiB will be written, but at least 1KiB will be. Generally, the only case
+ * where less data than +minimum_length+ will be written is if there is an
+ * error writing the data.
+ *
+ * Specifying a +length+ of 0 is valid and means try writing at least once, as
+ * much data as possible.
+ *
+ * Suggested implementation should try to write to +io+ in a non-blocking
+ * manner and call #io_wait if the +io+ is not ready (which will yield control
+ * to other fibers).
+ *
+ * See IO::Buffer for an interface available to get data from buffer
+ * efficiently.
+ *
+ * Expected to return number of bytes written, or, in case of an error,
+ * <tt>-errno</tt> (negated number corresponding to system's error code).
+ *
+ * The method should be considered _experimental_.
+ */
VALUE
-rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length)
+rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset)
{
VALUE arguments[] = {
- io, buffer, SIZET2NUM(length)
+ io, buffer, SIZET2NUM(length), SIZET2NUM(offset)
};
- return rb_check_funcall(scheduler, id_io_write, 3, arguments);
+ return rb_check_funcall(scheduler, id_io_write, 4, arguments);
}
+/*
+ * Document-method: Fiber::Scheduler#io_pwrite
+ * call-seq: io_pwrite(io, buffer, from, length, offset) -> written length or -errno
+ *
+ * Invoked by IO::Buffer#pwrite. See that method for description of arguments.
+ *
+ */
VALUE
-rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset)
+rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset)
{
VALUE arguments[] = {
- io, buffer, SIZET2NUM(length), OFFT2NUM(offset)
+ io, buffer, OFFT2NUM(from), SIZET2NUM(length), SIZET2NUM(offset)
};
- return rb_check_funcall(scheduler, id_io_pwrite, 4, arguments);
+ return rb_check_funcall(scheduler, id_io_pwrite, 5, arguments);
}
VALUE
@@ -276,7 +594,7 @@ rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *base, size_t
{
VALUE buffer = rb_io_buffer_new(base, size, RB_IO_BUFFER_LOCKED);
- VALUE result = rb_fiber_scheduler_io_read(scheduler, io, buffer, length);
+ VALUE result = rb_fiber_scheduler_io_read(scheduler, io, buffer, length, 0);
rb_io_buffer_unlock(buffer);
rb_io_buffer_free(buffer);
@@ -289,7 +607,7 @@ rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *base,
{
VALUE buffer = rb_io_buffer_new((void*)base, size, RB_IO_BUFFER_LOCKED|RB_IO_BUFFER_READONLY);
- VALUE result = rb_fiber_scheduler_io_write(scheduler, io, buffer, length);
+ VALUE result = rb_fiber_scheduler_io_write(scheduler, io, buffer, length, 0);
rb_io_buffer_unlock(buffer);
rb_io_buffer_free(buffer);
@@ -305,6 +623,38 @@ rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io)
return rb_check_funcall(scheduler, id_io_close, 1, arguments);
}
+/*
+ * Document-method: Fiber::Scheduler#address_resolve
+ * call-seq: address_resolve(hostname) -> array_of_strings or nil
+ *
+ * Invoked by any method that performs a non-reverse DNS lookup. The most
+ * notable method is Addrinfo.getaddrinfo, but there are many other.
+ *
+ * The method is expected to return an array of strings corresponding to ip
+ * addresses the +hostname+ is resolved to, or +nil+ if it can not be resolved.
+ *
+ * Fairly exhaustive list of all possible call-sites:
+ *
+ * - Addrinfo.getaddrinfo
+ * - Addrinfo.tcp
+ * - Addrinfo.udp
+ * - Addrinfo.ip
+ * - Addrinfo.new
+ * - Addrinfo.marshal_load
+ * - SOCKSSocket.new
+ * - TCPServer.new
+ * - TCPSocket.new
+ * - IPSocket.getaddress
+ * - TCPSocket.gethostbyname
+ * - UDPSocket#connect
+ * - UDPSocket#bind
+ * - UDPSocket#send
+ * - Socket.getaddrinfo
+ * - Socket.gethostbyname
+ * - Socket.pack_sockaddr_in
+ * - Socket.sockaddr_in
+ * - Socket.unpack_sockaddr_in
+ */
VALUE
rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname)
{
@@ -314,3 +664,24 @@ rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname)
return rb_check_funcall(scheduler, id_address_resolve, 1, arguments);
}
+
+/*
+ * Document-method: Fiber::Scheduler#fiber
+ * call-seq: fiber(&block)
+ *
+ * Implementation of the Fiber.schedule. The method is <em>expected</em> to immediately
+ * run the given block of code in a separate non-blocking fiber, and to return that Fiber.
+ *
+ * Minimal suggested implementation is:
+ *
+ * def fiber(&block)
+ * fiber = Fiber.new(blocking: false, &block)
+ * fiber.resume
+ * fiber
+ * end
+ */
+VALUE
+rb_fiber_scheduler_fiber(VALUE scheduler, int argc, VALUE *argv, int kw_splat)
+{
+ return rb_funcall_passing_block_kw(scheduler, id_fiber_schedule, argc, argv, kw_splat);
+}
diff --git a/shape.c b/shape.c
new file mode 100644
index 0000000000..1dc08dcb60
--- /dev/null
+++ b/shape.c
@@ -0,0 +1,825 @@
+#include "vm_core.h"
+#include "vm_sync.h"
+#include "shape.h"
+#include "gc.h"
+#include "symbol.h"
+#include "id_table.h"
+#include "internal/class.h"
+#include "internal/symbol.h"
+#include "internal/variable.h"
+#include "variable.h"
+#include <stdbool.h>
+
+#ifndef SHAPE_DEBUG
+#define SHAPE_DEBUG (VM_CHECK_MODE > 0)
+#endif
+
+static ID id_frozen;
+static ID id_t_object;
+static ID size_pool_edge_names[SIZE_POOL_COUNT];
+
+/*
+ * Shape getters
+ */
+rb_shape_t *
+rb_shape_get_root_shape(void)
+{
+ return GET_VM()->root_shape;
+}
+
+shape_id_t
+rb_shape_id(rb_shape_t * shape)
+{
+ return (shape_id_t)(shape - GET_VM()->shape_list);
+}
+
+bool
+rb_shape_root_shape_p(rb_shape_t* shape)
+{
+ return shape == rb_shape_get_root_shape();
+}
+
+void
+rb_shape_each_shape(each_shape_callback callback, void *data)
+{
+ rb_shape_t *cursor = rb_shape_get_root_shape();
+ rb_shape_t *end = rb_shape_get_shape_by_id(GET_VM()->next_shape_id);
+ while (cursor < end) {
+ callback(cursor, data);
+ cursor += 1;
+ }
+}
+
+rb_shape_t*
+rb_shape_get_shape_by_id(shape_id_t shape_id)
+{
+ RUBY_ASSERT(shape_id != INVALID_SHAPE_ID);
+
+ rb_vm_t *vm = GET_VM();
+ rb_shape_t *shape = &vm->shape_list[shape_id];
+ return shape;
+}
+
+rb_shape_t*
+rb_shape_get_shape_by_id_without_assertion(shape_id_t shape_id)
+{
+ RUBY_ASSERT(shape_id != INVALID_SHAPE_ID);
+
+ rb_vm_t *vm = GET_VM();
+ rb_shape_t *shape = &vm->shape_list[shape_id];
+ return shape;
+}
+
+rb_shape_t *
+rb_shape_get_parent(rb_shape_t * shape)
+{
+ return rb_shape_get_shape_by_id(shape->parent_id);
+}
+
+#if !SHAPE_IN_BASIC_FLAGS
+shape_id_t
+rb_rclass_shape_id(VALUE obj)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
+ return RCLASS_EXT(obj)->shape_id;
+}
+
+shape_id_t rb_generic_shape_id(VALUE obj);
+#endif
+
+shape_id_t
+rb_shape_get_shape_id(VALUE obj)
+{
+ if (RB_SPECIAL_CONST_P(obj)) {
+ return SPECIAL_CONST_SHAPE_ID;
+ }
+
+#if SHAPE_IN_BASIC_FLAGS
+ return RBASIC_SHAPE_ID(obj);
+#else
+ switch (BUILTIN_TYPE(obj)) {
+ case T_OBJECT:
+ return ROBJECT_SHAPE_ID(obj);
+ break;
+ case T_CLASS:
+ case T_MODULE:
+ return RCLASS_SHAPE_ID(obj);
+ default:
+ return rb_generic_shape_id(obj);
+ }
+#endif
+}
+
+size_t
+rb_shape_depth(rb_shape_t * shape)
+{
+ size_t depth = 1;
+
+ while (shape->parent_id != INVALID_SHAPE_ID) {
+ depth++;
+ shape = rb_shape_get_parent(shape);
+ }
+
+ return depth;
+}
+
+rb_shape_t*
+rb_shape_get_shape(VALUE obj)
+{
+ return rb_shape_get_shape_by_id(rb_shape_get_shape_id(obj));
+}
+
+static rb_shape_t*
+get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type, bool * variation_created, bool new_shapes_allowed)
+{
+ rb_shape_t *res = NULL;
+
+ // There should never be outgoing edges from "too complex"
+ RUBY_ASSERT(rb_shape_id(shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
+
+ *variation_created = false;
+
+ if (new_shapes_allowed) {
+ RB_VM_LOCK_ENTER();
+ {
+ bool had_edges = !!shape->edges;
+
+ if (!shape->edges) {
+ shape->edges = rb_id_table_create(0);
+ }
+
+ // Lookup the shape in edges - if there's already an edge and a corresponding shape for it,
+ // we can return that. Otherwise, we'll need to get a new shape
+ VALUE lookup_result;
+ if (rb_id_table_lookup(shape->edges, id, &lookup_result)) {
+ res = (rb_shape_t *)lookup_result;
+ }
+ else {
+ *variation_created = had_edges;
+
+ rb_shape_t * new_shape = rb_shape_alloc(id, shape);
+
+ new_shape->type = (uint8_t)shape_type;
+ new_shape->capacity = shape->capacity;
+
+ switch (shape_type) {
+ case SHAPE_IVAR:
+ new_shape->next_iv_index = shape->next_iv_index + 1;
+ break;
+ case SHAPE_CAPACITY_CHANGE:
+ case SHAPE_FROZEN:
+ case SHAPE_T_OBJECT:
+ new_shape->next_iv_index = shape->next_iv_index;
+ break;
+ case SHAPE_OBJ_TOO_COMPLEX:
+ case SHAPE_INITIAL_CAPACITY:
+ case SHAPE_ROOT:
+ rb_bug("Unreachable");
+ break;
+ }
+
+ rb_id_table_insert(shape->edges, id, (VALUE)new_shape);
+
+ res = new_shape;
+ }
+ }
+ RB_VM_LOCK_LEAVE();
+ }
+ return res;
+}
+
+MJIT_FUNC_EXPORTED int
+rb_shape_frozen_shape_p(rb_shape_t* shape)
+{
+ return SHAPE_FROZEN == (enum shape_type)shape->type;
+}
+
+static void
+move_iv(VALUE obj, ID id, attr_index_t from, attr_index_t to)
+{
+ switch(BUILTIN_TYPE(obj)) {
+ case T_CLASS:
+ case T_MODULE:
+ RCLASS_IVPTR(obj)[to] = RCLASS_IVPTR(obj)[from];
+ break;
+ case T_OBJECT:
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
+ ROBJECT_IVPTR(obj)[to] = ROBJECT_IVPTR(obj)[from];
+ break;
+ default: {
+ struct gen_ivtbl *ivtbl;
+ rb_gen_ivtbl_get(obj, id, &ivtbl);
+ ivtbl->ivptr[to] = ivtbl->ivptr[from];
+ break;
+ }
+ }
+}
+
+static rb_shape_t *
+remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed)
+{
+ if (shape->parent_id == INVALID_SHAPE_ID) {
+ // We've hit the top of the shape tree and couldn't find the
+ // IV we wanted to remove, so return NULL
+ return NULL;
+ }
+ else {
+ if (shape->type == SHAPE_IVAR && shape->edge_name == id) {
+ // We've hit the edge we wanted to remove, return it's _parent_
+ // as the new parent while we go back down the stack.
+ attr_index_t index = shape->next_iv_index - 1;
+
+ switch(BUILTIN_TYPE(obj)) {
+ case T_CLASS:
+ case T_MODULE:
+ *removed = RCLASS_IVPTR(obj)[index];
+ break;
+ case T_OBJECT:
+ *removed = ROBJECT_IVPTR(obj)[index];
+ break;
+ default: {
+ struct gen_ivtbl *ivtbl;
+ rb_gen_ivtbl_get(obj, id, &ivtbl);
+ *removed = ivtbl->ivptr[index];
+ break;
+ }
+ }
+ return rb_shape_get_parent(shape);
+ }
+ else {
+ // This isn't the IV we want to remove, keep walking up.
+ rb_shape_t * new_parent = remove_shape_recursive(obj, id, rb_shape_get_parent(shape), removed);
+
+ // We found a new parent. Create a child of the new parent that
+ // has the same attributes as this shape.
+ if (new_parent) {
+ bool dont_care;
+ rb_shape_t * new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type, &dont_care, true);
+ new_child->capacity = shape->capacity;
+ if (new_child->type == SHAPE_IVAR) {
+ move_iv(obj, id, shape->next_iv_index - 1, new_child->next_iv_index - 1);
+ }
+
+ return new_child;
+ }
+ else {
+ // We went all the way to the top of the shape tree and couldn't
+ // find an IV to remove, so return NULL
+ return NULL;
+ }
+ }
+ }
+}
+
+void
+rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE * removed)
+{
+ rb_shape_t * new_shape = remove_shape_recursive(obj, id, shape, removed);
+ if (new_shape) {
+ rb_shape_set_shape(obj, new_shape);
+ }
+}
+
+void
+rb_shape_transition_shape_frozen(VALUE obj)
+{
+ rb_shape_t* shape = rb_shape_get_shape(obj);
+ RUBY_ASSERT(shape);
+ RUBY_ASSERT(RB_OBJ_FROZEN(obj));
+
+ if (rb_shape_frozen_shape_p(shape) || rb_shape_obj_too_complex(obj)) {
+ return;
+ }
+
+ rb_shape_t* next_shape;
+
+ if (shape == rb_shape_get_root_shape()) {
+ rb_shape_set_shape_id(obj, SPECIAL_CONST_SHAPE_ID);
+ return;
+ }
+
+ bool dont_care;
+ next_shape = get_next_shape_internal(shape, (ID)id_frozen, SHAPE_FROZEN, &dont_care, true);
+
+ RUBY_ASSERT(next_shape);
+ rb_shape_set_shape(obj, next_shape);
+}
+
+/*
+ * This function is used for assertions where we don't want to increment
+ * max_iv_count
+ */
+rb_shape_t *
+rb_shape_get_next_iv_shape(rb_shape_t* shape, ID id)
+{
+ RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
+ bool dont_care;
+ return get_next_shape_internal(shape, id, SHAPE_IVAR, &dont_care, true);
+}
+
+rb_shape_t *
+rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
+{
+ RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
+
+ bool allow_new_shape = true;
+
+ if (BUILTIN_TYPE(obj) == T_OBJECT) {
+ VALUE klass = rb_obj_class(obj);
+ allow_new_shape = RCLASS_EXT(klass)->variation_count < SHAPE_MAX_VARIATIONS;
+ }
+
+ bool variation_created = false;
+ rb_shape_t * new_shape = get_next_shape_internal(shape, id, SHAPE_IVAR, &variation_created, allow_new_shape);
+
+ if (!new_shape) {
+ RUBY_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT);
+ new_shape = rb_shape_get_shape_by_id(OBJ_TOO_COMPLEX_SHAPE_ID);
+ }
+
+ // Check if we should update max_iv_count on the object's class
+ if (BUILTIN_TYPE(obj) == T_OBJECT) {
+ VALUE klass = rb_obj_class(obj);
+ if (new_shape->next_iv_index > RCLASS_EXT(klass)->max_iv_count) {
+ RCLASS_EXT(klass)->max_iv_count = new_shape->next_iv_index;
+ }
+
+ if (variation_created) {
+ RCLASS_EXT(klass)->variation_count++;
+ }
+ }
+
+ return new_shape;
+}
+
+rb_shape_t *
+rb_shape_transition_shape_capa(rb_shape_t* shape, uint32_t new_capacity)
+{
+ ID edge_name = rb_make_temporary_id(new_capacity);
+ bool dont_care;
+ rb_shape_t * new_shape = get_next_shape_internal(shape, edge_name, SHAPE_CAPACITY_CHANGE, &dont_care, true);
+ new_shape->capacity = new_capacity;
+ return new_shape;
+}
+
+bool
+rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value)
+{
+ // It doesn't make sense to ask for the index of an IV that's stored
+ // on an object that is "too complex" as it uses a hash for storing IVs
+ RUBY_ASSERT(rb_shape_id(shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
+
+ while (shape->parent_id != INVALID_SHAPE_ID) {
+ if (shape->edge_name == id) {
+ enum shape_type shape_type;
+ shape_type = (enum shape_type)shape->type;
+
+ switch (shape_type) {
+ case SHAPE_IVAR:
+ RUBY_ASSERT(shape->next_iv_index > 0);
+ *value = shape->next_iv_index - 1;
+ return true;
+ case SHAPE_CAPACITY_CHANGE:
+ case SHAPE_ROOT:
+ case SHAPE_INITIAL_CAPACITY:
+ case SHAPE_T_OBJECT:
+ return false;
+ case SHAPE_OBJ_TOO_COMPLEX:
+ case SHAPE_FROZEN:
+ rb_bug("Ivar should not exist on transition\n");
+ }
+ }
+ shape = rb_shape_get_parent(shape);
+ }
+ return false;
+}
+
+static rb_shape_t *
+shape_alloc(void)
+{
+ rb_vm_t *vm = GET_VM();
+ shape_id_t shape_id = vm->next_shape_id;
+ vm->next_shape_id++;
+
+ if (shape_id == MAX_SHAPE_ID) {
+ // TODO: Make an OutOfShapesError ??
+ rb_bug("Out of shapes\n");
+ }
+
+ return &GET_VM()->shape_list[shape_id];
+}
+
+rb_shape_t *
+rb_shape_alloc_with_parent_id(ID edge_name, shape_id_t parent_id)
+{
+ rb_shape_t * shape = shape_alloc();
+
+ shape->edge_name = edge_name;
+ shape->next_iv_index = 0;
+ shape->parent_id = parent_id;
+
+ return shape;
+}
+
+rb_shape_t *
+rb_shape_alloc_with_size_pool_index(ID edge_name, rb_shape_t * parent, uint8_t size_pool_index)
+{
+ rb_shape_t * shape = rb_shape_alloc_with_parent_id(edge_name, rb_shape_id(parent));
+ shape->size_pool_index = size_pool_index;
+ return shape;
+}
+
+
+rb_shape_t *
+rb_shape_alloc(ID edge_name, rb_shape_t * parent)
+{
+ return rb_shape_alloc_with_size_pool_index(edge_name, parent, parent->size_pool_index);
+}
+
+MJIT_FUNC_EXPORTED void
+rb_shape_set_shape(VALUE obj, rb_shape_t* shape)
+{
+ rb_shape_set_shape_id(obj, rb_shape_id(shape));
+}
+
+int32_t
+rb_shape_id_offset(void)
+{
+ return sizeof(uintptr_t) - SHAPE_ID_NUM_BITS / sizeof(uintptr_t);
+}
+
+rb_shape_t *
+rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
+{
+ RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
+ rb_shape_t *next_shape = initial_shape;
+
+ if (dest_shape->type != initial_shape->type) {
+ next_shape = rb_shape_traverse_from_new_root(initial_shape, rb_shape_get_parent(dest_shape));
+ if (!next_shape) {
+ return NULL;
+ }
+ }
+
+ switch ((enum shape_type)dest_shape->type) {
+ case SHAPE_IVAR:
+ case SHAPE_FROZEN:
+ if (!next_shape->edges) {
+ return NULL;
+ }
+
+ VALUE lookup_result;
+ if (rb_id_table_lookup(next_shape->edges, dest_shape->edge_name, &lookup_result)) {
+ next_shape = (rb_shape_t *)lookup_result;
+ }
+ else {
+ return NULL;
+ }
+ break;
+ case SHAPE_ROOT:
+ case SHAPE_CAPACITY_CHANGE:
+ case SHAPE_INITIAL_CAPACITY:
+ case SHAPE_T_OBJECT:
+ break;
+ case SHAPE_OBJ_TOO_COMPLEX:
+ rb_bug("Unreachable\n");
+ break;
+ }
+
+ return next_shape;
+}
+
+rb_shape_t *
+rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape)
+{
+ rb_shape_t * midway_shape;
+
+ RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
+
+ if (dest_shape->type != initial_shape->type) {
+ midway_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_parent(dest_shape));
+ }
+ else {
+ midway_shape = initial_shape;
+ }
+
+ switch ((enum shape_type)dest_shape->type) {
+ case SHAPE_IVAR:
+ if (midway_shape->capacity <= midway_shape->next_iv_index) {
+ // There isn't enough room to write this IV, so we need to increase the capacity
+ midway_shape = rb_shape_transition_shape_capa(midway_shape, midway_shape->capacity * 2);
+ }
+
+ midway_shape = rb_shape_get_next_iv_shape(midway_shape, dest_shape->edge_name);
+ break;
+ case SHAPE_ROOT:
+ case SHAPE_FROZEN:
+ case SHAPE_CAPACITY_CHANGE:
+ case SHAPE_INITIAL_CAPACITY:
+ case SHAPE_T_OBJECT:
+ break;
+ case SHAPE_OBJ_TOO_COMPLEX:
+ rb_bug("Unreachable\n");
+ break;
+ }
+
+ return midway_shape;
+}
+
+bool
+rb_shape_obj_too_complex(VALUE obj)
+{
+ return rb_shape_get_shape_id(obj) == OBJ_TOO_COMPLEX_SHAPE_ID;
+}
+
+void
+rb_shape_set_too_complex(VALUE obj)
+{
+ RUBY_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT);
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
+ rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
+}
+
+size_t
+rb_shape_edges_count(rb_shape_t *shape)
+{
+ if (shape->edges) {
+ return rb_id_table_size(shape->edges);
+ }
+ return 0;
+}
+
+size_t
+rb_shape_memsize(rb_shape_t *shape)
+{
+ size_t memsize = sizeof(rb_shape_t);
+ if (shape->edges) {
+ memsize += rb_id_table_memsize(shape->edges);
+ }
+ return memsize;
+}
+
+#if SHAPE_DEBUG
+/*
+ * Exposing Shape to Ruby via RubyVM.debug_shape
+ */
+
+/* :nodoc: */
+static VALUE
+rb_shape_too_complex(VALUE self)
+{
+ rb_shape_t * shape;
+ shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
+ if (rb_shape_id(shape) == OBJ_TOO_COMPLEX_SHAPE_ID) {
+ return Qtrue;
+ }
+ else {
+ return Qfalse;
+ }
+}
+
+static VALUE
+parse_key(ID key)
+{
+ if (is_instance_id(key)) {
+ return ID2SYM(key);
+ }
+ return LONG2NUM(key);
+}
+
+static VALUE rb_shape_edge_name(rb_shape_t * shape);
+
+static VALUE
+rb_shape_t_to_rb_cShape(rb_shape_t *shape)
+{
+ VALUE rb_cShape = rb_const_get(rb_cRubyVM, rb_intern("Shape"));
+
+ VALUE obj = rb_struct_new(rb_cShape,
+ INT2NUM(rb_shape_id(shape)),
+ INT2NUM(shape->parent_id),
+ rb_shape_edge_name(shape),
+ INT2NUM(shape->next_iv_index),
+ INT2NUM(shape->size_pool_index),
+ INT2NUM(shape->type),
+ INT2NUM(shape->capacity));
+ rb_obj_freeze(obj);
+ return obj;
+}
+
+static enum rb_id_table_iterator_result
+rb_edges_to_hash(ID key, VALUE value, void *ref)
+{
+ rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_shape_t_to_rb_cShape((rb_shape_t*)value));
+ return ID_TABLE_CONTINUE;
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_edges(VALUE self)
+{
+ rb_shape_t* shape;
+
+ shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
+
+ VALUE hash = rb_hash_new();
+
+ if (shape->edges) {
+ rb_id_table_foreach(shape->edges, rb_edges_to_hash, &hash);
+ }
+
+ return hash;
+}
+
+static VALUE
+rb_shape_edge_name(rb_shape_t * shape)
+{
+ if (shape->edge_name) {
+ if (is_instance_id(shape->edge_name)) {
+ return ID2SYM(shape->edge_name);
+ }
+ return INT2NUM(shape->capacity);
+ }
+ return Qnil;
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_export_depth(VALUE self)
+{
+ rb_shape_t* shape;
+ shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
+ return SIZET2NUM(rb_shape_depth(shape));
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_parent(VALUE self)
+{
+ rb_shape_t * shape;
+ shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
+ if (shape->parent_id != INVALID_SHAPE_ID) {
+ return rb_shape_t_to_rb_cShape(rb_shape_get_parent(shape));
+ }
+ else {
+ return Qnil;
+ }
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_debug_shape(VALUE self, VALUE obj)
+{
+ return rb_shape_t_to_rb_cShape(rb_shape_get_shape(obj));
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_root_shape(VALUE self)
+{
+ return rb_shape_t_to_rb_cShape(rb_shape_get_root_shape());
+}
+
+VALUE rb_obj_shape(rb_shape_t* shape);
+
+static enum rb_id_table_iterator_result collect_keys_and_values(ID key, VALUE value, void *ref)
+{
+ rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_obj_shape((rb_shape_t*)value));
+ return ID_TABLE_CONTINUE;
+}
+
+static VALUE edges(struct rb_id_table* edges)
+{
+ VALUE hash = rb_hash_new();
+ if (edges)
+ rb_id_table_foreach(edges, collect_keys_and_values, &hash);
+ return hash;
+}
+
+/* :nodoc: */
+VALUE
+rb_obj_shape(rb_shape_t* shape)
+{
+ VALUE rb_shape = rb_hash_new();
+
+ rb_hash_aset(rb_shape, ID2SYM(rb_intern("id")), INT2NUM(rb_shape_id(shape)));
+ rb_hash_aset(rb_shape, ID2SYM(rb_intern("edges")), edges(shape->edges));
+
+ if (shape == rb_shape_get_root_shape()) {
+ rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(ROOT_SHAPE_ID));
+ }
+ else {
+ rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(shape->parent_id));
+ }
+
+ rb_hash_aset(rb_shape, ID2SYM(rb_intern("edge_name")), rb_id2str(shape->edge_name));
+ return rb_shape;
+}
+
+/* :nodoc: */
+static VALUE
+shape_transition_tree(VALUE self)
+{
+ return rb_obj_shape(rb_shape_get_root_shape());
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_find_by_id(VALUE mod, VALUE id)
+{
+ shape_id_t shape_id = NUM2UINT(id);
+ if (shape_id >= GET_VM()->next_shape_id) {
+ rb_raise(rb_eArgError, "Shape ID %d is out of bounds\n", shape_id);
+ }
+ return rb_shape_t_to_rb_cShape(rb_shape_get_shape_by_id(shape_id));
+}
+#endif
+
+void
+Init_default_shapes(void)
+{
+ id_frozen = rb_make_internal_id();
+ id_t_object = rb_make_internal_id();
+
+ // Shapes by size pool
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ size_pool_edge_names[i] = rb_make_internal_id();
+ }
+
+ // Root shape
+ rb_shape_t * root = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
+ root->capacity = (uint32_t)((rb_size_pool_slot_size(0) - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
+ root->type = SHAPE_ROOT;
+ root->size_pool_index = 0;
+ GET_VM()->root_shape = root;
+ RUBY_ASSERT(rb_shape_id(GET_VM()->root_shape) == ROOT_SHAPE_ID);
+
+ // Shapes by size pool
+ for (int i = 1; i < SIZE_POOL_COUNT; i++) {
+ uint32_t capa = (uint32_t)((rb_size_pool_slot_size(i) - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
+ rb_shape_t * new_shape = rb_shape_transition_shape_capa(root, capa);
+ new_shape->type = SHAPE_INITIAL_CAPACITY;
+ new_shape->size_pool_index = i;
+ RUBY_ASSERT(rb_shape_id(new_shape) == (shape_id_t)i);
+ }
+
+ // Make shapes for T_OBJECT
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ rb_shape_t * shape = rb_shape_get_shape_by_id(i);
+ bool dont_care;
+ rb_shape_t * t_object_shape =
+ get_next_shape_internal(shape, id_t_object, SHAPE_T_OBJECT, &dont_care, true);
+ t_object_shape->edges = rb_id_table_create(0);
+ RUBY_ASSERT(rb_shape_id(t_object_shape) == (shape_id_t)(i + SIZE_POOL_COUNT));
+ }
+
+ bool dont_care;
+ // Special const shape
+#if RUBY_DEBUG
+ rb_shape_t * special_const_shape =
+#endif
+ get_next_shape_internal(root, (ID)id_frozen, SHAPE_FROZEN, &dont_care, true);
+ RUBY_ASSERT(rb_shape_id(special_const_shape) == SPECIAL_CONST_SHAPE_ID);
+ RUBY_ASSERT(SPECIAL_CONST_SHAPE_ID == (GET_VM()->next_shape_id - 1));
+ RUBY_ASSERT(rb_shape_frozen_shape_p(special_const_shape));
+
+ rb_shape_t * hash_fallback_shape = rb_shape_alloc_with_parent_id(0, ROOT_SHAPE_ID);
+ hash_fallback_shape->type = SHAPE_OBJ_TOO_COMPLEX;
+ hash_fallback_shape->size_pool_index = 0;
+ RUBY_ASSERT(OBJ_TOO_COMPLEX_SHAPE_ID == (GET_VM()->next_shape_id - 1));
+ RUBY_ASSERT(rb_shape_id(hash_fallback_shape) == OBJ_TOO_COMPLEX_SHAPE_ID);
+}
+
+void
+Init_shape(void)
+{
+#if SHAPE_DEBUG
+ VALUE rb_cShape = rb_struct_define_under(rb_cRubyVM, "Shape",
+ "id",
+ "parent_id",
+ "edge_name",
+ "next_iv_index",
+ "size_pool_index",
+ "type",
+ "capacity",
+ NULL);
+
+ rb_define_method(rb_cShape, "parent", rb_shape_parent, 0);
+ rb_define_method(rb_cShape, "edges", rb_shape_edges, 0);
+ rb_define_method(rb_cShape, "depth", rb_shape_export_depth, 0);
+ rb_define_method(rb_cShape, "too_complex?", rb_shape_too_complex, 0);
+ rb_define_const(rb_cShape, "SHAPE_ROOT", INT2NUM(SHAPE_ROOT));
+ rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR));
+ rb_define_const(rb_cShape, "SHAPE_T_OBJECT", INT2NUM(SHAPE_T_OBJECT));
+ rb_define_const(rb_cShape, "SHAPE_FROZEN", INT2NUM(SHAPE_FROZEN));
+ rb_define_const(rb_cShape, "SHAPE_ID_NUM_BITS", INT2NUM(SHAPE_ID_NUM_BITS));
+ rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT));
+ rb_define_const(rb_cShape, "SPECIAL_CONST_SHAPE_ID", INT2NUM(SPECIAL_CONST_SHAPE_ID));
+ rb_define_const(rb_cShape, "OBJ_TOO_COMPLEX_SHAPE_ID", INT2NUM(OBJ_TOO_COMPLEX_SHAPE_ID));
+ rb_define_const(rb_cShape, "SHAPE_MAX_VARIATIONS", INT2NUM(SHAPE_MAX_VARIATIONS));
+
+ rb_define_singleton_method(rb_cShape, "transition_tree", shape_transition_tree, 0);
+ rb_define_singleton_method(rb_cShape, "find_by_id", rb_shape_find_by_id, 1);
+ rb_define_singleton_method(rb_cShape, "of", rb_shape_debug_shape, 1);
+ rb_define_singleton_method(rb_cShape, "root_shape", rb_shape_root_shape, 0);
+#endif
+}
diff --git a/shape.h b/shape.h
new file mode 100644
index 0000000000..8f3451d492
--- /dev/null
+++ b/shape.h
@@ -0,0 +1,232 @@
+#ifndef RUBY_SHAPE_H
+#define RUBY_SHAPE_H
+
+#include "internal/gc.h"
+
+#if (SIZEOF_UINT64_T == SIZEOF_VALUE)
+#define SIZEOF_SHAPE_T 4
+#define SHAPE_IN_BASIC_FLAGS 1
+typedef uint32_t attr_index_t;
+#else
+#define SIZEOF_SHAPE_T 2
+#define SHAPE_IN_BASIC_FLAGS 0
+typedef uint16_t attr_index_t;
+#endif
+
+#define MAX_IVARS (attr_index_t)(-1)
+
+#if SIZEOF_SHAPE_T == 4
+typedef uint32_t shape_id_t;
+# define SHAPE_ID_NUM_BITS 32
+#else
+typedef uint16_t shape_id_t;
+# define SHAPE_ID_NUM_BITS 16
+#endif
+
+# define SHAPE_MASK (((uintptr_t)1 << SHAPE_ID_NUM_BITS) - 1)
+# define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_ID_NUM_BITS)
+
+# define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * 8) - SHAPE_ID_NUM_BITS)
+
+# define SHAPE_BITMAP_SIZE 16384
+
+# define SHAPE_MAX_VARIATIONS 8
+
+# define MAX_SHAPE_ID (SHAPE_MASK - 1)
+# define INVALID_SHAPE_ID SHAPE_MASK
+# define ROOT_SHAPE_ID 0x0
+
+# define SPECIAL_CONST_SHAPE_ID (SIZE_POOL_COUNT * 2)
+# define OBJ_TOO_COMPLEX_SHAPE_ID (SPECIAL_CONST_SHAPE_ID + 1)
+
+struct rb_shape {
+ struct rb_id_table * edges; // id_table from ID (ivar) to next shape
+ ID edge_name; // ID (ivar) for transition from parent to rb_shape
+ attr_index_t next_iv_index;
+ uint32_t capacity; // Total capacity of the object with this shape
+ uint8_t type;
+ uint8_t size_pool_index;
+ shape_id_t parent_id;
+};
+
+typedef struct rb_shape rb_shape_t;
+
+enum shape_type {
+ SHAPE_ROOT,
+ SHAPE_IVAR,
+ SHAPE_FROZEN,
+ SHAPE_CAPACITY_CHANGE,
+ SHAPE_INITIAL_CAPACITY,
+ SHAPE_T_OBJECT,
+ SHAPE_OBJ_TOO_COMPLEX,
+};
+
+#if SHAPE_IN_BASIC_FLAGS
+static inline shape_id_t
+RBASIC_SHAPE_ID(VALUE obj)
+{
+ RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj));
+ return (shape_id_t)(SHAPE_MASK & ((RBASIC(obj)->flags) >> SHAPE_FLAG_SHIFT));
+}
+
+static inline void
+RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
+{
+ // Ractors are occupying the upper 32 bits of flags, but only in debug mode
+ // Object shapes are occupying top bits
+ RBASIC(obj)->flags &= SHAPE_FLAG_MASK;
+ RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT);
+}
+
+static inline shape_id_t
+ROBJECT_SHAPE_ID(VALUE obj)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ return RBASIC_SHAPE_ID(obj);
+}
+
+static inline void
+ROBJECT_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ RBASIC_SET_SHAPE_ID(obj, shape_id);
+}
+
+static inline shape_id_t
+RCLASS_SHAPE_ID(VALUE obj)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
+ return RBASIC_SHAPE_ID(obj);
+}
+
+#else
+
+static inline shape_id_t
+ROBJECT_SHAPE_ID(VALUE obj)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ return (shape_id_t)(SHAPE_MASK & (RBASIC(obj)->flags >> SHAPE_FLAG_SHIFT));
+}
+
+static inline void
+ROBJECT_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
+{
+ RBASIC(obj)->flags &= SHAPE_FLAG_MASK;
+ RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT);
+}
+
+MJIT_SYMBOL_EXPORT_BEGIN
+shape_id_t rb_rclass_shape_id(VALUE obj);
+MJIT_SYMBOL_EXPORT_END
+
+static inline shape_id_t RCLASS_SHAPE_ID(VALUE obj)
+{
+ return rb_rclass_shape_id(obj);
+}
+
+#endif
+
+bool rb_shape_root_shape_p(rb_shape_t* shape);
+rb_shape_t * rb_shape_get_root_shape(void);
+uint8_t rb_shape_id_num_bits(void);
+int32_t rb_shape_id_offset(void);
+
+rb_shape_t* rb_shape_get_shape_by_id_without_assertion(shape_id_t shape_id);
+rb_shape_t * rb_shape_get_parent(rb_shape_t * shape);
+
+MJIT_SYMBOL_EXPORT_BEGIN
+rb_shape_t* rb_shape_get_shape_by_id(shape_id_t shape_id);
+shape_id_t rb_shape_get_shape_id(VALUE obj);
+rb_shape_t * rb_shape_get_next_iv_shape(rb_shape_t * shape, ID id);
+bool rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t * value);
+bool rb_shape_obj_too_complex(VALUE obj);
+MJIT_SYMBOL_EXPORT_END
+
+void rb_shape_set_shape(VALUE obj, rb_shape_t* shape);
+rb_shape_t* rb_shape_get_shape(VALUE obj);
+int rb_shape_frozen_shape_p(rb_shape_t* shape);
+void rb_shape_transition_shape_frozen(VALUE obj);
+void rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE * removed);
+rb_shape_t * rb_shape_transition_shape_capa(rb_shape_t * shape, uint32_t new_capacity);
+rb_shape_t* rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id);
+
+rb_shape_t * rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape);
+
+static inline uint32_t
+ROBJECT_IV_CAPACITY(VALUE obj)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ // Asking for capacity doesn't make sense when the object is using
+ // a hash table for storing instance variables
+ RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) != OBJ_TOO_COMPLEX_SHAPE_ID);
+ return rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))->capacity;
+}
+
+static inline st_table *
+ROBJECT_IV_HASH(VALUE obj)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID);
+ return (st_table *)ROBJECT(obj)->as.heap.ivptr;
+}
+
+static inline void
+ROBJECT_SET_IV_HASH(VALUE obj, const struct rb_id_table *tbl)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID);
+ ROBJECT(obj)->as.heap.ivptr = (VALUE *)tbl;
+}
+
+size_t rb_id_table_size(const struct rb_id_table *tbl);
+
+static inline uint32_t
+ROBJECT_IV_COUNT(VALUE obj)
+{
+ if (ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID) {
+ return (uint32_t)rb_st_table_size(ROBJECT_IV_HASH(obj));
+ }
+ else {
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) != OBJ_TOO_COMPLEX_SHAPE_ID);
+ return rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))->next_iv_index;
+ }
+}
+
+static inline uint32_t
+RBASIC_IV_COUNT(VALUE obj)
+{
+ return rb_shape_get_shape_by_id(rb_shape_get_shape_id(obj))->next_iv_index;
+}
+
+static inline uint32_t
+RCLASS_IV_COUNT(VALUE obj)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
+ uint32_t ivc = rb_shape_get_shape_by_id(RCLASS_SHAPE_ID(obj))->next_iv_index;
+ return ivc;
+}
+
+rb_shape_t * rb_shape_alloc(ID edge_name, rb_shape_t * parent);
+rb_shape_t * rb_shape_alloc_with_size_pool_index(ID edge_name, rb_shape_t * parent, uint8_t size_pool_index);
+rb_shape_t * rb_shape_alloc_with_parent_id(ID edge_name, shape_id_t parent_id);
+
+rb_shape_t *rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *orig_shape);
+
+bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id);
+
+VALUE rb_obj_debug_shape(VALUE self, VALUE obj);
+VALUE rb_shape_flags_mask(void);
+void rb_shape_set_too_complex(VALUE obj);
+
+// For ext/objspace
+RUBY_SYMBOL_EXPORT_BEGIN
+typedef void each_shape_callback(rb_shape_t * shape, void *data);
+void rb_shape_each_shape(each_shape_callback callback, void *data);
+size_t rb_shape_memsize(rb_shape_t *shape);
+size_t rb_shape_edges_count(rb_shape_t *shape);
+size_t rb_shape_depth(rb_shape_t *shape);
+shape_id_t rb_shape_id(rb_shape_t * shape);
+RUBY_SYMBOL_EXPORT_END
+
+#endif
diff --git a/signal.c b/signal.c
index 4ca52b2ee6..1072cb47f1 100644
--- a/signal.c
+++ b/signal.c
@@ -44,6 +44,7 @@
#include "ruby_atomic.h"
#include "vm_core.h"
#include "ractor_core.h"
+#include "ruby/internal/attr/nonstring.h"
#ifdef NEED_RUBY_ATOMIC_OPS
rb_atomic_t
@@ -56,11 +57,11 @@ ruby_atomic_exchange(rb_atomic_t *ptr, rb_atomic_t val)
rb_atomic_t
ruby_atomic_compare_and_swap(rb_atomic_t *ptr, rb_atomic_t cmp,
- rb_atomic_t newval)
+ rb_atomic_t newval)
{
rb_atomic_t old = *ptr;
if (old == cmp) {
- *ptr = newval;
+ *ptr = newval;
}
return old;
}
@@ -214,34 +215,34 @@ signm2signo(VALUE *sig_ptr, int negative, int exit, int *prefix_ptr)
int prefix = 0;
if (RB_SYMBOL_P(vsig)) {
- *sig_ptr = vsig = rb_sym2str(vsig);
+ *sig_ptr = vsig = rb_sym2str(vsig);
}
else if (!RB_TYPE_P(vsig, T_STRING)) {
- VALUE str = rb_check_string_type(vsig);
- if (NIL_P(str)) {
- rb_raise(rb_eArgError, "bad signal type %s",
- rb_obj_classname(vsig));
- }
- *sig_ptr = vsig = str;
+ VALUE str = rb_check_string_type(vsig);
+ if (NIL_P(str)) {
+ rb_raise(rb_eArgError, "bad signal type %s",
+ rb_obj_classname(vsig));
+ }
+ *sig_ptr = vsig = str;
}
rb_must_asciicompat(vsig);
RSTRING_GETMEM(vsig, nm, len);
if (memchr(nm, '\0', len)) {
- rb_raise(rb_eArgError, "signal name with null byte");
+ rb_raise(rb_eArgError, "signal name with null byte");
}
if (len > 0 && nm[0] == '-') {
- if (!negative)
- rb_raise(rb_eArgError, "negative signal name: % "PRIsVALUE, vsig);
- prefix = 1;
+ if (!negative)
+ rb_raise(rb_eArgError, "negative signal name: % "PRIsVALUE, vsig);
+ prefix = 1;
}
else {
- negative = 0;
+ negative = 0;
}
if (len >= prefix + signame_prefix_len) {
if (memcmp(nm + prefix, signame_prefix, signame_prefix_len) == 0)
- prefix += signame_prefix_len;
+ prefix += signame_prefix_len;
}
if (len <= (long)prefix) {
goto unsupported;
@@ -252,10 +253,10 @@ signm2signo(VALUE *sig_ptr, int negative, int exit, int *prefix_ptr)
nm += prefix;
if (nmlen > LONGEST_SIGNAME) goto unsupported;
FOREACH_SIGNAL(sigs, !exit) {
- if (memcmp(sigs->signm, nm, nmlen) == 0 &&
- sigs->signm[nmlen] == '\0') {
- return negative ? -sigs->signo : sigs->signo;
- }
+ if (memcmp(sigs->signm, nm, nmlen) == 0 &&
+ sigs->signm[nmlen] == '\0') {
+ return negative ? -sigs->signo : sigs->signo;
+ }
}
unsupported:
@@ -284,8 +285,8 @@ signo2signm(int no)
const struct signals *sigs;
FOREACH_SIGNAL(sigs, 0) {
- if (sigs->signo == no)
- return sigs->signm;
+ if (sigs->signo == no)
+ return sigs->signm;
}
return 0;
}
@@ -323,10 +324,10 @@ rb_signo2signm(int signo)
{
const char *const signm = signo2signm(signo);
if (signm) {
- return rb_sprintf("SIG%s", signm);
+ return rb_sprintf("SIG%s", signm);
}
else {
- return rb_sprintf("SIG%u", signo);
+ return rb_sprintf("SIG%u", signo);
}
}
@@ -347,29 +348,29 @@ esignal_init(int argc, VALUE *argv, VALUE self)
int signo;
if (argc > 0) {
- sig = rb_check_to_integer(argv[0], "to_int");
- if (!NIL_P(sig)) argnum = 2;
- else sig = argv[0];
+ sig = rb_check_to_integer(argv[0], "to_int");
+ if (!NIL_P(sig)) argnum = 2;
+ else sig = argv[0];
}
rb_check_arity(argc, 1, argnum);
if (argnum == 2) {
- signo = NUM2INT(sig);
- if (signo < 0 || signo > NSIG) {
- rb_raise(rb_eArgError, "invalid signal number (%d)", signo);
- }
- if (argc > 1) {
- sig = argv[1];
- }
- else {
- sig = rb_signo2signm(signo);
- }
+ signo = NUM2INT(sig);
+ if (signo < 0 || signo > NSIG) {
+ rb_raise(rb_eArgError, "invalid signal number (%d)", signo);
+ }
+ if (argc > 1) {
+ sig = argv[1];
+ }
+ else {
+ sig = rb_signo2signm(signo);
+ }
}
else {
- int prefix;
- signo = signm2signo(&sig, FALSE, FALSE, &prefix);
- if (prefix != signame_prefix_len) {
- sig = rb_str_append(rb_str_new_cstr("SIG"), sig);
- }
+ int prefix;
+ signo = signm2signo(&sig, FALSE, FALSE, &prefix);
+ if (prefix != signame_prefix_len) {
+ sig = rb_str_append(rb_str_new_cstr("SIG"), sig);
+ }
}
rb_call_super(1, &sig);
rb_ivar_set(self, id_signo, INT2NUM(signo));
@@ -432,72 +433,72 @@ rb_f_kill(int argc, const VALUE *argv)
rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS);
if (FIXNUM_P(argv[0])) {
- sig = FIX2INT(argv[0]);
+ sig = FIX2INT(argv[0]);
}
else {
- str = argv[0];
- sig = signm2signo(&str, TRUE, FALSE, NULL);
+ str = argv[0];
+ sig = signm2signo(&str, TRUE, FALSE, NULL);
}
if (argc <= 1) return INT2FIX(0);
if (sig < 0) {
- sig = -sig;
- for (i=1; i<argc; i++) {
- if (killpg(NUM2PIDT(argv[i]), sig) < 0)
- rb_sys_fail(0);
- }
+ sig = -sig;
+ for (i=1; i<argc; i++) {
+ if (killpg(NUM2PIDT(argv[i]), sig) < 0)
+ rb_sys_fail(0);
+ }
}
else {
- const rb_pid_t self = (GET_THREAD() == GET_VM()->ractor.main_thread) ? getpid() : -1;
- int wakeup = 0;
-
- for (i=1; i<argc; i++) {
- rb_pid_t pid = NUM2PIDT(argv[i]);
-
- if ((sig != 0) && (self != -1) && (pid == self)) {
- int t;
- /*
- * When target pid is self, many caller assume signal will be
- * delivered immediately and synchronously.
- */
- switch (sig) {
- case SIGSEGV:
+ const rb_pid_t self = (GET_THREAD() == GET_VM()->ractor.main_thread) ? getpid() : -1;
+ int wakeup = 0;
+
+ for (i=1; i<argc; i++) {
+ rb_pid_t pid = NUM2PIDT(argv[i]);
+
+ if ((sig != 0) && (self != -1) && (pid == self)) {
+ int t;
+ /*
+ * When target pid is self, many caller assume signal will be
+ * delivered immediately and synchronously.
+ */
+ switch (sig) {
+ case SIGSEGV:
#ifdef SIGBUS
- case SIGBUS:
+ case SIGBUS:
#endif
#ifdef SIGKILL
- case SIGKILL:
+ case SIGKILL:
#endif
#ifdef SIGILL
- case SIGILL:
+ case SIGILL:
#endif
#ifdef SIGFPE
- case SIGFPE:
+ case SIGFPE:
#endif
#ifdef SIGSTOP
- case SIGSTOP:
-#endif
- kill(pid, sig);
- break;
- default:
- t = signal_ignored(sig);
- if (t) {
- if (t < 0 && kill(pid, sig))
- rb_sys_fail(0);
- break;
- }
- signal_enque(sig);
- wakeup = 1;
- }
- }
- else if (kill(pid, sig) < 0) {
- rb_sys_fail(0);
- }
- }
- if (wakeup) {
- rb_threadptr_check_signal(GET_VM()->ractor.main_thread);
- }
+ case SIGSTOP:
+#endif
+ kill(pid, sig);
+ break;
+ default:
+ t = signal_ignored(sig);
+ if (t) {
+ if (t < 0 && kill(pid, sig))
+ rb_sys_fail(0);
+ break;
+ }
+ signal_enque(sig);
+ wakeup = 1;
+ }
+ }
+ else if (kill(pid, sig) < 0) {
+ rb_sys_fail(0);
+ }
+ }
+ if (wakeup) {
+ rb_threadptr_check_signal(GET_VM()->ractor.main_thread);
+ }
}
rb_thread_execute_interrupts(rb_thread_current());
@@ -542,10 +543,10 @@ rb_sigaltstack_size(void)
#endif
#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
{
- int pagesize;
- pagesize = (int)sysconf(_SC_PAGE_SIZE);
- if (size < pagesize)
- size = pagesize;
+ int pagesize;
+ pagesize = (int)sysconf(_SC_PAGE_SIZE);
+ if (size < pagesize)
+ size = pagesize;
}
#endif
@@ -559,7 +560,7 @@ rb_allocate_sigaltstack(void)
{
void *altstack;
if (!rb_sigaltstack_size_value) {
- rb_sigaltstack_size_value = rb_sigaltstack_size();
+ rb_sigaltstack_size_value = rb_sigaltstack_size();
}
altstack = malloc(rb_sigaltstack_size_value);
if (!altstack) rb_memerror();
@@ -610,42 +611,42 @@ ruby_signal(int signum, sighandler_t handler)
switch (signum) {
#if RUBY_SIGCHLD
case RUBY_SIGCHLD:
- if (handler == SIG_IGN) {
- ruby_nocldwait = 1;
+ if (handler == SIG_IGN) {
+ ruby_nocldwait = 1;
# ifdef USE_SIGALTSTACK
- if (sigact.sa_flags & SA_SIGINFO) {
- sigact.sa_sigaction = (ruby_sigaction_t*)sighandler;
- }
- else {
- sigact.sa_handler = sighandler;
- }
+ if (sigact.sa_flags & SA_SIGINFO) {
+ sigact.sa_sigaction = (ruby_sigaction_t*)sighandler;
+ }
+ else {
+ sigact.sa_handler = sighandler;
+ }
# else
- sigact.sa_handler = handler;
- sigact.sa_flags = 0;
+ sigact.sa_handler = handler;
+ sigact.sa_flags = 0;
# endif
- }
- else {
- ruby_nocldwait = 0;
- }
- break;
+ }
+ else {
+ ruby_nocldwait = 0;
+ }
+ break;
#endif
#if defined(SA_ONSTACK) && defined(USE_SIGALTSTACK)
case SIGSEGV:
#ifdef SIGBUS
case SIGBUS:
#endif
- sigact.sa_flags |= SA_ONSTACK;
- break;
+ sigact.sa_flags |= SA_ONSTACK;
+ break;
#endif
}
(void)VALGRIND_MAKE_MEM_DEFINED(&old, sizeof(old));
if (sigaction(signum, &sigact, &old) < 0) {
- return SIG_ERR;
+ return SIG_ERR;
}
if (old.sa_flags & SA_SIGINFO)
- handler = (sighandler_t)old.sa_sigaction;
+ handler = (sighandler_t)old.sa_sigaction;
else
- handler = old.sa_handler;
+ handler = old.sa_handler;
ASSUME(handler != SIG_ERR);
return handler;
}
@@ -661,8 +662,8 @@ static inline sighandler_t
ruby_signal(int signum, sighandler_t handler)
{
if (signum == SIGKILL) {
- errno = EINVAL;
- return SIG_ERR;
+ errno = EINVAL;
+ return SIG_ERR;
}
return signal(signum, handler);
}
@@ -775,14 +776,14 @@ rb_get_next_signal(void)
int i, sig = 0;
if (signal_buff.size != 0) {
- for (i=1; i<RUBY_NSIG; i++) {
- if (signal_buff.cnt[i] > 0) {
- ATOMIC_DEC(signal_buff.cnt[i]);
- ATOMIC_DEC(signal_buff.size);
- sig = i;
- break;
- }
- }
+ for (i=1; i<RUBY_NSIG; i++) {
+ if (signal_buff.cnt[i] > 0) {
+ ATOMIC_DEC(signal_buff.cnt[i]);
+ ATOMIC_DEC(signal_buff.size);
+ sig = i;
+ break;
+ }
+ }
}
return sig;
}
@@ -822,7 +823,7 @@ reset_sigmask(int sig)
sigemptyset(&mask);
sigaddset(&mask, sig);
if (ruby_sigunmask(SIG_UNBLOCK, &mask, NULL)) {
- rb_bug_errno(STRINGIZE(ruby_sigunmask)":unblock", errno);
+ rb_bug_errno(STRINGIZE(ruby_sigunmask)":unblock", errno);
}
#endif
}
@@ -879,18 +880,18 @@ check_stack_overflow(int sig, const uintptr_t addr, const ucontext_t *ctx)
* the fault page can be the next. */
if (sp_page == fault_page || sp_page == fault_page + 1 ||
(sp_page <= fault_page && fault_page <= bp_page)) {
- rb_execution_context_t *ec = GET_EC();
- int crit = FALSE;
- int uplevel = roomof(pagesize, sizeof(*ec->tag)) / 2; /* XXX: heuristic */
- while ((uintptr_t)ec->tag->buf / pagesize <= fault_page + 1) {
- /* drop the last tag if it is close to the fault,
- * otherwise it can cause stack overflow again at the same
- * place. */
- if ((crit = (!ec->tag->prev || !--uplevel)) != FALSE) break;
- ec->tag = ec->tag->prev;
- }
- reset_sigmask(sig);
- rb_ec_stack_overflow(ec, crit);
+ rb_execution_context_t *ec = GET_EC();
+ int crit = FALSE;
+ int uplevel = roomof(pagesize, sizeof(*ec->tag)) / 2; /* XXX: heuristic */
+ while ((uintptr_t)ec->tag->buf / pagesize <= fault_page + 1) {
+ /* drop the last tag if it is close to the fault,
+ * otherwise it can cause stack overflow again at the same
+ * place. */
+ if ((crit = (!ec->tag->prev || !--uplevel)) != FALSE) break;
+ ec->tag = ec->tag->prev;
+ }
+ reset_sigmask(sig);
+ rb_ec_stack_overflow(ec, crit);
}
}
# else
@@ -900,8 +901,8 @@ check_stack_overflow(int sig, const void *addr)
int ruby_stack_overflowed_p(const rb_thread_t *, const void *);
rb_thread_t *th = GET_THREAD();
if (ruby_stack_overflowed_p(th, addr)) {
- reset_sigmask(sig);
- rb_ec_stack_overflow(th->ec, FALSE);
+ reset_sigmask(sig);
+ rb_ec_stack_overflow(th->ec, FALSE);
}
}
# endif
@@ -1004,30 +1005,30 @@ check_reserved_signal_(const char *name, size_t name_len)
const char *prev = ATOMIC_PTR_EXCHANGE(received_signal, name);
if (prev) {
- ssize_t RB_UNUSED_VAR(err);
-#define NOZ(name, str) name[sizeof(str)-1] = str
- static const char NOZ(msg1, " received in ");
- static const char NOZ(msg2, " handler\n");
+ ssize_t RB_UNUSED_VAR(err);
+#define NOZ(name, str) RBIMPL_ATTR_NONSTRING() name[sizeof(str)-1] = str
+ static const char NOZ(msg1, " received in ");
+ static const char NOZ(msg2, " handler\n");
#ifdef HAVE_WRITEV
- struct iovec iov[4];
-
- iov[0].iov_base = (void *)name;
- iov[0].iov_len = name_len;
- iov[1].iov_base = (void *)msg1;
- iov[1].iov_len = sizeof(msg1);
- iov[2].iov_base = (void *)prev;
- iov[2].iov_len = strlen(prev);
- iov[3].iov_base = (void *)msg2;
- iov[3].iov_len = sizeof(msg2);
- err = writev(2, iov, 4);
+ struct iovec iov[4];
+
+ iov[0].iov_base = (void *)name;
+ iov[0].iov_len = name_len;
+ iov[1].iov_base = (void *)msg1;
+ iov[1].iov_len = sizeof(msg1);
+ iov[2].iov_base = (void *)prev;
+ iov[2].iov_len = strlen(prev);
+ iov[3].iov_base = (void *)msg2;
+ iov[3].iov_len = sizeof(msg2);
+ err = writev(2, iov, 4);
#else
- err = write(2, name, name_len);
- err = write(2, msg1, sizeof(msg1));
- err = write(2, prev, strlen(prev));
- err = write(2, msg2, sizeof(msg2));
+ err = write(2, name, name_len);
+ err = write(2, msg1, sizeof(msg1));
+ err = write(2, prev, strlen(prev));
+ err = write(2, msg2, sizeof(msg2));
#endif
- ruby_abort();
+ ruby_abort();
}
ruby_disable_gc = 1;
@@ -1055,12 +1056,12 @@ signal_exec(VALUE cmd, int sig)
* 3. rb_signal_exec runs on queued signal
*/
if (IMMEDIATE_P(cmd))
- return FALSE;
+ return FALSE;
ec->interrupt_mask |= TRAP_INTERRUPT_MASK;
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- VALUE signum = INT2NUM(sig);
+ VALUE signum = INT2NUM(sig);
rb_eval_cmd_kw(cmd, rb_ary_new3(1, signum), RB_NO_KEYWORDS);
}
EC_POP_TAG();
@@ -1068,8 +1069,8 @@ signal_exec(VALUE cmd, int sig)
ec->interrupt_mask = old_interrupt_mask;
if (state) {
- /* XXX: should be replaced with rb_threadptr_pending_interrupt_enque() */
- EC_JUMP_TAG(ec, state);
+ /* XXX: should be replaced with rb_threadptr_pending_interrupt_enque() */
+ EC_JUMP_TAG(ec, state);
}
return TRUE;
}
@@ -1080,7 +1081,7 @@ rb_vm_trap_exit(rb_vm_t *vm)
VALUE trap_exit = vm->trap_list.cmd[0];
if (trap_exit) {
- vm->trap_list.cmd[0] = 0;
+ vm->trap_list.cmd[0] = 0;
signal_exec(trap_exit, 0);
}
}
@@ -1103,34 +1104,34 @@ rb_signal_exec(rb_thread_t *th, int sig)
VALUE cmd = vm->trap_list.cmd[sig];
if (cmd == 0) {
- switch (sig) {
- case SIGINT:
- rb_interrupt();
- break;
+ switch (sig) {
+ case SIGINT:
+ rb_interrupt();
+ break;
#ifdef SIGHUP
- case SIGHUP:
+ case SIGHUP:
#endif
#ifdef SIGQUIT
- case SIGQUIT:
+ case SIGQUIT:
#endif
#ifdef SIGTERM
- case SIGTERM:
+ case SIGTERM:
#endif
#ifdef SIGALRM
- case SIGALRM:
+ case SIGALRM:
#endif
#ifdef SIGUSR1
- case SIGUSR1:
+ case SIGUSR1:
#endif
#ifdef SIGUSR2
- case SIGUSR2:
+ case SIGUSR2:
#endif
- rb_threadptr_signal_raise(th, sig);
- break;
- }
+ rb_threadptr_signal_raise(th, sig);
+ break;
+ }
}
- else if (cmd == Qundef) {
- rb_threadptr_signal_exit(th);
+ else if (UNDEF_P(cmd)) {
+ rb_threadptr_signal_exit(th);
}
else {
return signal_exec(cmd, sig);
@@ -1202,21 +1203,21 @@ trap_handler(VALUE *cmd, int sig)
VALUE command;
if (NIL_P(*cmd)) {
- func = SIG_IGN;
+ func = SIG_IGN;
}
else {
- command = rb_check_string_type(*cmd);
- if (NIL_P(command) && SYMBOL_P(*cmd)) {
- command = rb_sym2str(*cmd);
- if (!command) rb_raise(rb_eArgError, "bad handler");
- }
- if (!NIL_P(command)) {
- const char *cptr;
- long len;
+ command = rb_check_string_type(*cmd);
+ if (NIL_P(command) && SYMBOL_P(*cmd)) {
+ command = rb_sym2str(*cmd);
+ if (!command) rb_raise(rb_eArgError, "bad handler");
+ }
+ if (!NIL_P(command)) {
+ const char *cptr;
+ long len;
StringValue(command);
- *cmd = command;
- RSTRING_GETMEM(command, cptr, len);
- switch (len) {
+ *cmd = command;
+ RSTRING_GETMEM(command, cptr, len);
+ switch (len) {
sig_ign:
func = SIG_IGN;
*cmd = Qtrue;
@@ -1225,46 +1226,46 @@ trap_handler(VALUE *cmd, int sig)
func = default_handler(sig);
*cmd = 0;
break;
- case 0:
+ case 0:
goto sig_ign;
- break;
+ break;
case 14:
- if (memcmp(cptr, "SYSTEM_DEFAULT", 14) == 0) {
+ if (memcmp(cptr, "SYSTEM_DEFAULT", 14) == 0) {
if (sig == RUBY_SIGCHLD) {
goto sig_dfl;
}
func = SIG_DFL;
*cmd = 0;
- }
+ }
break;
- case 7:
- if (memcmp(cptr, "SIG_IGN", 7) == 0) {
+ case 7:
+ if (memcmp(cptr, "SIG_IGN", 7) == 0) {
goto sig_ign;
- }
- else if (memcmp(cptr, "SIG_DFL", 7) == 0) {
+ }
+ else if (memcmp(cptr, "SIG_DFL", 7) == 0) {
goto sig_dfl;
- }
- else if (memcmp(cptr, "DEFAULT", 7) == 0) {
+ }
+ else if (memcmp(cptr, "DEFAULT", 7) == 0) {
goto sig_dfl;
- }
- break;
- case 6:
- if (memcmp(cptr, "IGNORE", 6) == 0) {
+ }
+ break;
+ case 6:
+ if (memcmp(cptr, "IGNORE", 6) == 0) {
goto sig_ign;
- }
- break;
- case 4:
- if (memcmp(cptr, "EXIT", 4) == 0) {
- *cmd = Qundef;
- }
- break;
- }
- }
- else {
- rb_proc_t *proc;
- GetProcPtr(*cmd, proc);
- (void)proc;
- }
+ }
+ break;
+ case 4:
+ if (memcmp(cptr, "EXIT", 4) == 0) {
+ *cmd = Qundef;
+ }
+ break;
+ }
+ }
+ else {
+ rb_proc_t *proc;
+ GetProcPtr(*cmd, proc);
+ (void)proc;
+ }
}
return func;
@@ -1276,13 +1277,13 @@ trap_signm(VALUE vsig)
int sig = -1;
if (FIXNUM_P(vsig)) {
- sig = FIX2INT(vsig);
- if (sig < 0 || sig >= NSIG) {
- rb_raise(rb_eArgError, "invalid signal number (%d)", sig);
- }
+ sig = FIX2INT(vsig);
+ if (sig < 0 || sig >= NSIG) {
+ rb_raise(rb_eArgError, "invalid signal number (%d)", sig);
+ }
}
else {
- sig = signm2signo(&vsig, FALSE, TRUE, NULL);
+ sig = signm2signo(&vsig, FALSE, TRUE, NULL);
}
return sig;
}
@@ -1300,26 +1301,26 @@ trap(int sig, sighandler_t func, VALUE command)
* RUBY_VM_CHECK_INTS().
*/
if (sig == 0) {
- oldfunc = SIG_ERR;
+ oldfunc = SIG_ERR;
}
else {
- oldfunc = ruby_signal(sig, func);
- if (oldfunc == SIG_ERR) rb_sys_fail_str(rb_signo2signm(sig));
+ oldfunc = ruby_signal(sig, func);
+ if (oldfunc == SIG_ERR) rb_sys_fail_str(rb_signo2signm(sig));
}
oldcmd = vm->trap_list.cmd[sig];
switch (oldcmd) {
case 0:
case Qtrue:
- if (oldfunc == SIG_IGN) oldcmd = rb_str_new2("IGNORE");
+ if (oldfunc == SIG_IGN) oldcmd = rb_str_new2("IGNORE");
else if (oldfunc == SIG_DFL) oldcmd = rb_str_new2("SYSTEM_DEFAULT");
- else if (oldfunc == sighandler) oldcmd = rb_str_new2("DEFAULT");
- else oldcmd = Qnil;
- break;
+ else if (oldfunc == sighandler) oldcmd = rb_str_new2("DEFAULT");
+ else oldcmd = Qnil;
+ break;
case Qnil:
- break;
+ break;
case Qundef:
- oldcmd = rb_str_new2("EXIT");
- break;
+ oldcmd = rb_str_new2("EXIT");
+ break;
}
ACCESS_ONCE(VALUE, vm->trap_list.cmd[sig]) = command;
@@ -1333,25 +1334,25 @@ reserved_signal_p(int signo)
/* Synchronous signal can't deliver to main thread */
#ifdef SIGSEGV
if (signo == SIGSEGV)
- return 1;
+ return 1;
#endif
#ifdef SIGBUS
if (signo == SIGBUS)
- return 1;
+ return 1;
#endif
#ifdef SIGILL
if (signo == SIGILL)
- return 1;
+ return 1;
#endif
#ifdef SIGFPE
if (signo == SIGFPE)
- return 1;
+ return 1;
#endif
/* used ubf internal see thread_pthread.c. */
#ifdef SIGVTALRM
if (signo == SIGVTALRM)
- return 1;
+ return 1;
#endif
return 0;
@@ -1407,12 +1408,12 @@ sig_trap(int argc, VALUE *argv, VALUE _)
}
if (argc == 1) {
- cmd = rb_block_proc();
- func = sighandler;
+ cmd = rb_block_proc();
+ func = sighandler;
}
else {
- cmd = argv[1];
- func = trap_handler(&cmd, sig);
+ cmd = argv[1];
+ func = trap_handler(&cmd, sig);
}
if (rb_obj_is_proc(cmd) &&
@@ -1439,16 +1440,16 @@ sig_list(VALUE _)
const struct signals *sigs;
FOREACH_SIGNAL(sigs, 0) {
- rb_hash_aset(h, rb_fstring_cstr(sigs->signm), INT2FIX(sigs->signo));
+ rb_hash_aset(h, rb_fstring_cstr(sigs->signm), INT2FIX(sigs->signo));
}
return h;
}
#define INSTALL_SIGHANDLER(cond, signame, signum) do { \
- static const char failed[] = "failed to install "signame" handler"; \
- if (!(cond)) break; \
- if (reserved_signal_p(signum)) rb_bug(failed); \
- perror(failed); \
+ static const char failed[] = "failed to install "signame" handler"; \
+ if (!(cond)) break; \
+ if (reserved_signal_p(signum)) rb_bug(failed); \
+ perror(failed); \
} while (0)
static int
install_sighandler_core(int signum, sighandler_t handler, sighandler_t *old_handler)
@@ -1500,7 +1501,7 @@ ruby_sig_finalize(void)
oldfunc = ruby_signal(SIGINT, SIG_IGN);
if (oldfunc == sighandler) {
- ruby_signal(SIGINT, SIG_DFL);
+ ruby_signal(SIGINT, SIG_DFL);
}
}
@@ -1584,14 +1585,14 @@ Init_signal(void)
if (!ruby_enable_coredump) {
#ifdef SIGBUS
- force_install_sighandler(SIGBUS, (sighandler_t)sigbus, &default_sigbus_handler);
+ force_install_sighandler(SIGBUS, (sighandler_t)sigbus, &default_sigbus_handler);
#endif
#ifdef SIGILL
- force_install_sighandler(SIGILL, (sighandler_t)sigill, &default_sigill_handler);
+ force_install_sighandler(SIGILL, (sighandler_t)sigill, &default_sigill_handler);
#endif
#ifdef SIGSEGV
- RB_ALTSTACK_INIT(GET_VM()->main_altstack, rb_allocate_sigaltstack());
- force_install_sighandler(SIGSEGV, (sighandler_t)sigsegv, &default_sigsegv_handler);
+ RB_ALTSTACK_INIT(GET_VM()->main_altstack, rb_allocate_sigaltstack());
+ force_install_sighandler(SIGSEGV, (sighandler_t)sigsegv, &default_sigsegv_handler);
#endif
}
#ifdef SIGPIPE
diff --git a/siphash.c b/siphash.c
index 091376747f..62de622778 100644
--- a/siphash.c
+++ b/siphash.c
@@ -34,10 +34,11 @@
#error "Only strictly little or big endian supported"
#endif
+/* __POWERPC__ added to accommodate Darwin case. */
#ifndef UNALIGNED_WORD_ACCESS
# if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || \
- defined(__powerpc64__) || defined(__aarch64__) || \
+ defined(__powerpc64__) || defined(__POWERPC__) || defined(__aarch64__) || \
defined(__mc68020__)
# define UNALIGNED_WORD_ACCESS 1
# endif
@@ -96,7 +97,7 @@ u64to8_le(uint8_t *p, uint64_t v)
}
#define ROTL64_TO(v, s) ((s) > 32 ? rotl64_swap(rotl64_to(&(v), (s) - 32)) : \
- (s) == 32 ? rotl64_swap(&(v)) : rotl64_to(&(v), (s)))
+ (s) == 32 ? rotl64_swap(&(v)) : rotl64_to(&(v), (s)))
static inline uint64_t *
rotl64_to(uint64_t *v, unsigned int s)
{
@@ -139,6 +140,9 @@ xor64_to(uint64_t *v, const uint64_t s)
#endif
static const union {
+#if defined(__has_attribute) && __has_attribute(nonstring)
+ __attribute__((nonstring))
+#endif
char bin[32];
uint64_t u64[4];
} sip_init_state_bin = {"uespemos""modnarod""arenegyl""setybdet"};
@@ -188,9 +192,9 @@ int_sip_dump(sip_state *state)
for (v = 0; v < 4; v++) {
#ifdef HAVE_UINT64_T
- printf("v%d: %" PRIx64 "\n", v, state->v[v]);
+ printf("v%d: %" PRIx64 "\n", v, state->v[v]);
#else
- printf("v%d: %" PRIx32 "%.8" PRIx32 "\n", v, state->v[v].hi, state->v[v].lo);
+ printf("v%d: %" PRIx32 "%.8" PRIx32 "\n", v, state->v[v].hi, state->v[v].lo);
#endif
}
}
@@ -215,7 +219,7 @@ int_sip_round(sip_state *state, int n)
int i;
for (i = 0; i < n; i++) {
- SIP_COMPRESS(state->v[0], state->v[1], state->v[2], state->v[3]);
+ SIP_COMPRESS(state->v[0], state->v[1], state->v[2], state->v[3]);
}
}
@@ -249,8 +253,8 @@ int_sip_post_update(sip_state *state, const uint8_t *data, size_t len)
{
uint8_t r = len % sizeof(uint64_t);
if (r) {
- memcpy(state->buf, data + len - r, r);
- state->buflen = r;
+ memcpy(state->buf, data + len - r, r);
+ state->buflen = r;
}
}
@@ -269,16 +273,16 @@ int_sip_update(sip_state *state, const uint8_t *data, size_t len)
#if BYTE_ORDER == LITTLE_ENDIAN
while (data64 != end) {
- int_sip_update_block(state, *data64++);
+ int_sip_update_block(state, *data64++);
}
#elif BYTE_ORDER == BIG_ENDIAN
{
- uint64_t m;
- uint8_t *data8 = data;
- for (; data8 != (uint8_t *) end; data8 += sizeof(uint64_t)) {
- m = U8TO64_LE(data8);
- int_sip_update_block(state, m);
- }
+ uint64_t m;
+ uint8_t *data8 = data;
+ for (; data8 != (uint8_t *) end; data8 += sizeof(uint64_t)) {
+ m = U8TO64_LE(data8);
+ int_sip_update_block(state, m);
+ }
}
#endif
@@ -291,7 +295,7 @@ int_sip_pad_final_block(sip_state *state)
int i;
/* pad with 0's and finalize with msg_len mod 256 */
for (i = state->buflen; i < sizeof(uint64_t); i++) {
- state->buf[i] = 0x00;
+ state->buf[i] = 0x00;
}
state->buf[sizeof(uint64_t) - 1] = state->msglen_byte;
}
@@ -420,14 +424,14 @@ sip_hash13(const uint8_t key[16], const uint8_t *data, size_t len)
{
uint64_t *data64 = (uint64_t *)data;
while (data64 != (uint64_t *) end) {
- m = *data64++;
- SIP_ROUND(m, v0, v1, v2, v3);
+ m = *data64++;
+ SIP_ROUND(m, v0, v1, v2, v3);
}
}
#else
for (; data != end; data += sizeof(uint64_t)) {
- m = U8TO64_LE(data);
- SIP_ROUND(m, v0, v1, v2, v3);
+ m = U8TO64_LE(data);
+ SIP_ROUND(m, v0, v1, v2, v3);
}
#endif
@@ -438,40 +442,40 @@ sip_hash13(const uint8_t key[16], const uint8_t *data, size_t len)
last.hi = len << 24;
last.lo = 0;
#define OR_BYTE(n) do { \
- if (n >= 4) \
- last.hi |= ((uint32_t) end[n]) << ((n) >= 4 ? (n) * 8 - 32 : 0); \
- else \
- last.lo |= ((uint32_t) end[n]) << ((n) >= 4 ? 0 : (n) * 8); \
+ if (n >= 4) \
+ last.hi |= ((uint32_t) end[n]) << ((n) >= 4 ? (n) * 8 - 32 : 0); \
+ else \
+ last.lo |= ((uint32_t) end[n]) << ((n) >= 4 ? 0 : (n) * 8); \
} while (0)
#endif
switch (len % sizeof(uint64_t)) {
- case 7:
- OR_BYTE(6);
- case 6:
- OR_BYTE(5);
- case 5:
- OR_BYTE(4);
- case 4:
+ case 7:
+ OR_BYTE(6);
+ case 6:
+ OR_BYTE(5);
+ case 5:
+ OR_BYTE(4);
+ case 4:
#if BYTE_ORDER == LITTLE_ENDIAN && UNALIGNED_WORD_ACCESS
#ifdef HAVE_UINT64_T
- last |= (uint64_t) ((uint32_t *) end)[0];
+ last |= (uint64_t) ((uint32_t *) end)[0];
#else
- last.lo |= ((uint32_t *) end)[0];
+ last.lo |= ((uint32_t *) end)[0];
#endif
- break;
+ break;
#else
- OR_BYTE(3);
+ OR_BYTE(3);
#endif
- case 3:
- OR_BYTE(2);
- case 2:
- OR_BYTE(1);
- case 1:
- OR_BYTE(0);
- break;
- case 0:
- break;
+ case 3:
+ OR_BYTE(2);
+ case 2:
+ OR_BYTE(1);
+ case 1:
+ OR_BYTE(0);
+ break;
+ case 0:
+ break;
}
SIP_ROUND(last, v0, v1, v2, v3);
diff --git a/spec/README.md b/spec/README.md
index 6b82f8f06a..4fcf090759 100644
--- a/spec/README.md
+++ b/spec/README.md
@@ -10,6 +10,14 @@ To run rspec for bundler:
make test-bundler
```
+or run rspec with parallel execution:
+
+```bash
+make test-bundler-parallel
+```
+
+If you specify `BUNDLER_SPECS=foo/bar_spec.rb` then only `spec/bundler/foo/bar_spec.rb` will be run.
+
# spec/ruby
ruby/spec (https://github.com/ruby/spec/) is
@@ -138,3 +146,15 @@ end
```
For more details, see `spec/ruby/CONTRIBUTING.md`.
+
+# spec/syntax_suggest
+
+## Running spec/syntax_suggest
+
+To run rspec for syntax_suggest:
+
+```bash
+make test-syntax-suggest
+```
+
+If you specify `SYNTAX_SUGGEST_SPECS=foo/bar_spec.rb` then only `spec/syntax_suggest/foo/bar_spec.rb` will be run.
diff --git a/spec/bundler/bundler/bundler_spec.rb b/spec/bundler/bundler/bundler_spec.rb
index aeadcf9720..54fedc8568 100644
--- a/spec/bundler/bundler/bundler_spec.rb
+++ b/spec/bundler/bundler/bundler_spec.rb
@@ -4,6 +4,71 @@ require "bundler"
require "tmpdir"
RSpec.describe Bundler do
+ describe "#load_marshal" do
+ it "is a private method and raises an error" do
+ data = Marshal.dump(Bundler)
+ expect { Bundler.load_marshal(data) }.to raise_error(NoMethodError, /private method `load_marshal' called/)
+ end
+
+ it "loads any data" do
+ data = Marshal.dump(Bundler)
+ expect(Bundler.send(:load_marshal, data)).to eq(Bundler)
+ end
+ end
+
+ describe "#safe_load_marshal" do
+ it "fails on unexpected class" do
+ data = Marshal.dump(Bundler)
+ expect { Bundler.safe_load_marshal(data) }.to raise_error(Bundler::MarshalError)
+ end
+
+ it "loads simple structure" do
+ simple_structure = { "name" => [:abc] }
+ data = Marshal.dump(simple_structure)
+ expect(Bundler.safe_load_marshal(data)).to eq(simple_structure)
+ end
+
+ it "loads Gem::Specification" do
+ gem_spec = Gem::Specification.new do |s|
+ s.name = "bundler"
+ s.version = Gem::Version.new("2.4.7")
+ s.installed_by_version = Gem::Version.new("0")
+ s.authors = ["André Arko",
+ "Samuel Giddins",
+ "Colby Swandale",
+ "Hiroshi Shibata",
+ "David Rodríguez",
+ "Grey Baker",
+ "Stephanie Morillo",
+ "Chris Morris",
+ "James Wen",
+ "Tim Moore",
+ "André Medeiros",
+ "Jessica Lynn Suttles",
+ "Terence Lee",
+ "Carl Lerche",
+ "Yehuda Katz"]
+ s.date = Time.utc(2023, 2, 15)
+ s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably"
+ s.email = ["team@bundler.io"]
+ s.homepage = "https://bundler.io"
+ s.metadata = { "bug_tracker_uri" => "https://github.com/rubygems/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler",
+ "changelog_uri" => "https://github.com/rubygems/rubygems/blob/master/bundler/CHANGELOG.md",
+ "homepage_uri" => "https://bundler.io/",
+ "source_code_uri" => "https://github.com/rubygems/rubygems/tree/master/bundler" }
+ s.require_paths = ["lib"]
+ s.required_ruby_version = Gem::Requirement.new([">= 2.6.0"])
+ s.required_rubygems_version = Gem::Requirement.new([">= 3.0.1"])
+ s.rubygems_version = "3.4.7"
+ s.specification_version = 4
+ s.summary = "The best way to manage your application's dependencies"
+ s.license = false
+ end
+ data = Marshal.dump(gem_spec)
+ expect(Bundler.safe_load_marshal(data)).to eq(gem_spec)
+ end
+ end
+
describe "#load_gemspec_uncached" do
let(:app_gemspec_path) { tmp("test.gemspec") }
subject { Bundler.load_gemspec_uncached(app_gemspec_path) }
@@ -167,9 +232,9 @@ RSpec.describe Bundler do
allow(::Bundler::FileUtils).to receive(:remove_entry_secure).and_raise(ArgumentError)
allow(File).to receive(:world_writable?).and_return(true)
message = <<EOF
-It is a security vulnerability to allow your home directory to be world-writable, and bundler can not continue.
+It is a security vulnerability to allow your home directory to be world-writable, and bundler cannot continue.
You should probably consider fixing this issue by running `chmod o-w ~` on *nix.
-Please refer to https://ruby-doc.org/stdlib-2.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
+Please refer to https://ruby-doc.org/stdlib-3.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
EOF
expect(bundler_ui).to receive(:warn).with(message)
expect { Bundler.send(:rm_rf, bundled_app) }.to raise_error(Bundler::PathError)
@@ -189,22 +254,6 @@ EOF
Bundler.mkdir_p(bundled_app.join("foo", "bar"))
expect(bundled_app.join("foo", "bar")).to exist
end
-
- context "when mkdir_p requires sudo" do
- it "creates a new folder using sudo" do
- expect(Bundler).to receive(:requires_sudo?).and_return(true)
- expect(Bundler).to receive(:sudo).and_return true
- Bundler.mkdir_p(bundled_app.join("foo"))
- end
- end
-
- context "with :no_sudo option" do
- it "forces mkdir_p to not use sudo" do
- expect(Bundler).to receive(:requires_sudo?).and_return(true)
- expect(Bundler).to_not receive(:sudo)
- Bundler.mkdir_p(bundled_app.join("foo"), :no_sudo => true)
- end
- end
end
describe "#user_home" do
@@ -268,118 +317,6 @@ EOF
end
end
- describe "#requires_sudo?" do
- let!(:tmpdir) { Dir.mktmpdir }
- let(:bundle_path) { Pathname("#{tmpdir}/bundle") }
-
- def clear_cached_requires_sudo
- return unless Bundler.instance_variable_defined?(:@requires_sudo_ran)
- Bundler.remove_instance_variable(:@requires_sudo_ran)
- Bundler.remove_instance_variable(:@requires_sudo)
- end
-
- before do
- clear_cached_requires_sudo
- allow(Bundler).to receive(:which).with("sudo").and_return("/usr/bin/sudo")
- allow(Bundler).to receive(:bundle_path).and_return(bundle_path)
- end
-
- after do
- FileUtils.rm_rf(tmpdir)
- clear_cached_requires_sudo
- end
-
- subject { Bundler.requires_sudo? }
-
- context "bundle_path doesn't exist" do
- it { should be false }
-
- context "and parent dir can't be written" do
- before do
- FileUtils.chmod(0o500, tmpdir)
- end
-
- it { should be true }
- end
-
- context "with unwritable files in a parent dir" do
- # Regression test for https://github.com/rubygems/bundler/pull/6316
- # It doesn't matter if there are other unwritable files so long as
- # bundle_path can be created
- before do
- file = File.join(tmpdir, "unrelated_file")
- FileUtils.touch(file)
- FileUtils.chmod(0o400, file)
- end
-
- it { should be false }
- end
- end
-
- context "bundle_path exists" do
- before do
- FileUtils.mkdir_p(bundle_path)
- end
-
- it { should be false }
-
- context "and is unwritable" do
- before do
- FileUtils.chmod(0o500, bundle_path)
- end
-
- it { should be true }
- end
- end
-
- context "path writability" do
- before do
- FileUtils.mkdir_p("tmp/vendor/bundle")
- FileUtils.mkdir_p("tmp/vendor/bin_dir")
- end
- after do
- FileUtils.rm_rf("tmp/vendor/bundle")
- FileUtils.rm_rf("tmp/vendor/bin_dir")
- end
- context "writable paths" do
- it "should return false and display nothing" do
- allow(Bundler).to receive(:bundle_path).and_return(Pathname("tmp/vendor/bundle"))
- expect(Bundler.ui).to_not receive(:warn)
- expect(Bundler.requires_sudo?).to eq(false)
- end
- end
- context "unwritable paths" do
- before do
- FileUtils.touch("tmp/vendor/bundle/unwritable1.txt")
- FileUtils.touch("tmp/vendor/bundle/unwritable2.txt")
- FileUtils.touch("tmp/vendor/bin_dir/unwritable3.txt")
- FileUtils.chmod(0o400, "tmp/vendor/bundle/unwritable1.txt")
- FileUtils.chmod(0o400, "tmp/vendor/bundle/unwritable2.txt")
- FileUtils.chmod(0o400, "tmp/vendor/bin_dir/unwritable3.txt")
- end
- it "should return true and display warn message" do
- allow(Bundler).to receive(:bundle_path).and_return(Pathname("tmp/vendor/bundle"))
- bin_dir = Pathname("tmp/vendor/bin_dir/")
-
- # allow File#writable? to be called with args other than the stubbed on below
- allow(File).to receive(:writable?).and_call_original
-
- # fake make the directory unwritable
- allow(File).to receive(:writable?).with(bin_dir).and_return(false)
- allow(Bundler).to receive(:system_bindir).and_return(Pathname("tmp/vendor/bin_dir/"))
- message = <<-MESSAGE.chomp
-Following files may not be writable, so sudo is needed:
- tmp/vendor/bin_dir/
- tmp/vendor/bundle/unwritable1.txt
- tmp/vendor/bundle/unwritable2.txt
-MESSAGE
- expect(Bundler.ui).to receive(:warn).with(message)
- expect(Bundler.requires_sudo?).to eq(true)
- end
- end
- end
- end
-
context "user cache dir" do
let(:home_path) { Pathname.new(ENV["HOME"]) }
diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb
index 28ade90138..b752cd7e70 100644
--- a/spec/bundler/bundler/cli_spec.rb
+++ b/spec/bundler/bundler/cli_spec.rb
@@ -129,6 +129,52 @@ RSpec.describe "bundle executable" do
end
end
+ describe "bundle outdated" do
+ let(:run_command) do
+ bundle "install"
+
+ bundle "outdated #{flags}", :raise_on_error => false
+ end
+
+ before do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack", '0.9.1'
+ G
+ end
+
+ context "with --groups flag" do
+ let(:flags) { "--groups" }
+
+ it "prints a message when there are outdated gems" do
+ run_command
+
+ expect(out).to include("Gem Current Latest Requested Groups")
+ expect(out).to include("rack 0.9.1 1.0.0 = 0.9.1 default")
+ end
+ end
+
+ context "with --parseable" do
+ let(:flags) { "--parseable" }
+
+ it "prints a message when there are outdated gems" do
+ run_command
+
+ expect(out).to include("rack (newest 1.0.0, installed 0.9.1, requested = 0.9.1)")
+ end
+ end
+
+ context "with --groups and --parseable" do
+ let(:flags) { "--groups --parseable" }
+
+ it "prints a simplified message when there are outdated gems" do
+ run_command
+
+ expect(out).to include("rack (newest 1.0.0, installed 0.9.1, requested = 0.9.1)")
+ end
+ end
+ end
+
describe "printing the outdated warning" do
shared_examples_for "no warning" do
it "prints no warning" do
diff --git a/spec/bundler/bundler/definition_spec.rb b/spec/bundler/bundler/definition_spec.rb
index 13688c2b3d..59b958ae42 100644
--- a/spec/bundler/bundler/definition_spec.rb
+++ b/spec/bundler/bundler/definition_spec.rb
@@ -82,6 +82,24 @@ RSpec.describe Bundler::Definition do
G
end
+ it "with an explicit update" do
+ build_repo4 do
+ build_gem("ffi", "1.9.23") {|s| s.platform = "java" }
+ build_gem("ffi", "1.9.23")
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "ffi"
+ G
+
+ bundle "lock --add-platform java"
+
+ bundle "update ffi", :env => { "DEBUG" => "1" }
+
+ expect(out).to match(/because bundler is unlocking gems: \(ffi\)/)
+ end
+
it "for a path gem with deps and no changes" do
build_lib "foo", "1.0", :path => lib_path("foo") do |s|
s.add_dependency "rack", "1.0"
@@ -136,7 +154,7 @@ RSpec.describe Bundler::Definition do
only_java (1.1-java)
PLATFORMS
- #{lockfile_platforms_for(["java", specific_local_platform])}
+ #{lockfile_platforms("java")}
DEPENDENCIES
only_java
@@ -175,31 +193,6 @@ RSpec.describe Bundler::Definition do
describe "initialize" do
context "gem version promoter" do
- context "with lockfile" do
- before do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "foo"
- G
-
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- end
-
- it "should get a locked specs list when updating all" do
- definition = Bundler::Definition.new(bundled_app_lock, [], Bundler::SourceList.new, true)
- locked_specs = definition.gem_version_promoter.locked_specs
- expect(locked_specs.to_a.map(&:name)).to eq ["foo"]
- expect(definition.instance_variable_get("@locked_specs").empty?).to eq true
- end
- end
-
- context "without gemfile or lockfile" do
- it "should not attempt to parse empty lockfile contents" do
- definition = Bundler::Definition.new(nil, [], mock_source_list, true)
- expect(definition.gem_version_promoter.locked_specs.to_a).to eq []
- end
- end
-
context "eager unlock" do
let(:source_list) do
Bundler::SourceList.new.tap do |source_list|
diff --git a/spec/bundler/bundler/dep_proxy_spec.rb b/spec/bundler/bundler/dep_proxy_spec.rb
deleted file mode 100644
index 8d02a33725..0000000000
--- a/spec/bundler/bundler/dep_proxy_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe Bundler::DepProxy do
- let(:dep) { Bundler::Dependency.new("rake", ">= 0") }
- subject { described_class.get_proxy(dep, Gem::Platform::RUBY) }
- let(:same) { subject }
- let(:other) { described_class.get_proxy(dep, Gem::Platform::RUBY) }
- let(:different) { described_class.get_proxy(dep, Gem::Platform::JAVA) }
-
- describe "#eql?" do
- it { expect(subject.eql?(same)).to be true }
- it { expect(subject.eql?(other)).to be true }
- it { expect(subject.eql?(different)).to be false }
- it { expect(subject.eql?(nil)).to be false }
- it { expect(subject.eql?("foobar")).to be false }
- end
-
- describe "must use factory methods" do
- it { expect { described_class.new(dep, Gem::Platform::RUBY) }.to raise_error NoMethodError }
- it { expect { subject.dup }.to raise_error NoMethodError }
- it { expect { subject.clone }.to raise_error NoMethodError }
- end
-
- describe "frozen" do
- if Gem.ruby_version >= Gem::Version.new("2.5.0")
- error = Object.const_get("FrozenError")
- else
- error = RuntimeError
- end
- it { expect { subject.instance_variable_set(:@__platform, {}) }.to raise_error error }
- end
-end
diff --git a/spec/bundler/bundler/dependency_spec.rb b/spec/bundler/bundler/dependency_spec.rb
new file mode 100644
index 0000000000..6e346c36c1
--- /dev/null
+++ b/spec/bundler/bundler/dependency_spec.rb
@@ -0,0 +1,157 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Dependency do
+ let(:options) do
+ {}
+ end
+ let(:dependency) do
+ described_class.new(
+ "test_gem",
+ "1.0.0",
+ options
+ )
+ end
+
+ describe "to_lock" do
+ it "returns formatted string" do
+ expect(dependency.to_lock).to eq(" test_gem (= 1.0.0)")
+ end
+
+ it "matches format of Gem::Dependency#to_lock" do
+ gem_dependency = Gem::Dependency.new("test_gem", "1.0.0")
+ expect(dependency.to_lock).to eq(gem_dependency.to_lock)
+ end
+
+ context "when source is passed" do
+ let(:options) do
+ {
+ "source" => Bundler::Source::Git.new({}),
+ }
+ end
+
+ it "returns formatted string with exclamation mark" do
+ expect(dependency.to_lock).to eq(" test_gem (= 1.0.0)!")
+ end
+ end
+ end
+
+ describe "PLATFORM_MAP" do
+ subject { described_class::PLATFORM_MAP }
+
+ # rubocop:disable Naming/VariableNumber
+ let(:platforms) do
+ { :ruby => Gem::Platform::RUBY,
+ :ruby_18 => Gem::Platform::RUBY,
+ :ruby_19 => Gem::Platform::RUBY,
+ :ruby_20 => Gem::Platform::RUBY,
+ :ruby_21 => Gem::Platform::RUBY,
+ :ruby_22 => Gem::Platform::RUBY,
+ :ruby_23 => Gem::Platform::RUBY,
+ :ruby_24 => Gem::Platform::RUBY,
+ :ruby_25 => Gem::Platform::RUBY,
+ :ruby_26 => Gem::Platform::RUBY,
+ :ruby_27 => Gem::Platform::RUBY,
+ :ruby_30 => Gem::Platform::RUBY,
+ :ruby_31 => Gem::Platform::RUBY,
+ :ruby_32 => Gem::Platform::RUBY,
+ :ruby_33 => Gem::Platform::RUBY,
+ :mri => Gem::Platform::RUBY,
+ :mri_18 => Gem::Platform::RUBY,
+ :mri_19 => Gem::Platform::RUBY,
+ :mri_20 => Gem::Platform::RUBY,
+ :mri_21 => Gem::Platform::RUBY,
+ :mri_22 => Gem::Platform::RUBY,
+ :mri_23 => Gem::Platform::RUBY,
+ :mri_24 => Gem::Platform::RUBY,
+ :mri_25 => Gem::Platform::RUBY,
+ :mri_26 => Gem::Platform::RUBY,
+ :mri_27 => Gem::Platform::RUBY,
+ :mri_30 => Gem::Platform::RUBY,
+ :mri_31 => Gem::Platform::RUBY,
+ :mri_32 => Gem::Platform::RUBY,
+ :mri_33 => Gem::Platform::RUBY,
+ :rbx => Gem::Platform::RUBY,
+ :truffleruby => Gem::Platform::RUBY,
+ :jruby => Gem::Platform::JAVA,
+ :jruby_18 => Gem::Platform::JAVA,
+ :jruby_19 => Gem::Platform::JAVA,
+ :windows => Gem::Platform::WINDOWS,
+ :windows_18 => Gem::Platform::WINDOWS,
+ :windows_19 => Gem::Platform::WINDOWS,
+ :windows_20 => Gem::Platform::WINDOWS,
+ :windows_21 => Gem::Platform::WINDOWS,
+ :windows_22 => Gem::Platform::WINDOWS,
+ :windows_23 => Gem::Platform::WINDOWS,
+ :windows_24 => Gem::Platform::WINDOWS,
+ :windows_25 => Gem::Platform::WINDOWS,
+ :windows_26 => Gem::Platform::WINDOWS,
+ :windows_27 => Gem::Platform::WINDOWS,
+ :windows_30 => Gem::Platform::WINDOWS,
+ :windows_31 => Gem::Platform::WINDOWS,
+ :windows_32 => Gem::Platform::WINDOWS,
+ :windows_33 => Gem::Platform::WINDOWS,
+ :mswin => Gem::Platform::MSWIN,
+ :mswin_18 => Gem::Platform::MSWIN,
+ :mswin_19 => Gem::Platform::MSWIN,
+ :mswin_20 => Gem::Platform::MSWIN,
+ :mswin_21 => Gem::Platform::MSWIN,
+ :mswin_22 => Gem::Platform::MSWIN,
+ :mswin_23 => Gem::Platform::MSWIN,
+ :mswin_24 => Gem::Platform::MSWIN,
+ :mswin_25 => Gem::Platform::MSWIN,
+ :mswin_26 => Gem::Platform::MSWIN,
+ :mswin_27 => Gem::Platform::MSWIN,
+ :mswin_30 => Gem::Platform::MSWIN,
+ :mswin_31 => Gem::Platform::MSWIN,
+ :mswin_32 => Gem::Platform::MSWIN,
+ :mswin_33 => Gem::Platform::MSWIN,
+ :mswin64 => Gem::Platform::MSWIN64,
+ :mswin64_19 => Gem::Platform::MSWIN64,
+ :mswin64_20 => Gem::Platform::MSWIN64,
+ :mswin64_21 => Gem::Platform::MSWIN64,
+ :mswin64_22 => Gem::Platform::MSWIN64,
+ :mswin64_23 => Gem::Platform::MSWIN64,
+ :mswin64_24 => Gem::Platform::MSWIN64,
+ :mswin64_25 => Gem::Platform::MSWIN64,
+ :mswin64_26 => Gem::Platform::MSWIN64,
+ :mswin64_27 => Gem::Platform::MSWIN64,
+ :mswin64_30 => Gem::Platform::MSWIN64,
+ :mswin64_31 => Gem::Platform::MSWIN64,
+ :mswin64_32 => Gem::Platform::MSWIN64,
+ :mswin64_33 => Gem::Platform::MSWIN64,
+ :mingw => Gem::Platform::MINGW,
+ :mingw_18 => Gem::Platform::MINGW,
+ :mingw_19 => Gem::Platform::MINGW,
+ :mingw_20 => Gem::Platform::MINGW,
+ :mingw_21 => Gem::Platform::MINGW,
+ :mingw_22 => Gem::Platform::MINGW,
+ :mingw_23 => Gem::Platform::MINGW,
+ :mingw_24 => Gem::Platform::MINGW,
+ :mingw_25 => Gem::Platform::MINGW,
+ :mingw_26 => Gem::Platform::MINGW,
+ :mingw_27 => Gem::Platform::MINGW,
+ :mingw_30 => Gem::Platform::MINGW,
+ :mingw_31 => Gem::Platform::MINGW,
+ :mingw_32 => Gem::Platform::MINGW,
+ :mingw_33 => Gem::Platform::MINGW,
+ :x64_mingw => Gem::Platform::X64_MINGW,
+ :x64_mingw_20 => Gem::Platform::X64_MINGW,
+ :x64_mingw_21 => Gem::Platform::X64_MINGW,
+ :x64_mingw_22 => Gem::Platform::X64_MINGW,
+ :x64_mingw_23 => Gem::Platform::X64_MINGW,
+ :x64_mingw_24 => Gem::Platform::X64_MINGW,
+ :x64_mingw_25 => Gem::Platform::X64_MINGW,
+ :x64_mingw_26 => Gem::Platform::X64_MINGW,
+ :x64_mingw_27 => Gem::Platform::X64_MINGW,
+ :x64_mingw_30 => Gem::Platform::X64_MINGW,
+ :x64_mingw_31 => Gem::Platform::X64_MINGW,
+ :x64_mingw_32 => Gem::Platform::X64_MINGW,
+ :x64_mingw_33 => Gem::Platform::X64_MINGW }
+ end
+ # rubocop:enable Naming/VariableNumber
+
+ it "includes all platforms" do
+ expect(subject).to eq(platforms)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/dsl_spec.rb b/spec/bundler/bundler/dsl_spec.rb
index 19c8f2e889..8b5bf930f2 100644
--- a/spec/bundler/bundler/dsl_spec.rb
+++ b/spec/bundler/bundler/dsl_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe Bundler::Dsl do
subject.gem("sparks", :github => "https://github.com/indirect/sparks/pull/5")
github_uri = "https://github.com/indirect/sparks.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
- expect(subject.dependencies.first.source.branch).to eq("refs/pull/5/head")
+ expect(subject.dependencies.first.source.ref).to eq("refs/pull/5/head")
end
it "rejects :github PR URI with a branch, ref or tag" do
@@ -139,14 +139,19 @@ RSpec.describe Bundler::Dsl do
describe "#gem" do
# rubocop:disable Naming/VariableNumber
[:ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24, :ruby_25, :ruby_26, :ruby_27,
- :ruby_30, :ruby_31, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24, :mri_25, :mri_26,
- :mri_27, :mri_30, :mri_31, :jruby, :rbx, :truffleruby].each do |platform|
+ :ruby_30, :ruby_31, :ruby_32, :ruby_33, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24,
+ :mri_25, :mri_26, :mri_27, :mri_30, :mri_31, :mri_32, :mri_33, :jruby, :rbx, :truffleruby].each do |platform|
it "allows #{platform} as a valid platform" do
subject.gem("foo", :platform => platform)
end
end
# rubocop:enable Naming/VariableNumber
+ it "allows platforms matching the running Ruby version" do
+ platform = "ruby_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}"
+ subject.gem("foo", :platform => platform)
+ end
+
it "rejects invalid platforms" do
expect { subject.gem("foo", :platform => :bogus) }.
to raise_error(Bundler::GemfileError, /is not a valid platform/)
diff --git a/spec/bundler/bundler/endpoint_specification_spec.rb b/spec/bundler/bundler/endpoint_specification_spec.rb
index 02a90d507a..7dd6925007 100644
--- a/spec/bundler/bundler/endpoint_specification_spec.rb
+++ b/spec/bundler/bundler/endpoint_specification_spec.rb
@@ -48,6 +48,31 @@ RSpec.describe Bundler::EndpointSpecification do
end
end
+ describe "#required_ruby_version" do
+ context "required_ruby_version is already set on endpoint specification" do
+ existing_value = "already set value"
+ let(:required_ruby_version) { existing_value }
+
+ it "should return the current value when already set on endpoint specification" do
+ expect(spec.required_ruby_version). eql?(existing_value)
+ end
+ end
+
+ it "should return the remote spec value when not set on endpoint specification and remote spec has one" do
+ remote_value = "remote_value"
+ remote_spec = double(:remote_spec, :required_ruby_version => remote_value, :required_rubygems_version => nil)
+ allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
+
+ expect(spec.required_ruby_version). eql?(remote_value)
+ end
+
+ it "should use the default Gem Requirement value when not set on endpoint specification and not set on remote spec" do
+ remote_spec = double(:remote_spec, :required_ruby_version => nil, :required_rubygems_version => nil)
+ allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
+ expect(spec.required_ruby_version). eql?(Gem::Requirement.default)
+ end
+ end
+
it "supports equality comparison" do
remote_spec = double(:remote_spec, :required_ruby_version => nil, :required_rubygems_version => nil)
allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
diff --git a/spec/bundler/bundler/env_spec.rb b/spec/bundler/bundler/env_spec.rb
index a6f4b2ba85..a00489d0e5 100644
--- a/spec/bundler/bundler/env_spec.rb
+++ b/spec/bundler/bundler/env_spec.rb
@@ -4,7 +4,7 @@ require "bundler/settings"
require "openssl"
RSpec.describe Bundler::Env do
- let(:git_proxy_stub) { Bundler::Source::Git::GitProxy.new(nil, nil, nil) }
+ let(:git_proxy_stub) { Bundler::Source::Git::GitProxy.new(nil, nil) }
describe "#report" do
it "prints the environment" do
@@ -34,8 +34,6 @@ RSpec.describe Bundler::Env do
end
it "prints user home" do
- skip "needs to use a valid HOME" if Gem.win_platform? && RUBY_VERSION < "2.6.0"
-
with_clear_paths("HOME", "/a/b/c") do
out = described_class.report
expect(out).to include("User Home /a/b/c")
@@ -43,8 +41,6 @@ RSpec.describe Bundler::Env do
end
it "prints user path" do
- skip "needs to use a valid HOME" if Gem.win_platform? && RUBY_VERSION < "2.6.0"
-
with_clear_paths("HOME", "/a/b/c") do
allow(File).to receive(:exist?)
allow(File).to receive(:exist?).with("/a/b/c/.gem").and_return(true)
@@ -221,7 +217,7 @@ RSpec.describe Bundler::Env do
context "when the git version is OS specific" do
it "includes OS specific information with the version number" do
- expect(git_proxy_stub).to receive(:git).with("--version").
+ expect(git_proxy_stub).to receive(:git_local).with("--version").
and_return("git version 1.2.3 (Apple Git-BS)")
expect(Bundler::Source::Git::GitProxy).to receive(:new).and_return(git_proxy_stub)
diff --git a/spec/bundler/bundler/fetcher/dependency_spec.rb b/spec/bundler/bundler/fetcher/dependency_spec.rb
index 53249116cd..20307f9c99 100644
--- a/spec/bundler/bundler/fetcher/dependency_spec.rb
+++ b/spec/bundler/bundler/fetcher/dependency_spec.rb
@@ -155,9 +155,9 @@ RSpec.describe Bundler::Fetcher::Dependency do
end
end
- shared_examples_for "the error suggests retrying with the full index" do
- it "should log the inability to fetch from API at debug level" do
- expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API\nit's suggested to retry using the full index via `bundle install --full-index`")
+ shared_examples_for "the error is logged" do
+ it "should log the inability to fetch from API at debug level, and mention retrying" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API, trying the full index")
subject.specs(gem_names, full_dependency_list, last_spec_list)
end
end
@@ -166,25 +166,21 @@ RSpec.describe Bundler::Fetcher::Dependency do
before { allow(subject).to receive(:dependency_specs) { raise Bundler::HTTPError.new } }
it_behaves_like "the error is properly handled"
- it_behaves_like "the error suggests retrying with the full index"
+ it_behaves_like "the error is logged"
end
context "when a GemspecError occurs" do
before { allow(subject).to receive(:dependency_specs) { raise Bundler::GemspecError.new } }
it_behaves_like "the error is properly handled"
- it_behaves_like "the error suggests retrying with the full index"
+ it_behaves_like "the error is logged"
end
context "when a MarshalError occurs" do
before { allow(subject).to receive(:dependency_specs) { raise Bundler::MarshalError.new } }
it_behaves_like "the error is properly handled"
-
- it "should log the inability to fetch from API and mention retrying" do
- expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API, trying the full index")
- subject.specs(gem_names, full_dependency_list, last_spec_list)
- end
+ it_behaves_like "the error is logged"
end
end
@@ -222,7 +218,7 @@ RSpec.describe Bundler::Fetcher::Dependency do
it "should fetch dependencies from RubyGems and unmarshal them" do
expect(gem_names).to receive(:each_slice).with(rubygems_limit).and_call_original
expect(downloader).to receive(:fetch).with(dep_api_uri).and_return(fetch_response)
- expect(Bundler).to receive(:load_marshal).with(fetch_response.body).and_return([unmarshalled_gems])
+ expect(Bundler).to receive(:safe_load_marshal).with(fetch_response.body).and_return([unmarshalled_gems])
expect(subject.unmarshalled_dep_gems(gem_names)).to eq([unmarshalled_gems])
end
end
diff --git a/spec/bundler/bundler/fetcher/downloader_spec.rb b/spec/bundler/bundler/fetcher/downloader_spec.rb
index 94a0993a54..22aa3a8b0c 100644
--- a/spec/bundler/bundler/fetcher/downloader_spec.rb
+++ b/spec/bundler/bundler/fetcher/downloader_spec.rb
@@ -98,6 +98,16 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
+ context "when the request response is a Net::HTTPForbidden" do
+ let(:http_response) { Net::HTTPForbidden.new("1.1", 403, "Forbidden") }
+ let(:uri) { Bundler::URI("http://user:password@www.uri-to-fetch.com") }
+
+ it "should raise a Bundler::Fetcher::AuthenticationForbiddenError with the uri host" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationForbiddenError,
+ /Access token could not be authenticated for www.uri-to-fetch.com/)
+ end
+ end
+
context "when the request response is a Net::HTTPNotFound" do
let(:http_response) { Net::HTTPNotFound.new("1.1", 404, "Not Found") }
@@ -178,26 +188,6 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
- context "when the request response causes a NoMethodError" do
- before { allow(connection).to receive(:request).with(uri, net_http_get) { raise NoMethodError.new(message) } }
-
- context "and the error message is about use_ssl=" do
- let(:message) { "undefined method 'use_ssl='" }
-
- it "should raise a LoadError about openssl" do
- expect { subject.request(uri, options) }.to raise_error(LoadError, "cannot load such file -- openssl")
- end
- end
-
- context "and the error message is not about use_ssl=" do
- let(:message) { "undefined method 'undefined_method_call'" }
-
- it "should raise the original NoMethodError" do
- expect { subject.request(uri, options) }.to raise_error(NoMethodError, /undefined method 'undefined_method_call'/)
- end
- end
- end
-
context "when the request response causes a OpenSSL::SSL::SSLError" do
before { allow(connection).to receive(:request).with(uri, net_http_get) { raise OpenSSL::SSL::SSLError.new } }
diff --git a/spec/bundler/bundler/fetcher/index_spec.rb b/spec/bundler/bundler/fetcher/index_spec.rb
index f0db07583c..971b64ce8f 100644
--- a/spec/bundler/bundler/fetcher/index_spec.rb
+++ b/spec/bundler/bundler/fetcher/index_spec.rb
@@ -63,26 +63,9 @@ RSpec.describe Bundler::Fetcher::Index do
context "when a 403 response occurs" do
let(:error_message) { "403" }
- before do
- allow(remote_uri).to receive(:userinfo).and_return(userinfo)
- end
-
- context "and there was userinfo" do
- let(:userinfo) { double(:userinfo) }
-
- it "should raise a Bundler::Fetcher::BadAuthenticationError" do
- expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::BadAuthenticationError,
- %r{Bad username or password for http://remote-uri.org})
- end
- end
-
- context "and there was no userinfo" do
- let(:userinfo) { nil }
-
- it "should raise a Bundler::Fetcher::AuthenticationRequiredError" do
- expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
- %r{Authentication is required for http://remote-uri.org})
- end
+ it "should raise a Bundler::Fetcher::AuthenticationForbiddenError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationForbiddenError,
+ %r{Access token could not be authenticated for http://remote-uri.org})
end
end
diff --git a/spec/bundler/bundler/fetcher_spec.rb b/spec/bundler/bundler/fetcher_spec.rb
index a104760075..27a63c476d 100644
--- a/spec/bundler/bundler/fetcher_spec.rb
+++ b/spec/bundler/bundler/fetcher_spec.rb
@@ -159,4 +159,34 @@ RSpec.describe Bundler::Fetcher do
end
end
end
+
+ describe "#fetch_spec" do
+ let(:name) { "name" }
+ let(:version) { "1.3.17" }
+ let(:platform) { "platform" }
+ let(:downloader) { double("downloader") }
+ let(:body) { double(Net::HTTP::Get, :body => downloaded_data) }
+
+ context "when attempting to load a Gem::Specification" do
+ let(:spec) { Gem::Specification.new(name, version) }
+ let(:downloaded_data) { Zlib::Deflate.deflate(Marshal.dump(spec)) }
+
+ it "returns the spec" do
+ expect(Bundler::Fetcher::Downloader).to receive(:new).and_return(downloader)
+ expect(downloader).to receive(:fetch).once.and_return(body)
+ result = fetcher.fetch_spec([name, version, platform])
+ expect(result).to eq(spec)
+ end
+ end
+
+ context "when attempting to load an unexpected class" do
+ let(:downloaded_data) { Zlib::Deflate.deflate(Marshal.dump(3)) }
+
+ it "raises a HTTPError error" do
+ expect(Bundler::Fetcher::Downloader).to receive(:new).and_return(downloader)
+ expect(downloader).to receive(:fetch).once.and_return(body)
+ expect { fetcher.fetch_spec([name, version, platform]) }.to raise_error(Bundler::HTTPError, /Gemspec .* contained invalid data/i)
+ end
+ end
+ end
end
diff --git a/spec/bundler/bundler/friendly_errors_spec.rb b/spec/bundler/bundler/friendly_errors_spec.rb
index 69fba7b826..37afe488f3 100644
--- a/spec/bundler/bundler/friendly_errors_spec.rb
+++ b/spec/bundler/bundler/friendly_errors_spec.rb
@@ -110,19 +110,6 @@ RSpec.describe Bundler, "friendly errors" do
it_behaves_like "Bundler.ui receive error", Bundler::Thor::Error.new
end
- context "LoadError" do
- let(:error) { LoadError.new("cannot load such file -- openssl") }
-
- before do
- allow(error).to receive(:backtrace).and_return(["backtrace"])
- end
-
- it "Bundler.ui receive error" do
- expect(Bundler.ui).to receive(:error).with("\nCould not load OpenSSL. LoadError: cannot load such file -- openssl\nbacktrace")
- Bundler::FriendlyErrors.log_error(error)
- end
- end
-
context "Interrupt" do
it "Bundler.ui receive error" do
expect(Bundler.ui).to receive(:error).with("\nQuitting...")
diff --git a/spec/bundler/bundler/gem_helper_spec.rb b/spec/bundler/bundler/gem_helper_spec.rb
index 5cd79de620..7d955007ab 100644
--- a/spec/bundler/bundler/gem_helper_spec.rb
+++ b/spec/bundler/bundler/gem_helper_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe Bundler::GemHelper do
before(:each) do
global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__LINTER" => "false",
"BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__CHANGELOG" => "false"
+ sys_exec("git config --global init.defaultBranch main")
bundle "gem #{app_name}"
prepare_gemspec(app_gemspec_path)
end
@@ -295,7 +296,7 @@ RSpec.describe Bundler::GemHelper do
mock_confirm_message "Tagged v#{app_version}."
mock_confirm_message "Pushed git commits and release tag."
- sys_exec("git push -u origin master", :dir => app_path)
+ sys_exec("git push -u origin main", :dir => app_path)
end
it "calls rubygem_push with proper arguments" do
@@ -336,7 +337,7 @@ RSpec.describe Bundler::GemHelper do
mock_build_message app_name, app_version
mock_confirm_message "Pushed git commits and release tag."
- sys_exec("git push -u origin master", :dir => app_path)
+ sys_exec("git push -u origin main", :dir => app_path)
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
end
diff --git a/spec/bundler/bundler/gem_version_promoter_spec.rb b/spec/bundler/bundler/gem_version_promoter_spec.rb
index 43a3630bbb..8058412f32 100644
--- a/spec/bundler/bundler/gem_version_promoter_spec.rb
+++ b/spec/bundler/bundler/gem_version_promoter_spec.rb
@@ -1,178 +1,162 @@
# frozen_string_literal: true
RSpec.describe Bundler::GemVersionPromoter do
- context "conservative resolver" do
- def versions(result)
- result.flatten.map(&:version).map(&:to_s)
+ let(:gvp) { described_class.new }
+
+ # Rightmost (highest array index) in result is most preferred.
+ # Leftmost (lowest array index) in result is least preferred.
+ # `build_candidates` has all versions of gem in index.
+ # `build_spec` is the version currently in the .lock file.
+ #
+ # In default (not strict) mode, all versions in the index will
+ # be returned, allowing Bundler the best chance to resolve all
+ # dependencies, but sometimes resulting in upgrades that some
+ # would not consider conservative.
+
+ describe "#sort_versions" do
+ def build_candidates(versions)
+ versions.map do |v|
+ Bundler::Resolver::Candidate.new(v)
+ end
end
- def make_instance(*args)
- @gvp = Bundler::GemVersionPromoter.new(*args).tap do |gvp|
- gvp.class.class_eval { public :filter_dep_specs, :sort_dep_specs }
- end
+ def build_package(name, version, locked = [])
+ Bundler::Resolver::Package.new(name, [], :locked_specs => Bundler::SpecSet.new(build_spec(name, version)), :unlock => locked)
end
- def unlocking(options)
- make_instance(Bundler::SpecSet.new([]), ["foo"]).tap do |p|
- p.level = options[:level] if options[:level]
- p.strict = options[:strict] if options[:strict]
- end
+ def sorted_versions(candidates:, current:, name: "foo", locked: [])
+ gvp.sort_versions(
+ build_package(name, current, locked),
+ build_candidates(candidates)
+ ).flatten.map(&:version).map(&:to_s)
end
- def keep_locked(options)
- make_instance(Bundler::SpecSet.new([]), ["bar"]).tap do |p|
- p.level = options[:level] if options[:level]
- p.strict = options[:strict] if options[:strict]
- end
+ it "numerically sorts versions" do
+ versions = sorted_versions(:candidates => %w[1.7.7 1.7.8 1.7.9 1.7.15 1.8.0], :current => "1.7.8")
+ expect(versions).to eq %w[1.7.7 1.7.8 1.7.9 1.7.15 1.8.0]
end
- def build_spec_groups(name, versions)
- versions.map do |v|
- Bundler::Resolver::SpecGroup.create_for({ Gem::Platform::RUBY => build_spec(name, v) }, [Gem::Platform::RUBY], Gem::Platform::RUBY)
+ context "with no options" do
+ it "defaults to level=:major, strict=false, pre=false" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0]
end
end
- # Rightmost (highest array index) in result is most preferred.
- # Leftmost (lowest array index) in result is least preferred.
- # `build_spec_groups` has all versions of gem in index.
- # `build_spec` is the version currently in the .lock file.
- #
- # In default (not strict) mode, all versions in the index will
- # be returned, allowing Bundler the best chance to resolve all
- # dependencies, but sometimes resulting in upgrades that some
- # would not consider conservative.
- context "filter specs (strict) level patch" do
- it "when keeping build_spec, keep current, next release" do
- keep_locked(:level => :patch)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[1.7.8 1.7.9 1.8.0]),
- build_spec("foo", "1.7.8").first
- )
- expect(versions(res)).to eq %w[1.7.9 1.7.8]
- end
+ context "when strict" do
+ before { gvp.strict = true }
- it "when unlocking prefer next release first" do
- unlocking(:level => :patch)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[1.7.8 1.7.9 1.8.0]),
- build_spec("foo", "1.7.8").first
- )
- expect(versions(res)).to eq %w[1.7.8 1.7.9]
- end
+ context "when level is major" do
+ before { gvp.level = :major }
- it "when unlocking keep current when already at latest release" do
- unlocking(:level => :patch)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[1.7.9 1.8.0 2.0.0]),
- build_spec("foo", "1.7.9").first
- )
- expect(versions(res)).to eq %w[1.7.9]
+ it "keeps downgrades" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0]
+ end
end
- end
- context "filter specs (strict) level minor" do
- it "when unlocking favor next releases, remove minor and major increases" do
- unlocking(:level => :minor)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.0 2.0.1]),
- build_spec("foo", "0.2.0").first
- )
- expect(versions(res)).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0]
+ context "when level is minor" do
+ before { gvp.level = :minor }
+
+ it "removes downgrades and major upgrades" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.3.0 0.3.1 0.9.0]
+ end
end
- it "when keep locked, keep current, then favor next release, remove minor and major increases" do
- keep_locked(:level => :minor)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.0 2.0.1]),
- build_spec("foo", "0.2.0").first
- )
- expect(versions(res)).to eq %w[0.3.0 0.3.1 0.9.0 0.2.0]
+ context "when level is patch" do
+ before { gvp.level = :patch }
+
+ it "removes downgrades and major and minor upgrades" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.3.0 0.3.1]
+ end
end
end
- context "sort specs (not strict) level patch" do
- it "when not unlocking, same order but make sure build_spec version is most preferred to stay put" do
- keep_locked(:level => :patch)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[1.5.4 1.6.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 2.0.0 2.0.1]),
- build_spec("foo", "1.7.7").first
- )
- expect(versions(res)).to eq %w[1.5.4 1.6.5 1.7.6 2.0.0 2.0.1 1.8.0 1.8.1 1.7.8 1.7.9 1.7.7]
- end
+ context "when not strict" do
+ before { gvp.strict = false }
- it "when unlocking favor next release, then current over minor increase" do
- unlocking(:level => :patch)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[1.7.7 1.7.8 1.7.9 1.8.0]),
- build_spec("foo", "1.7.8").first
- )
- expect(versions(res)).to eq %w[1.7.7 1.8.0 1.7.8 1.7.9]
+ context "when level is major" do
+ before { gvp.level = :major }
+
+ it "orders by version" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0]
+ end
end
- it "when unlocking do proper integer comparison, not string" do
- unlocking(:level => :patch)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[1.7.7 1.7.8 1.7.9 1.7.15 1.8.0]),
- build_spec("foo", "1.7.8").first
- )
- expect(versions(res)).to eq %w[1.7.7 1.8.0 1.7.8 1.7.9 1.7.15]
+ context "when level is minor" do
+ before { gvp.level = :minor }
+
+ it "favors downgrades, then upgrades by major descending, minor ascending, patch ascending" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.2.0 2.0.1 2.1.0 1.0.0 0.3.0 0.3.1 0.9.0]
+ end
end
- it "leave current when unlocking but already at latest release" do
- unlocking(:level => :patch)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[1.7.9 1.8.0 2.0.0]),
- build_spec("foo", "1.7.9").first
- )
- expect(versions(res)).to eq %w[2.0.0 1.8.0 1.7.9]
+ context "when level is patch" do
+ before { gvp.level = :patch }
+
+ it "favors downgrades, then upgrades by major descending, minor descending, patch ascending" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.2.0 2.1.0 2.0.1 1.0.0 0.9.0 0.3.0 0.3.1]
+ end
end
end
- context "sort specs (not strict) level minor" do
- it "when unlocking favor next release, then minor increase over current" do
- unlocking(:level => :minor)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.0 2.0.1]),
- build_spec("foo", "0.2.0").first
- )
- expect(versions(res)).to eq %w[2.0.0 2.0.1 1.0.0 0.2.0 0.3.0 0.3.1 0.9.0]
+ context "when pre" do
+ before { gvp.pre = true }
+
+ it "sorts regardless of prerelease status" do
+ versions = sorted_versions(:candidates => %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0], :current => "1.8.0")
+ expect(versions).to eq %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0]
end
end
- context "level error handling" do
- subject { Bundler::GemVersionPromoter.new }
+ context "when not pre" do
+ before { gvp.pre = false }
- it "should raise if not major, minor or patch is passed" do
- expect { subject.level = :minjor }.to raise_error ArgumentError
+ it "deprioritizes prerelease gems" do
+ versions = sorted_versions(:candidates => %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0], :current => "1.8.0")
+ expect(versions).to eq %w[1.7.7.pre 1.8.1.pre 2.0.0.pre 1.8.0 1.8.1 2.0.0]
end
+ end
- it "should raise if invalid classes passed" do
- [123, nil].each do |value|
- expect { subject.level = value }.to raise_error ArgumentError
- end
+ context "when locking and not major" do
+ before { gvp.level = :minor }
+
+ it "keeps the current version last" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.1.0 2.0.1], :current => "0.3.0", :locked => ["bar"])
+ expect(versions.last).to eq("0.3.0")
end
+ end
+ end
- it "should accept major, minor patch symbols" do
- [:major, :minor, :patch].each do |value|
- subject.level = value
- expect(subject.level).to eq value
- end
+ describe "#level=" do
+ subject { described_class.new }
+
+ it "should raise if not major, minor or patch is passed" do
+ expect { subject.level = :minjor }.to raise_error ArgumentError
+ end
+
+ it "should raise if invalid classes passed" do
+ [123, nil].each do |value|
+ expect { subject.level = value }.to raise_error ArgumentError
end
+ end
- it "should accept major, minor patch strings" do
- %w[major minor patch].each do |value|
- subject.level = value
- expect(subject.level).to eq value.to_sym
- end
+ it "should accept major, minor patch symbols" do
+ [:major, :minor, :patch].each do |value|
+ subject.level = value
+ expect(subject.level).to eq value
end
end
- context "debug output" do
- it "should not kerblooie on its own debug output" do
- gvp = unlocking(:level => :patch)
- dep = Bundler::DepProxy.get_proxy(dep("foo", "1.2.0").first, "ruby")
- result = gvp.send(:debug_format_result, dep, build_spec_groups("foo", %w[1.2.0 1.3.0]))
- expect(result.class).to eq Array
+ it "should accept major, minor patch strings" do
+ %w[major minor patch].each do |value|
+ subject.level = value
+ expect(subject.level).to eq value.to_sym
end
end
end
diff --git a/spec/bundler/bundler/installer/gem_installer_spec.rb b/spec/bundler/bundler/installer/gem_installer_spec.rb
index 14a6a19a86..e86bdf009a 100644
--- a/spec/bundler/bundler/installer/gem_installer_spec.rb
+++ b/spec/bundler/bundler/installer/gem_installer_spec.rb
@@ -12,7 +12,10 @@ RSpec.describe Bundler::GemInstaller do
context "spec_settings is nil" do
it "invokes install method with empty build_args" do
- allow(spec_source).to receive(:install).with(spec, :force => false, :ensure_builtin_gems_cached => false, :build_args => [], :previous_spec => nil)
+ allow(spec_source).to receive(:install).with(
+ spec,
+ { :force => false, :ensure_builtin_gems_cached => false, :build_args => [], :previous_spec => nil }
+ )
subject.install_from_spec
end
end
@@ -23,7 +26,10 @@ RSpec.describe Bundler::GemInstaller do
allow(Bundler.settings).to receive(:[]).with(:inline)
allow(Bundler.settings).to receive(:[]).with(:forget_cli_options)
allow(Bundler.settings).to receive(:[]).with("build.dummy").and_return("--with-dummy-config=dummy")
- expect(spec_source).to receive(:install).with(spec, :force => false, :ensure_builtin_gems_cached => false, :build_args => ["--with-dummy-config=dummy"], :previous_spec => nil)
+ expect(spec_source).to receive(:install).with(
+ spec,
+ { :force => false, :ensure_builtin_gems_cached => false, :build_args => ["--with-dummy-config=dummy"], :previous_spec => nil }
+ )
subject.install_from_spec
end
end
@@ -36,10 +42,7 @@ RSpec.describe Bundler::GemInstaller do
allow(Bundler.settings).to receive(:[]).with("build.dummy").and_return("--with-dummy-config=dummy --with-another-dummy-config")
expect(spec_source).to receive(:install).with(
spec,
- :force => false,
- :ensure_builtin_gems_cached => false,
- :build_args => ["--with-dummy-config=dummy", "--with-another-dummy-config"],
- :previous_spec => nil
+ { :force => false, :ensure_builtin_gems_cached => false, :build_args => ["--with-dummy-config=dummy", "--with-another-dummy-config"], :previous_spec => nil }
)
subject.install_from_spec
end
diff --git a/spec/bundler/bundler/installer/parallel_installer_spec.rb b/spec/bundler/bundler/installer/parallel_installer_spec.rb
index e680633862..c8403a2e38 100644
--- a/spec/bundler/bundler/installer/parallel_installer_spec.rb
+++ b/spec/bundler/bundler/installer/parallel_installer_spec.rb
@@ -11,40 +11,6 @@ RSpec.describe Bundler::ParallelInstaller do
subject { described_class.new(installer, all_specs, size, standalone, force) }
- context "when dependencies that are not on the overall installation list are the only ones not installed" do
- let(:all_specs) do
- [
- build_spec("alpha", "1.0") {|s| s.runtime "a", "1" },
- ].flatten
- end
-
- it "prints a warning" do
- expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
-Your lockfile was created by an old Bundler that left some things out.
-You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile.
-The missing gems are:
-* a depended upon by alpha
- W
- subject.check_for_corrupt_lockfile
- end
-
- context "when size > 1" do
- let(:size) { 500 }
-
- it "prints a warning and sets size to 1" do
- expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
-Your lockfile was created by an old Bundler that left some things out.
-Because of the missing DEPENDENCIES, we can only install gems one at a time, instead of installing 500 at a time.
-You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile.
-The missing gems are:
-* a depended upon by alpha
- W
- subject.check_for_corrupt_lockfile
- expect(subject.size).to eq(1)
- end
- end
- end
-
context "when the spec set is not a valid resolution" do
let(:all_specs) do
[
@@ -56,9 +22,9 @@ The missing gems are:
it "prints a warning" do
expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
Your lockfile doesn't include a valid resolution.
-You can fix this by regenerating your lockfile or trying to manually editing the bad locked gems to a version that satisfies all dependencies.
+You can fix this by regenerating your lockfile or manually editing the bad locked gems to a version that satisfies all dependencies.
The unmet dependencies are:
-* diff-lcs (< 1.4), depended upon cucumber-4.1.0, unsatisfied by diff-lcs-1.4.4
+* diff-lcs (< 1.4), dependency of cucumber-4.1.0, unsatisfied by diff-lcs-1.4.4
W
subject.check_for_unmet_dependencies
end
diff --git a/spec/bundler/bundler/plugin/index_spec.rb b/spec/bundler/bundler/plugin/index_spec.rb
index d34b0de342..5a7047459f 100644
--- a/spec/bundler/bundler/plugin/index_spec.rb
+++ b/spec/bundler/bundler/plugin/index_spec.rb
@@ -140,7 +140,7 @@ RSpec.describe Bundler::Plugin::Index do
describe "after conflict" do
let(:commands) { ["foo"] }
let(:sources) { ["bar"] }
- let(:hooks) { ["hoook"] }
+ let(:hooks) { ["thehook"] }
shared_examples "it cleans up" do
it "the path" do
@@ -156,7 +156,7 @@ RSpec.describe Bundler::Plugin::Index do
end
it "the hook" do
- expect(index.hook_plugins("xhoook")).to be_empty
+ expect(index.hook_plugins("xthehook")).to be_empty
end
end
@@ -164,7 +164,7 @@ RSpec.describe Bundler::Plugin::Index do
before do
expect do
path = lib_path("cplugin")
- index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["xbar"], ["xhoook"])
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["xbar"], ["xthehook"])
end.to raise_error(Index::CommandConflict)
end
@@ -175,7 +175,7 @@ RSpec.describe Bundler::Plugin::Index do
before do
expect do
path = lib_path("cplugin")
- index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["xfoo"], ["bar"], ["xhoook"])
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["xfoo"], ["bar"], ["xthehook"])
end.to raise_error(Index::SourceConflict)
end
@@ -186,7 +186,7 @@ RSpec.describe Bundler::Plugin::Index do
before do
expect do
path = lib_path("cplugin")
- index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["bar"], ["xhoook"])
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["bar"], ["xthehook"])
end.to raise_error(Index::CommandConflict)
end
diff --git a/spec/bundler/bundler/remote_specification_spec.rb b/spec/bundler/bundler/remote_specification_spec.rb
index 8115e026d8..921a47a2d3 100644
--- a/spec/bundler/bundler/remote_specification_spec.rb
+++ b/spec/bundler/bundler/remote_specification_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe Bundler::RemoteSpecification do
let(:platform) { "jruby" }
it "should return the spec name, version, and platform" do
- expect(subject.full_name).to eq("foo-1.0.0-jruby")
+ expect(subject.full_name).to eq("foo-1.0.0-java")
end
end
end
diff --git a/spec/bundler/bundler/resolver/candidate_spec.rb b/spec/bundler/bundler/resolver/candidate_spec.rb
new file mode 100644
index 0000000000..cd52c867c4
--- /dev/null
+++ b/spec/bundler/bundler/resolver/candidate_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Resolver::Candidate do
+ it "compares fine" do
+ version1 = described_class.new("1.12.5", :specs => [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::RUBY }])
+ version2 = described_class.new("1.12.5") # passing no specs creates a platform specific candidate, so sorts higher
+
+ expect(version2 >= version1).to be true
+
+ expect(version1.generic! == version2.generic!).to be true
+ expect(version1.platform_specific! == version2.platform_specific!).to be true
+
+ expect(version1.platform_specific! >= version2.generic!).to be true
+ expect(version2.platform_specific! >= version1.generic!).to be true
+
+ version1 = described_class.new("1.12.5", :specs => [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::RUBY }])
+ version2 = described_class.new("1.12.5", :specs => [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::X64_LINUX }])
+
+ expect(version2 >= version1).to be true
+ end
+end
diff --git a/spec/bundler/bundler/ruby_dsl_spec.rb b/spec/bundler/bundler/ruby_dsl_spec.rb
index bc1ca98457..0ba55e949f 100644
--- a/spec/bundler/bundler/ruby_dsl_spec.rb
+++ b/spec/bundler/bundler/ruby_dsl_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe Bundler::RubyDsl do
let(:dsl) { MockDSL.new }
let(:ruby_version) { "2.0.0" }
+ let(:ruby_version_arg) { ruby_version }
let(:version) { "2.0.0" }
let(:engine) { "jruby" }
let(:engine_version) { "9000" }
@@ -23,7 +24,10 @@ RSpec.describe Bundler::RubyDsl do
let(:invoke) do
proc do
- args = Array(ruby_version) + [options]
+ args = []
+ args << Array(ruby_version_arg) if ruby_version_arg
+ args << options
+
dsl.ruby(*args)
end
end
@@ -91,5 +95,26 @@ RSpec.describe Bundler::RubyDsl do
it_behaves_like "it stores the ruby version"
end
end
+
+ context "with a file option" do
+ let(:options) { { :file => "foo" } }
+ let(:version) { "3.2.2" }
+ let(:ruby_version) { "3.2.2" }
+ let(:ruby_version_arg) { nil }
+ let(:engine_version) { version }
+ let(:patchlevel) { nil }
+ let(:engine) { "ruby" }
+ before { allow(Bundler).to receive(:read_file).with("foo").and_return("#{version}\n") }
+
+ it_behaves_like "it stores the ruby version"
+
+ context "and a version" do
+ let(:ruby_version_arg) { "2.0.0" }
+
+ it "raises an error" do
+ expect { subject }.to raise_error(Bundler::GemfileError, "Cannot specify version when using the file option")
+ end
+ end
+ end
end
end
diff --git a/spec/bundler/bundler/ruby_version_spec.rb b/spec/bundler/bundler/ruby_version_spec.rb
index 3e3850031c..39d0571361 100644
--- a/spec/bundler/bundler/ruby_version_spec.rb
+++ b/spec/bundler/bundler/ruby_version_spec.rb
@@ -400,19 +400,19 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
let(:bundler_system_ruby_version) { subject }
around do |example|
- if Bundler::RubyVersion.instance_variable_defined?("@ruby_version")
+ if Bundler::RubyVersion.instance_variable_defined?("@system")
begin
- old_ruby_version = Bundler::RubyVersion.instance_variable_get("@ruby_version")
- Bundler::RubyVersion.remove_instance_variable("@ruby_version")
+ old_ruby_version = Bundler::RubyVersion.instance_variable_get("@system")
+ Bundler::RubyVersion.remove_instance_variable("@system")
example.run
ensure
- Bundler::RubyVersion.instance_variable_set("@ruby_version", old_ruby_version)
+ Bundler::RubyVersion.instance_variable_set("@system", old_ruby_version)
end
else
begin
example.run
ensure
- Bundler::RubyVersion.remove_instance_variable("@ruby_version")
+ Bundler::RubyVersion.remove_instance_variable("@system")
end
end
end
@@ -427,9 +427,8 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
end
describe "#version" do
- it "should return a copy of the value of RUBY_VERSION" do
- expect(subject.versions).to eq([RUBY_VERSION])
- expect(subject.versions.first).to_not be(RUBY_VERSION)
+ it "should return the value of Gem.ruby_version as a string" do
+ expect(subject.versions).to eq([Gem.ruby_version.to_s])
end
end
@@ -446,13 +445,12 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
describe "#engine_version" do
context "engine is ruby" do
before do
- stub_const("RUBY_ENGINE_VERSION", "2.2.4")
+ allow(Gem).to receive(:ruby_version).and_return(Gem::Version.new("2.2.4"))
stub_const("RUBY_ENGINE", "ruby")
end
- it "should return a copy of the value of RUBY_ENGINE_VERSION" do
+ it "should return the value of Gem.ruby_version as a string" do
expect(bundler_system_ruby_version.engine_versions).to eq(["2.2.4"])
- expect(bundler_system_ruby_version.engine_versions.first).to_not be(RUBY_ENGINE_VERSION)
end
end
diff --git a/spec/bundler/bundler/rubygems_integration_spec.rb b/spec/bundler/bundler/rubygems_integration_spec.rb
index 369f28fcef..182aa3646a 100644
--- a/spec/bundler/bundler/rubygems_integration_spec.rb
+++ b/spec/bundler/bundler/rubygems_integration_spec.rb
@@ -89,5 +89,16 @@ RSpec.describe Bundler::RubygemsIntegration do
expect(result).to eq(%w[specs prerelease_specs])
end
end
+
+ context "when loading an unexpected class" do
+ let(:remote_no_mirror) { double("remote", :uri => uri, :original_uri => nil) }
+ let(:unexpected_specs_response) { Marshal.dump(3) }
+
+ it "raises a MarshalError error" do
+ expect(Bundler.rubygems).to receive(:gem_remote_fetcher).once.and_return(fetcher)
+ expect(fetcher).to receive(:fetch_path).with(uri + "specs.4.8.gz").and_return(unexpected_specs_response)
+ expect { Bundler.rubygems.fetch_all_remote_specs(remote_no_mirror) }.to raise_error(Bundler::MarshalError, /unexpected class/i)
+ end
+ end
end
end
diff --git a/spec/bundler/bundler/settings_spec.rb b/spec/bundler/bundler/settings_spec.rb
index 24e3de7ba8..4636993d9f 100644
--- a/spec/bundler/bundler/settings_spec.rb
+++ b/spec/bundler/bundler/settings_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Bundler::Settings do
"gem.mit" => "false",
"gem.test" => "minitest",
"thingy" => <<-EOS.tr("\n", " "),
---asdf --fdsa --ty=oh man i hope this doesnt break bundler because
+--asdf --fdsa --ty=oh man i hope this doesn't break bundler because
that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
--very-important-option=DontDeleteRoo
--very-important-option=DontDeleteRoo
diff --git a/spec/bundler/bundler/shared_helpers_spec.rb b/spec/bundler/bundler/shared_helpers_spec.rb
index 68a24be31c..3c6536c4eb 100644
--- a/spec/bundler/bundler/shared_helpers_spec.rb
+++ b/spec/bundler/bundler/shared_helpers_spec.rb
@@ -246,6 +246,15 @@ RSpec.describe Bundler::SharedHelpers do
end
end
+ shared_examples_for "ENV['BUNDLER_SETUP'] gets set correctly" do
+ it "ensures bundler/setup is set in ENV['BUNDLER_SETUP']" do
+ skip "Does not play well with DidYouMean being a bundled gem instead of a default gem in Ruby 2.6" if RUBY_VERSION < "2.7"
+
+ subject.set_bundle_environment
+ expect(ENV["BUNDLER_SETUP"]).to eq("#{source_lib_dir}/bundler/setup")
+ end
+ end
+
shared_examples_for "ENV['RUBYLIB'] gets set correctly" do
let(:ruby_lib_path) { "stubbed_ruby_lib_dir" }
@@ -281,7 +290,7 @@ RSpec.describe Bundler::SharedHelpers do
if Gem.respond_to?(:path_separator)
allow(Gem).to receive(:path_separator).and_return(":")
else
- stub_const("File::PATH_SEPARATOR", ":".freeze)
+ stub_const("File::PATH_SEPARATOR", ":")
end
allow(Bundler).to receive(:bundle_path) { Pathname.new("so:me/dir/bin") }
expect { subject.send(:validate_bundle_path) }.to raise_error(
diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb
index cffd72cc3f..c79dd8ff71 100644
--- a/spec/bundler/bundler/source/git/git_proxy_spec.rb
+++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb
@@ -6,26 +6,31 @@ RSpec.describe Bundler::Source::Git::GitProxy do
let(:ref) { "HEAD" }
let(:revision) { nil }
let(:git_source) { nil }
+ let(:clone_result) { double(Process::Status, :success? => true) }
+ let(:base_clone_args) { ["clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--depth", "1", "--single-branch"] }
subject { described_class.new(path, uri, ref, revision, git_source) }
context "with configured credentials" do
it "adds username and password to URI" do
Bundler.settings.temporary(uri => "u:p") do
- expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s)
+ allow(subject).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(subject).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
it "adds username and password to URI for host" do
Bundler.settings.temporary("github.com" => "u:p") do
- expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s)
+ allow(subject).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(subject).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
it "does not add username and password to mismatched URI" do
Bundler.settings.temporary("https://u:p@github.com/rubygems/rubygems-mismatch.git" => "u:p") do
- expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--", uri, path.to_s)
+ allow(subject).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(subject).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
@@ -34,7 +39,8 @@ RSpec.describe Bundler::Source::Git::GitProxy do
Bundler.settings.temporary("github.com" => "u:p") do
original = "https://orig:info@github.com/rubygems/rubygems.git"
subject = described_class.new(Pathname("path"), original, "HEAD")
- expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--", original, path.to_s)
+ allow(subject).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(subject).to receive(:capture).with([*base_clone_args, "--", original, path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
@@ -43,7 +49,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
describe "#version" do
context "with a normal version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(subject).to receive(:git_local).with("--version").
and_return("git version 1.2.3")
end
@@ -58,7 +64,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with a OSX version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(subject).to receive(:git_local).with("--version").
and_return("git version 1.2.3 (Apple Git-BS)")
end
@@ -73,7 +79,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with a msysgit version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(subject).to receive(:git_local).with("--version").
and_return("git version 1.2.3.msysgit.0")
end
@@ -90,7 +96,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
describe "#full_version" do
context "with a normal version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(subject).to receive(:git_local).with("--version").
and_return("git version 1.2.3")
end
@@ -101,7 +107,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with a OSX version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(subject).to receive(:git_local).with("--version").
and_return("git version 1.2.3 (Apple Git-BS)")
end
@@ -112,7 +118,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with a msysgit version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(subject).to receive(:git_local).with("--version").
and_return("git version 1.2.3.msysgit.0")
end
@@ -122,33 +128,6 @@ RSpec.describe Bundler::Source::Git::GitProxy do
end
end
- describe "#copy_to" do
- let(:cache) { tmpdir("cache_path") }
- let(:destination) { tmpdir("copy_to_path") }
- let(:submodules) { false }
-
- context "when given a SHA as a revision" do
- let(:revision) { "abcd" * 10 }
- let(:command) { ["reset", "--hard", revision] }
- let(:command_for_display) { "git #{command.shelljoin}" }
-
- it "fails gracefully when resetting to the revision fails" do
- expect(subject).to receive(:git_retry).with("clone", any_args) { destination.mkpath }
- expect(subject).to receive(:git_retry).with("fetch", any_args, :dir => destination)
- expect(subject).to receive(:git).with(*command, :dir => destination).and_raise(Bundler::Source::Git::GitCommandError.new(command_for_display, destination))
- expect(subject).not_to receive(:git)
-
- expect { subject.copy_to(destination, submodules) }.
- to raise_error(
- Bundler::Source::Git::MissingGitRevisionError,
- "Git error: command `#{command_for_display}` in directory #{destination} has failed.\n" \
- "Revision #{revision} does not exist in the repository #{uri}. Maybe you misspelled it?\n" \
- "If this error persists you could try removing the cache directory '#{destination}'"
- )
- end
- end
- end
-
it "doesn't allow arbitrary code execution through Gemfile uris with a leading dash" do
gemfile <<~G
gem "poc", git: "-u./pay:load.sh"
diff --git a/spec/bundler/bundler/vendored_persistent_spec.rb b/spec/bundler/bundler/vendored_persistent_spec.rb
deleted file mode 100644
index 3ed899dbcf..0000000000
--- a/spec/bundler/bundler/vendored_persistent_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-# frozen_string_literal: true
-
-require "bundler/vendored_persistent"
-
-RSpec.describe Bundler::PersistentHTTP do
- describe "#warn_old_tls_version_rubygems_connection" do
- let(:uri) { "https://index.rubygems.org" }
- let(:connection) { instance_double(Bundler::Persistent::Net::HTTP::Persistent::Connection) }
- let(:tls_version) { "TLSv1.2" }
- let(:socket) { double("Socket") }
- let(:socket_io) { double("SocketIO") }
-
- before do
- allow(connection).to receive_message_chain(:http, :use_ssl?).and_return(!tls_version.nil?)
- allow(socket).to receive(:io).and_return(socket_io) if socket
- connection.instance_variable_set(:@socket, socket)
-
- if tls_version
- allow(socket_io).to receive(:ssl_version).and_return(tls_version)
- end
- end
-
- shared_examples_for "does not warn" do
- it "does not warn" do
- allow(Bundler.ui).to receive(:warn).never
- subject.warn_old_tls_version_rubygems_connection(Bundler::URI(uri), connection)
- end
- end
-
- shared_examples_for "does warn" do |*expected|
- it "warns" do
- expect(Bundler.ui).to receive(:warn).with(*expected)
- subject.warn_old_tls_version_rubygems_connection(Bundler::URI(uri), connection)
- end
- end
-
- context "an HTTPS uri with TLSv1.2" do
- include_examples "does not warn"
- end
-
- context "without SSL" do
- let(:tls_version) { nil }
-
- include_examples "does not warn"
- end
-
- context "without a socket" do
- let(:socket) { nil }
-
- include_examples "does not warn"
- end
-
- context "with a different TLD" do
- let(:uri) { "https://foo.bar" }
- include_examples "does not warn"
-
- context "and an outdated TLS version" do
- let(:tls_version) { "TLSv1" }
- include_examples "does not warn"
- end
- end
-
- context "with a nonsense TLS version" do
- let(:tls_version) { "BlahBlah2.0Blah" }
- include_examples "does not warn"
- end
-
- context "with an outdated TLS version" do
- let(:tls_version) { "TLSv1" }
- include_examples "does warn",
- "Warning: Your Ruby version is compiled against a copy of OpenSSL that is very old. " \
- "Starting in January 2018, RubyGems.org will refuse connection requests from these very old versions of OpenSSL. " \
- "If you will need to continue installing gems after January 2018, please follow this guide to upgrade: http://ruby.to/tls-outdated.",
- :wrap => true
- end
- end
-end
diff --git a/spec/bundler/bundler/version_ranges_spec.rb b/spec/bundler/bundler/version_ranges_spec.rb
deleted file mode 100644
index bca044b0c0..0000000000
--- a/spec/bundler/bundler/version_ranges_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-require "bundler/version_ranges"
-
-RSpec.describe Bundler::VersionRanges do
- describe ".empty?" do
- shared_examples_for "empty?" do |exp, *req|
- it "returns #{exp} for #{req}" do
- r = Gem::Requirement.new(*req)
- ranges = described_class.for(r)
- expect(described_class.empty?(*ranges)).to eq(exp), "expected `#{r}` #{exp ? "" : "not "}to be empty"
- end
- end
-
- include_examples "empty?", false
- include_examples "empty?", false, "!= 1"
- include_examples "empty?", false, "!= 1", "= 2"
- include_examples "empty?", false, "!= 1", "> 1"
- include_examples "empty?", false, "!= 1", ">= 1"
- include_examples "empty?", false, "= 1", ">= 0.1", "<= 1.1"
- include_examples "empty?", false, "= 1", ">= 1", "<= 1"
- include_examples "empty?", false, "= 1", "~> 1"
- include_examples "empty?", false, ">= 0.z", "= 0"
- include_examples "empty?", false, ">= 0"
- include_examples "empty?", false, ">= 1.0.0", "< 2.0.0"
- include_examples "empty?", false, "~> 1"
- include_examples "empty?", false, "~> 2.0", "~> 2.1"
- include_examples "empty?", true, ">= 4.1.0", "< 5.0", "= 5.2.1"
- include_examples "empty?", true, "< 5.0", "< 5.3", "< 6.0", "< 6", "= 5.2.0", "> 2", ">= 3.0", ">= 3.1", ">= 3.2", ">= 4.0.0", ">= 4.1.0", ">= 4.2.0", ">= 4.2", ">= 4"
- include_examples "empty?", true, "!= 1", "< 2", "> 2"
- include_examples "empty?", true, "!= 1", "<= 1", ">= 1"
- include_examples "empty?", true, "< 2", "> 2"
- include_examples "empty?", true, "< 2", "> 2", "= 2"
- include_examples "empty?", true, "= 1", "!= 1"
- include_examples "empty?", true, "= 1", "= 2"
- include_examples "empty?", true, "= 1", "~> 2"
- include_examples "empty?", true, ">= 0", "<= 0.a"
- include_examples "empty?", true, "~> 2.0", "~> 3"
- end
-end
diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb
index b88993e9b1..890ba88605 100644
--- a/spec/bundler/cache/git_spec.rb
+++ b/spec/bundler/cache/git_spec.rb
@@ -15,7 +15,7 @@ end
RSpec.describe "bundle cache with git" do
it "copies repository to vendor cache and uses it" do
git = build_git "foo"
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -34,7 +34,7 @@ RSpec.describe "bundle cache with git" do
it "copies repository to vendor cache and uses it even when configured with `path`" do
git = build_git "foo"
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -72,7 +72,7 @@ RSpec.describe "bundle cache with git" do
it "tracks updates" do
git = build_git "foo"
- old_ref = git.ref_for("master", 11)
+ old_ref = git.ref_for("main", 11)
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -86,11 +86,10 @@ RSpec.describe "bundle cache with git" do
s.write "lib/foo.rb", "puts :CACHE"
end
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
expect(ref).not_to eq(old_ref)
bundle "update", :all => true
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
@@ -103,7 +102,7 @@ RSpec.describe "bundle cache with git" do
it "tracks updates when specifying the gem" do
git = build_git "foo"
- old_ref = git.ref_for("master", 11)
+ old_ref = git.ref_for("main", 11)
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -117,7 +116,7 @@ RSpec.describe "bundle cache with git" do
s.write "lib/foo.rb", "puts :CACHE"
end
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
expect(ref).not_to eq(old_ref)
bundle "update foo"
@@ -132,11 +131,11 @@ RSpec.describe "bundle cache with git" do
it "uses the local repository to generate the cache" do
git = build_git "foo"
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "foo", :git => '#{lib_path("foo-invalid")}', :branch => :master
+ gem "foo", :git => '#{lib_path("foo-invalid")}', :branch => :main
G
bundle %(config set local.foo #{lib_path("foo-1.0")})
@@ -156,6 +155,9 @@ RSpec.describe "bundle cache with git" do
end
it "copies repository to vendor cache, including submodules" do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_git "submodule", "1.0"
git = build_git "has_submodule", "1.0" do |s|
@@ -172,7 +174,7 @@ RSpec.describe "bundle cache with git" do
end
G
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
bundle "config set cache_all true"
bundle :cache
@@ -196,7 +198,7 @@ RSpec.describe "bundle cache with git" do
bundle "config set cache_all true"
bundle :cache
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
gemspec = bundled_app("vendor/cache/foo-1.0-#{ref}/foo.gemspec").read
expect(gemspec).to_not match("`echo bob`")
end
@@ -218,4 +220,57 @@ RSpec.describe "bundle cache with git" do
expect(the_bundle).to include_gem "foo 1.0"
end
end
+
+ it "respects the --no-install flag" do
+ git = build_git "foo", &:add_c_extension
+ ref = git.ref_for("main", 11)
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+ bundle "config set cache_all true"
+
+ # The algorithm for the cache location for a git checkout is
+ # in Bundle::Source::Git#cache_path
+ cache_path_name = "foo-1.0-#{Digest(:SHA1).hexdigest(lib_path("foo-1.0").to_s)}"
+
+ # Run this test twice. This is because materially different codepaths
+ # will get hit the second time around.
+ # The first time, Bundler::Sources::Git#install_path is set to the system
+ # wide cache directory bundler/gems; the second time, it's set to the
+ # vendor/cache directory. We don't want the native extension to appear in
+ # either of these places, so run the `bundle cache` command twice.
+ 2.times do
+ bundle :cache, "all-platforms" => true, :install => false
+
+ # it did _NOT_ actually install the gem - neither in $GEM_HOME (bundler 2 mode),
+ # nor in .bundle (bundler 3 mode)
+ expect(Pathname.new(File.join(default_bundle_path, "gems/foo-1.0-#{ref}"))).to_not exist
+ # it _did_ cache the gem in vendor/
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ # it did _NOT_ build the gems extensions in the vendor/ dir
+ expect(Dir[bundled_app("vendor/cache/foo-1.0-#{ref}/lib/foo_c*")]).to be_empty
+ # it _did_ cache the git checkout
+ expect(default_cache_path("git", cache_path_name)).to exist
+ # And the checkout is a bare checkout
+ expect(default_cache_path("git", cache_path_name, "HEAD")).to exist
+ end
+
+ # Subsequently installing the gem should compile it.
+ # _currently_, the gem gets compiled in vendor/cache, and vendor/cache is added
+ # to the $LOAD_PATH for git extensions, so it all kind of "works". However, in the
+ # future we would like to stop adding vendor/cache to the $LOAD_PATH for git extensions
+ # and instead treat them identically to normal gems (where the gem install location,
+ # not the cache location, is added to $LOAD_PATH).
+ # Verify that the compilation worked and the result is in $LOAD_PATH by simply attempting
+ # to require it; that should make sure this spec does not break if the load path behaviour
+ # is changed.
+ bundle :install, :local => true
+ ruby <<~R, :raise_on_error => false
+ require 'bundler/setup'
+ require 'foo_c'
+ R
+ expect(last_command).to_not be_failure
+ end
end
diff --git a/spec/bundler/commands/add_spec.rb b/spec/bundler/commands/add_spec.rb
index 96ea238063..5a5b534e8d 100644
--- a/spec/bundler/commands/add_spec.rb
+++ b/spec/bundler/commands/add_spec.rb
@@ -103,6 +103,15 @@ RSpec.describe "bundle add" do
end
end
+ describe "with --path" do
+ it "adds dependency with specified path" do
+ bundle "add 'foo' --path='#{lib_path("foo-2.0")}'"
+
+ expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :path => "#{lib_path("foo-2.0")}"/)
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
describe "with --git" do
it "adds dependency with specified git source" do
bundle "add foo --git=#{lib_path("foo-2.0")}"
@@ -135,7 +144,7 @@ RSpec.describe "bundle add" do
end
describe "with --github" do
- it "adds dependency with specified github source" do
+ it "adds dependency with specified github source", :realworld do
bundle "add rake --github=ruby/rake"
expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.0", :github => "ruby\/rake"})
@@ -143,7 +152,7 @@ RSpec.describe "bundle add" do
end
describe "with --github and --branch" do
- it "adds dependency with specified github source and branch" do
+ it "adds dependency with specified github source and branch", :realworld do
bundle "add rake --github=ruby/rake --branch=master"
expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.0", :github => "ruby\/rake", :branch => "master"})
@@ -151,7 +160,7 @@ RSpec.describe "bundle add" do
end
describe "with --github and --ref" do
- it "adds dependency with specified github source and ref" do
+ it "adds dependency with specified github source and ref", :realworld do
bundle "add rake --github=ruby/rake --ref=5c60da8"
expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.0", :github => "ruby\/rake", :ref => "5c60da8"})
diff --git a/spec/bundler/commands/binstubs_spec.rb b/spec/bundler/commands/binstubs_spec.rb
index 2634f43417..bfbef58181 100644
--- a/spec/bundler/commands/binstubs_spec.rb
+++ b/spec/bundler/commands/binstubs_spec.rb
@@ -166,6 +166,21 @@ RSpec.describe "bundle binstubs <gem>" do
end
end
+ context "and the version is newer when given `gems.rb` and `gems.locked`" do
+ before do
+ gemfile bundled_app("gems.rb"), gemfile
+ lockfile bundled_app("gems.locked"), lockfile.gsub(system_bundler_version, "999.999")
+ end
+
+ it "runs the correct version of bundler" do
+ sys_exec "bin/bundle install", :env => { "BUNDLE_GEMFILE" => "gems.rb" }, :raise_on_error => false
+
+ expect(exitstatus).to eq(42)
+ expect(err).to include("Activating bundler (~> 999.999) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`")
+ end
+ end
+
context "and the version is older and a different major" do
let(:system_bundler_version) { "55" }
@@ -181,6 +196,22 @@ RSpec.describe "bundle binstubs <gem>" do
end
end
+ context "and the version is older and a different major when given `gems.rb` and `gems.locked`" do
+ let(:system_bundler_version) { "55" }
+
+ before do
+ gemfile bundled_app("gems.rb"), gemfile
+ lockfile bundled_app("gems.locked"), lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 44.0")
+ end
+
+ it "runs the correct version of bundler" do
+ sys_exec "bin/bundle install", :env => { "BUNDLE_GEMFILE" => "gems.rb" }, :raise_on_error => false
+ expect(exitstatus).to eq(42)
+ expect(err).to include("Activating bundler (~> 44.0) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 44.0'`")
+ end
+ end
+
context "and the version is older and the same major" do
let(:system_bundler_version) { "2.999.999" }
@@ -188,7 +219,7 @@ RSpec.describe "bundle binstubs <gem>" do
lockfile lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 2.3.0")
end
- it "installs and runs the exact version of bundler", :rubygems => ">= 3.3.0.dev" do
+ it "installs and runs the exact version of bundler", :rubygems => ">= 3.3.0.dev", :realworld => true do
sys_exec "bin/bundle install --verbose", :artifice => "vcr"
expect(exitstatus).not_to eq(42)
expect(out).to include("Bundler 2.999.999 is running, but your lockfile was generated with 2.3.0. Installing Bundler 2.3.0 and restarting using that version.")
@@ -224,7 +255,7 @@ RSpec.describe "bundle binstubs <gem>" do
context "when update --bundler is called" do
before { lockfile.gsub(system_bundler_version, "1.1.1") }
- it "calls through to the latest bundler version" do
+ it "calls through to the latest bundler version", :realworld do
sys_exec "bin/bundle update --bundler", :env => { "DEBUG" => "1" }
using_bundler_line = /Using bundler ([\w\.]+)\n/.match(out)
expect(using_bundler_line).to_not be_nil
@@ -369,6 +400,7 @@ RSpec.describe "bundle binstubs <gem>" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
+ gem "rails"
G
end
@@ -396,6 +428,26 @@ RSpec.describe "bundle binstubs <gem>" do
expect(bundled_app("bin/rackup.cmd")).to exist
end
end
+
+ context "when the gem is bundler" do
+ it "warns without generating a standalone binstub" do
+ bundle "binstubs bundler --standalone"
+ expect(bundled_app("bin/bundle")).not_to exist
+ expect(bundled_app("bin/bundler")).not_to exist
+ expect(err).to include("Sorry, Bundler can only be run via RubyGems.")
+ end
+ end
+
+ context "when specified --all option" do
+ it "generates standalone binstubs for all gems except bundler" do
+ bundle "binstubs --standalone --all"
+ expect(bundled_app("bin/rackup")).to exist
+ expect(bundled_app("bin/rails")).to exist
+ expect(bundled_app("bin/bundle")).not_to exist
+ expect(bundled_app("bin/bundler")).not_to exist
+ expect(err).not_to include("Sorry, Bundler can only be run via RubyGems.")
+ end
+ end
end
context "when the bin already exists" do
diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb
index 356a658e7c..a9ed389233 100644
--- a/spec/bundler/commands/cache_spec.rb
+++ b/spec/bundler/commands/cache_spec.rb
@@ -291,7 +291,7 @@ RSpec.describe "bundle cache" do
G
subject
expect(exitstatus).to eq(16)
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
bundle "env"
diff --git a/spec/bundler/commands/check_spec.rb b/spec/bundler/commands/check_spec.rb
index b4996977c1..99a858e9e9 100644
--- a/spec/bundler/commands/check_spec.rb
+++ b/spec/bundler/commands/check_spec.rb
@@ -172,7 +172,7 @@ RSpec.describe "bundle check" do
rack (1.0.0)
PLATFORMS
- #{local}
+ #{generic_local_platform}
#{not_local}
DEPENDENCIES
@@ -203,7 +203,7 @@ RSpec.describe "bundle check" do
rack (1.0.0)
PLATFORMS
- #{local}
+ #{generic_local_platform}
#{not_local}
DEPENDENCIES
@@ -351,7 +351,7 @@ RSpec.describe "bundle check" do
PLATFORMS
ruby
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
rack
diff --git a/spec/bundler/commands/clean_spec.rb b/spec/bundler/commands/clean_spec.rb
index 76c7e2adec..471cd6c354 100644
--- a/spec/bundler/commands/clean_spec.rb
+++ b/spec/bundler/commands/clean_spec.rb
@@ -625,7 +625,7 @@ RSpec.describe "bundle clean" do
expect(out).to eq("1.0")
end
- it "when using --force, it doesn't remove default gem binaries" do
+ it "when using --force, it doesn't remove default gem binaries", :realworld do
skip "does not work on old rubies because the realworld gems that need to be installed don't support them" if RUBY_VERSION < "2.7.0"
skip "does not work on rubygems versions where `--install_dir` doesn't respect --default" unless Gem::Installer.for_spec(loaded_gemspec, :install_dir => "/foo").default_spec_file == "/foo/specifications/default/bundler-#{Bundler::VERSION}.gemspec" # Since rubygems 3.2.0.rc.2
@@ -638,11 +638,6 @@ RSpec.describe "bundle clean" do
s.executables = "irb"
end
- if Gem.win_platform? && RUBY_VERSION < "3.1.0"
- default_fiddle_version = ruby "require 'fiddle'; puts Gem.loaded_specs['fiddle'].version"
- realworld_system_gems "fiddle --version #{default_fiddle_version}"
- end
-
realworld_system_gems "tsort --version 0.1.0", "pathname --version 0.1.0", "set --version 1.0.1"
install_gemfile <<-G
diff --git a/spec/bundler/commands/config_spec.rb b/spec/bundler/commands/config_spec.rb
index 6148b1c7ce..ede93f99eb 100644
--- a/spec/bundler/commands/config_spec.rb
+++ b/spec/bundler/commands/config_spec.rb
@@ -143,17 +143,15 @@ RSpec.describe ".bundle/config" do
end
it "has lower precedence than env" do
- begin
- ENV["BUNDLE_FOO"] = "env"
+ ENV["BUNDLE_FOO"] = "env"
- bundle "config set --global foo global"
- expect(out).to match(/You have a bundler environment variable for foo set to "env"/)
+ bundle "config set --global foo global"
+ expect(out).to match(/You have a bundler environment variable for foo set to "env"/)
- run "puts Bundler.settings[:foo]"
- expect(out).to eq("env")
- ensure
- ENV.delete("BUNDLE_FOO")
- end
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("env")
+ ensure
+ ENV.delete("BUNDLE_FOO")
end
it "can be deleted" do
@@ -221,15 +219,13 @@ RSpec.describe ".bundle/config" do
end
it "has higher precedence than env" do
- begin
- ENV["BUNDLE_FOO"] = "env"
- bundle "config set --local foo local"
-
- run "puts Bundler.settings[:foo]"
- expect(out).to eq("local")
- ensure
- ENV.delete("BUNDLE_FOO")
- end
+ ENV["BUNDLE_FOO"] = "env"
+ bundle "config set --local foo local"
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("local")
+ ensure
+ ENV.delete("BUNDLE_FOO")
end
it "can be deleted" do
diff --git a/spec/bundler/commands/doctor_spec.rb b/spec/bundler/commands/doctor_spec.rb
index 1eeb276105..1afac00923 100644
--- a/spec/bundler/commands/doctor_spec.rb
+++ b/spec/bundler/commands/doctor_spec.rb
@@ -134,7 +134,7 @@ RSpec.describe "bundle doctor" do
end
end
- context "when home contains filesname with special characters" do
+ context "when home contains filenames with special characters" do
it "escape filename before command execute" do
doctor = Bundler::CLI::Doctor.new({})
expect(doctor).to receive(:`).with("/usr/bin/otool -L \\$\\(date\\)\\ \\\"\\'\\\\.bundle").and_return("dummy string")
diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb
index c6947afeae..099cdb39fa 100644
--- a/spec/bundler/commands/exec_spec.rb
+++ b/spec/bundler/commands/exec_spec.rb
@@ -2,11 +2,10 @@
RSpec.describe "bundle exec" do
let(:system_gems_to_install) { %w[rack-1.0.0 rack-0.9.1] }
- before :each do
- system_gems(system_gems_to_install, :path => default_bundle_path)
- end
it "works with --gemfile flag" do
+ system_gems(system_gems_to_install, :path => default_bundle_path)
+
create_file "CustomGemfile", <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", "1.0.0"
@@ -17,6 +16,8 @@ RSpec.describe "bundle exec" do
end
it "activates the correct gem" do
+ system_gems(system_gems_to_install, :path => default_bundle_path)
+
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
@@ -27,6 +28,8 @@ RSpec.describe "bundle exec" do
end
it "works and prints no warnings when HOME is not writable" do
+ system_gems(system_gems_to_install, :path => default_bundle_path)
+
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
@@ -209,8 +212,6 @@ RSpec.describe "bundle exec" do
end
context "with default gems" do
- let(:system_gems_to_install) { [] }
-
let(:default_irb_version) { ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", :raise_on_error => false }
context "when not specified in Gemfile" do
@@ -402,6 +403,8 @@ RSpec.describe "bundle exec" do
end
it "raises a helpful error when exec'ing to something outside of the bundle" do
+ system_gems(system_gems_to_install, :path => default_bundle_path)
+
bundle "config set clean false" # want to keep the rackup binstub
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -706,6 +709,8 @@ RSpec.describe "bundle exec" do
RUBY
before do
+ system_gems(system_gems_to_install, :path => default_bundle_path)
+
bundled_app(path).open("w") {|f| f << executable }
bundled_app(path).chmod(0o755)
@@ -911,6 +916,30 @@ Run `bundle install` to install missing gems.
end
end
+ context "when Bundler.setup fails and Gemfile is not the default" do
+ before do
+ create_file "CustomGemfile", <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'rack', '2'
+ G
+ ENV["BUNDLER_FORCE_TTY"] = "true"
+ ENV["BUNDLE_GEMFILE"] = "CustomGemfile"
+ ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"] = nil
+ end
+
+ let(:exit_code) { Bundler::GemNotFound.new.status_code }
+ let(:expected) { "" }
+
+ it "prints proper suggestion" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to include("Run `bundle install --gemfile CustomGemfile` to install missing gems.")
+ expect(out).to eq(expected)
+ end
+ end
+
context "when the executable exits non-zero via at_exit" do
let(:executable) { super() + "\n\nat_exit { $! ? raise($!) : exit(1) }" }
let(:exit_code) { 1 }
diff --git a/spec/bundler/commands/fund_spec.rb b/spec/bundler/commands/fund_spec.rb
index 5a0c5411da..5415b88eeb 100644
--- a/spec/bundler/commands/fund_spec.rb
+++ b/spec/bundler/commands/fund_spec.rb
@@ -5,20 +5,20 @@ RSpec.describe "bundle fund" do
build_repo2 do
build_gem "has_funding_and_other_metadata" do |s|
s.metadata = {
- "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
- "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
"documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
- "homepage_uri" => "https://bestgemever.example.io",
- "mailing_list_uri" => "https://groups.example.com/bestgemever",
- "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
- "source_code_uri" => "https://example.com/user/bestgemever",
- "wiki_uri" => "https://example.com/user/bestgemever/wiki",
+ "homepage_uri" => "https://bestgemever.example.io",
+ "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
+ "source_code_uri" => "https://example.com/user/bestgemever",
+ "wiki_uri" => "https://example.com/user/bestgemever/wiki",
}
end
build_gem "has_funding", "1.2.3" do |s|
s.metadata = {
- "funding_uri" => "https://example.com/has_funding/funding",
+ "funding_uri" => "https://example.com/has_funding/funding",
}
end
diff --git a/spec/bundler/commands/help_spec.rb b/spec/bundler/commands/help_spec.rb
index f72763900e..409c49f9e1 100644
--- a/spec/bundler/commands/help_spec.rb
+++ b/spec/bundler/commands/help_spec.rb
@@ -23,8 +23,8 @@ RSpec.describe "bundle help" do
end
it "still outputs the old help for commands that do not have man pages yet" do
- bundle "help version"
- expect(out).to include("Prints the bundler's version information")
+ bundle "help fund"
+ expect(out).to include("Lists information about gems seeking funding assistance")
end
it "looks for a binary and executes it with --help option if it's named bundler-<task>" do
diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb
index 74943703a2..851f50240f 100644
--- a/spec/bundler/commands/info_spec.rb
+++ b/spec/bundler/commands/info_spec.rb
@@ -6,13 +6,13 @@ RSpec.describe "bundle info" do
build_repo2 do
build_gem "has_metadata" do |s|
s.metadata = {
- "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
- "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
"documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
- "homepage_uri" => "https://bestgemever.example.io",
- "mailing_list_uri" => "https://groups.example.com/bestgemever",
- "source_code_uri" => "https://example.com/user/bestgemever",
- "wiki_uri" => "https://example.com/user/bestgemever/wiki",
+ "homepage_uri" => "https://bestgemever.example.io",
+ "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ "source_code_uri" => "https://example.com/user/bestgemever",
+ "wiki_uri" => "https://example.com/user/bestgemever/wiki",
}
end
end
@@ -163,10 +163,10 @@ RSpec.describe "bundle info" do
expect(the_bundle).to include_gems "foo 1.0"
bundle "info foo"
- expect(out).to include("foo (1.0 #{@git.ref_for("master", 6)}")
+ expect(out).to include("foo (1.0 #{@git.ref_for("main", 6)}")
end
- it "prints out branch names other than master" do
+ it "prints out branch names other than main" do
update_git "foo", :branch => "omg" do |s|
s.write "lib/foo.rb", "FOO = '1.0.omg'"
end
@@ -215,7 +215,7 @@ RSpec.describe "bundle info" do
G
bundle "info rac"
- expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>)?\z/)
+ expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>.*)?\z/)
end
end
diff --git a/spec/bundler/commands/init_spec.rb b/spec/bundler/commands/init_spec.rb
index 683a453c7d..6aa3e9edd1 100644
--- a/spec/bundler/commands/init_spec.rb
+++ b/spec/bundler/commands/init_spec.rb
@@ -7,6 +7,29 @@ RSpec.describe "bundle init" do
expect(bundled_app_gemfile).to be_file
end
+ context "with a template with permission flags not matching current process umask" do
+ let(:template_file) do
+ gemfile = Bundler.preferred_gemfile_name
+ templates_dir.join(gemfile)
+ end
+
+ let(:target_dir) { bundled_app("init_permissions_test") }
+
+ around do |example|
+ old_chmod = File.stat(template_file).mode
+ FileUtils.chmod(old_chmod | 0o111, template_file) # chmod +x
+ example.run
+ FileUtils.chmod(old_chmod, template_file)
+ end
+
+ it "honours the current process umask when generating from a template" do
+ FileUtils.mkdir(target_dir)
+ bundle :init, :dir => target_dir
+ generated_mode = File.stat(File.join(target_dir, "Gemfile")).mode & 0o111
+ expect(generated_mode).to be_zero
+ end
+ end
+
context "when a Gemfile already exists" do
before do
create_file "Gemfile", <<-G
@@ -42,7 +65,7 @@ RSpec.describe "bundle init" do
context "when the dir is not writable by the current user" do
let(:subdir) { "child_dir" }
- it "notifies the user that it can not write to it" do
+ it "notifies the user that it cannot write to it" do
FileUtils.mkdir bundled_app(subdir)
# chmod a-w it
mode = File.stat(bundled_app(subdir)).mode ^ 0o222
@@ -168,4 +191,17 @@ RSpec.describe "bundle init" do
end
end
end
+
+ describe "using the --gemfile" do
+ it "should use the --gemfile value to name the gemfile" do
+ custom_gemfile_name = "NiceGemfileName"
+
+ bundle :init, :gemfile => custom_gemfile_name
+
+ expect(out).to include("Writing new #{custom_gemfile_name}")
+ used_template = File.read("#{source_root}/lib/bundler/templates/Gemfile")
+ generated_gemfile = bundled_app(custom_gemfile_name).read
+ expect(generated_gemfile).to eq(used_template)
+ end
+ end
end
diff --git a/spec/bundler/commands/inject_spec.rb b/spec/bundler/commands/inject_spec.rb
index 92e86bd6cc..d711fe010d 100644
--- a/spec/bundler/commands/inject_spec.rb
+++ b/spec/bundler/commands/inject_spec.rb
@@ -109,7 +109,7 @@ Usage: "bundle inject GEM VERSION"
gem "rack-obama"
G
bundle "inject 'rack' '> 0'", :raise_on_error => false
- expect(err).to match(/trying to install in deployment mode after changing/)
+ expect(err).to match(/the lockfile can't be updated because frozen mode is set/)
expect(bundled_app_lock.read).not_to match(/rack-obama/)
end
diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb
index 57cff4e3b3..f572bdf176 100644
--- a/spec/bundler/commands/install_spec.rb
+++ b/spec/bundler/commands/install_spec.rb
@@ -285,7 +285,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "installs gems for windows" do
- simulate_platform mswin
+ simulate_platform x86_mswin32
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -293,7 +293,7 @@ RSpec.describe "bundle install with gem sources" do
G
run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
- expect(out).to eq("1.0.0 MSWIN")
+ expect(out).to eq("1.0 x86-mswin32")
end
end
@@ -522,14 +522,14 @@ RSpec.describe "bundle install with gem sources" do
ruby '~> 1.2'
source "#{file_uri_for(gem_repo1)}"
G
- expect(err).to include("Your Ruby version is #{RUBY_VERSION}, but your Gemfile specified ~> 1.2")
+ expect(err).to include("Your Ruby version is #{Gem.ruby_version}, but your Gemfile specified ~> 1.2")
end
end
context "and using a supported Ruby version" do
before do
install_gemfile <<-G
- ruby '~> #{RUBY_VERSION}'
+ ruby '~> #{Gem.ruby_version}'
source "#{file_uri_for(gem_repo1)}"
G
end
@@ -555,7 +555,7 @@ RSpec.describe "bundle install with gem sources" do
it "updates Gemfile.lock with updated yet still compatible ruby version" do
install_gemfile <<-G
- ruby '~> #{RUBY_VERSION[0..2]}'
+ ruby '~> #{current_ruby_minor}'
source "#{file_uri_for(gem_repo1)}"
G
@@ -913,7 +913,7 @@ RSpec.describe "bundle install with gem sources" do
gemfile <<-G
source "https://gem.repo4"
- ruby "#{RUBY_VERSION}"
+ ruby "#{Gem.ruby_version}"
gem "loofah", "~> 2.12.0"
G
@@ -1000,6 +1000,50 @@ RSpec.describe "bundle install with gem sources" do
end
end
+ context "with only option" do
+ before do
+ bundle "config set only a:b"
+ end
+
+ it "installs only gems of the specified groups" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rails"
+ gem "rack", group: :a
+ gem "rake", group: :b
+ gem "yard", group: :c
+ G
+
+ expect(out).to include("Installing rack")
+ expect(out).to include("Installing rake")
+ expect(out).not_to include("Installing yard")
+ end
+ end
+
+ context "with --prefer-local flag" do
+ before do
+ build_repo4 do
+ build_gem "foo", "1.0.1"
+ build_gem "foo", "1.0.0"
+ build_gem "bar", "1.0.0"
+ end
+
+ system_gems "foo-1.0.0", :path => default_bundle_path, :gem_repo => gem_repo4
+ end
+
+ it "fetches remote sources only when not available locally" do
+ install_gemfile <<-G, :"prefer-local" => true, :verbose => true
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "foo"
+ gem "bar"
+ G
+
+ expect(out).to include("Using foo 1.0.0").and include("Fetching bar 1.0.0").and include("Installing bar 1.0.0")
+ expect(last_command).to be_success
+ end
+ end
+
context "with a symlinked configured as bundle path and a gem with symlinks" do
before do
symlinked_bundled_app = tmp("bundled_app-symlink")
@@ -1032,4 +1076,31 @@ RSpec.describe "bundle install with gem sources" do
G
end
end
+
+ context "when a gem has equivalent versions with inconsistent dependencies" do
+ before do
+ build_repo4 do
+ build_gem "autobuild", "1.10.rc2" do |s|
+ s.add_dependency "utilrb", ">= 1.6.0"
+ end
+
+ build_gem "autobuild", "1.10.0.rc2" do |s|
+ s.add_dependency "utilrb", ">= 2.0"
+ end
+ end
+ end
+
+ it "does not crash unexpectedly" do
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "autobuild", "1.10.rc2"
+ G
+
+ bundle "install --jobs 1", :raise_on_error => false
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+ expect(err).to include("Could not find compatible versions")
+ end
+ end
end
diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb
index b20a6ded43..491fa30949 100644
--- a/spec/bundler/commands/lock_spec.rb
+++ b/spec/bundler/commands/lock_spec.rb
@@ -140,6 +140,62 @@ RSpec.describe "bundle lock" do
expect(read_lockfile).to eq(@lockfile)
end
+ it "does not unlock git sources when only uri shape changes" do
+ build_git("foo")
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}"
+ G
+
+ # Change uri format to end with "/" and reinstall
+ install_gemfile <<-G, :verbose => true
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}/"
+ G
+
+ expect(out).to include("using resolution from the lockfile")
+ expect(out).not_to include("re-resolving dependencies because the list of sources changed")
+ end
+
+ it "updates specific gems using --update using the locked revision of unrelated git gems for resolving" do
+ ref = build_git("foo").ref_for("HEAD")
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rake"
+ gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :branch => "deadbeef"
+ G
+
+ lockfile <<~L
+ GIT
+ remote: #{file_uri_for(lib_path("foo-1.0"))}
+ revision: #{ref}
+ branch: deadbeef
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+ rake (10.0.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+ rake
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update rake --verbose"
+ expect(out).to match(/Writing lockfile to.+lock/)
+ expect(lockfile).to include("rake (13.0.1)")
+ end
+
it "errors when updating a missing specific gems using --update" do
lockfile @lockfile
@@ -176,7 +232,10 @@ RSpec.describe "bundle lock" do
build_gem "foo", %w[1.5.1] do |s|
s.add_dependency "bar", "~> 3.0"
end
- build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ build_gem "foo", %w[2.0.0.pre] do |s|
+ s.add_dependency "bar"
+ end
+ build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 2.1.2.pre 3.0.0 3.1.0.pre 4.0.0.pre]
build_gem "qux", %w[1.0.0 1.0.1 1.1.0 2.0.0]
end
@@ -210,6 +269,55 @@ RSpec.describe "bundle lock" do
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort)
end
+
+ context "pre" do
+ it "defaults to major" do
+ bundle "lock --update --pre"
+
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
+ end
+
+ it "patch preferred" do
+ bundle "lock --update --patch --pre"
+
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.4.5 bar-2.1.2.pre qux-1.0.1].sort)
+ end
+
+ it "minor preferred" do
+ bundle "lock --update --minor --pre"
+
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.1 bar-3.1.0.pre qux-1.1.0].sort)
+ end
+
+ it "major preferred" do
+ bundle "lock --update --major --pre"
+
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
+ end
+ end
+ end
+
+ it "updates the bundler version in the lockfile to the latest bundler version" do
+ build_repo4 do
+ build_gem "bundler", "55"
+ end
+
+ system_gems "bundler-55", :gem_repo => gem_repo4
+
+ install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "https://gems.repo4"
+ G
+ lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
+
+ bundle "lock --update --bundler --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(lockfile).to end_with("BUNDLED WITH\n 55\n")
+
+ update_repo4 do
+ build_gem "bundler", "99"
+ end
+
+ bundle "lock --update --bundler --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(lockfile).to end_with("BUNDLED WITH\n 99\n")
end
it "supports adding new platforms" do
@@ -217,7 +325,7 @@ RSpec.describe "bundle lock" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array([java, mingw, specific_local_platform].uniq)
+ expect(lockfile.platforms).to match_array([java, x86_mingw32, local_platform].uniq)
end
it "supports adding new platforms with force_ruby_platform = true" do
@@ -226,11 +334,11 @@ RSpec.describe "bundle lock" do
remote: #{file_uri_for(gem_repo1)}/
specs:
platform_specific (1.0)
- platform_specific (1.0-x86-linux)
+ platform_specific (1.0-x86-64_linux)
PLATFORMS
ruby
- x86-linux
+ x86_64-linux
DEPENDENCIES
platform_specific
@@ -241,7 +349,7 @@ RSpec.describe "bundle lock" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to contain_exactly(rb, linux, java, mingw)
+ expect(lockfile.platforms).to contain_exactly(rb, linux, java, x86_mingw32)
end
it "supports adding the `ruby` platform" do
@@ -249,7 +357,7 @@ RSpec.describe "bundle lock" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array(["ruby", specific_local_platform].uniq)
+ expect(lockfile.platforms).to match_array(["ruby", local_platform].uniq)
end
it "warns when adding an unknown platform" do
@@ -262,16 +370,71 @@ RSpec.describe "bundle lock" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array([java, mingw, specific_local_platform].uniq)
+ expect(lockfile.platforms).to match_array([java, x86_mingw32, local_platform].uniq)
bundle "lock --remove-platform java"
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array([mingw, specific_local_platform].uniq)
+ expect(lockfile.platforms).to match_array([x86_mingw32, local_platform].uniq)
+ end
+
+ it "also cleans up redundant platform gems when removing platforms" do
+ build_repo4 do
+ build_gem "nokogiri", "1.12.0"
+ build_gem "nokogiri", "1.12.0" do |s|
+ s.platform = "x86_64-darwin"
+ end
+ end
+
+ simulate_platform "x86_64-darwin-22" do
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ G
+ end
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.12.0)
+ nokogiri (1.12.0-x86_64-darwin)
+
+ PLATFORMS
+ ruby
+ x86_64-darwin
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-darwin-22" do
+ bundle "lock --remove-platform ruby"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.12.0-x86_64-darwin)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
it "errors when removing all platforms" do
- bundle "lock --remove-platform #{specific_local_platform}", :raise_on_error => false
+ bundle "lock --remove-platform #{local_platform}", :raise_on_error => false
expect(err).to include("Removing all platforms from the bundle is not allowed")
end
@@ -280,7 +443,7 @@ RSpec.describe "bundle lock" do
build_repo4 do
build_gem "ffi", "1.9.14"
build_gem "ffi", "1.9.14" do |s|
- s.platform = mingw
+ s.platform = x86_mingw32
end
build_gem "gssapi", "0.1"
@@ -312,7 +475,7 @@ RSpec.describe "bundle lock" do
gem "gssapi"
G
- simulate_platform(mingw) { bundle :lock }
+ simulate_platform(x86_mingw32) { bundle :lock }
expect(lockfile).to eq <<~G
GEM
@@ -540,12 +703,66 @@ RSpec.describe "bundle lock" do
bundle "lock --add-platform x86_64-linux", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
end
- it "respects lower bound ruby requirements" do
- skip "this spec does not work with prereleases because their version is actually lower than their reported `RUBY_VERSION`" if RUBY_PATCHLEVEL == -1
+ it "does not crash on conflicting ruby requirements between platform versions in two different gems" do
+ build_repo4 do
+ build_gem "unf_ext", "0.0.8.2"
+
+ build_gem "unf_ext", "0.0.8.2" do |s|
+ s.required_ruby_version = [">= 2.4", "< #{previous_ruby_minor}"]
+ s.platform = "x64-mingw32"
+ end
+
+ build_gem "unf_ext", "0.0.8.2" do |s|
+ s.required_ruby_version = [">= #{previous_ruby_minor}", "< #{current_ruby_minor}"]
+ s.platform = "x64-mingw-ucrt"
+ end
+
+ build_gem "google-protobuf", "3.21.12"
+
+ build_gem "google-protobuf", "3.21.12" do |s|
+ s.required_ruby_version = [">= 2.5", "< #{previous_ruby_minor}"]
+ s.platform = "x64-mingw32"
+ end
+
+ build_gem "google-protobuf", "3.21.12" do |s|
+ s.required_ruby_version = [">= #{previous_ruby_minor}", "< #{current_ruby_minor}"]
+ s.platform = "x64-mingw-ucrt"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "google-protobuf"
+ gem "unf_ext"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ google-protobuf (3.21.12)
+ unf_ext (0.0.8.2)
+ PLATFORMS
+ x64-mingw-ucrt
+ x64-mingw32
+
+ DEPENDENCIES
+ google-protobuf
+ unf_ext
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "DEBUG_RESOLVER" => "1" }
+ end
+
+ it "respects lower bound ruby requirements" do
build_repo4 do
build_gem "our_private_gem", "0.1.0" do |s|
- s.required_ruby_version = ">= #{RUBY_VERSION}"
+ s.required_ruby_version = ">= #{Gem.ruby_version}"
end
end
@@ -597,4 +814,460 @@ RSpec.describe "bundle lock" do
expect(read_lockfile).to eq(@lockfile.sub("foo (1.0)", "foo (2.0)").sub(/foo$/, "foo (= 2.0)"))
end
end
+
+ context "when a system gem has incorrect dependencies, different from the lockfile" do
+ before do
+ build_repo4 do
+ build_gem "debug", "1.6.3" do |s|
+ s.add_dependency "irb", ">= 1.3.6"
+ end
+
+ build_gem "irb", "1.5.0"
+ end
+
+ system_gems "irb-1.5.0", :gem_repo => gem_repo4
+ system_gems "debug-1.6.3", :gem_repo => gem_repo4
+
+ # simulate gemspec with wrong empty dependencies
+ debug_gemspec_path = system_gem_path("specifications/debug-1.6.3.gemspec")
+ debug_gemspec = Gem::Specification.load(debug_gemspec_path.to_s)
+ debug_gemspec.dependencies.clear
+ File.write(debug_gemspec_path, debug_gemspec.to_ruby)
+ end
+
+ it "respects the existing lockfile, even when reresolving" do
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "debug"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ debug (1.6.3)
+ irb (>= 1.3.6)
+ irb (1.5.0)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ debug
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "arm64-darwin-22" do
+ bundle "lock"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ debug (1.6.3)
+ irb (>= 1.3.6)
+ irb (1.5.0)
+
+ PLATFORMS
+ arm64-darwin-22
+ x86_64-linux
+
+ DEPENDENCIES
+ debug
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "properly shows resolution errors including OR requirements" do
+ build_repo4 do
+ build_gem "activeadmin", "2.13.1" do |s|
+ s.add_dependency "railties", ">= 6.1", "< 7.1"
+ end
+ build_gem "actionpack", "6.1.4"
+ build_gem "actionpack", "7.0.3.1"
+ build_gem "actionpack", "7.0.4"
+ build_gem "railties", "6.1.4" do |s|
+ s.add_dependency "actionpack", "6.1.4"
+ end
+ build_gem "rails", "7.0.3.1" do |s|
+ s.add_dependency "railties", "7.0.3.1"
+ end
+ build_gem "rails", "7.0.4" do |s|
+ s.add_dependency "railties", "7.0.4"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "rails", ">= 7.0.3.1"
+ gem "activeadmin", "2.13.1"
+ G
+
+ bundle "lock", :raise_on_error => false
+
+ expect(err).to eq <<~ERR.strip
+ Could not find compatible versions
+
+ Because rails >= 7.0.4 depends on railties = 7.0.4
+ and rails < 7.0.4 depends on railties = 7.0.3.1,
+ railties = 7.0.3.1 OR = 7.0.4 is required.
+ So, because railties = 7.0.3.1 OR = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally,
+ version solving has failed.
+ ERR
+ end
+
+ it "is able to display some explanation on crazy irresolvable cases" do
+ build_repo4 do
+ build_gem "activeadmin", "2.13.1" do |s|
+ s.add_dependency "ransack", "= 3.1.0"
+ end
+
+ # Activemodel is missing as a dependency in lockfile
+ build_gem "ransack", "3.1.0" do |s|
+ s.add_dependency "activemodel", ">= 6.0.4"
+ s.add_dependency "activesupport", ">= 6.0.4"
+ end
+
+ %w[6.0.4 7.0.2.3 7.0.3.1 7.0.4].each do |version|
+ build_gem "activesupport", version
+
+ # Activemodel is only available on 6.0.4
+ if version == "6.0.4"
+ build_gem "activemodel", version do |s|
+ s.add_dependency "activesupport", version
+ end
+ end
+
+ build_gem "rails", version do |s|
+ # Depednencies of Rails 7.0.2.3 are in reverse order
+ if version == "7.0.2.3"
+ s.add_dependency "activesupport", version
+ s.add_dependency "activemodel", version
+ else
+ s.add_dependency "activemodel", version
+ s.add_dependency "activesupport", version
+ end
+ end
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "rails", ">= 7.0.2.3"
+ gem "activeadmin", "= 2.13.1"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ activeadmin (2.13.1)
+ ransack (= 3.1.0)
+ ransack (3.1.0)
+ activemodel (>= 6.0.4)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ activeadmin (= 2.13.1)
+ ransack (= 3.1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock", :raise_on_error => false
+
+ expect(err).to eq <<~ERR.strip
+ Could not find compatible versions
+
+ Because every version of activemodel depends on activesupport = 6.0.4
+ and rails >= 7.0.2.3, < 7.0.3.1 depends on activesupport = 7.0.2.3,
+ every version of activemodel is incompatible with rails >= 7.0.2.3, < 7.0.3.1.
+ And because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3,
+ rails >= 7.0.2.3, < 7.0.3.1 cannot be used.
+ (1) So, because rails >= 7.0.3.1, < 7.0.4 depends on activemodel = 7.0.3.1
+ and rails >= 7.0.4 depends on activemodel = 7.0.4,
+ rails >= 7.0.2.3 requires activemodel = 7.0.3.1 OR = 7.0.4.
+
+ Because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3
+ and rails >= 7.0.3.1, < 7.0.4 depends on activesupport = 7.0.3.1,
+ rails >= 7.0.2.3, < 7.0.4 requires activemodel = 7.0.2.3 or activesupport = 7.0.3.1.
+ And because rails >= 7.0.4 depends on activesupport = 7.0.4
+ and every version of activemodel depends on activesupport = 6.0.4,
+ activemodel != 7.0.2.3 is incompatible with rails >= 7.0.2.3.
+ And because rails >= 7.0.2.3 requires activemodel = 7.0.3.1 OR = 7.0.4 (1),
+ rails >= 7.0.2.3 cannot be used.
+ So, because Gemfile depends on rails >= 7.0.2.3,
+ version solving has failed.
+ ERR
+
+ lockfile lockfile.gsub(/PLATFORMS\n #{lockfile_platforms}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}")
+
+ bundle "lock", :raise_on_error => false
+
+ expect(err).to eq <<~ERR.strip
+ Could not find compatible versions
+
+ Because rails >= 7.0.3.1, < 7.0.4 depends on activemodel = 7.0.3.1
+ and rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3,
+ rails >= 7.0.2.3, < 7.0.4 requires activemodel = 7.0.2.3 OR = 7.0.3.1.
+ And because every version of activemodel depends on activesupport = 6.0.4,
+ rails >= 7.0.2.3, < 7.0.4 requires activesupport = 6.0.4.
+ Because rails >= 7.0.3.1, < 7.0.4 depends on activesupport = 7.0.3.1
+ and rails >= 7.0.2.3, < 7.0.3.1 depends on activesupport = 7.0.2.3,
+ rails >= 7.0.2.3, < 7.0.4 requires activesupport = 7.0.2.3 OR = 7.0.3.1.
+ Thus, rails >= 7.0.2.3, < 7.0.4 cannot be used.
+ And because rails >= 7.0.4 depends on activemodel = 7.0.4,
+ rails >= 7.0.2.3 requires activemodel = 7.0.4.
+ So, because activemodel = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally
+ and Gemfile depends on rails >= 7.0.2.3,
+ version solving has failed.
+ ERR
+ end
+
+ it "does not accidentally resolves to prereleases" do
+ build_repo4 do
+ build_gem "autoproj", "2.0.3" do |s|
+ s.add_dependency "autobuild", ">= 1.10.0.a"
+ s.add_dependency "tty-prompt"
+ end
+
+ build_gem "tty-prompt", "0.6.0"
+ build_gem "tty-prompt", "0.7.0"
+
+ build_gem "autobuild", "1.10.0.b3"
+ build_gem "autobuild", "1.10.1" do |s|
+ s.add_dependency "tty-prompt", "~> 0.6.0"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "autoproj", ">= 2.0.0"
+ G
+
+ bundle "lock"
+ expect(lockfile).to_not include("autobuild (1.10.0.b3)")
+ expect(lockfile).to include("autobuild (1.10.1)")
+ end
+
+ # Newer rails depends on Bundler, while ancient Rails does not. Bundler tries
+ # a first resolution pass that does not consider pre-releases. However, when
+ # using a pre-release Bundler (like the .dev version), that results in that
+ # pre-release being ignored and resolving to a version that does not depend on
+ # Bundler at all. We should avoid that and still consider .dev Bundler.
+ #
+ it "does not ignore prereleases with there's only one candidate" do
+ build_repo4 do
+ build_gem "rails", "7.4.0.2" do |s|
+ s.add_dependency "bundler", ">= 1.15.0"
+ end
+
+ build_gem "rails", "2.3.18"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "rails"
+ G
+
+ bundle "lock"
+ expect(lockfile).to_not include("rails (2.3.18)")
+ expect(lockfile).to include("rails (7.4.0.2)")
+ end
+
+ it "deals with platform specific incompatibilities" do
+ build_repo4 do
+ build_gem "activerecord", "6.0.6"
+ build_gem "activerecord-jdbc-adapter", "60.4" do |s|
+ s.platform = "java"
+ s.add_dependency "activerecord", "~> 6.0.0"
+ end
+ build_gem "activerecord-jdbc-adapter", "61.0" do |s|
+ s.platform = "java"
+ s.add_dependency "activerecord", "~> 6.1.0"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "activerecord", "6.0.6"
+ gem "activerecord-jdbc-adapter", "61.0"
+ G
+
+ simulate_platform "universal-java-19" do
+ bundle "lock", :raise_on_error => false
+ end
+
+ expect(err).to include("Could not find compatible versions")
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+ end
+
+ context "when re-resolving to include prereleases" do
+ before do
+ build_repo4 do
+ build_gem "tzinfo-data", "1.2022.7"
+ build_gem "rails", "7.1.0.alpha" do |s|
+ s.add_dependency "activesupport"
+ end
+ build_gem "activesupport", "7.1.0.alpha"
+ end
+ end
+
+ it "does not end up including gems scoped to other platforms in the lockfile" do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "rails"
+ gem "tzinfo-data", platform: :windows
+ G
+
+ simulate_platform "x86_64-darwin-22" do
+ bundle "lock"
+ end
+
+ expect(lockfile).not_to include("tzinfo-data (1.2022.7)")
+ end
+ end
+
+ context "when resolving platform specific gems as indirect dependencies on truffleruby", :truffleruby_only do
+ before do
+ build_lib "foo", :path => bundled_app do |s|
+ s.add_dependency "nokogiri"
+ end
+
+ build_repo4 do
+ build_gem "nokogiri", "1.14.2"
+ build_gem "nokogiri", "1.14.2" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gemspec
+ G
+ end
+
+ it "locks ruby specs" do
+ simulate_platform "x86_64-linux" do
+ bundle "lock"
+ end
+
+ expect(lockfile).to eq <<~L
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+ nokogiri
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.2)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when adding a new gem that requires unlocking other transitive deps" do
+ before do
+ build_repo4 do
+ build_gem "govuk_app_config", "0.1.0"
+
+ build_gem "govuk_app_config", "4.13.0" do |s|
+ s.add_dependency "railties", ">= 5.0"
+ end
+
+ %w[7.0.4.1 7.0.4.3].each do |v|
+ build_gem "railties", v do |s|
+ s.add_dependency "actionpack", v
+ s.add_dependency "activesupport", v
+ end
+
+ build_gem "activesupport", v
+ build_gem "actionpack", v
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "govuk_app_config"
+ gem "activesupport", "7.0.4.3"
+ G
+
+ # Simulate out of sync lockfile because top level dependency on
+ # activesuport has just been added to the Gemfile, and locked to a higher
+ # version
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ actionpack (7.0.4.1)
+ activesupport (7.0.4.1)
+ govuk_app_config (4.13.0)
+ railties (>= 5.0)
+ railties (7.0.4.1)
+ actionpack (= 7.0.4.1)
+ activesupport (= 7.0.4.1)
+
+ PLATFORMS
+ arm64-darwin-22
+
+ DEPENDENCIES
+ govuk_app_config
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "does not downgrade top level dependencies" do
+ simulate_platform "arm64-darwin-22" do
+ bundle "lock"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ actionpack (7.0.4.3)
+ activesupport (7.0.4.3)
+ govuk_app_config (4.13.0)
+ railties (>= 5.0)
+ railties (7.0.4.3)
+ actionpack (= 7.0.4.3)
+ activesupport (= 7.0.4.3)
+
+ PLATFORMS
+ arm64-darwin-22
+
+ DEPENDENCIES
+ activesupport (= 7.0.4.3)
+ govuk_app_config
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
end
diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb
index 55a04b69c5..ede1ff6b8e 100644
--- a/spec/bundler/commands/newgem_spec.rb
+++ b/spec/bundler/commands/newgem_spec.rb
@@ -310,30 +310,30 @@ RSpec.describe "bundle gem" do
expect(last_command).to be_success
end
- it "has no rubocop offenses when using --ext and --linter=rubocop flag", :readline do
+ it "has no rubocop offenses when using --ext=c and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --ext --linter=rubocop"
+ bundle "gem #{gem_name} --ext=c --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
- it "has no rubocop offenses when using --ext, --test=minitest, and --linter=rubocop flag", :readline do
+ it "has no rubocop offenses when using --ext=c, --test=minitest, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --ext --test=minitest --linter=rubocop"
+ bundle "gem #{gem_name} --ext=c --test=minitest --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
- it "has no rubocop offenses when using --ext, --test=rspec, and --linter=rubocop flag", :readline do
+ it "has no rubocop offenses when using --ext=c, --test=rspec, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --ext --test=rspec --linter=rubocop"
+ bundle "gem #{gem_name} --ext=c --test=rspec --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
- it "has no rubocop offenses when using --ext, --ext=test-unit, and --linter=rubocop flag", :readline do
+ it "has no rubocop offenses when using --ext=c, --test=test-unit, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --ext --test=test-unit --linter=rubocop"
+ bundle "gem #{gem_name} --ext=c --test=test-unit --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
@@ -345,10 +345,45 @@ RSpec.describe "bundle gem" do
expect(last_command).to be_success
end
+ it "has no rubocop offenses when using --ext=rust and --linter=rubocop flag", :readline do
+ skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
+ skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
+
+ bundle "gem #{gem_name} --ext=rust --linter=rubocop"
+ bundle_exec_rubocop
+ expect(last_command).to be_success
+ end
+
+ it "has no rubocop offenses when using --ext=rust, --test=minitest, and --linter=rubocop flag", :readline do
+ skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
+ skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
+
+ bundle "gem #{gem_name} --ext=rust --test=minitest --linter=rubocop"
+ bundle_exec_rubocop
+ expect(last_command).to be_success
+ end
+
+ it "has no rubocop offenses when using --ext=rust, --test=rspec, and --linter=rubocop flag", :readline do
+ skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
+ skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
+
+ bundle "gem #{gem_name} --ext=rust --test=rspec --linter=rubocop"
+ bundle_exec_rubocop
+ expect(last_command).to be_success
+ end
+
+ it "has no rubocop offenses when using --ext=rust, --test=test-unit, and --linter=rubocop flag", :readline do
+ skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
+ skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
+
+ bundle "gem #{gem_name} --ext=rust --test=test-unit --linter=rubocop"
+ bundle_exec_rubocop
+ expect(last_command).to be_success
+ end
+
shared_examples_for "CI config is absent" do
it "does not create any CI files" do
expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
end
@@ -598,11 +633,19 @@ RSpec.describe "bundle gem" do
it "does not include the gemspec file in files" do
bundle "gem #{gem_name}"
- bundler_gemspec = Bundler::GemHelper.new(gemspec_dir).gemspec
+ bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec
expect(bundler_gemspec.files).not_to include("#{gem_name}.gemspec")
end
+ it "does not include the Gemfile file in files" do
+ bundle "gem #{gem_name}"
+
+ bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec
+
+ expect(bundler_gemspec.files).not_to include("Gemfile")
+ end
+
it "runs rake without problems" do
bundle "gem #{gem_name}"
@@ -888,12 +931,29 @@ RSpec.describe "bundle gem" do
bundle "gem #{gem_name}"
expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
end
end
+ context "--ci set to travis" do
+ it "generates a GitHub Actions config file" do
+ bundle "gem #{gem_name} --ci=travis", :raise_on_error => false
+ expect(err).to include("Support for Travis CI was removed from gem skeleton generator.")
+
+ expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
+ end
+ end
+
+ context "--ci set to travis" do
+ it "generates a GitHub Actions config file" do
+ bundle "gem #{gem_name} --ci=travis", :raise_on_error => false
+ expect(err).to include("Support for Travis CI was removed from gem skeleton generator.")
+
+ expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
+ end
+ end
+
context "--ci set to github" do
it "generates a GitHub Actions config file" do
bundle "gem #{gem_name} --ci=github"
@@ -918,38 +978,32 @@ RSpec.describe "bundle gem" do
end
end
- context "--ci set to travis" do
- it "generates a Travis CI config file" do
- bundle "gem #{gem_name} --ci=travis"
-
- expect(bundled_app("#{gem_name}/.travis.yml")).to exist
- end
- end
-
context "gem.ci setting set to none" do
it "doesn't generate any CI config" do
expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
end
end
- context "gem.ci setting set to github" do
- it "generates a GitHub Actions config file" do
- bundle "config set gem.ci github"
- bundle "gem #{gem_name}"
+ context "gem.ci setting set to travis" do
+ it "errors with friendly message" do
+ bundle "config set gem.ci travis"
+ bundle "gem #{gem_name}", :raise_on_error => false
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
+ expect(err).to include("Support for Travis CI was removed from gem skeleton generator,")
+ expect(err).to include("bundle config unset gem.ci")
+
+ expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
end
end
- context "gem.ci setting set to travis" do
- it "generates a Travis CI config file" do
- bundle "config set gem.ci travis"
+ context "gem.ci setting set to github" do
+ it "generates a GitHub Actions config file" do
+ bundle "config set gem.ci github"
bundle "gem #{gem_name}"
- expect(bundled_app("#{gem_name}/.travis.yml")).to exist
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
end
end
@@ -1322,21 +1376,46 @@ RSpec.describe "bundle gem" do
include_examples "generating a gem"
- context "--ext parameter set" do
- let(:flags) { "--ext" }
+ context "--ext parameter with no value" do
+ context "is deprecated", :bundler => "< 3" do
+ it "prints deprecation when used after gem name" do
+ bundle ["gem", "--ext", gem_name].compact.join(" ")
+ expect(err).to include "[DEPRECATED]"
+ expect(err).to include "`--ext` with no arguments has been deprecated"
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
+ end
+
+ it "prints deprecation when used before gem name" do
+ bundle ["gem", gem_name, "--ext"].compact.join(" ")
+ expect(err).to include "[DEPRECATED]"
+ expect(err).to include "`--ext` with no arguments has been deprecated"
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
+ end
+ end
+ end
+
+ context "--ext parameter set with C" do
+ let(:flags) { "--ext=c" }
before do
bundle ["gem", gem_name, flags].compact.join(" ")
end
+ it "is not deprecated" do
+ expect(err).not_to include "[DEPRECATED] Option `--ext` without explicit value is deprecated."
+ end
+
it "builds ext skeleton" do
expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.h")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
end
- it "includes rake-compiler" do
+ it "includes rake-compiler, but no Rust related changes" do
expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"')
+
+ expect(bundled_app("#{gem_name}/Gemfile").read).to_not include('gem "rb_sys"')
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to_not include('spec.required_rubygems_version = ">= ')
end
it "depends on compile task for build" do
@@ -1358,6 +1437,64 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
end
end
+
+ context "--ext parameter set with rust and old RubyGems" do
+ it "fails in friendly way" do
+ if ::Gem::Version.new("3.3.11") <= ::Gem.rubygems_version
+ skip "RubyGems compatible with Rust builder"
+ end
+
+ expect do
+ bundle ["gem", gem_name, "--ext=rust"].compact.join(" ")
+ end.to raise_error(RuntimeError, /too old to build Rust extension/)
+ end
+ end
+
+ context "--ext parameter set with rust" do
+ let(:flags) { "--ext=rust" }
+
+ before do
+ skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
+
+ bundle ["gem", gem_name, flags].compact.join(" ")
+ end
+
+ it "is not deprecated" do
+ expect(err).not_to include "[DEPRECATED] Option `--ext` without explicit value is deprecated."
+ end
+
+ it "builds ext skeleton" do
+ expect(bundled_app("#{gem_name}/Cargo.toml")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/Cargo.toml")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/src/lib.rs")).to exist
+ end
+
+ it "includes rake-compiler, rb_sys gems and required_rubygems_version constraint" do
+ expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"')
+ expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rb_sys"')
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include('spec.required_rubygems_version = ">= ')
+ end
+
+ it "depends on compile task for build" do
+ rakefile = strip_whitespace <<-RAKEFILE
+ # frozen_string_literal: true
+
+ require "bundler/gem_tasks"
+ require "rb_sys/extensiontask"
+
+ task build: :compile
+
+ RbSys::ExtensionTask.new("#{gem_name}") do |ext|
+ ext.lib_dir = "lib/#{gem_name}"
+ end
+
+ task default: :compile
+ RAKEFILE
+
+ expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
+ end
+ end
end
context "gem naming with dashed", :readline do
diff --git a/spec/bundler/commands/open_spec.rb b/spec/bundler/commands/open_spec.rb
index 53dc35c2c7..e30ebfea5b 100644
--- a/spec/bundler/commands/open_spec.rb
+++ b/spec/bundler/commands/open_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe "bundle open" do
it "does not blow up if the gem to open does not have a Gemfile" do
git = build_git "foo"
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -58,6 +58,64 @@ RSpec.describe "bundle open" do
expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2")}")
end
+ it "opens subpath of the gem" do
+ bundle "open activerecord --path lib/activerecord", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/activerecord")
+ end
+
+ it "opens subpath file of the gem" do
+ bundle "open activerecord --path lib/version.rb", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/version.rb")
+ end
+
+ it "opens deep subpath of the gem" do
+ bundle "open activerecord --path lib/active_record", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/active_record")
+ end
+
+ it "requires value for --path arg" do
+ bundle "open activerecord --path", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ expect(err).to eq "Cannot specify `--path` option without a value"
+ end
+
+ it "suggests alternatives for similar-sounding gems when using subpath" do
+ bundle "open Rails --path README.md", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ expect(err).to match(/did you mean rails\?/i)
+ end
+
+ it "suggests alternatives for similar-sounding gems when using deep subpath" do
+ bundle "open Rails --path some/path/here", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ expect(err).to match(/did you mean rails\?/i)
+ end
+
+ it "opens subpath of the short worded gem" do
+ bundle "open rec --path CHANGELOG.md", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/CHANGELOG.md")
+ end
+
+ it "opens deep subpath of the short worded gem" do
+ bundle "open rec --path lib/activerecord", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/activerecord")
+ end
+
+ it "opens subpath of the selected matching gem", :readline do
+ env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open active --path CHANGELOG.md", :env => env do |input, _, _|
+ input.puts "2"
+ end
+
+ expect(out).to match(%r{bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}/CHANGELOG\.md\z})
+ end
+
+ it "opens deep subpath of the selected matching gem", :readline do
+ env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open active --path lib/activerecord/version.rb", :env => env do |input, _, _|
+ input.puts "2"
+ end
+
+ expect(out).to match(%r{bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}/lib/activerecord/version\.rb\z})
+ end
+
it "select the gem from many match gems", :readline do
env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
bundle "open active", :env => env do |input, _, _|
diff --git a/spec/bundler/commands/outdated_spec.rb b/spec/bundler/commands/outdated_spec.rb
index d0209022a2..bbc3cb54ae 100644
--- a/spec/bundler/commands/outdated_spec.rb
+++ b/spec/bundler/commands/outdated_spec.rb
@@ -192,7 +192,7 @@ RSpec.describe "bundle outdated" do
vcr (6.0.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
dotenv
@@ -604,6 +604,22 @@ RSpec.describe "bundle outdated" do
expect(out).to end_with(expected_output)
end
+ it "only reports gems that have a newer version that matches the specified dependency version requirements, using --strict alias" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ build_gem "weakling", "0.0.5"
+ end
+
+ bundle :outdated, :strict => true, :raise_on_error => false
+
+ expected_output = <<~TABLE.strip
+ Gem Current Latest Requested Groups
+ weakling 0.0.3 0.0.5 ~> 0.0.1 default
+ TABLE
+
+ expect(out).to end_with(expected_output)
+ end
+
it "doesn't crash when some deps unused on the current platform" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -1098,116 +1114,125 @@ RSpec.describe "bundle outdated" do
end
context "conservative updates" do
- context "without --strict" do
- before do
- build_repo4 do
- build_gem "patch", %w[1.0.0 1.0.1]
- build_gem "minor", %w[1.0.0 1.0.1 1.1.0]
- build_gem "major", %w[1.0.0 1.0.1 1.1.0 2.0.0]
- end
+ before do
+ build_repo4 do
+ build_gem "patch", %w[1.0.0 1.0.1]
+ build_gem "minor", %w[1.0.0 1.0.1 1.1.0]
+ build_gem "major", %w[1.0.0 1.0.1 1.1.0 2.0.0]
+ end
- # establish a lockfile set to 1.0.0
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem 'patch', '1.0.0'
- gem 'minor', '1.0.0'
- gem 'major', '1.0.0'
- G
+ # establish a lockfile set to 1.0.0
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'patch', '1.0.0'
+ gem 'minor', '1.0.0'
+ gem 'major', '1.0.0'
+ G
- # remove 1.4.3 requirement and bar altogether
- # to setup update specs below
- gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem 'patch'
- gem 'minor'
- gem 'major'
- G
- end
+ # remove all version requirements
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'patch'
+ gem 'minor'
+ gem 'major'
+ G
+ end
- it "shows nothing when patching and filtering to minor" do
- bundle "outdated --patch --filter-minor"
+ it "shows nothing when patching and filtering to minor" do
+ bundle "outdated --patch --filter-minor"
- expect(out).to end_with("No minor updates to display.")
- end
+ expect(out).to end_with("No minor updates to display.")
+ end
- it "shows all gems when patching and filtering to patch" do
- bundle "outdated --patch --filter-patch", :raise_on_error => false
+ it "shows all gems when patching and filtering to patch" do
+ bundle "outdated --patch --filter-patch", :raise_on_error => false
- expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
- major 1.0.0 1.0.1 >= 0 default
- minor 1.0.0 1.0.1 >= 0 default
- patch 1.0.0 1.0.1 >= 0 default
- TABLE
+ expected_output = <<~TABLE.strip
+ Gem Current Latest Requested Groups
+ major 1.0.0 1.0.1 >= 0 default
+ minor 1.0.0 1.0.1 >= 0 default
+ patch 1.0.0 1.0.1 >= 0 default
+ TABLE
- expect(out).to end_with(expected_output)
- end
+ expect(out).to end_with(expected_output)
+ end
- it "shows minor and major when updating to minor and filtering to patch and minor" do
- bundle "outdated --minor --filter-minor", :raise_on_error => false
+ it "shows minor and major when updating to minor and filtering to patch and minor" do
+ bundle "outdated --minor --filter-minor", :raise_on_error => false
- expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
- major 1.0.0 1.1.0 >= 0 default
- minor 1.0.0 1.1.0 >= 0 default
- TABLE
+ expected_output = <<~TABLE.strip
+ Gem Current Latest Requested Groups
+ major 1.0.0 1.1.0 >= 0 default
+ minor 1.0.0 1.1.0 >= 0 default
+ TABLE
- expect(out).to end_with(expected_output)
- end
+ expect(out).to end_with(expected_output)
+ end
- it "shows minor when updating to major and filtering to minor with parseable" do
- bundle "outdated --major --filter-minor --parseable", :raise_on_error => false
+ it "shows minor when updating to major and filtering to minor with parseable" do
+ bundle "outdated --major --filter-minor --parseable", :raise_on_error => false
- expect(out).not_to include("patch (newest")
- expect(out).to include("minor (newest")
- expect(out).not_to include("major (newest")
- end
+ expect(out).not_to include("patch (newest")
+ expect(out).to include("minor (newest")
+ expect(out).not_to include("major (newest")
end
+ end
- context "with --strict" do
- before do
- build_repo4 do
- build_gem "foo", %w[1.4.3 1.4.4] do |s|
- s.add_dependency "bar", "~> 2.0"
- end
- build_gem "foo", %w[1.4.5 1.5.0] do |s|
- s.add_dependency "bar", "~> 2.1"
- end
- build_gem "foo", %w[1.5.1] do |s|
- s.add_dependency "bar", "~> 3.0"
- end
- build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
- build_gem "qux", %w[1.0.0 1.1.0 2.0.0]
+ context "tricky conservative updates" do
+ before do
+ build_repo4 do
+ build_gem "foo", %w[1.4.3 1.4.4] do |s|
+ s.add_dependency "bar", "~> 2.0"
+ end
+ build_gem "foo", %w[1.4.5 1.5.0] do |s|
+ s.add_dependency "bar", "~> 2.1"
end
+ build_gem "foo", %w[1.5.1] do |s|
+ s.add_dependency "bar", "~> 3.0"
+ end
+ build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ build_gem "qux", %w[1.0.0 1.1.0 2.0.0]
+ end
- # establish a lockfile set to 1.4.3
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem 'foo', '1.4.3'
- gem 'bar', '2.0.3'
- gem 'qux', '1.0.0'
- G
+ # establish a lockfile set to 1.4.3
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'foo', '1.4.3'
+ gem 'bar', '2.0.3'
+ gem 'qux', '1.0.0'
+ G
- # remove 1.4.3 requirement and bar altogether
- # to setup update specs below
- gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem 'foo'
- gem 'qux'
- G
- end
+ # remove 1.4.3 requirement and bar altogether
+ # to setup update specs below
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'foo'
+ gem 'qux'
+ G
+ end
- it "shows gems with --strict updating to patch and filtering to patch" do
- bundle "outdated --patch --strict --filter-patch", :raise_on_error => false
+ it "shows gems updating to patch and filtering to patch" do
+ bundle "outdated --patch --filter-patch", :raise_on_error => false, :env => { "DEBUG_RESOLVER" => "1" }
- expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
- bar 2.0.3 2.0.5
- foo 1.4.3 1.4.4 >= 0 default
- TABLE
+ expected_output = <<~TABLE.strip
+ Gem Current Latest Requested Groups
+ bar 2.0.3 2.0.5
+ foo 1.4.3 1.4.4 >= 0 default
+ TABLE
- expect(out).to end_with(expected_output)
- end
+ expect(out).to end_with(expected_output)
+ end
+
+ it "shows gems updating to patch and filtering to patch, in debug mode" do
+ bundle "outdated --patch --filter-patch", :raise_on_error => false, :env => { "DEBUG" => "1" }
+
+ expected_output = <<~TABLE.strip
+ Gem Current Latest Requested Groups Path
+ bar 2.0.3 2.0.5
+ foo 1.4.3 1.4.4 >= 0 default
+ TABLE
+
+ expect(out).to end_with(expected_output)
end
end
diff --git a/spec/bundler/other/platform_spec.rb b/spec/bundler/commands/platform_spec.rb
index c157345cab..688bf7b6df 100644
--- a/spec/bundler/other/platform_spec.rb
+++ b/spec/bundler/commands/platform_spec.rb
@@ -16,10 +16,10 @@ RSpec.describe "bundle platform" do
Your platform is: #{Gem::Platform.local}
Your app has gems that work on these platforms:
-* #{specific_local_platform}
+* #{local_platform}
Your Gemfile specifies a Ruby version requirement:
-* ruby #{RUBY_VERSION}
+* ruby #{Gem.ruby_version}
Your current platform satisfies the Ruby version requirement.
G
@@ -39,10 +39,10 @@ G
Your platform is: #{Gem::Platform.local}
Your app has gems that work on these platforms:
-* #{specific_local_platform}
+* #{local_platform}
Your Gemfile specifies a Ruby version requirement:
-* ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
+* #{Bundler::RubyVersion.system.single_version_string}
Your current platform satisfies the Ruby version requirement.
G
@@ -60,7 +60,7 @@ G
Your platform is: #{Gem::Platform.local}
Your app has gems that work on these platforms:
-* #{specific_local_platform}
+* #{local_platform}
Your Gemfile does not specify a Ruby version requirement.
G
@@ -80,12 +80,12 @@ G
Your platform is: #{Gem::Platform.local}
Your app has gems that work on these platforms:
-* #{specific_local_platform}
+* #{local_platform}
Your Gemfile specifies a Ruby version requirement:
* ruby #{not_local_ruby_version}
-Your Ruby version is #{RUBY_VERSION}, but your Gemfile specified #{not_local_ruby_version}
+Your Ruby version is #{Gem.ruby_version}, but your Gemfile specified #{not_local_ruby_version}
G
end
end
@@ -231,7 +231,30 @@ G
L
bundle "platform --ruby"
- expect(out).to eq("ruby 1.0.0p127")
+ expect(out).to eq("ruby 1.0.0")
+ end
+
+ it "handles when there is a lockfile with no requirement" do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "platform --ruby"
+ expect(out).to eq("No ruby version specified")
end
it "handles when there is a requirement in the gemfile" do
@@ -255,18 +278,18 @@ G
end
end
- let(:ruby_version_correct) { "ruby \"#{RUBY_VERSION}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{local_engine_version}\"" }
- let(:ruby_version_correct_engineless) { "ruby \"#{RUBY_VERSION}\"" }
+ let(:ruby_version_correct) { "ruby \"#{Gem.ruby_version}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{local_engine_version}\"" }
+ let(:ruby_version_correct_engineless) { "ruby \"#{Gem.ruby_version}\"" }
let(:ruby_version_correct_patchlevel) { "#{ruby_version_correct}, :patchlevel => '#{RUBY_PATCHLEVEL}'" }
let(:ruby_version_incorrect) { "ruby \"#{not_local_ruby_version}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{not_local_ruby_version}\"" }
- let(:engine_incorrect) { "ruby \"#{RUBY_VERSION}\", :engine => \"#{not_local_tag}\", :engine_version => \"#{RUBY_VERSION}\"" }
- let(:engine_version_incorrect) { "ruby \"#{RUBY_VERSION}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{not_local_engine_version}\"" }
+ let(:engine_incorrect) { "ruby \"#{Gem.ruby_version}\", :engine => \"#{not_local_tag}\", :engine_version => \"#{Gem.ruby_version}\"" }
+ let(:engine_version_incorrect) { "ruby \"#{Gem.ruby_version}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{not_local_engine_version}\"" }
let(:patchlevel_incorrect) { "#{ruby_version_correct}, :patchlevel => '#{not_local_patchlevel}'" }
let(:patchlevel_fixnum) { "#{ruby_version_correct}, :patchlevel => #{RUBY_PATCHLEVEL}1" }
def should_be_ruby_version_incorrect
expect(exitstatus).to eq(18)
- expect(err).to be_include("Your Ruby version is #{RUBY_VERSION}, but your Gemfile specified #{not_local_ruby_version}")
+ expect(err).to be_include("Your Ruby version is #{Gem.ruby_version}, but your Gemfile specified #{not_local_ruby_version}")
end
def should_be_engine_incorrect
diff --git a/spec/bundler/commands/pristine_spec.rb b/spec/bundler/commands/pristine_spec.rb
index fe85e1ecd1..9e496dc91a 100644
--- a/spec/bundler/commands/pristine_spec.rb
+++ b/spec/bundler/commands/pristine_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe "bundle pristine" do
source "#{file_uri_for(gem_repo2)}"
gem "weakling"
gem "very_simple_binary"
- gem "foo", :git => "#{lib_path("foo")}", :branch => "master"
+ gem "foo", :git => "#{lib_path("foo")}", :branch => "main"
gem "git_with_ext", :git => "#{lib_path("git_with_ext")}"
gem "bar", :path => "#{lib_path("bar")}"
diff --git a/spec/bundler/commands/remove_spec.rb b/spec/bundler/commands/remove_spec.rb
index ceba6c5ede..d757e0be4b 100644
--- a/spec/bundler/commands/remove_spec.rb
+++ b/spec/bundler/commands/remove_spec.rb
@@ -86,7 +86,7 @@ RSpec.describe "bundle remove" do
gem 'git'
gem 'rack',
git: "#{lib_path("rack-1.0")}",
- branch: 'master'
+ branch: 'main'
gem 'nokogiri'
G
@@ -522,7 +522,7 @@ RSpec.describe "bundle remove" do
end
end
- context "when gems can not be removed from other gemfile" do
+ context "when gems cannot be removed from other gemfile" do
it "shows error" do
create_file "Gemfile-other", <<-G
gem "rails"; gem "rack"
@@ -574,7 +574,7 @@ RSpec.describe "bundle remove" do
end
context "when gem present in gemfiles but could not be removed from one from one of them" do
- it "removes gem which can be removed and shows warning for file from which it can not be removed" do
+ it "removes gem which can be removed and shows warning for file from which it cannot be removed" do
create_file "Gemfile-other", <<-G
gem "rack"
G
diff --git a/spec/bundler/commands/show_spec.rb b/spec/bundler/commands/show_spec.rb
index 2adb121616..1c6244be41 100644
--- a/spec/bundler/commands/show_spec.rb
+++ b/spec/bundler/commands/show_spec.rb
@@ -100,10 +100,10 @@ RSpec.describe "bundle show", :bundler => "< 3" do
expect(the_bundle).to include_gems "foo 1.0"
bundle :show
- expect(out).to include("foo (1.0 #{@git.ref_for("master", 6)}")
+ expect(out).to include("foo (1.0 #{@git.ref_for("main", 6)}")
end
- it "prints out branch names other than master" do
+ it "prints out branch names other than main" do
update_git "foo", :branch => "omg" do |s|
s.write "lib/foo.rb", "FOO = '1.0.omg'"
end
@@ -173,7 +173,7 @@ RSpec.describe "bundle show", :bundler => "< 3" do
G
bundle "show rac"
- expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>)?\z/)
+ expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>.*)?\z/)
end
end
diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb
index d45a565475..84042709bf 100644
--- a/spec/bundler/commands/update_spec.rb
+++ b/spec/bundler/commands/update_spec.rb
@@ -284,7 +284,7 @@ RSpec.describe "bundle update" do
countries (~> 3.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
countries
@@ -301,6 +301,128 @@ RSpec.describe "bundle update" do
expect(lockfile).to eq(previous_lockfile)
end
+ it "does not downgrade direct dependencies when run with --conservative" do
+ build_repo4 do
+ build_gem "oauth2", "2.0.6" do |s|
+ s.add_dependency "faraday", ">= 0.17.3", "< 3.0"
+ end
+
+ build_gem "oauth2", "1.4.10" do |s|
+ s.add_dependency "faraday", ">= 0.17.3", "< 3.0"
+ s.add_dependency "multi_json", "~> 1.3"
+ end
+
+ build_gem "faraday", "2.5.2"
+
+ build_gem "multi_json", "1.15.0"
+
+ build_gem "quickbooks-ruby", "1.0.19" do |s|
+ s.add_dependency "oauth2", "~> 1.4"
+ end
+
+ build_gem "quickbooks-ruby", "0.1.9" do |s|
+ s.add_dependency "oauth2"
+ end
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "oauth2"
+ gem "quickbooks-ruby"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ faraday (2.5.2)
+ multi_json (1.15.0)
+ oauth2 (1.4.10)
+ faraday (>= 0.17.3, < 3.0)
+ multi_json (~> 1.3)
+ quickbooks-ruby (1.0.19)
+ oauth2 (~> 1.4)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ oauth2
+ quickbooks-ruby
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update --conservative --verbose"
+
+ expect(out).not_to include("Installing quickbooks-ruby 0.1.9")
+ expect(out).to include("Installing quickbooks-ruby 1.0.19").and include("Installing oauth2 1.4.10")
+ end
+
+ it "does not downgrade direct dependencies when using gemspec sources" do
+ create_file("rails.gemspec", <<-G)
+ Gem::Specification.new do |gem|
+ gem.name = "rails"
+ gem.version = "7.1.0.alpha"
+ gem.author = "DHH"
+ gem.summary = "Full-stack web application framework."
+ end
+ G
+
+ build_repo4 do
+ build_gem "rake", "12.3.3"
+ build_gem "rake", "13.0.6"
+
+ build_gem "sneakers", "2.11.0" do |s|
+ s.add_dependency "rake"
+ end
+
+ build_gem "sneakers", "2.12.0" do |s|
+ s.add_dependency "rake", "~> 12.3"
+ end
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gemspec
+
+ gem "rake"
+ gem "sneakers"
+ G
+
+ lockfile <<~L
+ PATH
+ remote: .
+ specs:
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ rake (13.0.6)
+ sneakers (2.11.0)
+ rake
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rake
+ sneakers
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update --verbose"
+
+ expect(out).not_to include("Installing sneakers 2.12.0")
+ expect(out).not_to include("Installing rake 12.3.3")
+ expect(out).to include("Installing sneakers 2.11.0").and include("Installing rake 13.0.6")
+ end
+
it "does not downgrade indirect dependencies unnecessarily" do
build_repo4 do
build_gem "a" do |s|
@@ -536,22 +658,28 @@ RSpec.describe "bundle update" do
bundle "update", :all => true, :raise_on_error => false
expect(last_command).to be_failure
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m)
- expect(err).to match(/freeze \nby running `bundle config unset deployment`./m)
+ expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/)
+ expect(err).to match(/freeze by running `bundle config set frozen false`./)
end
- it "should suggest different command when frozen is set globally", :bundler => "< 3" do
+ it "should fail loudly when frozen is set globally" do
bundle "config set --global frozen 1"
bundle "update", :all => true, :raise_on_error => false
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m).
- and match(/freeze \nby running `bundle config unset frozen`./m)
+ expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/).
+ and match(/freeze by running `bundle config set frozen false`./)
end
- it "should suggest different command when frozen is set globally", :bundler => "3" do
+ it "should fail loudly when deployment is set globally" do
bundle "config set --global deployment true"
bundle "update", :all => true, :raise_on_error => false
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m).
- and match(/freeze \nby running `bundle config unset deployment`./m)
+ expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/).
+ and match(/freeze by running `bundle config set frozen false`./)
+ end
+
+ it "should not suggest any command to unfreeze bundler if frozen is set through ENV" do
+ bundle "update", :all => true, :raise_on_error => false, :env => { "BUNDLE_FROZEN" => "true" }
+ expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/)
+ expect(err).not_to match(/by running/)
end
end
@@ -644,7 +772,7 @@ RSpec.describe "bundle update" do
end
end
- it "shows the previous version of the gem when updated from rubygems source", :bundler => "< 3" do
+ it "shows the previous version of the gem when updated from rubygems source" do
build_repo2
install_gemfile <<-G
@@ -652,7 +780,7 @@ RSpec.describe "bundle update" do
gem "activesupport"
G
- bundle "update", :all => true
+ bundle "update", :all => true, :verbose => true
expect(out).to include("Using activesupport 2.3.5")
update_repo2 do
@@ -663,32 +791,28 @@ RSpec.describe "bundle update" do
expect(out).to include("Installing activesupport 3.0 (was 2.3.5)")
end
- context "with suppress_install_using_messages set" do
- before { bundle "config set suppress_install_using_messages true" }
-
- it "only prints `Using` for versions that have changed" do
- build_repo4 do
- build_gem "bar"
- build_gem "foo"
- end
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "bar"
- gem "foo"
- G
+ it "only prints `Using` for versions that have changed" do
+ build_repo4 do
+ build_gem "bar"
+ build_gem "foo"
+ end
- bundle "update", :all => true
- expect(out).to match(/Resolving dependencies\.\.\.\.*\nBundle updated!/)
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "bar"
+ gem "foo"
+ G
- update_repo4 do
- build_gem "foo", "2.0"
- end
+ bundle "update", :all => true
+ expect(out).to match(/Resolving dependencies\.\.\.\.*\nBundle updated!/)
- bundle "update", :all => true
- out.sub!("Removing foo (1.0)\n", "")
- expect(out).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/)
+ update_repo4 do
+ build_gem "foo", "2.0"
end
+
+ bundle "update", :all => true
+ out.sub!("Removing foo (1.0)\n", "")
+ expect(out).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/)
end
it "shows error message when Gemfile.lock is not preset and gem is specified" do
@@ -743,7 +867,7 @@ RSpec.describe "bundle update" do
vcr (6.0.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
dotenv
@@ -983,7 +1107,7 @@ RSpec.describe "bundle update --ruby" do
context "when the Gemfile removes the ruby" do
before do
install_gemfile <<-G
- ruby '~> #{RUBY_VERSION}'
+ ruby '~> #{Gem.ruby_version}'
source "#{file_uri_for(gem_repo1)}"
G
@@ -1013,12 +1137,12 @@ RSpec.describe "bundle update --ruby" do
context "when the Gemfile specified an updated Ruby version" do
before do
install_gemfile <<-G
- ruby '~> #{RUBY_VERSION}'
+ ruby '~> #{Gem.ruby_version}'
source "#{file_uri_for(gem_repo1)}"
G
gemfile <<-G
- ruby '~> #{RUBY_VERSION[0..2]}'
+ ruby '~> #{current_ruby_minor}'
source "#{file_uri_for(gem_repo1)}"
G
end
@@ -1047,7 +1171,7 @@ RSpec.describe "bundle update --ruby" do
context "when a different Ruby is being used than has been versioned" do
before do
install_gemfile <<-G
- ruby '~> #{RUBY_VERSION}'
+ ruby '~> #{Gem.ruby_version}'
source "#{file_uri_for(gem_repo1)}"
G
@@ -1083,7 +1207,7 @@ RSpec.describe "bundle update --ruby" do
L
gemfile <<-G
- ruby '~> #{RUBY_VERSION}'
+ ruby '~> #{Gem.ruby_version}'
source "#{file_uri_for(gem_repo1)}"
G
end
@@ -1111,7 +1235,7 @@ RSpec.describe "bundle update --ruby" do
end
RSpec.describe "bundle update --bundler" do
- it "updates the bundler version in the lockfile without re-resolving" do
+ it "updates the bundler version in the lockfile" do
build_repo4 do
build_gem "rack", "1.0"
end
@@ -1122,8 +1246,6 @@ RSpec.describe "bundle update --bundler" do
G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
- FileUtils.rm_r gem_repo4
-
bundle :update, :bundler => true, :artifice => "compact_index", :verbose => true
expect(out).to include("Using bundler #{Bundler::VERSION}")
@@ -1182,21 +1304,19 @@ RSpec.describe "bundle update --bundler" do
end
it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo, :realworld do
- skip "ruby-head has a default Bundler version too high for this spec to work" if RUBY_PATCHLEVEL == -1
-
pristine_system_gems "bundler-2.3.9"
build_repo4 do
build_gem "rack", "1.0"
end
- install_gemfile <<-G
+ install_gemfile <<-G, :env => { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
source "#{file_uri_for(gem_repo4)}"
gem "rack"
G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9")
- bundle :update, :bundler => true, :artifice => "vcr", :verbose => true
+ bundle :update, :bundler => true, :artifice => "vcr", :verbose => true, :env => { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
# Only updates properly on modern RubyGems.
@@ -1228,15 +1348,13 @@ RSpec.describe "bundle update --bundler" do
end
it "errors if the explicit target version does not exist", :realworld do
- skip "ruby-head has a default Bundler version too high for this spec to work" if RUBY_PATCHLEVEL == -1
-
pristine_system_gems "bundler-2.3.9"
build_repo4 do
build_gem "rack", "1.0"
end
- install_gemfile <<-G
+ install_gemfile <<-G, :env => { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
source "#{file_uri_for(gem_repo4)}"
gem "rack"
G
@@ -1264,7 +1382,7 @@ RSpec.describe "bundle update --bundler" do
gem "rack"
G
- bundle :update, :bundler => "2.3.0.dev"
+ bundle :update, :bundler => "2.3.0.dev", :verbose => "true"
# Only updates properly on modern RubyGems.
@@ -1301,7 +1419,7 @@ RSpec.describe "bundle update --bundler" do
gem "rack"
G
- bundle :update, :bundler => "2.3.9", :raise_on_error => false
+ bundle :update, :bundler => "2.3.9", :raise_on_error => false, :verbose => true
expect(out).not_to include("Fetching gem metadata from https://rubygems.org/")
@@ -1327,6 +1445,31 @@ RSpec.describe "bundle update --bundler" do
expect(out).to include("Using bundler 2.3.9")
end
end
+
+ it "prints an error when trying to update bundler in frozen mode" do
+ system_gems "bundler-2.3.9"
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo2)}"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ 2.1.4
+ L
+
+ bundle "update --bundler=2.3.9", :env => { "BUNDLE_FROZEN" => "true" }, :raise_on_error => false
+ expect(err).to include("An update to the version of bundler itself was requested, but the lockfile can't be updated because frozen mode is set")
+ end
end
# these specs are slow and focus on integration and therefore are not exhaustive. unit specs elsewhere handle that.
@@ -1343,7 +1486,10 @@ RSpec.describe "bundle update conservative" do
build_gem "foo", %w[1.5.1] do |s|
s.add_dependency "bar", "~> 3.0"
end
- build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ build_gem "foo", %w[2.0.0.pre] do |s|
+ s.add_dependency "bar"
+ end
+ build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 2.1.2.pre 3.0.0 3.1.0.pre 4.0.0.pre]
build_gem "qux", %w[1.0.0 1.0.1 1.1.0 2.0.0]
end
@@ -1408,6 +1554,32 @@ RSpec.describe "bundle update conservative" do
expect(the_bundle).to include_gems "foo 1.5.0", "bar 2.1.1", "qux 1.1.0"
end
end
+
+ context "pre" do
+ it "defaults to major" do
+ bundle "update --pre foo bar"
+
+ expect(the_bundle).to include_gems "foo 2.0.0.pre", "bar 4.0.0.pre", "qux 1.0.0"
+ end
+
+ it "patch preferred" do
+ bundle "update --patch --pre foo bar"
+
+ expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.2.pre", "qux 1.0.0"
+ end
+
+ it "minor preferred" do
+ bundle "update --minor --pre foo bar"
+
+ expect(the_bundle).to include_gems "foo 1.5.1", "bar 3.1.0.pre", "qux 1.0.0"
+ end
+
+ it "major preferred" do
+ bundle "update --major --pre foo bar"
+
+ expect(the_bundle).to include_gems "foo 2.0.0.pre", "bar 4.0.0.pre", "qux 1.0.0"
+ end
+ end
end
context "eager unlocking" do
@@ -1449,7 +1621,7 @@ RSpec.describe "bundle update conservative" do
shared_dep (~> 5.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
isolated_owner
@@ -1502,7 +1674,7 @@ RSpec.describe "bundle update conservative" do
shared_dep (~> 5.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
isolated_owner
diff --git a/spec/bundler/commands/viz_spec.rb b/spec/bundler/commands/viz_spec.rb
index 0efb24b504..cf612397ab 100644
--- a/spec/bundler/commands/viz_spec.rb
+++ b/spec/bundler/commands/viz_spec.rb
@@ -1,10 +1,8 @@
# frozen_string_literal: true
-RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot") do
+RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot"), :realworld => true do
before do
- graphviz_version = RUBY_VERSION >= "2.4" ? "1.2.5" : "1.2.4"
-
- realworld_system_gems "ruby-graphviz --version #{graphviz_version}"
+ realworld_system_gems "ruby-graphviz --version 1.2.5"
end
it "graphs gems from the Gemfile" do
diff --git a/spec/bundler/install/allow_offline_install_spec.rb b/spec/bundler/install/allow_offline_install_spec.rb
index 524363fde5..4c6c77a61e 100644
--- a/spec/bundler/install/allow_offline_install_spec.rb
+++ b/spec/bundler/install/allow_offline_install_spec.rb
@@ -53,7 +53,10 @@ RSpec.describe "bundle install with :allow_offline_install" do
File.open(tmp("broken_path/git"), "w", 0o755) do |f|
f.puts strip_whitespace(<<-RUBY)
#!/usr/bin/env ruby
- if %w(fetch --force --quiet --tags refs/heads/*:refs/heads/*).-(ARGV).empty? || %w(clone --bare --no-hardlinks --quiet).-(ARGV).empty?
+ fetch_args = %w(fetch --force --quiet)
+ clone_args = %w(clone --bare --no-hardlinks --quiet)
+
+ if (fetch_args.-(ARGV).empty? || clone_args.-(ARGV).empty?) && ARGV.any? {|arg| arg.start_with?("file://") }
warn "git remote ops have been disabled"
exit 1
end
diff --git a/spec/bundler/install/binstubs_spec.rb b/spec/bundler/install/binstubs_spec.rb
index 6961171f4f..928ba80b15 100644
--- a/spec/bundler/install/binstubs_spec.rb
+++ b/spec/bundler/install/binstubs_spec.rb
@@ -2,10 +2,8 @@
RSpec.describe "bundle install" do
describe "when system_bindir is set" do
- # On OS X, Gem.bindir defaults to /usr/bin, so system_bindir is useful if
- # you want to avoid sudo installs for system gems with OS X's default ruby
it "overrides Gem.bindir" do
- expect(Pathname.new("/usr/bin")).not_to be_writable unless Process.euid == 0
+ expect(Pathname.new("/usr/bin")).not_to be_writable
gemfile <<-G
def Gem.bindir; "/usr/bin"; end
source "#{file_uri_for(gem_repo1)}"
diff --git a/spec/bundler/install/bundler_spec.rb b/spec/bundler/install/bundler_spec.rb
index 963ce82db8..e7ec3bc7e7 100644
--- a/spec/bundler/install/bundler_spec.rb
+++ b/spec/bundler/install/bundler_spec.rb
@@ -37,12 +37,11 @@ RSpec.describe "bundle install" do
G
nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "bundler":
- In Gemfile:
- bundler (= 0.9.1)
+ Could not find compatible versions
- Current Bundler version:
- bundler (#{Bundler::VERSION})
+ Because the current Bundler version (#{Bundler::VERSION}) does not satisfy bundler = 0.9.1
+ and Gemfile depends on bundler = 0.9.1,
+ version solving has failed.
Your bundle requires a different version of Bundler than the one you're running.
Install the necessary version with `gem install bundler:0.9.1` and rerun bundler using `bundle _0.9.1_ install`
@@ -58,12 +57,14 @@ RSpec.describe "bundle install" do
G
nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "bundler":
- In Gemfile:
- bundler (~> 0.8)
+ Could not find compatible versions
- Current Bundler version:
- bundler (#{Bundler::VERSION})
+ Because rails >= 3.0 depends on bundler >= 0.9.0.pre
+ and the current Bundler version (#{Bundler::VERSION}) does not satisfy bundler >= 0.9.0.pre, < 1.A,
+ rails >= 3.0 requires bundler >= 1.A.
+ So, because Gemfile depends on rails = 3.0
+ and Gemfile depends on bundler ~> 0.8,
+ version solving has failed.
Your bundle requires a different version of Bundler than the one you're running.
Install the necessary version with `gem install bundler:0.9.1` and rerun bundler using `bundle _0.9.1_ install`
@@ -79,12 +80,11 @@ RSpec.describe "bundle install" do
G
nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "bundler":
- In Gemfile:
- bundler (= 0.9.2)
+ Could not find compatible versions
- Current Bundler version:
- bundler (#{Bundler::VERSION})
+ Because the current Bundler version (#{Bundler::VERSION}) does not satisfy bundler = 0.9.2
+ and Gemfile depends on bundler = 0.9.2,
+ version solving has failed.
Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.
E
@@ -150,13 +150,14 @@ RSpec.describe "bundle install" do
G
nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "activesupport":
- In Gemfile:
- activemerchant was resolved to 1.0, which depends on
- activesupport (>= 2.0.0)
-
- rails_pinned_to_old_activesupport was resolved to 1.0, which depends on
- activesupport (= 1.2.3)
+ Could not find compatible versions
+
+ Because every version of rails_pinned_to_old_activesupport depends on activesupport = 1.2.3
+ and every version of activemerchant depends on activesupport >= 2.0.0,
+ every version of rails_pinned_to_old_activesupport is incompatible with activemerchant >= 0.
+ So, because Gemfile depends on activemerchant >= 0
+ and Gemfile depends on rails_pinned_to_old_activesupport >= 0,
+ version solving has failed.
E
expect(err).to include(nice_error)
end
@@ -177,12 +178,13 @@ RSpec.describe "bundle install" do
G
nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "activesupport":
- In Gemfile:
- activesupport (= 2.3.5)
+ Could not find compatible versions
- rails_pinned_to_old_activesupport was resolved to 1.0, which depends on
- activesupport (= 1.2.3)
+ Because every version of rails_pinned_to_old_activesupport depends on activesupport = 1.2.3
+ and Gemfile depends on rails_pinned_to_old_activesupport >= 0,
+ activesupport = 1.2.3 is required.
+ So, because Gemfile depends on activesupport = 2.3.5,
+ version solving has failed.
E
expect(err).to include(nice_error)
end
@@ -208,6 +210,33 @@ RSpec.describe "bundle install" do
expect(err).to be_empty
end
+ it "prints the previous version when switching to a previously downloaded gem" do
+ build_repo4 do
+ build_gem "rails", "7.0.3"
+ build_gem "rails", "7.0.4"
+ end
+
+ bundle "config set path.system true"
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'rails', "7.0.4"
+ G
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'rails', "7.0.3"
+ G
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'rails', "7.0.4"
+ G
+
+ expect(out).to include("Using rails 7.0.4 (was 7.0.3)")
+ expect(err).to be_empty
+ end
+
it "can install dependencies with newer bundler version with system gems" do
bundle "config set path.system true"
diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb
index 3bcb6a703e..2a88ed5b06 100644
--- a/spec/bundler/install/deploy_spec.rb
+++ b/spec/bundler/install/deploy_spec.rb
@@ -39,6 +39,39 @@ RSpec.describe "install in deployment or frozen mode" do
bundle :install
expect(the_bundle).to include_gems "rack 1.0"
end
+
+ it "installs gems by default to vendor/bundle" do
+ bundle :lock
+ bundle "install --deployment"
+ expect(out).to include("vendor/bundle")
+ end
+
+ it "installs gems to custom path if specified" do
+ bundle :lock
+ bundle "install --path vendor/bundle2 --deployment"
+ expect(out).to include("vendor/bundle2")
+ end
+
+ it "works with the --frozen flag" do
+ bundle :lock
+ bundle "install --frozen"
+ end
+
+ it "explodes with the --deployment flag if you make a change and don't check in the lockfile" do
+ bundle :lock
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ bundle "install --deployment", :raise_on_error => false
+ expect(err).to include("frozen mode")
+ expect(err).to include("You have added to the Gemfile")
+ expect(err).to include("* rack-obama")
+ expect(err).not_to include("You have deleted from the Gemfile")
+ expect(err).not_to include("You have changed in the Gemfile")
+ end
end
it "still works if you are not in the app directory and specify --gemfile" do
@@ -141,7 +174,7 @@ RSpec.describe "install in deployment or frozen mode" do
rack (1.0.0)
PLATFORMS
- #{local}
+ #{generic_local_platform}
DEPENDENCIES
rack
@@ -202,29 +235,35 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "install"
end
- it "installs gems by default to vendor/bundle", :bundler => "< 3" do
- bundle "install --deployment"
+ it "installs gems by default to vendor/bundle" do
+ bundle "config set deployment true"
+ bundle "install"
expect(out).to include("vendor/bundle")
end
- it "installs gems to custom path if specified", :bundler => "< 3" do
- bundle "install --path vendor/bundle2 --deployment"
+ it "installs gems to custom path if specified" do
+ bundle "config set path vendor/bundle2"
+ bundle "config set deployment true"
+ bundle "install"
expect(out).to include("vendor/bundle2")
end
- it "works with the --deployment flag if you didn't change anything", :bundler => "< 3" do
- bundle "install --deployment"
+ it "installs gems to custom path if specified, even when configured through ENV" do
+ bundle "config set deployment true"
+ bundle "install", :env => { "BUNDLE_PATH" => "vendor/bundle2" }
+ expect(out).to include("vendor/bundle2")
end
- it "works with the --frozen flag if you didn't change anything", :bundler => "< 3" do
- bundle "install --frozen"
+ it "works with the `frozen` setting" do
+ bundle "config set frozen true"
+ bundle "install"
end
it "works with BUNDLE_FROZEN if you didn't change anything" do
bundle :install, :env => { "BUNDLE_FROZEN" => "true" }
end
- it "explodes with the --deployment flag if you make a change and don't check in the lockfile" do
+ it "explodes with the `deployment` setting if you make a change and don't check in the lockfile" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -233,7 +272,7 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
@@ -258,8 +297,12 @@ RSpec.describe "install in deployment or frozen mode" do
expect(out).to eq("WIN")
end
- it "works if a gem is missing, but it's on a different platform, and the Gemfile has no global source", :bundler => "< 3" do
+ it "works if a gem is missing, but it's on a different platform" do
+ build_repo2
+
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+
source "#{file_uri_for(gem_repo1)}" do
gem "rake", platform: :#{not_local_tag}
end
@@ -269,6 +312,40 @@ RSpec.describe "install in deployment or frozen mode" do
expect(last_command).to be_success
end
+ it "shows a good error if a gem is missing from the lockfile" do
+ build_repo4 do
+ build_gem "foo"
+ build_gem "bar"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "foo"
+ gem "bar"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ bar
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle :install, :env => { "BUNDLE_FROZEN" => "true" }, :raise_on_error => false, :artifice => "compact_index"
+ expect(err).to include("Your lock file is missing \"bar\", but the lockfile can't be updated because frozen mode is set")
+ end
+
it "explodes if a path gem is missing" do
build_lib "path_gem"
install_gemfile <<-G
@@ -285,7 +362,7 @@ RSpec.describe "install in deployment or frozen mode" do
expect(err).to include("The path `#{lib_path("path_gem-1.0")}` does not exist.")
end
- it "can have --frozen set via an environment variable", :bundler => "< 3" do
+ it "can have --frozen set via an environment variable" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -294,7 +371,7 @@ RSpec.describe "install in deployment or frozen mode" do
ENV["BUNDLE_FROZEN"] = "1"
bundle "install", :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
@@ -310,20 +387,20 @@ RSpec.describe "install in deployment or frozen mode" do
ENV["BUNDLE_DEPLOYMENT"] = "true"
bundle "install", :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
expect(err).not_to include("You have changed in the Gemfile")
end
- it "installs gems by default to vendor/bundle when deployment mode is set via an environment variable", :bundler => "< 3" do
+ it "installs gems by default to vendor/bundle when deployment mode is set via an environment variable" do
ENV["BUNDLE_DEPLOYMENT"] = "true"
bundle "install"
expect(out).to include("vendor/bundle")
end
- it "installs gems to custom path when deployment mode is set via an environment variable ", :bundler => "< 3" do
+ it "installs gems to custom path when deployment mode is set via an environment variable " do
ENV["BUNDLE_DEPLOYMENT"] = "true"
ENV["BUNDLE_PATH"] = "vendor/bundle2"
bundle "install"
@@ -340,7 +417,7 @@ RSpec.describe "install in deployment or frozen mode" do
ENV["BUNDLE_FROZEN"] = "false"
ENV["BUNDLE_DEPLOYMENT"] = "false"
bundle "install"
- expect(out).not_to include("deployment mode")
+ expect(out).not_to include("frozen mode")
expect(out).not_to include("You have added to the Gemfile")
expect(out).not_to include("* rack-obama")
end
@@ -353,7 +430,7 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile:\n* activesupport\n\n")
expect(err).to include("You have deleted from the Gemfile:\n* rack")
expect(err).not_to include("You have changed in the Gemfile")
@@ -367,7 +444,7 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).not_to include("You have added to the Gemfile")
expect(err).to include("You have changed in the Gemfile:\n* rack from `no specified source` to `git://hubz.com`")
end
@@ -387,7 +464,7 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).not_to include("You have deleted from the Gemfile")
expect(err).not_to include("You have added to the Gemfile")
expect(err).to include("You have changed in the Gemfile:\n* rack from `#{lib_path("rack-1.0")}` to `no specified source`")
@@ -411,7 +488,7 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have changed in the Gemfile:\n* rack from `#{lib_path("rack")}` to `no specified source`")
expect(err).not_to include("You have added to the Gemfile")
expect(err).not_to include("You have deleted from the Gemfile")
@@ -430,7 +507,7 @@ RSpec.describe "install in deployment or frozen mode" do
run "require 'rack'", :raise_on_error => false
expect(err).to include strip_whitespace(<<-E).strip
-The dependencies in your gemfile changed
+The dependencies in your gemfile changed, but the lockfile can't be updated because frozen mode is set (Bundler::ProductionError)
You have added to the Gemfile:
* rack (= 1.0.0)
@@ -463,7 +540,7 @@ You have deleted from the Gemfile:
simulate_new_machine
bundle "config set --local deployment true"
bundle "install --verbose"
- expect(out).not_to include("You are trying to install in deployment mode after changing your Gemfile")
+ expect(out).not_to include("but the lockfile can't be updated because frozen mode is set")
expect(out).not_to include("You have added to the Gemfile")
expect(out).not_to include("You have deleted from the Gemfile")
expect(out).to include("vendor/cache/foo")
diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb
index 941f1c6db9..73f04f071d 100644
--- a/spec/bundler/install/gemfile/gemspec_spec.rb
+++ b/spec/bundler/install/gemfile/gemspec_spec.rb
@@ -150,7 +150,7 @@ RSpec.describe "bundle install from an existing gemspec" do
output = bundle("install", :dir => tmp.join("foo"))
expect(output).not_to match(/You have added to the Gemfile/)
expect(output).not_to match(/You have deleted from the Gemfile/)
- expect(output).not_to match(/install in deployment mode after changing/)
+ expect(output).not_to match(/the lockfile can't be updated because frozen mode is set/)
end
it "should match a lockfile without needing to re-resolve" do
@@ -436,7 +436,7 @@ RSpec.describe "bundle install from an existing gemspec" do
simulate_new_machine
simulate_platform("jruby") { bundle "install" }
- simulate_platform(x64_mingw) { bundle "install" }
+ simulate_platform(x64_mingw32) { bundle "install" }
end
context "on ruby" do
@@ -674,7 +674,7 @@ RSpec.describe "bundle install from an existing gemspec" do
railties (6.1.4)
PLATFORMS
- #{lockfile_platforms_for(["java", specific_local_platform])}
+ #{lockfile_platforms("java")}
DEPENDENCIES
activeadmin!
diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb
index 3f8a5afc40..910f96f4ab 100644
--- a/spec/bundler/install/gemfile/git_spec.rb
+++ b/spec/bundler/install/gemfile/git_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.describe "bundle install with git sources" do
- describe "when floating on master" do
+ describe "when floating on main" do
before :each do
build_git "foo" do |s|
s.executables = "foobar"
@@ -30,6 +30,13 @@ RSpec.describe "bundle install with git sources" do
expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"]).to have_attributes :size => 1
end
+ it "does not write to cache on bundler/setup" do
+ cache_path = default_bundle_path.join("cache")
+ FileUtils.rm_rf(cache_path)
+ ruby "require 'bundler/setup'"
+ expect(cache_path).not_to exist
+ end
+
it "caches the git repo globally and properly uses the cached repo on the next invocation" do
simulate_new_machine
bundle "config set global_gem_cache true"
@@ -51,9 +58,10 @@ RSpec.describe "bundle install with git sources" do
bundle "update foo"
- sha = git.ref_for("master", 11)
- spec_file = default_bundle_path.join("bundler/gems/foo-1.0-#{sha}/foo.gemspec").to_s
- ruby_code = Gem::Specification.load(spec_file).to_ruby
+ sha = git.ref_for("main", 11)
+ spec_file = default_bundle_path.join("bundler/gems/foo-1.0-#{sha}/foo.gemspec")
+ expect(spec_file).to exist
+ ruby_code = Gem::Specification.load(spec_file.to_s).to_ruby
file_code = File.read(spec_file)
expect(file_code).to eq(ruby_code)
end
@@ -192,6 +200,7 @@ RSpec.describe "bundle install with git sources" do
gem "foo"
end
G
+ expect(err).to be_empty
run <<-RUBY
require 'foo'
@@ -218,13 +227,52 @@ RSpec.describe "bundle install with git sources" do
expect(out).to eq("WIN")
end
+ it "works when an abbreviated revision is added after an initial, potentially shallow clone" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ git "#{lib_path("foo-1.0")}", :ref => #{@revision[0..7].inspect} do
+ gem "foo"
+ end
+ G
+ end
+
+ it "works when a tag that does not look like a commit hash is used as the value of :ref" do
+ build_git "foo"
+ @remote = build_git("bar", :bare => true)
+ update_git "foo", :remote => file_uri_for(@remote.path)
+ update_git "foo", :push => "main"
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'foo', :git => "#{@remote.path}"
+ G
+
+ # Create a new tag on the remote that needs fetching
+ update_git "foo", :tag => "v1.0.0"
+ update_git "foo", :push => "v1.0.0"
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'foo', :git => "#{@remote.path}", :ref => "v1.0.0"
+ G
+
+ expect(err).to be_empty
+ end
+
it "works when the revision is a non-head ref" do
- # want to ensure we don't fallback to master
+ # want to ensure we don't fallback to main
update_git "foo", :path => lib_path("foo-1.0") do |s|
s.write("lib/foo.rb", "raise 'FAIL'")
end
- sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 master~1", :dir => lib_path("foo-1.0"))
+ sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", :dir => lib_path("foo-1.0"))
# want to ensure we don't fallback to HEAD
update_git "foo", :path => lib_path("foo-1.0"), :branch => "rando" do |s|
@@ -255,12 +303,12 @@ RSpec.describe "bundle install with git sources" do
end
G
- # want to ensure we don't fallback to master
+ # want to ensure we don't fallback to main
update_git "foo", :path => lib_path("foo-1.0") do |s|
s.write("lib/foo.rb", "raise 'FAIL'")
end
- sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 master~1", :dir => lib_path("foo-1.0"))
+ sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", :dir => lib_path("foo-1.0"))
# want to ensure we don't fallback to HEAD
update_git "foo", :path => lib_path("foo-1.0"), :branch => "rando" do |s|
@@ -284,7 +332,7 @@ RSpec.describe "bundle install with git sources" do
end
it "does not download random non-head refs" do
- sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 master~1", :dir => lib_path("foo-1.0"))
+ sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", :dir => lib_path("foo-1.0"))
bundle "config set global_gem_cache true"
@@ -420,7 +468,7 @@ RSpec.describe "bundle install with git sources" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main"
G
bundle %(config set local.rack #{lib_path("local-rack")})
@@ -441,7 +489,7 @@ RSpec.describe "bundle install with git sources" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main"
G
bundle %(config set local.rack #{lib_path("local-rack")})
@@ -461,7 +509,7 @@ RSpec.describe "bundle install with git sources" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main"
G
bundle %(config set local.rack #{lib_path("local-rack")})
@@ -477,7 +525,7 @@ RSpec.describe "bundle install with git sources" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main"
G
lockfile0 = File.read(bundled_app_lock)
@@ -499,7 +547,7 @@ RSpec.describe "bundle install with git sources" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main"
G
lockfile0 = File.read(bundled_app_lock)
@@ -519,7 +567,7 @@ RSpec.describe "bundle install with git sources" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main"
G
bundle %(config set local.rack #{lib_path("local-rack")})
@@ -583,12 +631,12 @@ RSpec.describe "bundle install with git sources" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main"
G
bundle %(config set local.rack #{lib_path("local-rack")})
bundle :install, :raise_on_error => false
- expect(err).to match(/is using branch another but Gemfile specifies master/)
+ expect(err).to match(/is using branch another but Gemfile specifies main/)
end
it "explodes on invalid revision on install" do
@@ -600,7 +648,7 @@ RSpec.describe "bundle install with git sources" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main"
G
bundle %(config set local.rack #{lib_path("local-rack")})
@@ -617,7 +665,7 @@ RSpec.describe "bundle install with git sources" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main"
G
bundle %(config set local.rack #{lib_path("local-rack")})
@@ -865,6 +913,9 @@ RSpec.describe "bundle install with git sources" do
end
it "ignores submodules if :submodule is not passed" do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_git "submodule", "1.0"
build_git "has_submodule", "1.0" do |s|
s.add_dependency "submodule"
@@ -878,12 +929,15 @@ RSpec.describe "bundle install with git sources" do
gem "has_submodule"
end
G
- expect(err).to match(/could not find gem 'submodule/i)
+ expect(err).to match(%r{submodule >= 0 could not be found in rubygems repository #{file_uri_for(gem_repo1)}/ or installed locally})
expect(the_bundle).not_to include_gems "has_submodule 1.0"
end
it "handles repos with submodules" do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_git "submodule", "1.0"
build_git "has_submodule", "1.0" do |s|
s.add_dependency "submodule"
@@ -902,6 +956,9 @@ RSpec.describe "bundle install with git sources" do
end
it "does not warn when deiniting submodules" do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_git "submodule", "1.0"
build_git "has_submodule", "1.0"
@@ -1094,6 +1151,17 @@ RSpec.describe "bundle install with git sources" do
G
expect(err).to include("Revision deadbeef does not exist in the repository")
end
+
+ it "gives a helpful error message when the remote branch no longer exists" do
+ build_git "foo"
+
+ install_gemfile <<-G, :env => { "LANG" => "en" }, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :branch => "deadbeef"
+ G
+
+ expect(err).to include("Revision deadbeef does not exist in the repository")
+ end
end
describe "bundle install with deployment mode configured and git sources" do
@@ -1211,7 +1279,7 @@ RSpec.describe "bundle install with git sources" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-RUBY
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"] unless RUBY_VERSION < "2.4"
+ $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
create_makefile("foo")
RUBY
s.write "ext/foo.c", "void Init_foo() {}"
@@ -1431,8 +1499,6 @@ In Gemfile:
describe "without git installed" do
it "prints a better error message when installing" do
- build_git "foo"
-
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -1519,7 +1585,7 @@ In Gemfile:
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "foo", :git => "#{lib_path("foo")}", :branch => "master"
+ gem "foo", :git => "#{lib_path("foo")}", :branch => "main"
G
bundle :install
diff --git a/spec/bundler/install/gemfile/path_spec.rb b/spec/bundler/install/gemfile/path_spec.rb
index 515901064f..a5207036c3 100644
--- a/spec/bundler/install/gemfile/path_spec.rb
+++ b/spec/bundler/install/gemfile/path_spec.rb
@@ -92,14 +92,12 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "demo", :path => lib_path("demo")
build_lib "aaa", :path => lib_path("demo/aaa")
- gemfile = <<-G
+ gemfile lib_path("demo/Gemfile"), <<-G
source "#{file_uri_for(gem_repo1)}"
gemspec
gem "aaa", :path => "./aaa"
G
- File.open(lib_path("demo/Gemfile"), "w") {|f| f.puts gemfile }
-
lockfile = <<~L
PATH
remote: .
@@ -314,18 +312,67 @@ RSpec.describe "bundle install with explicit source paths" do
s.add_dependency "rack", "1.0"
end
- gemfile = <<-G
+ gemfile lib_path("foo/Gemfile"), <<-G
source "#{file_uri_for(gem_repo1)}"
gemspec
G
- File.open(lib_path("foo/Gemfile"), "w") {|f| f.puts gemfile }
-
bundle "install", :dir => lib_path("foo")
expect(the_bundle).to include_gems "foo 1.0", :dir => lib_path("foo")
expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("foo")
end
+ it "does not unlock dependencies of path sources" do
+ build_repo4 do
+ build_gem "graphql", "2.0.15"
+ build_gem "graphql", "2.0.16"
+ end
+
+ build_lib "foo", "0.1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "graphql", "~> 2.0"
+ end
+
+ gemfile_path = lib_path("foo/Gemfile")
+
+ gemfile gemfile_path, <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gemspec
+ G
+
+ lockfile_path = lib_path("foo/Gemfile.lock")
+
+ original_lockfile = <<~L
+ PATH
+ remote: .
+ specs:
+ foo (0.1.0)
+ graphql (~> 2.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ graphql (2.0.15)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile lockfile_path, original_lockfile
+
+ build_lib "foo", "0.1.1", :path => lib_path("foo") do |s|
+ s.add_dependency "graphql", "~> 2.0"
+ end
+
+ bundle "install", :dir => lib_path("foo")
+ expect(lockfile_path).to read_as(original_lockfile.gsub("foo (0.1.0)", "foo (0.1.1)"))
+ end
+
it "supports gemspec syntax with an alternative path" do
build_lib "foo", "1.0", :path => lib_path("foo") do |s|
s.add_dependency "rack", "1.0"
@@ -735,6 +782,52 @@ RSpec.describe "bundle install with explicit source paths" do
expect(the_bundle).to include_gems "rack 0.9.1"
end
+
+ it "does not remove existing ruby platform" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "0.9.1"
+ end
+
+ lockfile <<~L
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ expect(lockfile).to eq <<~G
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ rack (= 0.9.1)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
end
describe "switching sources" do
@@ -791,13 +884,11 @@ RSpec.describe "bundle install with explicit source paths" do
describe "when there are both a gemspec and remote gems" do
it "doesn't query rubygems for local gemspec name" do
build_lib "private_lib", "2.2", :path => lib_path("private_lib")
- gemfile = <<-G
+ gemfile lib_path("private_lib/Gemfile"), <<-G
source "http://localgemserver.test"
gemspec
gem 'rack'
G
- File.open(lib_path("private_lib/Gemfile"), "w") {|f| f.puts gemfile }
-
bundle :install, :env => { "DEBUG" => "1" }, :artifice => "endpoint", :dir => lib_path("private_lib")
expect(out).to match(%r{^HTTP GET http://localgemserver\.test/api/v1/dependencies\?gems=rack$})
expect(out).not_to match(/^HTTP GET.*private_lib/)
diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb
index a357a92272..219ae6c2f4 100644
--- a/spec/bundler/install/gemfile/platform_spec.rb
+++ b/spec/bundler/install/gemfile/platform_spec.rb
@@ -75,6 +75,81 @@ RSpec.describe "bundle install across platforms" do
expect(the_bundle).to include_gems "platform_specific 1.0 RUBY"
end
+ context "on universal Rubies" do
+ before do
+ build_repo4 do
+ build_gem "darwin_single_arch" do |s|
+ s.platform = "ruby"
+ s.write "lib/darwin_single_arch.rb", "DARWIN_SINGLE_ARCH = '1.0 RUBY'"
+ end
+ build_gem "darwin_single_arch" do |s|
+ s.platform = "arm64-darwin"
+ s.write "lib/darwin_single_arch.rb", "DARWIN_SINGLE_ARCH = '1.0 arm64-darwin'"
+ end
+ build_gem "darwin_single_arch" do |s|
+ s.platform = "x86_64-darwin"
+ s.write "lib/darwin_single_arch.rb", "DARWIN_SINGLE_ARCH = '1.0 x86_64-darwin'"
+ end
+ end
+ end
+
+ it "pulls in the correct architecture gem" do
+ lockfile <<-G
+ GEM
+ remote: #{file_uri_for(gem_repo4)}
+ specs:
+ darwin_single_arch (1.0)
+ darwin_single_arch (1.0-arm64-darwin)
+ darwin_single_arch (1.0-x86_64-darwin)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ darwin_single_arch
+ G
+
+ simulate_platform "universal-darwin-21"
+ simulate_ruby_platform "universal.x86_64-darwin21" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "darwin_single_arch"
+ G
+
+ expect(the_bundle).to include_gems "darwin_single_arch 1.0 x86_64-darwin"
+ end
+ end
+
+ it "pulls in the correct architecture gem on arm64e macOS Ruby" do
+ lockfile <<-G
+ GEM
+ remote: #{file_uri_for(gem_repo4)}
+ specs:
+ darwin_single_arch (1.0)
+ darwin_single_arch (1.0-arm64-darwin)
+ darwin_single_arch (1.0-x86_64-darwin)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ darwin_single_arch
+ G
+
+ simulate_platform "universal-darwin-21"
+ simulate_ruby_platform "universal.arm64e-darwin21" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "darwin_single_arch"
+ G
+
+ expect(the_bundle).to include_gems "darwin_single_arch 1.0 arm64-darwin"
+ end
+ end
+ end
+
it "works with gems that have different dependencies" do
simulate_platform "java"
install_gemfile <<-G
@@ -384,7 +459,7 @@ RSpec.describe "bundle install with platform conditionals" do
tzinfo (2.0.4)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
activesupport
@@ -475,7 +550,7 @@ RSpec.describe "bundle install with platform conditionals" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :platform => [:mingw, :mswin, :x64_mingw, :jruby]
+ gem "rack", :platform => [:windows, :mingw, :mswin, :x64_mingw, :jruby]
G
bundle "install"
@@ -497,11 +572,31 @@ RSpec.describe "bundle install with platform conditionals" do
#{Bundler::VERSION}
L
end
+
+ it "resolves fine when a dependency is unused on a platform different from the current one, but reintroduced transitively" do
+ bundle "config set --local force_ruby_platform true"
+
+ build_repo4 do
+ build_gem "listen", "3.7.1" do |s|
+ s.add_dependency "ffi"
+ end
+
+ build_gem "ffi", "1.15.5"
+ end
+
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "listen"
+ gem "ffi", :platform => :windows
+ G
+ expect(err).to be_empty
+ end
end
RSpec.describe "when a gem has no architecture" do
it "still installs correctly" do
- simulate_platform mswin
+ simulate_platform x86_mswin32
build_repo2 do
# The rcov gem is platform mswin32, but has no arch
diff --git a/spec/bundler/install/gemfile/ruby_spec.rb b/spec/bundler/install/gemfile/ruby_spec.rb
index ba250acfdd..39f09031b7 100644
--- a/spec/bundler/install/gemfile/ruby_spec.rb
+++ b/spec/bundler/install/gemfile/ruby_spec.rb
@@ -11,13 +11,13 @@ RSpec.describe "ruby requirement" do
it "allows adding gems" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- ruby "#{RUBY_VERSION}"
+ ruby "#{Gem.ruby_version}"
gem "rack"
G
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- ruby "#{RUBY_VERSION}"
+ ruby "#{Gem.ruby_version}"
gem "rack"
gem "rack-obama"
G
@@ -28,7 +28,7 @@ RSpec.describe "ruby requirement" do
it "allows removing the ruby version requirement" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- ruby "~> #{RUBY_VERSION}"
+ ruby "~> #{Gem.ruby_version}"
gem "rack"
G
@@ -46,7 +46,7 @@ RSpec.describe "ruby requirement" do
it "allows changing the ruby version requirement to something compatible" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- ruby ">= #{RUBY_VERSION[0..2]}.0"
+ ruby ">= #{current_ruby_minor}"
gem "rack"
G
@@ -55,7 +55,7 @@ RSpec.describe "ruby requirement" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- ruby ">= #{RUBY_VERSION}"
+ ruby ">= #{Gem.ruby_version}"
gem "rack"
G
@@ -93,7 +93,7 @@ RSpec.describe "ruby requirement" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- ruby ">= #{RUBY_VERSION[0..2]}.0"
+ ruby ">= #{current_ruby_minor}"
gem "rack"
G
@@ -104,7 +104,7 @@ RSpec.describe "ruby requirement" do
it "allows requirements with trailing whitespace" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- ruby "#{RUBY_VERSION}\\n \t\\n"
+ ruby "#{Gem.ruby_version}\\n \t\\n"
gem "rack"
G
diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb
index 89c812e5d5..dd86187a6b 100644
--- a/spec/bundler/install/gemfile/sources_spec.rb
+++ b/spec/bundler/install/gemfile/sources_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "fails", :bundler => "3" do
- bundle :instal, :artifice => "compact_index", :raise_on_error => false
+ bundle :install, :artifice => "compact_index", :raise_on_error => false
expect(err).to include("Each source after the first must include a block")
expect(exitstatus).to eq(4)
end
@@ -262,7 +262,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
depends_on_rack!
@@ -371,7 +371,15 @@ RSpec.describe "bundle install with gems on multiple sources" do
it "fails" do
bundle :install, :artifice => "compact_index", :raise_on_error => false
- expect(err).to include("Could not find gem 'missing', which is required by gem 'depends_on_missing', in any of the sources.")
+ expect(err).to end_with <<~E.strip
+ Could not find compatible versions
+
+ Because every version of depends_on_missing depends on missing >= 0
+ and missing >= 0 could not be found in any of the sources,
+ depends_on_missing cannot be used.
+ So, because Gemfile depends on depends_on_missing >= 0,
+ version solving has failed.
+ E
end
end
@@ -425,9 +433,15 @@ RSpec.describe "bundle install with gems on multiple sources" do
it "does not find the dependency" do
bundle :install, :artifice => "compact_index", :raise_on_error => false
- expect(err).to include(
- "Could not find gem 'rack', which is required by gem 'depends_on_rack', in rubygems repository https://gem.repo2/ or installed locally."
- )
+ expect(err).to end_with <<~E.strip
+ Could not find compatible versions
+
+ Because every version of depends_on_rack depends on rack >= 0
+ and rack >= 0 could not be found in rubygems repository https://gem.repo2/ or installed locally,
+ depends_on_rack cannot be used.
+ So, because Gemfile depends on depends_on_rack >= 0,
+ version solving has failed.
+ E
end
end
@@ -626,7 +640,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
zeitwerk (2.4.2)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
activesupport
@@ -682,7 +696,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
sidekiq (>= 6.1.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
activesupport
@@ -766,7 +780,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
sidekiq (>= 6.1.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
activesupport
@@ -822,7 +836,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
sidekiq (>= 6.1.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
activesupport
@@ -910,7 +924,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
nokogiri (>= 1.2.3)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
handsoap!
@@ -970,7 +984,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack (0.9.1)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
rack!
@@ -1000,7 +1014,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack (0.9.1)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
rack!
@@ -1022,7 +1036,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack (0.9.1)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
rack!
@@ -1306,7 +1320,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(out).to include("Using example 0.1.0")
end
- it "fails inmmediately with a helpful error when a rubygems source does not exist and bundler/setup is required" do
+ it "fails immediately with a helpful error when a rubygems source does not exist and bundler/setup is required" do
gemfile <<-G
source "https://gem.repo1"
@@ -1325,7 +1339,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(err).to include("Could not find gem 'example' in locally installed gems.")
end
- it "fails inmmediately with a helpful error when a non retriable network error happens while resolving sources" do
+ it "fails immediately with a helpful error when a non retriable network error happens while resolving sources" do
gemfile <<-G
source "https://gem.repo1"
@@ -1434,7 +1448,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
mime-types (3.3.1)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
capybara (~> 2.5.0)
@@ -1458,7 +1472,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
mime-types (3.0.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
capybara (~> 2.5.0)
@@ -1470,6 +1484,59 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
end
+ context "when default source includes old gems with nil required_ruby_version" do
+ before do
+ build_repo2 do
+ build_gem "ruport", "1.7.0.3" do |s|
+ s.add_dependency "pdf-writer", "1.1.8"
+ end
+ end
+
+ build_repo gem_repo4 do
+ build_gem "pdf-writer", "1.1.8"
+ end
+
+ path = "#{gem_repo4}/#{Gem::MARSHAL_SPEC_DIR}/pdf-writer-1.1.8.gemspec.rz"
+ spec = Marshal.load(Bundler.rubygems.inflate(File.binread(path)))
+ spec.instance_variable_set(:@required_ruby_version, nil)
+ File.open(path, "wb") do |f|
+ f.write Gem.deflate(Marshal.dump(spec))
+ end
+
+ gemfile <<~G
+ source "https://localgemserver.test"
+
+ gem "ruport", "= 1.7.0.3", :source => "https://localgemserver.test/extra"
+ G
+ end
+
+ it "handles that fine" do
+ bundle "install", :artifice => "compact_index_extra", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://localgemserver.test/
+ specs:
+ pdf-writer (1.1.8)
+
+ GEM
+ remote: https://localgemserver.test/extra/
+ specs:
+ ruport (1.7.0.3)
+ pdf-writer (= 1.1.8)
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ ruport (= 1.7.0.3)!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
context "when default source includes old gems with nil required_rubygems_version" do
before do
build_repo2 do
@@ -1512,7 +1579,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
pdf-writer (= 1.1.8)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
ruport (= 1.7.0.3)!
@@ -1553,7 +1620,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
pdf-writer (1.1.8)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
pdf-writer (= 1.1.8)
@@ -1563,4 +1630,43 @@ RSpec.describe "bundle install with gems on multiple sources" do
L
end
end
+
+ context "when mistakenly adding a top level gem already depended on and cached under the wrong source" do
+ before do
+ build_repo4 do
+ build_gem "some_private_gem", "0.1.0" do |s|
+ s.add_dependency "example", "~> 1.0"
+ end
+ end
+
+ build_repo2 do
+ build_gem "example", "1.0.0"
+ end
+
+ install_gemfile <<~G, :artifice => "compact_index"
+ source "https://gem.repo2"
+
+ source "https://gem.repo4" do
+ gem "some_private_gem"
+ end
+ G
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ source "https://gem.repo4" do
+ gem "some_private_gem"
+ gem "example" # MISTAKE, example is not available at gem.repo4
+ end
+ G
+ end
+
+ it "shows a proper error message and does not generate a corrupted lockfile" do
+ expect do
+ bundle :install, :artifice => "compact_index", :raise_on_error => false, :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ end.not_to change { lockfile }
+
+ expect(err).to include("Could not find gem 'example' in rubygems repository https://gem.repo4/")
+ end
+ end
end
diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb
index 276a84f2a6..cab53a663a 100644
--- a/spec/bundler/install/gemfile/specific_platform_spec.rb
+++ b/spec/bundler/install/gemfile/specific_platform_spec.rb
@@ -6,10 +6,8 @@ RSpec.describe "bundle install with specific platforms" do
gem "google-protobuf"
G
- context "when on a darwin machine" do
- before { simulate_platform "x86_64-darwin-15" }
-
- it "locks to the specific darwin platform" do
+ it "locks to the specific darwin platform" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
@@ -19,8 +17,10 @@ RSpec.describe "bundle install with specific platforms" do
google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
])
end
+ end
- it "understands that a non-platform specific gem in a old lockfile doesn't necessarily mean installing the non-specific variant" do
+ it "understands that a non-platform specific gem in a old lockfile doesn't necessarily mean installing the non-specific variant" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
system_gems "bundler-2.1.4"
@@ -53,8 +53,10 @@ RSpec.describe "bundle install with specific platforms" do
# make sure the platform that got actually installed with the old bundler is used
expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
end
+ end
- it "understands that a non-platform specific gem in a new lockfile locked only to RUBY doesn't necessarily mean installing the non-specific variant" do
+ it "understands that a non-platform specific gem in a new lockfile locked only to RUBY doesn't necessarily mean installing the non-specific variant" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
system_gems "bundler-2.1.4"
@@ -103,8 +105,56 @@ RSpec.describe "bundle install with specific platforms" do
#{Bundler::VERSION}
L
end
+ end
+
+ context "when running on a legacy lockfile locked only to RUBY" do
+ around do |example|
+ build_repo4 do
+ build_gem "nokogiri", "1.3.10"
+ build_gem "nokogiri", "1.3.10" do |s|
+ s.platform = "arm64-darwin"
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.3.10)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
- it "doesn't discard previously installed platform specific gem and fall back to ruby on subsequent bundles" do
+ simulate_platform "arm64-darwin-22", &example
+ end
+
+ it "still installs the generic RUBY variant if necessary" do
+ bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(out).to include("Installing nokogiri 1.3.10")
+ end
+
+ it "still installs the generic RUBY variant if necessary, even in frozen mode" do
+ bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "BUNDLE_FROZEN" => "true" }
+ expect(out).to include("Installing nokogiri 1.3.10")
+ end
+ end
+
+ it "doesn't discard previously installed platform specific gem and fall back to ruby on subsequent bundles" do
+ simulate_platform "x86_64-darwin-15" do
build_repo2 do
build_gem("libv8", "8.4.255.0")
build_gem("libv8", "8.4.255.0") {|s| s.platform = "universal-darwin" }
@@ -147,8 +197,44 @@ RSpec.describe "bundle install with specific platforms" do
bundle "add mini_racer --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
expect(out).to include("Using libv8 8.4.255.0 (universal-darwin)")
end
+ end
+
+ it "chooses platform specific gems even when resolving upon materialization and the API returns more specific platforms first" do
+ simulate_platform "x86_64-darwin-15" do
+ build_repo4 do
+ build_gem("grpc", "1.50.0")
+ build_gem("grpc", "1.50.0") {|s| s.platform = "universal-darwin" }
+ end
+
+ gemfile <<-G
+ source "https://localgemserver.test"
+ gem "grpc"
+ G
- it "caches the universal-darwin gem when --all-platforms is passed and properly picks it up on further bundler invocations" do
+ # simulate lockfile created with old bundler, which only locks for ruby platform
+ lockfile <<-L
+ GEM
+ remote: https://localgemserver.test/
+ specs:
+ grpc (1.50.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ grpc
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose", :artifice => "compact_index_precompiled_before", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(out).to include("Installing grpc 1.50.0 (universal-darwin)")
+ end
+ end
+
+ it "caches the universal-darwin gem when --all-platforms is passed and properly picks it up on further bundler invocations" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
gemfile(google_protobuf)
bundle "cache --all-platforms"
@@ -157,8 +243,10 @@ RSpec.describe "bundle install with specific platforms" do
bundle "install --verbose"
expect(err).to be_empty
end
+ end
- it "caches the universal-darwin gem when cache_all_platforms is configured and properly picks it up on further bundler invocations" do
+ it "caches the universal-darwin gem when cache_all_platforms is configured and properly picks it up on further bundler invocations" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
gemfile(google_protobuf)
bundle "config set --local cache_all_platforms true"
@@ -168,44 +256,46 @@ RSpec.describe "bundle install with specific platforms" do
bundle "install --verbose"
expect(err).to be_empty
end
+ end
- it "caches multiplatform git gems with a single gemspec when --all-platforms is passed" do
- git = build_git "pg_array_parser", "1.0"
+ it "caches multiplatform git gems with a single gemspec when --all-platforms is passed" do
+ git = build_git "pg_array_parser", "1.0"
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "pg_array_parser", :git => "#{lib_path("pg_array_parser-1.0")}"
- G
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "pg_array_parser", :git => "#{lib_path("pg_array_parser-1.0")}"
+ G
- lockfile <<-L
- GIT
- remote: #{lib_path("pg_array_parser-1.0")}
- revision: #{git.ref_for("master")}
- specs:
- pg_array_parser (1.0-java)
- pg_array_parser (1.0)
+ lockfile <<-L
+ GIT
+ remote: #{lib_path("pg_array_parser-1.0")}
+ revision: #{git.ref_for("main")}
+ specs:
+ pg_array_parser (1.0-java)
+ pg_array_parser (1.0)
- GEM
- specs:
+ GEM
+ specs:
- PLATFORMS
- java
- #{lockfile_platforms}
+ PLATFORMS
+ java
+ #{lockfile_platforms}
- DEPENDENCIES
- pg_array_parser!
+ DEPENDENCIES
+ pg_array_parser!
- BUNDLED WITH
- #{Bundler::VERSION}
- L
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
- bundle "config set --local cache_all true"
- bundle "cache --all-platforms"
+ bundle "config set --local cache_all true"
+ bundle "cache --all-platforms"
- expect(err).to be_empty
- end
+ expect(err).to be_empty
+ end
- it "uses the platform-specific gem with extra dependencies" do
+ it "uses the platform-specific gem with extra dependencies" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem_with_different_dependencies_per_platform
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -218,25 +308,29 @@ RSpec.describe "bundle install with specific platforms" do
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(["CFPropertyList-1.0",
"facter-2.4.6-universal-darwin"])
end
+ end
- context "when adding a platform via lock --add_platform" do
- before do
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- end
+ context "when adding a platform via lock --add_platform" do
+ before do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ end
- it "adds the foreign platform" do
+ it "adds the foreign platform" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
- bundle "lock --add-platform=#{x64_mingw}"
+ bundle "lock --add-platform=#{x64_mingw32}"
- expect(the_bundle.locked_gems.platforms).to eq([x64_mingw, pl("x86_64-darwin-15")])
+ expect(the_bundle.locked_gems.platforms).to eq([x64_mingw32, pl("x86_64-darwin-15")])
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw32
])
end
+ end
- it "falls back on plain ruby when that version doesnt have a platform-specific gem" do
+ it "falls back on plain ruby when that version doesn't have a platform-specific gem" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
bundle "lock --add-platform=#{java}"
@@ -331,7 +425,13 @@ RSpec.describe "bundle install with specific platforms" do
G
error_message = <<~ERROR.strip
- Could not find gem 'sorbet-static (= 0.5.6433)' with platform 'arm64-darwin-21', which is required by gem 'sorbet (= 0.5.6433)', in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally.
+ Could not find compatible versions
+
+ Because every version of sorbet depends on sorbet-static = 0.5.6433
+ and sorbet-static = 0.5.6433 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally for any resolution platforms (arm64-darwin-21),
+ sorbet cannot be used.
+ So, because Gemfile depends on sorbet = 0.5.6433,
+ version solving has failed.
The source contains the following gems matching 'sorbet-static (= 0.5.6433)':
* sorbet-static-0.5.6433-universal-darwin-20
@@ -374,6 +474,458 @@ RSpec.describe "bundle install with specific platforms" do
ERROR
end
+ it "automatically fixes the lockfile if RUBY platform is locked and some gem has no RUBY variant available" do
+ build_repo4 do
+ build_gem("sorbet-static-and-runtime", "0.5.10160") do |s|
+ s.add_runtime_dependency "sorbet", "= 0.5.10160"
+ s.add_runtime_dependency "sorbet-runtime", "= 0.5.10160"
+ end
+
+ build_gem("sorbet", "0.5.10160") do |s|
+ s.add_runtime_dependency "sorbet-static", "= 0.5.10160"
+ end
+
+ build_gem("sorbet-runtime", "0.5.10160")
+
+ build_gem("sorbet-static", "0.5.10160") do |s|
+ s.platform = Gem::Platform.local
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "sorbet-static-and-runtime"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet (0.5.10160)
+ sorbet-static (= 0.5.10160)
+ sorbet-runtime (0.5.10160)
+ sorbet-static (0.5.10160-#{Gem::Platform.local})
+ sorbet-static-and-runtime (0.5.10160)
+ sorbet (= 0.5.10160)
+ sorbet-runtime (= 0.5.10160)
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ sorbet-static-and-runtime
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet (0.5.10160)
+ sorbet-static (= 0.5.10160)
+ sorbet-runtime (0.5.10160)
+ sorbet-static (0.5.10160-#{Gem::Platform.local})
+ sorbet-static-and-runtime (0.5.10160)
+ sorbet (= 0.5.10160)
+ sorbet-runtime (= 0.5.10160)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ sorbet-static-and-runtime
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile if both RUBY platform and a more specific platform are locked, and some gem has no RUBY variant available" do
+ build_repo4 do
+ build_gem "nokogiri", "1.12.0"
+ build_gem "nokogiri", "1.12.0" do |s|
+ s.platform = "x86_64-darwin"
+ end
+
+ build_gem "nokogiri", "1.13.0"
+ build_gem "nokogiri", "1.13.0" do |s|
+ s.platform = "x86_64-darwin"
+ end
+
+ build_gem("sorbet-static", "0.5.10601") do |s|
+ s.platform = "x86_64-darwin"
+ end
+ end
+
+ simulate_platform "x86_64-darwin-22" do
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ gem "sorbet-static"
+ G
+ end
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.12.0)
+ nokogiri (1.12.0-x86_64-darwin)
+ sorbet-static (0.5.10601-x86_64-darwin)
+
+ PLATFORMS
+ ruby
+ x86_64-darwin
+
+ DEPENDENCIES
+ nokogiri
+ sorbet
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-darwin-22" do
+ bundle "update --conservative nokogiri"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.13.0-x86_64-darwin)
+ sorbet-static (0.5.10601-x86_64-darwin)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ nokogiri
+ sorbet-static
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile if only RUBY platform is locked and some gem has no RUBY variant available" do
+ build_repo4 do
+ build_gem("sorbet-static-and-runtime", "0.5.10160") do |s|
+ s.add_runtime_dependency "sorbet", "= 0.5.10160"
+ s.add_runtime_dependency "sorbet-runtime", "= 0.5.10160"
+ end
+
+ build_gem("sorbet", "0.5.10160") do |s|
+ s.add_runtime_dependency "sorbet-static", "= 0.5.10160"
+ end
+
+ build_gem("sorbet-runtime", "0.5.10160")
+
+ build_gem("sorbet-static", "0.5.10160") do |s|
+ s.platform = Gem::Platform.local
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "sorbet-static-and-runtime"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet (0.5.10160)
+ sorbet-static (= 0.5.10160)
+ sorbet-runtime (0.5.10160)
+ sorbet-static (0.5.10160-#{Gem::Platform.local})
+ sorbet-static-and-runtime (0.5.10160)
+ sorbet (= 0.5.10160)
+ sorbet-runtime (= 0.5.10160)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ sorbet-static-and-runtime
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet (0.5.10160)
+ sorbet-static (= 0.5.10160)
+ sorbet-runtime (0.5.10160)
+ sorbet-static (0.5.10160-#{Gem::Platform.local})
+ sorbet-static-and-runtime (0.5.10160)
+ sorbet (= 0.5.10160)
+ sorbet-runtime (= 0.5.10160)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ sorbet-static-and-runtime
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile without removing other variants if it's missing platform gems, but they are installed locally" do
+ simulate_platform "x86_64-darwin-21" do
+ build_repo4 do
+ build_gem("sorbet-static", "0.5.10549") do |s|
+ s.platform = "universal-darwin-20"
+ end
+
+ build_gem("sorbet-static", "0.5.10549") do |s|
+ s.platform = "universal-darwin-21"
+ end
+ end
+
+ # Make sure sorbet-static-0.5.10549-universal-darwin-21 is installed
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "sorbet-static", "= 0.5.10549"
+ G
+
+ # Make sure the lockfile is missing sorbet-static-0.5.10549-universal-darwin-21
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet-static (0.5.10549-universal-darwin-20)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ sorbet-static (= 0.5.10549)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet-static (0.5.10549-universal-darwin-20)
+ sorbet-static (0.5.10549-universal-darwin-21)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ sorbet-static (= 0.5.10549)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "does not remove ruby if gems for other platforms, and not present in the lockfile, exist in the Gemfile" do
+ build_repo4 do
+ build_gem "nokogiri", "1.13.8"
+ build_gem "nokogiri", "1.13.8" do |s|
+ s.platform = Gem::Platform.local
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+
+ gem "tzinfo", "~> 1.2", platform: :#{not_local_tag}
+ G
+
+ original_lockfile = <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.13.8)
+ nokogiri (1.13.8-#{Gem::Platform.local})
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ nokogiri
+ tzinfo (~> 1.2)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile original_lockfile
+
+ bundle "lock --update"
+
+ expect(lockfile).to eq(original_lockfile)
+ end
+
+ it "does not remove ruby when adding a new gem to the Gemfile" do
+ build_repo4 do
+ build_gem "concurrent-ruby", "1.2.2"
+ build_gem "rack", "3.0.7"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "concurrent-ruby"
+ gem "rack"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ concurrent-ruby (1.2.2)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ concurrent-ruby
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ concurrent-ruby (1.2.2)
+ rack (3.0.7)
+
+ PLATFORMS
+ #{formatted_lockfile_platforms(*["ruby", generic_local_platform].uniq)}
+
+ DEPENDENCIES
+ concurrent-ruby
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "can fallback to a source gem when platform gems are incompatible with current ruby version" do
+ setup_multiplatform_gem_with_source_gem
+
+ source = file_uri_for(gem_repo2)
+
+ gemfile <<~G
+ source "#{source}"
+
+ gem "my-precompiled-gem"
+ G
+
+ # simulate lockfile which includes both a precompiled gem with:
+ # - Gem the current platform (with incompatible ruby version)
+ # - A source gem with compatible ruby version
+ lockfile <<-L
+ GEM
+ remote: #{source}/
+ specs:
+ my-precompiled-gem (3.0.0)
+ my-precompiled-gem (3.0.0-#{Bundler.local_platform})
+
+ PLATFORMS
+ ruby
+ #{Bundler.local_platform}
+
+ DEPENDENCIES
+ my-precompiled-gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle :install
+ end
+
+ it "automatically fixes the lockfile if the specific platform is locked and we move to a newer ruby version for which a native package is not available" do
+ #
+ # Given an existing application using native gems (e.g., nokogiri)
+ # And a lockfile generated with a stable ruby version
+ # When want test the application against ruby-head and `bundle install`
+ # Then bundler should fall back to the generic ruby platform gem
+ #
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.0"
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x86_64-linux"
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri", "1.14.0"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.0-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri (= 1.14.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle :install
+
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.0)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri (= 1.14.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
private
def setup_multiplatform_gem
@@ -404,4 +956,16 @@ RSpec.describe "bundle install with specific platforms" do
build_gem("CFPropertyList")
end
end
+
+ def setup_multiplatform_gem_with_source_gem
+ build_repo2 do
+ build_gem("my-precompiled-gem", "3.0.0")
+ build_gem("my-precompiled-gem", "3.0.0") do |s|
+ s.platform = Bundler.local_platform
+
+ # purposely unresolvable
+ s.required_ruby_version = ">= 1000.0.0"
+ end
+ end
+ end
end
diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb
index 5c25b1092a..65be262748 100644
--- a/spec/bundler/install/gems/compact_index_spec.rb
+++ b/spec/bundler/install/gems/compact_index_spec.rb
@@ -406,7 +406,7 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(out).to include("Fetching source index from http://localgemserver.test/extra")
end
- it "does not fetch every spec if the index of gems is large when doing back deps" do
+ it "does not fetch every spec when doing back deps" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -416,9 +416,7 @@ The checksum of /versions does not match the checksum provided by the server! So
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
- api_request_limit = low_api_request_limit_for(gem_repo2)
-
- install_gemfile <<-G, :artifice => "compact_index_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
+ install_gemfile <<-G, :artifice => "compact_index_extra_missing", :env => env_for_missing_prerelease_default_gem_activation
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -428,7 +426,7 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(the_bundle).to include_gems "back_deps 1.0"
end
- it "does not fetch every spec if the index of gems is large when doing back deps & everything is the compact index" do
+ it "does not fetch every spec when doing back deps & everything is the compact index" do
build_repo4 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -438,9 +436,7 @@ The checksum of /versions does not match the checksum provided by the server! So
FileUtils.rm_rf Dir[gem_repo4("gems/foo-*.gem")]
end
- api_request_limit = low_api_request_limit_for(gem_repo4)
-
- install_gemfile <<-G, :artifice => "compact_index_extra_api_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
+ install_gemfile <<-G, :artifice => "compact_index_extra_api_missing", :env => env_for_missing_prerelease_default_gem_activation
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -519,18 +515,6 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(out).to include("Fetching gem metadata from #{source_uri}")
end
- it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do
- sudo "mkdir -p #{system_gem_path("bin")}"
- sudo "chown -R root #{system_gem_path("bin")}"
-
- gemfile <<-G
- source "#{source_uri}"
- gem "rails"
- G
- bundle :install, :artifice => "compact_index"
- expect(the_bundle).to include_gems "rails 2.3.2"
- end
-
it "installs the binstubs", :bundler => "< 3" do
gemfile <<-G
source "#{source_uri}"
@@ -699,6 +683,15 @@ The checksum of /versions does not match the checksum provided by the server! So
bundle :install, :artifice => "compact_index_strict_basic_authentication", :raise_on_error => false
expect(err).to include("Bad username or password")
end
+
+ it "does not fallback to old dependency API if bad authentication is provided" do
+ bundle "config set #{source_hostname} #{user}:wrong"
+
+ bundle :install, :artifice => "compact_index_strict_basic_authentication", :raise_on_error => false, :verbose => true
+ expect(err).to include("Bad username or password")
+ expect(out).to include("HTTP 401 Unauthorized http://user@localgemserver.test/versions")
+ expect(out).not_to include("HTTP 401 Unauthorized http://user@localgemserver.test/api/v1/dependencies")
+ end
end
describe "with no password" do
@@ -942,7 +935,7 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(out).to include("rails-2.3.2 from rubygems remote at #{source_uri}/ has either corrupted API or lockfile dependencies")
expect(err).to include(<<-E.strip)
Bundler::APIResponseMismatchError: Downloading rails-2.3.2 revealed dependencies not in the API or the lockfile (#{deps.map(&:to_s).join(", ")}).
-Either installing with `--full-index` or running `bundle update rails` should fix the problem.
+Running `bundle update rails` should fix the problem.
E
end
diff --git a/spec/bundler/install/gems/dependency_api_spec.rb b/spec/bundler/install/gems/dependency_api_spec.rb
index 79317a7fad..a54f1db772 100644
--- a/spec/bundler/install/gems/dependency_api_spec.rb
+++ b/spec/bundler/install/gems/dependency_api_spec.rb
@@ -119,7 +119,7 @@ RSpec.describe "gemcutter's dependency API" do
end
it "falls back when the API errors out" do
- simulate_platform mswin
+ simulate_platform x86_mswin32
build_repo2 do
# The rcov gem is platform mswin32, but has no arch
@@ -359,7 +359,7 @@ RSpec.describe "gemcutter's dependency API" do
expect(out).to include("Fetching source index from http://localgemserver.test/extra")
end
- it "does not fetch every spec if the index of gems is large when doing back deps", :bundler => "< 3" do
+ it "does not fetch every spec when doing back deps", :bundler => "< 3" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -369,9 +369,7 @@ RSpec.describe "gemcutter's dependency API" do
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
- api_request_limit = low_api_request_limit_for(gem_repo2)
-
- install_gemfile <<-G, :artifice => "endpoint_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
+ install_gemfile <<-G, :artifice => "endpoint_extra_missing", :env => env_for_missing_prerelease_default_gem_activation
source "#{source_uri}"
source "#{source_uri}/extra"
gem "back_deps"
@@ -380,7 +378,7 @@ RSpec.describe "gemcutter's dependency API" do
expect(the_bundle).to include_gems "back_deps 1.0"
end
- it "does not fetch every spec if the index of gems is large when doing back deps using blocks" do
+ it "does not fetch every spec when doing back deps using blocks" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -390,9 +388,7 @@ RSpec.describe "gemcutter's dependency API" do
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
- api_request_limit = low_api_request_limit_for(gem_repo2)
-
- install_gemfile <<-G, :artifice => "endpoint_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
+ install_gemfile <<-G, :artifice => "endpoint_extra_missing", :env => env_for_missing_prerelease_default_gem_activation
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -443,6 +439,22 @@ RSpec.describe "gemcutter's dependency API" do
expect(the_bundle).to include_gems "back_deps 1.0"
end
+ it "does not fetch all marshaled specs" do
+ build_repo2 do
+ build_gem "foo", "1.0"
+ build_gem "foo", "2.0"
+ end
+
+ install_gemfile <<-G, :artifice => "endpoint", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, :verbose => true
+ source "#{source_uri}"
+
+ gem "foo"
+ G
+
+ expect(out).to include("foo-2.0.gemspec.rz")
+ expect(out).not_to include("foo-1.0.gemspec.rz")
+ end
+
it "does not refetch if the only unmet dependency is bundler" do
build_repo2 do
build_gem "bundler_dep" do |s|
@@ -460,18 +472,6 @@ RSpec.describe "gemcutter's dependency API" do
expect(out).to include("Fetching gem metadata from #{source_uri}")
end
- it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do
- sudo "mkdir -p #{system_gem_path("bin")}"
- sudo "chown -R root #{system_gem_path("bin")}"
-
- gemfile <<-G
- source "#{source_uri}"
- gem "rails"
- G
- bundle :install, :artifice => "endpoint"
- expect(the_bundle).to include_gems "rails 2.3.2"
- end
-
it "installs the binstubs", :bundler => "< 3" do
gemfile <<-G
source "#{source_uri}"
diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb
index ed61531574..d5fa55be48 100644
--- a/spec/bundler/install/gems/flex_spec.rb
+++ b/spec/bundler/install/gems/flex_spec.rb
@@ -190,28 +190,35 @@ RSpec.describe "bundle flex_install" do
expect(err).to match(/could not find gem 'rack-obama/i)
end
- it "suggests deleting the Gemfile.lock file when the Gemfile requires different versions than the lock" do
+ it "discards the locked gems when the Gemfile requires different versions than the lock" do
bundle "config set force_ruby_platform true"
nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "rack":
- In snapshot (Gemfile.lock):
- rack (= 0.9.1)
+ Could not find compatible versions
- In Gemfile:
- rack-obama (= 2.0) was resolved to 2.0, which depends on
- rack (= 1.2)
-
- rack_middleware was resolved to 1.0, which depends on
- rack (= 0.9.1)
-
- Deleting your Gemfile.lock file and running `bundle install` will rebuild your snapshot from scratch, using only
- the gems in your Gemfile, which may resolve the conflict.
+ Because rack-obama >= 2.0 depends on rack = 1.2
+ and rack = 1.2 could not be found in rubygems repository #{file_uri_for(gem_repo2)}/ or installed locally,
+ rack-obama >= 2.0 cannot be used.
+ So, because Gemfile depends on rack-obama = 2.0,
+ version solving has failed.
E
bundle :install, :retry => 0, :raise_on_error => false
expect(err).to end_with(nice_error)
end
+
+ it "does not include conflicts with a single requirement tree, because that can't possibly be a conflict" do
+ bundle "config set force_ruby_platform true"
+
+ bad_error = <<-E.strip.gsub(/^ {8}/, "")
+ Bundler could not find compatible versions for gem "rack-obama":
+ In Gemfile:
+ rack-obama (= 2.0)
+ E
+
+ bundle "update rack_middleware", :retry => 0, :raise_on_error => false
+ expect(err).not_to end_with(bad_error)
+ end
end
describe "when running bundle update and Gemfile conflicts with lockfile" do
@@ -230,22 +237,6 @@ RSpec.describe "bundle flex_install" do
gem "jekyll-feed", "~> 0.12"
G
- lockfile <<-L
- GEM
- remote: #{file_uri_for(gem_repo4)}/
- specs:
- jekyll-feed (0.16.0)
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- jekyll-feed
-
- BUNDLED WITH
- #{Bundler::VERSION}
- L
-
gemfile <<-G
source "#{file_uri_for(gem_repo4)}"
gem "github-pages", "~> 226"
@@ -253,24 +244,9 @@ RSpec.describe "bundle flex_install" do
G
end
- it "suggests deleting the Gemfile.lock file when the Gemfile requires different versions than the lock" do
- nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "jekyll-feed":
- In snapshot (Gemfile.lock):
- jekyll-feed (>= 0.16.0)
-
- In Gemfile:
- jekyll-feed (~> 0.12)
-
- github-pages (~> 226) was resolved to 226, which depends on
- jekyll-feed (= 0.15.1)
-
- Deleting your Gemfile.lock file and running `bundle install` will rebuild your snapshot from scratch, using only
- the gems in your Gemfile, which may resolve the conflict.
- E
-
- bundle :update, :raise_on_error => false
- expect(err).to end_with(nice_error)
+ it "discards the conflicting lockfile information and resolves properly" do
+ bundle :update, :raise_on_error => false, :all => true
+ expect(err).to be_empty
end
end
@@ -374,7 +350,7 @@ RSpec.describe "bundle flex_install" do
end
end
- it "prints the correct error message" do
+ it "resolves them" do
# install Rails 3.0.0.rc
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -383,13 +359,12 @@ RSpec.describe "bundle flex_install" do
G
# upgrade Rails to 3.0.0 and then install again
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "rails", "3.0.0"
gem "capybara", "0.3.9"
G
-
- expect(err).to include("Gemfile.lock")
+ expect(err).to be_empty
end
end
end
diff --git a/spec/bundler/install/gems/fund_spec.rb b/spec/bundler/install/gems/fund_spec.rb
index f521b0296f..9aadc9ed25 100644
--- a/spec/bundler/install/gems/fund_spec.rb
+++ b/spec/bundler/install/gems/fund_spec.rb
@@ -6,20 +6,20 @@ RSpec.describe "bundle install" do
build_repo2 do
build_gem "has_funding_and_other_metadata" do |s|
s.metadata = {
- "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
- "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
"documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
- "homepage_uri" => "https://bestgemever.example.io",
- "mailing_list_uri" => "https://groups.example.com/bestgemever",
- "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
- "source_code_uri" => "https://example.com/user/bestgemever",
- "wiki_uri" => "https://example.com/user/bestgemever/wiki",
+ "homepage_uri" => "https://bestgemever.example.io",
+ "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
+ "source_code_uri" => "https://example.com/user/bestgemever",
+ "wiki_uri" => "https://example.com/user/bestgemever/wiki",
}
end
build_gem "has_funding", "1.2.3" do |s|
s.metadata = {
- "funding_uri" => "https://example.com/has_funding/funding",
+ "funding_uri" => "https://example.com/has_funding/funding",
}
end
@@ -52,6 +52,33 @@ RSpec.describe "bundle install" do
end
end
+ context "when gems include a fund URI but `ignore_funding_requests` is configured" do
+ before do
+ bundle "config set ignore_funding_requests true"
+ end
+
+ it "does not display the plural fund message after installing" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem 'has_funding_and_other_metadata'
+ gem 'has_funding'
+ gem 'rack-obama'
+ G
+
+ expect(out).not_to include("2 installed gems you directly depend on are looking for funding.")
+ end
+
+ it "does not display the singular fund message after installing" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem 'has_funding'
+ gem 'rack-obama'
+ G
+
+ expect(out).not_to include("1 installed gem you directly depend on is looking for funding.")
+ end
+ end
+
context "when gems do not include fund messages" do
it "does not display any fund messages" do
install_gemfile <<-G
diff --git a/spec/bundler/install/gems/native_extensions_spec.rb b/spec/bundler/install/gems/native_extensions_spec.rb
index c3e05586bd..5c18d2cc51 100644
--- a/spec/bundler/install/gems/native_extensions_spec.rb
+++ b/spec/bundler/install/gems/native_extensions_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe "installing a gem with native extensions" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"] unless RUBY_VERSION < "2.4"
+ $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
name = "c_extension_bundle"
dir_config(name)
raise "OMG" unless with_config("c_extension") == "hello"
@@ -52,7 +52,7 @@ RSpec.describe "installing a gem with native extensions" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"] unless RUBY_VERSION < "2.4"
+ $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
name = "c_extension_bundle"
dir_config(name)
raise "OMG" unless with_config("c_extension") == "hello"
@@ -97,7 +97,7 @@ RSpec.describe "installing a gem with native extensions" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"] unless RUBY_VERSION < "2.4"
+ $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
name = "c_extension_bundle_#{n}"
dir_config(name)
raise "OMG" unless with_config("c_extension_#{n}") == "#{n}"
@@ -150,7 +150,7 @@ RSpec.describe "installing a gem with native extensions" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"] unless RUBY_VERSION < "2.4"
+ $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
name = "c_extension_bundle"
dir_config(name)
raise "OMG" unless with_config("c_extension") == "hello" && with_config("c_extension_bundle-dir") == "hola"
diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb
index b136dea8bd..fb6dda2f88 100644
--- a/spec/bundler/install/gems/resolving_spec.rb
+++ b/spec/bundler/install/gems/resolving_spec.rb
@@ -159,7 +159,7 @@ RSpec.describe "bundle install with install-time dependencies" do
bundle :install, :env => { "BUNDLER_DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
- expect(out).to include("BUNDLER: Starting resolution")
+ expect(out).to include("Resolving dependencies...")
end
end
@@ -173,7 +173,7 @@ RSpec.describe "bundle install with install-time dependencies" do
bundle :install, :env => { "DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
- expect(out).to include("BUNDLER: Starting resolution")
+ expect(out).to include("Resolving dependencies...")
end
end
@@ -187,12 +187,10 @@ RSpec.describe "bundle install with install-time dependencies" do
bundle :install, :env => { "DEBUG_RESOLVER_TREE" => "1", "DEBUG" => "1" }
- activated_groups = "net_b (1.0) (ruby), net_b (1.0) (#{specific_local_platform})"
-
expect(out).to include(" net_b").
- and include("BUNDLER: Starting resolution").
- and include("BUNDLER: Finished resolution").
- and include("Attempting to activate [#{activated_groups}]")
+ and include("Resolving dependencies...").
+ and include("Solution found after 1 attempts:").
+ and include("selected net_b 1.0")
end
end
end
@@ -211,12 +209,12 @@ RSpec.describe "bundle install with install-time dependencies" do
end
install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
- ruby "#{RUBY_VERSION}"
+ ruby "#{Gem.ruby_version}"
source "http://localgemserver.test/"
gem 'rack'
G
- expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
+ expect(err).to_not include("rack-9001.0.0 requires ruby version > 9000")
expect(the_bundle).to include_gems("rack 1.2")
end
@@ -232,61 +230,210 @@ RSpec.describe "bundle install with install-time dependencies" do
end
install_gemfile <<-G, :artifice => "endpoint", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
- ruby "#{RUBY_VERSION}"
+ ruby "#{Gem.ruby_version}"
source "http://localgemserver.test/"
gem 'rack'
G
- expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
+ expect(err).to_not include("rack-9001.0.0 requires ruby version > 9000")
expect(the_bundle).to include_gems("rack 1.2")
end
- it "gives a meaningful error if there's a lockfile using the newer incompatible version" do
- build_repo2 do
- build_gem "parallel_tests", "3.7.0" do |s|
- s.required_ruby_version = ">= #{current_ruby_minor}"
+ context "when there is a lockfile using the newer incompatible version" do
+ before do
+ build_repo2 do
+ build_gem "parallel_tests", "3.7.0" do |s|
+ s.required_ruby_version = ">= #{current_ruby_minor}"
+ end
+
+ build_gem "parallel_tests", "3.8.0" do |s|
+ s.required_ruby_version = ">= #{next_ruby_minor}"
+ end
end
- build_gem "parallel_tests", "3.8.0" do |s|
- s.required_ruby_version = ">= #{next_ruby_minor}"
+ gemfile <<-G
+ source "http://localgemserver.test/"
+ gem 'parallel_tests'
+ G
+
+ lockfile <<~L
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ parallel_tests (3.8.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ parallel_tests
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically updates lockfile to use the older version" do
+ bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ parallel_tests (3.7.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ parallel_tests
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "gives a meaningful error if we're in frozen mode" do
+ expect do
+ bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s, "BUNDLE_FROZEN" => "true" }, :raise_on_error => false
+ end.not_to change { lockfile }
+
+ expect(err).to include("parallel_tests-3.8.0 requires ruby version >= #{next_ruby_minor}")
+ expect(err).not_to include("That means the author of parallel_tests (3.8.0) has removed it.")
+ end
+ end
+
+ context "with transitive dependencies in a lockfile" do
+ before do
+ build_repo2 do
+ build_gem "rubocop", "1.28.2" do |s|
+ s.required_ruby_version = ">= #{current_ruby_minor}"
+
+ s.add_dependency "rubocop-ast", ">= 1.17.0", "< 2.0"
+ end
+
+ build_gem "rubocop", "1.35.0" do |s|
+ s.required_ruby_version = ">= #{next_ruby_minor}"
+
+ s.add_dependency "rubocop-ast", ">= 1.20.1", "< 2.0"
+ end
+
+ build_gem "rubocop-ast", "1.17.0" do |s|
+ s.required_ruby_version = ">= #{current_ruby_minor}"
+ end
+
+ build_gem "rubocop-ast", "1.21.0" do |s|
+ s.required_ruby_version = ">= #{next_ruby_minor}"
+ end
end
+
+ gemfile <<-G
+ source "http://localgemserver.test/"
+ gem 'rubocop'
+ G
+
+ lockfile <<~L
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ rubocop (1.35.0)
+ rubocop-ast (>= 1.20.1, < 2.0)
+ rubocop-ast (1.21.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ parallel_tests
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
- gemfile <<-G
- source "http://localgemserver.test/"
- gem 'parallel_tests'
- G
+ it "automatically updates lockfile to use the older compatible versions" do
+ bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ rubocop (1.28.2)
+ rubocop-ast (>= 1.17.0, < 2.0)
+ rubocop-ast (1.17.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rubocop
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with a Gemfile and lock file that don't resolve under the current platform" do
+ before do
+ build_repo4 do
+ build_gem "sorbet", "0.5.10554" do |s|
+ s.add_dependency "sorbet-static", "0.5.10554"
+ end
+
+ build_gem "sorbet-static", "0.5.10554" do |s|
+ s.platform = "universal-darwin-21"
+ end
+ end
- lockfile <<~L
- GEM
- remote: http://localgemserver.test/
- specs:
- parallel_tests (3.8.0)
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'sorbet', '= 0.5.10554'
+ G
- PLATFORMS
- #{lockfile_platforms}
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet (0.5.10554)
+ sorbet-static (= 0.5.10554)
+ sorbet-static (0.5.10554-universal-darwin-21)
- DEPENDENCIES
- parallel_tests
+ PLATFORMS
+ arm64-darwin-21
- BUNDLED WITH
- #{Bundler::VERSION}
- L
+ DEPENDENCIES
+ sorbet (= 0.5.10554)
- bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, :raise_on_error => false
- expect(err).to include("parallel_tests-3.8.0 requires ruby version >= #{next_ruby_minor}")
- expect(err).not_to include("That means the author of parallel_tests (3.8.0) has removed it.")
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "raises a proper error" do
+ simulate_platform "aarch64-linux" do
+ bundle "install", :raise_on_error => false
+ end
+
+ nice_error = strip_whitespace(<<-E).strip
+ Could not find gem 'sorbet-static (= 0.5.10554)' with platforms 'arm64-darwin-21', 'aarch64-linux' in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally.
+
+ The source contains the following gems matching 'sorbet-static (= 0.5.10554)':
+ * sorbet-static-0.5.10554-universal-darwin-21
+ E
+ expect(err).to end_with(nice_error)
+ end
end
it "gives a meaningful error on ruby version mismatches between dependencies" do
build_repo4 do
build_gem "requires-old-ruby" do |s|
- s.required_ruby_version = "< #{RUBY_VERSION}"
+ s.required_ruby_version = "< #{Gem.ruby_version}"
end
end
build_lib("foo", :path => bundled_app) do |s|
- s.required_ruby_version = ">= #{RUBY_VERSION}"
+ s.required_ruby_version = ">= #{Gem.ruby_version}"
s.add_dependency "requires-old-ruby"
end
@@ -296,7 +443,16 @@ RSpec.describe "bundle install with install-time dependencies" do
gemspec
G
- expect(err).to include("Bundler found conflicting requirements for the Ruby\0 version:")
+ expect(err).to end_with <<~E.strip
+ Could not find compatible versions
+
+ Because every version of foo depends on requires-old-ruby >= 0
+ and every version of requires-old-ruby depends on Ruby < #{Gem.ruby_version},
+ every version of foo requires Ruby < #{Gem.ruby_version}.
+ So, because Gemfile depends on foo >= 0
+ and current Ruby version is = #{Gem.ruby_version},
+ version solving has failed.
+ E
end
it "installs the older version under rate limiting conditions" do
@@ -309,13 +465,13 @@ RSpec.describe "bundle install with install-time dependencies" do
end
install_gemfile <<-G, :artifice => "compact_index_rate_limited", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
- ruby "#{RUBY_VERSION}"
+ ruby "#{Gem.ruby_version}"
source "http://localgemserver.test/"
gem 'rack'
gem 'foo1'
G
- expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
+ expect(err).to_not include("rack-9001.0.0 requires ruby version > 9000")
expect(the_bundle).to include_gems("rack 1.2")
end
@@ -325,22 +481,22 @@ RSpec.describe "bundle install with install-time dependencies" do
s.required_ruby_version = "> 9000"
end
build_gem "rack", "1.2" do |s|
- s.platform = mingw
+ s.platform = x86_mingw32
s.required_ruby_version = "> 9000"
end
build_gem "rack", "1.2"
end
- simulate_platform mingw do
+ simulate_platform x86_mingw32 do
install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
- ruby "#{RUBY_VERSION}"
+ ruby "#{Gem.ruby_version}"
source "http://localgemserver.test/"
gem 'rack'
G
end
- expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
- expect(out).to_not include("rack-1.2-#{Bundler.local_platform} requires ruby version > 9000")
+ expect(err).to_not include("rack-9001.0.0 requires ruby version > 9000")
+ expect(err).to_not include("rack-1.2-#{Bundler.local_platform} requires ruby version > 9000")
expect(the_bundle).to include_gems("rack 1.2")
end
end
@@ -354,8 +510,8 @@ RSpec.describe "bundle install with install-time dependencies" do
end
end
- let(:ruby_requirement) { %("#{RUBY_VERSION}") }
- let(:error_message_requirement) { "= #{RUBY_VERSION}" }
+ let(:ruby_requirement) { %("#{Gem.ruby_version}") }
+ let(:error_message_requirement) { "= #{Gem.ruby_version}" }
it "raises a proper error that mentions the current Ruby version during resolution" do
install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, :raise_on_error => false
@@ -366,14 +522,13 @@ RSpec.describe "bundle install with install-time dependencies" do
expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
nice_error = strip_whitespace(<<-E).strip
- Bundler found conflicting requirements for the Ruby\0 version:
- In Gemfile:
- require_ruby was resolved to 1.0, which depends on
- Ruby\0 (> 9000)
-
- Current Ruby\0 version:
- Ruby\0 (#{error_message_requirement})
+ Could not find compatible versions
+ Because every version of require_ruby depends on Ruby > 9000
+ and Gemfile depends on require_ruby >= 0,
+ Ruby > 9000 is required.
+ So, because current Ruby version is #{error_message_requirement},
+ version solving has failed.
E
expect(err).to end_with(nice_error)
end
@@ -389,14 +544,13 @@ RSpec.describe "bundle install with install-time dependencies" do
expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
nice_error = strip_whitespace(<<-E).strip
- Bundler found conflicting requirements for the Ruby\0 version:
- In Gemfile:
- require_ruby was resolved to 1.0, which depends on
- Ruby\0 (> 9000)
-
- Current Ruby\0 version:
- Ruby\0 (#{error_message_requirement})
+ Could not find compatible versions
+ Because every version of require_ruby depends on Ruby > 9000
+ and Gemfile depends on require_ruby >= 0,
+ Ruby > 9000 is required.
+ So, because current Ruby version is #{error_message_requirement},
+ version solving has failed.
E
expect(err).to end_with(nice_error)
end
@@ -434,14 +588,11 @@ RSpec.describe "bundle install with install-time dependencies" do
expect(err).to_not include("Gem::InstallError: require_rubygems requires RubyGems version > 9000")
nice_error = strip_whitespace(<<-E).strip
- Bundler found conflicting requirements for the RubyGems\0 version:
- In Gemfile:
- require_rubygems was resolved to 1.0, which depends on
- RubyGems\0 (> 9000)
-
- Current RubyGems\0 version:
- RubyGems\0 (= #{Gem::VERSION})
-
+ Because every version of require_rubygems depends on RubyGems > 9000
+ and Gemfile depends on require_rubygems >= 0,
+ RubyGems > 9000 is required.
+ So, because current RubyGems version is = #{Gem::VERSION},
+ version solving has failed.
E
expect(err).to end_with(nice_error)
end
diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb
index a5302877c1..4d08752256 100644
--- a/spec/bundler/install/gems/standalone_spec.rb
+++ b/spec/bundler/install/gems/standalone_spec.rb
@@ -128,11 +128,6 @@ RSpec.shared_examples "bundle install --standalone" do
skip "does not work on rubygems versions where `--install_dir` doesn't respect --default" unless Gem::Installer.for_spec(loaded_gemspec, :install_dir => "/foo").default_spec_file == "/foo/specifications/default/bundler-#{Bundler::VERSION}.gemspec" # Since rubygems 3.2.0.rc.2
skip "does not work on old rubies because the realworld gems that need to be installed don't support them" if RUBY_VERSION < "2.7.0"
- if Gem.win_platform? && RUBY_VERSION < "3.1.0"
- default_fiddle_version = ruby "require 'fiddle'; puts Gem.loaded_specs['fiddle'].version"
- realworld_system_gems "fiddle --version #{default_fiddle_version}"
- end
-
realworld_system_gems "tsort --version 0.1.0"
necessary_system_gems = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.2.0", "stringio --version 3.0.1"]
@@ -162,7 +157,7 @@ RSpec.shared_examples "bundle install --standalone" do
bundle "lock", :dir => cwd, :artifice => "compact_index"
end
- it "works and points to the vendored copies, not to the default copies" do
+ it "works and points to the vendored copies, not to the default copies", :realworld do
bundle "config set --local path #{bundled_app("bundle")}"
bundle :install, :standalone => true, :dir => cwd, :artifice => "compact_index", :env => { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
@@ -175,7 +170,7 @@ RSpec.shared_examples "bundle install --standalone" do
end
end
- describe "with Gemfiles using path sources and resulting bundle moved to a folder hierarchy with different nesting" do
+ describe "with Gemfiles using absolute path sources and resulting bundle moved to a folder hierarchy with different nesting" do
before do
build_lib "minitest", "1.0.0", :path => lib_path("minitest")
@@ -205,6 +200,35 @@ RSpec.shared_examples "bundle install --standalone" do
end
end
+ describe "with Gemfiles using relative path sources and app moved to a different root" do
+ before do
+ FileUtils.mkdir_p bundled_app("app/vendor")
+
+ build_lib "minitest", "1.0.0", :path => bundled_app("app/vendor/minitest")
+
+ gemfile bundled_app("app/Gemfile"), <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "minitest", :path => "vendor/minitest"
+ G
+
+ bundle "install", :standalone => true, :dir => bundled_app("app")
+
+ FileUtils.mv(bundled_app("app"), bundled_app2("app"))
+ end
+
+ it "also works" do
+ ruby <<-RUBY, :dir => bundled_app2("app")
+ require "./bundle/bundler/setup"
+
+ require "minitest"
+ puts MINITEST
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err).to be_empty
+ end
+ end
+
describe "with gems with native extension" do
before do
bundle "config set --local path #{bundled_app("bundle")}"
@@ -461,3 +485,31 @@ RSpec.describe "bundle install --standalone run in a subdirectory" do
include_examples("bundle install --standalone")
end
+
+RSpec.describe "bundle install --standalone --local" do
+ before do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ G
+
+ system_gems "rack-1.0.0", :path => default_bundle_path
+ end
+
+ it "generates script pointing to system gems" do
+ bundle "install --standalone --local --verbose"
+
+ expect(out).to include("Using rack 1.0.0")
+
+ load_error_ruby <<-RUBY, "spec"
+ require "./bundler/setup"
+
+ require "rack"
+ puts RACK
+ require "spec"
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err).to eq("ZOMG LOAD ERROR")
+ end
+end
diff --git a/spec/bundler/install/gems/sudo_spec.rb b/spec/bundler/install/gems/sudo_spec.rb
deleted file mode 100644
index 41b241da25..0000000000
--- a/spec/bundler/install/gems/sudo_spec.rb
+++ /dev/null
@@ -1,205 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "when using sudo", :sudo => true do
- describe "and BUNDLE_PATH is writable" do
- context "but BUNDLE_PATH/build_info is not writable" do
- let(:subdir) do
- system_gem_path("cache")
- end
-
- before do
- bundle "config set path.system true"
- subdir.mkpath
- sudo "chmod u-w #{subdir}"
- end
-
- after do
- sudo "chmod u+w #{subdir}"
- end
-
- it "installs" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
-
- expect(out).to_not match(/an error occurred/i)
- expect(system_gem_path("cache/rack-1.0.0.gem")).to exist
- expect(the_bundle).to include_gems "rack 1.0"
- end
- end
- end
-
- describe "and GEM_HOME is owned by root" do
- before :each do
- bundle "config set path.system true"
- chown_system_gems_to_root
- end
-
- it "installs" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- gem "thin"
- G
-
- expect(system_gem_path("gems/rack-1.0.0")).to exist
- expect(system_gem_path("gems/rack-1.0.0").stat.uid).to eq(0)
- expect(the_bundle).to include_gems "rack 1.0"
- end
-
- it "installs rake and a gem dependent on rake in the same session" do
- build_repo2 do
- build_gem "another_implicit_rake_dep" do |s|
- s.extensions << "Rakefile"
- s.write "Rakefile", <<-RUBY
- task :default do
- path = File.expand_path("lib", __dir__)
- FileUtils.mkdir_p(path)
- File.open("\#{path}/another_implicit_rake_dep.rb", "w") do |f|
- f.puts "ANOTHER_IMPLICIT_RAKE_DEP = 'YES'"
- end
- end
- RUBY
- end
- end
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rake"
- gem "another_implicit_rake_dep"
- G
- bundle "install"
- expect(system_gem_path("gems/another_implicit_rake_dep-1.0")).to exist
- end
-
- it "installs when BUNDLE_PATH is owned by root" do
- bundle_path = tmp("owned_by_root")
- FileUtils.mkdir_p bundle_path
- sudo "chown -R root #{bundle_path}"
-
- ENV["BUNDLE_PATH"] = bundle_path.to_s
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- G
-
- expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0")).to exist
- expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0").stat.uid).to eq(0)
- expect(the_bundle).to include_gems "rack 1.0"
- end
-
- it "installs when BUNDLE_PATH does not exist" do
- root_path = tmp("owned_by_root")
- FileUtils.mkdir_p root_path
- sudo "chown -R root #{root_path}"
- bundle_path = root_path.join("does_not_exist")
-
- ENV["BUNDLE_PATH"] = bundle_path.to_s
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- G
-
- expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0")).to exist
- expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0").stat.uid).to eq(0)
- expect(the_bundle).to include_gems "rack 1.0"
- end
-
- it "installs extensions/" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "very_simple_binary"
- G
-
- expect(system_gem_path("gems/very_simple_binary-1.0")).to exist
- binary_glob = system_gem_path("extensions/*/*/very_simple_binary-1.0")
- expect(Dir.glob(binary_glob).first).to be
- end
- end
-
- describe "and BUNDLE_PATH is not writable" do
- before do
- bundle "config set --local path .bundle"
- sudo "chmod ugo-w .bundle"
- end
-
- after do
- sudo "chmod ugo+w .bundle"
- end
-
- it "installs" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- G
-
- expect(local_gem_path("gems/rack-1.0.0")).to exist
- expect(the_bundle).to include_gems "rack 1.0"
- end
-
- it "cleans up the tmpdirs generated" do
- require "tmpdir"
- Dir.glob("#{Dir.tmpdir}/bundler*").each do |tmpdir|
- FileUtils.remove_entry_secure(tmpdir)
- end
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
- tmpdirs = Dir.glob("#{Dir.tmpdir}/bundler*")
-
- expect(tmpdirs).to be_empty
- end
- end
-
- describe "and GEM_HOME is not writable" do
- it "installs" do
- bundle "config set path.system true"
- gem_home = tmp("sudo_gem_home")
- sudo "mkdir -p #{gem_home}"
- sudo "chmod ugo-w #{gem_home}"
-
- system_gems :bundler, :path => gem_home
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- G
-
- bundle :install, :env => { "GEM_HOME" => gem_home.to_s, "GEM_PATH" => nil }
- expect(gem_home.join("bin/rackup")).to exist
- expect(the_bundle).to include_gems "rack 1.0", :env => { "GEM_HOME" => gem_home.to_s, "GEM_PATH" => nil }
-
- sudo "rm -rf #{tmp("sudo_gem_home")}"
- end
- end
-
- describe "and root runs install" do
- let(:warning) { "Don't run Bundler as root." }
-
- before do
- gemfile %(source "#{file_uri_for(gem_repo1)}")
- end
-
- it "warns against that" do
- bundle :install, :sudo => :preserve_env
- expect(err).to include(warning)
- end
-
- context "when ENV['BUNDLE_SILENCE_ROOT_WARNING'] is set" do
- it "skips the warning" do
- bundle :install, :sudo => :preserve_env, :env => { "BUNDLE_SILENCE_ROOT_WARNING" => "true" }
- expect(err).to_not include(warning)
- end
- end
-
- context "when silence_root_warning = false" do
- it "warns against that" do
- bundle :install, :sudo => :preserve_env, :env => { "BUNDLE_SILENCE_ROOT_WARNING" => "false" }
- expect(err).to include(warning)
- end
- end
- end
-end
diff --git a/spec/bundler/install/gemspecs_spec.rb b/spec/bundler/install/gemspecs_spec.rb
index 3684d8749d..7b58ea9839 100644
--- a/spec/bundler/install/gemspecs_spec.rb
+++ b/spec/bundler/install/gemspecs_spec.rb
@@ -94,7 +94,8 @@ RSpec.describe "bundle install" do
end
context "when ruby version is specified in gemspec and gemfile" do
- it "installs when patch level is not specified and the version matches" do
+ it "installs when patch level is not specified and the version matches",
+ :if => RUBY_PATCHLEVEL >= 0 do
build_lib("foo", :path => bundled_app) do |s|
s.required_ruby_version = "~> #{RUBY_VERSION}.0"
end
diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb
index d43aacee7e..882f2a2d42 100644
--- a/spec/bundler/install/git_spec.rb
+++ b/spec/bundler/install/git_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe "bundle install" do
gem "foo", :git => "#{file_uri_for(lib_path("foo"))}"
G
- expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at master@#{revision_for(lib_path("foo"))[0..6]})")
+ expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main@#{revision_for(lib_path("foo"))[0..6]})")
expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
end
@@ -37,16 +37,16 @@ RSpec.describe "bundle install" do
install_gemfile <<-G, :verbose => true
source "#{file_uri_for(gem_repo1)}"
- gem "foo", :git => "#{file_uri_for(lib_path("foo"))}", :ref => "master~2"
+ gem "foo", :git => "#{file_uri_for(lib_path("foo"))}", :ref => "main~2"
G
- expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at master~2@#{rev})")
+ expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main~2@#{rev})")
expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
update_git "foo", "4.0", :path => lib_path("foo"), :gemspec => true
bundle :update, :all => true, :verbose => true
- expect(out).to include("Using foo 2.0 (was 1.0) from #{file_uri_for(lib_path("foo"))} (at master~2@#{rev2})")
+ expect(out).to include("Using foo 2.0 (was 1.0) from #{file_uri_for(lib_path("foo"))} (at main~2@#{rev2})")
expect(the_bundle).to include_gems "foo 2.0", :source => "git@#{lib_path("foo")}"
end
@@ -98,5 +98,77 @@ RSpec.describe "bundle install" do
bundle "info zebra"
expect(out).to include("* zebra (2.0 #{revision_for(lib_path("gems"))[0..6]})")
end
+
+ it "should always sort dependencies in the same order" do
+ # This Gemfile + lockfile had a problem where the first
+ # `bundle install` would change the order, but the second would
+ # change it back.
+
+ # NOTE: both gems MUST have the same path! It has to be two gems in one repo.
+
+ test = build_git "test", "1.0.0", :path => lib_path("test-and-other")
+ other = build_git "other", "1.0.0", :path => lib_path("test-and-other")
+ test_ref = test.ref_for("HEAD")
+ other_ref = other.ref_for("HEAD")
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+
+ gem "test", git: #{test.path.to_s.inspect}
+ gem "other", ref: #{other_ref.inspect}, git: #{other.path.to_s.inspect}
+ G
+
+ lockfile <<-L
+ GIT
+ remote: #{test.path}
+ revision: #{test_ref}
+ specs:
+ test (1.0.0)
+
+ GIT
+ remote: #{other.path}
+ revision: #{other_ref}
+ ref: #{other_ref}
+ specs:
+ other (1.0.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ other!
+ test!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ # If GH#6743 is present, the first `bundle install` will change the
+ # lockfile, by flipping the order (`other` would be moved to the top).
+ #
+ # The second `bundle install` would then change the lockfile back
+ # to the original.
+ #
+ # The fix makes it so it may change it once, but it will not change
+ # it a second time.
+ #
+ # So, we run `bundle install` once, and store the value of the
+ # modified lockfile.
+ bundle :install
+ modified_lockfile = lockfile
+
+ # If GH#6743 is present, the second `bundle install` would change the
+ # lockfile back to what it was originally.
+ #
+ # This `expect` makes sure it doesn't change a second time.
+ bundle :install
+ expect(lockfile).to eq(modified_lockfile)
+
+ expect(out).to include("Bundle complete!")
+ end
end
end
diff --git a/spec/bundler/install/global_cache_spec.rb b/spec/bundler/install/global_cache_spec.rb
index 3021d6ae17..be34d8b5bc 100644
--- a/spec/bundler/install/global_cache_spec.rb
+++ b/spec/bundler/install/global_cache_spec.rb
@@ -220,9 +220,9 @@ RSpec.describe "global gem caching" do
gem "very_simple_path_binary", :path => "#{lib_path("very_simple_path_binary-1.0")}"
G
- gem_binary_cache = home(".bundle", "cache", "extensions", specific_local_platform.to_s, Bundler.ruby_scope,
+ gem_binary_cache = home(".bundle", "cache", "extensions", local_platform.to_s, Bundler.ruby_scope,
Digest(:MD5).hexdigest("#{gem_repo1}/"), "very_simple_binary-1.0")
- git_binary_cache = home(".bundle", "cache", "extensions", specific_local_platform.to_s, Bundler.ruby_scope,
+ git_binary_cache = home(".bundle", "cache", "extensions", local_platform.to_s, Bundler.ruby_scope,
"very_simple_git_binary-1.0-#{revision}", "very_simple_git_binary-1.0")
cached_extensions = Pathname.glob(home(".bundle", "cache", "extensions", "*", "*", "*", "*", "*")).sort
diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb
index 3acae85afd..dc054b50bb 100644
--- a/spec/bundler/install/yanked_spec.rb
+++ b/spec/bundler/install/yanked_spec.rb
@@ -15,7 +15,7 @@ RSpec.context "when installing a bundle that includes yanked gems" do
foo (10.0.0)
PLATFORMS
- ruby
+ #{lockfile_platforms}
DEPENDENCIES
foo (= 10.0.0)
@@ -30,6 +30,72 @@ RSpec.context "when installing a bundle that includes yanked gems" do
expect(err).to include("Your bundle is locked to foo (10.0.0)")
end
+ context "when a re-resolve is necessary, and a yanked version is considered by the resolver" do
+ before do
+ skip "Materialization on Windows is not yet strict, so the example does not detect the gem has been yanked" if Gem.win_platform?
+
+ build_repo4 do
+ build_gem "foo", "1.0.0", "1.0.1"
+ build_gem "actiontext", "6.1.7" do |s|
+ s.add_dependency "nokogiri", ">= 1.8"
+ end
+ build_gem "actiontext", "6.1.6" do |s|
+ s.add_dependency "nokogiri", ">= 1.8"
+ end
+ build_gem "actiontext", "6.1.7" do |s|
+ s.add_dependency "nokogiri", ">= 1.8"
+ end
+ build_gem "nokogiri", "1.13.8"
+ end
+
+ gemfile <<~G
+ source "#{source_uri}"
+ gem "foo", "1.0.1"
+ gem "actiontext", "6.1.6"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{source_uri}/
+ specs:
+ actiontext (6.1.6)
+ nokogiri (>= 1.8)
+ foo (1.0.0)
+ nokogiri (1.13.8-#{Bundler.local_platform})
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ actiontext (= 6.1.6)
+ foo (= 1.0.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ context "and the old index is used" do
+ let(:source_uri) { file_uri_for(gem_repo4) }
+
+ it "reports the yanked gem properly" do
+ bundle "install", :raise_on_error => false
+
+ expect(err).to include("Your bundle is locked to nokogiri (1.13.8-#{Bundler.local_platform})")
+ end
+ end
+
+ context "and the compact index API is used" do
+ let(:source_uri) { "https://gem.repo4" }
+
+ it "reports the yanked gem properly" do
+ bundle "install", :artifice => "compact_index", :raise_on_error => false
+
+ expect(err).to include("Your bundle is locked to nokogiri (1.13.8-#{Bundler.local_platform})")
+ end
+ end
+ end
+
it "throws the original error when only the Gemfile specifies a gem version that doesn't exist" do
bundle "config set force_ruby_platform true"
@@ -43,6 +109,63 @@ RSpec.context "when installing a bundle that includes yanked gems" do
end
end
+RSpec.context "when resolving a bundle that includes yanked gems, but unlocking an unrelated gem" do
+ before(:each) do
+ build_repo4 do
+ build_gem "foo", "10.0.0"
+
+ build_gem "bar", "1.0.0"
+ build_gem "bar", "2.0.0"
+ end
+
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}
+ specs:
+ foo (9.0.0)
+ bar (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ bar
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "foo"
+ gem "bar"
+ G
+ end
+
+ it "does not update the yanked gem" do
+ bundle "lock --update bar"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ bar (2.0.0)
+ foo (9.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ bar
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+end
+
RSpec.context "when using gem before installing" do
it "does not suggest the author has yanked the gem" do
gemfile <<-G
@@ -57,7 +180,7 @@ RSpec.context "when using gem before installing" do
rack (0.9.1)
PLATFORMS
- ruby
+ #{lockfile_platforms}
DEPENDENCIES
rack (= 0.9.1)
@@ -69,6 +192,12 @@ RSpec.context "when using gem before installing" do
expect(err).to_not include("Your bundle is locked to rack (0.9.1) from")
expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.")
expect(err).to_not include("You'll need to update your bundle to a different version of rack (0.9.1) that hasn't been removed in order to install.")
+
+ # Check error message is still correct when multiple platforms are locked
+ lockfile lockfile.gsub(/PLATFORMS\n #{lockfile_platforms}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}")
+
+ bundle :list, :raise_on_error => false
+ expect(err).to include("Could not find rack-0.9.1 in locally installed gems")
end
it "does not suggest the author has yanked the gem when using more than one gem, but shows all gems that couldn't be found in the source" do
@@ -86,7 +215,7 @@ RSpec.context "when using gem before installing" do
rack_middleware (1.0)
PLATFORMS
- ruby
+ #{lockfile_platforms}
DEPENDENCIES
rack (= 0.9.1)
diff --git a/spec/bundler/lock/git_spec.rb b/spec/bundler/lock/git_spec.rb
index 56db5d8305..ac3d10223c 100644
--- a/spec/bundler/lock/git_spec.rb
+++ b/spec/bundler/lock/git_spec.rb
@@ -14,6 +14,52 @@ RSpec.describe "bundle lock with git gems" do
expect(the_bundle).to include_gems "foo 1.0.0"
end
+ it "doesn't print errors even if running lock after removing the cache" do
+ FileUtils.rm_rf(Dir[default_cache_path("git/foo-1.0-*")].first)
+
+ bundle "lock --verbose"
+
+ expect(err).to be_empty
+ end
+
+ it "prints a proper error when changing a locked Gemfile to point to a bad branch" do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}", :branch => "bad"
+ G
+
+ bundle "lock --update foo", :env => { "LANG" => "en" }, :raise_on_error => false
+
+ expect(err).to include("Revision bad does not exist in the repository")
+ end
+
+ it "prints a proper error when installing a Gemfile with a locked ref that does not exist" do
+ lockfile <<~L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{"a" * 40}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install", :raise_on_error => false
+
+ expect(err).to include("Revision #{"a" * 40} does not exist in the repository")
+ end
+
it "locks a git source to the current ref" do
update_git "foo"
bundle :install
@@ -26,6 +72,87 @@ RSpec.describe "bundle lock with git gems" do
expect(out).to eq("WIN")
end
+ it "properly clones a git source locked to an out of date ref" do
+ update_git "foo"
+
+ bundle :install, :env => { "BUNDLE_PATH" => "foo" }
+ expect(err).to be_empty
+ end
+
+ it "properly fetches a git source locked to an unreachable ref" do
+ # Create a commit and make it unreachable
+ git "checkout -b foo ", lib_path("foo-1.0")
+ unreachable_sha = update_git("foo").ref_for("HEAD")
+ git "checkout main ", lib_path("foo-1.0")
+ git "branch -D foo ", lib_path("foo-1.0")
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile <<-L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{unreachable_sha}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(err).to be_empty
+ end
+
+ it "properly fetches a git source locked to an annotated tag" do
+ # Create an annotated tag
+ git("tag -a v1.0 -m 'Annotated v1.0'", lib_path("foo-1.0"))
+ annotated_tag = git("rev-parse v1.0", lib_path("foo-1.0"))
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile <<-L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{annotated_tag}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(err).to be_empty
+ end
+
it "provides correct #full_gem_path" do
run <<-RUBY
puts Bundler.rubygems.find_name('foo').first.full_gem_path
diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb
index ea9893fb0e..ccf23a9e3c 100644
--- a/spec/bundler/lock/lockfile_spec.rb
+++ b/spec/bundler/lock/lockfile_spec.rb
@@ -386,7 +386,7 @@ RSpec.describe "the lockfile format" do
expect(lockfile).to eq <<~G
GIT
remote: #{lib_path("foo-1.0")}
- revision: #{git.ref_for("master")}
+ revision: #{git.ref_for("main")}
specs:
foo (1.0)
@@ -457,7 +457,7 @@ RSpec.describe "the lockfile format" do
expect(lockfile).to eq <<~G
GIT
remote: #{lib_path("foo-1.0")}
- revision: #{git.ref_for("master")}
+ revision: #{git.ref_for("main")}
specs:
foo (1.0)
@@ -540,6 +540,92 @@ RSpec.describe "the lockfile format" do
G
end
+ it "is conservative with dependencies of git gems" do
+ build_repo4 do
+ build_gem "orm_adapter", "0.4.1"
+ build_gem "orm_adapter", "0.5.0"
+ end
+
+ FileUtils.mkdir_p lib_path("ckeditor/lib")
+
+ @remote = build_git("ckeditor_remote", :bare => true)
+
+ build_git "ckeditor", :path => lib_path("ckeditor") do |s|
+ s.write "lib/ckeditor.rb", "CKEDITOR = '4.0.7'"
+ s.version = "4.0.7"
+ s.add_dependency "orm_adapter"
+ end
+
+ update_git "ckeditor", :path => lib_path("ckeditor"), :remote => file_uri_for(@remote.path)
+ update_git "ckeditor", :path => lib_path("ckeditor"), :tag => "v4.0.7"
+ old_git = update_git "ckeditor", :path => lib_path("ckeditor"), :push => "v4.0.7"
+
+ update_git "ckeditor", :path => lib_path("ckeditor"), :gemspec => true do |s|
+ s.write "lib/ckeditor.rb", "CKEDITOR = '4.0.8'"
+ s.version = "4.0.8"
+ s.add_dependency "orm_adapter"
+ end
+ update_git "ckeditor", :path => lib_path("ckeditor"), :tag => "v4.0.8"
+
+ new_git = update_git "ckeditor", :path => lib_path("ckeditor"), :push => "v4.0.8"
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "ckeditor", :git => "#{@remote.path}", :tag => "v4.0.8"
+ G
+
+ lockfile <<~L
+ GIT
+ remote: #{@remote.path}
+ revision: #{old_git.ref_for("v4.0.7")}
+ tag: v4.0.7
+ specs:
+ ckeditor (4.0.7)
+ orm_adapter
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ orm_adapter (0.4.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ckeditor!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ # Bumps the git gem, but keeps its dependency locked
+ expect(lockfile).to eq <<~L
+ GIT
+ remote: #{@remote.path}
+ revision: #{new_git.ref_for("v4.0.8")}
+ tag: v4.0.8
+ specs:
+ ckeditor (4.0.8)
+ orm_adapter
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ orm_adapter (0.4.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ckeditor!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
it "serializes pinned path sources to the lockfile" do
build_lib "foo"
@@ -617,7 +703,7 @@ RSpec.describe "the lockfile format" do
expect(lockfile).to eq <<~G
GIT
remote: #{lib_path("bar-1.0")}
- revision: #{bar.ref_for("master")}
+ revision: #{bar.ref_for("main")}
specs:
bar (1.0)
@@ -982,7 +1068,7 @@ RSpec.describe "the lockfile format" do
rack (1.0.0)
PLATFORMS
- #{lockfile_platforms_for(["java", specific_local_platform])}
+ #{lockfile_platforms("java")}
DEPENDENCIES
rack
@@ -1174,7 +1260,7 @@ RSpec.describe "the lockfile format" do
it "captures the Ruby version in the lockfile" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}/"
- ruby '#{RUBY_VERSION}'
+ ruby '#{Gem.ruby_version}'
gem "rack", "> 0.9", "< 1.0"
G
@@ -1191,7 +1277,7 @@ RSpec.describe "the lockfile format" do
rack (> 0.9, < 1.0)
RUBY VERSION
- ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
+ #{Bundler::RubyVersion.system}
BUNDLED WITH
#{Bundler::VERSION}
@@ -1218,7 +1304,178 @@ RSpec.describe "the lockfile format" do
G
expect(err).to include("Downloading rack_middleware-1.0 revealed dependencies not in the API or the lockfile (#{Gem::Dependency.new("rack", "= 0.9.1")}).").
- and include("Either installing with `--full-index` or running `bundle update rack_middleware` should fix the problem.")
+ and include("Running `bundle update rack_middleware` should fix the problem.")
+ end
+
+ it "regenerates a lockfile with no specs" do
+ build_repo4 do
+ build_gem "indirect_dependency", "1.2.3" do |s|
+ s.metadata["funding_uri"] = "https://example.com/donate"
+ end
+
+ build_gem "direct_dependency", "4.5.6" do |s|
+ s.add_dependency "indirect_dependency", ">= 0"
+ end
+ end
+
+ lockfile <<-G
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ direct_dependency
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "direct_dependency"
+ G
+
+ expect(lockfile).to eq <<~G
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ direct_dependency (4.5.6)
+ indirect_dependency
+ indirect_dependency (1.2.3)
+
+ PLATFORMS
+ #{formatted_lockfile_platforms(*["ruby", generic_local_platform].uniq)}
+
+ DEPENDENCIES
+ direct_dependency
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ shared_examples_for "a lockfile missing dependent specs" do
+ it "auto-heals" do
+ build_repo4 do
+ build_gem "minitest-bisect", "1.6.0" do |s|
+ s.add_dependency "path_expander", "~> 1.1"
+ end
+
+ build_gem "path_expander", "1.1.1"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "minitest-bisect"
+ G
+
+ # Corrupt lockfile (completely missing path_expander)
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ minitest-bisect (1.6.0)
+
+ PLATFORMS
+ #{platforms}
+
+ DEPENDENCIES
+ minitest-bisect
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ cache_gems "minitest-bisect-1.6.0", "path_expander-1.1.1", :gem_repo => gem_repo4
+ bundle :install
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ minitest-bisect (1.6.0)
+ path_expander (~> 1.1)
+ path_expander (1.1.1)
+
+ PLATFORMS
+ #{platforms}
+
+ DEPENDENCIES
+ minitest-bisect
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with just specific platform" do
+ let(:platforms) { lockfile_platforms }
+
+ it_behaves_like "a lockfile missing dependent specs"
+ end
+
+ context "with both ruby and specific platform" do
+ let(:platforms) { lockfile_platforms("ruby") }
+
+ it_behaves_like "a lockfile missing dependent specs"
+ end
+
+ it "auto-heals when the lockfile is missing specs" do
+ build_repo4 do
+ build_gem "minitest-bisect", "1.6.0" do |s|
+ s.add_dependency "path_expander", "~> 1.1"
+ end
+
+ build_gem "path_expander", "1.1.1"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "minitest-bisect"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ minitest-bisect (1.6.0)
+ path_expander (~> 1.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ minitest-bisect
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose"
+ expect(out).to include("re-resolving dependencies because your lock file is missing \"minitest-bisect\"")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ minitest-bisect (1.6.0)
+ path_expander (~> 1.1)
+ path_expander (1.1.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ minitest-bisect
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
describe "a line ending" do
diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb
index e14b5ff003..7b7ceb4586 100644
--- a/spec/bundler/other/major_deprecation_spec.rb
+++ b/spec/bundler/other/major_deprecation_spec.rb
@@ -587,10 +587,9 @@ RSpec.describe "major deprecations" do
pending "fails with a helpful message", :bundler => "3"
end
- context "bundle viz" do
+ context "bundle viz", :realworld do
before do
- graphviz_version = RUBY_VERSION >= "2.4" ? "1.2.5" : "1.2.4"
- realworld_system_gems "ruby-graphviz --version #{graphviz_version}"
+ realworld_system_gems "ruby-graphviz --version 1.2.5"
create_file "gems.rb", "source \"#{file_uri_for(gem_repo1)}\""
bundle "viz"
end
diff --git a/spec/bundler/plugins/install_spec.rb b/spec/bundler/plugins/install_spec.rb
index 009516260a..aec131f2ee 100644
--- a/spec/bundler/plugins/install_spec.rb
+++ b/spec/bundler/plugins/install_spec.rb
@@ -22,6 +22,13 @@ RSpec.describe "bundler plugin install" do
plugin_should_be_installed("foo")
end
+ it "installs from rubygems source in frozen mode" do
+ bundle "plugin install foo --source #{file_uri_for(gem_repo2)}", :env => { "BUNDLE_DEPLOYMENT" => "true" }
+
+ expect(out).to include("Installed plugin foo")
+ plugin_should_be_installed("foo")
+ end
+
it "installs from sources configured as Gem.sources without any flags" do
bundle "plugin install foo", :env => { "BUNDLER_SPEC_GEM_SOURCES" => file_uri_for(gem_repo2).to_s }
@@ -32,7 +39,8 @@ RSpec.describe "bundler plugin install" do
it "shows help when --help flag is given" do
bundle "plugin install --help"
- expect(out).to include("bundle plugin install PLUGINS # Install the plugin from the source")
+ # The help message defined in ../../lib/bundler/man/bundle-plugin.1.ronn will be output.
+ expect(out).to include("You can install, uninstall, and list plugin(s)")
end
context "plugin is already installed" do
@@ -84,6 +92,26 @@ RSpec.describe "bundler plugin install" do
expect(out).to include("Using foo 1.1")
end
+ it "installs when --branch specified" do
+ bundle "plugin install foo --branch main --source #{file_uri_for(gem_repo2)}"
+
+ expect(out).to include("Installed plugin foo")
+ end
+
+ it "installs when --ref specified" do
+ bundle "plugin install foo --ref v1.2.3 --source #{file_uri_for(gem_repo2)}"
+
+ expect(out).to include("Installed plugin foo")
+ end
+
+ it "raises error when both --branch and --ref options are specified" do
+ bundle "plugin install foo --source #{file_uri_for(gem_repo2)} --branch main --ref v1.2.3", :raise_on_error => false
+
+ expect(out).not_to include("Installed plugin foo")
+
+ expect(err).to include("You cannot specify `--branch` and `--ref` at the same time.")
+ end
+
it "works with different load paths" do
build_repo2 do
build_plugin "testing" do |s|
diff --git a/spec/bundler/plugins/source/example_spec.rb b/spec/bundler/plugins/source/example_spec.rb
index 7d098997ec..9d153b6063 100644
--- a/spec/bundler/plugins/source/example_spec.rb
+++ b/spec/bundler/plugins/source/example_spec.rb
@@ -203,7 +203,7 @@ RSpec.describe "real source plugins" do
def initialize(opts)
super
- @ref = options["ref"] || options["branch"] || options["tag"] || "master"
+ @ref = options["ref"] || options["branch"] || options["tag"] || "main"
@unlocked = false
end
@@ -247,7 +247,7 @@ RSpec.describe "real source plugins" do
def options_to_lock
opts = {"revision" => revision}
- opts["ref"] = ref if ref != "master"
+ opts["ref"] = ref if ref != "main"
opts
end
@@ -304,13 +304,7 @@ RSpec.describe "real source plugins" do
@install_path ||= begin
git_scope = "\#{base_name}-\#{shortref_for_path(revision)}"
- path = gem_install_dir.join(git_scope)
-
- if !path.exist? && requires_sudo?
- user_bundle_path.join(ruby_scope).join(git_scope)
- else
- path
- end
+ gem_install_dir.join(git_scope)
end
end
@@ -435,7 +429,7 @@ RSpec.describe "real source plugins" do
describe "bundle cache with gitp" do
it "copies repository to vendor cache and uses it" do
git = build_git "foo"
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}" # plugin source
diff --git a/spec/bundler/quality_es_spec.rb b/spec/bundler/quality_es_spec.rb
index 90968e6270..0dbd77e451 100644
--- a/spec/bundler/quality_es_spec.rb
+++ b/spec/bundler/quality_es_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe "La biblioteca si misma" do
included = /ronn/
error_messages = []
man_tracked_files.each do |filename|
- next unless filename =~ included
+ next unless filename&.match?(included)
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
@@ -52,7 +52,7 @@ RSpec.describe "La biblioteca si misma" do
error_messages = []
exempt = /vendor/
lib_tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
diff --git a/spec/bundler/quality_spec.rb b/spec/bundler/quality_spec.rb
index 62f3722a39..a98815158e 100644
--- a/spec/bundler/quality_spec.rb
+++ b/spec/bundler/quality_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe "The library itself" do
failing_lines = []
each_line(filename) do |line, number|
- failing_lines << number + 1 if line =~ merge_conflicts_regex
+ failing_lines << number + 1 if line&.match?(merge_conflicts_regex)
end
return if failing_lines.empty?
@@ -22,7 +22,7 @@ RSpec.describe "The library itself" do
def check_for_tab_characters(filename)
failing_lines = []
each_line(filename) do |line, number|
- failing_lines << number + 1 if line =~ /\t/
+ failing_lines << number + 1 if line.include?("\t")
end
return if failing_lines.empty?
@@ -32,8 +32,8 @@ RSpec.describe "The library itself" do
def check_for_extra_spaces(filename)
failing_lines = []
each_line(filename) do |line, number|
- next if line =~ /^\s+#.*\s+\n$/
- failing_lines << number + 1 if line =~ /\s+\n$/
+ next if /^\s+#.*\s+\n$/.match?(line)
+ failing_lines << number + 1 if /\s+\n$/.match?(line)
end
return if failing_lines.empty?
@@ -45,7 +45,7 @@ RSpec.describe "The library itself" do
failing_lines = []
each_line(filename) do |line, number|
- failing_lines << number + 1 if line =~ /’/
+ failing_lines << number + 1 if /’/.match?(line)
end
return if failing_lines.empty?
@@ -89,7 +89,7 @@ RSpec.describe "The library itself" do
exempt = /\.gitmodules|fixtures|vendor|LICENSE|vcr_cassettes|rbreadline\.diff|index\.txt$/
error_messages = []
tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_tab_characters(filename)
error_messages << check_for_extra_spaces(filename)
end
@@ -100,7 +100,7 @@ RSpec.describe "The library itself" do
exempt = /vendor|vcr_cassettes|LICENSE|rbreadline\.diff/
error_messages = []
tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_straneous_quotes(filename)
end
expect(error_messages.compact).to be_well_formed
@@ -110,7 +110,7 @@ RSpec.describe "The library itself" do
error_messages = []
exempt = %r{lock/lockfile_spec|quality_spec|vcr_cassettes|\.ronn|lockfile_parser\.rb}
tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_git_merge_conflicts(filename)
end
expect(error_messages.compact).to be_well_formed
@@ -120,7 +120,7 @@ RSpec.describe "The library itself" do
included = /ronn/
error_messages = []
man_tracked_files.each do |filename|
- next unless filename =~ included
+ next unless filename&.match?(included)
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
@@ -131,7 +131,7 @@ RSpec.describe "The library itself" do
error_messages = []
exempt = /vendor|vcr_cassettes|CODE_OF_CONDUCT/
lib_tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
@@ -151,7 +151,6 @@ RSpec.describe "The library itself" do
git.allow_insecure
inline
trust-policy
- use_gem_version_promoter_for_major_updates
]
all_settings = Hash.new {|h, k| h[k] = [] }
@@ -198,7 +197,7 @@ RSpec.describe "The library itself" do
gem_list = loaded_gemspec.files
- expect(git_list.sort).to eq(gem_list.sort)
+ expect(git_list).to match_array(gem_list)
end
it "does not contain any warnings" do
@@ -207,7 +206,6 @@ RSpec.describe "The library itself" do
lib/bundler/deployment.rb
lib/bundler/gem_tasks.rb
lib/bundler/vlad.rb
- lib/bundler/templates/gems.rb
]
files_to_require = lib_tracked_files.grep(/\.rb$/) - exclusions
files_to_require.reject! {|f| f.start_with?("lib/bundler/vendor") }
@@ -230,7 +228,7 @@ RSpec.describe "The library itself" do
exempt = %r{templates/|\.5|\.1|vendor/}
all_bad_requires = []
lib_tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
each_line(filename) do |line, number|
line.scan(/^ *require "bundler/).each { all_bad_requires << "#{filename}:#{number.succ}" }
end
diff --git a/spec/bundler/realworld/dependency_api_spec.rb b/spec/bundler/realworld/dependency_api_spec.rb
index 08c6acf190..14f99bd262 100644
--- a/spec/bundler/realworld/dependency_api_spec.rb
+++ b/spec/bundler/realworld/dependency_api_spec.rb
@@ -13,12 +13,12 @@ RSpec.describe "gemcutter's dependency API", :realworld => true do
require_relative "../support/artifice/endpoint_timeout"
@t = Thread.new do
- server = Rack::Server.start(:app => EndpointTimeout,
- :Host => "0.0.0.0",
- :Port => port,
- :server => "webrick",
+ server = Rack::Server.start(:app => EndpointTimeout,
+ :Host => "0.0.0.0",
+ :Port => port,
+ :server => "webrick",
:AccessLog => [],
- :Logger => Spec::SilentLogger.new)
+ :Logger => Spec::SilentLogger.new)
server.start
end
@t.run
diff --git a/spec/bundler/realworld/edgecases_spec.rb b/spec/bundler/realworld/edgecases_spec.rb
index 30c922efb0..2f465b7b25 100644
--- a/spec/bundler/realworld/edgecases_spec.rb
+++ b/spec/bundler/realworld/edgecases_spec.rb
@@ -4,14 +4,15 @@ RSpec.describe "real world edgecases", :realworld => true do
def rubygems_version(name, requirement)
ruby <<-RUBY
require "#{spec_dir}/support/artifice/vcr"
- require "#{entrypoint}"
- require "#{entrypoint}/source/rubygems/remote"
- require "#{entrypoint}/fetcher"
+ require "bundler"
+ require "bundler/source/rubygems/remote"
+ require "bundler/fetcher"
rubygem = Bundler.ui.silence do
source = Bundler::Source::Rubygems::Remote.new(Bundler::URI("https://rubygems.org"))
fetcher = Bundler::Fetcher.new(source)
index = fetcher.specs([#{name.dump}], nil)
- index.search(Gem::Dependency.new(#{name.dump}, #{requirement.dump})).last
+ requirement = Gem::Requirement.create(#{requirement.dump})
+ index.search(#{name.dump}).select {|spec| requirement.satisfied_by?(spec.version) }.last
end
if rubygem.nil?
raise "Could not find #{name} (#{requirement}) on rubygems.org!\n" \
@@ -64,7 +65,7 @@ RSpec.describe "real world edgecases", :realworld => true do
it "is able to update a top-level dependency when there is a conflict on a shared transitive child" do
# from https://github.com/rubygems/bundler/issues/5031
- system_gems "bundler-2.99.0"
+ pristine_system_gems "bundler-1.99.0"
gemfile <<-G
source "https://rubygems.org"
@@ -154,7 +155,7 @@ RSpec.describe "real world edgecases", :realworld => true do
activemodel (= 4.2.7.1)
activerecord (= 4.2.7.1)
activesupport (= 4.2.7.1)
- bundler (>= 1.3.0, < 3.0)
+ bundler (>= 1.3.0, < 2.0)
railties (= 4.2.7.1)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
@@ -191,7 +192,7 @@ RSpec.describe "real world edgecases", :realworld => true do
rails (~> 4.2.7.1)
L
- bundle "lock --update paperclip", :env => { "BUNDLER_VERSION" => "2.99.0" }
+ bundle "lock --update paperclip", :env => { "BUNDLER_VERSION" => "1.99.0" }
expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0"))
end
@@ -218,14 +219,14 @@ RSpec.describe "real world edgecases", :realworld => true do
end
it "doesn't hang on big gemfile" do
- skip "Only for ruby 2.7.3" if RUBY_VERSION != "2.7.3" || RUBY_PLATFORM =~ /darwin/
+ skip "Only for ruby 2.7" unless RUBY_VERSION.start_with?("2.7")
gemfile <<~G
# frozen_string_literal: true
source "https://rubygems.org"
- ruby "2.7.3"
+ ruby "~> 2.7.7"
gem "rails"
gem "pg", ">= 0.18", "< 2.0"
@@ -320,17 +321,17 @@ RSpec.describe "real world edgecases", :realworld => true do
G
if Bundler.feature_flag.bundler_3_mode?
- # Conflicts on bundler version, so fails earlier
+ # Conflicts on bundler version, so we count attempts differently
bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }, :raise_on_error => false
- expect(out).to display_total_steps_of(435)
+ expect(out.split("\n").grep(/backtracking to/).count).to eq(16)
else
bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }
- expect(out).to display_total_steps_of(1025)
+ expect(out).to include("Solution found after 7 attempts")
end
end
it "doesn't hang on tricky gemfile" do
- skip "Only for ruby 2.7.3" if RUBY_VERSION != "2.7.3" || RUBY_PLATFORM =~ /darwin/
+ skip "Only for ruby 2.7" unless RUBY_VERSION.start_with?("2.7")
gemfile <<~G
source 'https://rubygems.org'
@@ -348,190 +349,168 @@ RSpec.describe "real world edgecases", :realworld => true do
bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }
- if Bundler.feature_flag.bundler_3_mode?
- expect(out).to display_total_steps_of(890)
- else
- expect(out).to display_total_steps_of(891)
- end
+ expect(out).to include("Solution found after 6 attempts")
end
it "doesn't hang on nix gemfile" do
- skip "Only for ruby 3.0.1" if RUBY_VERSION != "3.0.1" || RUBY_PLATFORM =~ /darwin/
+ skip "Only for ruby 3.0" unless RUBY_VERSION.start_with?("3.0")
gemfile <<~G
- source "https://rubygems.org" do
- gem "addressable"
- gem "atk"
- gem "awesome_print"
- gem "bacon"
- gem "byebug"
- gem "cairo"
- gem "cairo-gobject"
- gem "camping"
- gem "charlock_holmes"
- gem "cld3"
- gem "cocoapods"
- gem "cocoapods-acknowledgements"
- gem "cocoapods-art"
- gem "cocoapods-bin"
- gem "cocoapods-browser"
- gem "cocoapods-bugsnag"
- gem "cocoapods-check"
- gem "cocoapods-clean"
- gem "cocoapods-clean_build_phases_scripts"
- gem "cocoapods-core"
- gem "cocoapods-coverage"
- gem "cocoapods-deintegrate"
- gem "cocoapods-dependencies"
- gem "cocoapods-deploy"
- gem "cocoapods-downloader"
- gem "cocoapods-expert-difficulty"
- gem "cocoapods-fix-react-native"
- gem "cocoapods-generate"
- gem "cocoapods-git_url_rewriter"
- gem "cocoapods-keys"
- gem "cocoapods-no-dev-schemes"
- gem "cocoapods-open"
- gem "cocoapods-packager"
- gem "cocoapods-playgrounds"
- gem "cocoapods-plugins"
- gem "cocoapods-prune-localizations"
- gem "cocoapods-rome"
- gem "cocoapods-search"
- gem "cocoapods-sorted-search"
- gem "cocoapods-static-swift-framework"
- gem "cocoapods-stats"
- gem "cocoapods-tdfire-binary"
- gem "cocoapods-testing"
- gem "cocoapods-trunk"
- gem "cocoapods-try"
- gem "cocoapods-try-release-fix"
- gem "cocoapods-update-if-you-dare"
- gem "cocoapods-whitelist"
- gem "cocoapods-wholemodule"
- gem "coderay"
- gem "concurrent-ruby"
- gem "curb"
- gem "curses"
- gem "daemons"
- gem "dep-selector-libgecode"
- gem "digest-sha3"
- gem "domain_name"
- gem "do_sqlite3"
- gem "ethon"
- gem "eventmachine"
- gem "excon"
- gem "faraday"
- gem "ffi"
- gem "ffi-rzmq-core"
- gem "fog-dnsimple"
- gem "gdk_pixbuf2"
- gem "gio2"
- gem "gitlab-markup"
- gem "glib2"
- gem "gpgme"
- gem "gtk2"
- gem "hashie"
- gem "highline"
- gem "hike"
- gem "hitimes"
- gem "hpricot"
- gem "httpclient"
- gem "http-cookie"
- gem "iconv"
- gem "idn-ruby"
- gem "jbuilder"
- gem "jekyll"
- gem "jmespath"
- gem "jwt"
- gem "libv8"
- gem "libxml-ruby"
- gem "magic"
- gem "markaby"
- gem "method_source"
- gem "mini_magick"
- gem "msgpack"
- gem "mysql2"
- gem "ncursesw"
- gem "netrc"
- gem "net-scp"
- gem "net-ssh"
- gem "nokogiri"
- gem "opus-ruby"
- gem "ovirt-engine-sdk"
- gem "pango"
- gem "patron"
- gem "pcaprub"
- gem "pg"
- gem "pry"
- gem "pry-byebug"
- gem "pry-doc"
- gem "public_suffix"
- gem "puma"
- gem "rails"
- gem "rainbow"
- gem "rbnacl"
- gem "rb-readline"
- gem "re2"
- gem "redis"
- gem "redis-rack"
- gem "rest-client"
- gem "rmagick"
- gem "rpam2"
- gem "rspec"
- gem "rubocop"
- gem "rubocop-performance"
- gem "ruby-libvirt"
- gem "ruby-lxc"
- gem "ruby-progressbar"
- gem "ruby-terminfo"
- gem "ruby-vips"
- gem "rubyzip"
- gem "rugged"
- gem "sassc"
- gem "scrypt"
- gem "semian"
- gem "sequel"
- gem "sequel_pg"
- gem "simplecov"
- gem "sinatra"
- gem "slop"
- gem "snappy"
- gem "sqlite3"
- gem "taglib-ruby"
- gem "thrift"
- gem "tilt"
- gem "tiny_tds"
- gem "treetop"
- gem "typhoeus"
- gem "tzinfo"
- gem "unf_ext"
- gem "uuid4r"
- gem "whois"
- gem "zookeeper"
- end
+ source "https://rubygems.org"
+
+ gem "addressable"
+ gem "atk"
+ gem "awesome_print"
+ gem "bacon"
+ gem "byebug"
+ gem "cairo"
+ gem "cairo-gobject"
+ gem "camping"
+ gem "charlock_holmes"
+ gem "cld3"
+ gem "cocoapods"
+ gem "cocoapods-acknowledgements"
+ gem "cocoapods-art"
+ gem "cocoapods-bin"
+ gem "cocoapods-browser"
+ gem "cocoapods-bugsnag"
+ gem "cocoapods-check"
+ gem "cocoapods-clean"
+ gem "cocoapods-clean_build_phases_scripts"
+ gem "cocoapods-core"
+ gem "cocoapods-coverage"
+ gem "cocoapods-deintegrate"
+ gem "cocoapods-dependencies"
+ gem "cocoapods-deploy"
+ gem "cocoapods-downloader"
+ gem "cocoapods-expert-difficulty"
+ gem "cocoapods-fix-react-native"
+ gem "cocoapods-generate"
+ gem "cocoapods-git_url_rewriter"
+ gem "cocoapods-keys"
+ gem "cocoapods-no-dev-schemes"
+ gem "cocoapods-open"
+ gem "cocoapods-packager"
+ gem "cocoapods-playgrounds"
+ gem "cocoapods-plugins"
+ gem "cocoapods-prune-localizations"
+ gem "cocoapods-rome"
+ gem "cocoapods-search"
+ gem "cocoapods-sorted-search"
+ gem "cocoapods-static-swift-framework"
+ gem "cocoapods-stats"
+ gem "cocoapods-tdfire-binary"
+ gem "cocoapods-testing"
+ gem "cocoapods-trunk"
+ gem "cocoapods-try"
+ gem "cocoapods-try-release-fix"
+ gem "cocoapods-update-if-you-dare"
+ gem "cocoapods-whitelist"
+ gem "cocoapods-wholemodule"
+ gem "coderay"
+ gem "concurrent-ruby"
+ gem "curb"
+ gem "curses"
+ gem "daemons"
+ gem "dep-selector-libgecode"
+ gem "digest-sha3"
+ gem "domain_name"
+ gem "do_sqlite3"
+ gem "ethon"
+ gem "eventmachine"
+ gem "excon"
+ gem "faraday"
+ gem "ffi"
+ gem "ffi-rzmq-core"
+ gem "fog-dnsimple"
+ gem "gdk_pixbuf2"
+ gem "gio2"
+ gem "gitlab-markup"
+ gem "glib2"
+ gem "gpgme"
+ gem "gtk2"
+ gem "hashie"
+ gem "highline"
+ gem "hike"
+ gem "hitimes"
+ gem "hpricot"
+ gem "httpclient"
+ gem "http-cookie"
+ gem "iconv"
+ gem "idn-ruby"
+ gem "jbuilder"
+ gem "jekyll"
+ gem "jmespath"
+ gem "jwt"
+ gem "libv8"
+ gem "libxml-ruby"
+ gem "magic"
+ gem "markaby"
+ gem "method_source"
+ gem "mini_magick"
+ gem "msgpack"
+ gem "mysql2"
+ gem "ncursesw"
+ gem "netrc"
+ gem "net-scp"
+ gem "net-ssh"
+ gem "nokogiri"
+ gem "opus-ruby"
+ gem "ovirt-engine-sdk"
+ gem "pango"
+ gem "patron"
+ gem "pcaprub"
+ gem "pg"
+ gem "pry"
+ gem "pry-byebug"
+ gem "pry-doc"
+ gem "public_suffix"
+ gem "puma"
+ gem "rails"
+ gem "rainbow"
+ gem "rbnacl"
+ gem "rb-readline"
+ gem "re2"
+ gem "redis"
+ gem "redis-rack"
+ gem "rest-client"
+ gem "rmagick"
+ gem "rpam2"
+ gem "rspec"
+ gem "rubocop"
+ gem "rubocop-performance"
+ gem "ruby-libvirt"
+ gem "ruby-lxc"
+ gem "ruby-progressbar"
+ gem "ruby-terminfo"
+ gem "ruby-vips"
+ gem "rubyzip"
+ gem "rugged"
+ gem "sassc"
+ gem "scrypt"
+ gem "semian"
+ gem "sequel"
+ gem "sequel_pg"
+ gem "simplecov"
+ gem "sinatra"
+ gem "slop"
+ gem "snappy"
+ gem "sqlite3"
+ gem "taglib-ruby"
+ gem "thrift"
+ gem "tilt"
+ gem "tiny_tds"
+ gem "treetop"
+ gem "typhoeus"
+ gem "tzinfo"
+ gem "unf_ext"
+ gem "uuid4r"
+ gem "whois"
+ gem "zookeeper"
G
bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }
- if Bundler.feature_flag.bundler_3_mode?
- expect(out).to display_total_steps_of(1874)
- else
- expect(out).to display_total_steps_of(1922)
- end
- end
-
- private
-
- RSpec::Matchers.define :display_total_steps_of do |expected_steps|
- match do |out|
- out.include?("BUNDLER: Finished resolution (#{expected_steps} steps)")
- end
-
- failure_message do |out|
- actual_steps = out.scan(/BUNDLER: Finished resolution \((\d+) steps\)/).first.first
-
- "Expected resolution to finish in #{expected_steps} steps, but took #{actual_steps}"
- end
+ expect(out).to include("Solution found after 4 attempts")
end
end
diff --git a/spec/bundler/realworld/ffi_spec.rb b/spec/bundler/realworld/ffi_spec.rb
index 083ea38901..fdefc14091 100644
--- a/spec/bundler/realworld/ffi_spec.rb
+++ b/spec/bundler/realworld/ffi_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "loading dinamically linked library on a bundle exec context", :realworld => true do
+RSpec.describe "loading dynamically linked library on a bundle exec context", :realworld => true do
it "passes ENV right after argv in memory" do
create_file "foo.rb", <<~RUBY
require 'ffi'
diff --git a/spec/bundler/realworld/gemfile_source_header_spec.rb b/spec/bundler/realworld/gemfile_source_header_spec.rb
index ada2fc92ee..60c0055a62 100644
--- a/spec/bundler/realworld/gemfile_source_header_spec.rb
+++ b/spec/bundler/realworld/gemfile_source_header_spec.rb
@@ -40,12 +40,12 @@ RSpec.describe "fetching dependencies with a mirrored source", :realworld => tru
require_relative "../support/artifice/endpoint_mirror_source"
@t = Thread.new do
- Rack::Server.start(:app => EndpointMirrorSource,
- :Host => "0.0.0.0",
- :Port => @port,
- :server => "webrick",
+ Rack::Server.start(:app => EndpointMirrorSource,
+ :Host => "0.0.0.0",
+ :Port => @port,
+ :server => "webrick",
:AccessLog => [],
- :Logger => Spec::SilentLogger.new)
+ :Logger => Spec::SilentLogger.new)
end.run
wait_for_server("127.0.0.1", @port)
diff --git a/spec/bundler/realworld/git_spec.rb b/spec/bundler/realworld/git_spec.rb
new file mode 100644
index 0000000000..3d352626ea
--- /dev/null
+++ b/spec/bundler/realworld/git_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+RSpec.describe "github source", :realworld => true do
+ it "properly fetches PRs" do
+ install_gemfile <<-G
+ source "https://rubygems.org"
+
+ gem "reline", github: "https://github.com/ruby/reline/pull/488"
+ G
+ end
+end
diff --git a/spec/bundler/realworld/mirror_probe_spec.rb b/spec/bundler/realworld/mirror_probe_spec.rb
index 241424d4d6..f2ce477c10 100644
--- a/spec/bundler/realworld/mirror_probe_spec.rb
+++ b/spec/bundler/realworld/mirror_probe_spec.rb
@@ -113,12 +113,12 @@ RSpec.describe "fetching dependencies with a not available mirror", :realworld =
require_relative "../support/artifice/endpoint"
@server_thread = Thread.new do
- Rack::Server.start(:app => Endpoint,
- :Host => host,
- :Port => @server_port,
- :server => "webrick",
+ Rack::Server.start(:app => Endpoint,
+ :Host => host,
+ :Port => @server_port,
+ :server => "webrick",
:AccessLog => [],
- :Logger => Spec::SilentLogger.new)
+ :Logger => Spec::SilentLogger.new)
end.run
wait_for_server(host, @server_port)
diff --git a/spec/bundler/realworld/slow_perf_spec.rb b/spec/bundler/realworld/slow_perf_spec.rb
index 9f11821bf5..aa8a48fcc7 100644
--- a/spec/bundler/realworld/slow_perf_spec.rb
+++ b/spec/bundler/realworld/slow_perf_spec.rb
@@ -11,12 +11,23 @@ RSpec.describe "bundle install with complex dependencies", :realworld => true do
gem "mongoid", ">= 0.10.2"
G
- start_time = Time.now
+ expect { bundle "lock" }.to take_less_than(18) # seconds
+ end
+
+ it "resolves quickly (case 2)" do
+ gemfile <<-G
+ source "https://rubygems.org"
- bundle "lock"
+ gem 'metasploit-erd'
+ gem 'rails-erd'
+ gem 'yard'
- duration = Time.now - start_time
+ gem 'coveralls'
+ gem 'rails'
+ gem 'simplecov'
+ gem 'rspec-rails'
+ G
- expect(duration.to_f).to be < 18 # seconds
+ expect { bundle "lock" }.to take_less_than(30) # seconds
end
end
diff --git a/spec/bundler/resolver/basic_spec.rb b/spec/bundler/resolver/basic_spec.rb
index ee62dc3577..f739f8c02b 100644
--- a/spec/bundler/resolver/basic_spec.rb
+++ b/spec/bundler/resolver/basic_spec.rb
@@ -104,7 +104,7 @@ RSpec.describe "Resolving" do
dep "chef_app_error"
expect do
resolve
- end.to raise_error(Bundler::VersionConflict)
+ end.to raise_error(Bundler::SolveFailure)
end
it "raises an exception with the minimal set of conflicting dependencies" do
@@ -118,14 +118,15 @@ RSpec.describe "Resolving" do
dep "c"
expect do
resolve
- end.to raise_error(Bundler::VersionConflict, <<-E.strip)
-Bundler could not find compatible versions for gem "a":
- In Gemfile:
- b was resolved to 1.0, which depends on
- a (>= 2)
-
- c was resolved to 1.0, which depends on
- a (< 1)
+ end.to raise_error(Bundler::SolveFailure, <<~E.strip)
+ Could not find compatible versions
+
+ Because every version of c depends on a < 1
+ and every version of b depends on a >= 2,
+ every version of c is incompatible with b >= 0.
+ So, because Gemfile depends on b >= 0
+ and Gemfile depends on c >= 0,
+ version solving has failed.
E
end
@@ -134,7 +135,7 @@ Bundler could not find compatible versions for gem "a":
dep "circular_app"
expect do
- resolve
+ Bundler::SpecSet.new(resolve).sort
end.to raise_error(Bundler::CyclicDependencyError, /please remove either gem 'bar' or gem 'foo'/i)
end
@@ -174,12 +175,7 @@ Bundler could not find compatible versions for gem "a":
dep "foo"
dep "Ruby\0", "1.8.7"
- deps = []
- @deps.each do |d|
- deps << Bundler::DepProxy.get_proxy(d, "ruby")
- end
-
- should_resolve_and_include %w[foo-1.0.0 bar-1.0.0], [[]]
+ should_resolve_and_include %w[foo-1.0.0 bar-1.0.0]
end
context "conservative" do
@@ -305,4 +301,50 @@ Bundler could not find compatible versions for gem "a":
end
end
end
+
+ it "handles versions that redundantly depend on themselves" do
+ @index = build_index do
+ gem "rack", "3.0.0"
+
+ gem "standalone_migrations", "7.1.0" do
+ dep "rack", "~> 2.0"
+ end
+
+ gem "standalone_migrations", "2.0.4" do
+ dep "standalone_migrations", ">= 0"
+ end
+
+ gem "standalone_migrations", "1.0.13" do
+ dep "rack", ">= 0"
+ end
+ end
+
+ dep "rack", "~> 3.0"
+ dep "standalone_migrations"
+
+ should_resolve_as %w[rack-3.0.0 standalone_migrations-2.0.4]
+ end
+
+ it "ignores versions that incorrectly depend on themselves" do
+ @index = build_index do
+ gem "rack", "3.0.0"
+
+ gem "standalone_migrations", "7.1.0" do
+ dep "rack", "~> 2.0"
+ end
+
+ gem "standalone_migrations", "2.0.4" do
+ dep "standalone_migrations", ">= 2.0.5"
+ end
+
+ gem "standalone_migrations", "1.0.13" do
+ dep "rack", ">= 0"
+ end
+ end
+
+ dep "rack", "~> 3.0"
+ dep "standalone_migrations"
+
+ should_resolve_as %w[rack-3.0.0 standalone_migrations-1.0.13]
+ end
end
diff --git a/spec/bundler/resolver/platform_spec.rb b/spec/bundler/resolver/platform_spec.rb
index 8eaed4220a..a710dfcb28 100644
--- a/spec/bundler/resolver/platform_spec.rb
+++ b/spec/bundler/resolver/platform_spec.rb
@@ -82,21 +82,105 @@ RSpec.describe "Resolving platform craziness" do
should_resolve_as %w[foo-1.0.0-x64-mingw32]
end
- it "takes the latest ruby gem if the platform specific gem doesn't match the required_ruby_version" do
- @index = build_index do
- gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32"
- gem "foo", "1.1.0"
- gem "foo", "1.1.0", "x64-mingw32" do |s|
- s.required_ruby_version = [">= 2.0", "< 2.4"]
+ describe "on a linux platform", :rubygems => ">= 3.1.0.pre.1" do
+ # Ruby's platform is *-linux => platform's libc is glibc, so not musl
+ # Ruby's platform is *-linux-musl => platform's libc is musl, so not glibc
+ # Gem's platform is *-linux => gem is glibc + maybe musl compatible
+ # Gem's platform is *-linux-musl => gem is musl compatible but not glibc
+
+ it "favors the platform version-specific gem on a version-specifying linux platform" do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x86_64-linux"
+ gem "foo", "1.0.0", "x86_64-linux-musl"
end
- gem "Ruby\0", "2.5.1"
+ dep "foo"
+ platforms "x86_64-linux-musl"
+
+ should_resolve_as %w[foo-1.0.0-x86_64-linux-musl]
end
- dep "foo"
- dep "Ruby\0", "2.5.1"
- platforms "x64-mingw32"
- should_resolve_as %w[foo-1.1.0]
+ it "favors the version-less gem over the version-specific gem on a gnu linux platform" do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x86_64-linux"
+ gem "foo", "1.0.0", "x86_64-linux-musl"
+ end
+ dep "foo"
+ platforms "x86_64-linux"
+
+ should_resolve_as %w[foo-1.0.0-x86_64-linux]
+ end
+
+ it "ignores the platform version-specific gem on a gnu linux platform" do
+ @index = build_index do
+ gem "foo", "1.0.0", "x86_64-linux-musl"
+ end
+ dep "foo"
+ platforms "x86_64-linux"
+
+ should_not_resolve
+ end
+
+ it "falls back to the platform version-less gem on a linux platform with a version" do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x86_64-linux"
+ end
+ dep "foo"
+ platforms "x86_64-linux-musl"
+
+ should_resolve_as %w[foo-1.0.0-x86_64-linux]
+ end
+
+ it "falls back to the ruby platform gem on a gnu linux platform when only a version-specifying gem is available" do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x86_64-linux-musl"
+ end
+ dep "foo"
+ platforms "x86_64-linux"
+
+ should_resolve_as %w[foo-1.0.0]
+ end
+
+ it "falls back to the platform version-less gem on a version-specifying linux platform and no ruby platform gem is available" do
+ @index = build_index do
+ gem "foo", "1.0.0", "x86_64-linux"
+ end
+ dep "foo"
+ platforms "x86_64-linux-musl"
+
+ should_resolve_as %w[foo-1.0.0-x86_64-linux]
+ end
+ end
+
+ context "when the platform specific gem doesn't match the required_ruby_version" do
+ before do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x64-mingw32"
+ gem "foo", "1.1.0"
+ gem "foo", "1.1.0", "x64-mingw32" do |s|
+ s.required_ruby_version = [">= 2.0", "< 2.4"]
+ end
+ gem "Ruby\0", "2.5.1"
+ end
+ dep "Ruby\0", "2.5.1"
+ platforms "x64-mingw32"
+ end
+
+ it "takes the latest ruby gem" do
+ dep "foo"
+
+ should_resolve_as %w[foo-1.1.0]
+ end
+
+ it "takes the latest ruby gem, even if requirement does not match previous versions with the same ruby requirement" do
+ dep "foo", "1.1.0"
+
+ should_resolve_as %w[foo-1.1.0]
+ end
end
it "takes the latest ruby gem with required_ruby_version if the platform specific gem doesn't match the required_ruby_version" do
@@ -137,39 +221,6 @@ RSpec.describe "Resolving platform craziness" do
should_resolve_as %w[foo-1.1.0]
end
- it "doesn't include gems not needed for none of the platforms" do
- @index = build_index do
- gem "empyrean", "0.1.0"
- gem "coderay", "1.1.2"
- gem "method_source", "0.9.0"
-
- gem "spoon", "0.0.6" do
- dep "ffi", ">= 0"
- end
-
- gem "pry", "0.11.3", "java" do
- dep "coderay", "~> 1.1.0"
- dep "method_source", "~> 0.9.0"
- dep "spoon", "~> 0.0"
- end
-
- gem "pry", "0.11.3" do
- dep "coderay", "~> 1.1.0"
- dep "method_source", "~> 0.9.0"
- end
-
- gem "ffi", "1.9.23", "java"
- gem "ffi", "1.9.23"
- end
-
- dep "empyrean", "0.1.0"
- dep "pry"
-
- platforms "ruby", "java"
-
- should_resolve_as %w[coderay-1.1.2 empyrean-0.1.0 ffi-1.9.23-java method_source-0.9.0 pry-0.11.3 pry-0.11.3-java spoon-0.0.6]
- end
-
it "includes gems needed for at least one platform" do
@index = build_index do
gem "empyrean", "0.1.0"
diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb
index dd22c86f90..29ef036828 100644
--- a/spec/bundler/runtime/inline_spec.rb
+++ b/spec/bundler/runtime/inline_spec.rb
@@ -96,12 +96,14 @@ RSpec.describe "bundler/inline#gemfile" do
it "lets me use my own ui object" do
script <<-RUBY, :artifice => "endpoint"
require '#{entrypoint}'
- class MyBundlerUI < Bundler::UI::Silent
+ class MyBundlerUI < Bundler::UI::Shell
def confirm(msg, newline = nil)
puts "CONFIRMED!"
end
end
- gemfile(true, :ui => MyBundlerUI.new) do
+ my_ui = MyBundlerUI.new
+ my_ui.level = "confirm"
+ gemfile(true, :ui => my_ui) do
source "https://notaserver.com"
gem "activesupport", :require => true
end
@@ -166,6 +168,54 @@ RSpec.describe "bundler/inline#gemfile" do
expect(err).to be_empty
end
+ it "installs subdependencies quietly if necessary when the install option is not set" do
+ build_repo4 do
+ build_gem "rack" do |s|
+ s.add_dependency "rackdep"
+ end
+
+ build_gem "rackdep", "1.0.0"
+ end
+
+ script <<-RUBY
+ gemfile do
+ source "#{file_uri_for(gem_repo4)}"
+ gem "rack"
+ end
+
+ require "rackdep"
+ puts RACKDEP
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err).to be_empty
+ end
+
+ it "installs subdependencies quietly if necessary when the install option is not set, and multiple sources used" do
+ build_repo4 do
+ build_gem "rack" do |s|
+ s.add_dependency "rackdep"
+ end
+
+ build_gem "rackdep", "1.0.0"
+ end
+
+ script <<-RUBY
+ gemfile do
+ source "#{file_uri_for(gem_repo1)}"
+ source "#{file_uri_for(gem_repo4)}" do
+ gem "rack"
+ end
+ end
+
+ require "rackdep"
+ puts RACKDEP
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err).to be_empty
+ end
+
it "installs quietly from git if necessary when the install option is not set" do
build_git "foo", "1.0.0"
baz_ref = build_git("baz", "2.0.0").ref_for("HEAD")
@@ -205,6 +255,113 @@ RSpec.describe "bundler/inline#gemfile" do
expect(err).to be_empty
end
+ it "doesn't reinstall already installed gems" do
+ system_gems "rack-1.0.0"
+
+ script <<-RUBY
+ require '#{entrypoint}'
+ ui = Bundler::UI::Shell.new
+ ui.level = "confirm"
+
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "activesupport"
+ gem "rack"
+ end
+ RUBY
+
+ expect(out).to include("Installing activesupport")
+ expect(out).not_to include("Installing rack")
+ expect(err).to be_empty
+ end
+
+ it "installs gems in later gemfile calls" do
+ system_gems "rack-1.0.0"
+
+ script <<-RUBY
+ require '#{entrypoint}'
+ ui = Bundler::UI::Shell.new
+ ui.level = "confirm"
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ end
+
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "activesupport"
+ end
+ RUBY
+
+ expect(out).to include("Installing activesupport")
+ expect(out).not_to include("Installing rack")
+ expect(err).to be_empty
+ end
+
+ it "doesn't reinstall already installed gems in later gemfile calls" do
+ system_gems "rack-1.0.0"
+
+ script <<-RUBY
+ require '#{entrypoint}'
+ ui = Bundler::UI::Shell.new
+ ui.level = "confirm"
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "activesupport"
+ end
+
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ end
+ RUBY
+
+ expect(out).to include("Installing activesupport")
+ expect(out).not_to include("Installing rack")
+ expect(err).to be_empty
+ end
+
+ it "installs gems with native extensions in later gemfile calls" do
+ system_gems "rack-1.0.0"
+
+ build_git "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("lib", __dir__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/foo.rb", "w") do |f|
+ f.puts "FOO = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ script <<-RUBY
+ require '#{entrypoint}'
+ ui = Bundler::UI::Shell.new
+ ui.level = "confirm"
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ end
+
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ end
+
+ require 'foo'
+ puts FOO
+ puts $:.grep(/ext/)
+ RUBY
+
+ expect(out).to include("YES")
+ expect(out).to include(Pathname.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s)
+ expect(err).to be_empty
+ end
+
it "installs inline gems when a Gemfile.lock is present" do
gemfile <<-G
source "https://notaserver.com"
@@ -355,6 +512,20 @@ RSpec.describe "bundler/inline#gemfile" do
expect(err).to be_empty
end
+ it "still installs if the application has `bundle package` no_install config set" do
+ bundle "config set --local no_install true"
+
+ script <<-RUBY
+ gemfile do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ end
+ RUBY
+
+ expect(last_command).to be_success
+ expect(system_gem_path("gems/rack-1.0.0")).to exist
+ end
+
it "preserves previous BUNDLE_GEMFILE value" do
ENV["BUNDLE_GEMFILE"] = ""
script <<-RUBY
@@ -420,7 +591,7 @@ RSpec.describe "bundler/inline#gemfile" do
expect(err).to be_empty
end
- it "when requiring fileutils after does not show redefinition warnings" do
+ it "when requiring fileutils after does not show redefinition warnings", :realworld do
dependency_installer_loads_fileutils = ruby "require 'rubygems/dependency_installer'; puts $LOADED_FEATURES.grep(/fileutils/)", :raise_on_error => false
skip "does not work if rubygems/dependency_installer loads fileutils, which happens until rubygems 3.2.0" unless dependency_installer_loads_fileutils.empty?
@@ -435,10 +606,11 @@ RSpec.describe "bundler/inline#gemfile" do
realworld_system_gems "pathname --version 0.2.0"
- realworld_system_gems "fiddle" # not sure why, but this is needed on Windows to boot rubygems successfully
-
realworld_system_gems "timeout uri" # this spec uses net/http which requires these default gems
+ # on prerelease rubies, a required_rubygems_version constraint is added by RubyGems to the resolution, causing Molinillo to load the `set` gem
+ realworld_system_gems "set --version 1.0.3" if Gem.ruby_version.prerelease?
+
script <<-RUBY, :dir => tmp("path_without_gemfile"), :env => { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
require "bundler/inline"
diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb
index 34dc5ba799..a9933f90e9 100644
--- a/spec/bundler/runtime/platform_spec.rb
+++ b/spec/bundler/runtime/platform_spec.rb
@@ -86,7 +86,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
racc (1.5.2)
PLATFORMS
- #{lockfile_platforms_for(["ruby", specific_local_platform])}
+ #{lockfile_platforms("ruby")}
DEPENDENCIES
nokogiri (~> 1.11)
@@ -281,30 +281,59 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
expect(the_bundle).to include_gems "platform_specific 1.0 RUBY"
- build_repo4 do
- build_gem "libv8"
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "libv8"
- build_gem "libv8" do |s|
- s.platform = Bundler.local_platform
+ build_gem "libv8" do |s|
+ s.platform = "x86_64-linux"
+ end
end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "libv8"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ libv8 (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ libv8
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "libv8 1.0 x86_64-linux"
end
+ end
+ it "doesn't pull platform specific gems on truffleruby, even if lockfile only includes those", :truffleruby_only do
gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "libv8"
+ source "#{file_uri_for(gem_repo1)}"
+ gem "platform_specific"
G
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo4)}/
+ remote: #{file_uri_for(gem_repo1)}/
specs:
- libv8 (1.0)
+ platform_specific (1.0-x86-darwin-100)
PLATFORMS
- ruby
+ x86-darwin-100
DEPENDENCIES
- libv8
+ platform_specific
BUNDLED WITH
#{Bundler::VERSION}
@@ -312,7 +341,24 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
bundle "install"
- expect(the_bundle).to include_gems "libv8 1.0 #{Bundler.local_platform}"
+ expect(the_bundle).to include_gems "platform_specific 1.0 RUBY"
+ end
+
+ it "pulls platform specific gems correctly on musl" do
+ build_repo4 do
+ build_gem "nokogiri", "1.13.8" do |s|
+ s.platform = "aarch64-linux"
+ end
+ end
+
+ simulate_platform "aarch64-linux-musl" do
+ install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, :verbose => true
+ source "https://gems.repo4"
+ gem "nokogiri"
+ G
+ end
+
+ expect(out).to include("Fetching nokogiri 1.13.8 (aarch64-linux)")
end
it "allows specifying only-ruby-platform on windows with dependency platforms" do
@@ -357,7 +403,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
s.add_dependency "platform_specific"
end
end
- simulate_windows x64_mingw do
+ simulate_windows x64_mingw32 do
lockfile <<-L
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -379,7 +425,40 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
gem "requires_platform_specific"
G
+ expect(out).to include("lockfile does not have all gems needed for the current platform")
expect(the_bundle).to include_gem "platform_specific 1.0 x64-mingw32"
end
end
+
+ %w[x86-mswin32 x64-mswin64 x86-mingw32 x64-mingw32 x64-mingw-ucrt].each do |arch|
+ it "allows specifying platform windows on #{arch} arch" do
+ platform = send(arch.tr("-", "_"))
+
+ simulate_windows platform do
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+ platform_specific (1.0-#{platform})
+ requires_platform_specific (1.0)
+ platform_specific
+
+ PLATFORMS
+ #{platform}
+
+ DEPENDENCIES
+ requires_platform_specific
+ L
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "platform_specific", :platforms => [:windows]
+ G
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 #{platform}"
+ end
+ end
+ end
end
diff --git a/spec/bundler/runtime/self_management_spec.rb b/spec/bundler/runtime/self_management_spec.rb
index 0032c6aef6..61cfc9b795 100644
--- a/spec/bundler/runtime/self_management_spec.rb
+++ b/spec/bundler/runtime/self_management_spec.rb
@@ -91,7 +91,7 @@ RSpec.describe "Self management", :rubygems => ">= 3.3.0.dev", :realworld => tru
expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION)
end
- it "shows a discreet message if locked bundler does not exist" do
+ it "shows a discrete message if locked bundler does not exist" do
missing_minor ="#{Bundler::VERSION[0]}.999.999"
lockfile_bundled_with(missing_minor)
diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb
index fecf4cd98b..2d39d72937 100644
--- a/spec/bundler/runtime/setup_spec.rb
+++ b/spec/bundler/runtime/setup_spec.rb
@@ -370,7 +370,7 @@ RSpec.describe "Bundler.setup" do
context "when the ruby stdlib is a substring of Gem.path" do
it "does not reject the stdlib from $LOAD_PATH" do
- substring = "/" + $LOAD_PATH.find {|p| p =~ /vendor_ruby/ }.split("/")[2]
+ substring = "/" + $LOAD_PATH.find {|p| p.include?("vendor_ruby") }.split("/")[2]
run "puts 'worked!'", :env => { "GEM_PATH" => substring }
expect(out).to eq("worked!")
end
@@ -489,7 +489,7 @@ RSpec.describe "Bundler.setup" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main"
G
bundle %(config set local.rack #{lib_path("local-rack")})
@@ -507,7 +507,7 @@ RSpec.describe "Bundler.setup" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main"
G
bundle %(config set local.rack #{lib_path("local-rack")})
@@ -529,7 +529,7 @@ RSpec.describe "Bundler.setup" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main"
G
bundle %(config set local.rack #{lib_path("local-rack")})
@@ -541,7 +541,7 @@ RSpec.describe "Bundler.setup" do
G
run "require 'rack'", :raise_on_error => false
- expect(err).to match(/is using branch master but Gemfile specifies changed/)
+ expect(err).to match(/is using branch main but Gemfile specifies changed/)
end
it "explodes on refs with different branches on runtime" do
@@ -551,17 +551,17 @@ RSpec.describe "Bundler.setup" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "master"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "main", :branch => "main"
G
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "nonexistant"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "main", :branch => "nonexistent"
G
bundle %(config set local.rack #{lib_path("local-rack")})
run "require 'rack'", :raise_on_error => false
- expect(err).to match(/is using branch master but Gemfile specifies nonexistant/)
+ expect(err).to match(/is using branch main but Gemfile specifies nonexistent/)
end
end
@@ -634,6 +634,17 @@ RSpec.describe "Bundler.setup" do
ruby "require '#{system_gem_path("gems/bundler-9.99.9.beta1/lib/bundler.rb")}'; Bundler.setup", :env => { "DEBUG" => "1" }
expect(out).to include("Found no changes, using resolution from the lockfile")
+ expect(out).not_to include("lockfile does not have all gems needed for the current platform")
+ expect(err).to be_empty
+ end
+
+ it "doesn't fail in frozen mode when bundler is a Gemfile dependency" do
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "bundler"
+ G
+
+ bundle "install --verbose", :env => { "BUNDLE_FROZEN" => "true" }
expect(err).to be_empty
end
@@ -864,7 +875,7 @@ end
gemspec_content = File.binread(gemspec).
sub("Bundler::VERSION", %("#{Bundler::VERSION}")).
- lines.reject {|line| line =~ %r{lib/bundler/version} }.join
+ lines.reject {|line| line.include?("lib/bundler/version") }.join
File.open(File.join(specifications_dir, "#{full_name}.gemspec"), "wb") do |f|
f.write(gemspec_content)
@@ -1303,18 +1314,14 @@ end
describe "default gem activation" do
let(:exemptions) do
- exempts = if Gem.rubygems_version >= Gem::Version.new("2.7")
- %w[did_you_mean]
- else
- %w[io-console openssl]
- end << "bundler"
- exempts << "fiddle" if Gem.win_platform? && Gem.rubygems_version >= Gem::Version.new("2.7")
+ exempts = %w[did_you_mean bundler]
exempts << "uri" if Gem.ruby_version >= Gem::Version.new("2.7")
exempts << "pathname" if Gem.ruby_version >= Gem::Version.new("3.0")
exempts << "set" unless Gem.rubygems_version >= Gem::Version.new("3.2.6")
exempts << "tsort" unless Gem.rubygems_version >= Gem::Version.new("3.2.31")
exempts << "error_highlight" # added in Ruby 3.1 as a default gem
exempts << "ruby2_keywords" # added in Ruby 3.1 as a default gem
+ exempts << "syntax_suggest" # added in Ruby 3.2 as a default gem
exempts
end
@@ -1522,4 +1529,29 @@ end
expect(err).to be_empty
end
end
+
+ it "does not undo the Kernel.require decorations", :rubygems => ">= 3.4.6" do
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
+ script = bundled_app("bin/script")
+ create_file(script, <<~RUBY)
+ module Kernel
+ module_function
+
+ alias_method :require_before_extra_monkeypatches, :require
+
+ def require(path)
+ puts "requiring \#{path} used the monkeypatch"
+
+ require_before_extra_monkeypatches(path)
+ end
+ end
+
+ require "bundler/setup"
+
+ require "foo"
+ RUBY
+
+ sys_exec "#{Gem.ruby} #{script}", :raise_on_error => false
+ expect(out).to include("requiring foo used the monkeypatch")
+ end
end
diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb
index 892ad10e98..6a7e2891a6 100644
--- a/spec/bundler/spec_helper.rb
+++ b/spec/bundler/spec_helper.rb
@@ -23,7 +23,6 @@ require_relative "support/indexes"
require_relative "support/matchers"
require_relative "support/permissions"
require_relative "support/platforms"
-require_relative "support/sudo"
$debug = false
@@ -40,7 +39,6 @@ RSpec.configure do |config|
config.include Spec::Matchers
config.include Spec::Path
config.include Spec::Platforms
- config.include Spec::Sudo
config.include Spec::Permissions
# Enable flags like --only-failures and --next-failure
@@ -60,6 +58,8 @@ RSpec.configure do |config|
config.expect_with :rspec do |c|
c.syntax = :expect
+
+ c.max_formatted_output_length = 1000
end
config.mock_with :rspec do |mocks|
@@ -96,27 +96,21 @@ RSpec.configure do |config|
end
config.around :each do |example|
- begin
- FileUtils.cp_r pristine_system_gem_path, system_gem_path
-
- with_gem_path_as(system_gem_path) do
- Bundler.ui.silence { example.run }
-
- all_output = all_commands_output
- if example.exception && !all_output.empty?
- message = all_output + "\n" + example.exception.message
- (class << example.exception; self; end).send(:define_method, :message) do
- message
- end
+ FileUtils.cp_r pristine_system_gem_path, system_gem_path
+
+ with_gem_path_as(system_gem_path) do
+ Bundler.ui.silence { example.run }
+
+ all_output = all_commands_output
+ if example.exception && !all_output.empty?
+ message = all_output + "\n" + example.exception.message
+ (class << example.exception; self; end).send(:define_method, :message) do
+ message
end
end
- ensure
- reset!
end
- end
-
- config.before :each, :sudo => true do
- Spec::Sudo.write_safe_config
+ ensure
+ reset!
end
config.after :suite do
diff --git a/spec/bundler/support/api_request_limit_hax.rb b/spec/bundler/support/api_request_limit_hax.rb
deleted file mode 100644
index 37ff0203b3..0000000000
--- a/spec/bundler/support/api_request_limit_hax.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-if ENV["BUNDLER_SPEC_API_REQUEST_LIMIT"]
- require_relative "path"
- require "bundler/source"
- require "bundler/source/rubygems"
-
- module Bundler
- class Source
- class Rubygems < Source
- remove_const :API_REQUEST_LIMIT
- API_REQUEST_LIMIT = ENV["BUNDLER_SPEC_API_REQUEST_LIMIT"].to_i
- end
- end
- end
-end
diff --git a/spec/bundler/support/artifice/compact_index.rb b/spec/bundler/support/artifice/compact_index.rb
index fb068fa9b5..ebc4d0ae5b 100644
--- a/spec/bundler/support/artifice/compact_index.rb
+++ b/spec/bundler/support/artifice/compact_index.rb
@@ -1,120 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-$LOAD_PATH.unshift Dir[Spec::Path.base_system_gem_path.join("gems/compact_index*/lib")].first.to_s
-require "compact_index"
-
-class CompactIndexAPI < Endpoint
- helpers do
- include Spec::Path
-
- def load_spec(name, version, platform, gem_repo)
- full_name = "#{name}-#{version}"
- full_name += "-#{platform}" if platform != "ruby"
- Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz"))))
- end
-
- def etag_response
- response_body = yield
- checksum = Digest(:MD5).hexdigest(response_body)
- return if not_modified?(checksum)
- headers "ETag" => quote(checksum)
- headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
- content_type "text/plain"
- requested_range_for(response_body)
- rescue StandardError => e
- puts e
- puts e.backtrace
- raise
- end
-
- def not_modified?(checksum)
- etags = parse_etags(request.env["HTTP_IF_NONE_MATCH"])
-
- return unless etags.include?(checksum)
- headers "ETag" => quote(checksum)
- status 304
- body ""
- end
-
- def requested_range_for(response_body)
- ranges = Rack::Utils.byte_ranges(env, response_body.bytesize)
-
- if ranges
- status 206
- body ranges.map! {|range| slice_body(response_body, range) }.join
- else
- status 200
- body response_body
- end
- end
-
- def quote(string)
- %("#{string}")
- end
-
- def parse_etags(value)
- value ? value.split(/, ?/).select {|s| s.sub!(/"(.*)"/, '\1') } : []
- end
-
- def slice_body(body, range)
- body.byteslice(range)
- end
-
- def gems(gem_repo = default_gem_repo)
- @gems ||= {}
- @gems[gem_repo] ||= begin
- specs = Bundler::Deprecate.skip_during do
- %w[specs.4.8 prerelease_specs.4.8].map do |filename|
- Marshal.load(File.open(gem_repo.join(filename)).read).map do |name, version, platform|
- load_spec(name, version, platform, gem_repo)
- end
- end.flatten
- end
-
- specs.group_by(&:name).map do |name, versions|
- gem_versions = versions.map do |spec|
- deps = spec.dependencies.select {|d| d.type == :runtime }.map do |d|
- reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ")
- CompactIndex::Dependency.new(d.name, reqs)
- end
- checksum = begin
- Digest(:SHA256).file("#{gem_repo}/gems/#{spec.original_name}.gem").base64digest
- rescue StandardError
- nil
- end
- CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
- deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s)
- end
- CompactIndex::Gem.new(name, gem_versions)
- end
- end
- end
- end
-
- get "/names" do
- etag_response do
- CompactIndex.names(gems.map(&:name))
- end
- end
-
- get "/versions" do
- etag_response do
- file = tmp("versions.list")
- FileUtils.rm_f(file)
- file = CompactIndex::VersionsFile.new(file.to_s)
- file.create(gems)
- file.contents
- end
- end
-
- get "/info/:name" do
- etag_response do
- gem = gems.find {|g| g.name == params[:name] }
- CompactIndex.info(gem ? gem.versions : [])
- end
- end
-end
+require_relative "helpers/compact_index"
+require_relative "helpers/artifice"
Artifice.activate_with(CompactIndexAPI)
diff --git a/spec/bundler/support/artifice/compact_index_api_missing.rb b/spec/bundler/support/artifice/compact_index_api_missing.rb
index 6514fde01e..f771f7d1f0 100644
--- a/spec/bundler/support/artifice/compact_index_api_missing.rb
+++ b/spec/bundler/support/artifice/compact_index_api_missing.rb
@@ -1,18 +1,13 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexApiMissing < CompactIndexAPI
get "/fetch/actual/gem/:id" do
- warn params[:id]
- if params[:id] == "rack-1.0.gemspec.rz"
- halt 404
- else
- File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
- end
+ halt 404
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexApiMissing)
diff --git a/spec/bundler/support/artifice/compact_index_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_basic_authentication.rb
index 775f1a3977..b9115cdd86 100644
--- a/spec/bundler/support/artifice/compact_index_basic_authentication.rb
+++ b/spec/bundler/support/artifice/compact_index_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexBasicAuthentication < CompactIndexAPI
before do
@@ -12,4 +10,6 @@ class CompactIndexBasicAuthentication < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexBasicAuthentication)
diff --git a/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
index 1abe64236c..a6545b9ee4 100644
--- a/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
+++ b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexChecksumMismatch < CompactIndexAPI
get "/versions" do
@@ -13,4 +11,6 @@ class CompactIndexChecksumMismatch < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexChecksumMismatch)
diff --git a/spec/bundler/support/artifice/compact_index_concurrent_download.rb b/spec/bundler/support/artifice/compact_index_concurrent_download.rb
index 14c31f35a4..35548f278c 100644
--- a/spec/bundler/support/artifice/compact_index_concurrent_download.rb
+++ b/spec/bundler/support/artifice/compact_index_concurrent_download.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexConcurrentDownload < CompactIndexAPI
get "/versions" do
@@ -29,4 +27,6 @@ class CompactIndexConcurrentDownload < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexConcurrentDownload)
diff --git a/spec/bundler/support/artifice/compact_index_creds_diff_host.rb b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb
index cfe22c7f51..401e8a98d8 100644
--- a/spec/bundler/support/artifice/compact_index_creds_diff_host.rb
+++ b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexCredsDiffHost < CompactIndexAPI
helpers do
@@ -36,4 +34,6 @@ class CompactIndexCredsDiffHost < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexCredsDiffHost)
diff --git a/spec/bundler/support/artifice/compact_index_extra.rb b/spec/bundler/support/artifice/compact_index_extra.rb
index cec368276a..cd41b3ecca 100644
--- a/spec/bundler/support/artifice/compact_index_extra.rb
+++ b/spec/bundler/support/artifice/compact_index_extra.rb
@@ -1,37 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
-
-class CompactIndexExtra < CompactIndexAPI
- get "/extra/versions" do
- halt 404
- end
-
- get "/extra/api/v1/dependencies" do
- halt 404
- end
-
- get "/extra/specs.4.8.gz" do
- File.binread("#{gem_repo2}/specs.4.8.gz")
- end
-
- get "/extra/prerelease_specs.4.8.gz" do
- File.binread("#{gem_repo2}/prerelease_specs.4.8.gz")
- end
-
- get "/extra/quick/Marshal.4.8/:id" do
- redirect "/extra/fetch/actual/gem/#{params[:id]}"
- end
-
- get "/extra/fetch/actual/gem/:id" do
- File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
- end
-
- get "/extra/gems/:id" do
- File.binread("#{gem_repo2}/gems/#{params[:id]}")
- end
-end
+require_relative "helpers/compact_index_extra"
+require_relative "helpers/artifice"
Artifice.activate_with(CompactIndexExtra)
diff --git a/spec/bundler/support/artifice/compact_index_extra_api.rb b/spec/bundler/support/artifice/compact_index_extra_api.rb
index 5cc13421a8..8b9d304ab4 100644
--- a/spec/bundler/support/artifice/compact_index_extra_api.rb
+++ b/spec/bundler/support/artifice/compact_index_extra_api.rb
@@ -1,52 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
-
-class CompactIndexExtraApi < CompactIndexAPI
- get "/extra/names" do
- etag_response do
- CompactIndex.names(gems(gem_repo4).map(&:name))
- end
- end
-
- get "/extra/versions" do
- etag_response do
- file = tmp("versions.list")
- FileUtils.rm_f(file)
- file = CompactIndex::VersionsFile.new(file.to_s)
- file.create(gems(gem_repo4))
- file.contents
- end
- end
-
- get "/extra/info/:name" do
- etag_response do
- gem = gems(gem_repo4).find {|g| g.name == params[:name] }
- CompactIndex.info(gem ? gem.versions : [])
- end
- end
-
- get "/extra/specs.4.8.gz" do
- File.binread("#{gem_repo4}/specs.4.8.gz")
- end
-
- get "/extra/prerelease_specs.4.8.gz" do
- File.binread("#{gem_repo4}/prerelease_specs.4.8.gz")
- end
-
- get "/extra/quick/Marshal.4.8/:id" do
- redirect "/extra/fetch/actual/gem/#{params[:id]}"
- end
-
- get "/extra/fetch/actual/gem/:id" do
- File.binread("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}")
- end
-
- get "/extra/gems/:id" do
- File.binread("#{gem_repo4}/gems/#{params[:id]}")
- end
-end
+require_relative "helpers/compact_index_extra_api"
+require_relative "helpers/artifice"
Artifice.activate_with(CompactIndexExtraApi)
diff --git a/spec/bundler/support/artifice/compact_index_extra_api_missing.rb b/spec/bundler/support/artifice/compact_index_extra_api_missing.rb
index b9d757c266..df6ede584c 100644
--- a/spec/bundler/support/artifice/compact_index_extra_api_missing.rb
+++ b/spec/bundler/support/artifice/compact_index_extra_api_missing.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index_extra_api"
-
-Artifice.deactivate
+require_relative "helpers/compact_index_extra_api"
class CompactIndexExtraAPIMissing < CompactIndexExtraApi
get "/extra/fetch/actual/gem/:id" do
@@ -14,4 +12,6 @@ class CompactIndexExtraAPIMissing < CompactIndexExtraApi
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexExtraAPIMissing)
diff --git a/spec/bundler/support/artifice/compact_index_extra_missing.rb b/spec/bundler/support/artifice/compact_index_extra_missing.rb
index ff1e47a1bb..255c89afdb 100644
--- a/spec/bundler/support/artifice/compact_index_extra_missing.rb
+++ b/spec/bundler/support/artifice/compact_index_extra_missing.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index_extra"
-
-Artifice.deactivate
+require_relative "helpers/compact_index_extra"
class CompactIndexExtraMissing < CompactIndexExtra
get "/extra/fetch/actual/gem/:id" do
@@ -14,4 +12,6 @@ class CompactIndexExtraMissing < CompactIndexExtra
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexExtraMissing)
diff --git a/spec/bundler/support/artifice/compact_index_forbidden.rb b/spec/bundler/support/artifice/compact_index_forbidden.rb
index 3eebe0fbd8..18c30ed9a2 100644
--- a/spec/bundler/support/artifice/compact_index_forbidden.rb
+++ b/spec/bundler/support/artifice/compact_index_forbidden.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexForbidden < CompactIndexAPI
get "/versions" do
@@ -10,4 +8,6 @@ class CompactIndexForbidden < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexForbidden)
diff --git a/spec/bundler/support/artifice/compact_index_host_redirect.rb b/spec/bundler/support/artifice/compact_index_host_redirect.rb
index 304c897d68..9a711186db 100644
--- a/spec/bundler/support/artifice/compact_index_host_redirect.rb
+++ b/spec/bundler/support/artifice/compact_index_host_redirect.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexHostRedirect < CompactIndexAPI
get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do
@@ -18,4 +16,6 @@ class CompactIndexHostRedirect < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexHostRedirect)
diff --git a/spec/bundler/support/artifice/compact_index_no_gem.rb b/spec/bundler/support/artifice/compact_index_no_gem.rb
index 0a4be08a46..71f6629688 100644
--- a/spec/bundler/support/artifice/compact_index_no_gem.rb
+++ b/spec/bundler/support/artifice/compact_index_no_gem.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexNoGem < CompactIndexAPI
get "/gems/:id" do
@@ -10,4 +8,6 @@ class CompactIndexNoGem < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexNoGem)
diff --git a/spec/bundler/support/artifice/compact_index_partial_update.rb b/spec/bundler/support/artifice/compact_index_partial_update.rb
index cb1c7b9481..8c73011346 100644
--- a/spec/bundler/support/artifice/compact_index_partial_update.rb
+++ b/spec/bundler/support/artifice/compact_index_partial_update.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexPartialUpdate < CompactIndexAPI
# Stub the server to never return 304s. This simulates the behaviour of
@@ -35,4 +33,6 @@ class CompactIndexPartialUpdate < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexPartialUpdate)
diff --git a/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb b/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb
index acf76dfbf0..20546ba4c3 100644
--- a/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb
+++ b/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexPartialUpdateNoEtagNotIncremental < CompactIndexAPI
def partial_update_no_etag
@@ -37,4 +35,6 @@ class CompactIndexPartialUpdateNoEtagNotIncremental < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexPartialUpdateNoEtagNotIncremental)
diff --git a/spec/bundler/support/artifice/compact_index_precompiled_before.rb b/spec/bundler/support/artifice/compact_index_precompiled_before.rb
new file mode 100644
index 0000000000..b5f72f546a
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_precompiled_before.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+class CompactIndexPrecompiledBefore < CompactIndexAPI
+ get "/info/:name" do
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ move_ruby_variant_to_the_end(CompactIndex.info(gem ? gem.versions : []))
+ end
+ end
+
+ private
+
+ def move_ruby_variant_to_the_end(response)
+ lines = response.split("\n")
+ ruby = lines.find {|line| /\A\d+\.\d+\.\d* \|/.match(line) }
+ lines.delete(ruby)
+ lines.push(ruby).join("\n")
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexPrecompiledBefore)
diff --git a/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb b/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
index bb616125bb..8a7c4b79b0 100644
--- a/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
+++ b/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexRangeNotSatisfiable < CompactIndexAPI
get "/versions" do
@@ -31,4 +29,6 @@ class CompactIndexRangeNotSatisfiable < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexRangeNotSatisfiable)
diff --git a/spec/bundler/support/artifice/compact_index_rate_limited.rb b/spec/bundler/support/artifice/compact_index_rate_limited.rb
index 570105e2a0..4495491635 100644
--- a/spec/bundler/support/artifice/compact_index_rate_limited.rb
+++ b/spec/bundler/support/artifice/compact_index_rate_limited.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexRateLimited < CompactIndexAPI
class RequestCounter
@@ -45,4 +43,6 @@ class CompactIndexRateLimited < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexRateLimited)
diff --git a/spec/bundler/support/artifice/compact_index_redirects.rb b/spec/bundler/support/artifice/compact_index_redirects.rb
index 99adc797bf..f7ba393239 100644
--- a/spec/bundler/support/artifice/compact_index_redirects.rb
+++ b/spec/bundler/support/artifice/compact_index_redirects.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexRedirect < CompactIndexAPI
get "/fetch/actual/gem/:id" do
@@ -18,4 +16,6 @@ class CompactIndexRedirect < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexRedirect)
diff --git a/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
index 7d427b5382..96259385e7 100644
--- a/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
+++ b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexStrictBasicAuthentication < CompactIndexAPI
before do
@@ -12,9 +10,11 @@ class CompactIndexStrictBasicAuthentication < CompactIndexAPI
# Only accepts password == "password"
unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz"
- halt 403, "Authentication failed"
+ halt 401, "Authentication failed"
end
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexStrictBasicAuthentication)
diff --git a/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb
index 036fac70b3..15850599b6 100644
--- a/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb
+++ b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexWrongDependencies < CompactIndexAPI
get "/info/:name" do
@@ -14,4 +12,6 @@ class CompactIndexWrongDependencies < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexWrongDependencies)
diff --git a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
index 8add32b88f..acc13a56ff 100644
--- a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
+++ b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexWrongGemChecksum < CompactIndexAPI
get "/info/:name" do
@@ -17,4 +15,6 @@ class CompactIndexWrongGemChecksum < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexWrongGemChecksum)
diff --git a/spec/bundler/support/artifice/endpoint.rb b/spec/bundler/support/artifice/endpoint.rb
index c00113b28f..15242a7942 100644
--- a/spec/bundler/support/artifice/endpoint.rb
+++ b/spec/bundler/support/artifice/endpoint.rb
@@ -1,115 +1,6 @@
# frozen_string_literal: true
-require_relative "../path"
-
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{artifice,mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
-
-require "artifice"
-require "sinatra/base"
-
-ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant
-ALL_REQUESTS_MUTEX = Thread::Mutex.new
-
-at_exit do
- if expected = ENV["BUNDLER_SPEC_ALL_REQUESTS"]
- expected = expected.split("\n").sort
- actual = ALL_REQUESTS.sort
-
- unless expected == actual
- raise "Unexpected requests!\nExpected:\n\t#{expected.join("\n\t")}\n\nActual:\n\t#{actual.join("\n\t")}"
- end
- end
-end
-
-class Endpoint < Sinatra::Base
- def self.all_requests
- @all_requests ||= []
- end
-
- set :raise_errors, true
- set :show_exceptions, false
-
- def call!(*)
- super.tap do
- ALL_REQUESTS_MUTEX.synchronize do
- ALL_REQUESTS << @request.url
- end
- end
- end
-
- helpers do
- include Spec::Path
-
- def default_gem_repo
- if ENV["BUNDLER_SPEC_GEM_REPO"]
- Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"])
- else
- case request.host
- when "gem.repo1"
- Spec::Path.gem_repo1
- when "gem.repo2"
- Spec::Path.gem_repo2
- when "gem.repo3"
- Spec::Path.gem_repo3
- when "gem.repo4"
- Spec::Path.gem_repo4
- else
- Spec::Path.gem_repo1
- end
- end
- end
-
- def dependencies_for(gem_names, gem_repo = default_gem_repo)
- return [] if gem_names.nil? || gem_names.empty?
-
- all_specs = %w[specs.4.8 prerelease_specs.4.8].map do |filename|
- Marshal.load(File.open(gem_repo.join(filename)).read)
- end.inject(:+)
-
- all_specs.map do |name, version, platform|
- spec = load_spec(name, version, platform, gem_repo)
- next unless gem_names.include?(spec.name)
- {
- :name => spec.name,
- :number => spec.version.version,
- :platform => spec.platform.to_s,
- :dependencies => spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep|
- [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")]
- end,
- }
- end.compact
- end
-
- def load_spec(name, version, platform, gem_repo)
- full_name = "#{name}-#{version}"
- full_name += "-#{platform}" if platform != "ruby"
- Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz"))))
- end
- end
-
- get "/quick/Marshal.4.8/:id" do
- redirect "/fetch/actual/gem/#{params[:id]}"
- end
-
- get "/fetch/actual/gem/:id" do
- File.binread("#{default_gem_repo}/quick/Marshal.4.8/#{params[:id]}")
- end
-
- get "/gems/:id" do
- File.binread("#{default_gem_repo}/gems/#{params[:id]}")
- end
-
- get "/api/v1/dependencies" do
- Marshal.dump(dependencies_for(params[:gems]))
- end
-
- get "/specs.4.8.gz" do
- File.binread("#{default_gem_repo}/specs.4.8.gz")
- end
-
- get "/prerelease_specs.4.8.gz" do
- File.binread("#{default_gem_repo}/prerelease_specs.4.8.gz")
- end
-end
+require_relative "helpers/endpoint"
+require_relative "helpers/artifice"
Artifice.activate_with(Endpoint)
diff --git a/spec/bundler/support/artifice/endpoint_500.rb b/spec/bundler/support/artifice/endpoint_500.rb
index a0d850a44d..d8ab6b65bc 100644
--- a/spec/bundler/support/artifice/endpoint_500.rb
+++ b/spec/bundler/support/artifice/endpoint_500.rb
@@ -2,17 +2,16 @@
require_relative "../path"
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{artifice,mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
-require "artifice"
require "sinatra/base"
-Artifice.deactivate
-
class Endpoint500 < Sinatra::Base
before do
halt 500
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(Endpoint500)
diff --git a/spec/bundler/support/artifice/endpoint_api_forbidden.rb b/spec/bundler/support/artifice/endpoint_api_forbidden.rb
index edc2463424..6bdc5896d6 100644
--- a/spec/bundler/support/artifice/endpoint_api_forbidden.rb
+++ b/spec/bundler/support/artifice/endpoint_api_forbidden.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointApiForbidden < Endpoint
get "/api/v1/dependencies" do
@@ -10,4 +8,6 @@ class EndpointApiForbidden < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointApiForbidden)
diff --git a/spec/bundler/support/artifice/endpoint_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_basic_authentication.rb
index ff3d1493d6..e8e3569e63 100644
--- a/spec/bundler/support/artifice/endpoint_basic_authentication.rb
+++ b/spec/bundler/support/artifice/endpoint_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointBasicAuthentication < Endpoint
before do
@@ -12,4 +10,6 @@ class EndpointBasicAuthentication < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint_creds_diff_host.rb b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb
index 8b8972cedd..ce30de0a68 100644
--- a/spec/bundler/support/artifice/endpoint_creds_diff_host.rb
+++ b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointCredsDiffHost < Endpoint
helpers do
@@ -36,4 +34,6 @@ class EndpointCredsDiffHost < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointCredsDiffHost)
diff --git a/spec/bundler/support/artifice/endpoint_extra.rb b/spec/bundler/support/artifice/endpoint_extra.rb
index 942c4352b7..021fd435fe 100644
--- a/spec/bundler/support/artifice/endpoint_extra.rb
+++ b/spec/bundler/support/artifice/endpoint_extra.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointExtra < Endpoint
get "/extra/api/v1/dependencies" do
@@ -30,4 +28,6 @@ class EndpointExtra < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointExtra)
diff --git a/spec/bundler/support/artifice/endpoint_extra_api.rb b/spec/bundler/support/artifice/endpoint_extra_api.rb
index 1cfef7a7fc..a965af6e73 100644
--- a/spec/bundler/support/artifice/endpoint_extra_api.rb
+++ b/spec/bundler/support/artifice/endpoint_extra_api.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointExtraApi < Endpoint
get "/extra/api/v1/dependencies" do
@@ -31,4 +29,6 @@ class EndpointExtraApi < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointExtraApi)
diff --git a/spec/bundler/support/artifice/endpoint_extra_missing.rb b/spec/bundler/support/artifice/endpoint_extra_missing.rb
index 5fd9238207..73e2defb32 100644
--- a/spec/bundler/support/artifice/endpoint_extra_missing.rb
+++ b/spec/bundler/support/artifice/endpoint_extra_missing.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint_extra"
-
-Artifice.deactivate
+require_relative "helpers/endpoint_extra"
class EndpointExtraMissing < EndpointExtra
get "/extra/fetch/actual/gem/:id" do
@@ -14,4 +12,6 @@ class EndpointExtraMissing < EndpointExtra
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointExtraMissing)
diff --git a/spec/bundler/support/artifice/endpoint_fallback.rb b/spec/bundler/support/artifice/endpoint_fallback.rb
index 08edf232e3..742e563f07 100644
--- a/spec/bundler/support/artifice/endpoint_fallback.rb
+++ b/spec/bundler/support/artifice/endpoint_fallback.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointFallback < Endpoint
DEPENDENCY_LIMIT = 60
@@ -16,4 +14,6 @@ class EndpointFallback < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointFallback)
diff --git a/spec/bundler/support/artifice/endpoint_host_redirect.rb b/spec/bundler/support/artifice/endpoint_host_redirect.rb
index 338cbcad00..0efb6cda02 100644
--- a/spec/bundler/support/artifice/endpoint_host_redirect.rb
+++ b/spec/bundler/support/artifice/endpoint_host_redirect.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointHostRedirect < Endpoint
get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do
@@ -14,4 +12,6 @@ class EndpointHostRedirect < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointHostRedirect)
diff --git a/spec/bundler/support/artifice/endpoint_marshal_fail.rb b/spec/bundler/support/artifice/endpoint_marshal_fail.rb
index 22c13e3e17..74ce321de6 100644
--- a/spec/bundler/support/artifice/endpoint_marshal_fail.rb
+++ b/spec/bundler/support/artifice/endpoint_marshal_fail.rb
@@ -1,13 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint_fallback"
-
-Artifice.deactivate
-
-class EndpointMarshalFail < EndpointFallback
- get "/api/v1/dependencies" do
- "f0283y01hasf"
- end
-end
+require_relative "helpers/endpoint_marshal_fail"
+require_relative "helpers/artifice"
Artifice.activate_with(EndpointMarshalFail)
diff --git a/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb
index c341c3993f..ea4cfbe965 100644
--- a/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb
+++ b/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint_marshal_fail"
-
-Artifice.deactivate
+require_relative "helpers/endpoint_marshal_fail"
class EndpointMarshalFailBasicAuthentication < EndpointMarshalFail
before do
@@ -12,4 +10,6 @@ class EndpointMarshalFailBasicAuthentication < EndpointMarshalFail
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointMarshalFailBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint_mirror_source.rb b/spec/bundler/support/artifice/endpoint_mirror_source.rb
index 788a9027f3..6ea1a77eca 100644
--- a/spec/bundler/support/artifice/endpoint_mirror_source.rb
+++ b/spec/bundler/support/artifice/endpoint_mirror_source.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
+require_relative "helpers/endpoint"
class EndpointMirrorSource < Endpoint
get "/gems/:id" do
@@ -12,4 +12,6 @@ class EndpointMirrorSource < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointMirrorSource)
diff --git a/spec/bundler/support/artifice/endpoint_redirect.rb b/spec/bundler/support/artifice/endpoint_redirect.rb
index ee97fccf64..84f546ba9d 100644
--- a/spec/bundler/support/artifice/endpoint_redirect.rb
+++ b/spec/bundler/support/artifice/endpoint_redirect.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointRedirect < Endpoint
get "/fetch/actual/gem/:id" do
@@ -14,4 +12,6 @@ class EndpointRedirect < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointRedirect)
diff --git a/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
index 4d4da08770..dff360c5c5 100644
--- a/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
+++ b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointStrictBasicAuthentication < Endpoint
before do
@@ -12,9 +10,11 @@ class EndpointStrictBasicAuthentication < Endpoint
# Only accepts password == "password"
unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz"
- halt 403, "Authentication failed"
+ halt 401, "Authentication failed"
end
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointStrictBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint_timeout.rb b/spec/bundler/support/artifice/endpoint_timeout.rb
index c118da1893..86b793e499 100644
--- a/spec/bundler/support/artifice/endpoint_timeout.rb
+++ b/spec/bundler/support/artifice/endpoint_timeout.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint_fallback"
-
-Artifice.deactivate
+require_relative "helpers/endpoint_fallback"
class EndpointTimeout < EndpointFallback
SLEEP_TIMEOUT = 3
@@ -12,4 +10,6 @@ class EndpointTimeout < EndpointFallback
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointTimeout)
diff --git a/spec/bundler/support/artifice/fail.rb b/spec/bundler/support/artifice/fail.rb
index f69f2eccc6..6286e43fbd 100644
--- a/spec/bundler/support/artifice/fail.rb
+++ b/spec/bundler/support/artifice/fail.rb
@@ -2,10 +2,6 @@
require "net/http"
-# We can't use artifice here because it uses rack
-
-module Artifice; end # for < 2.0, Net::HTTP::Persistent::SSLReuse
-
class Fail < Net::HTTP
# Net::HTTP uses a @newimpl instance variable to decide whether
# to use a legacy implementation. Since we are subclassing
@@ -27,8 +23,7 @@ class Fail < Net::HTTP
end
end
+require_relative "helpers/artifice"
+
# Replace Net::HTTP with our failing subclass
-::Net.class_eval do
- remove_const(:HTTP)
- const_set(:HTTP, ::Fail)
-end
+Artifice.replace_net_http(::Fail)
diff --git a/spec/bundler/support/artifice/helpers/artifice.rb b/spec/bundler/support/artifice/helpers/artifice.rb
new file mode 100644
index 0000000000..b8c78614fb
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/artifice.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+# This module was initially borrowed from https://github.com/wycats/artifice
+module Artifice
+ # Activate Artifice with a particular Rack endpoint.
+ #
+ # Calling this method will replace the Net::HTTP system
+ # with a replacement that routes all requests to the
+ # Rack endpoint.
+ #
+ # @param [#call] endpoint A valid Rack endpoint
+ def self.activate_with(endpoint)
+ require_relative "rack_request"
+
+ Net::HTTP.endpoint = endpoint
+ replace_net_http(Artifice::Net::HTTP)
+ end
+
+ # Deactivate the Artifice replacement.
+ def self.deactivate
+ replace_net_http(::Net::HTTP)
+ end
+
+ def self.replace_net_http(value)
+ ::Net.class_eval do
+ remove_const(:HTTP)
+ const_set(:HTTP, value)
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/compact_index.rb b/spec/bundler/support/artifice/helpers/compact_index.rb
new file mode 100644
index 0000000000..4df47a9659
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/compact_index.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+require_relative "endpoint"
+
+$LOAD_PATH.unshift Dir[Spec::Path.base_system_gem_path.join("gems/compact_index*/lib")].first.to_s
+require "compact_index"
+
+class CompactIndexAPI < Endpoint
+ helpers do
+ include Spec::Path
+
+ def load_spec(name, version, platform, gem_repo)
+ full_name = "#{name}-#{version}"
+ full_name += "-#{platform}" if platform != "ruby"
+ Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz"))))
+ end
+
+ def etag_response
+ response_body = yield
+ checksum = Digest(:MD5).hexdigest(response_body)
+ return if not_modified?(checksum)
+ headers "ETag" => quote(checksum)
+ headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
+ content_type "text/plain"
+ requested_range_for(response_body)
+ rescue StandardError => e
+ puts e
+ puts e.backtrace
+ raise
+ end
+
+ def not_modified?(checksum)
+ etags = parse_etags(request.env["HTTP_IF_NONE_MATCH"])
+
+ return unless etags.include?(checksum)
+ headers "ETag" => quote(checksum)
+ status 304
+ body ""
+ end
+
+ def requested_range_for(response_body)
+ ranges = Rack::Utils.byte_ranges(env, response_body.bytesize)
+
+ if ranges
+ status 206
+ body ranges.map! {|range| slice_body(response_body, range) }.join
+ else
+ status 200
+ body response_body
+ end
+ end
+
+ def quote(string)
+ %("#{string}")
+ end
+
+ def parse_etags(value)
+ value ? value.split(/, ?/).select {|s| s.sub!(/"(.*)"/, '\1') } : []
+ end
+
+ def slice_body(body, range)
+ body.byteslice(range)
+ end
+
+ def gems(gem_repo = default_gem_repo)
+ @gems ||= {}
+ @gems[gem_repo] ||= begin
+ specs = Bundler::Deprecate.skip_during do
+ %w[specs.4.8 prerelease_specs.4.8].map do |filename|
+ Marshal.load(File.open(gem_repo.join(filename)).read).map do |name, version, platform|
+ load_spec(name, version, platform, gem_repo)
+ end
+ end.flatten
+ end
+
+ specs.group_by(&:name).map do |name, versions|
+ gem_versions = versions.map do |spec|
+ deps = spec.dependencies.select {|d| d.type == :runtime }.map do |d|
+ reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ")
+ CompactIndex::Dependency.new(d.name, reqs)
+ end
+ checksum = begin
+ Digest(:SHA256).file("#{gem_repo}/gems/#{spec.original_name}.gem").base64digest
+ rescue StandardError
+ nil
+ end
+ CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
+ deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s)
+ end
+ CompactIndex::Gem.new(name, gem_versions)
+ end
+ end
+ end
+ end
+
+ get "/names" do
+ etag_response do
+ CompactIndex.names(gems.map(&:name))
+ end
+ end
+
+ get "/versions" do
+ etag_response do
+ file = tmp("versions.list")
+ FileUtils.rm_f(file)
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ file.contents
+ end
+ end
+
+ get "/info/:name" do
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/compact_index_extra.rb b/spec/bundler/support/artifice/helpers/compact_index_extra.rb
new file mode 100644
index 0000000000..9e742630dd
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/compact_index_extra.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require_relative "compact_index"
+
+class CompactIndexExtra < CompactIndexAPI
+ get "/extra/versions" do
+ halt 404
+ end
+
+ get "/extra/api/v1/dependencies" do
+ halt 404
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.binread("#{gem_repo2}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.binread("#{gem_repo2}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.binread("#{gem_repo2}/gems/#{params[:id]}")
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/compact_index_extra_api.rb b/spec/bundler/support/artifice/helpers/compact_index_extra_api.rb
new file mode 100644
index 0000000000..d9a7d83d23
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/compact_index_extra_api.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require_relative "compact_index"
+
+class CompactIndexExtraApi < CompactIndexAPI
+ get "/extra/names" do
+ etag_response do
+ CompactIndex.names(gems(gem_repo4).map(&:name))
+ end
+ end
+
+ get "/extra/versions" do
+ etag_response do
+ file = tmp("versions.list")
+ FileUtils.rm_f(file)
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems(gem_repo4))
+ file.contents
+ end
+ end
+
+ get "/extra/info/:name" do
+ etag_response do
+ gem = gems(gem_repo4).find {|g| g.name == params[:name] }
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.binread("#{gem_repo4}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.binread("#{gem_repo4}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.binread("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.binread("#{gem_repo4}/gems/#{params[:id]}")
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint.rb b/spec/bundler/support/artifice/helpers/endpoint.rb
new file mode 100644
index 0000000000..fc0381dc38
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/endpoint.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+require_relative "../../path"
+
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
+
+require "sinatra/base"
+
+ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant
+ALL_REQUESTS_MUTEX = Thread::Mutex.new
+
+at_exit do
+ if expected = ENV["BUNDLER_SPEC_ALL_REQUESTS"]
+ expected = expected.split("\n").sort
+ actual = ALL_REQUESTS.sort
+
+ unless expected == actual
+ raise "Unexpected requests!\nExpected:\n\t#{expected.join("\n\t")}\n\nActual:\n\t#{actual.join("\n\t")}"
+ end
+ end
+end
+
+class Endpoint < Sinatra::Base
+ def self.all_requests
+ @all_requests ||= []
+ end
+
+ set :raise_errors, true
+ set :show_exceptions, false
+
+ def call!(*)
+ super.tap do
+ ALL_REQUESTS_MUTEX.synchronize do
+ ALL_REQUESTS << @request.url
+ end
+ end
+ end
+
+ helpers do
+ include Spec::Path
+
+ def default_gem_repo
+ if ENV["BUNDLER_SPEC_GEM_REPO"]
+ Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"])
+ else
+ case request.host
+ when "gem.repo1"
+ Spec::Path.gem_repo1
+ when "gem.repo2"
+ Spec::Path.gem_repo2
+ when "gem.repo3"
+ Spec::Path.gem_repo3
+ when "gem.repo4"
+ Spec::Path.gem_repo4
+ else
+ Spec::Path.gem_repo1
+ end
+ end
+ end
+
+ def dependencies_for(gem_names, gem_repo = default_gem_repo)
+ return [] if gem_names.nil? || gem_names.empty?
+
+ all_specs = %w[specs.4.8 prerelease_specs.4.8].map do |filename|
+ Marshal.load(File.open(gem_repo.join(filename)).read)
+ end.inject(:+)
+
+ all_specs.map do |name, version, platform|
+ spec = load_spec(name, version, platform, gem_repo)
+ next unless gem_names.include?(spec.name)
+ {
+ :name => spec.name,
+ :number => spec.version.version,
+ :platform => spec.platform.to_s,
+ :dependencies => spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep|
+ [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")]
+ end,
+ }
+ end.compact
+ end
+
+ def load_spec(name, version, platform, gem_repo)
+ full_name = "#{name}-#{version}"
+ full_name += "-#{platform}" if platform != "ruby"
+ Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz"))))
+ end
+ end
+
+ get "/quick/Marshal.4.8/:id" do
+ redirect "/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/fetch/actual/gem/:id" do
+ File.binread("#{default_gem_repo}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/gems/:id" do
+ File.binread("#{default_gem_repo}/gems/#{params[:id]}")
+ end
+
+ get "/api/v1/dependencies" do
+ Marshal.dump(dependencies_for(params[:gems]))
+ end
+
+ get "/specs.4.8.gz" do
+ File.binread("#{default_gem_repo}/specs.4.8.gz")
+ end
+
+ get "/prerelease_specs.4.8.gz" do
+ File.binread("#{default_gem_repo}/prerelease_specs.4.8.gz")
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint_extra.rb b/spec/bundler/support/artifice/helpers/endpoint_extra.rb
new file mode 100644
index 0000000000..ad08495b50
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/endpoint_extra.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require_relative "endpoint"
+
+class EndpointExtra < Endpoint
+ get "/extra/api/v1/dependencies" do
+ halt 404
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.binread("#{gem_repo2}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.binread("#{gem_repo2}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.binread("#{gem_repo2}/gems/#{params[:id]}")
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint_fallback.rb b/spec/bundler/support/artifice/helpers/endpoint_fallback.rb
new file mode 100644
index 0000000000..a232930b67
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/endpoint_fallback.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require_relative "endpoint"
+
+class EndpointFallback < Endpoint
+ DEPENDENCY_LIMIT = 60
+
+ get "/api/v1/dependencies" do
+ if params[:gems] && params[:gems].size <= DEPENDENCY_LIMIT
+ Marshal.dump(dependencies_for(params[:gems]))
+ else
+ halt 413, "Too many gems to resolve, please request less than #{DEPENDENCY_LIMIT} gems"
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb b/spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb
new file mode 100644
index 0000000000..c409d39d99
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require_relative "endpoint_fallback"
+
+class EndpointMarshalFail < EndpointFallback
+ get "/api/v1/dependencies" do
+ "f0283y01hasf"
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/rack_request.rb b/spec/bundler/support/artifice/helpers/rack_request.rb
new file mode 100644
index 0000000000..c4a07812a6
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/rack_request.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require "rack/test"
+require "net/http"
+
+module Artifice
+ module Net
+ # This is an internal object that can receive Rack requests
+ # to the application using the Rack::Test API
+ class RackRequest
+ include Rack::Test::Methods
+ attr_reader :app
+
+ def initialize(app)
+ @app = app
+ end
+ end
+
+ class HTTP < ::Net::HTTP
+ class << self
+ attr_accessor :endpoint
+ end
+
+ # Net::HTTP uses a @newimpl instance variable to decide whether
+ # to use a legacy implementation. Since we are subclassing
+ # Net::HTTP, we must set it
+ @newimpl = true
+
+ # We don't need to connect, so blank out this method
+ def connect
+ end
+
+ # Replace the Net::HTTP request method with a method
+ # that converts the request into a Rack request and
+ # dispatches it to the Rack endpoint.
+ #
+ # @param [Net::HTTPRequest] req A Net::HTTPRequest
+ # object, or one if its subclasses
+ # @param [optional, String, #read] body This should
+ # be sent as "rack.input". If it's a String, it will
+ # be converted to a StringIO.
+ # @return [Net::HTTPResponse]
+ #
+ # @yield [Net::HTTPResponse] If a block is provided,
+ # this method will yield the Net::HTTPResponse to
+ # it after the body is read.
+ def request(req, body = nil, &block)
+ rack_request = RackRequest.new(self.class.endpoint)
+
+ req.each_header do |header, value|
+ rack_request.header(header, value)
+ end
+
+ scheme = use_ssl? ? "https" : "http"
+ prefix = "#{scheme}://#{addr_port}"
+ body_stream_contents = req.body_stream.read if req.body_stream
+
+ response = rack_request.request("#{prefix}#{req.path}",
+ { :method => req.method, :input => body || req.body || body_stream_contents })
+
+ make_net_http_response(response, &block)
+ end
+
+ private
+
+ # This method takes a Rack response and creates a Net::HTTPResponse
+ # Instead of trying to mock HTTPResponse directly, we just convert
+ # the Rack response into a String that looks like a normal HTTP
+ # response and call Net::HTTPResponse.read_new
+ #
+ # @param [Array(#to_i, Hash, #each)] response a Rack response
+ # @return [Net::HTTPResponse]
+ # @yield [Net::HTTPResponse] If a block is provided, yield the
+ # response to it after the body is read
+ def make_net_http_response(response)
+ status = response.status
+ headers = response.headers
+ body = response.body
+
+ response_string = []
+ response_string << "HTTP/1.1 #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}"
+
+ headers.each do |header, value|
+ response_string << "#{header}: #{value}"
+ end
+
+ response_string << "" << body
+
+ response_io = ::Net::BufferedIO.new(StringIO.new(response_string.join("\n")))
+ res = ::Net::HTTPResponse.read_new(response_io)
+
+ res.reading_body(response_io, true) do
+ yield res if block_given?
+ end
+
+ res
+ end
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/used_cassettes.txt b/spec/bundler/support/artifice/used_cassettes.txt
new file mode 100644
index 0000000000..a96efc790d
--- /dev/null
+++ b/spec/bundler/support/artifice/used_cassettes.txt
@@ -0,0 +1,20908 @@
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
diff --git a/spec/bundler/support/artifice/vcr.rb b/spec/bundler/support/artifice/vcr.rb
index ceb133346f..6a346f1ff9 100644
--- a/spec/bundler/support/artifice/vcr.rb
+++ b/spec/bundler/support/artifice/vcr.rb
@@ -4,6 +4,7 @@ require "net/http"
require_relative "../path"
CASSETTE_PATH = "#{Spec::Path.spec_dir}/support/artifice/vcr_cassettes"
+USED_CASSETTES_PATH = "#{Spec::Path.spec_dir}/support/artifice/used_cassettes.txt"
CASSETTE_NAME = ENV.fetch("BUNDLER_SPEC_VCR_CASSETTE_NAME") { "realworld" }
class BundlerVCRHTTP < Net::HTTP
@@ -22,6 +23,10 @@ class BundlerVCRHTTP < Net::HTTP
@__vcr_request_handler = handler
end
+ File.open(USED_CASSETTES_PATH, "a+") do |f|
+ f.puts request_pair_paths.map {|path| Pathname.new(path).relative_path_from(Spec::Path.source_root).to_s }.join("\n")
+ end
+
if recorded_response?
recorded_response
else
@@ -42,7 +47,7 @@ class BundlerVCRHTTP < Net::HTTP
response.uri = request.uri
response.reading_body(response_io, request.response_body_permitted?) do
- response_block.call(response) if response_block
+ response_block&.call(response)
end
end
end
@@ -74,25 +79,8 @@ class BundlerVCRHTTP < Net::HTTP
def request_pair_paths
%w[request response].map do |kind|
- File.join(CASSETTE_PATH, CASSETTE_NAME, file_name_for_key(key + [kind]))
- end
- end
-
- def read_stored_request(path)
- contents = File.binread(path)
- headers = {}
- method = nil
- path = nil
- contents.lines.grep(/^> /).each do |line|
- if line =~ /^> (GET|HEAD|POST|PATCH|PUT|DELETE) (.*)/
- method = $1
- path = $2.strip
- elsif line =~ /^> (.*?): (.*)/
- headers[$1] = $2
- end
+ File.join(CASSETTE_PATH, CASSETTE_NAME, file_name_for_key(key), kind)
end
- body = contents =~ /^([^>].*)/m && $1
- Net::HTTP.const_get(method.capitalize).new(path, headers).tap {|r| r.body = body if body }
end
def request_to_string(request)
@@ -158,8 +146,7 @@ class BundlerVCRHTTP < Net::HTTP
alias_method :request, :request_with_vcr
end
+require_relative "helpers/artifice"
+
# Replace Net::HTTP with our VCR subclass
-::Net.class_eval do
- remove_const(:HTTP)
- const_set(:HTTP, BundlerVCRHTTP)
-end
+Artifice.replace_net_http(BundlerVCRHTTP)
diff --git a/spec/bundler/support/artifice/windows.rb b/spec/bundler/support/artifice/windows.rb
index 674470688d..4d90e0a426 100644
--- a/spec/bundler/support/artifice/windows.rb
+++ b/spec/bundler/support/artifice/windows.rb
@@ -2,13 +2,10 @@
require_relative "../path"
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{artifice,mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
-require "artifice"
require "sinatra/base"
-Artifice.deactivate
-
class Windows < Sinatra::Base
set :raise_errors, true
set :show_exceptions, false
@@ -43,4 +40,6 @@ class Windows < Sinatra::Base
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(Windows)
diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb
index 285b68c047..dfc7139523 100644
--- a/spec/bundler/support/builders.rb
+++ b/spec/bundler/support/builders.rb
@@ -17,18 +17,6 @@ module Spec
Gem::Platform.new(platform)
end
- # Returns a number smaller than the size of the index. Useful for specs that
- # need the API request limit to be reached for some reason.
- def low_api_request_limit_for(gem_repo)
- all_gems = Dir[gem_repo.join("gems/*.gem")]
-
- all_gem_names = all_gems.map do |file|
- File.basename(file, ".gem").match(/\A(?<gem_name>[^-]+)-.*\z/)[:gem_name]
- end.uniq
-
- (all_gem_names - ["bundler"]).size
- end
-
def build_repo1
rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first
@@ -94,8 +82,8 @@ module Spec
end
build_gem "platform_specific" do |s|
- s.platform = Bundler.local_platform
- s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 #{Bundler.local_platform}'"
+ s.platform = Gem::Platform.local
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 #{Gem::Platform.local}'"
end
build_gem "platform_specific" do |s|
@@ -110,19 +98,27 @@ module Spec
build_gem "platform_specific" do |s|
s.platform = "x86-mswin32"
- s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 MSWIN'"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x86-mswin32'"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "x64-mswin64"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x64-mswin64'"
end
build_gem "platform_specific" do |s|
s.platform = "x86-mingw32"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x86-mingw32'"
end
build_gem "platform_specific" do |s|
s.platform = "x64-mingw32"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x64-mingw32'"
end
build_gem "platform_specific" do |s|
s.platform = "x64-mingw-ucrt"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x64-mingw-ucrt'"
end
build_gem "platform_specific" do |s|
@@ -212,10 +208,8 @@ module Spec
update_repo(gem_repo4, &blk)
end
- def update_repo2
- update_repo gem_repo2 do
- yield if block_given?
- end
+ def update_repo2(&blk)
+ update_repo(gem_repo2, &blk)
end
def build_security_repo
@@ -290,10 +284,6 @@ module Spec
end
end
- def build_dep(name, requirements = Gem::Requirement.default, type = :runtime)
- Bundler::Dependency.new(name, :version => requirements)
- end
-
def build_lib(name, *args, &blk)
build_with(LibBuilder, name, args, &blk)
end
@@ -448,7 +438,7 @@ module Spec
write "ext/extconf.rb", <<-RUBY
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"] unless RUBY_VERSION < "2.4"
+ $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
extension_name = "#{name}_c"
if extra_lib_dir = with_config("ext-lib")
@@ -528,7 +518,7 @@ module Spec
class GitBuilder < LibBuilder
def _build(options)
- default_branch = options[:default_branch] || "master"
+ default_branch = options[:default_branch] || "main"
path = options[:path] || _default_path
source = options[:source] || "git@#{path}"
super(options.merge(:path => path, :source => source))
diff --git a/spec/bundler/support/bundle.rb b/spec/bundler/support/bundle.rb
index bb21526d35..5f808531ff 100644
--- a/spec/bundler/support/bundle.rb
+++ b/spec/bundler/support/bundle.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
require "rubygems"
+Gem.instance_variable_set(:@ruby, ENV["RUBY"]) if ENV["RUBY"]
+
require_relative "path"
bundler_gemspec = Spec::Path.loaded_gemspec
bundler_gemspec.instance_variable_set(:@full_gem_path, Spec::Path.source_root)
diff --git a/spec/bundler/support/filters.rb b/spec/bundler/support/filters.rb
index c3b7a425ae..78545d2e64 100644
--- a/spec/bundler/support/filters.rb
+++ b/spec/bundler/support/filters.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require_relative "sudo"
-
class RequirementChecker < Proc
def self.against(present)
provided = Gem::Version.new(present)
@@ -21,10 +19,9 @@ class RequirementChecker < Proc
end
RSpec.configure do |config|
- config.filter_run_excluding :sudo => true
config.filter_run_excluding :realworld => true
- git_version = Bundler::Source::Git::GitProxy.new(nil, nil, nil).version
+ git_version = Bundler::Source::Git::GitProxy.new(nil, nil).version
config.filter_run_excluding :git => RequirementChecker.against(git_version)
config.filter_run_excluding :bundler => RequirementChecker.against(Bundler::VERSION.split(".")[0])
diff --git a/spec/bundler/support/hax.rb b/spec/bundler/support/hax.rb
index da67e8c5d1..c7fe3637cc 100644
--- a/spec/bundler/support/hax.rb
+++ b/spec/bundler/support/hax.rb
@@ -1,5 +1,10 @@
# frozen_string_literal: true
+if ENV["BUNDLER_SPEC_RUBY_PLATFORM"]
+ Object.send(:remove_const, :RUBY_PLATFORM)
+ RUBY_PLATFORM = ENV["BUNDLER_SPEC_RUBY_PLATFORM"]
+end
+
module Gem
def self.ruby=(ruby)
@ruby = ruby
@@ -19,20 +24,30 @@ module Gem
end
if ENV["BUNDLER_SPEC_PLATFORM"]
+ previous_platforms = @platforms
+ previous_local = Platform.local
+
class Platform
@local = new(ENV["BUNDLER_SPEC_PLATFORM"])
end
- @platforms = [Gem::Platform::RUBY, Gem::Platform.local]
+ @platforms = previous_platforms.map {|platform| platform == previous_local ? Platform.local : platform }
end
if ENV["BUNDLER_SPEC_GEM_SOURCES"]
self.sources = [ENV["BUNDLER_SPEC_GEM_SOURCES"]]
end
- # We only need this hack for rubygems versions without the BundlerVersionFinder
- if Gem.rubygems_version < Gem::Version.new("2.7.0")
- @path_to_default_spec_map.delete_if do |_path, spec|
- spec.name == "bundler"
+ if ENV["BUNDLER_IGNORE_DEFAULT_GEM"]
+ module RemoveDefaultBundlerStub
+ def default_stubs(pattern = "*")
+ super.delete_if {|stub| stub.name == "bundler" }
+ end
+ end
+
+ class Specification
+ class << self
+ prepend RemoveDefaultBundlerStub
+ end
end
end
end
diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb
index 03c0df3b50..7b8c56b6ad 100644
--- a/spec/bundler/support/helpers.rb
+++ b/spec/bundler/support/helpers.rb
@@ -78,9 +78,6 @@ module Spec
end
def bundle(cmd, options = {}, &block)
- with_sudo = options.delete(:sudo)
- sudo = with_sudo == :preserve_env ? "sudo -E --preserve-env=RUBYOPT" : "sudo" if with_sudo
-
bundle_bin = options.delete(:bundle_bin)
bundle_bin ||= installed_bindir.join("bundle")
@@ -119,7 +116,7 @@ module Spec
end
end.join
- ruby_cmd = build_ruby_cmd({ :sudo => sudo, :load_path => load_path, :requires => requires })
+ ruby_cmd = build_ruby_cmd({ :load_path => load_path, :requires => requires })
cmd = "#{ruby_cmd} #{bundle_bin} #{cmd}#{args}"
sys_exec(cmd, { :env => env, :dir => dir, :raise_on_error => raise_on_error }, &block)
end
@@ -146,8 +143,6 @@ module Spec
end
def build_ruby_cmd(options = {})
- sudo = options.delete(:sudo)
-
libs = options.delete(:load_path)
lib_option = libs ? "-I#{libs.join(File::PATH_SEPARATOR)}" : []
@@ -155,7 +150,7 @@ module Spec
requires << "#{Path.spec_dir}/support/hax.rb"
require_option = requires.map {|r| "-r#{r}" }
- [sudo, Gem.ruby, *lib_option, *require_option].compact.join(" ")
+ [Gem.ruby, *lib_option, *require_option].compact.join(" ")
end
def gembin(cmd, options = {})
@@ -295,7 +290,7 @@ module Spec
if gem_name.start_with?("bundler")
version = gem_name.match(/\Abundler-(?<version>.*)\z/)[:version] if gem_name != "bundler"
with_built_bundler(version) {|gem_path| install_gem(gem_path, default) }
- elsif gem_name =~ %r{\A(?:[a-zA-Z]:)?/.*\.gem\z}
+ elsif %r{\A(?:[a-zA-Z]:)?/.*\.gem\z}.match?(gem_name)
install_gem(gem_name, default)
else
install_gem("#{gem_repo}/gems/#{gem_name}.gem", default)
@@ -307,7 +302,7 @@ module Spec
def install_gem(path, default = false)
raise "OMG `#{path}` does not exist!" unless File.exist?(path)
- args = "--no-document --ignore-dependencies"
+ args = "--no-document --ignore-dependencies --verbose --local"
args += " --default --install-dir #{system_gem_path}" if default
gem_command "install #{args} '#{path}'"
@@ -419,14 +414,14 @@ module Spec
end
end
- def cache_gems(*gems)
+ def cache_gems(*gems, gem_repo: gem_repo1)
gems = gems.flatten
FileUtils.rm_rf("#{bundled_app}/vendor/cache")
FileUtils.mkdir_p("#{bundled_app}/vendor/cache")
gems.each do |g|
- path = "#{gem_repo1}/gems/#{g}.gem"
+ path = "#{gem_repo}/gems/#{g}.gem"
raise "OMG `#{path}` does not exist!" unless File.exist?(path)
FileUtils.cp(path, "#{bundled_app}/vendor/cache")
end
@@ -437,6 +432,14 @@ module Spec
pristine_system_gems :bundler
end
+ def simulate_ruby_platform(ruby_platform)
+ old = ENV["BUNDLER_SPEC_RUBY_PLATFORM"]
+ ENV["BUNDLER_SPEC_RUBY_PLATFORM"] = ruby_platform.to_s
+ yield
+ ensure
+ ENV["BUNDLER_SPEC_RUBY_PLATFORM"] = old
+ end
+
def simulate_platform(platform)
old = ENV["BUNDLER_SPEC_PLATFORM"]
ENV["BUNDLER_SPEC_PLATFORM"] = platform.to_s
@@ -445,7 +448,7 @@ module Spec
ENV["BUNDLER_SPEC_PLATFORM"] = old if block_given?
end
- def simulate_windows(platform = mswin)
+ def simulate_windows(platform = x86_mswin32)
old = ENV["BUNDLER_SPEC_WINDOWS"]
ENV["BUNDLER_SPEC_WINDOWS"] = "true"
simulate_platform platform do
@@ -476,17 +479,27 @@ module Spec
end
def current_ruby_minor
- Gem.ruby_version.segments[0..1].join(".")
+ Gem.ruby_version.segments.tap {|s| s.delete_at(2) }.join(".")
end
def next_ruby_minor
- Gem.ruby_version.segments[0..1].map.with_index {|s, i| i == 1 ? s + 1 : s }.join(".")
+ ruby_major_minor.map.with_index {|s, i| i == 1 ? s + 1 : s }.join(".")
+ end
+
+ def previous_ruby_minor
+ return "2.7" if ruby_major_minor == [3, 0]
+
+ ruby_major_minor.map.with_index {|s, i| i == 1 ? s - 1 : s }.join(".")
+ end
+
+ def ruby_major_minor
+ Gem.ruby_version.segments[0..1]
end
- # versions providing a bundler version finder but not including
+ # versions not including
# https://github.com/rubygems/rubygems/commit/929e92d752baad3a08f3ac92eaec162cb96aedd1
def rubygems_version_failing_to_activate_bundler_prereleases
- Gem.rubygems_version < Gem::Version.new("3.1.0.pre.1") && Gem.rubygems_version >= Gem::Version.new("2.7.0")
+ Gem.rubygems_version < Gem::Version.new("3.1.0.pre.1")
end
def revision_for(path)
diff --git a/spec/bundler/support/indexes.rb b/spec/bundler/support/indexes.rb
index 638f394e76..78372302f1 100644
--- a/spec/bundler/support/indexes.rb
+++ b/spec/bundler/support/indexes.rb
@@ -16,20 +16,23 @@ module Spec
def resolve(args = [])
@platforms ||= ["ruby"]
- deps = []
default_source = instance_double("Bundler::Source::Rubygems", :specs => @index, :to_s => "locally install gems")
source_requirements = { :default => default_source }
+ base = args[0] || Bundler::SpecSet.new([])
+ base.each {|ls| ls.source = default_source }
+ gem_version_promoter = args[1] || Bundler::GemVersionPromoter.new
+ originally_locked = args[2] || Bundler::SpecSet.new([])
+ unlock = args[3] || []
@deps.each do |d|
- source_requirements[d.name] = d.source = default_source
- @platforms.each do |p|
- deps << Bundler::DepProxy.get_proxy(d, p)
- end
+ name = d.name
+ source_requirements[name] = d.source = default_source
end
- args[0] ||= [] # base
- args[1] ||= Bundler::GemVersionPromoter.new # gem_version_promoter
- args[2] ||= [] # additional_base_requirements
- args[3] ||= @platforms # platforms
- Bundler::Resolver.resolve(deps, source_requirements, *args)
+ packages = Bundler::Resolver::Base.new(source_requirements, @deps, base, @platforms, :locked_specs => originally_locked, :unlock => unlock)
+ Bundler::Resolver.new(packages, gem_version_promoter).start
+ end
+
+ def should_not_resolve
+ expect { resolve }.to raise_error(Bundler::GemNotFound)
end
def should_resolve_as(specs)
@@ -46,13 +49,6 @@ module Spec
end
end
- def should_conflict_on(names)
- got = resolve
- raise "The resolve succeeded with: #{got.map(&:full_name).sort.inspect}"
- rescue Bundler::VersionConflict => e
- expect(Array(names).sort).to eq(e.conflicts.sort)
- end
-
def gem(*args, &blk)
build_spec(*args, &blk).first
end
@@ -66,12 +62,11 @@ module Spec
def should_conservative_resolve_and_include(opts, unlock, specs)
# empty unlock means unlock all
opts = Array(opts)
- search = Bundler::GemVersionPromoter.new(@locked, unlock).tap do |s|
+ search = Bundler::GemVersionPromoter.new.tap do |s|
s.level = opts.first
s.strict = opts.include?(:strict)
- s.prerelease_specified = Hash[@deps.map {|d| [d.name, d.requirement.prerelease?] }]
end
- should_resolve_and_include specs, [@base, search]
+ should_resolve_and_include specs, [@base, search, @locked, unlock]
end
def an_awesome_index
@@ -126,7 +121,7 @@ module Spec
next if version == v("1.4.2.1") && platform != pl("x86-mswin32")
next if version == v("1.4.2") && platform == pl("x86-mswin32")
gem "nokogiri", version, platform do
- dep "weakling", ">= 0.0.3" if platform =~ pl("java")
+ dep "weakling", ">= 0.0.3" if platform =~ pl("java") # rubocop:disable Performance/RegexpMatch
end
end
end
diff --git a/spec/bundler/support/matchers.rb b/spec/bundler/support/matchers.rb
index ce6b216619..ea7c784683 100644
--- a/spec/bundler/support/matchers.rb
+++ b/spec/bundler/support/matchers.rb
@@ -97,6 +97,18 @@ module Spec
end
end
+ RSpec::Matchers.define :take_less_than do |seconds|
+ match do |actual|
+ start_time = Time.now
+
+ actual.call
+
+ (Time.now - start_time).to_f < seconds
+ end
+
+ supports_block_expectations
+ end
+
define_compound_matcher :read_as, [exist] do |file_contents|
diffable
@@ -150,7 +162,7 @@ module Spec
end
if exitstatus == 65
actual_platform = out.split("\n").last
- next "#{name} was expected to be of platform #{platform} but was #{actual_platform}"
+ next "#{name} was expected to be of platform #{platform || "ruby"} but was #{actual_platform || "ruby"}"
end
if exitstatus == 66
actual_source = out.split("\n").last
diff --git a/spec/bundler/support/path.rb b/spec/bundler/support/path.rb
index a39e46c78a..8b9c0e1290 100644
--- a/spec/bundler/support/path.rb
+++ b/spec/bundler/support/path.rb
@@ -71,10 +71,6 @@ module Spec
@spec_dir ||= source_root.join(ruby_core? ? "spec/bundler" : "spec")
end
- def api_request_limit_hack_file
- spec_dir.join("support/api_request_limit_hax.rb")
- end
-
def man_dir
@man_dir ||= lib_dir.join("bundler/man")
end
@@ -118,6 +114,14 @@ module Spec
end
end
+ def default_cache_path(*path)
+ if Bundler.feature_flag.global_gem_cache?
+ home(".bundle/cache", *path)
+ else
+ default_bundle_path("cache/bundler", *path)
+ end
+ end
+
def bundled_app(*path)
root = tmp.join("bundled_app")
FileUtils.mkdir_p(root)
@@ -287,29 +291,19 @@ module Spec
end
def rubocop_gemfile_basename
- filename = if RUBY_VERSION.start_with?("2.3")
- "rubocop23_gems"
- elsif RUBY_VERSION.start_with?("2.4")
- "rubocop24_gems"
- else
- "rubocop_gems"
- end
- tool_dir.join("#{filename}.rb")
+ tool_dir.join("rubocop_gems.rb")
end
def standard_gemfile_basename
- filename = if RUBY_VERSION.start_with?("2.3")
- "standard23_gems"
- elsif RUBY_VERSION.start_with?("2.4")
- "standard24_gems"
- else
- "standard_gems"
- end
- tool_dir.join("#{filename}.rb")
+ tool_dir.join("standard_gems.rb")
end
def tool_dir
- source_root.join("tool/bundler")
+ ruby_core? ? source_root.join("tool/bundler") : source_root.join("../tool/bundler")
+ end
+
+ def templates_dir
+ lib_dir.join("bundler", "templates")
end
extend self
diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb
index 48479723e4..eca1b2e60d 100644
--- a/spec/bundler/support/platforms.rb
+++ b/spec/bundler/support/platforms.rb
@@ -21,31 +21,35 @@ module Spec
end
def linux
- Gem::Platform.new(["x86", "linux", nil])
+ Gem::Platform.new("x86_64-linux")
end
- def mswin
+ def x86_mswin32
Gem::Platform.new(["x86", "mswin32", nil])
end
- def mingw
+ def x64_mswin64
+ Gem::Platform.new(["x64", "mswin64", nil])
+ end
+
+ def x86_mingw32
Gem::Platform.new(["x86", "mingw32", nil])
end
- def x64_mingw
+ def x64_mingw32
Gem::Platform.new(["x64", "mingw32", nil])
end
- def all_platforms
- [rb, java, linux, mswin, mingw, x64_mingw]
+ def x64_mingw_ucrt
+ Gem::Platform.new(["x64", "mingw", "ucrt"])
end
- def local
- generic_local_platform
+ def windows_platforms
+ [x86_mswin32, x64_mswin64, x86_mingw32, x64_mingw32, x64_mingw_ucrt]
end
- def specific_local_platform
- Bundler.local_platform
+ def all_platforms
+ [rb, java, linux, windows_platforms].flatten
end
def not_local
@@ -56,14 +60,14 @@ module Spec
if RUBY_PLATFORM == "java"
:jruby
elsif ["x64-mingw32", "x64-mingw-ucrt"].include?(RUBY_PLATFORM)
- :x64_mingw
+ :windows
else
:ruby
end
end
def not_local_tag
- [:jruby, :x64_mingw, :ruby].find {|tag| tag != local_tag }
+ [:jruby, :windows, :ruby].find {|tag| tag != local_tag }
end
def local_ruby_engine
@@ -71,12 +75,12 @@ module Spec
end
def local_engine_version
- RUBY_ENGINE_VERSION
+ RUBY_ENGINE == "ruby" ? Gem.ruby_version : RUBY_ENGINE_VERSION
end
def not_local_engine_version
case not_local_tag
- when :ruby, :x64_mingw
+ when :ruby, :windows
not_local_ruby_version
when :jruby
"1.6.1"
@@ -91,11 +95,11 @@ module Spec
9999
end
- def lockfile_platforms
- lockfile_platforms_for([specific_local_platform])
+ def lockfile_platforms(*extra)
+ formatted_lockfile_platforms(local_platform, *extra)
end
- def lockfile_platforms_for(platforms)
+ def formatted_lockfile_platforms(*platforms)
platforms.map(&:to_s).sort.join("\n ")
end
end
diff --git a/spec/bundler/support/rubygems_version_manager.rb b/spec/bundler/support/rubygems_version_manager.rb
index d1b1f8dd03..5653601ae8 100644
--- a/spec/bundler/support/rubygems_version_manager.rb
+++ b/spec/bundler/support/rubygems_version_manager.rb
@@ -113,7 +113,7 @@ class RubygemsVersionManager
end
def resolve_target_tag
- return "v#{@source}" if @source.match(/^\d/)
+ return "v#{@source}" if @source.match?(/^\d/)
@source
end
diff --git a/spec/bundler/support/sudo.rb b/spec/bundler/support/sudo.rb
deleted file mode 100644
index 7b9b392754..0000000000
--- a/spec/bundler/support/sudo.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-module Spec
- module Sudo
- def self.present?
- @which_sudo ||= Bundler.which("sudo")
- end
-
- def self.write_safe_config
- File.write(Spec::Path.tmp("gitconfig"), "[safe]\n\tdirectory = #{Spec::Path.git_root}")
- end
-
- def sudo(cmd)
- raise "sudo not present" unless Sudo.present?
- sys_exec("sudo #{cmd}")
- end
-
- def chown_system_gems_to_root
- sudo "chown -R root #{system_gem_path}"
- end
- end
-end
diff --git a/spec/bundler/update/gems/fund_spec.rb b/spec/bundler/update/gems/fund_spec.rb
index 0dfe63d36d..d80f4018f3 100644
--- a/spec/bundler/update/gems/fund_spec.rb
+++ b/spec/bundler/update/gems/fund_spec.rb
@@ -5,20 +5,20 @@ RSpec.describe "bundle update" do
build_repo2 do
build_gem "has_funding_and_other_metadata" do |s|
s.metadata = {
- "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
- "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
"documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
- "homepage_uri" => "https://bestgemever.example.io",
- "mailing_list_uri" => "https://groups.example.com/bestgemever",
- "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
- "source_code_uri" => "https://example.com/user/bestgemever",
- "wiki_uri" => "https://example.com/user/bestgemever/wiki",
+ "homepage_uri" => "https://bestgemever.example.io",
+ "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
+ "source_code_uri" => "https://example.com/user/bestgemever",
+ "wiki_uri" => "https://example.com/user/bestgemever/wiki",
}
end
build_gem "has_funding", "1.2.3" do |s|
s.metadata = {
- "funding_uri" => "https://example.com/has_funding/funding",
+ "funding_uri" => "https://example.com/has_funding/funding",
}
end
end
diff --git a/spec/bundler/update/git_spec.rb b/spec/bundler/update/git_spec.rb
index 0787ee41a7..59e3d2f5fb 100644
--- a/spec/bundler/update/git_spec.rb
+++ b/spec/bundler/update/git_spec.rb
@@ -57,7 +57,7 @@ RSpec.describe "bundle update" do
expect(the_bundle).to include_gems "foo 1.1"
end
- it "floats on master when updating all gems that are pinned to the source even if you have child dependencies" do
+ it "floats on main when updating all gems that are pinned to the source even if you have child dependencies" do
build_git "foo", :path => lib_path("foo")
build_gem "bar", :to_bundle => true do |s|
s.add_dependency "foo"
@@ -103,7 +103,7 @@ RSpec.describe "bundle update" do
build_git "foo"
@remote = build_git("bar", :bare => true)
update_git "foo", :remote => file_uri_for(@remote.path)
- update_git "foo", :push => "master"
+ update_git "foo", :push => "main"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -120,10 +120,14 @@ RSpec.describe "bundle update" do
G
bundle "update", :all => true
+ expect(err).to be_empty
end
describe "with submodules" do
before :each do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_repo4 do
build_gem "submodule" do |s|
s.write "lib/submodule.rb", "puts 'GEM'"
@@ -211,7 +215,7 @@ RSpec.describe "bundle update" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{file_uri_for(lib_path("rack-0.8"))}", :branch => "master"
+ gem "rack", :git => "#{file_uri_for(lib_path("rack-0.8"))}", :branch => "main"
G
bundle %(config set local.rack #{lib_path("local-rack")})
@@ -230,7 +234,7 @@ RSpec.describe "bundle update" do
update_git "rails", "3.0", :path => lib_path("rails"), :gemspec => true
bundle "update", :all => true
- expect(out).to include("Using rails 3.0 (was 2.3.2) from #{file_uri_for(lib_path("rails"))} (at master@#{revision_for(lib_path("rails"))[0..6]})")
+ expect(out).to include("Using rails 3.0 (was 2.3.2) from #{file_uri_for(lib_path("rails"))} (at main@#{revision_for(lib_path("rails"))[0..6]})")
end
end
@@ -301,7 +305,7 @@ RSpec.describe "bundle update" do
s.write "foo.gemspec", spec_lines.join("\n")
end
- ref = @git.ref_for "master"
+ ref = @git.ref_for "main"
bundle "update --source bar"
diff --git a/spec/mspec/lib/mspec/commands/mkspec.rb b/spec/mspec/lib/mspec/commands/mkspec.rb
index d10cc35d18..a31cb2191c 100755
--- a/spec/mspec/lib/mspec/commands/mkspec.rb
+++ b/spec/mspec/lib/mspec/commands/mkspec.rb
@@ -95,7 +95,9 @@ class MkSpec
def write_spec(file, meth, exists)
if exists
- out = `#{ruby} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e '#{meth}' #{file}`
+ command = "#{RbConfig.ruby} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e '#{meth}' #{file}"
+ puts "$ #{command}" if $DEBUG
+ out = `#{command}`
return if out.include?(meth)
end
@@ -133,18 +135,6 @@ EOS
end
end
- ##
- # Determine and return the path of the ruby executable.
-
- def ruby
- ruby = File.join(RbConfig::CONFIG['bindir'],
- RbConfig::CONFIG['ruby_install_name'])
-
- ruby.gsub! File::SEPARATOR, File::ALT_SEPARATOR if File::ALT_SEPARATOR
-
- return ruby
- end
-
def self.main
ENV['MSPEC_RUNNER'] = '1'
diff --git a/spec/mspec/lib/mspec/guards/superuser.rb b/spec/mspec/lib/mspec/guards/superuser.rb
index e92ea7e862..24daf9b26c 100644
--- a/spec/mspec/lib/mspec/guards/superuser.rb
+++ b/spec/mspec/lib/mspec/guards/superuser.rb
@@ -6,10 +6,20 @@ class SuperUserGuard < SpecGuard
end
end
+class RealSuperUserGuard < SpecGuard
+ def match?
+ Process.uid == 0
+ end
+end
+
def as_superuser(&block)
SuperUserGuard.new.run_if(:as_superuser, &block)
end
+def as_real_superuser(&block)
+ RealSuperUserGuard.new.run_if(:as_real_superuser, &block)
+end
+
def as_user(&block)
SuperUserGuard.new.run_unless(:as_user, &block)
end
diff --git a/spec/mspec/lib/mspec/runner/actions/leakchecker.rb b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
index 596b120d9f..69181b71d3 100644
--- a/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
+++ b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
@@ -173,7 +173,8 @@ class LeakChecker
def find_threads
Thread.list.find_all {|t|
- t != Thread.current && t.alive?
+ t != Thread.current && t.alive? &&
+ !(t.thread_variable?(:"\0__detached_thread__") && t.thread_variable_get(:"\0__detached_thread__"))
}
end
diff --git a/spec/mspec/lib/mspec/utils/name_map.rb b/spec/mspec/lib/mspec/utils/name_map.rb
index a389b9d1de..bf70e651a2 100644
--- a/spec/mspec/lib/mspec/utils/name_map.rb
+++ b/spec/mspec/lib/mspec/utils/name_map.rb
@@ -51,6 +51,10 @@ class NameMap
SpecVersion
]
+ ALWAYS_PRIVATE = %w[
+ initialize initialize_copy initialize_clone initialize_dup respond_to_missing?
+ ].map(&:to_sym)
+
def initialize(filter = false)
@seen = {}
@filter = filter
@@ -86,7 +90,8 @@ class NameMap
hash["#{name}."] = ms.sort unless ms.empty?
ms = m.public_instance_methods(false) +
- m.protected_instance_methods(false)
+ m.protected_instance_methods(false) +
+ (m.private_instance_methods(false) & ALWAYS_PRIVATE)
ms.map! { |x| x.to_s }
hash["#{name}#"] = ms.sort unless ms.empty?
diff --git a/spec/mspec/lib/mspec/utils/script.rb b/spec/mspec/lib/mspec/utils/script.rb
index b9f8b17fdc..e86beaab86 100644
--- a/spec/mspec/lib/mspec/utils/script.rb
+++ b/spec/mspec/lib/mspec/utils/script.rb
@@ -283,7 +283,6 @@ class MSpecScript
script = new
script.load_default
- script.try_load '~/.mspecrc'
script.options
script.signals
script.register
diff --git a/spec/mspec/spec/commands/mkspec_spec.rb b/spec/mspec/spec/commands/mkspec_spec.rb
index 825add7212..32262723de 100644
--- a/spec/mspec/spec/commands/mkspec_spec.rb
+++ b/spec/mspec/spec/commands/mkspec_spec.rb
@@ -194,7 +194,7 @@ RSpec.describe MkSpec, "#write_spec" do
end
it "checks if specs exist for the method if the spec file exists" do
- name = Regexp.escape(@script.ruby)
+ name = Regexp.escape(RbConfig.ruby)
expect(@script).to receive(:`).with(
%r"#{name} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e 'Object#inspect' spec/core/tcejbo/inspect_spec.rb")
@script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
diff --git a/spec/mspec/spec/utils/script_spec.rb b/spec/mspec/spec/utils/script_spec.rb
index d9f6eac9a9..c35bda8b47 100644
--- a/spec/mspec/spec/utils/script_spec.rb
+++ b/spec/mspec/spec/utils/script_spec.rb
@@ -96,11 +96,6 @@ RSpec.describe MSpecScript, ".main" do
MSpecScript.main
end
- it "attempts to load the '~/.mspecrc' script" do
- expect(@script).to receive(:try_load).with('~/.mspecrc')
- MSpecScript.main
- end
-
it "calls the #options method on the script" do
expect(@script).to receive(:options)
MSpecScript.main
diff --git a/spec/mspec/tool/tag_from_output.rb b/spec/mspec/tool/tag_from_output.rb
index ebe13434c2..a6e60945cd 100755
--- a/spec/mspec/tool/tag_from_output.rb
+++ b/spec/mspec/tool/tag_from_output.rb
@@ -11,6 +11,11 @@ abort 'Could not find tags directory' unless tags_dir
output = ARGF.readlines
+# Automatically strip datetime of GitHub Actions
+if output.first =~ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+Z /
+ output = output.map { |line| line.split(' ', 2).last }
+end
+
NUMBER = /^\d+\)$/
ERROR_OR_FAILED = / (ERROR|FAILED)$/
SPEC_FILE = /^(\/.+_spec\.rb)\:\d+/
@@ -22,11 +27,24 @@ output.slice_before(NUMBER).select { |number, *rest|
description = error_line.match(ERROR_OR_FAILED).pre_match
spec_file = rest.find { |line| line =~ SPEC_FILE }
- unless spec_file
- warn "Could not find file for:\n#{error_line}"
- next
+ if spec_file
+ spec_file = spec_file[SPEC_FILE, 1] or raise
+ else
+ if error_line =~ /^(\w+)[#\.](\w+) /
+ module_method = error_line.split(' ', 2).first
+ file = "#{$1.downcase}/#{$2}_spec.rb"
+ spec_file = ['spec/ruby/core', 'spec/ruby/library', *Dir.glob('spec/ruby/library/*')].find { |dir|
+ path = "#{dir}/#{file}"
+ break path if File.exist?(path)
+ }
+ end
+
+ unless spec_file
+ warn "Could not find file for:\n#{error_line}"
+ next
+ end
end
- spec_file = spec_file[SPEC_FILE, 1]
+
prefix = spec_file.index('spec/ruby/') || spec_file.index('spec/truffle/')
spec_file = spec_file[prefix..-1]
diff --git a/spec/ruby/.rubocop.yml b/spec/ruby/.rubocop.yml
index 1200e9d7ce..82733c4b4d 100644
--- a/spec/ruby/.rubocop.yml
+++ b/spec/ruby/.rubocop.yml
@@ -115,6 +115,10 @@ Lint/EmptyWhen:
- language/case_spec.rb
- optional/capi/spec_helper.rb
+Lint/ErbNewArguments:
+ Exclude:
+ - 'library/erb/new_spec.rb'
+
Lint/FormatParameterMismatch:
Exclude:
- 'core/kernel/shared/sprintf.rb'
@@ -161,6 +165,9 @@ Lint/Debugger:
Lint/Loop:
Enabled: false
+Style/BlockComments:
+ Enabled: true
+
Style/Lambda:
Enabled: true
EnforcedStyle: literal
diff --git a/spec/ruby/CONTRIBUTING.md b/spec/ruby/CONTRIBUTING.md
index 20258e5c36..adfc2fb0ca 100644
--- a/spec/ruby/CONTRIBUTING.md
+++ b/spec/ruby/CONTRIBUTING.md
@@ -175,9 +175,10 @@ end
#### Guard for bug
-In case there is a bug in MRI but the expected behavior is obvious.
+In case there is a bug in MRI and the fix will be backported to previous versions.
+If it is not backported or not likely, use `ruby_version_is` instead.
First, file a bug at https://bugs.ruby-lang.org/.
-It is better to use a `ruby_version_is` guard if there was a release with the fix.
+The problem is `ruby_bug` would make non-MRI implementations fail this spec while MRI itself does not pass it, so it should only be used if the bug is/will be fixed and backported.
```ruby
ruby_bug '#13669', ''...'3.2' do
diff --git a/spec/ruby/README.md b/spec/ruby/README.md
index 55b248a6c3..018bf0ca3e 100644
--- a/spec/ruby/README.md
+++ b/spec/ruby/README.md
@@ -1,7 +1,6 @@
# The Ruby Spec Suite
[![Actions Build Status](https://github.com/ruby/spec/workflows/CI/badge.svg)](https://github.com/ruby/spec/actions)
-[![Gitter](https://badges.gitter.im/ruby/spec.svg)](https://gitter.im/ruby/spec)
The Ruby Spec Suite, abbreviated `ruby/spec`, is a test suite for the behavior of the Ruby programming language.
@@ -144,10 +143,9 @@ The file `/etc/services` is required for socket specs (package `netbase` on Debi
### Socket specs from rubysl-socket
-Most specs under `library/socket` were imported from [the rubysl-socket project](https://github.com/rubysl/rubysl-socket).
+Most specs under `library/socket` were imported from the rubysl-socket project (which is no longer on GitHub).
The 3 copyright holders of rubysl-socket, Yorick Peterse, Chuck Remes and
-Brian Shirai, [agreed to relicense those specs](https://github.com/rubysl/rubysl-socket/issues/15)
-under the MIT license in ruby/spec.
+Brian Shirai, agreed to relicense those specs under the MIT license in ruby/spec.
### History and RubySpec
diff --git a/spec/ruby/core/array/keep_if_spec.rb b/spec/ruby/core/array/keep_if_spec.rb
index bf2bdeaf91..40f7329b7c 100644
--- a/spec/ruby/core/array/keep_if_spec.rb
+++ b/spec/ruby/core/array/keep_if_spec.rb
@@ -1,3 +1,4 @@
+require_relative '../../spec_helper'
require_relative 'shared/keep_if'
describe "Array#keep_if" do
diff --git a/spec/ruby/core/array/pack/c_spec.rb b/spec/ruby/core/array/pack/c_spec.rb
index 7200830331..be03551629 100644
--- a/spec/ruby/core/array/pack/c_spec.rb
+++ b/spec/ruby/core/array/pack/c_spec.rb
@@ -45,8 +45,18 @@ describe :array_pack_8bit, shared: true do
[1, 2, 3, 4, 5].pack(pack_format('*')).should == "\x01\x02\x03\x04\x05"
end
- it "ignores NULL bytes between directives" do
- [1, 2, 3].pack(pack_format("\000", 2)).should == "\x01\x02"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [1, 2, 3].pack(pack_format("\000", 2)).should == "\x01\x02"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [1, 2, 3].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/array/pack/m_spec.rb b/spec/ruby/core/array/pack/m_spec.rb
index 2b1a84abca..c6364af12d 100644
--- a/spec/ruby/core/array/pack/m_spec.rb
+++ b/spec/ruby/core/array/pack/m_spec.rb
@@ -80,8 +80,16 @@ describe "Array#pack with format 'M'" do
].should be_computed_by(:pack, "M")
end
- it "encodes a tab followed by a newline with an encoded newline" do
+ it "encodes a tab at the end of a line with an encoded newline" do
+ ["\t"].pack("M").should == "\t=\n"
["\t\n"].pack("M").should == "\t=\n\n"
+ ["abc\t\nxyz"].pack("M").should == "abc\t=\n\nxyz=\n"
+ end
+
+ it "encodes a space at the end of a line with an encoded newline" do
+ [" "].pack("M").should == " =\n"
+ [" \n"].pack("M").should == " =\n\n"
+ ["abc \nxyz"].pack("M").should == "abc =\n\nxyz=\n"
end
it "encodes 127..255 in hex format" do
diff --git a/spec/ruby/core/array/pack/shared/basic.rb b/spec/ruby/core/array/pack/shared/basic.rb
index 23e239d3de..65fdaa45d8 100644
--- a/spec/ruby/core/array/pack/shared/basic.rb
+++ b/spec/ruby/core/array/pack/shared/basic.rb
@@ -27,6 +27,42 @@ describe :array_pack_basic_non_float, shared: true do
[@obj, @obj].pack("a \t\n\v\f\r"+pack_format).should be_an_instance_of(String)
end
+ it "ignores comments in the format string" do
+ # 2 additional directives ('a') are required for the X directive
+ [@obj, @obj, @obj, @obj].pack("aa #{pack_format} # some comment \n#{pack_format}").should be_an_instance_of(String)
+ end
+
+ ruby_version_is ""..."3.2" do
+ it "warns in verbose mode that a directive is unknown" do
+ # additional directive ('a') is required for the X directive
+ -> { [@obj, @obj].pack("a R" + pack_format) }.should complain(/unknown pack directive 'R'/, verbose: true)
+ -> { [@obj, @obj].pack("a 0" + pack_format) }.should complain(/unknown pack directive '0'/, verbose: true)
+ -> { [@obj, @obj].pack("a :" + pack_format) }.should complain(/unknown pack directive ':'/, verbose: true)
+ end
+ end
+
+ ruby_version_is "3.2"..."3.3" do
+ # https://bugs.ruby-lang.org/issues/19150
+ # NOTE: it's just a plan of the Ruby core team
+ it "warns that a directive is unknown" do
+ # additional directive ('a') is required for the X directive
+ -> { [@obj, @obj].pack("a R" + pack_format) }.should complain(/unknown pack directive 'R'/)
+ -> { [@obj, @obj].pack("a 0" + pack_format) }.should complain(/unknown pack directive '0'/)
+ -> { [@obj, @obj].pack("a :" + pack_format) }.should complain(/unknown pack directive ':'/)
+ end
+ end
+
+ ruby_version_is "3.3" do
+ # https://bugs.ruby-lang.org/issues/19150
+ # NOTE: Added this case just to not forget about the decision in the ticket
+ it "raise ArgumentError when a directive is unknown" do
+ # additional directive ('a') is required for the X directive
+ -> { [@obj, @obj].pack("a R" + pack_format) }.should raise_error(ArgumentError)
+ -> { [@obj, @obj].pack("a 0" + pack_format) }.should raise_error(ArgumentError)
+ -> { [@obj, @obj].pack("a :" + pack_format) }.should raise_error(ArgumentError)
+ end
+ end
+
it "calls #to_str to coerce the directives string" do
d = mock("pack directive")
d.should_receive(:to_str).and_return("x"+pack_format)
@@ -39,6 +75,10 @@ describe :array_pack_basic_float, shared: true do
[9.3, 4.7].pack(" \t\n\v\f\r"+pack_format).should be_an_instance_of(String)
end
+ it "ignores comments in the format string" do
+ [9.3, 4.7].pack(pack_format + "# some comment \n" + pack_format).should be_an_instance_of(String)
+ end
+
it "calls #to_str to coerce the directives string" do
d = mock("pack directive")
d.should_receive(:to_str).and_return("x"+pack_format)
diff --git a/spec/ruby/core/array/pack/shared/float.rb b/spec/ruby/core/array/pack/shared/float.rb
index ba174a071a..9510cffed7 100644
--- a/spec/ruby/core/array/pack/shared/float.rb
+++ b/spec/ruby/core/array/pack/shared/float.rb
@@ -25,8 +25,18 @@ describe :array_pack_float_le, shared: true do
[2.9, 1.4, 8.2].pack(pack_format("*")).should == "\x9a\x999@33\xb3?33\x03A"
end
- it "ignores NULL bytes between directives" do
- [5.3, 9.2].pack(pack_format("\000", 2)).should == "\x9a\x99\xa9@33\x13A"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [5.3, 9.2].pack(pack_format("\000", 2)).should == "\x9a\x99\xa9@33\x13A"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [5.3, 9.2].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -74,6 +84,11 @@ describe :array_pack_float_be, shared: true do
it "converts an Integer to a Float" do
[8].pack(pack_format).should == "A\x00\x00\x00"
+ [bignum_value].pack(pack_format).should == "_\x80\x00\x00"
+ end
+
+ it "converts a Rational to a Float" do
+ [Rational(8)].pack(pack_format).should == "A\x00\x00\x00"
end
it "raises a TypeError if passed a String representation of a floating point number" do
@@ -88,8 +103,18 @@ describe :array_pack_float_be, shared: true do
[2.9, 1.4, 8.2].pack(pack_format("*")).should == "@9\x99\x9a?\xb333A\x0333"
end
- it "ignores NULL bytes between directives" do
- [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\xa9\x99\x9aA\x1333"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\xa9\x99\x9aA\x1333"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [5.3, 9.2].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -129,6 +154,11 @@ describe :array_pack_double_le, shared: true do
it "converts an Integer to a Float" do
[8].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\x20@"
+ [bignum_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xF0C"
+ end
+
+ it "converts a Rational to a Float" do
+ [Rational(8)].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00 @"
end
it "raises a TypeError if passed a String representation of a floating point number" do
@@ -143,8 +173,18 @@ describe :array_pack_double_le, shared: true do
[2.9, 1.4, 8.2].pack(pack_format("*")).should == "333333\x07@ffffff\xf6?ffffff\x20@"
end
- it "ignores NULL bytes between directives" do
- [5.3, 9.2].pack(pack_format("\000", 2)).should == "333333\x15@ffffff\x22@"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [5.3, 9.2].pack(pack_format("\000", 2)).should == "333333\x15@ffffff\x22@"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [5.3, 9.2].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -202,8 +242,18 @@ describe :array_pack_double_be, shared: true do
[2.9, 1.4, 8.2].pack(pack_format("*")).should == "@\x07333333?\xf6ffffff@\x20ffffff"
end
- it "ignores NULL bytes between directives" do
- [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\x15333333@\x22ffffff"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\x15333333@\x22ffffff"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [5.3, 9.2].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/array/pack/shared/integer.rb b/spec/ruby/core/array/pack/shared/integer.rb
index 6592f85022..d3ce9b5792 100644
--- a/spec/ruby/core/array/pack/shared/integer.rb
+++ b/spec/ruby/core/array/pack/shared/integer.rb
@@ -41,9 +41,19 @@ describe :array_pack_16bit_le, shared: true do
str.should == "\x78\x65\xcd\xab\x21\x43"
end
- it "ignores NULL bytes between directives" do
- str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
- str.should == "\x78\x65\xcd\xab"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ str.should == "\x78\x65\xcd\xab"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -93,9 +103,19 @@ describe :array_pack_16bit_be, shared: true do
str.should == "\x65\x78\xab\xcd\x43\x21"
end
- it "ignores NULL bytes between directives" do
- str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
- str.should == "\x65\x78\xab\xcd"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ str.should == "\x65\x78\xab\xcd"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -145,9 +165,19 @@ describe :array_pack_32bit_le, shared: true do
str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde\x21\x43\x65\x78"
end
- it "ignores NULL bytes between directives" do
- str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
- str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -197,9 +227,19 @@ describe :array_pack_32bit_be, shared: true do
str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd\x78\x65\x43\x21"
end
- it "ignores NULL bytes between directives" do
- str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
- str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -309,9 +349,19 @@ describe :array_pack_64bit_le, shared: true do
str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78"
end
- it "ignores NULL bytes between directives" do
- str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
- str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
+ str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -369,9 +419,19 @@ describe :array_pack_64bit_be, shared: true do
str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0"
end
- it "ignores NULL bytes between directives" do
- str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
- str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
+ str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/array/pack/shared/numeric_basic.rb b/spec/ruby/core/array/pack/shared/numeric_basic.rb
index 7c36ba4a32..545e215e64 100644
--- a/spec/ruby/core/array/pack/shared/numeric_basic.rb
+++ b/spec/ruby/core/array/pack/shared/numeric_basic.rb
@@ -37,8 +37,14 @@ describe :array_pack_float, shared: true do
-> { ["a"].pack(pack_format) }.should raise_error(TypeError)
end
- it "raises a TypeError when the object does not respond to #to_f" do
- obj = mock('not an float')
+ it "raises a TypeError when the object is not Numeric" do
+ obj = Object.new
+ -> { [obj].pack(pack_format) }.should raise_error(TypeError, /can't convert Object into Float/)
+ end
+
+ it "raises a TypeError when the Numeric object does not respond to #to_f" do
+ klass = Class.new(Numeric)
+ obj = klass.new
-> { [obj].pack(pack_format) }.should raise_error(TypeError)
end
end
diff --git a/spec/ruby/core/array/pack/shared/unicode.rb b/spec/ruby/core/array/pack/shared/unicode.rb
index dd0f8b38aa..130c447bb7 100644
--- a/spec/ruby/core/array/pack/shared/unicode.rb
+++ b/spec/ruby/core/array/pack/shared/unicode.rb
@@ -67,8 +67,18 @@ describe :array_pack_unicode, shared: true do
-> { [obj].pack("U") }.should raise_error(TypeError)
end
- it "ignores NULL bytes between directives" do
- [1, 2, 3].pack("U\x00U").should == "\x01\x02"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [1, 2, 3].pack("U\x00U").should == "\x01\x02"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [1, 2, 3].pack("U\x00U")
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/array/pack/w_spec.rb b/spec/ruby/core/array/pack/w_spec.rb
index 439fa02198..e241d1519c 100644
--- a/spec/ruby/core/array/pack/w_spec.rb
+++ b/spec/ruby/core/array/pack/w_spec.rb
@@ -24,8 +24,18 @@ describe "Array#pack with format 'w'" do
[obj].pack("w").should == "\x05"
end
- it "ignores NULL bytes between directives" do
- [1, 2, 3].pack("w\x00w").should == "\x01\x02"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [1, 2, 3].pack("w\x00w").should == "\x01\x02"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [1, 2, 3].pack("w\x00w")
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/array/shared/slice.rb b/spec/ruby/core/array/shared/slice.rb
index 3b09fdcbc6..8fb33738b9 100644
--- a/spec/ruby/core/array/shared/slice.rb
+++ b/spec/ruby/core/array/shared/slice.rb
@@ -784,6 +784,102 @@ describe :array_slice, shared: true do
a.send(@method, (...-9)).should == []
end
+ ruby_version_is "3.2" do
+ describe "can be sliced with Enumerator::ArithmeticSequence" do
+ it "with infinite/inverted ranges and negative steps" do
+ @array = [0, 1, 2, 3, 4, 5]
+ @array.send(@method, (2..).step(-1)).should == [2, 1, 0]
+ @array.send(@method, (2..).step(-2)).should == [2, 0]
+ @array.send(@method, (2..).step(-3)).should == [2]
+ @array.send(@method, (2..).step(-4)).should == [2]
+
+ @array.send(@method, (-3..).step(-1)).should == [3, 2, 1, 0]
+ @array.send(@method, (-3..).step(-2)).should == [3, 1]
+ @array.send(@method, (-3..).step(-3)).should == [3, 0]
+ @array.send(@method, (-3..).step(-4)).should == [3]
+ @array.send(@method, (-3..).step(-5)).should == [3]
+
+ @array.send(@method, (..0).step(-1)).should == [5, 4, 3, 2, 1, 0]
+ @array.send(@method, (..0).step(-2)).should == [5, 3, 1]
+ @array.send(@method, (..0).step(-3)).should == [5, 2]
+ @array.send(@method, (..0).step(-4)).should == [5, 1]
+ @array.send(@method, (..0).step(-5)).should == [5, 0]
+ @array.send(@method, (..0).step(-6)).should == [5]
+ @array.send(@method, (..0).step(-7)).should == [5]
+
+ @array.send(@method, (...0).step(-1)).should == [5, 4, 3, 2, 1]
+ @array.send(@method, (...0).step(-2)).should == [5, 3, 1]
+ @array.send(@method, (...0).step(-3)).should == [5, 2]
+ @array.send(@method, (...0).step(-4)).should == [5, 1]
+ @array.send(@method, (...0).step(-5)).should == [5]
+ @array.send(@method, (...0).step(-6)).should == [5]
+
+ @array.send(@method, (...1).step(-1)).should == [5, 4, 3, 2]
+ @array.send(@method, (...1).step(-2)).should == [5, 3]
+ @array.send(@method, (...1).step(-3)).should == [5, 2]
+ @array.send(@method, (...1).step(-4)).should == [5]
+ @array.send(@method, (...1).step(-5)).should == [5]
+
+ @array.send(@method, (..-5).step(-1)).should == [5, 4, 3, 2, 1]
+ @array.send(@method, (..-5).step(-2)).should == [5, 3, 1]
+ @array.send(@method, (..-5).step(-3)).should == [5, 2]
+ @array.send(@method, (..-5).step(-4)).should == [5, 1]
+ @array.send(@method, (..-5).step(-5)).should == [5]
+ @array.send(@method, (..-5).step(-6)).should == [5]
+
+ @array.send(@method, (...-5).step(-1)).should == [5, 4, 3, 2]
+ @array.send(@method, (...-5).step(-2)).should == [5, 3]
+ @array.send(@method, (...-5).step(-3)).should == [5, 2]
+ @array.send(@method, (...-5).step(-4)).should == [5]
+ @array.send(@method, (...-5).step(-5)).should == [5]
+
+ @array.send(@method, (4..1).step(-1)).should == [4, 3, 2, 1]
+ @array.send(@method, (4..1).step(-2)).should == [4, 2]
+ @array.send(@method, (4..1).step(-3)).should == [4, 1]
+ @array.send(@method, (4..1).step(-4)).should == [4]
+ @array.send(@method, (4..1).step(-5)).should == [4]
+
+ @array.send(@method, (4...1).step(-1)).should == [4, 3, 2]
+ @array.send(@method, (4...1).step(-2)).should == [4, 2]
+ @array.send(@method, (4...1).step(-3)).should == [4]
+ @array.send(@method, (4...1).step(-4)).should == [4]
+
+ @array.send(@method, (-2..1).step(-1)).should == [4, 3, 2, 1]
+ @array.send(@method, (-2..1).step(-2)).should == [4, 2]
+ @array.send(@method, (-2..1).step(-3)).should == [4, 1]
+ @array.send(@method, (-2..1).step(-4)).should == [4]
+ @array.send(@method, (-2..1).step(-5)).should == [4]
+
+ @array.send(@method, (-2...1).step(-1)).should == [4, 3, 2]
+ @array.send(@method, (-2...1).step(-2)).should == [4, 2]
+ @array.send(@method, (-2...1).step(-3)).should == [4]
+ @array.send(@method, (-2...1).step(-4)).should == [4]
+
+ @array.send(@method, (4..-5).step(-1)).should == [4, 3, 2, 1]
+ @array.send(@method, (4..-5).step(-2)).should == [4, 2]
+ @array.send(@method, (4..-5).step(-3)).should == [4, 1]
+ @array.send(@method, (4..-5).step(-4)).should == [4]
+ @array.send(@method, (4..-5).step(-5)).should == [4]
+
+ @array.send(@method, (4...-5).step(-1)).should == [4, 3, 2]
+ @array.send(@method, (4...-5).step(-2)).should == [4, 2]
+ @array.send(@method, (4...-5).step(-3)).should == [4]
+ @array.send(@method, (4...-5).step(-4)).should == [4]
+
+ @array.send(@method, (-2..-5).step(-1)).should == [4, 3, 2, 1]
+ @array.send(@method, (-2..-5).step(-2)).should == [4, 2]
+ @array.send(@method, (-2..-5).step(-3)).should == [4, 1]
+ @array.send(@method, (-2..-5).step(-4)).should == [4]
+ @array.send(@method, (-2..-5).step(-5)).should == [4]
+
+ @array.send(@method, (-2...-5).step(-1)).should == [4, 3, 2]
+ @array.send(@method, (-2...-5).step(-2)).should == [4, 2]
+ @array.send(@method, (-2...-5).step(-3)).should == [4]
+ @array.send(@method, (-2...-5).step(-4)).should == [4]
+ end
+ end
+ end
+
it "can accept nil...nil ranges" do
a = [0, 1, 2, 3, 4, 5]
a.send(@method, eval("(nil...nil)")).should == a
diff --git a/spec/ruby/core/array/shared/unshift.rb b/spec/ruby/core/array/shared/unshift.rb
index fc82e19e2a..4941e098f6 100644
--- a/spec/ruby/core/array/shared/unshift.rb
+++ b/spec/ruby/core/array/shared/unshift.rb
@@ -22,6 +22,11 @@ describe :array_unshift, shared: true do
a.should == [3, 4]
end
+ it "returns self" do
+ a = [1, 2, 3]
+ a.send(@method, "a").should.equal?(a)
+ end
+
it "quietly ignores unshifting nothing" do
[].send(@method).should == []
end
@@ -43,4 +48,17 @@ describe :array_unshift, shared: true do
it "raises a FrozenError on a frozen array when the array would not be modified" do
-> { ArraySpecs.frozen_array.send(@method) }.should raise_error(FrozenError)
end
+
+ # https://github.com/oracle/truffleruby/issues/2772
+ it "doesn't rely on Array#[]= so it can be overridden" do
+ subclass = Class.new(Array) do
+ def []=(*)
+ raise "[]= is called"
+ end
+ end
+
+ array = subclass.new
+ array.send(@method, 1)
+ array.should == [1]
+ end
end
diff --git a/spec/ruby/core/array/values_at_spec.rb b/spec/ruby/core/array/values_at_spec.rb
index 2c6fd16947..e85bbee400 100644
--- a/spec/ruby/core/array/values_at_spec.rb
+++ b/spec/ruby/core/array/values_at_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+# Should be synchronized with core/struct/values_at_spec.rb
describe "Array#values_at" do
it "returns an array of elements at the indexes when passed indexes" do
[1, 2, 3, 4, 5].values_at().should == []
diff --git a/spec/ruby/core/array/zip_spec.rb b/spec/ruby/core/array/zip_spec.rb
index af4013debe..2a0f64cb49 100644
--- a/spec/ruby/core/array/zip_spec.rb
+++ b/spec/ruby/core/array/zip_spec.rb
@@ -62,4 +62,10 @@ describe "Array#zip" do
it "does not return subclass instance on Array subclasses" do
ArraySpecs::MyArray[1, 2, 3].zip(["a", "b"]).should be_an_instance_of(Array)
end
+
+ it "raises TypeError when some argument isn't Array and doesn't respond to #to_ary and #to_enum" do
+ -> { [1, 2, 3].zip(Object.new) }.should raise_error(TypeError, "wrong argument type Object (must respond to :each)")
+ -> { [1, 2, 3].zip(1) }.should raise_error(TypeError, "wrong argument type Integer (must respond to :each)")
+ -> { [1, 2, 3].zip(true) }.should raise_error(TypeError, "wrong argument type TrueClass (must respond to :each)")
+ end
end
diff --git a/spec/ruby/core/basicobject/instance_eval_spec.rb b/spec/ruby/core/basicobject/instance_eval_spec.rb
index b6a146095d..350b08a30e 100644
--- a/spec/ruby/core/basicobject/instance_eval_spec.rb
+++ b/spec/ruby/core/basicobject/instance_eval_spec.rb
@@ -20,12 +20,18 @@ describe "BasicObject#instance_eval" do
a.instance_eval('self').equal?(a).should be_true
end
- it "expects a block with no arguments" do
- -> { "hola".instance_eval }.should raise_error(ArgumentError)
+ it "raises an ArgumentError when no arguments and no block are given" do
+ -> { "hola".instance_eval }.should raise_error(ArgumentError, "wrong number of arguments (given 0, expected 1..3)")
end
- it "takes no arguments with a block" do
- -> { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError)
+ it "raises an ArgumentError when a block and normal arguments are given" do
+ -> { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError, "wrong number of arguments (given 2, expected 0)")
+ end
+
+ it "raises an ArgumentError when more than 3 arguments are given" do
+ -> {
+ "hola".instance_eval("1 + 1", "some file", 0, "bogus")
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
it "yields the object to the block" do
@@ -185,4 +191,58 @@ end
x.should == :value
end
+
+ it "converts string argument with #to_str method" do
+ source_code = Object.new
+ def source_code.to_str() "1" end
+
+ a = BasicObject.new
+ a.instance_eval(source_code).should == 1
+ end
+
+ it "raises ArgumentError if returned value is not String" do
+ source_code = Object.new
+ def source_code.to_str() :symbol end
+
+ a = BasicObject.new
+ -> { a.instance_eval(source_code) }.should raise_error(TypeError, /can't convert Object to String/)
+ end
+
+ it "converts filename argument with #to_str method" do
+ filename = Object.new
+ def filename.to_str() "file.rb" end
+
+ err = begin
+ Object.new.instance_eval("raise", filename)
+ rescue => e
+ e
+ end
+ err.backtrace.first.split(":")[0].should == "file.rb"
+ end
+
+ it "raises ArgumentError if returned value is not String" do
+ filename = Object.new
+ def filename.to_str() :symbol end
+
+ -> { Object.new.instance_eval("raise", filename) }.should raise_error(TypeError, /can't convert Object to String/)
+ end
+
+ it "converts lineno argument with #to_int method" do
+ lineno = Object.new
+ def lineno.to_int() 15 end
+
+ err = begin
+ Object.new.instance_eval("raise", "file.rb", lineno)
+ rescue => e
+ e
+ end
+ err.backtrace.first.split(":")[1].should == "15"
+ end
+
+ it "raises ArgumentError if returned value is not Integer" do
+ lineno = Object.new
+ def lineno.to_int() :symbol end
+
+ -> { Object.new.instance_eval("raise", "file.rb", lineno) }.should raise_error(TypeError, /can't convert Object to Integer/)
+ end
end
diff --git a/spec/ruby/core/builtin_constants/builtin_constants_spec.rb b/spec/ruby/core/builtin_constants/builtin_constants_spec.rb
index b0b86030e2..1960f5721f 100644
--- a/spec/ruby/core/builtin_constants/builtin_constants_spec.rb
+++ b/spec/ruby/core/builtin_constants/builtin_constants_spec.rb
@@ -34,12 +34,6 @@ describe "RUBY_PLATFORM" do
it "is a String" do
RUBY_PLATFORM.should be_kind_of(String)
end
-
- platform_is :darwin do
- it 'ends with the build time kernel major version on darwin' do
- RUBY_PLATFORM.should =~ /-darwin\d+$/
- end
- end
end
describe "RUBY_RELEASE_DATE" do
diff --git a/spec/ruby/core/class/attached_object_spec.rb b/spec/ruby/core/class/attached_object_spec.rb
new file mode 100644
index 0000000000..115d5fa563
--- /dev/null
+++ b/spec/ruby/core/class/attached_object_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '3.2' do
+ describe "Class#attached_object" do
+ it "returns the object that is attached to a singleton class" do
+ a = Class.new
+
+ a_obj = a.new
+ a_obj.singleton_class.attached_object.should == a_obj
+ end
+
+ it "returns the class object that is attached to a class's singleton class" do
+ a = Class.new
+ singleton_class = (class << a; self; end)
+
+ singleton_class.attached_object.should == a
+ end
+
+ it "raises TypeError if the class is not a singleton class" do
+ a = Class.new
+
+ -> { a.attached_object }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError for special singleton classes" do
+ -> { nil.singleton_class.attached_object }.should raise_error(TypeError)
+ -> { true.singleton_class.attached_object }.should raise_error(TypeError)
+ -> { false.singleton_class.attached_object }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/class/subclasses_spec.rb b/spec/ruby/core/class/subclasses_spec.rb
index ddbcfb02c0..a16b934d4f 100644
--- a/spec/ruby/core/class/subclasses_spec.rb
+++ b/spec/ruby/core/class/subclasses_spec.rb
@@ -31,6 +31,28 @@ ruby_version_is '3.1' do
ModuleSpecs::Parent.subclasses.should == ModuleSpecs::Parent.subclasses.uniq
end
+ it "works when creating subclasses concurrently" do
+ t = 16
+ n = 1000
+ go = false
+ superclass = Class.new
+
+ threads = t.times.map do
+ Thread.new do
+ Thread.pass until go
+ n.times.map do
+ Class.new(superclass)
+ end
+ end
+ end
+
+ go = true
+ classes = threads.map(&:value)
+
+ superclass.subclasses.size.should == t * n
+ superclass.subclasses.each { |c| c.should be_kind_of(Class) }
+ end
+
def assert_subclasses(mod,subclasses)
mod.subclasses.sort_by(&:inspect).should == subclasses.sort_by(&:inspect)
end
diff --git a/spec/ruby/core/complex/polar_spec.rb b/spec/ruby/core/complex/polar_spec.rb
index 2a5d8ebd69..3bb3751bc6 100644
--- a/spec/ruby/core/complex/polar_spec.rb
+++ b/spec/ruby/core/complex/polar_spec.rb
@@ -10,6 +10,22 @@ describe "Complex.polar" do
->{ Complex.polar(nil) }.should raise_error(TypeError)
->{ Complex.polar(nil, nil) }.should raise_error(TypeError)
end
+
+ ruby_bug "#19004", ""..."3.2" do
+ it "computes the real values of the real & imaginary parts from the polar form" do
+ a = Complex.polar(1.0+0.0i, Math::PI/2+0.0i)
+ a.real.should be_close(0.0, TOLERANCE)
+ a.imag.should be_close(1.0, TOLERANCE)
+ a.real.real?.should be_true
+ a.imag.real?.should be_true
+
+ b = Complex.polar(1+0.0i)
+ b.real.should be_close(1.0, TOLERANCE)
+ b.imag.should be_close(0.0, TOLERANCE)
+ b.real.real?.should be_true
+ b.imag.real?.should be_true
+ end
+ end
end
describe "Complex#polar" do
diff --git a/spec/ruby/core/data/constants_spec.rb b/spec/ruby/core/data/constants_spec.rb
index 1d469f9237..d9d55b50f9 100644
--- a/spec/ruby/core/data/constants_spec.rb
+++ b/spec/ruby/core/data/constants_spec.rb
@@ -14,10 +14,22 @@ ruby_version_is ''...'3.0' do
end
end
-ruby_version_is '3.0' do
+ruby_version_is '3.0'...'3.2' do
describe "Data" do
it "does not exist anymore" do
Object.should_not have_constant(:Data)
end
end
end
+
+ruby_version_is '3.2' do
+ describe "Data" do
+ it "is a new constant" do
+ Data.superclass.should == Object
+ end
+
+ it "is not deprecated" do
+ -> { Data }.should_not complain
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/fixtures/common.rb b/spec/ruby/core/dir/fixtures/common.rb
index a8d6e69c44..087f46b331 100644
--- a/spec/ruby/core/dir/fixtures/common.rb
+++ b/spec/ruby/core/dir/fixtures/common.rb
@@ -82,6 +82,7 @@ module DirSpecs
special/test{1}/file[1]
special/{}/special
+ special/test\ +()[]{}/hello_world.erb
]
platform_is_not :windows do
diff --git a/spec/ruby/core/dir/glob_spec.rb b/spec/ruby/core/dir/glob_spec.rb
index 43dac73eee..06b52b90fb 100644
--- a/spec/ruby/core/dir/glob_spec.rb
+++ b/spec/ruby/core/dir/glob_spec.rb
@@ -79,6 +79,7 @@ describe "Dir.glob" do
nested/
nested/.dotsubir/
special/
+ special/test\ +()[]{}/
special/test{1}/
special/{}/
subdir_one/
@@ -130,6 +131,7 @@ describe "Dir.glob" do
./nested/
./nested/.dotsubir/
./special/
+ ./special/test\ +()[]{}/
./special/test{1}/
./special/{}/
./subdir_one/
diff --git a/spec/ruby/core/dir/home_spec.rb b/spec/ruby/core/dir/home_spec.rb
index 8377f1dc97..bbe347ba9e 100644
--- a/spec/ruby/core/dir/home_spec.rb
+++ b/spec/ruby/core/dir/home_spec.rb
@@ -19,6 +19,45 @@ describe "Dir.home" do
it "returns a non-frozen string" do
Dir.home.should_not.frozen?
end
+
+ it "returns a string with the filesystem encoding" do
+ Dir.home.encoding.should == Encoding.find("filesystem")
+ end
+
+ platform_is_not :windows do
+ it "works even if HOME is unset" do
+ ENV.delete('HOME')
+ Dir.home.should.start_with?('/')
+ Dir.home.encoding.should == Encoding.find("filesystem")
+ end
+ end
+
+ platform_is :windows do
+ ruby_version_is "3.2" do
+ it "returns the home directory with forward slashs and as UTF-8" do
+ ENV['HOME'] = "C:\\rubyspäc\\home"
+ home = Dir.home
+ home.should == "C:/rubyspäc/home"
+ home.encoding.should == Encoding::UTF_8
+ end
+ end
+
+ it "retrieves the directory from HOME, USERPROFILE, HOMEDRIVE/HOMEPATH and the WinAPI in that order" do
+ old_dirs = [ENV.delete('HOME'), ENV.delete('USERPROFILE'), ENV.delete('HOMEDRIVE'), ENV.delete('HOMEPATH')]
+
+ Dir.home.should == old_dirs[1].gsub("\\", "/")
+ ENV['HOMEDRIVE'] = "C:"
+ ENV['HOMEPATH'] = "\\rubyspec\\home1"
+ Dir.home.should == "C:/rubyspec/home1"
+ ENV['USERPROFILE'] = "C:\\rubyspec\\home2"
+ # https://bugs.ruby-lang.org/issues/19244
+ # Dir.home.should == "C:/rubyspec/home2"
+ ENV['HOME'] = "C:\\rubyspec\\home3"
+ Dir.home.should == "C:/rubyspec/home3"
+ ensure
+ ENV['HOME'], ENV['USERPROFILE'], ENV['HOMEDRIVE'], ENV['HOMEPATH'] = *old_dirs
+ end
+ end
end
describe "when called with the current user name" do
@@ -37,6 +76,10 @@ describe "Dir.home" do
it "returns a non-frozen string" do
Dir.home(ENV['USER']).should_not.frozen?
end
+
+ it "returns a string with the filesystem encoding" do
+ Dir.home(ENV['USER']).encoding.should == Encoding.find("filesystem")
+ end
end
it "raises an ArgumentError if the named user doesn't exist" do
diff --git a/spec/ruby/core/dir/mkdir_spec.rb b/spec/ruby/core/dir/mkdir_spec.rb
index 0ed28f5a99..076ec19dd9 100644
--- a/spec/ruby/core/dir/mkdir_spec.rb
+++ b/spec/ruby/core/dir/mkdir_spec.rb
@@ -46,7 +46,7 @@ describe "Dir.mkdir" do
end
end
- it "calls #to_path on non-String arguments" do
+ it "calls #to_path on non-String path arguments" do
DirSpecs.clear_dirs
p = mock('path')
p.should_receive(:to_path).and_return(DirSpecs.mock_dir('nonexisting'))
@@ -54,6 +54,22 @@ describe "Dir.mkdir" do
DirSpecs.clear_dirs
end
+ it "calls #to_int on non-Integer permissions argument" do
+ DirSpecs.clear_dirs
+ path = DirSpecs.mock_dir('nonexisting')
+ permissions = mock('permissions')
+ permissions.should_receive(:to_int).and_return(0666)
+ Dir.mkdir(path, permissions)
+ DirSpecs.clear_dirs
+ end
+
+ it "raises TypeError if non-Integer permissions argument does not have #to_int method" do
+ path = DirSpecs.mock_dir('nonexisting')
+ permissions = Object.new
+
+ -> { Dir.mkdir(path, permissions) }.should raise_error(TypeError, 'no implicit conversion of Object into Integer')
+ end
+
it "raises a SystemCallError if any of the directories in the path before the last does not exist" do
-> { Dir.mkdir "#{DirSpecs.nonexistent}/subdir" }.should raise_error(SystemCallError)
end
diff --git a/spec/ruby/core/dir/shared/chroot.rb b/spec/ruby/core/dir/shared/chroot.rb
index b14a433670..8c0599fe3f 100644
--- a/spec/ruby/core/dir/shared/chroot.rb
+++ b/spec/ruby/core/dir/shared/chroot.rb
@@ -3,7 +3,7 @@ describe :dir_chroot_as_root, shared: true do
DirSpecs.create_mock_dirs
@real_root = "../" * (File.dirname(__FILE__).count('/') - 1)
- @ref_dir = File.join("/", Dir.new('/').entries.first)
+ @ref_dir = File.join("/", File.basename(Dir["/*"].first))
end
after :all do
@@ -14,10 +14,13 @@ describe :dir_chroot_as_root, shared: true do
DirSpecs.delete_mock_dirs
end
+ # Pending until https://github.com/ruby/ruby/runs/8075149420 is fixed
+ compilations_ci = ENV["GITHUB_WORKFLOW"] == "Compilations"
+
it "can be used to change the process' root directory" do
-> { Dir.send(@method, File.dirname(__FILE__)) }.should_not raise_error
File.should.exist?("/#{File.basename(__FILE__)}")
- end
+ end unless compilations_ci
it "returns 0 if successful" do
Dir.send(@method, '/').should == 0
@@ -31,7 +34,7 @@ describe :dir_chroot_as_root, shared: true do
Dir.send(@method, @real_root)
File.should.exist?(@ref_dir)
File.should_not.exist?("/#{File.basename(__FILE__)}")
- end
+ end unless compilations_ci
it "calls #to_path on non-String argument" do
p = mock('path')
diff --git a/spec/ruby/core/dir/shared/glob.rb b/spec/ruby/core/dir/shared/glob.rb
index 60d4a8c97a..33b2828c27 100644
--- a/spec/ruby/core/dir/shared/glob.rb
+++ b/spec/ruby/core/dir/shared/glob.rb
@@ -111,6 +111,10 @@ describe :dir_glob, shared: true do
it "matches files with backslashes in their name" do
Dir.glob('special/\\\\{a,b}').should == ['special/\a']
end
+
+ it "matches directory with special characters in their name in complex patterns" do
+ Dir.glob("special/test +()\\[\\]\\{\\}/hello_world{.{en},}{.{html},}{+{phone},}{.{erb},}").should == ['special/test +()[]{}/hello_world.erb']
+ end
end
it "matches regexp special ^" do
@@ -225,6 +229,7 @@ describe :dir_glob, shared: true do
dir/
nested/
special/
+ special/test\ +()[]{}/
special/test{1}/
special/{}/
subdir_one/
diff --git a/spec/ruby/core/encoding/replicate_spec.rb b/spec/ruby/core/encoding/replicate_spec.rb
index 45727a5c0d..848415eeb4 100644
--- a/spec/ruby/core/encoding/replicate_spec.rb
+++ b/spec/ruby/core/encoding/replicate_spec.rb
@@ -2,66 +2,74 @@
require_relative '../../spec_helper'
describe "Encoding#replicate" do
- before :all do
- @i = 0
- end
+ ruby_version_is ""..."3.3" do
+ before :all do
+ @i = 0
+ end
- before :each do
- @i += 1
- @prefix = "RS#{@i}"
- end
+ before :each do
+ @i += 1
+ @prefix = "RS#{@i}"
+ end
- it "returns a replica of ASCII" do
- name = @prefix + '-ASCII'
- e = Encoding::ASCII.replicate(name)
- e.name.should == name
- Encoding.find(name).should == e
+ it "returns a replica of ASCII" do
+ name = @prefix + '-ASCII'
+ e = suppress_warning { Encoding::ASCII.replicate(name) }
+ e.name.should == name
+ Encoding.find(name).should == e
- "a".force_encoding(e).valid_encoding?.should be_true
- "\x80".force_encoding(e).valid_encoding?.should be_false
- end
+ "a".force_encoding(e).valid_encoding?.should be_true
+ "\x80".force_encoding(e).valid_encoding?.should be_false
+ end
- it "returns a replica of UTF-8" do
- name = @prefix + 'UTF-8'
- e = Encoding::UTF_8.replicate(name)
- e.name.should == name
- Encoding.find(name).should == e
+ it "returns a replica of UTF-8" do
+ name = @prefix + 'UTF-8'
+ e = suppress_warning { Encoding::UTF_8.replicate(name) }
+ e.name.should == name
+ Encoding.find(name).should == e
- "a".force_encoding(e).valid_encoding?.should be_true
- "\u3042".force_encoding(e).valid_encoding?.should be_true
- "\x80".force_encoding(e).valid_encoding?.should be_false
- end
+ "a".force_encoding(e).valid_encoding?.should be_true
+ "\u3042".force_encoding(e).valid_encoding?.should be_true
+ "\x80".force_encoding(e).valid_encoding?.should be_false
+ end
- it "returns a replica of UTF-16BE" do
- name = @prefix + 'UTF-16-BE'
- e = Encoding::UTF_16BE.replicate(name)
- e.name.should == name
- Encoding.find(name).should == e
+ it "returns a replica of UTF-16BE" do
+ name = @prefix + 'UTF-16-BE'
+ e = suppress_warning { Encoding::UTF_16BE.replicate(name) }
+ e.name.should == name
+ Encoding.find(name).should == e
- "a".force_encoding(e).valid_encoding?.should be_false
- "\x30\x42".force_encoding(e).valid_encoding?.should be_true
- "\x80".force_encoding(e).valid_encoding?.should be_false
- end
+ "a".force_encoding(e).valid_encoding?.should be_false
+ "\x30\x42".force_encoding(e).valid_encoding?.should be_true
+ "\x80".force_encoding(e).valid_encoding?.should be_false
+ end
- it "returns a replica of ISO-2022-JP" do
- name = @prefix + 'ISO-2022-JP'
- e = Encoding::ISO_2022_JP.replicate(name)
- Encoding.find(name).should == e
+ it "returns a replica of ISO-2022-JP" do
+ name = @prefix + 'ISO-2022-JP'
+ e = suppress_warning { Encoding::ISO_2022_JP.replicate(name) }
+ Encoding.find(name).should == e
- e.name.should == name
- e.dummy?.should be_true
- end
+ e.name.should == name
+ e.dummy?.should be_true
+ end
- # NOTE: it's unclear of the value of this (for the complexity cost of it),
- # but it is the current CRuby behavior.
- it "can be associated with a String" do
- name = @prefix + '-US-ASCII'
- e = Encoding::US_ASCII.replicate(name)
- e.name.should == name
- Encoding.find(name).should == e
+ # NOTE: it's unclear of the value of this (for the complexity cost of it),
+ # but it is the current CRuby behavior.
+ it "can be associated with a String" do
+ name = @prefix + '-US-ASCII'
+ e = suppress_warning { Encoding::US_ASCII.replicate(name) }
+ e.name.should == name
+ Encoding.find(name).should == e
+
+ s = "abc".force_encoding(e)
+ s.encoding.should == e
+ s.encoding.name.should == name
+ end
+ end
- s = "abc".force_encoding(e)
- s.encoding.should == e
- s.encoding.name.should == name
+ ruby_version_is "3.3" do
+ it "has been removed" do
+ Encoding::US_ASCII.should_not.respond_to?(:replicate, true)
+ end
end
end
diff --git a/spec/ruby/core/enumerable/compact_spec.rb b/spec/ruby/core/enumerable/compact_spec.rb
new file mode 100644
index 0000000000..86e95dce08
--- /dev/null
+++ b/spec/ruby/core/enumerable/compact_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is '3.1' do
+ describe "Enumerable#compact" do
+ it 'returns array without nil elements' do
+ arr = EnumerableSpecs::Numerous.new(nil, 1, 2, nil, true)
+ arr.compact.should == [1, 2, true]
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/each_cons_spec.rb b/spec/ruby/core/enumerable/each_cons_spec.rb
index ba658203a2..8fb31fb925 100644
--- a/spec/ruby/core/enumerable/each_cons_spec.rb
+++ b/spec/ruby/core/enumerable/each_cons_spec.rb
@@ -56,6 +56,12 @@ describe "Enumerable#each_cons" do
multi.each_cons(2).to_a.should == [[[1, 2], [3, 4, 5]], [[3, 4, 5], [6, 7, 8, 9]]]
end
+ ruby_version_is "3.1" do
+ it "returns self when a block is given" do
+ @enum.each_cons(3){}.should == @enum
+ end
+ end
+
describe "when no block is given" do
it "returns an enumerator" do
e = @enum.each_cons(3)
diff --git a/spec/ruby/core/enumerable/each_slice_spec.rb b/spec/ruby/core/enumerable/each_slice_spec.rb
index 2ea89f5e72..a57a1dba81 100644
--- a/spec/ruby/core/enumerable/each_slice_spec.rb
+++ b/spec/ruby/core/enumerable/each_slice_spec.rb
@@ -57,6 +57,12 @@ describe "Enumerable#each_slice" do
e.to_a.should == @sliced
end
+ ruby_version_is "3.1" do
+ it "returns self when a block is given" do
+ @enum.each_slice(3){}.should == @enum
+ end
+ end
+
it "gathers whole arrays as elements when each yields multiple" do
multi = EnumerableSpecs::YieldsMulti.new
multi.each_slice(2).to_a.should == [[[1, 2], [3, 4, 5]], [[6, 7, 8, 9]]]
diff --git a/spec/ruby/core/enumerable/sum_spec.rb b/spec/ruby/core/enumerable/sum_spec.rb
index 4a978794e5..fc173e4173 100644
--- a/spec/ruby/core/enumerable/sum_spec.rb
+++ b/spec/ruby/core/enumerable/sum_spec.rb
@@ -22,8 +22,21 @@ describe 'Enumerable#sum' do
@enum.sum.should == 5/3r
end
- it 'takes a block to transform the elements' do
- @enum.sum { |element| element * 2 }.should == 10/3r
+ context 'with a block' do
+ it 'transforms the elements' do
+ @enum.sum { |element| element * 2 }.should == 10/3r
+ end
+
+ it 'does not destructure array elements' do
+ class << @enum
+ def each
+ yield [1,2]
+ yield [3]
+ end
+ end
+
+ @enum.sum(&:last).should == 5
+ end
end
# https://bugs.ruby-lang.org/issues/12217
diff --git a/spec/ruby/core/enumerable/zip_spec.rb b/spec/ruby/core/enumerable/zip_spec.rb
index 9ec15aa030..ab148f2a6e 100644
--- a/spec/ruby/core/enumerable/zip_spec.rb
+++ b/spec/ruby/core/enumerable/zip_spec.rb
@@ -38,4 +38,9 @@ describe "Enumerable#zip" do
multi.zip(multi).should == [[[1, 2], [1, 2]], [[3, 4, 5], [3, 4, 5]], [[6, 7, 8, 9], [6, 7, 8, 9]]]
end
+ it "raises TypeError when some argument isn't Array and doesn't respond to #to_ary and #to_enum" do
+ -> { EnumerableSpecs::Numerous.new(1,2,3).zip(Object.new) }.should raise_error(TypeError, "wrong argument type Object (must respond to :each)")
+ -> { EnumerableSpecs::Numerous.new(1,2,3).zip(1) }.should raise_error(TypeError, "wrong argument type Integer (must respond to :each)")
+ -> { EnumerableSpecs::Numerous.new(1,2,3).zip(true) }.should raise_error(TypeError, "wrong argument type TrueClass (must respond to :each)")
+ end
end
diff --git a/spec/ruby/core/enumerator/lazy/compact_spec.rb b/spec/ruby/core/enumerator/lazy/compact_spec.rb
new file mode 100644
index 0000000000..80b6f9481d
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/compact_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is '3.1' do
+ describe "Enumerator::Lazy#compact" do
+ it 'returns array without nil elements' do
+ arr = [1, nil, 3, false, 5].to_enum.lazy.compact
+ arr.should be_an_instance_of(Enumerator::Lazy)
+ arr.force.should == [1, 3, false, 5]
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/lazy_spec.rb b/spec/ruby/core/enumerator/lazy/lazy_spec.rb
index cde9b31066..0fb104e25a 100644
--- a/spec/ruby/core/enumerator/lazy/lazy_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/lazy_spec.rb
@@ -16,6 +16,10 @@ describe "Enumerator::Lazy" do
]
lazy_methods += [:chunk_while, :uniq]
+ ruby_version_is '3.1' do
+ lazy_methods += [:compact]
+ end
+
Enumerator::Lazy.instance_methods(false).should include(*lazy_methods)
end
end
diff --git a/spec/ruby/core/env/shared/update.rb b/spec/ruby/core/env/shared/update.rb
index 3101f9c561..7d4799955b 100644
--- a/spec/ruby/core/env/shared/update.rb
+++ b/spec/ruby/core/env/shared/update.rb
@@ -17,10 +17,9 @@ describe :env_update, shared: true do
ruby_version_is "3.2" do
it "adds the multiple parameter hashes to ENV, returning ENV" do
- ENV.send(@method, {"foo" => "0", "bar" => "1"}, {"baz" => "2"}).should equal(ENV)
- ENV["foo"].should == "0"
- ENV["bar"].should == "1"
- ENV["baz"].should == "2"
+ ENV.send(@method, {"foo" => "multi1"}, {"bar" => "multi2"}).should equal(ENV)
+ ENV["foo"].should == "multi1"
+ ENV["bar"].should == "multi2"
end
end
diff --git a/spec/ruby/core/false/case_compare_spec.rb b/spec/ruby/core/false/case_compare_spec.rb
new file mode 100644
index 0000000000..0bd0ab44ae
--- /dev/null
+++ b/spec/ruby/core/false/case_compare_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "FalseClass#===" do
+ it "returns true for false" do
+ (false === false).should == true
+ end
+
+ it "returns false for non-false object" do
+ (false === 0).should == false
+ (false === "").should == false
+ (false === Object).should == false
+ (false === nil).should == false
+ end
+end
diff --git a/spec/ruby/core/fiber/blocking_spec.rb b/spec/ruby/core/fiber/blocking_spec.rb
index 5ae5fbd577..eeee5a71c1 100644
--- a/spec/ruby/core/fiber/blocking_spec.rb
+++ b/spec/ruby/core/fiber/blocking_spec.rb
@@ -60,3 +60,20 @@ ruby_version_is "3.0" do
end
end
end
+
+ruby_version_is "3.2" do
+ describe "Fiber.blocking" do
+ context "when fiber is non-blocking" do
+ it "can become blocking" do
+ fiber = Fiber.new(blocking: false) do
+ Fiber.blocking do |f|
+ f.blocking? ? :blocking : :non_blocking
+ end
+ end
+
+ blocking = fiber.resume
+ blocking.should == :blocking
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/fiber/storage_spec.rb b/spec/ruby/core/fiber/storage_spec.rb
new file mode 100644
index 0000000000..e2bf6da04c
--- /dev/null
+++ b/spec/ruby/core/fiber/storage_spec.rb
@@ -0,0 +1,117 @@
+require_relative '../../spec_helper'
+
+require 'fiber'
+
+describe "Fiber.new(storage:)" do
+ ruby_version_is "3.2" do
+ it "creates a Fiber with the given storage" do
+ storage = {life: 42}
+ fiber = Fiber.new(storage: storage) { Fiber.current.storage }
+ fiber.resume.should == storage
+ end
+
+ it "creates a fiber with lazily initialized storage" do
+ Fiber.new(storage: nil) { Fiber.current.storage }.resume.should == {}
+ end
+
+ it "creates a fiber by inheriting the storage of the parent fiber" do
+ fiber = Fiber.new(storage: {life: 42}) do
+ Fiber.new { Fiber.current.storage }.resume
+ end
+ fiber.resume.should == {life: 42}
+ end
+
+ it "cannot create a fiber with non-hash storage" do
+ -> { Fiber.new(storage: 42) {} }.should raise_error(TypeError)
+ end
+ end
+end
+
+describe "Fiber#storage=" do
+ ruby_version_is "3.2" do
+ it "can clear the storage of the fiber" do
+ fiber = Fiber.new(storage: {life: 42}) {
+ Fiber.current.storage = nil
+ Fiber.current.storage
+ }
+ fiber.resume.should == {}
+ end
+
+ it "can set the storage of the fiber" do
+ fiber = Fiber.new(storage: {life: 42}) {
+ Fiber.current.storage = {life: 43}
+ Fiber.current.storage
+ }
+ fiber.resume.should == {life: 43}
+ end
+
+ it "can't set the storage of the fiber to non-hash" do
+ -> { Fiber.current.storage = 42 }.should raise_error(TypeError)
+ end
+
+ it "can't set the storage of the fiber to a frozen hash" do
+ -> { Fiber.current.storage = {life: 43}.freeze }.should raise_error(FrozenError)
+ end
+
+ it "can't set the storage of the fiber to a hash with non-symbol keys" do
+ -> { Fiber.current.storage = {life: 43, Object.new => 44} }.should raise_error(TypeError)
+ end
+ end
+end
+
+describe "Fiber.[]" do
+ ruby_version_is "3.2" do
+ it "returns the value of the given key in the storage of the current fiber" do
+ Fiber.new(storage: {life: 42}) { Fiber[:life] }.resume.should == 42
+ end
+
+ it "returns nil if the key is not present in the storage of the current fiber" do
+ Fiber.new(storage: {life: 42}) { Fiber[:death] }.resume.should be_nil
+ end
+
+ it "returns nil if the current fiber has no storage" do
+ Fiber.new { Fiber[:life] }.resume.should be_nil
+ end
+ end
+
+ ruby_version_is "3.2.3" do
+ it "can use dynamically defined keys" do
+ key = :"#{self.class.name}#.#{self.object_id}"
+ Fiber.new { Fiber[key] = 42; Fiber[key] }.resume.should == 42
+ end
+
+ it "can't use invalid keys" do
+ invalid_keys = [Object.new, "Foo", 12]
+ invalid_keys.each do |key|
+ -> { Fiber[key] }.should raise_error(TypeError)
+ end
+ end
+ end
+end
+
+describe "Fiber.[]=" do
+ ruby_version_is "3.2" do
+ it "sets the value of the given key in the storage of the current fiber" do
+ Fiber.new(storage: {life: 42}) { Fiber[:life] = 43; Fiber[:life] }.resume.should == 43
+ end
+
+ it "sets the value of the given key in the storage of the current fiber" do
+ Fiber.new(storage: {life: 42}) { Fiber[:death] = 43; Fiber[:death] }.resume.should == 43
+ end
+
+ it "sets the value of the given key in the storage of the current fiber" do
+ Fiber.new { Fiber[:life] = 43; Fiber[:life] }.resume.should == 43
+ end
+ end
+end
+
+describe "Thread.new" do
+ ruby_version_is "3.2" do
+ it "creates a thread with the storage of the current fiber" do
+ fiber = Fiber.new(storage: {life: 42}) do
+ Thread.new { Fiber.current.storage }.value
+ end
+ fiber.resume.should == {life: 42}
+ end
+ end
+end
diff --git a/spec/ruby/core/file/atime_spec.rb b/spec/ruby/core/file/atime_spec.rb
index cef07ba010..3df258016c 100644
--- a/spec/ruby/core/file/atime_spec.rb
+++ b/spec/ruby/core/file/atime_spec.rb
@@ -15,11 +15,11 @@ describe "File.atime" do
File.atime(@file).should be_kind_of(Time)
end
- platform_is :linux, :windows do
- platform_is_not :"powerpc64le-linux" do # https://bugs.ruby-lang.org/issues/17926
+ platform_is :linux do
+ unless ENV.key?('TRAVIS') # https://bugs.ruby-lang.org/issues/17926
## NOTE also that some Linux systems disable atime (e.g. via mount params) for better filesystem speed.
it "returns the last access time for the named file with microseconds" do
- supports_subseconds = Integer(`stat -c%x '#{__FILE__}'`[/\.(\d+)/, 1], 10)
+ supports_subseconds = Integer(`stat -c%x '#{__FILE__}'`[/\.(\d{1,6})/, 1], 10)
if supports_subseconds != 0
expected_time = Time.at(Time.now.to_i + 0.123456)
File.utime expected_time, 0, @file
diff --git a/spec/ruby/core/file/ctime_spec.rb b/spec/ruby/core/file/ctime_spec.rb
index b16eb13c1e..9b7ab272ff 100644
--- a/spec/ruby/core/file/ctime_spec.rb
+++ b/spec/ruby/core/file/ctime_spec.rb
@@ -14,9 +14,9 @@ describe "File.ctime" do
File.ctime(@file).should be_kind_of(Time)
end
- platform_is :linux, :windows do
+ platform_is :linux do
it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself) with microseconds." do
- supports_subseconds = Integer(`stat -c%z '#{__FILE__}'`[/\.(\d+)/, 1], 10)
+ supports_subseconds = Integer(`stat -c%z '#{__FILE__}'`[/\.(\d{1,6})/, 1], 10)
if supports_subseconds != 0
File.ctime(__FILE__).usec.should > 0
else
diff --git a/spec/ruby/core/file/mtime_spec.rb b/spec/ruby/core/file/mtime_spec.rb
index 8d47d3021a..6c43265a2c 100644
--- a/spec/ruby/core/file/mtime_spec.rb
+++ b/spec/ruby/core/file/mtime_spec.rb
@@ -15,15 +15,17 @@ describe "File.mtime" do
File.mtime(@filename).should be_close(@mtime, TIME_TOLERANCE)
end
- platform_is :linux, :windows do
- it "returns the modification Time of the file with microseconds" do
- supports_subseconds = Integer(`stat -c%y '#{__FILE__}'`[/\.(\d+)/, 1], 10)
- if supports_subseconds != 0
- expected_time = Time.at(Time.now.to_i + 0.123456)
- File.utime 0, expected_time, @filename
- File.mtime(@filename).usec.should == expected_time.usec
- else
- File.mtime(__FILE__).usec.should == 0
+ platform_is :linux do
+ unless ENV.key?('TRAVIS') # https://bugs.ruby-lang.org/issues/17926
+ it "returns the modification Time of the file with microseconds" do
+ supports_subseconds = Integer(`stat -c%y '#{__FILE__}'`[/\.(\d{1,6})/, 1], 10)
+ if supports_subseconds != 0
+ expected_time = Time.at(Time.now.to_i + 0.123456)
+ File.utime 0, expected_time, @filename
+ File.mtime(@filename).usec.should == expected_time.usec
+ else
+ File.mtime(__FILE__).usec.should == 0
+ end
end
end
end
diff --git a/spec/ruby/core/file/shared/fnmatch.rb b/spec/ruby/core/file/shared/fnmatch.rb
index 00682bb64c..94f22144b0 100644
--- a/spec/ruby/core/file/shared/fnmatch.rb
+++ b/spec/ruby/core/file/shared/fnmatch.rb
@@ -159,10 +159,10 @@ describe :file_fnmatch, shared: true do
end
it "does not match leading periods in filenames with wildcards by default" do
- File.send(@method, '*', '.profile').should == false
- File.send(@method, '*', 'home/.profile').should == true
- File.send(@method, '*/*', 'home/.profile').should == true
- File.send(@method, '*/*', 'dave/.profile', File::FNM_PATHNAME).should == false
+ File.should_not.send(@method, '*', '.profile')
+ File.should.send(@method, '*', 'home/.profile')
+ File.should.send(@method, '*/*', 'home/.profile')
+ File.should_not.send(@method, '*/*', 'dave/.profile', File::FNM_PATHNAME)
end
it "matches patterns with leading periods to dotfiles by default" do
diff --git a/spec/ruby/core/file/shared/path.rb b/spec/ruby/core/file/shared/path.rb
index 0a5abe33f0..ee8109ba05 100644
--- a/spec/ruby/core/file/shared/path.rb
+++ b/spec/ruby/core/file/shared/path.rb
@@ -78,13 +78,15 @@ describe :file_path, shared: true do
rm_r @dir
end
- it "raises IOError if file was opened with File::TMPFILE" do
- begin
- File.open(@dir, File::RDWR | File::TMPFILE) do |f|
- -> { f.send(@method) }.should raise_error(IOError)
+ ruby_version_is ""..."3.1" do
+ it "raises IOError if file was opened with File::TMPFILE" do
+ begin
+ File.open(@dir, File::RDWR | File::TMPFILE) do |f|
+ -> { f.send(@method) }.should raise_error(IOError)
+ end
+ rescue Errno::EOPNOTSUPP, Errno::EINVAL, Errno::EISDIR
+ skip "no support from the filesystem"
end
- rescue Errno::EOPNOTSUPP, Errno::EINVAL, Errno::EISDIR
- skip "no support from the filesystem"
end
end
end
diff --git a/spec/ruby/core/file/utime_spec.rb b/spec/ruby/core/file/utime_spec.rb
index a191e29240..cf7a0aec20 100644
--- a/spec/ruby/core/file/utime_spec.rb
+++ b/spec/ruby/core/file/utime_spec.rb
@@ -19,18 +19,20 @@ describe "File.utime" do
rm_r @file1, @file2
end
- it "sets the access and modification time of each file" do
- File.utime(@atime, @mtime, @file1, @file2)
- if @time_is_float
- File.atime(@file1).should be_close(@atime, 0.0001)
- File.mtime(@file1).should be_close(@mtime, 0.0001)
- File.atime(@file2).should be_close(@atime, 0.0001)
- File.mtime(@file2).should be_close(@mtime, 0.0001)
- else
- File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
- File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
- File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
- File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ platform_is_not :windows do
+ it "sets the access and modification time of each file" do
+ File.utime(@atime, @mtime, @file1, @file2)
+ if @time_is_float
+ File.atime(@file1).should be_close(@atime, 0.0001)
+ File.mtime(@file1).should be_close(@mtime, 0.0001)
+ File.atime(@file2).should be_close(@atime, 0.0001)
+ File.mtime(@file2).should be_close(@mtime, 0.0001)
+ else
+ File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
+ File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
+ File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ end
end
end
@@ -83,17 +85,19 @@ describe "File.utime" do
platform_is :linux do
platform_is wordsize: 64 do
- it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19)" do
+ it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do
# https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps
# "Therefore, timestamps should not overflow until May 2446."
# https://lwn.net/Articles/804382/
# "On-disk timestamps hitting the y2038 limit..."
# The problem seems to be being improved, but currently it actually fails on XFS on RHEL8
# https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz
+ # Amazon Linux 2023 returns 2486-07-02 in this example
+ # http://rubyci.s3.amazonaws.com/amazon2023/ruby-master/log/20230322T063004Z.fail.html.gz
time = Time.at(1<<44)
File.utime(time, time, @file1)
- [559444, 2446, 2038].should.include? File.atime(@file1).year
- [559444, 2446, 2038].should.include? File.mtime(@file1).year
+ [559444, 2486, 2446, 2038].should.include? File.atime(@file1).year
+ [559444, 2486, 2446, 2038].should.include? File.mtime(@file1).year
end
end
end
diff --git a/spec/ruby/core/float/comparison_spec.rb b/spec/ruby/core/float/comparison_spec.rb
index 53e7ec332a..1373b3a1fb 100644
--- a/spec/ruby/core/float/comparison_spec.rb
+++ b/spec/ruby/core/float/comparison_spec.rb
@@ -7,9 +7,25 @@ describe "Float#<=>" do
((bignum_value*1.1) <=> bignum_value).should == 1
end
- it "returns nil when either argument is NaN" do
- (nan_value <=> 71.2).should be_nil
- (1771.176 <=> nan_value).should be_nil
+ it "returns nil if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value <=> n).should == nil
+ (n <=> nan_value).should == nil
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value <=> n).should == 1
+ (n <=> infinity_value).should == -1
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value <=> n).should == -1
+ (n <=> -infinity_value).should == 1
+ }
end
it "returns nil when the given argument is not a Float" do
@@ -49,21 +65,10 @@ describe "Float#<=>" do
}.should raise_error(TypeError, "coerce must return [x, y]")
end
- # The 4 tests below are taken from matz's revision 23730 for Ruby trunk
- #
- it "returns 1 when self is Infinity and other is an Integer" do
+ it "returns the correct result when one side is infinite" do
(infinity_value <=> Float::MAX.to_i*2).should == 1
- end
-
- it "returns -1 when self is negative and other is Infinity" do
(-Float::MAX.to_i*2 <=> infinity_value).should == -1
- end
-
- it "returns -1 when self is -Infinity and other is negative" do
(-infinity_value <=> -Float::MAX.to_i*2).should == -1
- end
-
- it "returns 1 when self is negative and other is -Infinity" do
(-Float::MAX.to_i*2 <=> -infinity_value).should == 1
end
diff --git a/spec/ruby/core/float/divmod_spec.rb b/spec/ruby/core/float/divmod_spec.rb
index 523217ac1f..dad45a9b89 100644
--- a/spec/ruby/core/float/divmod_spec.rb
+++ b/spec/ruby/core/float/divmod_spec.rb
@@ -23,7 +23,7 @@ describe "Float#divmod" do
# Behaviour established as correct in r23953
it "raises a FloatDomainError if other is NaN" do
- -> { 1.divmod(nan_value) }.should raise_error(FloatDomainError)
+ -> { 1.0.divmod(nan_value) }.should raise_error(FloatDomainError)
end
# Behaviour established as correct in r23953
diff --git a/spec/ruby/core/float/gt_spec.rb b/spec/ruby/core/float/gt_spec.rb
index 0d73f1c3df..33078e07ce 100644
--- a/spec/ruby/core/float/gt_spec.rb
+++ b/spec/ruby/core/float/gt_spec.rb
@@ -14,4 +14,25 @@ describe "Float#>" do
-> { 5.0 > "4" }.should raise_error(ArgumentError)
-> { 5.0 > mock('x') }.should raise_error(ArgumentError)
end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value > n).should == false
+ (n > nan_value).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value > n).should == true
+ (n > infinity_value).should == false
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value > n).should == false
+ (n > -infinity_value).should == true
+ }
+ end
end
diff --git a/spec/ruby/core/float/gte_spec.rb b/spec/ruby/core/float/gte_spec.rb
index 98ec60b70b..44c0a81b43 100644
--- a/spec/ruby/core/float/gte_spec.rb
+++ b/spec/ruby/core/float/gte_spec.rb
@@ -14,4 +14,25 @@ describe "Float#>=" do
-> { 5.0 >= "4" }.should raise_error(ArgumentError)
-> { 5.0 >= mock('x') }.should raise_error(ArgumentError)
end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value >= n).should == false
+ (n >= nan_value).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value >= n).should == true
+ (n >= infinity_value).should == false
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value >= n).should == false
+ (n >= -infinity_value).should == true
+ }
+ end
end
diff --git a/spec/ruby/core/float/lt_spec.rb b/spec/ruby/core/float/lt_spec.rb
index c01b6e0e02..94dcfc42f8 100644
--- a/spec/ruby/core/float/lt_spec.rb
+++ b/spec/ruby/core/float/lt_spec.rb
@@ -14,4 +14,25 @@ describe "Float#<" do
-> { 5.0 < "4" }.should raise_error(ArgumentError)
-> { 5.0 < mock('x') }.should raise_error(ArgumentError)
end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value < n).should == false
+ (n < nan_value).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value < n).should == false
+ (n < infinity_value).should == true
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value < n).should == true
+ (n < -infinity_value).should == false
+ }
+ end
end
diff --git a/spec/ruby/core/float/lte_spec.rb b/spec/ruby/core/float/lte_spec.rb
index 66f2ddc2c7..7b5a86ee76 100644
--- a/spec/ruby/core/float/lte_spec.rb
+++ b/spec/ruby/core/float/lte_spec.rb
@@ -15,4 +15,25 @@ describe "Float#<=" do
-> { 5.0 <= "4" }.should raise_error(ArgumentError)
-> { 5.0 <= mock('x') }.should raise_error(ArgumentError)
end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value <= n).should == false
+ (n <= nan_value).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value <= n).should == false
+ (n <= infinity_value).should == true
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value <= n).should == true
+ (n <= -infinity_value).should == false
+ }
+ end
end
diff --git a/spec/ruby/core/float/shared/equal.rb b/spec/ruby/core/float/shared/equal.rb
index 668aa069b5..4d524e1cf2 100644
--- a/spec/ruby/core/float/shared/equal.rb
+++ b/spec/ruby/core/float/shared/equal.rb
@@ -14,4 +14,25 @@ describe :float_equal, shared: true do
1.0.send(@method, x).should == false
2.0.send(@method, x).should == true
end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value.send(@method, n)).should == false
+ (n.send(@method, nan_value)).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value.send(@method, n)).should == false
+ (n.send(@method, infinity_value)).should == false
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ ((-infinity_value).send(@method, n)).should == false
+ (n.send(@method, -infinity_value)).should == false
+ }
+ end
end
diff --git a/spec/ruby/core/float/shared/to_i.rb b/spec/ruby/core/float/shared/to_i.rb
index 960295f095..33b32ca533 100644
--- a/spec/ruby/core/float/shared/to_i.rb
+++ b/spec/ruby/core/float/shared/to_i.rb
@@ -7,4 +7,8 @@ describe :float_to_i, shared: true do
-9223372036854775808.1.send(@method).should eql(-9223372036854775808)
9223372036854775808.1.send(@method).should eql(9223372036854775808)
end
+
+ it "raises a FloatDomainError for NaN" do
+ -> { nan_value.send(@method) }.should raise_error(FloatDomainError)
+ end
end
diff --git a/spec/ruby/core/hash/hash_spec.rb b/spec/ruby/core/hash/hash_spec.rb
index 3649d4d8de..2ccb483120 100644
--- a/spec/ruby/core/hash/hash_spec.rb
+++ b/spec/ruby/core/hash/hash_spec.rb
@@ -41,4 +41,13 @@ describe "Hash#hash" do
h.hash.should == {x: [h]}.hash
# Like above, because h.eql?(x: [h])
end
+
+ ruby_version_is "3.1" do
+ it "allows ommiting values" do
+ a = 1
+ b = 2
+
+ eval('{a:, b:}.should == { a: 1, b: 2 }')
+ end
+ end
end
diff --git a/spec/ruby/core/io/fixtures/classes.rb b/spec/ruby/core/io/fixtures/classes.rb
index 067ab59d93..204a2a101b 100644
--- a/spec/ruby/core/io/fixtures/classes.rb
+++ b/spec/ruby/core/io/fixtures/classes.rb
@@ -7,6 +7,18 @@ module IOSpecs
class SubIO < IO
end
+ class SubIOWithRedefinedNew < IO
+ def self.new(...)
+ ScratchPad << :redefined_new_called
+ super
+ end
+
+ def initialize(...)
+ ScratchPad << :call_original_initialize
+ super
+ end
+ end
+
def self.collector
Proc.new { |x| ScratchPad << x }
end
diff --git a/spec/ruby/core/io/gets_spec.rb b/spec/ruby/core/io/gets_spec.rb
index b9f82f8133..d0c91705af 100644
--- a/spec/ruby/core/io/gets_spec.rb
+++ b/spec/ruby/core/io/gets_spec.rb
@@ -213,6 +213,12 @@ describe "IO#gets" do
it "returns empty string when 0 passed as a limit" do
@io.gets(0).should == ""
+ @io.gets(nil, 0).should == ""
+ @io.gets("", 0).should == ""
+ end
+
+ it "does not accept limit that doesn't fit in a C off_t" do
+ -> { @io.gets(2**128) }.should raise_error(RangeError)
end
end
diff --git a/spec/ruby/core/io/lineno_spec.rb b/spec/ruby/core/io/lineno_spec.rb
index 99266ecca1..9a4ad90880 100644
--- a/spec/ruby/core/io/lineno_spec.rb
+++ b/spec/ruby/core/io/lineno_spec.rb
@@ -92,8 +92,13 @@ describe "IO#lineno=" do
@io.lineno.should == 92233
end
- it "raises TypeError on nil argument" do
- -> { @io.lineno = nil }.should raise_error(TypeError)
+ it "raises TypeError if cannot convert argument to Integer implicitly" do
+ -> { @io.lineno = "1" }.should raise_error(TypeError, 'no implicit conversion of String into Integer')
+ -> { @io.lineno = nil }.should raise_error(TypeError, 'no implicit conversion from nil to integer')
+ end
+
+ it "does not accept Integers that don't fit in a C int" do
+ -> { @io.lineno = 2**32 }.should raise_error(RangeError)
end
it "sets the current line number to the given value" do
diff --git a/spec/ruby/core/io/new_spec.rb b/spec/ruby/core/io/new_spec.rb
index 3597098caf..0ef30991fd 100644
--- a/spec/ruby/core/io/new_spec.rb
+++ b/spec/ruby/core/io/new_spec.rb
@@ -1,6 +1,8 @@
require_relative '../../spec_helper'
require_relative 'shared/new'
+# NOTE: should be syncronized with library/stringio/initialize_spec.rb
+
describe "IO.new" do
it_behaves_like :io_new, :new
end
diff --git a/spec/ruby/core/io/path_spec.rb b/spec/ruby/core/io/path_spec.rb
new file mode 100644
index 0000000000..8145c32f39
--- /dev/null
+++ b/spec/ruby/core/io/path_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "IO#path" do
+ ruby_version_is "3.2" do
+ it "returns the path of the file associated with the IO object" do
+ path = tmp("io_path.txt")
+ File.open(path, "w") do |file|
+ IO.new(file.fileno, path: file.path, autoclose: false).path.should == file.path
+ end
+ ensure
+ File.unlink(path)
+ end
+ end
+end
diff --git a/spec/ruby/core/io/pipe_spec.rb b/spec/ruby/core/io/pipe_spec.rb
index 2f2cf06f4d..aee0d9003f 100644
--- a/spec/ruby/core/io/pipe_spec.rb
+++ b/spec/ruby/core/io/pipe_spec.rb
@@ -25,6 +25,17 @@ describe "IO.pipe" do
@r.should be_an_instance_of(IOSpecs::SubIO)
@w.should be_an_instance_of(IOSpecs::SubIO)
end
+
+ it "does not use IO.new method to create pipes and allows its overriding" do
+ ScratchPad.record []
+
+ # so redefined .new is not called, but original #initialize is
+ @r, @w = IOSpecs::SubIOWithRedefinedNew.pipe
+ ScratchPad.recorded.should == [:call_original_initialize, :call_original_initialize] # called 2 times - for each pipe (r and w)
+
+ @r.should be_an_instance_of(IOSpecs::SubIOWithRedefinedNew)
+ @w.should be_an_instance_of(IOSpecs::SubIOWithRedefinedNew)
+ end
end
describe "IO.pipe" do
diff --git a/spec/ruby/core/io/print_spec.rb b/spec/ruby/core/io/print_spec.rb
index 04e971ef6d..085852024c 100644
--- a/spec/ruby/core/io/print_spec.rb
+++ b/spec/ruby/core/io/print_spec.rb
@@ -3,16 +3,27 @@ require_relative 'fixtures/classes'
describe "IO#print" do
before :each do
- @old_separator = $\
- suppress_warning {$\ = '->'}
+ @old_record_separator = $\
+ @old_field_separator = $,
+ suppress_warning {
+ $\ = '->'
+ $, = '^^'
+ }
@name = tmp("io_print")
end
after :each do
- suppress_warning {$\ = @old_separator}
+ suppress_warning {
+ $\ = @old_record_separator
+ $, = @old_field_separator
+ }
rm_r @name
end
+ it "returns nil" do
+ touch(@name) { |f| f.print.should be_nil }
+ end
+
it "writes $_.to_s followed by $\\ (if any) to the stream if no arguments given" do
o = mock('o')
o.should_receive(:to_s).and_return("mockmockmock")
@@ -38,13 +49,15 @@ describe "IO#print" do
IO.read(@name).should == "hello#{$\}"
end
- it "writes each obj.to_s to the stream and appends $\\ (if any) given multiple objects" do
+ it "writes each obj.to_s to the stream separated by $, (if any) and appends $\\ (if any) given multiple objects" do
o, o2 = Object.new, Object.new
def o.to_s(); 'o'; end
def o2.to_s(); 'o2'; end
- touch(@name) { |f| f.print(o, o2) }
- IO.read(@name).should == "#{o.to_s}#{o2.to_s}#{$\}"
+ suppress_warning {
+ touch(@name) { |f| f.print(o, o2) }
+ }
+ IO.read(@name).should == "#{o.to_s}#{$,}#{o2.to_s}#{$\}"
end
it "raises IOError on closed stream" do
diff --git a/spec/ruby/core/io/read_nonblock_spec.rb b/spec/ruby/core/io/read_nonblock_spec.rb
index e50531d336..a62b75274c 100644
--- a/spec/ruby/core/io/read_nonblock_spec.rb
+++ b/spec/ruby/core/io/read_nonblock_spec.rb
@@ -55,6 +55,27 @@ describe "IO#read_nonblock" do
@read.read_nonblock(4).should == "hell"
end
+ it "reads after ungetc with data in the buffer" do
+ @write.write("foobar")
+ @read.set_encoding(
+ 'utf-8', universal_newline: false
+ )
+ c = @read.getc
+ @read.ungetc(c)
+ @read.read_nonblock(3).should == "foo"
+ @read.read_nonblock(3).should == "bar"
+ end
+
+ it "raises an exception after ungetc with data in the buffer and character conversion enabled" do
+ @write.write("foobar")
+ @read.set_encoding(
+ 'utf-8', universal_newline: true
+ )
+ c = @read.getc
+ @read.ungetc(c)
+ -> { @read.read_nonblock(3).should == "foo" }.should raise_error(IOError)
+ end
+
it "returns less data if that is all that is available" do
@write << "hello"
@read.read_nonblock(10).should == "hello"
@@ -70,6 +91,10 @@ describe "IO#read_nonblock" do
@read.read_nonblock(1).should == "1"
end
+ it "raises ArgumentError when length is less than 0" do
+ -> { @read.read_nonblock(-1) }.should raise_error(ArgumentError)
+ end
+
it "reads into the passed buffer" do
buffer = ""
@write.write("1")
@@ -84,6 +109,21 @@ describe "IO#read_nonblock" do
output.should equal(buffer)
end
+ it "discards the existing buffer content upon successful read" do
+ buffer = "existing content"
+ @write.write("hello world")
+ @write.close
+ @read.read_nonblock(11, buffer)
+ buffer.should == "hello world"
+ end
+
+ it "discards the existing buffer content upon error" do
+ buffer = "existing content"
+ @write.close
+ -> { @read.read_nonblock(1, buffer) }.should raise_error(EOFError)
+ buffer.should be_empty
+ end
+
it "raises IOError on closed stream" do
-> { IOSpecs.closed_io.read_nonblock(5) }.should raise_error(IOError)
end
@@ -96,4 +136,13 @@ describe "IO#read_nonblock" do
-> { @read.read_nonblock(5) }.should raise_error(EOFError)
end
+
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @write.write("abc")
+ @write.close
+ @read.read_nonblock(10, buffer)
+
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
end
diff --git a/spec/ruby/core/io/read_spec.rb b/spec/ruby/core/io/read_spec.rb
index 841e693f37..529afbf0ff 100644
--- a/spec/ruby/core/io/read_spec.rb
+++ b/spec/ruby/core/io/read_spec.rb
@@ -104,6 +104,14 @@ describe "IO.read" do
str = IO.read(@fname, encoding: Encoding::ISO_8859_1)
str.encoding.should == Encoding::ISO_8859_1
end
+
+ platform_is :windows do
+ it "reads the file in text mode" do
+ # 0x1A is CTRL+Z and is EOF in Windows text mode.
+ File.binwrite(@fname, "\x1Abbb")
+ IO.read(@fname).should.empty?
+ end
+ end
end
describe "IO.read from a pipe" do
@@ -262,6 +270,13 @@ describe "IO#read" do
@io.read(nil, buf).should equal buf
end
+ it "returns the given buffer when there is nothing to read" do
+ buf = ""
+
+ @io.read
+ @io.read(nil, buf).should equal buf
+ end
+
it "coerces the second argument to string and uses it as a buffer" do
buf = "ABCDE"
obj = mock("buff")
@@ -304,6 +319,9 @@ describe "IO#read" do
-> { IOSpecs.closed_io.read }.should raise_error(IOError)
end
+ it "raises ArgumentError when length is less than 0" do
+ -> { @io.read(-1) }.should raise_error(ArgumentError)
+ end
platform_is_not :windows do
it "raises IOError when stream is closed by another thread" do
@@ -384,13 +402,6 @@ describe "IO#read in binary mode" do
xE2 = [226].pack('C*')
result.should == ("abc" + xE2 + "def").force_encoding(Encoding::BINARY)
end
-
- it "does not transcode file contents when an internal encoding is specified" do
- result = File.open(@name, "r:binary:utf-8") { |f| f.read }.chomp
- result.encoding.should == Encoding::BINARY
- xE2 = [226].pack('C*')
- result.should == ("abc" + xE2 + "def").force_encoding(Encoding::BINARY)
- end
end
describe "IO#read in text mode" do
diff --git a/spec/ruby/core/io/readchar_spec.rb b/spec/ruby/core/io/readchar_spec.rb
index b5f762a846..a66773851a 100644
--- a/spec/ruby/core/io/readchar_spec.rb
+++ b/spec/ruby/core/io/readchar_spec.rb
@@ -1,6 +1,16 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+describe :io_readchar_internal_encoding, shared: true do
+ it "returns a transcoded String" do
+ @io.readchar.should == "あ"
+ end
+
+ it "sets the String encoding to the internal encoding" do
+ @io.readchar.encoding.should equal(Encoding::UTF_8)
+ end
+end
+
describe "IO#readchar" do
before :each do
@io = IOSpecs.io_fixture "lines.txt"
@@ -29,6 +39,62 @@ describe "IO#readchar" do
end
end
+describe "IO#readchar with internal encoding" do
+ after :each do
+ @io.close if @io
+ end
+
+ describe "not specified" do
+ before :each do
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", "r:euc-jp"
+ end
+
+ it "does not transcode the String" do
+ @io.readchar.should == ("あ").encode(Encoding::EUC_JP)
+ end
+
+ it "sets the String encoding to the external encoding" do
+ @io.readchar.encoding.should equal(Encoding::EUC_JP)
+ end
+ end
+
+ describe "specified by open mode" do
+ before :each do
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", "r:euc-jp:utf-8"
+ end
+
+ it_behaves_like :io_readchar_internal_encoding, nil
+ end
+
+ describe "specified by mode: option" do
+ before :each do
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", mode: "r:euc-jp:utf-8"
+ end
+
+ it_behaves_like :io_readchar_internal_encoding, nil
+ end
+
+ describe "specified by internal_encoding: option" do
+ before :each do
+ options = { mode: "r",
+ internal_encoding: "utf-8",
+ external_encoding: "euc-jp" }
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", options
+ end
+
+ it_behaves_like :io_readchar_internal_encoding, nil
+ end
+
+ describe "specified by encoding: option" do
+ before :each do
+ options = { mode: "r", encoding: "euc-jp:utf-8" }
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", options
+ end
+
+ it_behaves_like :io_readchar_internal_encoding, nil
+ end
+end
+
describe "IO#readchar" do
before :each do
@io = IOSpecs.io_fixture "empty.txt"
diff --git a/spec/ruby/core/io/readline_spec.rb b/spec/ruby/core/io/readline_spec.rb
index ca30f31e39..cf9f0dfc11 100644
--- a/spec/ruby/core/io/readline_spec.rb
+++ b/spec/ruby/core/io/readline_spec.rb
@@ -51,6 +51,10 @@ describe "IO#readline" do
it "returns an empty string when passed 0 as a limit" do
@io.readline(0).should == ""
end
+
+ it "does not accept Integers that don't fit in a C off_t" do
+ -> { @io.readline(2**128) }.should raise_error(RangeError)
+ end
end
describe "when passed separator and limit" do
diff --git a/spec/ruby/core/io/readlines_spec.rb b/spec/ruby/core/io/readlines_spec.rb
index 15af6debbe..496003002d 100644
--- a/spec/ruby/core/io/readlines_spec.rb
+++ b/spec/ruby/core/io/readlines_spec.rb
@@ -106,6 +106,10 @@ describe "IO#readlines" do
it "raises ArgumentError when passed 0 as a limit" do
-> { @io.readlines(0) }.should raise_error(ArgumentError)
end
+
+ it "does not accept Integers that don't fit in a C off_t" do
+ -> { @io.readlines(2**128) }.should raise_error(RangeError)
+ end
end
describe "when passed chomp" do
diff --git a/spec/ruby/core/io/readpartial_spec.rb b/spec/ruby/core/io/readpartial_spec.rb
index 2b33a0d5b1..2901b429c2 100644
--- a/spec/ruby/core/io/readpartial_spec.rb
+++ b/spec/ruby/core/io/readpartial_spec.rb
@@ -59,7 +59,7 @@ describe "IO#readpartial" do
end
it "discards the existing buffer content upon successful read" do
- buffer = "existing"
+ buffer = "existing content"
@wr.write("hello world")
@wr.close
@rd.readpartial(11, buffer)
@@ -93,4 +93,19 @@ describe "IO#readpartial" do
@rd.readpartial(0).should == ""
end
+ ruby_bug "#18421", ""..."3.0.4" do
+ it "clears and returns the given buffer if the length argument is 0" do
+ buffer = "existing content"
+ @rd.readpartial(0, buffer).should == buffer
+ buffer.should == ""
+ end
+ end
+
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @wr.write("abc")
+ @wr.close
+ @rd.readpartial(10, buffer)
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
end
diff --git a/spec/ruby/core/io/rewind_spec.rb b/spec/ruby/core/io/rewind_spec.rb
index 649041afaf..5579cbd988 100644
--- a/spec/ruby/core/io/rewind_spec.rb
+++ b/spec/ruby/core/io/rewind_spec.rb
@@ -18,6 +18,17 @@ describe "IO#rewind" do
@io.readline.should == "Voici la ligne une.\n"
end
+ it "positions the instance to the beginning of output for write-only IO" do
+ name = tmp("io_rewind_spec")
+ io = File.open(name, "w")
+ io.write("Voici la ligne une.\n")
+ io.rewind
+ io.pos.should == 0
+ ensure
+ io.close
+ rm_r name
+ end
+
it "positions the instance to the beginning of input and clears EOF" do
value = @io.read
@io.rewind
@@ -32,6 +43,10 @@ describe "IO#rewind" do
@io.lineno.should == 0
end
+ it "returns 0" do
+ @io.rewind.should == 0
+ end
+
it "raises IOError on closed stream" do
-> { IOSpecs.closed_io.rewind }.should raise_error(IOError)
end
diff --git a/spec/ruby/core/io/set_encoding_by_bom_spec.rb b/spec/ruby/core/io/set_encoding_by_bom_spec.rb
index b52d3a943a..92433d6640 100644
--- a/spec/ruby/core/io/set_encoding_by_bom_spec.rb
+++ b/spec/ruby/core/io/set_encoding_by_bom_spec.rb
@@ -12,45 +12,232 @@ describe "IO#set_encoding_by_bom" do
rm_r @name
end
+ it "returns nil if not readable" do
+ not_readable_io = new_io(@name, 'wb')
+
+ not_readable_io.set_encoding_by_bom.should be_nil
+ not_readable_io.external_encoding.should == Encoding::ASCII_8BIT
+ ensure
+ not_readable_io.close
+ end
+
it "returns the result encoding if found BOM UTF-8 sequence" do
+ File.binwrite(@name, "\u{FEFF}")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_8
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
File.binwrite(@name, "\u{FEFF}abc")
@io.set_encoding_by_bom.should == Encoding::UTF_8
@io.external_encoding.should == Encoding::UTF_8
+ @io.read.b.should == "abc".b
end
it "returns the result encoding if found BOM UTF_16LE sequence" do
+ File.binwrite(@name, "\xFF\xFE")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
File.binwrite(@name, "\xFF\xFEabc")
@io.set_encoding_by_bom.should == Encoding::UTF_16LE
@io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "abc".b
end
it "returns the result encoding if found BOM UTF_16BE sequence" do
+ File.binwrite(@name, "\xFE\xFF")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16BE
+ @io.external_encoding.should == Encoding::UTF_16BE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
File.binwrite(@name, "\xFE\xFFabc")
@io.set_encoding_by_bom.should == Encoding::UTF_16BE
@io.external_encoding.should == Encoding::UTF_16BE
+ @io.read.b.should == "abc".b
end
it "returns the result encoding if found BOM UTF_32LE sequence" do
+ File.binwrite(@name, "\xFF\xFE\x00\x00")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_32LE
+ @io.external_encoding.should == Encoding::UTF_32LE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
File.binwrite(@name, "\xFF\xFE\x00\x00abc")
@io.set_encoding_by_bom.should == Encoding::UTF_32LE
@io.external_encoding.should == Encoding::UTF_32LE
+ @io.read.b.should == "abc".b
end
it "returns the result encoding if found BOM UTF_32BE sequence" do
+ File.binwrite(@name, "\x00\x00\xFE\xFF")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_32BE
+ @io.external_encoding.should == Encoding::UTF_32BE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
File.binwrite(@name, "\x00\x00\xFE\xFFabc")
@io.set_encoding_by_bom.should == Encoding::UTF_32BE
@io.external_encoding.should == Encoding::UTF_32BE
+ @io.read.b.should == "abc".b
+ end
+
+ it "returns nil if io is empty" do
+ @io.set_encoding_by_bom.should be_nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "returns nil if UTF-8 BOM sequence is incomplete" do
+ File.write(@name, "\xEF")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xEF".b
+ @io.rewind
+
+ File.write(@name, "\xEFa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xEFa".b
+ @io.rewind
+
+ File.write(@name, "\xEF\xBB")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xEF\xBB".b
+ @io.rewind
+
+ File.write(@name, "\xEF\xBBa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xEF\xBBa".b
+ end
+
+ it "returns nil if UTF-16BE BOM sequence is incomplete" do
+ File.write(@name, "\xFE")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xFE".b
+ @io.rewind
+
+ File.write(@name, "\xFEa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xFEa".b
+ end
+
+ it "returns nil if UTF-16LE/UTF-32LE BOM sequence is incomplete" do
+ File.write(@name, "\xFF")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xFF".b
+ @io.rewind
+
+ File.write(@name, "\xFFa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xFFa".b
+ end
+
+ it "returns UTF-16LE if UTF-32LE BOM sequence is incomplete" do
+ File.write(@name, "\xFF\xFE")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
+ File.write(@name, "\xFF\xFE\x00")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "\x00".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
+ File.write(@name, "\xFF\xFE\x00a")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "\x00a".b
+ end
+
+ it "returns nil if UTF-32BE BOM sequence is incomplete" do
+ File.write(@name, "\x00")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00".b
+ @io.rewind
+
+ File.write(@name, "\x00a")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00a".b
+ @io.rewind
+
+ File.write(@name, "\x00\x00")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00\x00".b
+ @io.rewind
+
+ File.write(@name, "\x00\x00a")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00\x00a".b
+ @io.rewind
+
+ File.write(@name, "\x00\x00\xFE")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00\x00\xFE".b
+ @io.rewind
+
+ File.write(@name, "\x00\x00\xFEa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00\x00\xFEa".b
end
it "returns nil if found BOM sequence not provided" do
File.write(@name, "abc")
@io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read(3).should == "abc".b
end
it 'returns exception if io not in binary mode' do
diff --git a/spec/ruby/core/io/set_encoding_spec.rb b/spec/ruby/core/io/set_encoding_spec.rb
index 5aec6a96c3..22d9017635 100644
--- a/spec/ruby/core/io/set_encoding_spec.rb
+++ b/spec/ruby/core/io/set_encoding_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe :io_set_encoding_write, shared: true do
- it "sets the encodings to nil" do
+ it "sets the encodings to nil when they were set previously" do
@io = new_io @name, "#{@object}:ibm437:ibm866"
@io.set_encoding nil, nil
@@ -9,6 +9,19 @@ describe :io_set_encoding_write, shared: true do
@io.internal_encoding.should be_nil
end
+ it "sets the encodings to nil when the IO is built with no explicit encoding" do
+ @io = new_io @name, @object
+
+ # Checking our assumptions first
+ @io.external_encoding.should be_nil
+ @io.internal_encoding.should be_nil
+
+ @io.set_encoding nil, nil
+
+ @io.external_encoding.should be_nil
+ @io.internal_encoding.should be_nil
+ end
+
it "prevents the encodings from changing when Encoding defaults are changed" do
@io = new_io @name, "#{@object}:utf-8:us-ascii"
@io.set_encoding nil, nil
@@ -38,6 +51,7 @@ describe "IO#set_encoding when passed nil, nil" do
@external = Encoding.default_external
@internal = Encoding.default_internal
+ # The defaults
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = nil
@@ -113,6 +127,22 @@ describe "IO#set_encoding when passed nil, nil" do
describe "with 'a+' mode" do
it_behaves_like :io_set_encoding_write, nil, "a+"
end
+
+ describe "with standard IOs" do
+ it "correctly resets them" do
+ STDOUT.external_encoding.should == nil
+ STDOUT.internal_encoding.should == nil
+
+ begin
+ STDOUT.set_encoding(Encoding::US_ASCII, Encoding::ISO_8859_1)
+ ensure
+ STDOUT.set_encoding(nil, nil)
+ end
+
+ STDOUT.external_encoding.should == nil
+ STDOUT.internal_encoding.should == nil
+ end
+ end
end
describe "IO#set_encoding" do
@@ -188,4 +218,21 @@ describe "IO#set_encoding" do
@io.external_encoding.should == Encoding::UTF_8
@io.internal_encoding.should == Encoding::UTF_16BE
end
+
+ it "saves encoding options passed as a hash in the last argument" do
+ File.write(@name, "\xff")
+ io = File.open(@name)
+ io.set_encoding(Encoding::EUC_JP, Encoding::SHIFT_JIS, invalid: :replace, replace: ".")
+ io.read.should == "."
+ ensure
+ io.close
+ end
+
+ it "raises ArgumentError when no arguments are given" do
+ -> { @io.set_encoding() }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError when too many arguments are given" do
+ -> { @io.set_encoding(1, 2, 3) }.should raise_error(ArgumentError)
+ end
end
diff --git a/spec/ruby/core/io/shared/each.rb b/spec/ruby/core/io/shared/each.rb
index badf8985e0..02bbe19c1a 100644
--- a/spec/ruby/core/io/shared/each.rb
+++ b/spec/ruby/core/io/shared/each.rb
@@ -77,6 +77,10 @@ describe :io_each, shared: true do
-> { @io.send(@method, 0){} }.should raise_error(ArgumentError)
end
end
+
+ it "does not accept Integers that don't fit in a C off_t" do
+ -> { @io.send(@method, 2**128){} }.should raise_error(RangeError)
+ end
end
describe "when passed a String containing one space as a separator" do
@@ -113,6 +117,13 @@ describe :io_each, shared: true do
@io.send(@method, "") { |s| ScratchPad << s }
ScratchPad.recorded.should == IOSpecs.paragraphs
end
+
+ it "discards leading newlines" do
+ @io.readline
+ @io.readline
+ @io.send(@method, "") { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.paragraphs[1..-1]
+ end
end
describe "with both separator and limit" do
@@ -152,6 +163,13 @@ describe :io_each, shared: true do
@io.send(@method, "", 1024) { |s| ScratchPad << s }
ScratchPad.recorded.should == IOSpecs.paragraphs
end
+
+ it "discards leading newlines" do
+ @io.readline
+ @io.readline
+ @io.send(@method, "", 1024) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.paragraphs[1..-1]
+ end
end
end
end
@@ -190,10 +208,20 @@ describe :io_each, shared: true do
end
describe "when passed chomp and nil as a separator" do
- it "yields self's content without trailing new line character" do
- @io.pos = 100
- @io.send(@method, nil, chomp: true) { |s| ScratchPad << s }
- ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six."]
+ ruby_version_is "3.2" do
+ it "yields self's content" do
+ @io.pos = 100
+ @io.send(@method, nil, chomp: true) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six.\n"]
+ end
+ end
+
+ ruby_version_is ""..."3.2" do
+ it "yields self's content without trailing new line character" do
+ @io.pos = 100
+ @io.send(@method, nil, chomp: true) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six."]
+ end
end
end
@@ -210,6 +238,14 @@ describe :io_each, shared: true do
]
end
end
+
+ describe "when passed too many arguments" do
+ it "raises ArgumentError" do
+ -> {
+ @io.send(@method, "", 1, "excess argument", chomp: true) {}
+ }.should raise_error(ArgumentError)
+ end
+ end
end
describe :io_each_default_separator, shared: true do
diff --git a/spec/ruby/core/io/shared/new.rb b/spec/ruby/core/io/shared/new.rb
index f2a0970a40..7677aada6e 100644
--- a/spec/ruby/core/io/shared/new.rb
+++ b/spec/ruby/core/io/shared/new.rb
@@ -1,5 +1,7 @@
require_relative '../fixtures/classes'
+# NOTE: should be syncronized with library/stringio/initialize_spec.rb
+
# This group of specs may ONLY contain specs that do successfully create
# an IO instance from the file descriptor returned by #new_fd helper.
describe :io_new, shared: true do
diff --git a/spec/ruby/core/io/shared/pos.rb b/spec/ruby/core/io/shared/pos.rb
index d83a6c6692..3fdd3eb2b3 100644
--- a/spec/ruby/core/io/shared/pos.rb
+++ b/spec/ruby/core/io/shared/pos.rb
@@ -60,7 +60,13 @@ describe :io_set_pos, shared: true do
end
end
- it "does not accept Integers that don't fit in a C long" do
+ it "raises TypeError when cannot convert implicitly argument to Integer" do
+ File.open @fname do |io|
+ -> { io.send @method, Object.new }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
+ end
+ end
+
+ it "does not accept Integers that don't fit in a C off_t" do
File.open @fname do |io|
-> { io.send @method, 2**128 }.should raise_error(RangeError)
end
diff --git a/spec/ruby/core/io/shared/readlines.rb b/spec/ruby/core/io/shared/readlines.rb
index 479452b71c..7681e1b5c1 100644
--- a/spec/ruby/core/io/shared/readlines.rb
+++ b/spec/ruby/core/io/shared/readlines.rb
@@ -79,6 +79,10 @@ describe :io_readlines_options_19, shared: true do
(result ? result : ScratchPad.recorded).should == IOSpecs.lines
end
+ it "does not accept Integers that don't fit in a C off_t" do
+ -> { IO.send(@method, @name, 2**128, &@object) }.should raise_error(RangeError)
+ end
+
ruby_bug "#18767", ""..."3.3" do
describe "when passed limit" do
it "raises ArgumentError when passed 0 as a limit" do
diff --git a/spec/ruby/core/io/shared/write.rb b/spec/ruby/core/io/shared/write.rb
index 270b84ac39..9503f94212 100644
--- a/spec/ruby/core/io/shared/write.rb
+++ b/spec/ruby/core/io/shared/write.rb
@@ -69,16 +69,6 @@ describe :io_write, shared: true do
-> { IOSpecs.closed_io.send(@method, "hello") }.should raise_error(IOError)
end
- it "does not modify the passed argument" do
- File.open(@filename, "w") do |f|
- f.set_encoding(Encoding::IBM437)
- # A character whose codepoint differs between UTF-8 and IBM437
- f.write "ƒ".freeze
- end
-
- File.binread(@filename).bytes.should == [159]
- end
-
describe "on a pipe" do
before :each do
@r, @w = IO.pipe
diff --git a/spec/ruby/core/io/sysread_spec.rb b/spec/ruby/core/io/sysread_spec.rb
index 8201ad47ca..e7f63cefec 100644
--- a/spec/ruby/core/io/sysread_spec.rb
+++ b/spec/ruby/core/io/sysread_spec.rb
@@ -6,7 +6,7 @@ describe "IO#sysread on a file" do
@file_name = tmp("IO_sysread_file") + $$.to_s
File.open(@file_name, "w") do |f|
# write some stuff
- f.write("012345678901234567890123456789")
+ f.write("012345678901234567890123456789\nabcdef")
end
@file = File.open(@file_name, "r+")
end
@@ -84,6 +84,29 @@ describe "IO#sysread on a file" do
it "raises IOError on closed stream" do
-> { IOSpecs.closed_io.sysread(5) }.should raise_error(IOError)
end
+
+ it "immediately returns an empty string if the length argument is 0" do
+ @file.sysread(0).should == ""
+ end
+
+ it "immediately returns the given buffer if the length argument is 0" do
+ buffer = "existing content"
+ @file.sysread(0, buffer).should == buffer
+ buffer.should == "existing content"
+ end
+
+ it "discards the existing buffer content upon successful read" do
+ buffer = "existing content"
+ @file.sysread(11, buffer)
+ buffer.should == "01234567890"
+ end
+
+ it "discards the existing buffer content upon error" do
+ buffer = "existing content"
+ @file.seek(0, :END)
+ -> { @file.sysread(1, buffer) }.should raise_error(EOFError)
+ buffer.should be_empty
+ end
end
describe "IO#sysread" do
@@ -100,4 +123,10 @@ describe "IO#sysread" do
@write.syswrite "ab"
@read.sysread(3).should == "ab"
end
+
+ guard_not -> { platform_is :windows and ruby_version_is ""..."3.2" } do # https://bugs.ruby-lang.org/issues/18880
+ it "raises ArgumentError when length is less than 0" do
+ -> { @read.sysread(-1) }.should raise_error(ArgumentError)
+ end
+ end
end
diff --git a/spec/ruby/core/io/sysseek_spec.rb b/spec/ruby/core/io/sysseek_spec.rb
index e631939bce..002f2a14eb 100644
--- a/spec/ruby/core/io/sysseek_spec.rb
+++ b/spec/ruby/core/io/sysseek_spec.rb
@@ -4,7 +4,7 @@ require_relative 'fixtures/classes'
require_relative 'shared/pos'
describe "IO#sysseek" do
- it_behaves_like :io_set_pos, :seek
+ it_behaves_like :io_set_pos, :sysseek
end
describe "IO#sysseek" do
diff --git a/spec/ruby/core/io/syswrite_spec.rb b/spec/ruby/core/io/syswrite_spec.rb
index a28cc62174..eeb8d302a7 100644
--- a/spec/ruby/core/io/syswrite_spec.rb
+++ b/spec/ruby/core/io/syswrite_spec.rb
@@ -29,6 +29,16 @@ describe "IO#syswrite on a file" do
end
end
+ it "does not modify the passed argument" do
+ File.open(@filename, "w") do |f|
+ f.set_encoding(Encoding::IBM437)
+ # A character whose codepoint differs between UTF-8 and IBM437
+ f.syswrite("ƒ".freeze)
+ end
+
+ File.binread(@filename).bytes.should == [198, 146]
+ end
+
it "warns if called immediately after a buffered IO#write" do
@file.write("abcde")
-> { @file.syswrite("fghij") }.should complain(/syswrite/)
diff --git a/spec/ruby/core/io/write_nonblock_spec.rb b/spec/ruby/core/io/write_nonblock_spec.rb
index a8b9ce0a36..5532556d8a 100644
--- a/spec/ruby/core/io/write_nonblock_spec.rb
+++ b/spec/ruby/core/io/write_nonblock_spec.rb
@@ -31,6 +31,16 @@ platform_is_not :windows do
end
end
+ it "does not modify the passed argument" do
+ File.open(@filename, "w") do |f|
+ f.set_encoding(Encoding::IBM437)
+ # A character whose codepoint differs between UTF-8 and IBM437
+ f.write_nonblock("ƒ".freeze)
+ end
+
+ File.binread(@filename).bytes.should == [198, 146]
+ end
+
it "checks if the file is writable if writing zero bytes" do
-> {
@readonly_file.write_nonblock("")
diff --git a/spec/ruby/core/io/write_spec.rb b/spec/ruby/core/io/write_spec.rb
index f29fdf3a01..bcc0582d9e 100644
--- a/spec/ruby/core/io/write_spec.rb
+++ b/spec/ruby/core/io/write_spec.rb
@@ -44,6 +44,16 @@ describe "IO#write on a file" do
@file.write("hellø").should == 6
end
+ it "does not modify the passed argument" do
+ File.open(@filename, "w") do |f|
+ f.set_encoding(Encoding::IBM437)
+ # A character whose codepoint differs between UTF-8 and IBM437
+ f.write("ƒ".freeze)
+ end
+
+ File.binread(@filename).bytes.should == [159]
+ end
+
it "uses the encoding from the given option for non-ascii encoding" do
File.open(@filename, "w", encoding: Encoding::UTF_32LE) do |file|
file.write("hi").should == 8
diff --git a/spec/ruby/core/kernel/Complex_spec.rb b/spec/ruby/core/kernel/Complex_spec.rb
index 4f043526b8..cc8177fa02 100644
--- a/spec/ruby/core/kernel/Complex_spec.rb
+++ b/spec/ruby/core/kernel/Complex_spec.rb
@@ -1,4 +1,6 @@
require_relative '../../spec_helper'
+require_relative '../../shared/kernel/complex'
+require_relative 'fixtures/Complex'
describe "Kernel.Complex()" do
describe "when passed [Complex, Complex]" do
@@ -58,7 +60,92 @@ describe "Kernel.Complex()" do
end
end
- describe "when passed a String" do
+ describe "when passed [String]" do
+ it_behaves_like :kernel_complex, :Complex_method, KernelSpecs
+
+ context "invalid argument" do
+ it "raises Encoding::CompatibilityError if String is in not ASCII-compatible encoding" do
+ -> {
+ Complex("79+4i".encode("UTF-16"))
+ }.should raise_error(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16")
+ end
+
+ it "raises ArgumentError for unrecognised Strings" do
+ -> {
+ Complex("ruby")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "ruby"')
+ end
+
+ it "raises ArgumentError for trailing garbage" do
+ -> {
+ Complex("79+4iruby")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "79+4iruby"')
+ end
+
+ it "does not understand Float::INFINITY" do
+ -> {
+ Complex("Infinity")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "Infinity"')
+
+ -> {
+ Complex("-Infinity")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "-Infinity"')
+ end
+
+ it "does not understand Float::NAN" do
+ -> {
+ Complex("NaN")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "NaN"')
+ end
+
+ it "does not understand a sequence of _" do
+ -> {
+ Complex("7__9+4__0i")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "7__9+4__0i"')
+ end
+
+ it "does not allow null-byte" do
+ -> {
+ Complex("1-2i\0")
+ }.should raise_error(ArgumentError, "string contains null byte")
+ end
+ end
+
+ context "invalid argument and exception: false passed" do
+ it "raises Encoding::CompatibilityError if String is in not ASCII-compatible encoding" do
+ -> {
+ Complex("79+4i".encode("UTF-16"), exception: false)
+ }.should raise_error(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16")
+ end
+
+ it "returns nil for unrecognised Strings" do
+ Complex("ruby", exception: false).should == nil
+ end
+
+ it "returns nil when trailing garbage" do
+ Complex("79+4iruby", exception: false).should == nil
+ end
+
+ it "returns nil for Float::INFINITY" do
+ Complex("Infinity", exception: false).should == nil
+ Complex("-Infinity", exception: false).should == nil
+ end
+
+ it "returns nil for Float::NAN" do
+ Complex("NaN", exception: false).should == nil
+ end
+
+ it "returns nil when there is a sequence of _" do
+ Complex("7__9+4__0i", exception: false).should == nil
+ end
+
+ it "returns nil when String contains null-byte" do
+ Complex("1-2i\0", exception: false).should == nil
+ end
+ end
+ end
+
+ describe "when passes [String, String]" do
it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/core/kernel/fixtures/Complex.rb b/spec/ruby/core/kernel/fixtures/Complex.rb
new file mode 100644
index 0000000000..bf14d55ad5
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/Complex.rb
@@ -0,0 +1,5 @@
+module KernelSpecs
+ def self.Complex_method(string)
+ Complex(string)
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/warn_core_method.rb b/spec/ruby/core/kernel/fixtures/warn_core_method.rb
index f5dee6b668..fd82562404 100644
--- a/spec/ruby/core/kernel/fixtures/warn_core_method.rb
+++ b/spec/ruby/core/kernel/fixtures/warn_core_method.rb
@@ -1,6 +1,6 @@
raise 'should be run without RubyGems' if defined?(Gem)
-def deprecated(n=1)
+public def deprecated(n=1)
# puts nil, caller(0), nil
warn "use X instead", uplevel: n
end
diff --git a/spec/ruby/core/kernel/p_spec.rb b/spec/ruby/core/kernel/p_spec.rb
index 1bdd1740ca..eae191aa54 100644
--- a/spec/ruby/core/kernel/p_spec.rb
+++ b/spec/ruby/core/kernel/p_spec.rb
@@ -76,10 +76,8 @@ describe "Kernel#p" do
-> { p(*[]) }.should output("")
end
-=begin Not sure how to spec this, but wanted to note the behavior here
- it "does not flush if receiver is not a TTY or a File" do
- end
-=end
+ # Not sure how to spec this, but wanted to note the behavior here
+ it "does not flush if receiver is not a TTY or a File"
end
describe "Kernel.p" do
diff --git a/spec/ruby/core/kernel/shared/load.rb b/spec/ruby/core/kernel/shared/load.rb
index 120619abef..5c41c19bf6 100644
--- a/spec/ruby/core/kernel/shared/load.rb
+++ b/spec/ruby/core/kernel/shared/load.rb
@@ -88,12 +88,12 @@ describe :kernel_load, shared: true do
describe "when passed true for 'wrap'" do
it "loads from an existing path" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true).should be_true
end
it "sets the enclosing scope to an anonymous module" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
Object.const_defined?(:LoadSpecWrap).should be_false
@@ -103,14 +103,14 @@ describe :kernel_load, shared: true do
end
it "allows referencing outside namespaces" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
ScratchPad.recorded[0].should equal(String)
end
it "sets self as a copy of the top-level main" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
top_level = ScratchPad.recorded[2]
@@ -127,7 +127,7 @@ describe :kernel_load, shared: true do
main_ancestors = main.singleton_class.ancestors[1..-1]
main_ancestors.first.should == mod
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
top_level = ScratchPad.recorded[2]
@@ -154,6 +154,41 @@ describe :kernel_load, shared: true do
end
end
+ describe "when passed a module for 'wrap'" do
+ ruby_version_is "3.1" do
+ it "sets the enclosing scope to the supplied module" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ mod = Module.new
+ @object.load(path, mod)
+
+ Object.const_defined?(:LoadSpecWrap).should be_false
+ mod.const_defined?(:LoadSpecWrap).should be_true
+
+ wrap_module = ScratchPad.recorded[1]
+ wrap_module.should == mod
+ end
+
+ it "makes constants and instance methods in the source file reachable with the supplied module" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ mod = Module.new
+ @object.load(path, mod)
+
+ mod::LOAD_WRAP_SPECS_TOP_LEVEL_CONSTANT.should == 1
+ obj = Object.new
+ obj.extend(mod)
+ obj.send(:load_wrap_specs_top_level_method).should == :load_wrap_specs_top_level_method
+ end
+
+ it "makes instance methods in the source file private" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ mod = Module.new
+ @object.load(path, mod)
+
+ mod.private_instance_methods.include?(:load_wrap_specs_top_level_method).should == true
+ end
+ end
+ end
+
describe "(shell expansion)" do
before :each do
@env_home = ENV["HOME"]
diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb
index 666ca15e11..ae814aa317 100644
--- a/spec/ruby/core/kernel/shared/require.rb
+++ b/spec/ruby/core/kernel/shared/require.rb
@@ -237,6 +237,17 @@ describe :kernel_require, shared: true do
}.should complain(/circular require considered harmful/, verbose: true)
ScratchPad.recorded.should == [:loaded]
end
+
+ ruby_bug "#17340", ''...'3.3' do
+ it "loads a file concurrently" do
+ path = File.expand_path "concurrent_require_fixture.rb", CODE_LOADING_DIR
+ ScratchPad.record(@object)
+ -> {
+ @object.require(path)
+ }.should_not complain(/circular require considered harmful/, verbose: true)
+ ScratchPad.recorded.join
+ end
+ end
end
describe "(non-extensioned path)" do
diff --git a/spec/ruby/core/kernel/shared/sprintf.rb b/spec/ruby/core/kernel/shared/sprintf.rb
index 84d472b0d1..2db50bd686 100644
--- a/spec/ruby/core/kernel/shared/sprintf.rb
+++ b/spec/ruby/core/kernel/shared/sprintf.rb
@@ -289,21 +289,80 @@ describe :kernel_sprintf, shared: true do
@method.call("%c", "a").should == "a"
end
- it "raises ArgumentError if argument is a string of several characters" do
+ ruby_version_is ""..."3.2" do
+ it "raises ArgumentError if argument is a string of several characters" do
+ -> {
+ @method.call("%c", "abc")
+ }.should raise_error(ArgumentError, /%c requires a character/)
+ end
+
+ it "raises ArgumentError if argument is an empty string" do
+ -> {
+ @method.call("%c", "")
+ }.should raise_error(ArgumentError, /%c requires a character/)
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "displays only the first character if argument is a string of several characters" do
+ @method.call("%c", "abc").should == "a"
+ end
+
+ it "displays no characters if argument is an empty string" do
+ @method.call("%c", "").should == ""
+ end
+ end
+
+ it "raises TypeError if argument is not String or Integer and cannot be converted to them" do
-> {
- @method.call("%c", "abc")
- }.should raise_error(ArgumentError)
+ @method.call("%c", [])
+ }.should raise_error(TypeError, /no implicit conversion of Array into Integer/)
end
- it "raises ArgumentError if argument is an empty string" do
+ it "raises TypeError if argument is nil" do
-> {
- @method.call("%c", "")
- }.should raise_error(ArgumentError)
+ @method.call("%c", nil)
+ }.should raise_error(TypeError, /no implicit conversion from nil to integer/)
end
- it "supports Unicode characters" do
- @method.call("%c", 1286).should == "Ԇ"
- @method.call("%c", "ش").should == "ش"
+ it "tries to convert argument to String with to_str" do
+ obj = BasicObject.new
+ def obj.to_str
+ "a"
+ end
+
+ @method.call("%c", obj).should == "a"
+ end
+
+ it "tries to convert argument to Integer with to_int" do
+ obj = BasicObject.new
+ def obj.to_int
+ 90
+ end
+
+ @method.call("%c", obj).should == "Z"
+ end
+
+ it "raises TypeError if converting to String with to_str returns non-String" do
+ obj = BasicObject.new
+ def obj.to_str
+ :foo
+ end
+
+ -> {
+ @method.call("%c", obj)
+ }.should raise_error(TypeError, /can't convert BasicObject to String/)
+ end
+
+ it "raises TypeError if converting to Integer with to_int returns non-Integer" do
+ obj = BasicObject.new
+ def obj.to_str
+ :foo
+ end
+
+ -> {
+ @method.call("%c", obj)
+ }.should raise_error(TypeError, /can't convert BasicObject to String/)
end
end
@@ -362,11 +421,11 @@ describe :kernel_sprintf, shared: true do
@method.call("%4.6s", "abcdefg").should == "abcdef"
end
- it "formats nli with width" do
+ it "formats nil with width" do
@method.call("%6s", nil).should == " "
end
- it "formats nli with precision" do
+ it "formats nil with precision" do
@method.call("%.6s", nil).should == ""
end
@@ -927,4 +986,8 @@ describe :kernel_sprintf, shared: true do
}
end
end
+
+ it "does not raise error when passed more arguments than needed" do
+ sprintf("%s %d %c", "string", 2, "c", []).should == "string 2 c"
+ end
end
diff --git a/spec/ruby/core/kernel/shared/sprintf_encoding.rb b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
index 5ca66b9083..9cedb8b662 100644
--- a/spec/ruby/core/kernel/shared/sprintf_encoding.rb
+++ b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
@@ -1,3 +1,5 @@
+# Keep encoding-related specs in a separate shared example to be able to skip them in IO/File/StringIO specs.
+# It's difficult to check result's encoding in the test after writing to a file/io buffer.
describe :kernel_sprintf_encoding, shared: true do
it "can produce a string with valid encoding" do
string = @method.call("good day %{valid}", valid: "e")
@@ -25,7 +27,7 @@ describe :kernel_sprintf_encoding, shared: true do
result.encoding.should equal(Encoding::UTF_8)
end
- it "raises Encoding::CompatibilityError if both encodings are ASCII compatible and there ano not ASCII characters" do
+ it "raises Encoding::CompatibilityError if both encodings are ASCII compatible and there are not ASCII characters" do
string = "Ä %s".encode('windows-1252')
argument = "Ђ".encode('windows-1251')
@@ -33,4 +35,33 @@ describe :kernel_sprintf_encoding, shared: true do
@method.call(string, argument)
}.should raise_error(Encoding::CompatibilityError)
end
+
+ describe "%c" do
+ it "supports Unicode characters" do
+ result = @method.call("%c", 1286)
+ result.should == "Ԇ"
+ result.bytes.should == [212, 134]
+
+ result = @method.call("%c", "ش")
+ result.should == "ش"
+ result.bytes.should == [216, 180]
+ end
+
+ it "raises error when a codepoint isn't representable in an encoding of a format string" do
+ format = "%c".encode("ASCII")
+
+ -> {
+ @method.call(format, 1286)
+ }.should raise_error(RangeError, /out of char range/)
+ end
+
+ it "uses the encoding of the format string to interpret codepoints" do
+ format = "%c".force_encoding("euc-jp")
+ result = @method.call(format, 9415601)
+
+ result.encoding.should == Encoding::EUC_JP
+ result.should == "é".encode(Encoding::EUC_JP)
+ result.bytes.should == [143, 171, 177]
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/singleton_class_spec.rb b/spec/ruby/core/kernel/singleton_class_spec.rb
index a8bcd916d1..c56fa08cc1 100644
--- a/spec/ruby/core/kernel/singleton_class_spec.rb
+++ b/spec/ruby/core/kernel/singleton_class_spec.rb
@@ -1,3 +1,5 @@
+require_relative '../../spec_helper'
+
describe "Kernel#singleton_class" do
it "returns class extended from an object" do
x = Object.new
diff --git a/spec/ruby/core/main/fixtures/using.rb b/spec/ruby/core/main/fixtures/using.rb
new file mode 100644
index 0000000000..30713ef309
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/using.rb
@@ -0,0 +1 @@
+using Module.new
diff --git a/spec/ruby/core/main/fixtures/using_in_main.rb b/spec/ruby/core/main/fixtures/using_in_main.rb
new file mode 100644
index 0000000000..a4a71c89cc
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/using_in_main.rb
@@ -0,0 +1,5 @@
+MAIN = self
+
+module X
+ MAIN.send(:using, Module.new)
+end
diff --git a/spec/ruby/core/main/fixtures/using_in_method.rb b/spec/ruby/core/main/fixtures/using_in_method.rb
new file mode 100644
index 0000000000..d9ea2e9ef0
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/using_in_method.rb
@@ -0,0 +1,5 @@
+def foo
+ using Module.new
+end
+
+foo
diff --git a/spec/ruby/core/main/using_spec.rb b/spec/ruby/core/main/using_spec.rb
index f9f709f7dc..8a23970c4b 100644
--- a/spec/ruby/core/main/using_spec.rb
+++ b/spec/ruby/core/main/using_spec.rb
@@ -129,4 +129,24 @@ describe "main.using" do
x.call_bar(cls2.new).should == 'bar'
end
+
+ it "raises error when called from method in wrapped script" do
+ -> do
+ load File.expand_path('../fixtures/using_in_method.rb', __FILE__), true
+ end.should raise_error(RuntimeError)
+ end
+
+ it "raises error when called on toplevel from module" do
+ -> do
+ load File.expand_path('../fixtures/using_in_main.rb', __FILE__), true
+ end.should raise_error(RuntimeError)
+ end
+
+ ruby_version_is "3.2" do
+ it "does not raise error when wrapped with module" do
+ -> do
+ load File.expand_path('../fixtures/using.rb', __FILE__), true
+ end.should_not raise_error
+ end
+ end
end
diff --git a/spec/ruby/core/marshal/dump_spec.rb b/spec/ruby/core/marshal/dump_spec.rb
index 079010e554..879ea287ce 100644
--- a/spec/ruby/core/marshal/dump_spec.rb
+++ b/spec/ruby/core/marshal/dump_spec.rb
@@ -1,5 +1,6 @@
# -*- encoding: binary -*-
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
require_relative 'fixtures/marshal_data'
describe "Marshal.dump" do
@@ -106,7 +107,7 @@ describe "Marshal.dump" do
end
describe "with an object responding to #_dump" do
- it "dumps the object returned by #marshal_dump" do
+ it "dumps the object returned by #_dump" do
Marshal.dump(UserDefined.new).should == "\004\bu:\020UserDefined\022\004\b[\a:\nstuff;\000"
end
@@ -122,6 +123,34 @@ describe "Marshal.dump" do
m.should_not_receive(:_dump)
Marshal.dump(m)
end
+
+ it "indexes instance variables of a String returned by #_dump at first and then indexes the object itself" do
+ class MarshalSpec::M1::A
+ def _dump(level)
+ s = "<dump>"
+ s.instance_variable_set(:@foo, "bar")
+ s
+ end
+ end
+
+ a = MarshalSpec::M1::A.new
+
+ # 0-based index of the object a = 2, that is encoded as \x07 and printed as "\a" character.
+ # Objects are serialized in the following order: Array, a, "bar".
+ # But they are indexed in different order: Array (index=0), "bar" (index=1), a (index=2)
+ # So the second occurenc of the object a is encoded as an index 2.
+ reference = "@\a"
+ Marshal.dump([a, a]).should == "\x04\b[\aIu:\x17MarshalSpec::M1::A\v<dump>\x06:\t@foo\"\bbar#{reference}"
+ end
+
+ describe "Core library classes with #_dump returning a String with instance variables" do
+ it "indexes instance variables and then a Time object itself" do
+ t = Time.utc(2022)
+ reference = "@\a"
+
+ Marshal.dump([t, t]).should == "\x04\b[\aIu:\tTime\r \x80\x1E\xC0\x00\x00\x00\x00\x06:\tzoneI\"\bUTC\x06:\x06EF#{reference}"
+ end
+ end
end
describe "with a Class" do
@@ -185,6 +214,20 @@ describe "Marshal.dump" do
[Marshal, -2**64, "\004\bl-\n\000\000\000\000\000\000\000\000\001\000"],
].should be_computed_by(:dump)
end
+
+ it "increases the object links counter" do
+ obj = Object.new
+ object_1_link = "\x06" # representing of (0-based) index=1 (by adding 5 for small Integers)
+ object_2_link = "\x07" # representing of index=2
+
+ # objects: Array, Object, Object
+ Marshal.dump([obj, obj]).should == "\x04\b[\ao:\vObject\x00@#{object_1_link}"
+
+ # objects: Array, Bignum, Object, Object
+ Marshal.dump([2**64, obj, obj]).should == "\x04\b[\bl+\n\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00o:\vObject\x00@#{object_2_link}"
+ Marshal.dump([2**48, obj, obj]).should == "\x04\b[\bl+\t\x00\x00\x00\x00\x00\x00\x01\x00o:\vObject\x00@#{object_2_link}"
+ Marshal.dump([2**32, obj, obj]).should == "\x04\b[\bl+\b\x00\x00\x00\x00\x01\x00o:\vObject\x00@#{object_2_link}"
+ end
end
describe "with a String" do
diff --git a/spec/ruby/core/marshal/fixtures/classes.rb b/spec/ruby/core/marshal/fixtures/classes.rb
new file mode 100644
index 0000000000..7c81c64927
--- /dev/null
+++ b/spec/ruby/core/marshal/fixtures/classes.rb
@@ -0,0 +1,4 @@
+module MarshalSpec
+ # empty modules
+ module M1 end
+end
diff --git a/spec/ruby/core/marshal/shared/load.rb b/spec/ruby/core/marshal/shared/load.rb
index 98fae44296..479c7477ec 100644
--- a/spec/ruby/core/marshal/shared/load.rb
+++ b/spec/ruby/core/marshal/shared/load.rb
@@ -720,6 +720,17 @@ describe :marshal_load, shared: true do
new_obj_metaclass_ancestors[@num_self_class, 3].should ==
[Meths, UserRegexp, Regexp]
end
+
+ ruby_bug "#19439", ""..."3.3" do
+ it "restore the regexp instance variables" do
+ obj = Regexp.new("hello")
+ obj.instance_variable_set(:@regexp_ivar, [42])
+
+ new_obj = Marshal.send(@method, "\x04\bI/\nhello\x00\a:\x06EF:\x11@regexp_ivar[\x06i/")
+ new_obj.instance_variables.should == [:@regexp_ivar]
+ new_obj.instance_variable_get(:@regexp_ivar).should == [42]
+ end
+ end
end
describe "for a Float" do
diff --git a/spec/ruby/core/matchdata/element_reference_spec.rb b/spec/ruby/core/matchdata/element_reference_spec.rb
index 8965f902a0..7c0f089bb4 100644
--- a/spec/ruby/core/matchdata/element_reference_spec.rb
+++ b/spec/ruby/core/matchdata/element_reference_spec.rb
@@ -26,6 +26,21 @@ describe "MatchData#[]" do
it "supports ranges [start..end]" do
/(.)(.)(\d+)(\d)/.match("THX1138.")[1..3].should == %w|H X 113|
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3..10].should == %w|113 8|
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[-30..2].should == nil
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3..1].should == []
+ end
+
+ it "supports endless ranges [start..]" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3..].should == %w|113 8|
+ end
+
+ it "supports beginningless ranges [..end]" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[..1].should == %w|HX1138 H|
+ end
+
+ it "supports beginningless endless ranges [nil..nil]" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[nil..nil].should == %w|HX1138 H X 113 8|
end
ruby_version_is "3.0" do
diff --git a/spec/ruby/core/matchdata/values_at_spec.rb b/spec/ruby/core/matchdata/values_at_spec.rb
index 8f7fdf557c..4fd0bfc42a 100644
--- a/spec/ruby/core/matchdata/values_at_spec.rb
+++ b/spec/ruby/core/matchdata/values_at_spec.rb
@@ -1,21 +1,76 @@
require_relative '../../spec_helper'
-describe "MatchData#values_at" do
- it "returns an array of the matching value" do
- /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0, 2, -2).should == ["HX1138", "X", "113"]
+describe "Struct#values_at" do
+ # Should be synchronized with core/array/values_at_spec.rb and core/struct/values_at_spec.rb
+ #
+ # /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").to_a # => ["HX1138", "H", "X", "113", "8"]
+
+ context "when passed a list of Integers" do
+ it "returns an array containing each value given by one of integers" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0, 2, -2).should == ["HX1138", "X", "113"]
+ end
+
+ it "returns nil value for any integer that is out of range" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(5).should == [nil]
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(-6).should == [nil]
+ end
end
- describe "when passed a Range" do
- it "returns an array of the matching value" do
- /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(2..4, 0..1).should == ["X", "113", "8", "HX1138", "H"]
+ context "when passed an integer Range" do
+ it "returns an array containing each value given by the elements of the range" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..2).should == ["HX1138", "H", "X"]
+ end
+
+ it "fills with nil values for range elements larger than the captured values number" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..5).should == ["HX1138", "H", "X", "113", "8", nil]
+ end
+
+ it "raises RangeError if any element of the range is negative and out of range" do
+ -> { /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(-6..3) }.should raise_error(RangeError, "-6..3 out of range")
+ end
+
+ it "supports endless Range" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..).should == ["HX1138", "H", "X", "113", "8"]
+ end
+
+ it "supports beginningless Range" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..2).should == ["HX1138", "H", "X"]
+ end
+
+ it "returns an empty Array when Range is empty" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(2..0).should == []
+ end
+ end
+
+ context "when passed names" do
+ it 'slices captures with the given names' do
+ /(?<a>.)(?<b>.)(?<c>.)/.match('012').values_at(:c, :a).should == ['2', '0']
+ end
+
+ it 'slices captures with the given String names' do
+ /(?<a>.)(?<b>.)(?<c>.)/.match('012').values_at('c', 'a').should == ['2', '0']
end
end
- it 'slices captures with the given names' do
- /(?<a>.)(?<b>.)(?<c>.)/.match('012').values_at(:c, :a).should == ['2', '0']
+ it "supports multiple integer Ranges" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(1..2, 2..3).should == ["H", "X", "X", "113"]
end
- it 'takes names and indices' do
+ it "supports mixing integer Ranges and Integers" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(1..2, 4).should == ["H", "X", "8"]
+ end
+
+ it 'supports mixing of names and indices' do
/\A(?<a>.)(?<b>.)\z/.match('01').values_at(0, 1, 2, :a, :b).should == ['01', '0', '1', '0', '1']
end
+
+ it "returns a new empty Array if no arguments given" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at().should == []
+ end
+
+ it "fails when passed arguments of unsupported types" do
+ -> {
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(Object.new)
+ }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
+ end
end
diff --git a/spec/ruby/core/method/fixtures/classes.rb b/spec/ruby/core/method/fixtures/classes.rb
index be96f65e25..464a519aea 100644
--- a/spec/ruby/core/method/fixtures/classes.rb
+++ b/spec/ruby/core/method/fixtures/classes.rb
@@ -84,6 +84,12 @@ module MethodSpecs
def two_req_one_opt_with_splat_and_block(a, b, c=nil, *d, &blk); end
def one_req_two_opt_with_splat_and_block(a, b=nil, c=nil, *d, &blk); end
+ def my_public_method; end
+ def my_protected_method; end
+ def my_private_method; end
+ protected :my_protected_method
+ private :my_private_method
+
define_method(:zero_defined_method, Proc.new {||})
define_method(:zero_with_splat_defined_method, Proc.new {|*x|})
define_method(:one_req_defined_method, Proc.new {|x|})
@@ -213,4 +219,28 @@ module MethodSpecs
n * m
end
end
+
+ module InheritedMethods
+ module A
+ private
+ def derp(message)
+ 'A'
+ end
+ end
+
+ module B
+ private
+ def derp
+ 'B' + super('superclass')
+ end
+ end
+
+ class C
+ include A
+ include B
+
+ public :derp
+ alias_method :meow, :derp
+ end
+ end
end
diff --git a/spec/ruby/core/method/owner_spec.rb b/spec/ruby/core/method/owner_spec.rb
index ca5dff7295..05422f1697 100644
--- a/spec/ruby/core/method/owner_spec.rb
+++ b/spec/ruby/core/method/owner_spec.rb
@@ -23,4 +23,10 @@ describe "Method#owner" do
@m.method(:handled_via_method_missing).owner.should == MethodSpecs::Methods
end
end
+
+ ruby_version_is "3.2" do
+ it "returns the class on which public was called for a private method in ancestor" do
+ MethodSpecs::InheritedMethods::C.new.method(:derp).owner.should == MethodSpecs::InheritedMethods::C
+ end
+ end
end
diff --git a/spec/ruby/core/method/private_spec.rb b/spec/ruby/core/method/private_spec.rb
new file mode 100644
index 0000000000..230a4e9e81
--- /dev/null
+++ b/spec/ruby/core/method/private_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.1"..."3.2" do
+ describe "Method#private?" do
+ it "returns false when the method is public" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_public_method).private?.should == false
+ end
+
+ it "returns false when the method is protected" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_protected_method).private?.should == false
+ end
+
+ it "returns true when the method is private" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_private_method).private?.should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/method/protected_spec.rb b/spec/ruby/core/method/protected_spec.rb
new file mode 100644
index 0000000000..6ee85f7738
--- /dev/null
+++ b/spec/ruby/core/method/protected_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.1"..."3.2" do
+ describe "Method#protected?" do
+ it "returns false when the method is public" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_public_method).protected?.should == false
+ end
+
+ it "returns true when the method is protected" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_protected_method).protected?.should == true
+ end
+
+ it "returns false when the method is private" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_private_method).protected?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/method/public_spec.rb b/spec/ruby/core/method/public_spec.rb
new file mode 100644
index 0000000000..3988468551
--- /dev/null
+++ b/spec/ruby/core/method/public_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.1"..."3.2" do
+ describe "Method#public?" do
+ it "returns true when the method is public" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_public_method).public?.should == true
+ end
+
+ it "returns false when the method is protected" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_protected_method).public?.should == false
+ end
+
+ it "returns false when the method is private" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_private_method).public?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/method/super_method_spec.rb b/spec/ruby/core/method/super_method_spec.rb
index e5d8b87a06..f9a18f3878 100644
--- a/spec/ruby/core/method/super_method_spec.rb
+++ b/spec/ruby/core/method/super_method_spec.rb
@@ -42,4 +42,25 @@ describe "Method#super_method" do
method.super_method.should == nil
end
+
+ # https://github.com/jruby/jruby/issues/7240
+ context "after changing an inherited methods visibility" do
+ it "calls the proper super method" do
+ MethodSpecs::InheritedMethods::C.new.derp.should == 'BA'
+ end
+
+ it "returns the expected super_method" do
+ method = MethodSpecs::InheritedMethods::C.new.method(:derp)
+ method.super_method.owner.should == MethodSpecs::InheritedMethods::A
+ end
+ end
+
+ ruby_version_is "2.7.3" do
+ context "after aliasing an inherited method" do
+ it "returns the expected super_method" do
+ method = MethodSpecs::InheritedMethods::C.new.method(:meow)
+ method.super_method.owner.should == MethodSpecs::InheritedMethods::A
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/method/unbind_spec.rb b/spec/ruby/core/method/unbind_spec.rb
index cdd3a808f2..bdedd513ce 100644
--- a/spec/ruby/core/method/unbind_spec.rb
+++ b/spec/ruby/core/method/unbind_spec.rb
@@ -27,8 +27,16 @@ describe "Method#unbind" do
@string.should =~ /MethodSpecs::MyMod/
end
- it "returns a String containing the Module the method is referenced from" do
- @string.should =~ /MethodSpecs::MySub/
+ ruby_version_is ""..."3.2" do
+ it "returns a String containing the Module the method is referenced from" do
+ @string.should =~ /MethodSpecs::MySub/
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "returns a String containing the Module the method is referenced from" do
+ @string.should =~ /MethodSpecs::MyMod/
+ end
end
end
diff --git a/spec/ruby/core/module/const_defined_spec.rb b/spec/ruby/core/module/const_defined_spec.rb
index 75730395e8..0c15629c08 100644
--- a/spec/ruby/core/module/const_defined_spec.rb
+++ b/spec/ruby/core/module/const_defined_spec.rb
@@ -17,11 +17,16 @@ describe "Module#const_defined?" do
ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4).should be_true
end
- it "returns true if the constant is defined in a mixed-in module of the receiver" do
+ it "returns true if the constant is defined in a mixed-in module of the receiver's parent" do
# CS_CONST10 is defined in a module included by ChildA
ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST10).should be_true
end
+ it "returns true if the constant is defined in a mixed-in module (with prepends) of the receiver" do
+ # CS_CONST11 is defined in the module included by ContainerPrepend
+ ConstantSpecs::ContainerPrepend.const_defined?(:CS_CONST11).should be_true
+ end
+
it "returns true if the constant is defined in Object and the receiver is a module" do
# CS_CONST1 is defined in Object
ConstantSpecs::ModuleA.const_defined?(:CS_CONST1).should be_true
diff --git a/spec/ruby/core/module/define_method_spec.rb b/spec/ruby/core/module/define_method_spec.rb
index c65b30de24..ce94436bfd 100644
--- a/spec/ruby/core/module/define_method_spec.rb
+++ b/spec/ruby/core/module/define_method_spec.rb
@@ -219,18 +219,55 @@ describe "Module#define_method" do
o.block_test2.should == o
end
+ it "raises TypeError if name cannot converted to String" do
+ -> {
+ Class.new { define_method(1001, -> {}) }
+ }.should raise_error(TypeError, /is not a symbol nor a string/)
+
+ -> {
+ Class.new { define_method([], -> {}) }
+ }.should raise_error(TypeError, /is not a symbol nor a string/)
+ end
+
+ it "converts non-String name to String with #to_str" do
+ obj = Object.new
+ def obj.to_str() "foo" end
+
+ new_class = Class.new { define_method(obj, -> { :called }) }
+ new_class.new.foo.should == :called
+ end
+
+ it "raises TypeError when #to_str called on non-String name returns non-String value" do
+ obj = Object.new
+ def obj.to_str() [] end
+
+ -> {
+ Class.new { define_method(obj, -> {}) }
+ }.should raise_error(TypeError, /can't convert Object to String/)
+ end
+
it "raises a TypeError when the given method is no Method/Proc" do
-> {
Class.new { define_method(:test, "self") }
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, "wrong argument type String (expected Proc/Method/UnboundMethod)")
-> {
Class.new { define_method(:test, 1234) }
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, "wrong argument type Integer (expected Proc/Method/UnboundMethod)")
-> {
Class.new { define_method(:test, nil) }
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, "wrong argument type NilClass (expected Proc/Method/UnboundMethod)")
+ end
+
+ it "uses provided Method/Proc even if block is specified" do
+ new_class = Class.new do
+ define_method(:test, -> { :method_is_called }) do
+ :block_is_called
+ end
+ end
+
+ new_class.new.test.should == :method_is_called
end
it "raises an ArgumentError when no block is given" do
diff --git a/spec/ruby/core/module/fixtures/classes.rb b/spec/ruby/core/module/fixtures/classes.rb
index a64f672b2f..bc6b940a6c 100644
--- a/spec/ruby/core/module/fixtures/classes.rb
+++ b/spec/ruby/core/module/fixtures/classes.rb
@@ -42,6 +42,14 @@ module ModuleSpecs
class LookupChild < Lookup
end
+ module ModuleWithPrepend
+ prepend LookupMod
+ end
+
+ class WithPrependedModule
+ include ModuleWithPrepend
+ end
+
class Parent
# For private_class_method spec
def self.private_method; end
@@ -380,6 +388,7 @@ module ModuleSpecs
# empty modules
module M1; end
module M2; end
+ module M3; end
module Autoload
def self.use_ex1
diff --git a/spec/ruby/core/module/include_spec.rb b/spec/ruby/core/module/include_spec.rb
index c47e052d22..c073bc31ca 100644
--- a/spec/ruby/core/module/include_spec.rb
+++ b/spec/ruby/core/module/include_spec.rb
@@ -104,9 +104,9 @@ describe "Module#include" do
class A; include M; end
class B < A; include M; end
- all = [A,B,M]
+ all = [A, B, M]
- (B.ancestors & all).should == [B, A, M]
+ (B.ancestors.filter { |a| all.include?(a) }).should == [B, A, M]
end
end
diff --git a/spec/ruby/core/module/included_modules_spec.rb b/spec/ruby/core/module/included_modules_spec.rb
index 40e20953f4..ce94ed1285 100644
--- a/spec/ruby/core/module/included_modules_spec.rb
+++ b/spec/ruby/core/module/included_modules_spec.rb
@@ -4,9 +4,11 @@ require_relative 'fixtures/classes'
describe "Module#included_modules" do
it "returns a list of modules included in self" do
ModuleSpecs.included_modules.should == []
+
ModuleSpecs::Child.included_modules.should include(ModuleSpecs::Super, ModuleSpecs::Basic, Kernel)
ModuleSpecs::Parent.included_modules.should include(Kernel)
ModuleSpecs::Basic.included_modules.should == []
ModuleSpecs::Super.included_modules.should include(ModuleSpecs::Basic)
+ ModuleSpecs::WithPrependedModule.included_modules.should include(ModuleSpecs::ModuleWithPrepend)
end
end
diff --git a/spec/ruby/core/module/instance_method_spec.rb b/spec/ruby/core/module/instance_method_spec.rb
index b4d6a0d8c8..8d006e647e 100644
--- a/spec/ruby/core/module/instance_method_spec.rb
+++ b/spec/ruby/core/module/instance_method_spec.rb
@@ -45,20 +45,46 @@ describe "Module#instance_method" do
@parent_um.inspect.should =~ /\bModuleSpecs::InstanceMeth\b/
@child_um.inspect.should =~ /\bfoo\b/
@child_um.inspect.should =~ /\bModuleSpecs::InstanceMeth\b/
- @child_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/
+
@mod_um.inspect.should =~ /\bbar\b/
@mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethMod\b/
- @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/
+
+ ruby_version_is ""..."3.2" do
+ @child_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/
+ @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/
+ end
end
- it "raises a TypeError if not passed a symbol" do
- -> { Object.instance_method([]) }.should raise_error(TypeError)
- -> { Object.instance_method(0) }.should raise_error(TypeError)
+ it "raises a TypeError if the given name is not a String/Symbol" do
+ -> { Object.instance_method([]) }.should raise_error(TypeError, /is not a symbol nor a string/)
+ -> { Object.instance_method(0) }.should raise_error(TypeError, /is not a symbol nor a string/)
+ -> { Object.instance_method(nil) }.should raise_error(TypeError, /is not a symbol nor a string/)
+ -> { Object.instance_method(mock('x')) }.should raise_error(TypeError, /is not a symbol nor a string/)
end
- it "raises a TypeError if the given name is not a string/symbol" do
- -> { Object.instance_method(nil) }.should raise_error(TypeError)
- -> { Object.instance_method(mock('x')) }.should raise_error(TypeError)
+ it "accepts String name argument" do
+ method = ModuleSpecs::InstanceMeth.instance_method(:foo)
+ method.should be_kind_of(UnboundMethod)
+ end
+
+ it "accepts Symbol name argument" do
+ method = ModuleSpecs::InstanceMeth.instance_method("foo")
+ method.should be_kind_of(UnboundMethod)
+ end
+
+ it "converts non-String name by calling #to_str method" do
+ obj = Object.new
+ def obj.to_str() "foo" end
+
+ method = ModuleSpecs::InstanceMeth.instance_method(obj)
+ method.should be_kind_of(UnboundMethod)
+ end
+
+ it "raises TypeError when passed non-String name and #to_str returns non-String value" do
+ obj = Object.new
+ def obj.to_str() [] end
+
+ -> { ModuleSpecs::InstanceMeth.instance_method(obj) }.should raise_error(TypeError, /can't convert Object to String/)
end
it "raises a NameError if the method has been undefined" do
diff --git a/spec/ruby/core/module/prepend_spec.rb b/spec/ruby/core/module/prepend_spec.rb
index d636e023ed..976b09b105 100644
--- a/spec/ruby/core/module/prepend_spec.rb
+++ b/spec/ruby/core/module/prepend_spec.rb
@@ -611,6 +611,18 @@ describe "Module#prepend" do
ScratchPad.recorded.should == [[:prepend_features, c], [:prepended, c]]
end
+ it "prepends a module if it is included in a super class" do
+ module ModuleSpecs::M3
+ module M; end
+ class A; include M; end
+ class B < A; prepend M; end
+
+ all = [A, B, M]
+
+ (B.ancestors.filter { |a| all.include?(a) }).should == [M, B, A, M]
+ end
+ end
+
it "detects cyclic prepends" do
-> {
module ModuleSpecs::P
diff --git a/spec/ruby/core/module/shared/class_eval.rb b/spec/ruby/core/module/shared/class_eval.rb
index 224078ae54..9ef7b5be44 100644
--- a/spec/ruby/core/module/shared/class_eval.rb
+++ b/spec/ruby/core/module/shared/class_eval.rb
@@ -55,40 +55,49 @@ describe :module_class_eval, shared: true do
it "converts a non-string filename to a string using to_str" do
(file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__)
ModuleSpecs.send(@method, "1+1", file)
+
+ (file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__)
+ ModuleSpecs.send(@method, "1+1", file, 15)
end
it "raises a TypeError when the given filename can't be converted to string using to_str" do
(file = mock('123')).should_receive(:to_str).and_return(123)
- -> { ModuleSpecs.send(@method, "1+1", file) }.should raise_error(TypeError)
+ -> { ModuleSpecs.send(@method, "1+1", file) }.should raise_error(TypeError, /can't convert MockObject to String/)
end
it "converts non string eval-string to string using to_str" do
(o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1")
ModuleSpecs.send(@method, o).should == 2
+
+ (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1")
+ ModuleSpecs.send(@method, o, "file.rb").should == 2
+
+ (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1")
+ ModuleSpecs.send(@method, o, "file.rb", 15).should == 2
end
it "raises a TypeError when the given eval-string can't be converted to string using to_str" do
o = mock('x')
- -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError)
+ -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError, "no implicit conversion of MockObject into String")
(o = mock('123')).should_receive(:to_str).and_return(123)
- -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError)
+ -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError, /can't convert MockObject to String/)
end
it "raises an ArgumentError when no arguments and no block are given" do
- -> { ModuleSpecs.send(@method) }.should raise_error(ArgumentError)
+ -> { ModuleSpecs.send(@method) }.should raise_error(ArgumentError, "wrong number of arguments (given 0, expected 1..3)")
end
it "raises an ArgumentError when more than 3 arguments are given" do
-> {
ModuleSpecs.send(@method, "1 + 1", "some file", 0, "bogus")
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
it "raises an ArgumentError when a block and normal arguments are given" do
-> {
ModuleSpecs.send(@method, "1 + 1") { 1 + 1 }
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 1, expected 0)")
end
# This case was found because Rubinius was caching the compiled
diff --git a/spec/ruby/core/objectspace/define_finalizer_spec.rb b/spec/ruby/core/objectspace/define_finalizer_spec.rb
index 281785b0a4..d9db027e0b 100644
--- a/spec/ruby/core/objectspace/define_finalizer_spec.rb
+++ b/spec/ruby/core/objectspace/define_finalizer_spec.rb
@@ -169,4 +169,26 @@ describe "ObjectSpace.define_finalizer" do
ruby_exe(code).lines.sort.should == ["finalized1\n", "finalized2\n"]
end
+
+ ruby_version_is "3.1" do
+ describe "when $VERBOSE is not nil" do
+ it "warns if an exception is raised in finalizer" do
+ code = <<-RUBY
+ ObjectSpace.define_finalizer(Object.new) { raise "finalizing" }
+ RUBY
+
+ ruby_exe(code, args: "2>&1").should include("warning: Exception in finalizer", "finalizing")
+ end
+ end
+
+ describe "when $VERBOSE is nil" do
+ it "does not warn even if an exception is raised in finalizer" do
+ code = <<-RUBY
+ ObjectSpace.define_finalizer(Object.new) { raise "finalizing" }
+ RUBY
+
+ ruby_exe(code, args: "2>&1", options: "-W0").should == ""
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/process/_fork_spec.rb b/spec/ruby/core/process/_fork_spec.rb
new file mode 100644
index 0000000000..6f711ad2dd
--- /dev/null
+++ b/spec/ruby/core/process/_fork_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.1" do
+ describe "Process._fork" do
+ it "for #respond_to? returns the same as Process.respond_to?(:fork)" do
+ Process.respond_to?(:_fork).should == Process.respond_to?(:fork)
+ end
+
+ guard_not -> { Process.respond_to?(:fork) } do
+ it "raises a NotImplementedError when called" do
+ -> { Process._fork }.should raise_error(NotImplementedError)
+ end
+ end
+
+ guard -> { Process.respond_to?(:fork) } do
+ it "is called by Process#fork" do
+ Process.should_receive(:_fork).once.and_return(42)
+
+ pid = Process.fork {}
+ pid.should equal(42)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/constants_spec.rb b/spec/ruby/core/process/constants_spec.rb
index b61f5ab64e..4130bb58a5 100644
--- a/spec/ruby/core/process/constants_spec.rb
+++ b/spec/ruby/core/process/constants_spec.rb
@@ -1,3 +1,4 @@
+require_relative '../../spec_helper'
describe "Process::Constants" do
platform_is :darwin, :netbsd, :freebsd do
diff --git a/spec/ruby/core/process/daemon_spec.rb b/spec/ruby/core/process/daemon_spec.rb
index 70ffd1b320..20b0d743b9 100644
--- a/spec/ruby/core/process/daemon_spec.rb
+++ b/spec/ruby/core/process/daemon_spec.rb
@@ -2,6 +2,9 @@ require_relative '../../spec_helper'
require_relative 'fixtures/common'
platform_is_not :windows do
+ # macOS 15 is not working this examples
+ return if /darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion`
+
describe :process_daemon_keep_stdio_open_false, shared: true do
it "redirects stdout to /dev/null" do
@daemon.invoke("keep_stdio_open_false_stdout", @object).should == ""
diff --git a/spec/ruby/core/process/detach_spec.rb b/spec/ruby/core/process/detach_spec.rb
index 1c27ed9c2c..91661afcea 100644
--- a/spec/ruby/core/process/detach_spec.rb
+++ b/spec/ruby/core/process/detach_spec.rb
@@ -42,5 +42,34 @@ describe "Process.detach" do
thr.pid.should == pid
end
+
+ it "tolerates not existing child process pid" do
+ # ensure there is no child process with this hardcoded pid
+ # `kill 0 pid` for existing process returns "1" and raises Errno::ESRCH if process doesn't exist
+ -> { Process.kill(0, 100500) }.should raise_error(Errno::ESRCH)
+
+ thr = Process.detach(100500)
+ thr.join
+
+ thr.should be_kind_of(Thread)
+ end
+
+ it "calls #to_int to implicitly convert non-Integer pid to Integer" do
+ pid = MockObject.new('mock-enumerable')
+ pid.should_receive(:to_int).and_return(100500)
+
+ Process.detach(pid).join
+ end
+
+ it "raises TypeError when pid argument does not have #to_int method" do
+ -> { Process.detach(Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
+ end
+
+ it "raises TypeError when #to_int returns non-Integer value" do
+ pid = MockObject.new('mock-enumerable')
+ pid.should_receive(:to_int).and_return(:symbol)
+
+ -> { Process.detach(pid) }.should raise_error(TypeError, "can't convert MockObject to Integer (MockObject#to_int gives Symbol)")
+ end
end
end
diff --git a/spec/ruby/core/process/spawn_spec.rb b/spec/ruby/core/process/spawn_spec.rb
index 9aa8da8125..c8a58c4d04 100644
--- a/spec/ruby/core/process/spawn_spec.rb
+++ b/spec/ruby/core/process/spawn_spec.rb
@@ -477,6 +477,16 @@ describe "Process.spawn" do
# redirection
+ it 'redirects to the wrapped IO using wrapped_io.to_io if out: wrapped_io' do
+ File.open(@name, 'w') do |file|
+ -> do
+ wrapped_io = mock('wrapped IO')
+ wrapped_io.should_receive(:to_io).and_return(file)
+ Process.wait Process.spawn('echo Hello World', out: wrapped_io)
+ end.should output_to_fd("Hello World\n", file)
+ end
+ end
+
it "redirects STDOUT to the given file descriptor if out: Integer" do
File.open(@name, 'w') do |file|
-> do
@@ -567,6 +577,24 @@ describe "Process.spawn" do
end
end
+ platform_is_not :windows do
+ it "redirects non-default file descriptor to itself" do
+ File.open(@name, 'w') do |file|
+ -> do
+ Process.wait Process.spawn(
+ ruby_cmd("f = IO.new(#{file.fileno}, 'w'); f.print(:bang); f.flush"), file.fileno => file.fileno)
+ end.should output_to_fd("bang", file)
+ end
+ end
+ end
+
+ it "redirects default file descriptor to itself" do
+ -> do
+ Process.wait Process.spawn(
+ ruby_cmd("f = IO.new(#{STDOUT.fileno}, 'w'); f.print(:bang); f.flush"), STDOUT.fileno => STDOUT.fileno)
+ end.should output_to_fd("bang", STDOUT)
+ end
+
# :close_others
platform_is_not :windows do
diff --git a/spec/ruby/core/process/times_spec.rb b/spec/ruby/core/process/times_spec.rb
index b47189a7e7..6142cd257c 100644
--- a/spec/ruby/core/process/times_spec.rb
+++ b/spec/ruby/core/process/times_spec.rb
@@ -5,12 +5,16 @@ describe "Process.times" do
Process.times.should be_kind_of(Process::Tms)
end
- it "returns current cpu times" do
- t = Process.times
- user = t.utime
+ # TODO: Intel C Compiler does not work this example
+ # http://rubyci.s3.amazonaws.com/icc-x64/ruby-master/log/20221013T030005Z.fail.html.gz
+ unless RbConfig::CONFIG['CC']&.include?("icx")
+ it "returns current cpu times" do
+ t = Process.times
+ user = t.utime
- 1 until Process.times.utime > user
- Process.times.utime.should > user
+ 1 until Process.times.utime > user
+ Process.times.utime.should > user
+ end
end
platform_is_not :windows do
diff --git a/spec/ruby/core/queue/initialize_spec.rb b/spec/ruby/core/queue/initialize_spec.rb
index 83c7e595fe..c45abcd29d 100644
--- a/spec/ruby/core/queue/initialize_spec.rb
+++ b/spec/ruby/core/queue/initialize_spec.rb
@@ -7,6 +7,10 @@ describe "Queue#initialize" do
q.should.empty?
end
+ it "is a private method" do
+ Queue.private_instance_methods.include?(:initialize).should == true
+ end
+
ruby_version_is '3.1' do
it "adds all elements of the passed Enumerable to self" do
q = Queue.new([1, 2, 3])
@@ -30,9 +34,16 @@ describe "Queue#initialize" do
q.should.empty?
end
- it "raises if the provided Enumerable does not respond to #to_a" do
+ it "raises TypeError if the provided Enumerable does not respond to #to_a" do
enumerable = MockObject.new('mock-enumerable')
-> { Queue.new(enumerable) }.should raise_error(TypeError, "can't convert MockObject into Array")
end
+
+ it "raises TypeError if #to_a does not return Array" do
+ enumerable = MockObject.new('mock-enumerable')
+ enumerable.should_receive(:to_a).and_return("string")
+
+ -> { Queue.new(enumerable) }.should raise_error(TypeError, "can't convert MockObject to Array (MockObject#to_a gives String)")
+ end
end
end
diff --git a/spec/ruby/core/range/case_compare_spec.rb b/spec/ruby/core/range/case_compare_spec.rb
index 4a3faa3163..65878aaabe 100644
--- a/spec/ruby/core/range/case_compare_spec.rb
+++ b/spec/ruby/core/range/case_compare_spec.rb
@@ -10,4 +10,10 @@ describe "Range#===" do
it_behaves_like :range_cover_and_include, :===
it_behaves_like :range_cover, :===
+
+ ruby_bug "#19533", "3.2"..."3.3" do
+ it "returns true on any value if begin and end are both nil" do
+ (nil..nil).should === 1
+ end
+ end
end
diff --git a/spec/ruby/core/range/clone_spec.rb b/spec/ruby/core/range/clone_spec.rb
new file mode 100644
index 0000000000..cf6ce74da0
--- /dev/null
+++ b/spec/ruby/core/range/clone_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+
+describe "Range#clone" do
+ it "duplicates the range" do
+ original = (1..3)
+ copy = original.clone
+ copy.begin.should == 1
+ copy.end.should == 3
+ copy.should_not.exclude_end?
+ copy.should_not.equal? original
+
+ original = ("a"..."z")
+ copy = original.clone
+ copy.begin.should == "a"
+ copy.end.should == "z"
+ copy.should.exclude_end?
+ copy.should_not.equal? original
+ end
+
+ it "maintains the frozen state" do
+ (1..2).clone.frozen?.should == (1..2).frozen?
+ (1..).clone.frozen?.should == (1..).frozen?
+ Range.new(1, 2).clone.frozen?.should == Range.new(1, 2).frozen?
+ Class.new(Range).new(1, 2).clone.frozen?.should == Class.new(Range).new(1, 2).frozen?
+ end
+end
diff --git a/spec/ruby/core/range/dup_spec.rb b/spec/ruby/core/range/dup_spec.rb
index 976d4fd1d0..fab3c3f1b2 100644
--- a/spec/ruby/core/range/dup_spec.rb
+++ b/spec/ruby/core/range/dup_spec.rb
@@ -2,10 +2,12 @@ require_relative '../../spec_helper'
describe "Range#dup" do
it "duplicates the range" do
- copy = (1..3).dup
+ original = (1..3)
+ copy = original.dup
copy.begin.should == 1
copy.end.should == 3
copy.should_not.exclude_end?
+ copy.should_not.equal?(original)
copy = ("a"..."z").dup
copy.begin.should == "a"
diff --git a/spec/ruby/core/range/last_spec.rb b/spec/ruby/core/range/last_spec.rb
index d7ef776b42..6698686dd5 100644
--- a/spec/ruby/core/range/last_spec.rb
+++ b/spec/ruby/core/range/last_spec.rb
@@ -8,6 +8,12 @@ describe "Range#last" do
(1..5).last(3).should == [3, 4, 5]
end
+ ruby_bug '#18994', '2.7'...'3.2' do
+ it "returns the specified number if elements for single element inclusive range" do
+ (1..1).last(1).should == [1]
+ end
+ end
+
it "returns an empty array for an empty Range" do
(0...0).last(2).should == []
end
diff --git a/spec/ruby/core/range/new_spec.rb b/spec/ruby/core/range/new_spec.rb
index 85e99babff..40df914b83 100644
--- a/spec/ruby/core/range/new_spec.rb
+++ b/spec/ruby/core/range/new_spec.rb
@@ -65,5 +65,15 @@ describe "Range.new" do
range_exclude.should_not == range_include
end
+
+ ruby_version_is "3.0" do
+ it "creates a frozen range if the class is Range.class" do
+ Range.new(1, 2).should.frozen?
+ end
+
+ it "does not create a frozen range if the class is not Range.class" do
+ Class.new(Range).new(1, 2).should_not.frozen?
+ end
+ end
end
end
diff --git a/spec/ruby/core/range/size_spec.rb b/spec/ruby/core/range/size_spec.rb
index 5462a1a5e1..9b625c9963 100644
--- a/spec/ruby/core/range/size_spec.rb
+++ b/spec/ruby/core/range/size_spec.rb
@@ -34,11 +34,28 @@ describe "Range#size" do
eval("([]...)").size.should == nil
end
- it 'returns Float::INFINITY for all beginless ranges' do
- (..1).size.should == Float::INFINITY
- (...0.5).size.should == Float::INFINITY
- (..nil).size.should == Float::INFINITY
- (...'o').size.should == Float::INFINITY
+ ruby_version_is ""..."3.2" do
+ it 'returns Float::INFINITY for all beginless ranges' do
+ (..1).size.should == Float::INFINITY
+ (...0.5).size.should == Float::INFINITY
+ (..nil).size.should == Float::INFINITY
+ (...'o').size.should == Float::INFINITY
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it 'returns Float::INFINITY for all beginless ranges if the start is numeric' do
+ (..1).size.should == Float::INFINITY
+ (...0.5).size.should == Float::INFINITY
+ end
+
+ it 'returns nil for all beginless ranges if the start is numeric' do
+ (...'o').size.should == nil
+ end
+
+ it 'returns nil if the start and the end is both nil' do
+ (nil..nil).size.should == nil
+ end
end
it "returns nil if first and last are not Numeric" do
diff --git a/spec/ruby/core/refinement/import_methods_spec.rb b/spec/ruby/core/refinement/import_methods_spec.rb
new file mode 100644
index 0000000000..1c526f5822
--- /dev/null
+++ b/spec/ruby/core/refinement/import_methods_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+
+describe "Refinement#import_methods" do
+ ruby_version_is "3.1" do
+ context "when methods are defined in Ruby code" do
+ it "imports methods" do
+ str_utils = Module.new do
+ def indent(level)
+ " " * level + self
+ end
+ end
+
+ Module.new do
+ refine String do
+ import_methods str_utils
+ "foo".indent(3).should == " foo"
+ end
+ end
+ end
+ end
+
+ context "when methods are not defined in Ruby code" do
+ it "raises ArgumentError" do
+ Module.new do
+ refine String do
+ -> {
+ import_methods Kernel
+ }.should raise_error(ArgumentError)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/refinement/include_spec.rb b/spec/ruby/core/refinement/include_spec.rb
new file mode 100644
index 0000000000..25a53f0ec7
--- /dev/null
+++ b/spec/ruby/core/refinement/include_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+describe "Refinement#include" do
+ ruby_version_is "3.1"..."3.2" do
+ it "warns about deprecation" do
+ Module.new do
+ refine String do
+ -> {
+ include Module.new
+ }.should complain(/warning: Refinement#include is deprecated and will be removed in Ruby 3.2/)
+ end
+ end
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "raises a TypeError" do
+ Module.new do
+ refine String do
+ -> {
+ include Module.new
+ }.should raise_error(TypeError, "Refinement#include has been removed")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/refinement/prepend_spec.rb b/spec/ruby/core/refinement/prepend_spec.rb
new file mode 100644
index 0000000000..27b70d392a
--- /dev/null
+++ b/spec/ruby/core/refinement/prepend_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+describe "Refinement#prepend" do
+ ruby_version_is "3.1"..."3.2" do
+ it "warns about deprecation" do
+ Module.new do
+ refine String do
+ -> {
+ prepend Module.new
+ }.should complain(/warning: Refinement#prepend is deprecated and will be removed in Ruby 3.2/)
+ end
+ end
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "raises a TypeError" do
+ Module.new do
+ refine String do
+ -> {
+ prepend Module.new
+ }.should raise_error(TypeError, "Refinement#prepend has been removed")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/regexp/compile_spec.rb b/spec/ruby/core/regexp/compile_spec.rb
index 329cb4f753..c41399cfbb 100644
--- a/spec/ruby/core/regexp/compile_spec.rb
+++ b/spec/ruby/core/regexp/compile_spec.rb
@@ -13,3 +13,7 @@ end
describe "Regexp.compile given a Regexp" do
it_behaves_like :regexp_new_regexp, :compile
end
+
+describe "Regexp.new given a non-String/Regexp" do
+ it_behaves_like :regexp_new_non_string_or_regexp, :compile
+end
diff --git a/spec/ruby/core/regexp/initialize_spec.rb b/spec/ruby/core/regexp/initialize_spec.rb
index 772a233e82..a1583384af 100644
--- a/spec/ruby/core/regexp/initialize_spec.rb
+++ b/spec/ruby/core/regexp/initialize_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "Regexp#initialize" do
it "is a private method" do
- Regexp.should have_private_method(:initialize)
+ Regexp.should have_private_instance_method(:initialize)
end
ruby_version_is ""..."3.0" do
diff --git a/spec/ruby/core/regexp/new_spec.rb b/spec/ruby/core/regexp/new_spec.rb
index ce662b7a4f..65f612df55 100644
--- a/spec/ruby/core/regexp/new_spec.rb
+++ b/spec/ruby/core/regexp/new_spec.rb
@@ -11,17 +11,9 @@ end
describe "Regexp.new given a Regexp" do
it_behaves_like :regexp_new_regexp, :new
- it_behaves_like :regexp_new_string_binary, :compile
+ it_behaves_like :regexp_new_string_binary, :new
end
-describe "Regexp.new given an Integer" do
- it "raises a TypeError" do
- -> { Regexp.new(1) }.should raise_error(TypeError)
- end
-end
-
-describe "Regexp.new given a Float" do
- it "raises a TypeError" do
- -> { Regexp.new(1.0) }.should raise_error(TypeError)
- end
+describe "Regexp.new given a non-String/Regexp" do
+ it_behaves_like :regexp_new_non_string_or_regexp, :new
end
diff --git a/spec/ruby/core/regexp/shared/new.rb b/spec/ruby/core/regexp/shared/new.rb
index a6d9c48112..058a51b1aa 100644
--- a/spec/ruby/core/regexp/shared/new.rb
+++ b/spec/ruby/core/regexp/shared/new.rb
@@ -24,6 +24,32 @@ describe :regexp_new, shared: true do
end
end
+describe :regexp_new_non_string_or_regexp, shared: true do
+ it "calls #to_str method for non-String/Regexp argument" do
+ obj = Object.new
+ def obj.to_str() "a" end
+
+ Regexp.send(@method, obj).should == /a/
+ end
+
+ it "raises TypeError if there is no #to_str method for non-String/Regexp argument" do
+ obj = Object.new
+ -> { Regexp.send(@method, obj) }.should raise_error(TypeError, "no implicit conversion of Object into String")
+
+ -> { Regexp.send(@method, 1) }.should raise_error(TypeError, "no implicit conversion of Integer into String")
+ -> { Regexp.send(@method, 1.0) }.should raise_error(TypeError, "no implicit conversion of Float into String")
+ -> { Regexp.send(@method, :symbol) }.should raise_error(TypeError, "no implicit conversion of Symbol into String")
+ -> { Regexp.send(@method, []) }.should raise_error(TypeError, "no implicit conversion of Array into String")
+ end
+
+ it "raises TypeError if #to_str returns non-String value" do
+ obj = Object.new
+ def obj.to_str() [] end
+
+ -> { Regexp.send(@method, obj) }.should raise_error(TypeError, /can't convert Object to String/)
+ end
+end
+
describe :regexp_new_string, shared: true do
it "uses the String argument as an unescaped literal to construct a Regexp object" do
Regexp.send(@method, "^hi{2,3}fo.o$").should == /^hi{2,3}fo.o$/
@@ -97,6 +123,16 @@ describe :regexp_new_string, shared: true do
(r.options & Regexp::EXTENDED).should_not == 0
end
+ it "does not try to convert the second argument to Integer with #to_int method call" do
+ ScratchPad.clear
+ obj = Object.new
+ def obj.to_int() ScratchPad.record(:called) end
+
+ Regexp.send(@method, "Hi", obj)
+
+ ScratchPad.recorded.should == nil
+ end
+
ruby_version_is ""..."3.2" do
it "treats any non-Integer, non-nil, non-false second argument as IGNORECASE" do
r = Regexp.send(@method, 'Hi', Object.new)
@@ -161,48 +197,50 @@ describe :regexp_new_string, shared: true do
end
end
- it "ignores the third argument if it is 'e' or 'euc' (case-insensitive)" do
- -> {
- Regexp.send(@method, 'Hi', nil, 'e').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'euc').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'E').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'EUC').encoding.should == Encoding::US_ASCII
- }.should complain(/encoding option is ignored/)
- end
+ ruby_version_is ""..."3.2" do
+ it "ignores the third argument if it is 'e' or 'euc' (case-insensitive)" do
+ -> {
+ Regexp.send(@method, 'Hi', nil, 'e').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'euc').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'E').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'EUC').encoding.should == Encoding::US_ASCII
+ }.should complain(/encoding option is ignored/)
+ end
- it "ignores the third argument if it is 's' or 'sjis' (case-insensitive)" do
- -> {
- Regexp.send(@method, 'Hi', nil, 's').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'sjis').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'S').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'SJIS').encoding.should == Encoding::US_ASCII
- }.should complain(/encoding option is ignored/)
- end
+ it "ignores the third argument if it is 's' or 'sjis' (case-insensitive)" do
+ -> {
+ Regexp.send(@method, 'Hi', nil, 's').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'sjis').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'S').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'SJIS').encoding.should == Encoding::US_ASCII
+ }.should complain(/encoding option is ignored/)
+ end
- it "ignores the third argument if it is 'u' or 'utf8' (case-insensitive)" do
- -> {
- Regexp.send(@method, 'Hi', nil, 'u').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'utf8').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'U').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'UTF8').encoding.should == Encoding::US_ASCII
- }.should complain(/encoding option is ignored/)
- end
+ it "ignores the third argument if it is 'u' or 'utf8' (case-insensitive)" do
+ -> {
+ Regexp.send(@method, 'Hi', nil, 'u').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'utf8').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'U').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'UTF8').encoding.should == Encoding::US_ASCII
+ }.should complain(/encoding option is ignored/)
+ end
- it "uses US_ASCII encoding if third argument is 'n' or 'none' (case insensitive) and only ascii characters" do
- Regexp.send(@method, 'Hi', nil, 'n').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'none').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'N').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'NONE').encoding.should == Encoding::US_ASCII
- end
+ it "uses US_ASCII encoding if third argument is 'n' or 'none' (case insensitive) and only ascii characters" do
+ Regexp.send(@method, 'Hi', nil, 'n').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'none').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'N').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'NONE').encoding.should == Encoding::US_ASCII
+ end
- it "uses ASCII_8BIT encoding if third argument is 'n' or 'none' (case insensitive) and non-ascii characters" do
- a = "(?:[\x8E\xA1-\xFE])"
- str = "\A(?:#{a}|x*)\z"
+ it "uses ASCII_8BIT encoding if third argument is 'n' or 'none' (case insensitive) and non-ascii characters" do
+ a = "(?:[\x8E\xA1-\xFE])"
+ str = "\A(?:#{a}|x*)\z"
- Regexp.send(@method, str, nil, 'N').encoding.should == Encoding::BINARY
- Regexp.send(@method, str, nil, 'n').encoding.should == Encoding::BINARY
- Regexp.send(@method, str, nil, 'none').encoding.should == Encoding::BINARY
- Regexp.send(@method, str, nil, 'NONE').encoding.should == Encoding::BINARY
+ Regexp.send(@method, str, nil, 'N').encoding.should == Encoding::BINARY
+ Regexp.send(@method, str, nil, 'n').encoding.should == Encoding::BINARY
+ Regexp.send(@method, str, nil, 'none').encoding.should == Encoding::BINARY
+ Regexp.send(@method, str, nil, 'NONE').encoding.should == Encoding::BINARY
+ end
end
describe "with escaped characters" do
@@ -562,8 +600,10 @@ describe :regexp_new_regexp, shared: true do
Regexp.send(@method, /Hi/n).encoding.should == Encoding::US_ASCII
end
- it "sets the encoding to source String's encoding if the Regexp literal has the 'n' option and the source String is not ASCII only" do
- Regexp.send(@method, Regexp.new("\\xff", nil, 'n')).encoding.should == Encoding::BINARY
+ ruby_version_is ''...'3.2' do
+ it "sets the encoding to source String's encoding if the Regexp literal has the 'n' option and the source String is not ASCII only" do
+ Regexp.send(@method, Regexp.new("\\xff", nil, 'n')).encoding.should == Encoding::BINARY
+ end
end
end
end
diff --git a/spec/ruby/core/regexp/shared/quote.rb b/spec/ruby/core/regexp/shared/quote.rb
index 33bdfd9979..9533102766 100644
--- a/spec/ruby/core/regexp/shared/quote.rb
+++ b/spec/ruby/core/regexp/shared/quote.rb
@@ -17,6 +17,11 @@ describe :regexp_quote, shared: true do
Regexp.send(@method, str).should == '\+\[\]\('
end
+ it "works for broken strings" do
+ Regexp.send(@method, "a.\x85b.".force_encoding("US-ASCII")).should =="a\\.\x85b\\.".force_encoding("US-ASCII")
+ Regexp.send(@method, "a.\x80".force_encoding("UTF-8")).should == "a\\.\x80".force_encoding("UTF-8")
+ end
+
it "sets the encoding of the result to US-ASCII if there are only US-ASCII characters present in the input String" do
str = "abc".force_encoding("euc-jp")
Regexp.send(@method, str).encoding.should == Encoding::US_ASCII
diff --git a/spec/ruby/core/regexp/source_spec.rb b/spec/ruby/core/regexp/source_spec.rb
index 709fee49b3..5f253da9ea 100644
--- a/spec/ruby/core/regexp/source_spec.rb
+++ b/spec/ruby/core/regexp/source_spec.rb
@@ -9,8 +9,26 @@ describe "Regexp#source" do
/x(.)xz/.source.should == "x(.)xz"
end
- it "will remove escape characters" do
- /foo\/bar/.source.should == "foo/bar"
+ it "keeps escape sequences as is" do
+ /\x20\+/.source.should == '\x20\+'
+ end
+
+ describe "escaping" do
+ it "keeps escaping of metacharacter" do
+ /\$/.source.should == "\\$"
+ end
+
+ it "keeps escaping of metacharacter used as a terminator" do
+ %r+\++.source.should == "\\+"
+ end
+
+ it "removes escaping of non-metacharacter used as a terminator" do
+ %r@\@@.source.should == "@"
+ end
+
+ it "keeps escaping of non-metacharacter not used as a terminator" do
+ /\@/.source.should == "\\@"
+ end
end
not_supported_on :opal do
diff --git a/spec/ruby/core/regexp/timeout_spec.rb b/spec/ruby/core/regexp/timeout_spec.rb
new file mode 100644
index 0000000000..6fce261814
--- /dev/null
+++ b/spec/ruby/core/regexp/timeout_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.2" do
+ describe "Regexp.timeout" do
+ after :each do
+ Regexp.timeout = nil
+ end
+
+ it "returns global timeout" do
+ Regexp.timeout = 3
+ Regexp.timeout.should == 3
+ end
+
+ it "raises Regexp::TimeoutError after global timeout elapsed" do
+ Regexp.timeout = 0.001
+ Regexp.timeout.should == 0.001
+
+ -> {
+ # A typical ReDoS case
+ /^(a*)*$/ =~ "a" * 1000000 + "x"
+ }.should raise_error(Regexp::TimeoutError, "regexp match timeout")
+ end
+
+ it "raises Regexp::TimeoutError after timeout keyword value elapsed" do
+ Regexp.timeout = 3 # This should be ignored
+ Regexp.timeout.should == 3
+
+ re = Regexp.new("^a*b?a*$", timeout: 0.001)
+
+ -> {
+ re =~ "a" * 1000000 + "x"
+ }.should raise_error(Regexp::TimeoutError, "regexp match timeout")
+ end
+ end
+end
diff --git a/spec/ruby/core/signal/trap_spec.rb b/spec/ruby/core/signal/trap_spec.rb
index 2a94a81634..10e122e072 100644
--- a/spec/ruby/core/signal/trap_spec.rb
+++ b/spec/ruby/core/signal/trap_spec.rb
@@ -221,6 +221,18 @@ describe "Signal.trap" do
Signal.trap(:HUP, @saved_trap).should equal(@proc)
end
+ it "raises ArgumentError when passed unknown signal" do
+ -> { Signal.trap(300) { } }.should raise_error(ArgumentError, "invalid signal number (300)")
+ -> { Signal.trap("USR10") { } }.should raise_error(ArgumentError, "unsupported signal `SIGUSR10'")
+ -> { Signal.trap("SIGUSR10") { } }.should raise_error(ArgumentError, "unsupported signal `SIGUSR10'")
+ end
+
+ it "raises ArgumentError when passed signal is not Integer, String or Symbol" do
+ -> { Signal.trap(nil) { } }.should raise_error(ArgumentError, "bad signal type NilClass")
+ -> { Signal.trap(100.0) { } }.should raise_error(ArgumentError, "bad signal type Float")
+ -> { Signal.trap(Rational(100)) { } }.should raise_error(ArgumentError, "bad signal type Rational")
+ end
+
# See man 2 signal
%w[KILL STOP].each do |signal|
it "raises ArgumentError or Errno::EINVAL for SIG#{signal}" do
diff --git a/spec/ruby/core/string/byteindex_spec.rb b/spec/ruby/core/string/byteindex_spec.rb
new file mode 100644
index 0000000000..af70c729e8
--- /dev/null
+++ b/spec/ruby/core/string/byteindex_spec.rb
@@ -0,0 +1,16 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#byteindex with Regexp" do
+ ruby_version_is "3.2" do
+ it "always clear $~" do
+ "a".byteindex(/a/)
+ $~.should_not == nil
+
+ string = "blablabla"
+ string.byteindex(/bla/, string.bytesize + 1)
+ $~.should == nil
+ end
+ end
+end
diff --git a/spec/ruby/core/string/byteslice_spec.rb b/spec/ruby/core/string/byteslice_spec.rb
index a49da040eb..312229523d 100644
--- a/spec/ruby/core/string/byteslice_spec.rb
+++ b/spec/ruby/core/string/byteslice_spec.rb
@@ -24,4 +24,10 @@ describe "String#byteslice on on non ASCII strings" do
"\u3042".byteslice(1..2).should == "\x81\x82".force_encoding("UTF-8")
"\u3042".byteslice(-1).should == "\x82".force_encoding("UTF-8")
end
+
+ it "returns a String in the same encoding as self" do
+ "ruby".encode("UTF-8").slice(0).encoding.should == Encoding::UTF_8
+ "ruby".encode("US-ASCII").slice(0).encoding.should == Encoding::US_ASCII
+ "ruby".encode("Windows-1251").slice(0).encoding.should == Encoding::Windows_1251
+ end
end
diff --git a/spec/ruby/core/string/capitalize_spec.rb b/spec/ruby/core/string/capitalize_spec.rb
index 8afaefc021..3f85cf5ae4 100644
--- a/spec/ruby/core/string/capitalize_spec.rb
+++ b/spec/ruby/core/string/capitalize_spec.rb
@@ -10,6 +10,7 @@ describe "String#capitalize" do
"hello".capitalize.should == "Hello"
"HELLO".capitalize.should == "Hello"
"123ABC".capitalize.should == "123abc"
+ "abcdef"[1...-1].capitalize.should == "Bcde"
end
describe "full Unicode case mapping" do
@@ -37,7 +38,7 @@ describe "String#capitalize" do
end
it "handles non-ASCII substrings properly" do
- "garçon"[1..-1].capitalize(:ascii).should == "Arçon"
+ "garçon"[1...-1].capitalize(:ascii).should == "Arço"
end
end
@@ -90,6 +91,10 @@ describe "String#capitalize" do
StringSpecs::MyString.new("Hello").capitalize.should be_an_instance_of(String)
end
end
+
+ it "returns a String in the same encoding as self" do
+ "h".encode("US-ASCII").capitalize.encoding.should == Encoding::US_ASCII
+ end
end
describe "String#capitalize!" do
diff --git a/spec/ruby/core/string/chars_spec.rb b/spec/ruby/core/string/chars_spec.rb
index e4f26bc0cc..715e65dc90 100644
--- a/spec/ruby/core/string/chars_spec.rb
+++ b/spec/ruby/core/string/chars_spec.rb
@@ -1,5 +1,4 @@
require_relative 'shared/chars'
-require_relative 'shared/each_char_without_block'
describe "String#chars" do
it_behaves_like :string_chars, :chars
@@ -7,4 +6,10 @@ describe "String#chars" do
it "returns an array when no block given" do
"hello".chars.should == ['h', 'e', 'l', 'l', 'o']
end
+
+ it "returns Strings in the same encoding as self" do
+ "hello".encode("US-ASCII").chars.each do |c|
+ c.encoding.should == Encoding::US_ASCII
+ end
+ end
end
diff --git a/spec/ruby/core/string/chomp_spec.rb b/spec/ruby/core/string/chomp_spec.rb
index c03bfc7951..d0508d938f 100644
--- a/spec/ruby/core/string/chomp_spec.rb
+++ b/spec/ruby/core/string/chomp_spec.rb
@@ -40,6 +40,10 @@ describe "String#chomp" do
"".chomp.should == ""
end
+ it "returns a String in the same encoding as self" do
+ "abc\n\n".encode("US-ASCII").chomp.encoding.should == Encoding::US_ASCII
+ end
+
ruby_version_is ''...'3.0' do
it "returns subclass instances when called on a subclass" do
str = StringSpecs::MyString.new("hello\n").chomp
diff --git a/spec/ruby/core/string/chop_spec.rb b/spec/ruby/core/string/chop_spec.rb
index 266d973f67..f598d34bc8 100644
--- a/spec/ruby/core/string/chop_spec.rb
+++ b/spec/ruby/core/string/chop_spec.rb
@@ -60,6 +60,10 @@ describe "String#chop" do
StringSpecs::MyString.new("hello\n").chop.should be_an_instance_of(String)
end
end
+
+ it "returns a String in the same encoding as self" do
+ "abc\n\n".encode("US-ASCII").chop.encoding.should == Encoding::US_ASCII
+ end
end
describe "String#chop!" do
diff --git a/spec/ruby/core/string/clone_spec.rb b/spec/ruby/core/string/clone_spec.rb
index f8d40423f0..a2ba2f9877 100644
--- a/spec/ruby/core/string/clone_spec.rb
+++ b/spec/ruby/core/string/clone_spec.rb
@@ -54,4 +54,8 @@ describe "String#clone" do
orig.should == "xtring"
clone.should == "string"
end
+
+ it "returns a String in the same encoding as self" do
+ "a".encode("US-ASCII").clone.encoding.should == Encoding::US_ASCII
+ end
end
diff --git a/spec/ruby/core/string/delete_prefix_spec.rb b/spec/ruby/core/string/delete_prefix_spec.rb
index a063e443d8..238de85f05 100644
--- a/spec/ruby/core/string/delete_prefix_spec.rb
+++ b/spec/ruby/core/string/delete_prefix_spec.rb
@@ -21,6 +21,10 @@ describe "String#delete_prefix" do
r.should == s
end
+ it "does not remove partial bytes, only full characters" do
+ "\xe3\x81\x82".delete_prefix("\xe3").should == "\xe3\x81\x82"
+ end
+
it "doesn't set $~" do
$~ = nil
@@ -47,6 +51,10 @@ describe "String#delete_prefix" do
s.delete_prefix('hell').should be_an_instance_of(String)
end
end
+
+ it "returns a String in the same encoding as self" do
+ 'hello'.encode("US-ASCII").delete_prefix('hell').encoding.should == Encoding::US_ASCII
+ end
end
describe "String#delete_prefix!" do
diff --git a/spec/ruby/core/string/delete_spec.rb b/spec/ruby/core/string/delete_spec.rb
index b91e88b76f..87831a9d19 100644
--- a/spec/ruby/core/string/delete_spec.rb
+++ b/spec/ruby/core/string/delete_spec.rb
@@ -95,6 +95,10 @@ describe "String#delete" do
StringSpecs::MyString.new("oh no!!!").delete("!").should be_an_instance_of(String)
end
end
+
+ it "returns a String in the same encoding as self" do
+ "hello".encode("US-ASCII").delete("lo").encoding.should == Encoding::US_ASCII
+ end
end
describe "String#delete!" do
diff --git a/spec/ruby/core/string/delete_suffix_spec.rb b/spec/ruby/core/string/delete_suffix_spec.rb
index 3d3274bc5b..6883d6938c 100644
--- a/spec/ruby/core/string/delete_suffix_spec.rb
+++ b/spec/ruby/core/string/delete_suffix_spec.rb
@@ -21,6 +21,10 @@ describe "String#delete_suffix" do
r.should == s
end
+ it "does not remove partial bytes, only full characters" do
+ "\xe3\x81\x82".delete_suffix("\x82").should == "\xe3\x81\x82"
+ end
+
it "doesn't set $~" do
$~ = nil
@@ -47,6 +51,10 @@ describe "String#delete_suffix" do
s.delete_suffix('ello').should be_an_instance_of(String)
end
end
+
+ it "returns a String in the same encoding as self" do
+ "hello".encode("US-ASCII").delete_suffix("ello").encoding.should == Encoding::US_ASCII
+ end
end
describe "String#delete_suffix!" do
diff --git a/spec/ruby/core/string/downcase_spec.rb b/spec/ruby/core/string/downcase_spec.rb
index 86d8480889..153b4ce191 100644
--- a/spec/ruby/core/string/downcase_spec.rb
+++ b/spec/ruby/core/string/downcase_spec.rb
@@ -8,6 +8,10 @@ describe "String#downcase" do
"hello".downcase.should == "hello"
end
+ it "returns a String in the same encoding as self" do
+ "hELLO".encode("US-ASCII").downcase.encoding.should == Encoding::US_ASCII
+ end
+
describe "full Unicode case mapping" do
it "works for all of Unicode with no option" do
"ÄÖÜ".downcase.should == "äöü"
@@ -27,6 +31,10 @@ describe "String#downcase" do
it "does not downcase non-ASCII characters" do
"CÅR".downcase(:ascii).should == "cÅr"
end
+
+ it "works with substrings" do
+ "prefix TÉ"[-2..-1].downcase(:ascii).should == "tÉ"
+ end
end
describe "full Unicode case mapping adapted for Turkic languages" do
diff --git a/spec/ruby/core/string/dump_spec.rb b/spec/ruby/core/string/dump_spec.rb
index 79a8b55e6d..81de0cfae4 100644
--- a/spec/ruby/core/string/dump_spec.rb
+++ b/spec/ruby/core/string/dump_spec.rb
@@ -350,7 +350,7 @@ describe "String#dump" do
].should be_computed_by(:dump)
end
- it "returns a string with multi-byte UTF-8 characters replaced by \\u{} notation with upper-case hex digits" do
+ it "returns a string with multi-byte UTF-8 characters less than or equal 0xFFFF replaced by \\uXXXX notation with upper-case hex digits" do
[ [0200.chr('utf-8'), '"\u0080"'],
[0201.chr('utf-8'), '"\u0081"'],
[0202.chr('utf-8'), '"\u0082"'],
@@ -382,15 +382,21 @@ describe "String#dump" do
[0235.chr('utf-8'), '"\u009D"'],
[0236.chr('utf-8'), '"\u009E"'],
[0237.chr('utf-8'), '"\u009F"'],
+ [0177777.chr('utf-8'), '"\uFFFF"'],
].should be_computed_by(:dump)
end
+ it "returns a string with multi-byte UTF-8 characters greater than 0xFFFF replaced by \\u{XXXXXX} notation with upper-case hex digits" do
+ 0x10000.chr('utf-8').dump.should == '"\u{10000}"'
+ 0x10FFFF.chr('utf-8').dump.should == '"\u{10FFFF}"'
+ end
+
it "includes .force_encoding(name) if the encoding isn't ASCII compatible" do
"\u{876}".encode('utf-16be').dump.should.end_with?(".force_encoding(\"UTF-16BE\")")
"\u{876}".encode('utf-16le').dump.should.end_with?(".force_encoding(\"UTF-16LE\")")
end
- it "keeps origin encoding" do
+ it "returns a String in the same encoding as self" do
"foo".encode("ISO-8859-1").dump.encoding.should == Encoding::ISO_8859_1
"foo".encode('windows-1251').dump.encoding.should == Encoding::Windows_1251
1.chr.dump.encoding.should == Encoding::US_ASCII
diff --git a/spec/ruby/core/string/dup_spec.rb b/spec/ruby/core/string/dup_spec.rb
index eec3cf0a70..73f71b8ffc 100644
--- a/spec/ruby/core/string/dup_spec.rb
+++ b/spec/ruby/core/string/dup_spec.rb
@@ -58,4 +58,8 @@ describe "String#dup" do
orig.should == "c"
copy.should == "b"
end
+
+ it "returns a String in the same encoding as self" do
+ "hello".encode("US-ASCII").dup.encoding.should == Encoding::US_ASCII
+ end
end
diff --git a/spec/ruby/core/string/element_set_spec.rb b/spec/ruby/core/string/element_set_spec.rb
index 881b4343d4..fa041fa31d 100644
--- a/spec/ruby/core/string/element_set_spec.rb
+++ b/spec/ruby/core/string/element_set_spec.rb
@@ -357,11 +357,11 @@ describe "String#[]= with a Range index" do
end
it "raises a RangeError if negative Range begin is out of range" do
- -> { "abc"[-4..-2] = "x" }.should raise_error(RangeError)
+ -> { "abc"[-4..-2] = "x" }.should raise_error(RangeError, "-4..-2 out of range")
end
it "raises a RangeError if positive Range begin is greater than String size" do
- -> { "abc"[4..2] = "x" }.should raise_error(RangeError)
+ -> { "abc"[4..2] = "x" }.should raise_error(RangeError, "4..2 out of range")
end
it "uses the Range end as an index rather than a count" do
diff --git a/spec/ruby/core/string/encoding_spec.rb b/spec/ruby/core/string/encoding_spec.rb
index 4d17a39f29..574a1e2f92 100644
--- a/spec/ruby/core/string/encoding_spec.rb
+++ b/spec/ruby/core/string/encoding_spec.rb
@@ -10,6 +10,7 @@ describe "String#encoding" do
it "is equal to the source encoding by default" do
s = StringSpecs::ISO88599Encoding.new
s.cedilla.encoding.should == s.source_encoding
+ s.cedilla.encode("utf-8").should == 350.chr(Encoding::UTF_8) # S-cedilla
end
it "returns the given encoding if #force_encoding has been called" do
diff --git a/spec/ruby/core/string/fixtures/iso-8859-9-encoding.rb b/spec/ruby/core/string/fixtures/iso-8859-9-encoding.rb
index 61a691ff78..cfa91dedc3 100644
--- a/spec/ruby/core/string/fixtures/iso-8859-9-encoding.rb
+++ b/spec/ruby/core/string/fixtures/iso-8859-9-encoding.rb
@@ -4,6 +4,6 @@ module StringSpecs
def source_encoding; __ENCODING__; end
def x_escape; [0xDF].pack('C').force_encoding("iso-8859-9"); end
def ascii_only; "glark"; end
- def cedilla; "Ş"; end
+ def cedilla; ""; end # S-cedilla
end
end
diff --git a/spec/ruby/core/string/fixtures/to_c.rb b/spec/ruby/core/string/fixtures/to_c.rb
new file mode 100644
index 0000000000..7776933263
--- /dev/null
+++ b/spec/ruby/core/string/fixtures/to_c.rb
@@ -0,0 +1,5 @@
+module StringSpecs
+ def self.to_c_method(string)
+ string.to_c
+ end
+end
diff --git a/spec/ruby/core/string/gsub_spec.rb b/spec/ruby/core/string/gsub_spec.rb
index 3211ebbd0a..c87a566591 100644
--- a/spec/ruby/core/string/gsub_spec.rb
+++ b/spec/ruby/core/string/gsub_spec.rb
@@ -210,8 +210,6 @@ describe "String#gsub with pattern and replacement" do
end
end
- # Note: $~ cannot be tested because mspec messes with it
-
it "sets $~ to MatchData of last match and nil when there's none" do
'hello.'.gsub('hello', 'x')
$~[0].should == 'hello'
@@ -225,6 +223,18 @@ describe "String#gsub with pattern and replacement" do
'hello.'.gsub(/not/, 'x')
$~.should == nil
end
+
+ it "handles a pattern in a superset encoding" do
+ result = 'abc'.force_encoding(Encoding::US_ASCII).gsub('é', 'è')
+ result.should == 'abc'
+ result.encoding.should == Encoding::US_ASCII
+ end
+
+ it "handles a pattern in a subset encoding" do
+ result = 'été'.gsub('t'.force_encoding(Encoding::US_ASCII), 'u')
+ result.should == 'éué'
+ result.encoding.should == Encoding::UTF_8
+ end
end
describe "String#gsub with pattern and Hash" do
@@ -521,6 +531,27 @@ describe "String#gsub! with pattern and replacement" do
-> { s.gsub!(/e/, "e") }.should raise_error(FrozenError)
-> { s.gsub!(/[aeiou]/, '*') }.should raise_error(FrozenError)
end
+
+ it "handles a pattern in a superset encoding" do
+ string = 'abc'.force_encoding(Encoding::US_ASCII)
+
+ result = string.gsub!('é', 'è')
+
+ result.should == nil
+ string.should == 'abc'
+ string.encoding.should == Encoding::US_ASCII
+ end
+
+ it "handles a pattern in a subset encoding" do
+ string = 'été'
+ pattern = 't'.force_encoding(Encoding::US_ASCII)
+
+ result = string.gsub!(pattern, 'u')
+
+ result.should == string
+ string.should == 'éué'
+ string.encoding.should == Encoding::UTF_8
+ end
end
describe "String#gsub! with pattern and block" do
diff --git a/spec/ruby/core/string/include_spec.rb b/spec/ruby/core/string/include_spec.rb
index e32eb17c29..23e1e134ec 100644
--- a/spec/ruby/core/string/include_spec.rb
+++ b/spec/ruby/core/string/include_spec.rb
@@ -13,6 +13,20 @@ describe "String#include? with String" do
StringSpecs::MyString.new("hello").include?(StringSpecs::MyString.new("lo")).should == true
end
+ it "returns true if both strings are empty" do
+ "".should.include?("")
+ "".force_encoding("EUC-JP").should.include?("")
+ "".should.include?("".force_encoding("EUC-JP"))
+ "".force_encoding("EUC-JP").should.include?("".force_encoding("EUC-JP"))
+ end
+
+ it "returns true if the RHS is empty" do
+ "a".should.include?("")
+ "a".force_encoding("EUC-JP").should.include?("")
+ "a".should.include?("".force_encoding("EUC-JP"))
+ "a".force_encoding("EUC-JP").should.include?("".force_encoding("EUC-JP"))
+ end
+
it "tries to convert other to string using to_str" do
other = mock('lo')
other.should_receive(:to_str).and_return("lo")
diff --git a/spec/ruby/core/string/index_spec.rb b/spec/ruby/core/string/index_spec.rb
index 5d77a88e4e..2eeee9be87 100644
--- a/spec/ruby/core/string/index_spec.rb
+++ b/spec/ruby/core/string/index_spec.rb
@@ -159,6 +159,14 @@ describe "String#index with String" do
"あれ".index char
end.should raise_error(Encoding::CompatibilityError)
end
+
+ it "handles a substring in a superset encoding" do
+ 'abc'.force_encoding(Encoding::US_ASCII).index('é').should == nil
+ end
+
+ it "handles a substring in a subset encoding" do
+ 'été'.index('t'.force_encoding(Encoding::US_ASCII)).should == 1
+ end
end
describe "String#index with Regexp" do
diff --git a/spec/ruby/core/string/inspect_spec.rb b/spec/ruby/core/string/inspect_spec.rb
index 8bfd465144..8bf3d3161f 100644
--- a/spec/ruby/core/string/inspect_spec.rb
+++ b/spec/ruby/core/string/inspect_spec.rb
@@ -19,6 +19,21 @@ describe "String#inspect" do
].should be_computed_by(:inspect)
end
+ it "returns a string with special characters replaced with \\<char> notation for UTF-16" do
+ pairs = [
+ ["\a", '"\\a"'],
+ ["\b", '"\\b"'],
+ ["\t", '"\\t"'],
+ ["\n", '"\\n"'],
+ ["\v", '"\\v"'],
+ ["\f", '"\\f"'],
+ ["\r", '"\\r"'],
+ ["\e", '"\\e"']
+ ].map { |str, result| [str.encode('UTF-16LE'), result] }
+
+ pairs.should be_computed_by(:inspect)
+ end
+
it "returns a string with \" and \\ escaped with a backslash" do
[ ["\"", '"\\""'],
["\\", '"\\\\"']
@@ -311,6 +326,11 @@ describe "String#inspect" do
"\xF0\x9F".inspect.should == '"\\xF0\\x9F"'
end
+ it "works for broken US-ASCII strings" do
+ s = "©".force_encoding("US-ASCII")
+ s.inspect.should == '"\xC2\xA9"'
+ end
+
describe "when default external is UTF-8" do
before :each do
@extenc, Encoding.default_external = Encoding.default_external, Encoding::UTF_8
diff --git a/spec/ruby/core/string/lines_spec.rb b/spec/ruby/core/string/lines_spec.rb
index ad4b119074..40ab5f71d8 100644
--- a/spec/ruby/core/string/lines_spec.rb
+++ b/spec/ruby/core/string/lines_spec.rb
@@ -1,7 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/each_line'
-require_relative 'shared/each_line_without_block'
describe "String#lines" do
it_behaves_like :string_each_line, :lines
diff --git a/spec/ruby/core/string/lstrip_spec.rb b/spec/ruby/core/string/lstrip_spec.rb
index 02bc6b4322..75434613f1 100644
--- a/spec/ruby/core/string/lstrip_spec.rb
+++ b/spec/ruby/core/string/lstrip_spec.rb
@@ -10,6 +10,14 @@ describe "String#lstrip" do
" hello world ".lstrip.should == "hello world "
"\n\r\t\n\v\r hello world ".lstrip.should == "hello world "
"hello".lstrip.should == "hello"
+ " こにちわ".lstrip.should == "こにちわ"
+ end
+
+ it "works with lazy substrings" do
+ " hello "[1...-1].lstrip.should == "hello "
+ " hello world "[1...-1].lstrip.should == "hello world "
+ "\n\r\t\n\v\r hello world "[1...-1].lstrip.should == "hello world "
+ " こにちわ "[1...-1].lstrip.should == "こにちわ"
end
ruby_version_is '3.0' do
@@ -27,20 +35,26 @@ describe "String#lstrip!" do
a.should == "hello "
end
+ it "returns nil if no modifications were made" do
+ a = "hello"
+ a.lstrip!.should == nil
+ a.should == "hello"
+ end
+
+ it "makes a string empty if it is only whitespace" do
+ "".lstrip!.should == nil
+ " ".lstrip.should == ""
+ " ".lstrip.should == ""
+ end
+
ruby_version_is '3.0' do
- it "strips leading \\0" do
+ it "removes leading NULL bytes and whitespace" do
a = "\000 \000hello\000 \000"
a.lstrip!
a.should == "hello\000 \000"
end
end
- it "returns nil if no modifications were made" do
- a = "hello"
- a.lstrip!.should == nil
- a.should == "hello"
- end
-
it "raises a FrozenError on a frozen instance that is modified" do
-> { " hello ".freeze.lstrip! }.should raise_error(FrozenError)
end
@@ -51,9 +65,13 @@ describe "String#lstrip!" do
-> { "".freeze.lstrip! }.should raise_error(FrozenError)
end
- it "raises an ArgumentError if the first codepoint is invalid" do
+ it "raises an ArgumentError if the first non-space codepoint is invalid" do
s = "\xDFabc".force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
-> { s.lstrip! }.should raise_error(ArgumentError)
+
+ s = " \xDFabc".force_encoding(Encoding::UTF_8)
+ s.valid_encoding?.should be_false
+ -> { s.lstrip! }.should raise_error(ArgumentError)
end
end
diff --git a/spec/ruby/core/string/modulo_spec.rb b/spec/ruby/core/string/modulo_spec.rb
index 99c1694417..bf96a82874 100644
--- a/spec/ruby/core/string/modulo_spec.rb
+++ b/spec/ruby/core/string/modulo_spec.rb
@@ -368,8 +368,16 @@ describe "String#%" do
("%c" % 'A').should == "A"
end
- it "raises an exception for multiple character strings as argument for %c" do
- -> { "%c" % 'AA' }.should raise_error(ArgumentError)
+ ruby_version_is ""..."3.2" do
+ it "raises an exception for multiple character strings as argument for %c" do
+ -> { "%c" % 'AA' }.should raise_error(ArgumentError)
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "supports only the first character as argument for %c" do
+ ("%c" % 'AA').should == "A"
+ end
end
it "calls to_str on argument for %c formats" do
diff --git a/spec/ruby/core/string/ord_spec.rb b/spec/ruby/core/string/ord_spec.rb
index cfc630a124..4cf26990fe 100644
--- a/spec/ruby/core/string/ord_spec.rb
+++ b/spec/ruby/core/string/ord_spec.rb
@@ -25,4 +25,9 @@ describe "String#ord" do
it "raises an ArgumentError if called on an empty String" do
-> { ''.ord }.should raise_error(ArgumentError)
end
+
+ it "raises ArgumentError if the character is broken" do
+ s = "©".force_encoding("US-ASCII")
+ -> { s.ord }.should raise_error(ArgumentError, "invalid byte sequence in US-ASCII")
+ end
end
diff --git a/spec/ruby/core/string/partition_spec.rb b/spec/ruby/core/string/partition_spec.rb
index 98311f2be4..9cb3672881 100644
--- a/spec/ruby/core/string/partition_spec.rb
+++ b/spec/ruby/core/string/partition_spec.rb
@@ -38,4 +38,26 @@ describe "String#partition with String" do
it "takes precedence over a given block" do
"hello world".partition("o") { true }.should == ["hell", "o", " world"]
end
+
+ it "handles a pattern in a superset encoding" do
+ string = "hello".force_encoding(Encoding::US_ASCII)
+
+ result = string.partition("é")
+
+ result.should == ["hello", "", ""]
+ result[0].encoding.should == Encoding::US_ASCII
+ result[1].encoding.should == Encoding::US_ASCII
+ result[2].encoding.should == Encoding::US_ASCII
+ end
+
+ it "handles a pattern in a subset encoding" do
+ pattern = "o".force_encoding(Encoding::US_ASCII)
+
+ result = "héllo world".partition(pattern)
+
+ result.should == ["héll", "o", " world"]
+ result[0].encoding.should == Encoding::UTF_8
+ result[1].encoding.should == Encoding::US_ASCII
+ result[2].encoding.should == Encoding::UTF_8
+ end
end
diff --git a/spec/ruby/core/string/reverse_spec.rb b/spec/ruby/core/string/reverse_spec.rb
index bade4685d9..73526256ef 100644
--- a/spec/ruby/core/string/reverse_spec.rb
+++ b/spec/ruby/core/string/reverse_spec.rb
@@ -29,6 +29,18 @@ describe "String#reverse" do
it "reverses a string with multi byte characters" do
"微軟正黑體".reverse.should == "體黑正軟微"
end
+
+ it "works with a broken string" do
+ str = "微軟\xDF\xDE正黑體".force_encoding(Encoding::UTF_8)
+
+ str.valid_encoding?.should be_false
+
+ str.reverse.should == "體黑正\xDE\xDF軟微"
+ end
+
+ it "returns a String in the same encoding as self" do
+ "stressed".encode("US-ASCII").reverse.encoding.should == Encoding::US_ASCII
+ end
end
describe "String#reverse!" do
@@ -55,4 +67,13 @@ describe "String#reverse!" do
str.reverse!
str.should == "體黑正軟微"
end
+
+ it "works with a broken string" do
+ str = "微軟\xDF\xDE正黑體".force_encoding(Encoding::UTF_8)
+
+ str.valid_encoding?.should be_false
+ str.reverse!
+
+ str.should == "體黑正\xDE\xDF軟微"
+ end
end
diff --git a/spec/ruby/core/string/rindex_spec.rb b/spec/ruby/core/string/rindex_spec.rb
index a3b437a1e4..cd2aabba34 100644
--- a/spec/ruby/core/string/rindex_spec.rb
+++ b/spec/ruby/core/string/rindex_spec.rb
@@ -196,6 +196,14 @@ describe "String#rindex with String" do
it "raises a TypeError when given offset is nil" do
-> { "str".rindex("st", nil) }.should raise_error(TypeError)
end
+
+ it "handles a substring in a superset encoding" do
+ 'abc'.force_encoding(Encoding::US_ASCII).rindex('é').should == nil
+ end
+
+ it "handles a substring in a subset encoding" do
+ 'été'.rindex('t'.force_encoding(Encoding::US_ASCII)).should == 1
+ end
end
describe "String#rindex with Regexp" do
@@ -257,6 +265,15 @@ describe "String#rindex with Regexp" do
$~.should == nil
end
+ it "always clear $~" do
+ "a".rindex(/a/)
+ $~.should_not == nil
+
+ string = "blablabla"
+ string.rindex(/bla/, -(string.length + 1))
+ $~.should == nil
+ end
+
it "starts the search at the given offset" do
"blablabla".rindex(/.{0}/, 5).should == 5
"blablabla".rindex(/.{1}/, 5).should == 5
diff --git a/spec/ruby/core/string/rpartition_spec.rb b/spec/ruby/core/string/rpartition_spec.rb
index c8f9afaee9..21e87f530a 100644
--- a/spec/ruby/core/string/rpartition_spec.rb
+++ b/spec/ruby/core/string/rpartition_spec.rb
@@ -46,4 +46,26 @@ describe "String#rpartition with String" do
->{ "hello".rpartition(5) }.should raise_error(TypeError)
->{ "hello".rpartition(nil) }.should raise_error(TypeError)
end
+
+ it "handles a pattern in a superset encoding" do
+ string = "hello".force_encoding(Encoding::US_ASCII)
+
+ result = string.rpartition("é")
+
+ result.should == ["", "", "hello"]
+ result[0].encoding.should == Encoding::US_ASCII
+ result[1].encoding.should == Encoding::US_ASCII
+ result[2].encoding.should == Encoding::US_ASCII
+ end
+
+ it "handles a pattern in a subset encoding" do
+ pattern = "o".force_encoding(Encoding::US_ASCII)
+
+ result = "héllo world".rpartition(pattern)
+
+ result.should == ["héllo w", "o", "rld"]
+ result[0].encoding.should == Encoding::UTF_8
+ result[1].encoding.should == Encoding::US_ASCII
+ result[2].encoding.should == Encoding::UTF_8
+ end
end
diff --git a/spec/ruby/core/string/rstrip_spec.rb b/spec/ruby/core/string/rstrip_spec.rb
index dc34b12719..e96ce4120f 100644
--- a/spec/ruby/core/string/rstrip_spec.rb
+++ b/spec/ruby/core/string/rstrip_spec.rb
@@ -11,6 +11,14 @@ describe "String#rstrip" do
" hello world \n\r\t\n\v\r".rstrip.should == " hello world"
"hello".rstrip.should == "hello"
"hello\x00".rstrip.should == "hello"
+ "こにちわ ".rstrip.should == "こにちわ"
+ end
+
+ it "works with lazy substrings" do
+ " hello "[1...-1].rstrip.should == " hello"
+ " hello world "[1...-1].rstrip.should == " hello world"
+ " hello world \n\r\t\n\v\r"[1...-1].rstrip.should == " hello world"
+ " こにちわ "[1...-1].rstrip.should == "こにちわ"
end
it "returns a copy of self with all trailing whitespace and NULL bytes removed" do
@@ -37,6 +45,20 @@ describe "String#rstrip!" do
a.should == "hello"
end
+ it "makes a string empty if it is only whitespace" do
+ "".rstrip!.should == nil
+ " ".rstrip.should == ""
+ " ".rstrip.should == ""
+ end
+
+ ruby_version_is '3.0' do
+ it "removes trailing NULL bytes and whitespace" do
+ a = "\000 goodbye \000"
+ a.rstrip!
+ a.should == "\000 goodbye"
+ end
+ end
+
it "raises a FrozenError on a frozen instance that is modified" do
-> { " hello ".freeze.rstrip! }.should raise_error(FrozenError)
end
@@ -47,9 +69,27 @@ describe "String#rstrip!" do
-> { "".freeze.rstrip! }.should raise_error(FrozenError)
end
- it "raises an ArgumentError if the last codepoint is invalid" do
- s = "abc\xDF".force_encoding(Encoding::UTF_8)
- s.valid_encoding?.should be_false
- -> { s.rstrip! }.should raise_error(ArgumentError)
+ ruby_version_is "3.2" do
+ it "raises an Encoding::CompatibilityError if the last non-space codepoint is invalid" do
+ s = "abc\xDF".force_encoding(Encoding::UTF_8)
+ s.valid_encoding?.should be_false
+ -> { s.rstrip! }.should raise_error(Encoding::CompatibilityError)
+
+ s = "abc\xDF ".force_encoding(Encoding::UTF_8)
+ s.valid_encoding?.should be_false
+ -> { s.rstrip! }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+
+ ruby_version_is ""..."3.2" do
+ it "raises an ArgumentError if the last non-space codepoint is invalid" do
+ s = "abc\xDF".force_encoding(Encoding::UTF_8)
+ s.valid_encoding?.should be_false
+ -> { s.rstrip! }.should raise_error(ArgumentError)
+
+ s = "abc\xDF ".force_encoding(Encoding::UTF_8)
+ s.valid_encoding?.should be_false
+ -> { s.rstrip! }.should raise_error(ArgumentError)
+ end
end
end
diff --git a/spec/ruby/core/string/scan_spec.rb b/spec/ruby/core/string/scan_spec.rb
index ab73f5747b..a2d1815132 100644
--- a/spec/ruby/core/string/scan_spec.rb
+++ b/spec/ruby/core/string/scan_spec.rb
@@ -69,6 +69,12 @@ describe "String#scan" do
it "does not raise any errors when passed a multi-byte string" do
"あああaaaあああ".scan("あああ").should == ["あああ", "あああ"]
end
+
+ it "returns Strings in the same encoding as self" do
+ "cruel world".encode("US-ASCII").scan(/\w+/).each do |s|
+ s.encoding.should == Encoding::US_ASCII
+ end
+ end
end
describe "String#scan with pattern and block" do
diff --git a/spec/ruby/core/string/scrub_spec.rb b/spec/ruby/core/string/scrub_spec.rb
index 66755bcc7b..a51fbd020a 100644
--- a/spec/ruby/core/string/scrub_spec.rb
+++ b/spec/ruby/core/string/scrub_spec.rb
@@ -31,6 +31,11 @@ describe "String#scrub with a default replacement" do
input.scrub.should == "abc?????"
end
+ it "returns a String in the same encoding as self" do
+ x81 = [0x81].pack('C').force_encoding('utf-8')
+ "abc\u3042#{x81}".scrub.encoding.should == Encoding::UTF_8
+ end
+
ruby_version_is '3.0' do
it "returns String instances when called on a subclass" do
StringSpecs::MyString.new("foo").scrub.should be_an_instance_of(String)
@@ -80,6 +85,11 @@ describe "String#scrub with a custom replacement" do
block.should raise_error(ArgumentError)
end
+ it "returns a String in the same encoding as self" do
+ x81 = [0x81].pack('C').force_encoding('utf-8')
+ "abc\u3042#{x81}".scrub("*").encoding.should == Encoding::UTF_8
+ end
+
it "raises TypeError when a non String replacement is given" do
x81 = [0x81].pack('C').force_encoding('utf-8')
block = -> { "foo#{x81}".scrub(1) }
diff --git a/spec/ruby/core/string/setbyte_spec.rb b/spec/ruby/core/string/setbyte_spec.rb
index 03e5bad88b..77bff64038 100644
--- a/spec/ruby/core/string/setbyte_spec.rb
+++ b/spec/ruby/core/string/setbyte_spec.rb
@@ -36,6 +36,12 @@ describe "String#setbyte" do
str.valid_encoding?.should be_true
str.setbyte(2,253)
str.valid_encoding?.should be_false
+
+ str = "ABC"
+ str.setbyte(0, 0x20) # ' '
+ str.should.valid_encoding?
+ str.setbyte(0, 0xE3)
+ str.should_not.valid_encoding?
end
it "regards a negative index as counting from the end of the String" do
diff --git a/spec/ruby/core/string/shared/dedup.rb b/spec/ruby/core/string/shared/dedup.rb
index 345e874583..6ffcb9b045 100644
--- a/spec/ruby/core/string/shared/dedup.rb
+++ b/spec/ruby/core/string/shared/dedup.rb
@@ -38,6 +38,16 @@ describe :string_dedup, shared: true do
dynamic.send(@method).should equal("this string is frozen".send(@method).freeze)
end
+ it "does not deduplicate a frozen string when it has instance variables" do
+ dynamic = %w(this string is frozen).join(' ')
+ dynamic.instance_variable_set(:@a, 1)
+ dynamic.freeze
+
+ dynamic.send(@method).should_not equal("this string is frozen".freeze)
+ dynamic.send(@method).should_not equal("this string is frozen".send(@method).freeze)
+ dynamic.send(@method).should equal(dynamic)
+ end
+
ruby_version_is "3.0" do
it "interns the provided string if it is frozen" do
dynamic = "this string is unique and frozen #{rand}".freeze
diff --git a/spec/ruby/core/string/shared/each_line.rb b/spec/ruby/core/string/shared/each_line.rb
index bfedf8f35a..df78bd2186 100644
--- a/spec/ruby/core/string/shared/each_line.rb
+++ b/spec/ruby/core/string/shared/each_line.rb
@@ -122,6 +122,12 @@ describe :string_each_line, shared: true do
out.should == ["hello\n", "world."]
end
+ it "returns Strings in the same encoding as self" do
+ "one\ntwo\r\nthree".encode("US-ASCII").send(@method) do |s|
+ s.encoding.should == Encoding::US_ASCII
+ end
+ end
+
it "raises a TypeError when the separator can't be converted to a string" do
-> { "hello world".send(@method, false) {} }.should raise_error(TypeError)
-> { "hello world".send(@method, mock('x')) {} }.should raise_error(TypeError)
diff --git a/spec/ruby/core/string/shared/partition.rb b/spec/ruby/core/string/shared/partition.rb
index 7dc3d9cc0b..41b3c7e0c9 100644
--- a/spec/ruby/core/string/shared/partition.rb
+++ b/spec/ruby/core/string/shared/partition.rb
@@ -33,4 +33,19 @@ describe :string_partition, shared: true do
end
end
end
+
+ it "returns before- and after- parts in the same encoding as self" do
+ strings = "hello".encode("US-ASCII").send(@method, "ello")
+ strings[0].encoding.should == Encoding::US_ASCII
+ strings[2].encoding.should == Encoding::US_ASCII
+
+ strings = "hello".encode("US-ASCII").send(@method, /ello/)
+ strings[0].encoding.should == Encoding::US_ASCII
+ strings[2].encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns the matching part in the separator's encoding" do
+ strings = "hello".encode("US-ASCII").send(@method, "ello")
+ strings[1].encoding.should == Encoding::UTF_8
+ end
end
diff --git a/spec/ruby/core/string/shared/slice.rb b/spec/ruby/core/string/shared/slice.rb
index 713234fffd..a7c1d05b56 100644
--- a/spec/ruby/core/string/shared/slice.rb
+++ b/spec/ruby/core/string/shared/slice.rb
@@ -80,7 +80,7 @@ describe :string_slice_index_length, shared: true do
"hello there".send(@method, -3,2).should == "er"
end
- it "returns a string with the same encoding" do
+ it "returns a string with the same encoding as self" do
s = "hello there"
s.send(@method, 1, 9).encoding.should == s.encoding
@@ -206,6 +206,10 @@ describe :string_slice_range, shared: true do
"x".send(@method, 1..-1).should == ""
end
+ it "returns a String in the same encoding as self" do
+ "hello there".encode("US-ASCII").send(@method, 1..1).encoding.should == Encoding::US_ASCII
+ end
+
it "returns nil if the beginning of the range falls outside of self" do
"hello there".send(@method, 12..-1).should == nil
"hello there".send(@method, 20..25).should == nil
@@ -328,7 +332,8 @@ describe :string_slice_regexp, shared: true do
"hello there".send(@method, /xyz/).should == nil
end
- not_supported_on :opal do
+ it "returns a String in the same encoding as self" do
+ "hello there".encode("US-ASCII").send(@method, /[aeiou](.)\1/).encoding.should == Encoding::US_ASCII
end
ruby_version_is ''...'3.0' do
@@ -391,6 +396,10 @@ describe :string_slice_regexp_index, shared: true do
$~[1].should == nil
end
+ it "returns a String in the same encoding as self" do
+ "hello there".encode("US-ASCII").send(@method, /[aeiou](.)\1/, 0).encoding.should == Encoding::US_ASCII
+ end
+
it "calls to_int on the given index" do
obj = mock('2')
obj.should_receive(:to_int).and_return(2)
diff --git a/spec/ruby/core/string/shared/strip.rb b/spec/ruby/core/string/shared/strip.rb
index 9c232b4694..0c0aae20f3 100644
--- a/spec/ruby/core/string/shared/strip.rb
+++ b/spec/ruby/core/string/shared/strip.rb
@@ -2,6 +2,10 @@ require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
describe :string_strip, shared: true do
+ it "returns a String in the same encoding as self" do
+ " hello ".encode("US-ASCII").send(@method).encoding.should == Encoding::US_ASCII
+ end
+
ruby_version_is '3.0' do
it "returns String instances when called on a subclass" do
StringSpecs::MyString.new(" hello ").send(@method).should be_an_instance_of(String)
diff --git a/spec/ruby/core/string/shared/succ.rb b/spec/ruby/core/string/shared/succ.rb
index 66edf6dc82..3605fa99a2 100644
--- a/spec/ruby/core/string/shared/succ.rb
+++ b/spec/ruby/core/string/shared/succ.rb
@@ -74,6 +74,10 @@ describe :string_succ, shared: true do
StringSpecs::MyString.new("z").send(@method).should be_an_instance_of(String)
end
end
+
+ it "returns a String in the same encoding as self" do
+ "z".encode("US-ASCII").send(@method).encoding.should == Encoding::US_ASCII
+ end
end
describe :string_succ_bang, shared: true do
diff --git a/spec/ruby/core/string/split_spec.rb b/spec/ruby/core/string/split_spec.rb
index 7ef34c65da..519c5d845d 100644
--- a/spec/ruby/core/string/split_spec.rb
+++ b/spec/ruby/core/string/split_spec.rb
@@ -29,9 +29,35 @@ describe "String#split with String" do
"1,2,,3,4,,".split(',').should == ["1", "2", "", "3", "4"]
"1,2,,3,4,,".split(',', 0).should == ["1", "2", "", "3", "4"]
" a b c\nd ".split(" ").should == ["", "a", "b", "c\nd"]
+ " a あ c\nd ".split(" ").should == ["", "a", "あ", "c\nd"]
"hai".split("hai").should == []
",".split(",").should == []
",".split(",", 0).should == []
+ "あ".split("あ").should == []
+ "あ".split("あ", 0).should == []
+ end
+
+ it "does not suppress trailing empty fields when a positive limit is given" do
+ " 1 2 ".split(" ", 2).should == ["1", "2 "]
+ " 1 2 ".split(" ", 3).should == ["1", "2", ""]
+ " 1 2 ".split(" ", 4).should == ["1", "2", ""]
+ " 1 あ ".split(" ", 2).should == ["1", "あ "]
+ " 1 あ ".split(" ", 3).should == ["1", "あ", ""]
+ " 1 あ ".split(" ", 4).should == ["1", "あ", ""]
+
+ "1,2,".split(',', 2).should == ["1", "2,"]
+ "1,2,".split(',', 3).should == ["1", "2", ""]
+ "1,2,".split(',', 4).should == ["1", "2", ""]
+ "1,あ,".split(',', 2).should == ["1", "あ,"]
+ "1,あ,".split(',', 3).should == ["1", "あ", ""]
+ "1,あ,".split(',', 4).should == ["1", "あ", ""]
+
+ "1 2 ".split(/ /, 2).should == ["1", "2 "]
+ "1 2 ".split(/ /, 3).should == ["1", "2", ""]
+ "1 2 ".split(/ /, 4).should == ["1", "2", ""]
+ "1 あ ".split(/ /, 2).should == ["1", "あ "]
+ "1 あ ".split(/ /, 3).should == ["1", "あ", ""]
+ "1 あ ".split(/ /, 4).should == ["1", "あ", ""]
end
it "returns an array with one entry if limit is 1: the original string" do
@@ -220,6 +246,13 @@ describe "String#split with String" do
it "doesn't split on non-ascii whitespace" do
"a\u{2008}b".split(" ").should == ["a\u{2008}b"]
end
+
+ it "returns Strings in the same encoding as self" do
+ strings = "hello world".encode("US-ASCII").split(" ")
+
+ strings[0].encoding.should == Encoding::US_ASCII
+ strings[1].encoding.should == Encoding::US_ASCII
+ end
end
describe "String#split with Regexp" do
@@ -417,13 +450,12 @@ describe "String#split with Regexp" do
end
end
- it "retains the encoding of the source string" do
+ it "returns Strings in the same encoding as self" do
ary = "а б в".split
encodings = ary.map { |s| s.encoding }
encodings.should == [Encoding::UTF_8, Encoding::UTF_8, Encoding::UTF_8]
end
-
it "splits a string on each character for a multibyte encoding and empty split" do
"That's why efficiency could not be helped".split("").size.should == 39
end
@@ -455,6 +487,14 @@ describe "String#split with Regexp" do
a.should == ["Chunky", "Bacon"]
end
+ it "yields each split substring with default pattern for a lazy substring" do
+ a = []
+ returned_object = "chunky bacon"[1...-1].split { |str| a << str.capitalize }
+
+ returned_object.should == "hunky baco"
+ a.should == ["Hunky", "Baco"]
+ end
+
it "yields each split substring with default pattern for a non-ASCII string" do
a = []
returned_object = "l'été arrive bientôt".split { |str| a << str }
@@ -463,6 +503,14 @@ describe "String#split with Regexp" do
a.should == ["l'été", "arrive", "bientôt"]
end
+ it "yields each split substring with default pattern for a non-ASCII lazy substring" do
+ a = []
+ returned_object = "l'été arrive bientôt"[1...-1].split { |str| a << str }
+
+ returned_object.should == "'été arrive bientô"
+ a.should == ["'été", "arrive", "bientô"]
+ end
+
it "yields the string when limit is 1" do
a = []
returned_object = "chunky bacon".split("", 1) { |str| a << str.capitalize }
@@ -556,4 +604,11 @@ describe "String#split with Regexp" do
-> { "hello".split(false) }.should raise_error(TypeError)
-> { "hello".split(Object.new) }.should raise_error(TypeError)
end
+
+ it "returns Strings in the same encoding as self" do
+ strings = "hello world".encode("US-ASCII").split(/ /)
+
+ strings[0].encoding.should == Encoding::US_ASCII
+ strings[1].encoding.should == Encoding::US_ASCII
+ end
end
diff --git a/spec/ruby/core/string/squeeze_spec.rb b/spec/ruby/core/string/squeeze_spec.rb
index 5dc12a4247..2f3fa65745 100644
--- a/spec/ruby/core/string/squeeze_spec.rb
+++ b/spec/ruby/core/string/squeeze_spec.rb
@@ -64,6 +64,11 @@ describe "String#squeeze" do
"hello room".squeeze(other_string, other_string2).should == "hello rom"
end
+ it "returns a String in the same encoding as self" do
+ "yellow moon".encode("US-ASCII").squeeze.encoding.should == Encoding::US_ASCII
+ "yellow moon".encode("US-ASCII").squeeze("a").encoding.should == Encoding::US_ASCII
+ end
+
it "raises a TypeError when one set arg can't be converted to a string" do
-> { "hello world".squeeze([]) }.should raise_error(TypeError)
-> { "hello world".squeeze(Object.new)}.should raise_error(TypeError)
diff --git a/spec/ruby/core/string/start_with_spec.rb b/spec/ruby/core/string/start_with_spec.rb
index aaed197ff3..3833289f96 100644
--- a/spec/ruby/core/string/start_with_spec.rb
+++ b/spec/ruby/core/string/start_with_spec.rb
@@ -5,4 +5,14 @@ require_relative '../../shared/string/start_with'
describe "String#start_with?" do
it_behaves_like :start_with, :to_s
+
+ # Here and not in the shared examples because this is invalid as a Symbol
+ it "does not check that we are not starting to match at the head of a character" do
+ "\xA9".should.start_with?("\xA9") # A9 is not a character head for UTF-8
+ end
+
+ it "does not check we are matching only part of a character" do
+ "\xe3\x81\x82".size.should == 1
+ "\xe3\x81\x82".should.start_with?("\xe3")
+ end
end
diff --git a/spec/ruby/core/string/strip_spec.rb b/spec/ruby/core/string/strip_spec.rb
index e841db54ce..662f13b032 100644
--- a/spec/ruby/core/string/strip_spec.rb
+++ b/spec/ruby/core/string/strip_spec.rb
@@ -35,6 +35,12 @@ describe "String#strip!" do
a.should == "hello"
end
+ it "makes a string empty if it is only whitespace" do
+ "".strip!.should == nil
+ " ".strip.should == ""
+ " ".strip.should == ""
+ end
+
ruby_version_is '3.0' do
it "removes leading and trailing NULL bytes and whitespace" do
a = "\000 goodbye \000"
diff --git a/spec/ruby/core/string/sub_spec.rb b/spec/ruby/core/string/sub_spec.rb
index 9effe88c27..99dd7b45a8 100644
--- a/spec/ruby/core/string/sub_spec.rb
+++ b/spec/ruby/core/string/sub_spec.rb
@@ -214,6 +214,17 @@ describe "String#sub with pattern, replacement" do
"ababa".sub(/(b)/, '\\\\\1').should == "a\\baba"
end
+ it "handles a pattern in a superset encoding" do
+ result = 'abc'.force_encoding(Encoding::US_ASCII).sub('é', 'è')
+ result.should == 'abc'
+ result.encoding.should == Encoding::US_ASCII
+ end
+
+ it "handles a pattern in a subset encoding" do
+ result = 'été'.sub('t'.force_encoding(Encoding::US_ASCII), 'u')
+ result.should == 'éué'
+ result.encoding.should == Encoding::UTF_8
+ end
end
describe "String#sub with pattern and block" do
@@ -299,6 +310,27 @@ describe "String#sub! with pattern, replacement" do
-> { s.sub!(/e/, "e") }.should raise_error(FrozenError)
-> { s.sub!(/[aeiou]/, '*') }.should raise_error(FrozenError)
end
+
+ it "handles a pattern in a superset encoding" do
+ string = 'abc'.force_encoding(Encoding::US_ASCII)
+
+ result = string.sub!('é', 'è')
+
+ result.should == nil
+ string.should == 'abc'
+ string.encoding.should == Encoding::US_ASCII
+ end
+
+ it "handles a pattern in a subset encoding" do
+ string = 'été'
+ pattern = 't'.force_encoding(Encoding::US_ASCII)
+
+ result = string.sub!(pattern, 'u')
+
+ result.should == string
+ string.should == 'éué'
+ string.encoding.should == Encoding::UTF_8
+ end
end
describe "String#sub! with pattern and block" do
diff --git a/spec/ruby/core/string/swapcase_spec.rb b/spec/ruby/core/string/swapcase_spec.rb
index 417f6c6d8d..d369ab3e4e 100644
--- a/spec/ruby/core/string/swapcase_spec.rb
+++ b/spec/ruby/core/string/swapcase_spec.rb
@@ -9,6 +9,10 @@ describe "String#swapcase" do
"+++---111222???".swapcase.should == "+++---111222???"
end
+ it "returns a String in the same encoding as self" do
+ "Hello".encode("US-ASCII").swapcase.encoding.should == Encoding::US_ASCII
+ end
+
describe "full Unicode case mapping" do
it "works for all of Unicode with no option" do
"äÖü".swapcase.should == "ÄöÜ"
@@ -28,6 +32,10 @@ describe "String#swapcase" do
it "does not swapcase non-ASCII characters" do
"aßet".swapcase(:ascii).should == "AßET"
end
+
+ it "works with substrings" do
+ "prefix aTé"[-3..-1].swapcase(:ascii).should == "Até"
+ end
end
describe "full Unicode case mapping adapted for Turkic languages" do
diff --git a/spec/ruby/core/string/to_c_spec.rb b/spec/ruby/core/string/to_c_spec.rb
index 9c84b14f4d..edc8a4f14f 100644
--- a/spec/ruby/core/string/to_c_spec.rb
+++ b/spec/ruby/core/string/to_c_spec.rb
@@ -1,99 +1,41 @@
require_relative '../../spec_helper'
+require_relative '../../shared/kernel/complex'
+require_relative 'fixtures/to_c'
describe "String#to_c" do
- it "returns a Complex object" do
- '9'.to_c.should be_an_instance_of(Complex)
- end
-
- it "understands integers" do
- '20'.to_c.should == Complex(20)
- end
-
- it "understands negative integers" do
- '-3'.to_c.should == Complex(-3)
- end
-
- it "understands fractions (numerator/denominator) for the real part" do
- '2/3'.to_c.should == Complex(Rational(2, 3))
- end
-
- it "understands fractions (numerator/denominator) for the imaginary part" do
- '4+2/3i'.to_c.should == Complex(4, Rational(2, 3))
- end
-
- it "understands negative fractions (-numerator/denominator) for the real part" do
- '-2/3'.to_c.should == Complex(Rational(-2, 3))
- end
-
- it "understands negative fractions (-numerator/denominator) for the imaginary part" do
- '7-2/3i'.to_c.should == Complex(7, Rational(-2, 3))
- end
-
- it "understands floats (a.b) for the real part" do
- '2.3'.to_c.should == Complex(2.3)
- end
-
- it "understands floats (a.b) for the imaginary part" do
- '4+2.3i'.to_c.should == Complex(4, 2.3)
- end
-
- it "understands negative floats (-a.b) for the real part" do
- '-2.33'.to_c.should == Complex(-2.33)
- end
-
- it "understands negative floats (-a.b) for the imaginary part" do
- '7-28.771i'.to_c.should == Complex(7, -28.771)
- end
-
- it "understands an integer followed by 'i' to mean that integer is the imaginary part" do
- '35i'.to_c.should == Complex(0,35)
- end
-
- it "understands a negative integer followed by 'i' to mean that negative integer is the imaginary part" do
- '-29i'.to_c.should == Complex(0,-29)
- end
-
- it "understands an 'i' by itself as denoting a complex number with an imaginary part of 1" do
- 'i'.to_c.should == Complex(0,1)
- end
-
- it "understands a '-i' by itself as denoting a complex number with an imaginary part of -1" do
- '-i'.to_c.should == Complex(0,-1)
- end
-
- it "understands 'a+bi' to mean a complex number with 'a' as the real part, 'b' as the imaginary" do
- '79+4i'.to_c.should == Complex(79,4)
- end
-
- it "understands 'a-bi' to mean a complex number with 'a' as the real part, '-b' as the imaginary" do
- '79-4i'.to_c.should == Complex(79,-4)
- end
-
- it "understands scientific notation for the real part" do
- '2e3+4i'.to_c.should == Complex(2e3,4)
- end
+ it_behaves_like :kernel_complex, :to_c_method, StringSpecs
+end
- it "understands negative scientific notation for the real part" do
- '-2e3+4i'.to_c.should == Complex(-2e3,4)
+describe "String#to_c" do
+ it "returns a complex number with 0 as the real part, 0 as the imaginary part for unrecognised Strings" do
+ 'ruby'.to_c.should == Complex(0, 0)
end
- it "understands scientific notation for the imaginary part" do
- '4+2e3i'.to_c.should == Complex(4, 2e3)
+ it "ignores trailing garbage" do
+ '79+4iruby'.to_c.should == Complex(79, 4)
+ ruby_bug "[Bug #19087]", ""..."3.2" do
+ '7__9+4__0i'.to_c.should == Complex(7, 0)
+ end
end
- it "understands negative scientific notation for the imaginary part" do
- '4-2e3i'.to_c.should == Complex(4, -2e3)
+ it "understands Float::INFINITY" do
+ 'Infinity'.to_c.should == Complex(0, 1)
+ '-Infinity'.to_c.should == Complex(0, -1)
end
- it "understands scientific notation for the real and imaginary part in the same String" do
- '2e3+2e4i'.to_c.should == Complex(2e3,2e4)
+ it "understands Float::NAN" do
+ 'NaN'.to_c.should == Complex(0, 0)
end
- it "understands negative scientific notation for the real and imaginary part in the same String" do
- '-2e3-2e4i'.to_c.should == Complex(-2e3,-2e4)
+ it "allows null-byte" do
+ "1-2i\0".to_c.should == Complex(1, -2)
+ "1\0-2i".to_c.should == Complex(1, 0)
+ "\01-2i".to_c.should == Complex(0, 0)
end
- it "returns a complex number with 0 as the real part, 0 as the imaginary part for unrecognised Strings" do
- 'ruby'.to_c.should == Complex(0,0)
+ it "raises Encoding::CompatibilityError if String is in not ASCII-compatible encoding" do
+ -> {
+ '79+4i'.encode("UTF-16").to_c
+ }.should raise_error(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16")
end
end
diff --git a/spec/ruby/core/string/undump_spec.rb b/spec/ruby/core/string/undump_spec.rb
index 08058d9bd1..6ff220161c 100644
--- a/spec/ruby/core/string/undump_spec.rb
+++ b/spec/ruby/core/string/undump_spec.rb
@@ -389,7 +389,7 @@ describe "String#undump" do
'"\\bv".force_encoding("UTF-16BE")'.undump.should == "\u0876".encode('utf-16be')
end
- it "keeps origin encoding" do
+ it "returns a String in the same encoding as self" do
'"foo"'.encode("ISO-8859-1").undump.encoding.should == Encoding::ISO_8859_1
'"foo"'.encode('windows-1251').undump.encoding.should == Encoding::Windows_1251
end
diff --git a/spec/ruby/core/string/unpack/b_spec.rb b/spec/ruby/core/string/unpack/b_spec.rb
index 1a838d6c7c..2cf5ebad34 100644
--- a/spec/ruby/core/string/unpack/b_spec.rb
+++ b/spec/ruby/core/string/unpack/b_spec.rb
@@ -86,13 +86,28 @@ describe "String#unpack with format 'B'" do
].should be_computed_by(:unpack, "BBB")
end
- it "ignores NULL bytes between directives" do
- "\x80\x00".unpack("B\x00B").should == ["1", "0"]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "\x80\x00".unpack("B\x00B").should == ["1", "0"]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "\x80\x00".unpack("B\x00B")
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
"\x80\x00".unpack("B B").should == ["1", "0"]
end
+
+ it "decodes into US-ASCII string values" do
+ str = "s".force_encoding('UTF-8').unpack("B*")[0]
+ str.encoding.name.should == 'US-ASCII'
+ end
end
describe "String#unpack with format 'b'" do
@@ -177,8 +192,18 @@ describe "String#unpack with format 'b'" do
].should be_computed_by(:unpack, "bbb")
end
- it "ignores NULL bytes between directives" do
- "\x01\x00".unpack("b\x00b").should == ["1", "0"]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "\x01\x00".unpack("b\x00b").should == ["1", "0"]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "\x01\x00".unpack("b\x00b")
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -189,5 +214,4 @@ describe "String#unpack with format 'b'" do
str = "s".force_encoding('UTF-8').unpack("b*")[0]
str.encoding.name.should == 'US-ASCII'
end
-
end
diff --git a/spec/ruby/core/string/unpack/c_spec.rb b/spec/ruby/core/string/unpack/c_spec.rb
index ed8caa4895..dbcbacc74d 100644
--- a/spec/ruby/core/string/unpack/c_spec.rb
+++ b/spec/ruby/core/string/unpack/c_spec.rb
@@ -35,8 +35,18 @@ describe :string_unpack_8bit, shared: true do
].should be_computed_by(:unpack, unpack_format(3))
end
- it "ignores NULL bytes between directives" do
- "abc".unpack(unpack_format("\000", 2)).should == [97, 98]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "abc".unpack(unpack_format("\000", 2)).should == [97, 98]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "abc".unpack(unpack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/string/unpack/h_spec.rb b/spec/ruby/core/string/unpack/h_spec.rb
index f2f5dcf396..ee08d20926 100644
--- a/spec/ruby/core/string/unpack/h_spec.rb
+++ b/spec/ruby/core/string/unpack/h_spec.rb
@@ -56,8 +56,18 @@ describe "String#unpack with format 'H'" do
].should be_computed_by(:unpack, "HHH")
end
- it "ignores NULL bytes between directives" do
- "\x01\x10".unpack("H\x00H").should == ["0", "1"]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "\x01\x10".unpack("H\x00H").should == ["0", "1"]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "\x01\x10".unpack("H\x00H")
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -121,8 +131,18 @@ describe "String#unpack with format 'h'" do
].should be_computed_by(:unpack, "hhh")
end
- it "ignores NULL bytes between directives" do
- "\x01\x10".unpack("h\x00h").should == ["1", "0"]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "\x01\x10".unpack("h\x00h").should == ["1", "0"]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "\x01\x10".unpack("h\x00h")
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/string/unpack/m_spec.rb b/spec/ruby/core/string/unpack/m_spec.rb
index 21134514a1..c551c755d1 100644
--- a/spec/ruby/core/string/unpack/m_spec.rb
+++ b/spec/ruby/core/string/unpack/m_spec.rb
@@ -97,6 +97,11 @@ describe "String#unpack with format 'M'" do
["=FF=\n", ["\xff"]]
].should be_computed_by(:unpack, "M")
end
+
+ it "unpacks incomplete escape sequences as literal characters" do
+ "foo=".unpack("M").should == ["foo="]
+ "foo=4".unpack("M").should == ["foo=4"]
+ end
end
describe "String#unpack with format 'm'" do
diff --git a/spec/ruby/core/string/unpack/shared/basic.rb b/spec/ruby/core/string/unpack/shared/basic.rb
index f636f4689f..bb5302edc5 100644
--- a/spec/ruby/core/string/unpack/shared/basic.rb
+++ b/spec/ruby/core/string/unpack/shared/basic.rb
@@ -8,20 +8,6 @@ describe :string_unpack_basic, shared: true do
d.should_receive(:to_str).and_return("a"+unpack_format)
"abc".unpack(d).should be_an_instance_of(Array)
end
-
- it "raises a TypeError when passed nil" do
- -> { "abc".unpack(nil) }.should raise_error(TypeError)
- end
-
- it "raises a TypeError when passed an Integer" do
- -> { "abc".unpack(1) }.should raise_error(TypeError)
- end
-
- ruby_version_is "3.1" do
- it "starts unpacking from the given offset" do
- "abc".unpack("CC", offset: 1).should == [98, 99]
- end
- end
end
describe :string_unpack_no_platform, shared: true do
@@ -32,18 +18,4 @@ describe :string_unpack_no_platform, shared: true do
it "raises an ArgumentError when the format modifier is '!'" do
-> { "abcdefgh".unpack(unpack_format("!")) }.should raise_error(ArgumentError)
end
-
- ruby_version_is "3.1" do
- it "raises an ArgumentError when the offset is negative" do
- -> { "a".unpack("C", offset: -1) }.should raise_error(ArgumentError)
- end
-
- it "returns nil if the offset is at the end of the string" do
- "a".unpack("C", offset: 1).should == [nil]
- end
-
- it "raises an ArgumentError when the offset is larget than the string" do
- -> { "a".unpack("C", offset: 2) }.should raise_error(ArgumentError)
- end
- end
end
diff --git a/spec/ruby/core/string/unpack/shared/float.rb b/spec/ruby/core/string/unpack/shared/float.rb
index 99bd8a3401..ccddf94f99 100644
--- a/spec/ruby/core/string/unpack/shared/float.rb
+++ b/spec/ruby/core/string/unpack/shared/float.rb
@@ -56,9 +56,19 @@ describe :string_unpack_float_le, shared: true do
[nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true
end
- it "ignores NULL bytes between directives" do
- array = "\x9a\x999@33\xb3?".unpack(unpack_format("\000", 2))
- array.should == [2.9000000953674316, 1.399999976158142]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ array = "\x9a\x999@33\xb3?".unpack(unpack_format("\000", 2))
+ array.should == [2.9000000953674316, 1.399999976158142]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "\x9a\x999@33\xb3?".unpack(unpack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -123,9 +133,19 @@ describe :string_unpack_float_be, shared: true do
[nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true
end
- it "ignores NULL bytes between directives" do
- array = "@9\x99\x9a?\xb333".unpack(unpack_format("\000", 2))
- array.should == [2.9000000953674316, 1.399999976158142]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ array = "@9\x99\x9a?\xb333".unpack(unpack_format("\000", 2))
+ array.should == [2.9000000953674316, 1.399999976158142]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "@9\x99\x9a?\xb333".unpack(unpack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -193,8 +213,18 @@ describe :string_unpack_double_le, shared: true do
[nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true
end
- it "ignores NULL bytes between directives" do
- "333333\x07@ffffff\xf6?".unpack(unpack_format("\000", 2)).should == [2.9, 1.4]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "333333\x07@ffffff\xf6?".unpack(unpack_format("\000", 2)).should == [2.9, 1.4]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "333333\x07@ffffff\xf6?".unpack(unpack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -261,8 +291,18 @@ describe :string_unpack_double_be, shared: true do
[nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true
end
- it "ignores NULL bytes between directives" do
- "@\x07333333?\xf6ffffff".unpack(unpack_format("\000", 2)).should == [2.9, 1.4]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "@\x07333333?\xf6ffffff".unpack(unpack_format("\000", 2)).should == [2.9, 1.4]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "@\x07333333?\xf6ffffff".unpack(unpack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/string/unpack/shared/integer.rb b/spec/ruby/core/string/unpack/shared/integer.rb
index cbaa743683..ba4f149dad 100644
--- a/spec/ruby/core/string/unpack/shared/integer.rb
+++ b/spec/ruby/core/string/unpack/shared/integer.rb
@@ -32,8 +32,18 @@ describe :string_unpack_16bit_le, shared: true do
].should be_computed_by(:unpack, unpack_format(3))
end
- it "ignores NULL bytes between directives" do
- "abcd".unpack(unpack_format("\000", 2)).should == [25185, 25699]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "abcd".unpack(unpack_format("\000", 2)).should == [25185, 25699]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "abcd".unpack(unpack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -85,8 +95,18 @@ describe :string_unpack_16bit_be, shared: true do
].should be_computed_by(:unpack, unpack_format(3))
end
- it "ignores NULL bytes between directives" do
- "badc".unpack(unpack_format("\000", 2)).should == [25185, 25699]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "badc".unpack(unpack_format("\000", 2)).should == [25185, 25699]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "badc".unpack(unpack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -139,8 +159,18 @@ describe :string_unpack_32bit_le, shared: true do
].should be_computed_by(:unpack, unpack_format(3))
end
- it "ignores NULL bytes between directives" do
- "abcdefgh".unpack(unpack_format("\000", 2)).should == [1684234849, 1751606885]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "abcdefgh".unpack(unpack_format("\000", 2)).should == [1684234849, 1751606885]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "abcdefgh".unpack(unpack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -193,8 +223,18 @@ describe :string_unpack_32bit_be, shared: true do
].should be_computed_by(:unpack, unpack_format(3))
end
- it "ignores NULL bytes between directives" do
- "dcbahgfe".unpack(unpack_format("\000", 2)).should == [1684234849, 1751606885]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "dcbahgfe".unpack(unpack_format("\000", 2)).should == [1684234849, 1751606885]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "dcbahgfe".unpack(unpack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -243,9 +283,19 @@ describe :string_unpack_64bit_le, shared: true do
"abc".unpack(unpack_format('*')).should == []
end
- it "ignores NULL bytes between directives" do
- array = "abcdefghabghefcd".unpack(unpack_format("\000", 2))
- array.should == [7523094288207667809, 7233738012216484449]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ array = "abcdefghabghefcd".unpack(unpack_format("\000", 2))
+ array.should == [7523094288207667809, 7233738012216484449]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "badc".unpack(unpack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -305,9 +355,19 @@ describe :string_unpack_64bit_be, shared: true do
"abc".unpack(unpack_format('*')).should == []
end
- it "ignores NULL bytes between directives" do
- array = "hgfedcbadcfehgba".unpack(unpack_format("\000", 2))
- array.should == [7523094288207667809, 7233738012216484449]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ array = "hgfedcbadcfehgba".unpack(unpack_format("\000", 2))
+ array.should == [7523094288207667809, 7233738012216484449]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "hgfedcbadcfehgba".unpack(unpack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/string/unpack/shared/unicode.rb b/spec/ruby/core/string/unpack/shared/unicode.rb
index a2b4e142b2..ce1f29fe87 100644
--- a/spec/ruby/core/string/unpack/shared/unicode.rb
+++ b/spec/ruby/core/string/unpack/shared/unicode.rb
@@ -50,8 +50,18 @@ describe :string_unpack_unicode, shared: true do
"\xc2\x80".unpack("UUUU").should == [0x80]
end
- it "ignores NULL bytes between directives" do
- "\x01\x02".unpack("U\x00U").should == [1, 2]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "\x01\x02".unpack("U\x00U").should == [1, 2]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "\x01\x02".unpack("U\x00U")
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/string/unpack/w_spec.rb b/spec/ruby/core/string/unpack/w_spec.rb
index 011c75f5c4..b213b32921 100644
--- a/spec/ruby/core/string/unpack/w_spec.rb
+++ b/spec/ruby/core/string/unpack/w_spec.rb
@@ -15,8 +15,18 @@ describe "String#unpack with directive 'w'" do
].should be_computed_by(:unpack, "w")
end
- it "ignores NULL bytes between directives" do
- "\x01\x02\x03".unpack("w\x00w").should == [1, 2]
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ "\x01\x02\x03".unpack("w\x00w").should == [1, 2]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ "\x01\x02\x03".unpack("w\x00w")
+ }.should raise_error(ArgumentError, /unknown unpack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/string/unpack1_spec.rb b/spec/ruby/core/string/unpack1_spec.rb
index f59bd92d6a..df830916a3 100644
--- a/spec/ruby/core/string/unpack1_spec.rb
+++ b/spec/ruby/core/string/unpack1_spec.rb
@@ -15,16 +15,22 @@ describe "String#unpack1" do
"ZA".unpack1("B*", offset: 1).should == "01000001"
end
+ it "traits offset as a bytes offset" do
+ "؈".unpack("CC").should == [216, 136]
+ "؈".unpack1("C").should == 216
+ "؈".unpack1("C", offset: 1).should == 136
+ end
+
it "raises an ArgumentError when the offset is negative" do
- -> { "a".unpack1("C", offset: -1) }.should raise_error(ArgumentError)
+ -> { "a".unpack1("C", offset: -1) }.should raise_error(ArgumentError, "offset can't be negative")
end
it "returns nil if the offset is at the end of the string" do
"a".unpack1("C", offset: 1).should == nil
end
- it "raises an ArgumentError when the offset is larget than the string" do
- -> { "a".unpack1("C", offset: 2) }.should raise_error(ArgumentError)
+ it "raises an ArgumentError when the offset is larger than the string bytesize" do
+ -> { "a".unpack1("C", offset: 2) }.should raise_error(ArgumentError, "offset outside of string")
end
end
end
diff --git a/spec/ruby/core/string/unpack_spec.rb b/spec/ruby/core/string/unpack_spec.rb
new file mode 100644
index 0000000000..4ff7d07460
--- /dev/null
+++ b/spec/ruby/core/string/unpack_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+
+describe "String#unpack" do
+ it "raises a TypeError when passed nil" do
+ -> { "abc".unpack(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed an Integer" do
+ -> { "abc".unpack(1) }.should raise_error(TypeError)
+ end
+
+ ruby_version_is "3.1" do
+ it "starts unpacking from the given offset" do
+ "abc".unpack("CC", offset: 1).should == [98, 99]
+ end
+
+ it "traits offset as a bytes offset" do
+ "؈".unpack("CC").should == [216, 136]
+ "؈".unpack("CC", offset: 1).should == [136, nil]
+ end
+
+ it "raises an ArgumentError when the offset is negative" do
+ -> { "a".unpack("C", offset: -1) }.should raise_error(ArgumentError, "offset can't be negative")
+ end
+
+ it "returns nil if the offset is at the end of the string" do
+ "a".unpack("C", offset: 1).should == [nil]
+ end
+
+ it "raises an ArgumentError when the offset is larget than the string" do
+ -> { "a".unpack("C", offset: 2) }.should raise_error(ArgumentError, "offset outside of string")
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/ruby/core/string/upcase_spec.rb b/spec/ruby/core/string/upcase_spec.rb
index b2b34190fe..5ce7b0b95f 100644
--- a/spec/ruby/core/string/upcase_spec.rb
+++ b/spec/ruby/core/string/upcase_spec.rb
@@ -8,6 +8,10 @@ describe "String#upcase" do
"hello".upcase.should == "HELLO"
end
+ it "returns a String in the same encoding as self" do
+ "hello".encode("US-ASCII").upcase.encoding.should == Encoding::US_ASCII
+ end
+
describe "full Unicode case mapping" do
it "works for all of Unicode with no option" do
"äöü".upcase.should == "ÄÖÜ"
@@ -27,6 +31,10 @@ describe "String#upcase" do
it "does not upcase non-ASCII characters" do
"aßet".upcase(:ascii).should == "AßET"
end
+
+ it "works with substrings" do
+ "prefix té"[-2..-1].upcase(:ascii).should == "Té"
+ end
end
describe "full Unicode case mapping adapted for Turkic languages" do
diff --git a/spec/ruby/core/string/valid_encoding/utf_8_spec.rb b/spec/ruby/core/string/valid_encoding/utf_8_spec.rb
new file mode 100644
index 0000000000..a14c3af830
--- /dev/null
+++ b/spec/ruby/core/string/valid_encoding/utf_8_spec.rb
@@ -0,0 +1,214 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../../spec_helper'
+
+describe "String#valid_encoding? and UTF-8" do
+ def utf8(bytes)
+ bytes.pack("C*").force_encoding("UTF-8")
+ end
+
+ describe "1-byte character" do
+ it "is valid if is in format 0xxxxxxx" do
+ utf8([0b00000000]).valid_encoding?.should == true
+ utf8([0b01111111]).valid_encoding?.should == true
+ end
+
+ it "is not valid if is not in format 0xxxxxxx" do
+ utf8([0b10000000]).valid_encoding?.should == false
+ utf8([0b11111111]).valid_encoding?.should == false
+ end
+ end
+
+ describe "2-bytes character" do
+ it "is valid if in format [110xxxxx 10xxxxx]" do
+ utf8([0b11000010, 0b10000000]).valid_encoding?.should == true
+ utf8([0b11000010, 0b10111111]).valid_encoding?.should == true
+
+ utf8([0b11011111, 0b10000000]).valid_encoding?.should == true
+ utf8([0b11011111, 0b10111111]).valid_encoding?.should == true
+ end
+
+ it "is not valid if the first byte is not in format 110xxxxx" do
+ utf8([0b00000010, 0b10000000]).valid_encoding?.should == false
+ utf8([0b00100010, 0b10000000]).valid_encoding?.should == false
+ utf8([0b01000010, 0b10000000]).valid_encoding?.should == false
+ utf8([0b01100010, 0b10000000]).valid_encoding?.should == false
+ utf8([0b10000010, 0b10000000]).valid_encoding?.should == false
+ utf8([0b10100010, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11000010, 0b10000000]).valid_encoding?.should == true # correct bytes
+ utf8([0b11100010, 0b10000000]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the second byte is not in format 10xxxxxx" do
+ utf8([0b11000010, 0b00000000]).valid_encoding?.should == false
+ utf8([0b11000010, 0b01000000]).valid_encoding?.should == false
+ utf8([0b11000010, 0b11000000]).valid_encoding?.should == false
+ end
+
+ it "is not valid if is smaller than [xxxxxx10 xx000000] (codepoints < U+007F, that are encoded with the 1-byte format)" do
+ utf8([0b11000000, 0b10111111]).valid_encoding?.should == false
+ utf8([0b11000001, 0b10111111]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the first byte is missing" do
+ bytes = [0b11000010, 0b10000000]
+ utf8(bytes[1..1]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the second byte is missing" do
+ bytes = [0b11000010, 0b10000000]
+ utf8(bytes[0..0]).valid_encoding?.should == false
+ end
+ end
+
+ describe "3-bytes character" do
+ it "is valid if in format [1110xxxx 10xxxxxx 10xxxxxx]" do
+ utf8([0b11100000, 0b10100000, 0b10000000]).valid_encoding?.should == true
+ utf8([0b11100000, 0b10100000, 0b10111111]).valid_encoding?.should == true
+ utf8([0b11100000, 0b10111111, 0b10111111]).valid_encoding?.should == true
+ utf8([0b11101111, 0b10111111, 0b10111111]).valid_encoding?.should == true
+ end
+
+ it "is not valid if the first byte is not in format 1110xxxx" do
+ utf8([0b00000000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b00010000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b00100000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b00110000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b01000000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b01010000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b01100000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b01110000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b10000000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b10010000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b10100000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b10110000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11000000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11010000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11100000, 0b10100000, 0b10000000]).valid_encoding?.should == true # correct bytes
+ utf8([0b11110000, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the second byte is not in format 10xxxxxx" do
+ utf8([0b11100000, 0b00100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11100000, 0b01100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11100000, 0b11100000, 0b10000000]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the third byte is not in format 10xxxxxx" do
+ utf8([0b11100000, 0b10100000, 0b00000000]).valid_encoding?.should == false
+ utf8([0b11100000, 0b10100000, 0b01000000]).valid_encoding?.should == false
+ utf8([0b11100000, 0b10100000, 0b01000000]).valid_encoding?.should == false
+ end
+
+ it "is not valid if is smaller than [xxxx0000 xx100000 xx000000] (codepoints < U+07FF that are encoded with the 2-byte format)" do
+ utf8([0b11100000, 0b10010000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11100000, 0b10001000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11100000, 0b10000100, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11100000, 0b10000010, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11100000, 0b10000001, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11100000, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ end
+
+ it "is not valid if in range [xxxx1101 xx100000 xx000000] - [xxxx1101 xx111111 xx111111] (codepoints U+D800 - U+DFFF)" do
+ utf8([0b11101101, 0b10100000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11101101, 0b10100000, 0b10000001]).valid_encoding?.should == false
+ utf8([0b11101101, 0b10111111, 0b10111111]).valid_encoding?.should == false
+
+ utf8([0b11101101, 0b10011111, 0b10111111]).valid_encoding?.should == true # lower boundary - 1
+ utf8([0b11101110, 0b10000000, 0b10000000]).valid_encoding?.should == true # upper boundary + 1
+ end
+
+ it "is not valid if the first byte is missing" do
+ bytes = [0b11100000, 0b10100000, 0b10000000]
+ utf8(bytes[2..3]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the second byte is missing" do
+ bytes = [0b11100000, 0b10100000, 0b10000000]
+ utf8([bytes[0], bytes[2]]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the second and the third bytes are missing" do
+ bytes = [0b11100000, 0b10100000, 0b10000000]
+ utf8(bytes[0..0]).valid_encoding?.should == false
+ end
+ end
+
+ describe "4-bytes character" do
+ it "is valid if in format [11110xxx 10xxxxxx 10xxxxxx 10xxxxxx]" do
+ utf8([0b11110000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == true
+ utf8([0b11110000, 0b10010000, 0b10000000, 0b10111111]).valid_encoding?.should == true
+ utf8([0b11110000, 0b10010000, 0b10111111, 0b10111111]).valid_encoding?.should == true
+ utf8([0b11110000, 0b10111111, 0b10111111, 0b10111111]).valid_encoding?.should == true
+ utf8([0b11110100, 0b10001111, 0b10111111, 0b10111111]).valid_encoding?.should == true
+ end
+
+ it "is not valid if the first byte is not in format 11110xxx" do
+ utf8([0b11100000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11010000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b10110000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b01110000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the second byte is not in format 10xxxxxx" do
+ utf8([0b11110000, 0b00010000, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b01010000, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == true # correct bytes
+ utf8([0b11110000, 0b11010000, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the third byte is not in format 10xxxxxx" do
+ utf8([0b11110000, 0b10010000, 0b00000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b10010000, 0b01000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == true # correct bytes
+ utf8([0b11110000, 0b10010000, 0b11000000, 0b10000000]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the forth byte is not in format 10xxxxxx" do
+ utf8([0b11110000, 0b10010000, 0b10000000, 0b00000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b10010000, 0b10000000, 0b01000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == true # correct bytes
+ utf8([0b11110000, 0b10010000, 0b10000000, 0b11000000]).valid_encoding?.should == false
+ end
+
+ it "is not valid if is smaller than [xxxxx000 xx001000 xx000000 xx000000] (codepoint < U+10000)" do
+ utf8([0b11110000, 0b10000111, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b10000110, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b10000101, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b10000100, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b10000011, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b10000010, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b10000001, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110000, 0b10000000, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ end
+
+ it "is not valid if is greater than [xxxxx100 xx001111 xx111111 xx111111] (codepoint > U+10FFFF)" do
+ utf8([0b11110100, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110100, 0b10100000, 0b10000000, 0b10000000]).valid_encoding?.should == false
+ utf8([0b11110100, 0b10110000, 0b10000000, 0b10000000]).valid_encoding?.should == false
+
+ utf8([0b11110101, 0b10001111, 0b10111111, 0b10111111]).valid_encoding?.should == false
+ utf8([0b11110110, 0b10001111, 0b10111111, 0b10111111]).valid_encoding?.should == false
+ utf8([0b11110111, 0b10001111, 0b10111111, 0b10111111]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the first byte is missing" do
+ bytes = [0b11110000, 0b10010000, 0b10000000, 0b10000000]
+ utf8(bytes[1..3]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the second byte is missing" do
+ bytes = [0b11110000, 0b10010000, 0b10000000, 0b10000000]
+ utf8([bytes[0], bytes[2], bytes[3]]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the second and the third bytes are missing" do
+ bytes = [0b11110000, 0b10010000, 0b10000000, 0b10000000]
+ utf8([bytes[0], bytes[3]]).valid_encoding?.should == false
+ end
+
+ it "is not valid if the second, the third and the fourth bytes are missing" do
+ bytes = [0b11110000, 0b10010000, 0b10000000, 0b10000000]
+ utf8(bytes[0..0]).valid_encoding?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/struct/initialize_spec.rb b/spec/ruby/core/struct/initialize_spec.rb
index e82289071a..cfb302209e 100644
--- a/spec/ruby/core/struct/initialize_spec.rb
+++ b/spec/ruby/core/struct/initialize_spec.rb
@@ -40,4 +40,12 @@ describe "Struct#initialize" do
it "can be overridden" do
StructClasses::SubclassX.new(:y).new.key.should == :value
end
+
+ ruby_version_is "3.1"..."3.2" do
+ it "warns about passing only keyword arguments" do
+ -> {
+ StructClasses::Ruby.new(version: "3.1", platform: "OS")
+ }.should complain(/warning: Passing only keyword arguments/)
+ end
+ end
end
diff --git a/spec/ruby/core/struct/keyword_init_spec.rb b/spec/ruby/core/struct/keyword_init_spec.rb
new file mode 100644
index 0000000000..061f4c56e0
--- /dev/null
+++ b/spec/ruby/core/struct/keyword_init_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.1" do
+ # See https://bugs.ruby-lang.org/issues/18008
+ describe "StructClass#keyword_init?" do
+ it "returns true for a struct that accepts keyword arguments to initialize" do
+ struct = Struct.new(:arg, keyword_init: true)
+ struct.keyword_init?.should be_true
+ end
+
+ it "returns false for a struct that does not accept keyword arguments to initialize" do
+ struct = Struct.new(:arg, keyword_init: false)
+ struct.keyword_init?.should be_false
+ end
+
+ it "returns nil for a struct that did not explicitly specify keyword_init" do
+ struct = Struct.new(:arg)
+ struct.keyword_init?.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/struct/values_at_spec.rb b/spec/ruby/core/struct/values_at_spec.rb
index e7d287cba2..5e5a496600 100644
--- a/spec/ruby/core/struct/values_at_spec.rb
+++ b/spec/ruby/core/struct/values_at_spec.rb
@@ -1,16 +1,59 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+# Should be synchronized with core/array/values_at_spec.rb
describe "Struct#values_at" do
- it "returns an array of values" do
+ before do
clazz = Struct.new(:name, :director, :year)
- movie = clazz.new('Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002)
- movie.values_at(0, 1).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park']
- movie.values_at(0..2).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002]
+ @movie = clazz.new('Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002)
+ end
+
+ context "when passed a list of Integers" do
+ it "returns an array containing each value given by one of integers" do
+ @movie.values_at(0, 1).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park']
+ end
+
+ it "raises IndexError if any of integers is out of range" do
+ -> { @movie.values_at(3) }.should raise_error(IndexError, "offset 3 too large for struct(size:3)")
+ -> { @movie.values_at(-4) }.should raise_error(IndexError, "offset -4 too small for struct(size:3)")
+ end
+ end
+
+ context "when passed an integer Range" do
+ it "returns an array containing each value given by the elements of the range" do
+ @movie.values_at(0..2).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002]
+ end
+
+ it "fills with nil values for range elements larger than the structure" do
+ @movie.values_at(0..3).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002, nil]
+ end
+
+ it "raises RangeError if any element of the range is negative and out of range" do
+ -> { @movie.values_at(-4..3) }.should raise_error(RangeError, "-4..3 out of range")
+ end
+
+ it "supports endless Range" do
+ @movie.values_at(0..).should == ["Sympathy for Mr. Vengeance", "Chan-wook Park", 2002]
+ end
+
+ it "supports beginningless Range" do
+ @movie.values_at(..2).should == ["Sympathy for Mr. Vengeance", "Chan-wook Park", 2002]
+ end
+ end
+
+ it "supports multiple integer Ranges" do
+ @movie.values_at(0..2, 1..2).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002, 'Chan-wook Park', 2002]
+ end
+
+ it "supports mixing integer Ranges and Integers" do
+ @movie.values_at(0..2, 2).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002, 2002]
+ end
+
+ it "returns a new empty Array if no arguments given" do
+ @movie.values_at().should == []
end
it "fails when passed unsupported types" do
- car = StructClasses::Car.new('Ford', 'Ranger')
- -> { car.values_at('make') }.should raise_error(TypeError)
+ -> { @movie.values_at('make') }.should raise_error(TypeError, "no implicit conversion of String into Integer")
end
end
diff --git a/spec/ruby/core/symbol/shared/id2name.rb b/spec/ruby/core/symbol/shared/id2name.rb
index 47f97bd332..d012b7634e 100644
--- a/spec/ruby/core/symbol/shared/id2name.rb
+++ b/spec/ruby/core/symbol/shared/id2name.rb
@@ -6,4 +6,11 @@ describe :symbol_id2name, shared: true do
:@ruby.send(@method).should == "@ruby"
:@@ruby.send(@method).should == "@@ruby"
end
+
+ it "returns a String in the same encoding as self" do
+ string = "ruby".encode("US-ASCII")
+ symbol = string.to_sym
+
+ symbol.send(@method).encoding.should == Encoding::US_ASCII
+ end
end
diff --git a/spec/ruby/core/symbol/to_proc_spec.rb b/spec/ruby/core/symbol/to_proc_spec.rb
index 47f2a939ab..6d9c4bc622 100644
--- a/spec/ruby/core/symbol/to_proc_spec.rb
+++ b/spec/ruby/core/symbol/to_proc_spec.rb
@@ -46,6 +46,33 @@ describe "Symbol#to_proc" do
end
end
+ ruby_version_is "3.2" do
+ it "only calls public methods" do
+ body = proc do
+ public def pub; @a << :pub end
+ protected def pro; @a << :pro end
+ private def pri; @a << :pri end
+ attr_reader :a
+ end
+
+ @a = []
+ singleton_class.class_eval(&body)
+ tap(&:pub)
+ proc{tap(&:pro)}.should raise_error(NoMethodError, /protected method `pro' called/)
+ proc{tap(&:pri)}.should raise_error(NoMethodError, /private method `pri' called/)
+ @a.should == [:pub]
+
+ @a = []
+ c = Class.new(&body)
+ o = c.new
+ o.instance_variable_set(:@a, [])
+ o.tap(&:pub)
+ proc{tap(&:pro)}.should raise_error(NoMethodError, /protected method `pro' called/)
+ proc{o.tap(&:pri)}.should raise_error(NoMethodError, /private method `pri' called/)
+ o.a.should == [:pub]
+ end
+ end
+
it "raises an ArgumentError when calling #call on the Proc without receiver" do
-> {
:object_id.to_proc.call
diff --git a/spec/ruby/core/thread/backtrace/limit_spec.rb b/spec/ruby/core/thread/backtrace/limit_spec.rb
new file mode 100644
index 0000000000..26a87a806c
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/limit_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is "3.1" do
+ describe "Thread::Backtrace.limit" do
+ it "returns maximum backtrace length set by --backtrace-limit command-line option" do
+ out = ruby_exe("print Thread::Backtrace.limit", options: "--backtrace-limit=2")
+ out.should == "2"
+ end
+
+ it "returns -1 when --backtrace-limit command-line option is not set" do
+ out = ruby_exe("print Thread::Backtrace.limit")
+ out.should == "-1"
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
index 4136f09348..e35e1fc0b4 100644
--- a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
+++ b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
@@ -17,6 +17,15 @@ describe 'Thread::Backtrace::Location#absolute_path' do
end
end
+ it 'returns the correct absolute path when using a relative main script path and changing CWD' do
+ script = fixture(__FILE__, 'subdir/absolute_path_main_chdir.rb')
+ sibling = fixture(__FILE__, 'subdir/sibling.rb')
+ subdir = File.dirname script
+ Dir.chdir(fixture(__FILE__)) do
+ ruby_exe('subdir/absolute_path_main_chdir.rb').should == "subdir/absolute_path_main_chdir.rb\n#{subdir}\n#{subdir}\n#{script}\n#{sibling}\n"
+ end
+ end
+
context "when used in eval with a given filename" do
code = "caller_locations(0)[0].absolute_path"
diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/subdir/absolute_path_main_chdir.rb b/spec/ruby/core/thread/backtrace/location/fixtures/subdir/absolute_path_main_chdir.rb
new file mode 100644
index 0000000000..33c8fb36ef
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/fixtures/subdir/absolute_path_main_chdir.rb
@@ -0,0 +1,11 @@
+puts __FILE__
+puts __dir__
+Dir.chdir __dir__
+
+# Check __dir__ is still correct after chdir
+puts __dir__
+
+puts caller_locations(0)[0].absolute_path
+
+# require_relative also needs to know the absolute path of the current file so we test it here too
+require_relative 'sibling'
diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/subdir/sibling.rb b/spec/ruby/core/thread/backtrace/location/fixtures/subdir/sibling.rb
new file mode 100644
index 0000000000..2a854ddccd
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/fixtures/subdir/sibling.rb
@@ -0,0 +1 @@
+puts __FILE__
diff --git a/spec/ruby/core/thread/native_thread_id_spec.rb b/spec/ruby/core/thread/native_thread_id_spec.rb
new file mode 100644
index 0000000000..d6cc332bf6
--- /dev/null
+++ b/spec/ruby/core/thread/native_thread_id_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+if ruby_version_is "3.1" and Thread.method_defined?(:native_thread_id)
+ # This method is very platform specific
+
+ describe "Thread#native_thread_id" do
+ it "returns an integer when the thread is alive" do
+ Thread.current.native_thread_id.should be_kind_of(Integer)
+ end
+
+ it "returns nil when the thread is not running" do
+ t = Thread.new {}
+ t.join
+ t.native_thread_id.should == nil
+ end
+ end
+end
diff --git a/spec/ruby/core/time/at_spec.rb b/spec/ruby/core/time/at_spec.rb
index 2cc46ab8c9..0459589f01 100644
--- a/spec/ruby/core/time/at_spec.rb
+++ b/spec/ruby/core/time/at_spec.rb
@@ -251,6 +251,22 @@ describe "Time.at" do
time.to_i.should == @epoch_time
end
+ it "could be UTC offset as a 'UTC' String" do
+ time = Time.at(@epoch_time, in: "UTC")
+
+ time.utc_offset.should == 0
+ time.zone.should == "UTC"
+ time.to_i.should == @epoch_time
+ end
+
+ it "could be UTC offset as a military zone A-Z" do
+ time = Time.at(@epoch_time, in: "B")
+
+ time.utc_offset.should == 3600 * 2
+ time.zone.should == nil
+ time.to_i.should == @epoch_time
+ end
+
it "could be a timezone object" do
zone = TimeSpecs::TimezoneWithName.new(name: "Asia/Colombo")
time = Time.at(@epoch_time, in: zone)
@@ -266,5 +282,10 @@ describe "Time.at" do
time.zone.should == zone
time.to_i.should == @epoch_time
end
+
+ it "raises ArgumentError if format is invalid" do
+ -> { Time.at(@epoch_time, in: "+09:99") }.should raise_error(ArgumentError)
+ -> { Time.at(@epoch_time, in: "ABC") }.should raise_error(ArgumentError)
+ end
end
end
diff --git a/spec/ruby/core/time/localtime_spec.rb b/spec/ruby/core/time/localtime_spec.rb
index 2975e112d0..609b6532a1 100644
--- a/spec/ruby/core/time/localtime_spec.rb
+++ b/spec/ruby/core/time/localtime_spec.rb
@@ -29,10 +29,10 @@ describe "Time#localtime" do
time.localtime.should equal(time)
end
- it "raises a RuntimeError if the time has a different time zone" do
+ it "raises a FrozenError if the time has a different time zone" do
time = Time.gm(2007, 1, 9, 12, 0, 0)
time.freeze
- -> { time.localtime }.should raise_error(RuntimeError)
+ -> { time.localtime }.should raise_error(FrozenError)
end
end
@@ -79,6 +79,18 @@ describe "Time#localtime" do
t.utc_offset.should == -3600
end
+ it "returns a Time with a UTC offset specified as UTC" do
+ t = Time.new(2007, 1, 9, 12, 0, 0, 3600)
+ t.localtime("UTC")
+ t.utc_offset.should == 0
+ end
+
+ it "returns a Time with a UTC offset specified as A-Z military zone" do
+ t = Time.new(2007, 1, 9, 12, 0, 0, 3600)
+ t.localtime("B")
+ t.utc_offset.should == 3600 * 2
+ end
+
platform_is_not :windows do
it "changes the timezone according to the set one" do
t = Time.new(2005, 2, 27, 22, 50, 0, -3600)
diff --git a/spec/ruby/core/time/new_spec.rb b/spec/ruby/core/time/new_spec.rb
index 09b4d03a44..727fdf92c2 100644
--- a/spec/ruby/core/time/new_spec.rb
+++ b/spec/ruby/core/time/new_spec.rb
@@ -58,6 +58,32 @@ describe "Time.new with a utc_offset argument" do
Time.new(2000, 1, 1, 0, 0, 0, "-04:10:43").utc_offset.should == -15043
end
+ ruby_bug '#13669', '3.0'...'3.1' do
+ it "returns a Time with a UTC offset specified as +HH" do
+ Time.new(2000, 1, 1, 0, 0, 0, "+05").utc_offset.should == 3600 * 5
+ end
+
+ it "returns a Time with a UTC offset specified as -HH" do
+ Time.new(2000, 1, 1, 0, 0, 0, "-05").utc_offset.should == -3600 * 5
+ end
+
+ it "returns a Time with a UTC offset specified as +HHMM" do
+ Time.new(2000, 1, 1, 0, 0, 0, "+0530").utc_offset.should == 19800
+ end
+
+ it "returns a Time with a UTC offset specified as -HHMM" do
+ Time.new(2000, 1, 1, 0, 0, 0, "-0530").utc_offset.should == -19800
+ end
+
+ it "returns a Time with a UTC offset specified as +HHMMSS" do
+ Time.new(2000, 1, 1, 0, 0, 0, "+053037").utc_offset.should == 19837
+ end
+
+ it "returns a Time with a UTC offset specified as -HHMMSS" do
+ Time.new(2000, 1, 1, 0, 0, 0, "-053037").utc_offset.should == -19837
+ end
+ end
+
describe "with an argument that responds to #to_str" do
it "coerces using #to_str" do
o = mock('string')
@@ -66,6 +92,57 @@ describe "Time.new with a utc_offset argument" do
end
end
+ it "returns a Time with UTC offset specified as UTC" do
+ Time.new(2000, 1, 1, 0, 0, 0, "UTC").utc_offset.should == 0
+ end
+
+ it "returns a Time with UTC offset specified as a single letter military timezone" do
+ [
+ ["A", 3600],
+ ["B", 3600 * 2],
+ ["C", 3600 * 3],
+ ["D", 3600 * 4],
+ ["E", 3600 * 5],
+ ["F", 3600 * 6],
+ ["G", 3600 * 7],
+ ["H", 3600 * 8],
+ ["I", 3600 * 9],
+ # J is not supported
+ ["K", 3600 * 10],
+ ["L", 3600 * 11],
+ ["M", 3600 * 12],
+ ["N", 3600 * -1],
+ ["O", 3600 * -2],
+ ["P", 3600 * -3],
+ ["Q", 3600 * -4],
+ ["R", 3600 * -5],
+ ["S", 3600 * -6],
+ ["T", 3600 * -7],
+ ["U", 3600 * -8],
+ ["V", 3600 * -9],
+ ["W", 3600 * -10],
+ ["X", 3600 * -11],
+ ["Y", 3600 * -12],
+ ["Z", 0]
+ ].each do |letter, offset|
+ Time.new(2000, 1, 1, 0, 0, 0, letter).utc_offset.should == offset
+ end
+ end
+
+ ruby_version_is ""..."3.1" do
+ it "raises ArgumentError if the string argument is J" do
+ message = '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset'
+ -> { Time.new(2000, 1, 1, 0, 0, 0, "J") }.should raise_error(ArgumentError, message)
+ end
+ end
+
+ ruby_version_is "3.1" do
+ it "raises ArgumentError if the string argument is J" do
+ message = '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: J'
+ -> { Time.new(2000, 1, 1, 0, 0, 0, "J") }.should raise_error(ArgumentError, message)
+ end
+ end
+
it "returns a local Time if the argument is nil" do
with_timezone("PST", -8) do
t = Time.new(2000, 1, 1, 0, 0, 0, nil)
@@ -93,7 +170,12 @@ describe "Time.new with a utc_offset argument" do
end
it "raises ArgumentError if the String argument is not in an ASCII-compatible encoding" do
- -> { Time.new(2000, 1, 1, 0, 0, 0, "-04:10".encode("UTF-16LE")) }.should raise_error(ArgumentError)
+ # Don't check exception message - it was changed in previous CRuby versions:
+ # - "string contains null byte"
+ # - '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset'
+ -> {
+ Time.new(2000, 1, 1, 0, 0, 0, "-04:10".encode("UTF-16LE"))
+ }.should raise_error(ArgumentError)
end
it "raises ArgumentError if the argument represents a value less than or equal to -86400 seconds" do
@@ -106,19 +188,9 @@ describe "Time.new with a utc_offset argument" do
-> { Time.new(2000, 1, 1, 0, 0, 0, 86400) }.should raise_error(ArgumentError)
end
- it "raises ArgumentError if the seconds argument is negative" do
- -> { Time.new(2000, 1, 1, 0, 0, -1) }.should raise_error(ArgumentError)
- end
-
it "raises ArgumentError if the utc_offset argument is greater than or equal to 10e9" do
-> { Time.new(2000, 1, 1, 0, 0, 0, 1000000000) }.should raise_error(ArgumentError)
end
-
- it "raises ArgumentError if the month is greater than 12" do
- # For some reason MRI uses a different message for month in 13-15 and month>=16
- -> { Time.new(2000, 13, 1, 0, 0, 0, "+01:00") }.should raise_error(ArgumentError, /(mon|argument) out of range/)
- -> { Time.new(2000, 16, 1, 0, 0, 0, "+01:00") }.should raise_error(ArgumentError, "argument out of range")
- end
end
describe "Time.new with a timezone argument" do
@@ -332,4 +404,55 @@ describe "Time.new with a timezone argument" do
end
end
end
+
+ ruby_version_is '3.1' do # https://bugs.ruby-lang.org/issues/17485
+ describe ":in keyword argument" do
+ it "could be UTC offset as a String in '+HH:MM or '-HH:MM' format" do
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: "+05:00")
+
+ time.utc_offset.should == 5*60*60
+ time.zone.should == nil
+
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: "-09:00")
+
+ time.utc_offset.should == -9*60*60
+ time.zone.should == nil
+ end
+
+ it "could be UTC offset as a number of seconds" do
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: 5*60*60)
+
+ time.utc_offset.should == 5*60*60
+ time.zone.should == nil
+
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: -9*60*60)
+
+ time.utc_offset.should == -9*60*60
+ time.zone.should == nil
+ end
+
+ it "could be a timezone object" do
+ zone = TimeSpecs::TimezoneWithName.new(name: "Asia/Colombo")
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: zone)
+
+ time.utc_offset.should == 5*3600+30*60
+ time.zone.should == zone
+
+ zone = TimeSpecs::TimezoneWithName.new(name: "PST")
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: zone)
+
+ time.utc_offset.should == -9*60*60
+ time.zone.should == zone
+ end
+
+ it "raises ArgumentError if format is invalid" do
+ -> { Time.new(2000, 1, 1, 12, 0, 0, in: "+09:99") }.should raise_error(ArgumentError)
+ -> { Time.new(2000, 1, 1, 12, 0, 0, in: "ABC") }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if two offset arguments are given" do
+ -> { Time.new(2000, 1, 1, 12, 0, 0, "+05:00", in: "+05:00") }.should raise_error(ArgumentError)
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/time/now_spec.rb b/spec/ruby/core/time/now_spec.rb
index 7dc7951996..2b2e53a17c 100644
--- a/spec/ruby/core/time/now_spec.rb
+++ b/spec/ruby/core/time/now_spec.rb
@@ -3,4 +3,49 @@ require_relative 'shared/now'
describe "Time.now" do
it_behaves_like :time_now, :now
+
+ describe ":in keyword argument" do
+ it "could be UTC offset as a String in '+HH:MM or '-HH:MM' format" do
+ time = Time.now(in: "+05:00")
+
+ time.utc_offset.should == 5*60*60
+ time.zone.should == nil
+
+ time = Time.now(in: "-09:00")
+
+ time.utc_offset.should == -9*60*60
+ time.zone.should == nil
+ end
+
+ it "could be UTC offset as a number of seconds" do
+ time = Time.now(in: 5*60*60)
+
+ time.utc_offset.should == 5*60*60
+ time.zone.should == nil
+
+ time = Time.now(in: -9*60*60)
+
+ time.utc_offset.should == -9*60*60
+ time.zone.should == nil
+ end
+
+ it "could be a timezone object" do
+ zone = TimeSpecs::TimezoneWithName.new(name: "Asia/Colombo")
+ time = Time.now(in: zone)
+
+ time.utc_offset.should == 5*3600+30*60
+ time.zone.should == zone
+
+ zone = TimeSpecs::TimezoneWithName.new(name: "PST")
+ time = Time.now(in: zone)
+
+ time.utc_offset.should == -9*60*60
+ time.zone.should == zone
+ end
+
+ it "raises ArgumentError if format is invalid" do
+ -> { Time.now(in: "+09:99") }.should raise_error(ArgumentError)
+ -> { Time.now(in: "ABC") }.should raise_error(ArgumentError)
+ end
+ end
end
diff --git a/spec/ruby/core/time/shared/gmtime.rb b/spec/ruby/core/time/shared/gmtime.rb
index 5ed64c2ab6..bae19da462 100644
--- a/spec/ruby/core/time/shared/gmtime.rb
+++ b/spec/ruby/core/time/shared/gmtime.rb
@@ -22,11 +22,11 @@ describe :time_gmtime, shared: true do
time.send(@method).should equal(time)
end
- it "raises a RuntimeError if the time is not UTC" do
+ it "raises a FrozenError if the time is not UTC" do
with_timezone("CST", -6) do
time = Time.now
time.freeze
- -> { time.send(@method) }.should raise_error(RuntimeError)
+ -> { time.send(@method) }.should raise_error(FrozenError)
end
end
end
diff --git a/spec/ruby/core/time/shared/local.rb b/spec/ruby/core/time/shared/local.rb
index 43f331c4c1..068e314999 100644
--- a/spec/ruby/core/time/shared/local.rb
+++ b/spec/ruby/core/time/shared/local.rb
@@ -7,12 +7,10 @@ describe :time_local, shared: true do
end
platform_is_not :windows do
- describe "timezone changes" do
- it "correctly adjusts the timezone change to 'CEST' on 'Europe/Amsterdam'" do
- with_timezone("Europe/Amsterdam") do
- Time.send(@method, 1940, 5, 16).to_a.should ==
- [0, 40, 1, 16, 5, 1940, 4, 137, true, "CEST"]
- end
+ it "uses the 'CET' timezone with TZ=Europe/Amsterdam in 1970" do
+ with_timezone("Europe/Amsterdam") do
+ Time.send(@method, 1970, 5, 16).to_a.should ==
+ [0, 0, 0, 16, 5, 1970, 6, 136, false, "CET"]
end
end
end
@@ -41,5 +39,4 @@ describe :time_local_10_arg, shared: true do
end
end
end
-
end
diff --git a/spec/ruby/core/time/shared/time_params.rb b/spec/ruby/core/time/shared/time_params.rb
index 63d0dbc120..b6a6c88c8e 100644
--- a/spec/ruby/core/time/shared/time_params.rb
+++ b/spec/ruby/core/time/shared/time_params.rb
@@ -145,9 +145,10 @@ describe :time_params, shared: true do
end
it "raises an ArgumentError for out of range month" do
+ # For some reason MRI uses a different message for month in 13-15 and month>=16
-> {
- Time.send(@method, 2008, 13, 31, 23, 59, 59)
- }.should raise_error(ArgumentError)
+ Time.send(@method, 2008, 16, 31, 23, 59, 59)
+ }.should raise_error(ArgumentError, /(mon|argument) out of range/)
end
it "raises an ArgumentError for out of range day" do
@@ -169,9 +170,13 @@ describe :time_params, shared: true do
end
it "raises an ArgumentError for out of range second" do
+ # For some reason MRI uses different messages for seconds 61-63 and seconds >= 64
-> {
Time.send(@method, 2008, 12, 31, 23, 59, 61)
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, /(sec|argument) out of range/)
+ -> {
+ Time.send(@method, 2008, 12, 31, 23, 59, -1)
+ }.should raise_error(ArgumentError, "argument out of range")
end
it "raises ArgumentError when given 9 arguments" do
diff --git a/spec/ruby/core/time/strftime_spec.rb b/spec/ruby/core/time/strftime_spec.rb
index 1bd24b0538..4cb300c916 100644
--- a/spec/ruby/core/time/strftime_spec.rb
+++ b/spec/ruby/core/time/strftime_spec.rb
@@ -49,4 +49,45 @@ describe "Time#strftime" do
time = @new_time_with_offset[2012, 1, 1, 0, 0, 0, Rational(36645, 10)]
time.strftime("%::z").should == "+01:01:05"
end
+
+ ruby_version_is "3.1" do
+ it "supports RFC 3339 UTC for unknown offset local time, -0000, as %-z" do
+ time = Time.gm(2022)
+
+ time.strftime("%z").should == "+0000"
+ time.strftime("%-z").should == "-0000"
+ time.strftime("%-:z").should == "-00:00"
+ time.strftime("%-::z").should == "-00:00:00"
+ end
+
+ it "applies '-' flag to UTC time" do
+ time = Time.utc(2022)
+ time.strftime("%-z").should == "-0000"
+
+ time = Time.gm(2022)
+ time.strftime("%-z").should == "-0000"
+
+ time = Time.new(2022, 1, 1, 0, 0, 0, "Z")
+ time.strftime("%-z").should == "-0000"
+
+ time = Time.new(2022, 1, 1, 0, 0, 0, "-00:00")
+ time.strftime("%-z").should == "-0000"
+
+ time = Time.new(2022, 1, 1, 0, 0, 0, "+03:00").utc
+ time.strftime("%-z").should == "-0000"
+ end
+
+ it "ignores '-' flag for non-UTC time" do
+ time = Time.new(2022, 1, 1, 0, 0, 0, "+03:00")
+ time.strftime("%-z").should == "+0300"
+ end
+
+ it "works correctly with width, _ and 0 flags, and :" do
+ Time.now.utc.strftime("%-_10z").should == " -000"
+ Time.now.utc.strftime("%-10z").should == "-000000000"
+ Time.now.utc.strftime("%-010:z").should == "-000000:00"
+ Time.now.utc.strftime("%-_10:z").should == " -0:00"
+ Time.now.utc.strftime("%-_10::z").should == " -0:00:00"
+ end
+ end
end
diff --git a/spec/ruby/core/time/utc_spec.rb b/spec/ruby/core/time/utc_spec.rb
index 74c17a93d1..809accc809 100644
--- a/spec/ruby/core/time/utc_spec.rb
+++ b/spec/ruby/core/time/utc_spec.rb
@@ -4,8 +4,45 @@ require_relative 'shared/gmtime'
require_relative 'shared/time_params'
describe "Time#utc?" do
- it "returns true if time represents a time in UTC (GMT)" do
- Time.now.should_not.utc?
+ it "returns true only if time represents a time in UTC (GMT)" do
+ Time.now.utc?.should == false
+ Time.now.utc.utc?.should == true
+ end
+
+ it "treats time as UTC what was created in different ways" do
+ Time.now.utc.utc?.should == true
+ Time.now.gmtime.utc?.should == true
+ Time.now.getgm.utc?.should == true
+ Time.now.getutc.utc?.should == true
+ Time.utc(2022).utc?.should == true
+ end
+
+ it "does treat time with 'UTC' offset as UTC" do
+ Time.new(2022, 1, 1, 0, 0, 0, "UTC").utc?.should == true
+ Time.now.localtime("UTC").utc?.should == true
+ Time.at(Time.now, in: 'UTC').utc?.should == true
+ end
+
+ it "does treat time with Z offset as UTC" do
+ Time.new(2022, 1, 1, 0, 0, 0, "Z").utc?.should == true
+ Time.now.localtime("Z").utc?.should == true
+ Time.at(Time.now, in: 'Z').utc?.should == true
+ end
+
+ ruby_version_is "3.1" do
+ it "does treat time with -00:00 offset as UTC" do
+ Time.new(2022, 1, 1, 0, 0, 0, "-00:00").utc?.should == true
+ Time.now.localtime("-00:00").utc?.should == true
+ Time.at(Time.now, in: '-00:00').utc?.should == true
+ end
+ end
+
+ it "does not treat time with +00:00 offset as UTC" do
+ Time.new(2022, 1, 1, 0, 0, 0, "+00:00").utc?.should == false
+ end
+
+ it "does not treat time with 0 offset as UTC" do
+ Time.new(2022, 1, 1, 0, 0, 0, 0).utc?.should == false
end
end
diff --git a/spec/ruby/core/time/zone_spec.rb b/spec/ruby/core/time/zone_spec.rb
index 907ccf9f4b..cbb0977f24 100644
--- a/spec/ruby/core/time/zone_spec.rb
+++ b/spec/ruby/core/time/zone_spec.rb
@@ -52,14 +52,28 @@ describe "Time#zone" do
end
it "doesn't raise errors for a Time with a fixed offset" do
- -> {
- Time.new(2001, 1, 1, 0, 0, 0, "+05:00").zone
- }.should_not raise_error
+ Time.new(2001, 1, 1, 0, 0, 0, "+05:00").zone.should == nil
end
end
it "returns UTC when called on a UTC time" do
Time.now.utc.zone.should == "UTC"
+ Time.now.gmtime.zone.should == "UTC"
+ Time.now.getgm.zone.should == "UTC"
+ Time.now.getutc.zone.should == "UTC"
+ Time.utc(2022).zone.should == "UTC"
+ Time.new(2022, 1, 1, 0, 0, 0, "UTC").zone.should == "UTC"
+ Time.new(2022, 1, 1, 0, 0, 0, "Z").zone.should == "UTC"
+ Time.now.localtime("UTC").zone.should == "UTC"
+ Time.now.localtime("Z").zone.should == "UTC"
+ Time.at(Time.now, in: 'UTC').zone.should == "UTC"
+ Time.at(Time.now, in: 'Z').zone.should == "UTC"
+
+ ruby_version_is "3.1" do
+ Time.new(2022, 1, 1, 0, 0, 0, "-00:00").zone.should == "UTC"
+ Time.now.localtime("-00:00").zone.should == "UTC"
+ Time.at(Time.now, in: '-00:00').zone.should == "UTC"
+ end
end
platform_is_not :aix, :windows do
diff --git a/spec/ruby/core/tracepoint/allow_reentry_spec.rb b/spec/ruby/core/tracepoint/allow_reentry_spec.rb
new file mode 100644
index 0000000000..6bff1bed76
--- /dev/null
+++ b/spec/ruby/core/tracepoint/allow_reentry_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.1" do
+ describe 'TracePoint.allow_reentry' do
+ it 'allows the reentrance in a given block' do
+ event_lines = []
+ l1 = l2 = l3 = l4 = nil
+ TracePoint.new(:line) do |tp|
+ next unless TracePointSpec.target_thread?
+
+ event_lines << tp.lineno
+ next if (__LINE__ + 2 .. __LINE__ + 4).cover?(tp.lineno)
+ TracePoint.allow_reentry do
+ a = 1; l3 = __LINE__
+ b = 2; l4 = __LINE__
+ end
+ end.enable do
+ c = 3; l1 = __LINE__
+ d = 4; l2 = __LINE__
+ end
+
+ event_lines.should == [l1, l3, l4, l2, l3, l4]
+ end
+
+ it 'raises RuntimeError when not called inside a TracePoint' do
+ -> {
+ TracePoint.allow_reentry{}
+ }.should raise_error(RuntimeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/inspect_spec.rb b/spec/ruby/core/tracepoint/inspect_spec.rb
index a07b626212..151a08e7b4 100644
--- a/spec/ruby/core/tracepoint/inspect_spec.rb
+++ b/spec/ruby/core/tracepoint/inspect_spec.rb
@@ -16,6 +16,15 @@ describe 'TracePoint#inspect' do
TracePoint.new(:line) {}.inspect.should == '#<TracePoint:disabled>'
end
+ it "shows only whether it's enabled when outside the TracePoint handler" do
+ trace = TracePoint.new(:line) {}
+ trace.enable
+
+ trace.inspect.should == '#<TracePoint:enabled>'
+
+ trace.disable
+ end
+
it 'returns a String showing the event, path and line' do
inspect = nil
line = nil
diff --git a/spec/ruby/core/unboundmethod/equal_value_spec.rb b/spec/ruby/core/unboundmethod/equal_value_spec.rb
index 6242b04884..036c6b7f8c 100644
--- a/spec/ruby/core/unboundmethod/equal_value_spec.rb
+++ b/spec/ruby/core/unboundmethod/equal_value_spec.rb
@@ -76,19 +76,38 @@ describe "UnboundMethod#==" do
(@identical_body == @original_body).should == false
end
- it "returns false if same method but one extracted from a subclass" do
- (@parent == @child1).should == false
- (@child1 == @parent).should == false
- end
+ ruby_version_is ""..."3.2" do
+ it "returns false if same method but one extracted from a subclass" do
+ (@parent == @child1).should == false
+ (@child1 == @parent).should == false
+ end
+
+ it "returns false if same method but extracted from two different subclasses" do
+ (@child2 == @child1).should == false
+ (@child1 == @child2).should == false
+ end
- it "returns false if same method but extracted from two different subclasses" do
- (@child2 == @child1).should == false
- (@child1 == @child2).should == false
+ it "returns false if methods are the same but added from an included Module" do
+ (@includee == @includer).should == false
+ (@includer == @includee).should == false
+ end
end
- it "returns false if methods are the same but added from an included Module" do
- (@includee == @includer).should == false
- (@includer == @includee).should == false
+ ruby_version_is "3.2" do
+ it "returns true if same method but one extracted from a subclass" do
+ (@parent == @child1).should == true
+ (@child1 == @parent).should == true
+ end
+
+ it "returns false if same method but extracted from two different subclasses" do
+ (@child2 == @child1).should == true
+ (@child1 == @child2).should == true
+ end
+
+ it "returns true if methods are the same but added from an included Module" do
+ (@includee == @includer).should == true
+ (@includer == @includee).should == true
+ end
end
it "returns false if both have same Module, same name, identical body but not the same" do
@@ -98,4 +117,41 @@ describe "UnboundMethod#==" do
(@discard_1 == UnboundMethodSpecs::Methods.instance_method(:discard_1)).should == false
end
+
+ it "considers methods through aliasing equal" do
+ c = Class.new do
+ class << self
+ alias_method :n, :new
+ end
+ end
+
+ c.method(:new).should == c.method(:n)
+ c.method(:n).should == Class.instance_method(:new).bind(c)
+ end
+
+ # On CRuby < 3.2, the 2 specs below pass due to method/instance_method skipping zsuper methods.
+ # We are interested in the general pattern working, i.e. the combination of method/instance_method
+ # and #== exposes the wanted behavior.
+ it "considers methods through visibility change equal" do
+ c = Class.new do
+ class << self
+ private :new
+ end
+ end
+
+ c.method(:new).should == Class.instance_method(:new).bind(c)
+ end
+
+ it "considers methods through aliasing and visibility change equal" do
+ c = Class.new do
+ class << self
+ alias_method :n, :new
+ private :new
+ end
+ end
+
+ c.method(:new).should == c.method(:n)
+ c.method(:n).should == Class.instance_method(:new).bind(c)
+ c.method(:new).should == Class.instance_method(:new).bind(c)
+ end
end
diff --git a/spec/ruby/core/unboundmethod/fixtures/classes.rb b/spec/ruby/core/unboundmethod/fixtures/classes.rb
index 46b1c51669..6ab958d447 100644
--- a/spec/ruby/core/unboundmethod/fixtures/classes.rb
+++ b/spec/ruby/core/unboundmethod/fixtures/classes.rb
@@ -53,6 +53,12 @@ module UnboundMethodSpecs
def discard_1(); :discard; end
def discard_2(); :discard; end
+
+ def my_public_method; end
+ def my_protected_method; end
+ def my_private_method; end
+ protected :my_protected_method
+ private :my_private_method
end
class Parent
@@ -84,4 +90,14 @@ module UnboundMethodSpecs
class C < B
def overridden; end
end
+
+ module HashSpecs
+ class SuperClass
+ def foo
+ end
+ end
+
+ class SubClass < SuperClass
+ end
+ end
end
diff --git a/spec/ruby/core/unboundmethod/hash_spec.rb b/spec/ruby/core/unboundmethod/hash_spec.rb
index 12dce0020f..6888675bc1 100644
--- a/spec/ruby/core/unboundmethod/hash_spec.rb
+++ b/spec/ruby/core/unboundmethod/hash_spec.rb
@@ -12,4 +12,11 @@ describe "UnboundMethod#hash" do
to_s, inspect = Array.instance_method(:to_s), Array.instance_method(:inspect)
to_s.hash.should == inspect.hash
end
+
+ it "equals a hash of the same method in the superclass" do
+ foo_in_superclass = UnboundMethodSpecs::HashSpecs::SuperClass.instance_method(:foo)
+ foo = UnboundMethodSpecs::HashSpecs::SubClass.instance_method(:foo)
+
+ foo.hash.should == foo_in_superclass.hash
+ end
end
diff --git a/spec/ruby/core/unboundmethod/owner_spec.rb b/spec/ruby/core/unboundmethod/owner_spec.rb
index 5f1f4646b3..e8a734dac4 100644
--- a/spec/ruby/core/unboundmethod/owner_spec.rb
+++ b/spec/ruby/core/unboundmethod/owner_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+require_relative '../method/fixtures/classes'
describe "UnboundMethod#owner" do
it "returns the owner of the method" do
@@ -23,4 +24,10 @@ describe "UnboundMethod#owner" do
child_singleton_class.instance_method(:class_method).owner.should == parent_singleton_class
child_singleton_class.instance_method(:another_class_method).owner.should == child_singleton_class
end
+
+ ruby_version_is "3.2" do
+ it "returns the class on which public was called for a private method in ancestor" do
+ MethodSpecs::InheritedMethods::C.instance_method(:derp).owner.should == MethodSpecs::InheritedMethods::C
+ end
+ end
end
diff --git a/spec/ruby/core/unboundmethod/private_spec.rb b/spec/ruby/core/unboundmethod/private_spec.rb
new file mode 100644
index 0000000000..fa735846bb
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/private_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.1"..."3.2" do
+ describe "UnboundMethod#private?" do
+ it "returns false when the method is public" do
+ obj = UnboundMethodSpecs::Methods.new
+ obj.method(:my_public_method).unbind.private?.should == false
+ end
+
+ it "returns false when the method is protected" do
+ obj = UnboundMethodSpecs::Methods.new
+ obj.method(:my_protected_method).unbind.private?.should == false
+ end
+
+ it "returns true when the method is private" do
+ obj = UnboundMethodSpecs::Methods.new
+ obj.method(:my_private_method).unbind.private?.should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/protected_spec.rb b/spec/ruby/core/unboundmethod/protected_spec.rb
new file mode 100644
index 0000000000..db00e7ef43
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/protected_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.1"..."3.2" do
+ describe "UnboundMethod#protected?" do
+ it "returns false when the method is public" do
+ obj = UnboundMethodSpecs::Methods.new
+ obj.method(:my_public_method).unbind.protected?.should == false
+ end
+
+ it "returns true when the method is protected" do
+ obj = UnboundMethodSpecs::Methods.new
+ obj.method(:my_protected_method).unbind.protected?.should == true
+ end
+
+ it "returns false when the method is private" do
+ obj = UnboundMethodSpecs::Methods.new
+ obj.method(:my_private_method).unbind.protected?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/public_spec.rb b/spec/ruby/core/unboundmethod/public_spec.rb
new file mode 100644
index 0000000000..7b87a03b15
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/public_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.1"..."3.2" do
+ describe "UnboundMethod#public?" do
+ it "returns true when the method is public" do
+ obj = UnboundMethodSpecs::Methods.new
+ obj.method(:my_public_method).unbind.public?.should == true
+ end
+
+ it "returns false when the method is protected" do
+ obj = UnboundMethodSpecs::Methods.new
+ obj.method(:my_protected_method).unbind.public?.should == false
+ end
+
+ it "returns false when the method is private" do
+ obj = UnboundMethodSpecs::Methods.new
+ obj.method(:my_private_method).unbind.public?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/shared/to_s.rb b/spec/ruby/core/unboundmethod/shared/to_s.rb
index 38503c3c60..b92bb0b207 100644
--- a/spec/ruby/core/unboundmethod/shared/to_s.rb
+++ b/spec/ruby/core/unboundmethod/shared/to_s.rb
@@ -20,12 +20,22 @@ describe :unboundmethod_to_s, shared: true do
it "the String shows the method name, Module defined in and Module extracted from" do
@from_module.send(@method).should =~ /\bfrom_mod\b/
@from_module.send(@method).should =~ /\bUnboundMethodSpecs::Mod\b/
- @from_method.send(@method).should =~ /\bUnboundMethodSpecs::Methods\b/
+
+ ruby_version_is ""..."3.2" do
+ @from_method.send(@method).should =~ /\bUnboundMethodSpecs::Methods\b/
+ end
end
it "returns a String including all details" do
- @from_module.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Methods(UnboundMethodSpecs::Mod)#from_mod"
- @from_method.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Methods(UnboundMethodSpecs::Mod)#from_mod"
+ ruby_version_is ""..."3.2" do
+ @from_module.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Methods(UnboundMethodSpecs::Mod)#from_mod"
+ @from_method.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Methods(UnboundMethodSpecs::Mod)#from_mod"
+ end
+
+ ruby_version_is "3.2" do
+ @from_module.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Mod#from_mod"
+ @from_method.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Mod#from_mod"
+ end
end
it "does not show the defining module if it is the same as the origin" do
diff --git a/spec/ruby/core/unboundmethod/super_method_spec.rb b/spec/ruby/core/unboundmethod/super_method_spec.rb
index c9fa1ec533..101c83b8b3 100644
--- a/spec/ruby/core/unboundmethod/super_method_spec.rb
+++ b/spec/ruby/core/unboundmethod/super_method_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+require_relative '../method/fixtures/classes'
describe "UnboundMethod#super_method" do
it "returns the method that would be called by super in the method" do
@@ -25,4 +26,26 @@ describe "UnboundMethod#super_method" do
method.super_method.should == nil
end
+
+ # https://github.com/jruby/jruby/issues/7240
+ context "after changing an inherited methods visibility" do
+ it "calls the proper super method" do
+ method = MethodSpecs::InheritedMethods::C.instance_method(:derp)
+ method.bind(MethodSpecs::InheritedMethods::C.new).call.should == 'BA'
+ end
+
+ it "returns the expected super_method" do
+ method = MethodSpecs::InheritedMethods::C.instance_method(:derp)
+ method.super_method.owner.should == MethodSpecs::InheritedMethods::A
+ end
+ end
+
+ ruby_version_is "2.7.3" do
+ context "after aliasing an inherited method" do
+ it "returns the expected super_method" do
+ method = MethodSpecs::InheritedMethods::C.instance_method(:meow)
+ method.super_method.owner.should == MethodSpecs::InheritedMethods::A
+ end
+ end
+ end
end
diff --git a/spec/ruby/fixtures/code/concurrent_require_fixture.rb b/spec/ruby/fixtures/code/concurrent_require_fixture.rb
new file mode 100644
index 0000000000..d4ce734183
--- /dev/null
+++ b/spec/ruby/fixtures/code/concurrent_require_fixture.rb
@@ -0,0 +1,4 @@
+object = ScratchPad.recorded
+thread = Thread.new { object.require(__FILE__) }
+Thread.pass until thread.stop?
+ScratchPad.record(thread)
diff --git a/spec/ruby/fixtures/code/wrap_fixture.rb b/spec/ruby/fixtures/code/load_wrap_fixture.rb
index 7ddf5a62e0..526bbf8c82 100644
--- a/spec/ruby/fixtures/code/wrap_fixture.rb
+++ b/spec/ruby/fixtures/code/load_wrap_fixture.rb
@@ -2,7 +2,10 @@ class LoadSpecWrap
ScratchPad << String
end
+LOAD_WRAP_SPECS_TOP_LEVEL_CONSTANT = 1
+
def load_wrap_specs_top_level_method
+ :load_wrap_specs_top_level_method
end
ScratchPad << method(:load_wrap_specs_top_level_method).owner
diff --git a/spec/ruby/fixtures/constants.rb b/spec/ruby/fixtures/constants.rb
index 4c456dae89..47a8e87e56 100644
--- a/spec/ruby/fixtures/constants.rb
+++ b/spec/ruby/fixtures/constants.rb
@@ -75,6 +75,13 @@ module ConstantSpecs
CS_CONST10 = :const10_8
end
+ # Included in ContainerA
+ module ModuleIncludePrepended
+ prepend ModuleD
+
+ CS_CONST11 = :const11_8
+ end
+
# The following classes/modules have all the constants set "statically".
# Contrast with the classes below where the constants are set as the specs
# are run.
@@ -169,6 +176,10 @@ module ConstantSpecs
def const10; CS_CONST10; end
end
+ class ContainerPrepend
+ include ModuleIncludePrepended
+ end
+
class ContainerA::ChildA
def self.const23; CS_CONST23; end
end
diff --git a/spec/ruby/language/block_spec.rb b/spec/ruby/language/block_spec.rb
index 42652152a1..8488b945d5 100644
--- a/spec/ruby/language/block_spec.rb
+++ b/spec/ruby/language/block_spec.rb
@@ -263,12 +263,55 @@ describe "A block yielded a single" do
m(obj) { |a, b, c| [a, b, c] }.should == [obj, nil, nil]
end
+ it "receives the object if it does not respond to #to_ary" do
+ obj = Object.new
+
+ m(obj) { |a, b, c| [a, b, c] }.should == [obj, nil, nil]
+ end
+
+ it "calls #respond_to? to check if object has method #to_ary" do
+ obj = mock("destructure block arguments")
+ obj.should_receive(:respond_to?).with(:to_ary, true).and_return(true)
+ obj.should_receive(:to_ary).and_return([1, 2])
+
+ m(obj) { |a, b, c| [a, b, c] }.should == [1, 2, nil]
+ end
+
+ it "receives the object if it does not respond to #respond_to?" do
+ obj = BasicObject.new
+
+ m(obj) { |a, b, c| [a, b, c] }.should == [obj, nil, nil]
+ end
+
+ it "calls #to_ary on the object when it is defined dynamically" do
+ obj = Object.new
+ def obj.method_missing(name, *args, &block)
+ if name == :to_ary
+ [1, 2]
+ else
+ super
+ end
+ end
+ def obj.respond_to_missing?(name, include_private)
+ name == :to_ary
+ end
+
+ m(obj) { |a, b, c| [a, b, c] }.should == [1, 2, nil]
+ end
+
it "raises a TypeError if #to_ary does not return an Array" do
obj = mock("destructure block arguments")
obj.should_receive(:to_ary).and_return(1)
-> { m(obj) { |a, b| } }.should raise_error(TypeError)
end
+
+ it "raises error transparently if #to_ary raises error on its own" do
+ obj = Object.new
+ def obj.to_ary; raise "Exception raised in #to_ary" end
+
+ -> { m(obj) { |a, b| } }.should raise_error(RuntimeError, "Exception raised in #to_ary")
+ end
end
end
@@ -983,3 +1026,77 @@ describe "Post-args" do
end
end
end
+
+describe "Anonymous block forwarding" do
+ ruby_version_is "3.1" do
+ it "forwards blocks to other functions that formally declare anonymous blocks" do
+ eval <<-EOF
+ def b(&); c(&) end
+ def c(&); yield :non_null end
+ EOF
+
+ b { |c| c }.should == :non_null
+ end
+
+ it "requires the anonymous block parameter to be declared if directly passing a block" do
+ -> { eval "def a; b(&); end; def b; end" }.should raise_error(SyntaxError)
+ end
+
+ it "works when it's the only declared parameter" do
+ eval <<-EOF
+ def inner; yield end
+ def block_only(&); inner(&) end
+ EOF
+
+ block_only { 1 }.should == 1
+ end
+
+ it "works alongside positional parameters" do
+ eval <<-EOF
+ def inner; yield end
+ def pos(arg1, &); inner(&) end
+ EOF
+
+ pos(:a) { 1 }.should == 1
+ end
+
+ it "works alongside positional arguments and splatted keyword arguments" do
+ eval <<-EOF
+ def inner; yield end
+ def pos_kwrest(arg1, **kw, &); inner(&) end
+ EOF
+
+ pos_kwrest(:a, arg: 3) { 1 }.should == 1
+ end
+
+ it "works alongside positional arguments and disallowed keyword arguments" do
+ eval <<-EOF
+ def inner; yield end
+ def no_kw(arg1, **nil, &); inner(&) end
+ EOF
+
+ no_kw(:a) { 1 }.should == 1
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "works alongside explicit keyword arguments" do
+ eval <<-EOF
+ def inner; yield end
+ def rest_kw(*a, kwarg: 1, &); inner(&) end
+ def kw(kwarg: 1, &); inner(&) end
+ def pos_kw_kwrest(arg1, kwarg: 1, **kw, &); inner(&) end
+ def pos_rkw(arg1, kwarg1:, &); inner(&) end
+ def all(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, &); inner(&) end
+ def all_kwrest(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, **kw, &); inner(&) end
+ EOF
+
+ rest_kw { 1 }.should == 1
+ kw { 1 }.should == 1
+ pos_kw_kwrest(:a) { 1 }.should == 1
+ pos_rkw(:a, kwarg1: 3) { 1 }.should == 1
+ all(:a, :b, :c, :d, :e, okw1: 'x', okw2: 'y') { 1 }.should == 1
+ all_kwrest(:a, :b, :c, :d, :e, okw1: 'x', okw2: 'y') { 1 }.should == 1
+ end
+ end
+end
diff --git a/spec/ruby/language/case_spec.rb b/spec/ruby/language/case_spec.rb
index bf06803764..915c032a71 100644
--- a/spec/ruby/language/case_spec.rb
+++ b/spec/ruby/language/case_spec.rb
@@ -103,7 +103,7 @@ describe "The 'case'-construct" do
$1.should == "42"
end
- it "tests with a regexp interpolated within another regexp" do
+ it "tests with a string interpolated in a regexp" do
digits = '\d+'
case "foo44"
when /oo(#{digits})/
@@ -116,7 +116,7 @@ describe "The 'case'-construct" do
$1.should == "44"
end
- it "tests with a string interpolated in a regexp" do
+ it "tests with a regexp interpolated within another regexp" do
digits_regexp = /\d+/
case "foo43"
when /oo(#{digits_regexp})/
diff --git a/spec/ruby/language/keyword_arguments_spec.rb b/spec/ruby/language/keyword_arguments_spec.rb
index 0c72f59d38..c47b7b0ae9 100644
--- a/spec/ruby/language/keyword_arguments_spec.rb
+++ b/spec/ruby/language/keyword_arguments_spec.rb
@@ -58,6 +58,24 @@ ruby_version_is "3.0" do
m(kw: 1).should == []
-> { m(kw: 1, kw2: 2) }.should raise_error(ArgumentError, 'unknown keyword: :kw2')
-> { m(kw: 1, true => false) }.should raise_error(ArgumentError, 'unknown keyword: true')
+ -> { m(kw: 1, a: 1, b: 2, c: 3) }.should raise_error(ArgumentError, 'unknown keywords: :a, :b, :c')
+ end
+
+ it "raises ArgumentError exception when required keyword argument is not passed" do
+ def m(a:, b:, c:)
+ [a, b, c]
+ end
+
+ -> { m(a: 1, b: 2) }.should raise_error(ArgumentError, /missing keyword: :c/)
+ -> { m() }.should raise_error(ArgumentError, /missing keywords: :a, :b, :c/)
+ end
+
+ it "raises ArgumentError for missing keyword arguments even if there are extra ones" do
+ def m(a:)
+ a
+ end
+
+ -> { m(b: 1) }.should raise_error(ArgumentError, /missing keyword: :a/)
end
it "handle * and ** at the same call site" do
@@ -303,6 +321,21 @@ ruby_version_is "3.0" do
m({a: 1}).should == [[{a: 1}], {}]
end
+ ruby_version_is "3.1" do
+ describe "omitted values" do
+ it "accepts short notation 'key' for 'key: value' syntax" do
+ def m(a:, b:)
+ [a, b]
+ end
+
+ a = 1
+ b = 2
+
+ eval('m(a:, b:).should == [1, 2]')
+ end
+ end
+ end
+
ruby_version_is "3.2" do
it "does not work with call(*ruby2_keyword_args) with missing ruby2_keywords in between" do
class << self
diff --git a/spec/ruby/language/method_spec.rb b/spec/ruby/language/method_spec.rb
index d464e79403..b80b314f6f 100644
--- a/spec/ruby/language/method_spec.rb
+++ b/spec/ruby/language/method_spec.rb
@@ -572,6 +572,13 @@ describe "A method" do
end
evaluate <<-ruby do
+ def m(a:, **kw) [a, kw] end
+ ruby
+
+ -> { m(b: 1) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
def m(a: 1) a end
ruby
@@ -1672,6 +1679,15 @@ ruby_version_is "3.0" do
m.should == 42
end
+
+ context "without parenthesis" do
+ evaluate <<-ruby do
+ def m = 42
+ ruby
+
+ m.should == 42
+ end
+ end
end
context "with arguments" do
@@ -1709,6 +1725,16 @@ ruby_version_is "3.0" do
m("meow", num: 2).should == "meow" * 4
end
end
+
+ ruby_version_is ""..."3.0" do
+ context "inside 'endless' method definitions" do
+ it "does not allow method calls without parenthesis" do
+ -> {
+ eval("def greet(person) = 'Hi, '.concat person")
+ }.should raise_error(SyntaxError)
+ end
+ end
+ end
end
describe "Keyword arguments are now separated from positional arguments" do
@@ -1817,4 +1843,14 @@ ruby_version_is "3.1" do
end
end
end
+
+ describe "Inside 'endless' method definitions" do
+ it "allows method calls without parenthesis" do
+ eval <<-ruby
+ def greet(person) = "Hi, ".concat person
+ ruby
+
+ greet("Homer").should == "Hi, Homer"
+ end
+ end
end
diff --git a/spec/ruby/language/module_spec.rb b/spec/ruby/language/module_spec.rb
index 1a5fcaf46f..e361bf58f5 100644
--- a/spec/ruby/language/module_spec.rb
+++ b/spec/ruby/language/module_spec.rb
@@ -28,9 +28,18 @@ describe "The module keyword" do
ModuleSpecs::Reopened.should be_true
end
- it "reopens a module included in Object" do
- module IncludedModuleSpecs; Reopened = true; end
- ModuleSpecs::IncludedInObject::IncludedModuleSpecs::Reopened.should be_true
+ ruby_version_is '3.2' do
+ it "does not reopen a module included in Object" do
+ module IncludedModuleSpecs; Reopened = true; end
+ ModuleSpecs::IncludedInObject::IncludedModuleSpecs.should_not == Object::IncludedModuleSpecs
+ end
+ end
+
+ ruby_version_is ''...'3.2' do
+ it "reopens a module included in Object" do
+ module IncludedModuleSpecs; Reopened = true; end
+ ModuleSpecs::IncludedInObject::IncludedModuleSpecs::Reopened.should be_true
+ end
end
it "raises a TypeError if the constant is a Class" do
diff --git a/spec/ruby/language/precedence_spec.rb b/spec/ruby/language/precedence_spec.rb
index 5a3c2861ce..c5adcca2c0 100644
--- a/spec/ruby/language/precedence_spec.rb
+++ b/spec/ruby/language/precedence_spec.rb
@@ -14,46 +14,44 @@ require_relative 'fixtures/precedence'
# the level below (as well as showing associativity within
# the precedence level).
-=begin
-Excerpted from 'Programming Ruby: The Pragmatic Programmer's Guide'
-Second Edition by Dave Thomas, Chad Fowler, and Andy Hunt, page 324
-
-Table 22.4. Ruby operators (high to low precedence)
-Method Operator Description
------------------------------------------------------------------------
- :: .
- x* [ ] [ ]= Element reference, element set
- x ** Exponentiation
- x ! ~ + - Not, complement, unary plus and minus
- (method names for the last two are +@ and -@)
- x * / % Multiply, divide, and modulo
- x + - Plus and minus
- x >> << Right and left shift
- x & “And” (bitwise for integers)
- x ^ | Exclusive “or” and regular “or” (bitwise for integers)
- x <= < > >= Comparison operators
- x <=> == === != =~ !~ Equality and pattern match operators (!=
- and !~ may not be defined as methods)
- && Logical “and”
- || Logical “or”
- .. ... Range (inclusive and exclusive)
- ? : Ternary if-then-else
- = %= /= -= += |= &= Assignment
- >>= <<= *= &&= ||= **=
- defined? Check if symbol defined
- not Logical negation
- or and Logical composition
- if unless while until Expression modifiers
- begin/end Block expression
------------------------------------------------------------------------
-
-* Operators marked with 'x' in the Method column are implemented as methods
-and can be overridden (except != and !~ as noted). (But see the specs
-below for implementations that define != and !~ as methods.)
-
-** These are not included in the excerpted table but are shown here for
-completeness.
-=end
+# Excerpted from 'Programming Ruby: The Pragmatic Programmer's Guide'
+# Second Edition by Dave Thomas, Chad Fowler, and Andy Hunt, page 324
+#
+# Table 22.4. Ruby operators (high to low precedence)
+# Method Operator Description
+# -----------------------------------------------------------------------
+# :: .
+# x* [ ] [ ]= Element reference, element set
+# x ** Exponentiation
+# x ! ~ + - Not, complement, unary plus and minus
+# (method names for the last two are +@ and -@)
+# x * / % Multiply, divide, and modulo
+# x + - Plus and minus
+# x >> << Right and left shift
+# x & “And” (bitwise for integers)
+# x ^ | Exclusive “or” and regular “or” (bitwise for integers)
+# x <= < > >= Comparison operators
+# x <=> == === != =~ !~ Equality and pattern match operators (!=
+# and !~ may not be defined as methods)
+# && Logical “and”
+# || Logical “or”
+# .. ... Range (inclusive and exclusive)
+# ? : Ternary if-then-else
+# = %= /= -= += |= &= Assignment
+# >>= <<= *= &&= ||= **=
+# defined? Check if symbol defined
+# not Logical negation
+# or and Logical composition
+# if unless while until Expression modifiers
+# begin/end Block expression
+# -----------------------------------------------------------------------
+#
+# * Operators marked with 'x' in the Method column are implemented as methods
+# and can be overridden (except != and !~ as noted). (But see the specs
+# below for implementations that define != and !~ as methods.)
+#
+# ** These are not included in the excerpted table but are shown here for
+# completeness.
# -----------------------------------------------------------------------
# It seems that this table is not correct anymore
diff --git a/spec/ruby/language/predefined_spec.rb b/spec/ruby/language/predefined_spec.rb
index 87385a29e5..d1cda25918 100644
--- a/spec/ruby/language/predefined_spec.rb
+++ b/spec/ruby/language/predefined_spec.rb
@@ -7,37 +7,33 @@ require 'stringio'
# Entries marked [r/o] are read-only and an error will be raised of the program attempts to
# modify them. Entries marked [thread] are thread local.
-=begin
-Exception Information
----------------------------------------------------------------------------------------------------
-
-$! Exception The exception object passed to raise. [thread]
-$@ Array The stack backtrace generated by the last exception. [thread]
-=end
-
-=begin
-Pattern Matching Variables
----------------------------------------------------------------------------------------------------
-
-These variables are set to nil after an unsuccessful pattern match.
-
-$& String The string matched (following a successful pattern match). This variable is
- local to the current scope. [r/o, thread]
-$+ String The contents of the highest-numbered group matched following a successful
- pattern match. Thus, in "cat" =~/(c|a)(t|z)/, $+ will be set to “t”. This
- variable is local to the current scope. [r/o, thread]
-$` String The string preceding the match in a successful pattern match. This variable
- is local to the current scope. [r/o, thread]
-$' String The string following the match in a successful pattern match. This variable
- is local to the current scope. [r/o, thread]
-$1 to $<N> String The contents of successive groups matched in a successful pattern match. In
- "cat" =~/(c|a)(t|z)/, $1 will be set to “a” and $2 to “t”. This variable
- is local to the current scope. [r/o, thread]
-$~ MatchData An object that encapsulates the results of a successful pattern match. The
- variables $&, $`, $', and $1 to $<N> are all derived from $~. Assigning to $~
- changes the values of these derived variables. This variable is local to the
- current scope. [thread]
-=end
+# Exception Information
+# ---------------------------------------------------------------------------------------------------
+#
+# $! Exception The exception object passed to raise. [thread]
+# $@ Array The stack backtrace generated by the last exception. [thread]
+
+# Pattern Matching Variables
+# ---------------------------------------------------------------------------------------------------
+#
+# These variables are set to nil after an unsuccessful pattern match.
+#
+# $& String The string matched (following a successful pattern match). This variable is
+# local to the current scope. [r/o, thread]
+# $+ String The contents of the highest-numbered group matched following a successful
+# pattern match. Thus, in "cat" =~/(c|a)(t|z)/, $+ will be set to “t”. This
+# variable is local to the current scope. [r/o, thread]
+# $` String The string preceding the match in a successful pattern match. This variable
+# is local to the current scope. [r/o, thread]
+# $' String The string following the match in a successful pattern match. This variable
+# is local to the current scope. [r/o, thread]
+# $1 to $<N> String The contents of successive groups matched in a successful pattern match. In
+# "cat" =~/(c|a)(t|z)/, $1 will be set to “a” and $2 to “t”. This variable
+# is local to the current scope. [r/o, thread]
+# $~ MatchData An object that encapsulates the results of a successful pattern match. The
+# variables $&, $`, $', and $1 to $<N> are all derived from $~. Assigning to $~
+# changes the values of these derived variables. This variable is local to the
+# current scope. [thread]
describe "Predefined global $~" do
@@ -506,41 +502,39 @@ describe "Predefined global $!" do
end
end
-=begin
-Input/Output Variables
----------------------------------------------------------------------------------------------------
-
-$/ String The input record separator (newline by default). This is the value that rou-
- tines such as Kernel#gets use to determine record boundaries. If set to
- nil, gets will read the entire file.
-$-0 String Synonym for $/.
-$\ String The string appended to the output of every call to methods such as
- Kernel#print and IO#write. The default value is nil.
-$, String The separator string output between the parameters to methods such as
- Kernel#print and Array#join. Defaults to nil, which adds no text.
-$. Integer The number of the last line read from the current input file.
-$; String The default separator pattern used by String#split. May be set from the
- command line using the -F flag.
-$< Object An object that provides access to the concatenation of the contents of all
- the files given as command-line arguments or $stdin (in the case where
- there are no arguments). $< supports methods similar to a File object:
- binmode, close, closed?, each, each_byte, each_line, eof, eof?,
- file, filename, fileno, getc, gets, lineno, lineno=, path, pos, pos=,
- read, readchar, readline, readlines, rewind, seek, skip, tell, to_a,
- to_i, to_io, to_s, along with the methods in Enumerable. The method
- file returns a File object for the file currently being read. This may change
- as $< reads through the files on the command line. [r/o]
-$> IO The destination of output for Kernel#print and Kernel#printf. The
- default value is $stdout.
-$_ String The last line read by Kernel#gets or Kernel#readline. Many string-
- related functions in the Kernel module operate on $_ by default. The vari-
- able is local to the current scope. [thread]
-$-F String Synonym for $;.
-$stderr IO The current standard error output.
-$stdin IO The current standard input.
-$stdout IO The current standard output. Assignment to $stdout is deprecated: use
- $stdout.reopen instead.
-=end
+# Input/Output Variables
+# ---------------------------------------------------------------------------------------------------
+#
+# $/ String The input record separator (newline by default). This is the value that rou-
+# tines such as Kernel#gets use to determine record boundaries. If set to
+# nil, gets will read the entire file.
+# $-0 String Synonym for $/.
+# $\ String The string appended to the output of every call to methods such as
+# Kernel#print and IO#write. The default value is nil.
+# $, String The separator string output between the parameters to methods such as
+# Kernel#print and Array#join. Defaults to nil, which adds no text.
+# $. Integer The number of the last line read from the current input file.
+# $; String The default separator pattern used by String#split. May be set from the
+# command line using the -F flag.
+# $< Object An object that provides access to the concatenation of the contents of all
+# the files given as command-line arguments or $stdin (in the case where
+# there are no arguments). $< supports methods similar to a File object:
+# binmode, close, closed?, each, each_byte, each_line, eof, eof?,
+# file, filename, fileno, getc, gets, lineno, lineno=, path, pos, pos=,
+# read, readchar, readline, readlines, rewind, seek, skip, tell, to_a,
+# to_i, to_io, to_s, along with the methods in Enumerable. The method
+# file returns a File object for the file currently being read. This may change
+# as $< reads through the files on the command line. [r/o]
+# $> IO The destination of output for Kernel#print and Kernel#printf. The
+# default value is $stdout.
+# $_ String The last line read by Kernel#gets or Kernel#readline. Many string-
+# related functions in the Kernel module operate on $_ by default. The vari-
+# able is local to the current scope. [thread]
+# $-F String Synonym for $;.
+# $stderr IO The current standard error output.
+# $stdin IO The current standard input.
+# $stdout IO The current standard output. Assignment to $stdout is deprecated: use
+# $stdout.reopen instead.
describe "Predefined global $/" do
before :each do
@@ -803,54 +797,52 @@ describe "Predefined global $_" do
end
end
-=begin
-Execution Environment Variables
----------------------------------------------------------------------------------------------------
-
-$0 String The name of the top-level Ruby program being executed. Typically this will
- be the program’s filename. On some operating systems, assigning to this
- variable will change the name of the process reported (for example) by the
- ps(1) command.
-$* Array An array of strings containing the command-line options from the invoca-
- tion of the program. Options used by the Ruby interpreter will have been
- removed. [r/o]
-$" Array An array containing the filenames of modules loaded by require. [r/o]
-$$ Integer The process number of the program being executed. [r/o]
-$? Process::Status The exit status of the last child process to terminate. [r/o, thread]
-$: Array An array of strings, where each string specifies a directory to be searched for
- Ruby scripts and binary extensions used by the load and require methods.
- The initial value is the value of the arguments passed via the -I command-
- line option, followed by an installation-defined standard library location, fol-
- lowed by the current directory (“.”). This variable may be set from within a
- program to alter the default search path; typically, programs use $: << dir
- to append dir to the path. [r/o]
-$-a Object True if the -a option is specified on the command line. [r/o]
-$-d Object Synonym for $DEBUG.
-$DEBUG Object Set to true if the -d command-line option is specified.
-__FILE__ String The name of the current source file. [r/o]
-$F Array The array that receives the split input line if the -a command-line option is
- used.
-$FILENAME String The name of the current input file. Equivalent to $<.filename. [r/o]
-$-i String If in-place edit mode is enabled (perhaps using the -i command-line
- option), $-i holds the extension used when creating the backup file. If you
- set a value into $-i, enables in-place edit mode.
-$-I Array Synonym for $:. [r/o]
-$-K String Sets the multibyte coding system for strings and regular expressions. Equiv-
- alent to the -K command-line option.
-$-l Object Set to true if the -l option (which enables line-end processing) is present
- on the command line. [r/o]
-__LINE__ String The current line number in the source file. [r/o]
-$LOAD_PATH Array A synonym for $:. [r/o]
-$-p Object Set to true if the -p option (which puts an implicit while gets . . . end
- loop around your program) is present on the command line. [r/o]
-$VERBOSE Object Set to true if the -v, --version, -W, or -w option is specified on the com-
- mand line. Set to false if no option, or -W1 is given. Set to nil if -W0
- was specified. Setting this option to true causes the interpreter and some
- library routines to report additional information. Setting to nil suppresses
- all warnings (including the output of Kernel.warn).
-$-v Object Synonym for $VERBOSE.
-$-w Object Synonym for $VERBOSE.
-=end
+# Execution Environment Variables
+# ---------------------------------------------------------------------------------------------------
+#
+# $0 String The name of the top-level Ruby program being executed. Typically this will
+# be the program’s filename. On some operating systems, assigning to this
+# variable will change the name of the process reported (for example) by the
+# ps(1) command.
+# $* Array An array of strings containing the command-line options from the invoca-
+# tion of the program. Options used by the Ruby interpreter will have been
+# removed. [r/o]
+# $" Array An array containing the filenames of modules loaded by require. [r/o]
+# $$ Integer The process number of the program being executed. [r/o]
+# $? Process::Status The exit status of the last child process to terminate. [r/o, thread]
+# $: Array An array of strings, where each string specifies a directory to be searched for
+# Ruby scripts and binary extensions used by the load and require methods.
+# The initial value is the value of the arguments passed via the -I command-
+# line option, followed by an installation-defined standard library location, fol-
+# lowed by the current directory (“.”). This variable may be set from within a
+# program to alter the default search path; typically, programs use $: << dir
+# to append dir to the path. [r/o]
+# $-a Object True if the -a option is specified on the command line. [r/o]
+# $-d Object Synonym for $DEBUG.
+# $DEBUG Object Set to true if the -d command-line option is specified.
+# __FILE__ String The name of the current source file. [r/o]
+# $F Array The array that receives the split input line if the -a command-line option is
+# used.
+# $FILENAME String The name of the current input file. Equivalent to $<.filename. [r/o]
+# $-i String If in-place edit mode is enabled (perhaps using the -i command-line
+# option), $-i holds the extension used when creating the backup file. If you
+# set a value into $-i, enables in-place edit mode.
+# $-I Array Synonym for $:. [r/o]
+# $-K String Sets the multibyte coding system for strings and regular expressions. Equiv-
+# alent to the -K command-line option.
+# $-l Object Set to true if the -l option (which enables line-end processing) is present
+# on the command line. [r/o]
+# __LINE__ String The current line number in the source file. [r/o]
+# $LOAD_PATH Array A synonym for $:. [r/o]
+# $-p Object Set to true if the -p option (which puts an implicit while gets . . . end
+# loop around your program) is present on the command line. [r/o]
+# $VERBOSE Object Set to true if the -v, --version, -W, or -w option is specified on the com-
+# mand line. Set to false if no option, or -W1 is given. Set to nil if -W0
+# was specified. Setting this option to true causes the interpreter and some
+# library routines to report additional information. Setting to nil suppresses
+# all warnings (including the output of Kernel.warn).
+# $-v Object Synonym for $VERBOSE.
+# $-w Object Synonym for $VERBOSE.
describe "Execution variable $:" do
it "is initialized to an array of strings" do
$:.is_a?(Array).should == true
@@ -1068,22 +1060,20 @@ describe "Global variable $0" do
end
end
-=begin
-Standard Objects
----------------------------------------------------------------------------------------------------
-
-ARGF Object A synonym for $<.
-ARGV Array A synonym for $*.
-ENV Object A hash-like object containing the program’s environment variables. An
- instance of class Object, ENV implements the full set of Hash methods. Used
- to query and set the value of an environment variable, as in ENV["PATH"]
- and ENV["term"]="ansi".
-false FalseClass Singleton instance of class FalseClass. [r/o]
-nil NilClass The singleton instance of class NilClass. The value of uninitialized
- instance and global variables. [r/o]
-self Object The receiver (object) of the current method. [r/o]
-true TrueClass Singleton instance of class TrueClass. [r/o]
-=end
+# Standard Objects
+# ---------------------------------------------------------------------------------------------------
+#
+# ARGF Object A synonym for $<.
+# ARGV Array A synonym for $*.
+# ENV Object A hash-like object containing the program’s environment variables. An
+# instance of class Object, ENV implements the full set of Hash methods. Used
+# to query and set the value of an environment variable, as in ENV["PATH"]
+# and ENV["term"]="ansi".
+# false FalseClass Singleton instance of class FalseClass. [r/o]
+# nil NilClass The singleton instance of class NilClass. The value of uninitialized
+# instance and global variables. [r/o]
+# self Object The receiver (object) of the current method. [r/o]
+# true TrueClass Singleton instance of class TrueClass. [r/o]
describe "The predefined standard objects" do
it "includes ARGF" do
@@ -1136,36 +1126,34 @@ describe "The self pseudo-variable" do
end
end
-=begin
-Global Constants
----------------------------------------------------------------------------------------------------
-
-The following constants are defined by the Ruby interpreter.
-
-DATA IO If the main program file contains the directive __END__, then
- the constant DATA will be initialized so that reading from it will
- return lines following __END__ from the source file.
-FALSE FalseClass Synonym for false (deprecated, removed in Ruby 3).
-NIL NilClass Synonym for nil (deprecated, removed in Ruby 3).
-RUBY_PLATFORM String The identifier of the platform running this program. This string
- is in the same form as the platform identifier used by the GNU
- configure utility (which is not a coincidence).
-RUBY_RELEASE_DATE String The date of this release.
-RUBY_VERSION String The version number of the interpreter.
-STDERR IO The actual standard error stream for the program. The initial
- value of $stderr.
-STDIN IO The actual standard input stream for the program. The initial
- value of $stdin.
-STDOUT IO The actual standard output stream for the program. The initial
- value of $stdout.
-SCRIPT_LINES__ Hash If a constant SCRIPT_LINES__ is defined and references a Hash,
- Ruby will store an entry containing the contents of each file it
- parses, with the file’s name as the key and an array of strings as
- the value.
-TOPLEVEL_BINDING Binding A Binding object representing the binding at Ruby’s top level—
- the level where programs are initially executed.
-TRUE TrueClass Synonym for true (deprecated, removed in Ruby 3).
-=end
+# Global Constants
+# ---------------------------------------------------------------------------------------------------
+#
+# The following constants are defined by the Ruby interpreter.
+#
+# DATA IO If the main program file contains the directive __END__, then
+# the constant DATA will be initialized so that reading from it will
+# return lines following __END__ from the source file.
+# FALSE FalseClass Synonym for false (deprecated, removed in Ruby 3).
+# NIL NilClass Synonym for nil (deprecated, removed in Ruby 3).
+# RUBY_PLATFORM String The identifier of the platform running this program. This string
+# is in the same form as the platform identifier used by the GNU
+# configure utility (which is not a coincidence).
+# RUBY_RELEASE_DATE String The date of this release.
+# RUBY_VERSION String The version number of the interpreter.
+# STDERR IO The actual standard error stream for the program. The initial
+# value of $stderr.
+# STDIN IO The actual standard input stream for the program. The initial
+# value of $stdin.
+# STDOUT IO The actual standard output stream for the program. The initial
+# value of $stdout.
+# SCRIPT_LINES__ Hash If a constant SCRIPT_LINES__ is defined and references a Hash,
+# Ruby will store an entry containing the contents of each file it
+# parses, with the file’s name as the key and an array of strings as
+# the value.
+# TOPLEVEL_BINDING Binding A Binding object representing the binding at Ruby’s top level—
+# the level where programs are initially executed.
+# TRUE TrueClass Synonym for true (deprecated, removed in Ruby 3).
describe "The predefined global constants" do
describe "TRUE" do
diff --git a/spec/ruby/language/proc_spec.rb b/spec/ruby/language/proc_spec.rb
index 8360967ec8..f8a29962b0 100644
--- a/spec/ruby/language/proc_spec.rb
+++ b/spec/ruby/language/proc_spec.rb
@@ -237,4 +237,11 @@ describe "A Proc" do
end
end
end
+
+ describe "taking |required keyword arguments, **kw| arguments" do
+ it "raises ArgumentError for missing required argument" do
+ p = proc { |a:, **kw| [a, kw] }
+ -> { p.call() }.should raise_error(ArgumentError)
+ end
+ end
end
diff --git a/spec/ruby/language/range_spec.rb b/spec/ruby/language/range_spec.rb
index 55dc65882a..ccc9f55537 100644
--- a/spec/ruby/language/range_spec.rb
+++ b/spec/ruby/language/range_spec.rb
@@ -10,6 +10,14 @@ describe "Literal Ranges" do
(1...10).should == Range.new(1, 10, true)
end
+ it "creates a simple range as an object literal" do
+ ary = []
+ 2.times do
+ ary.push(1..3)
+ end
+ ary[0].should.equal?(ary[1])
+ end
+
it "creates endless ranges" do
(1..).should == Range.new(1, nil)
(1...).should == Range.new(1, nil, true)
diff --git a/spec/ruby/language/regexp/character_classes_spec.rb b/spec/ruby/language/regexp/character_classes_spec.rb
index 0cf1e9b6f4..12a51178b2 100644
--- a/spec/ruby/language/regexp/character_classes_spec.rb
+++ b/spec/ruby/language/regexp/character_classes_spec.rb
@@ -609,6 +609,11 @@ describe "Regexp with character classes" do
"루비(Ruby)".match(/\p{Hangul}+/u).to_a.should == ["루비"]
end
+ it "supports negated property condition" do
+ "a".match(/\P{L}/).should be_nil
+ "1".match(/\P{N}/).should be_nil
+ end
+
ruby_bug "#17340", ''...'3.0' do
it "raises a RegexpError for an unterminated unicode property" do
-> { Regexp.new('\p{') }.should raise_error(RegexpError)
diff --git a/spec/ruby/language/regexp/escapes_spec.rb b/spec/ruby/language/regexp/escapes_spec.rb
index 2e5fe5ad2e..16a4d8c23b 100644
--- a/spec/ruby/language/regexp/escapes_spec.rb
+++ b/spec/ruby/language/regexp/escapes_spec.rb
@@ -2,8 +2,10 @@
require_relative '../../spec_helper'
require_relative '../fixtures/classes'
+# TODO: synchronize with spec/core/regexp/new_spec.rb -
+# escaping is also tested there
describe "Regexps with escape characters" do
- it "they're supported" do
+ it "supports escape sequences" do
/\t/.match("\t").to_a.should == ["\t"] # horizontal tab
/\v/.match("\v").to_a.should == ["\v"] # vertical tab
/\n/.match("\n").to_a.should == ["\n"] # newline
@@ -15,9 +17,7 @@ describe "Regexps with escape characters" do
# \nnn octal char (encoded byte value)
end
- it "support quoting meta-characters via escape sequence" do
- /\\/.match("\\").to_a.should == ["\\"]
- /\//.match("/").to_a.should == ["/"]
+ it "supports quoting meta-characters via escape sequence" do
# parenthesis, etc
/\(/.match("(").to_a.should == ["("]
/\)/.match(")").to_a.should == [")"]
@@ -25,6 +25,8 @@ describe "Regexps with escape characters" do
/\]/.match("]").to_a.should == ["]"]
/\{/.match("{").to_a.should == ["{"]
/\}/.match("}").to_a.should == ["}"]
+ /\</.match("<").to_a.should == ["<"]
+ /\>/.match(">").to_a.should == [">"]
# alternation separator
/\|/.match("|").to_a.should == ["|"]
# quantifiers
@@ -37,11 +39,81 @@ describe "Regexps with escape characters" do
/\$/.match("$").to_a.should == ["$"]
end
+ it "supports quoting meta-characters via escape sequence when used as a terminator" do
+ # parenthesis, etc
+ # %r[[, %r((, etc literals - are forbidden
+ %r(\().match("(").to_a.should == ["("]
+ %r(\)).match(")").to_a.should == [")"]
+ %r)\().match("(").to_a.should == ["("]
+ %r)\)).match(")").to_a.should == [")"]
+
+ %r[\[].match("[").to_a.should == ["["]
+ %r[\]].match("]").to_a.should == ["]"]
+ %r]\[].match("[").to_a.should == ["["]
+ %r]\]].match("]").to_a.should == ["]"]
+
+ %r{\{}.match("{").to_a.should == ["{"]
+ %r{\}}.match("}").to_a.should == ["}"]
+ %r}\{}.match("{").to_a.should == ["{"]
+ %r}\}}.match("}").to_a.should == ["}"]
+
+ %r<\<>.match("<").to_a.should == ["<"]
+ %r<\>>.match(">").to_a.should == [">"]
+ %r>\<>.match("<").to_a.should == ["<"]
+ %r>\>>.match(">").to_a.should == [">"]
+
+ # alternation separator
+ %r|\||.match("|").to_a.should == ["|"]
+ # quantifiers
+ %r?\??.match("?").to_a.should == ["?"]
+ %r.\...match(".").to_a.should == ["."]
+ %r*\**.match("*").to_a.should == ["*"]
+ %r+\++.match("+").to_a.should == ["+"]
+ # line anchors
+ %r^\^^.match("^").to_a.should == ["^"]
+ %r$\$$.match("$").to_a.should == ["$"]
+ end
+
+ it "supports quoting non-meta-characters via escape sequence when used as a terminator" do
+ non_meta_character_terminators = [
+ '!', '"', '#', '%', '&', "'", ',', '-', ':', ';', '@', '_', '`', '/', '=', '~'
+ ]
+
+ non_meta_character_terminators.each do |c|
+ pattern = eval("%r" + c + "\\" + c + c)
+ pattern.match(c).to_a.should == [c]
+ end
+ end
+
+ it "does not change semantics of escaped non-meta-character when used as a terminator" do
+ all_terminators = [*("!".."/"), *(":".."@"), *("[".."`"), *("{".."~")]
+ meta_character_terminators = ["$", "^", "*", "+", ".", "?", "|", "}", ")", ">", "]"]
+ special_cases = ['(', '{', '[', '<', '\\']
+
+ # it should be equivalent to
+ # [ '!', '"', '#', '%', '&', "'", ',', '-', ':', ';', '@', '_', '`', '/', '=', '~' ]
+ non_meta_character_terminators = all_terminators - meta_character_terminators - special_cases
+
+ non_meta_character_terminators.each do |c|
+ pattern = eval("%r" + c + "\\" + c + c)
+ pattern.should == /#{c}/
+ end
+ end
+
+ it "does not change semantics of escaped meta-character when used as a terminator" do
+ meta_character_terminators = ["$", "^", "*", "+", ".", "?", "|", "}", ")", ">", "]"]
+
+ meta_character_terminators.each do |c|
+ pattern = eval("%r" + c + "\\" + c + c)
+ pattern.should == eval("/\\#{c}/")
+ end
+ end
+
it "allows any character to be escaped" do
/\y/.match("y").to_a.should == ["y"]
end
- it "support \\x (hex characters)" do
+ it "supports \\x (hex characters)" do
/\xA/.match("\nxyz").to_a.should == ["\n"]
/\x0A/.match("\n").to_a.should == ["\n"]
/\xAA/.match("\nA").should be_nil
@@ -53,7 +125,7 @@ describe "Regexps with escape characters" do
# \x{7HHHHHHH} wide hexadecimal char (character code point value)
end
- it "support \\c (control characters)" do
+ it "supports \\c (control characters)" do
#/\c \c@\c`/.match("\00\00\00").to_a.should == ["\00\00\00"]
/\c#\cc\cC/.match("\03\03\03").to_a.should == ["\03\03\03"]
/\c'\cG\cg/.match("\a\a\a").to_a.should == ["\a\a\a"]
diff --git a/spec/ruby/language/regexp_spec.rb b/spec/ruby/language/regexp_spec.rb
index 8fc4dc4f0a..d49689349d 100644
--- a/spec/ruby/language/regexp_spec.rb
+++ b/spec/ruby/language/regexp_spec.rb
@@ -96,7 +96,6 @@ describe "Literal Regexps" do
/./.match("\0").to_a.should == ["\0"]
end
-
it "supports | (alternations)" do
/a|b/.match("a").to_a.should == ["a"]
end
@@ -161,26 +160,6 @@ describe "Literal Regexps" do
pattern.should_not =~ 'T'
end
- escapable_terminators = ['!', '"', '#', '%', '&', "'", ',', '-', ':', ';', '@', '_', '`']
-
- it "supports escaping characters when used as a terminator" do
- escapable_terminators.each do |c|
- ref = "(?-mix:#{c})"
- pattern = eval("%r" + c + "\\" + c + c)
- pattern.to_s.should == ref
- end
- end
-
- it "treats an escaped non-escapable character normally when used as a terminator" do
- all_terminators = [*("!".."/"), *(":".."@"), *("[".."`"), *("{".."~")]
- special_cases = ['(', '{', '[', '<', '\\', '=', '~']
- (all_terminators - special_cases - escapable_terminators).each do |c|
- ref = "(?-mix:\\#{c})"
- pattern = eval("%r" + c + "\\" + c + c)
- pattern.to_s.should == ref
- end
- end
-
it "support handling unicode 9.0 characters with POSIX bracket expressions" do
char_lowercase = "\u{104D8}" # OSAGE SMALL LETTER A
/[[:lower:]]/.match(char_lowercase).to_s.should == char_lowercase
diff --git a/spec/ruby/language/return_spec.rb b/spec/ruby/language/return_spec.rb
index 94c15b697e..a62ed1242d 100644
--- a/spec/ruby/language/return_spec.rb
+++ b/spec/ruby/language/return_spec.rb
@@ -435,6 +435,21 @@ describe "The return keyword" do
end
end
+ describe "within BEGIN" do
+ it "is allowed" do
+ File.write(@filename, <<-END_OF_CODE)
+ BEGIN {
+ ScratchPad << "before call"
+ return
+ ScratchPad << "after call"
+ }
+ END_OF_CODE
+
+ load @filename
+ ScratchPad.recorded.should == ["before call"]
+ end
+ end
+
describe "file loading" do
it "stops file loading and execution" do
File.write(@filename, <<-END_OF_CODE)
diff --git a/spec/ruby/library/bigdecimal/exponent_spec.rb b/spec/ruby/library/bigdecimal/exponent_spec.rb
index f63c4e5798..8877147955 100644
--- a/spec/ruby/library/bigdecimal/exponent_spec.rb
+++ b/spec/ruby/library/bigdecimal/exponent_spec.rb
@@ -18,17 +18,6 @@ describe "BigDecimal#exponent" do
BigDecimal("1234567E10").exponent.should == 17
end
-# commenting this spec out after discussion with Defiler, since it seems to be an MRI bug, not a real feature
-=begin
- platform_is wordsize: 32 do
- # TODO: write specs for both 32 and 64 bit
- it "returns 0 if exponent can't be represented as Integer" do
- BigDecimal("2E1000000000000000").exponent.should == 0
- BigDecimal("-5E-999999999999999").exponent.should == 0
- end
- end
-=end
-
it "returns 0 if self is 0" do
BigDecimal("0").exponent.should == 0
BigDecimal("+0").exponent.should == 0
diff --git a/spec/ruby/library/bigdecimal/round_spec.rb b/spec/ruby/library/bigdecimal/round_spec.rb
index 501a1a7342..bfc6dbc763 100644
--- a/spec/ruby/library/bigdecimal/round_spec.rb
+++ b/spec/ruby/library/bigdecimal/round_spec.rb
@@ -228,7 +228,15 @@ describe "BigDecimal#round" do
-> { BigDecimal('-Infinity').round(2) }.should_not raise_error(FloatDomainError)
end
- it "raise for a non-existent round mode" do
- -> { @p1_50.round(0, :nonsense) }.should raise_error(ArgumentError, "invalid rounding mode")
+ ruby_version_is ''...'3.2' do
+ it 'raise for a non-existent round mode' do
+ -> { @p1_50.round(0, :nonsense) }.should raise_error(ArgumentError, "invalid rounding mode")
+ end
+ end
+
+ ruby_version_is '3.2' do
+ it 'raise for a non-existent round mode' do
+ -> { @p1_50.round(0, :nonsense) }.should raise_error(ArgumentError, "invalid rounding mode (nonsense)")
+ end
end
end
diff --git a/spec/ruby/library/bigdecimal/to_r_spec.rb b/spec/ruby/library/bigdecimal/to_r_spec.rb
index 91d2b33993..c350beff08 100644
--- a/spec/ruby/library/bigdecimal/to_r_spec.rb
+++ b/spec/ruby/library/bigdecimal/to_r_spec.rb
@@ -13,4 +13,16 @@ describe "BigDecimal#to_r" do
r.denominator.should eql(1000000000000000000000000)
end
+ it "returns a Rational from a BigDecimal with an exponent" do
+ r = BigDecimal("1E2").to_r
+ r.numerator.should eql(100)
+ r.denominator.should eql(1)
+ end
+
+ it "returns a Rational from a negative BigDecimal with an exponent" do
+ r = BigDecimal("-1E2").to_r
+ r.numerator.should eql(-100)
+ r.denominator.should eql(1)
+ end
+
end
diff --git a/spec/ruby/library/cgi/cookie/name_spec.rb b/spec/ruby/library/cgi/cookie/name_spec.rb
index 14226824c8..326a43ade3 100644
--- a/spec/ruby/library/cgi/cookie/name_spec.rb
+++ b/spec/ruby/library/cgi/cookie/name_spec.rb
@@ -6,18 +6,18 @@ describe "CGI::Cookie#name" do
cookie = CGI::Cookie.new("test-cookie")
cookie.name.should == "test-cookie"
- cookie = CGI::Cookie.new("name" => "another cookie")
- cookie.name.should == "another cookie"
+ cookie = CGI::Cookie.new("name" => "another-cookie")
+ cookie.name.should == "another-cookie"
end
end
describe "CGI::Cookie#name=" do
it "sets self's expiration date" do
cookie = CGI::Cookie.new("test-cookie")
- cookie.name = "another name"
- cookie.name.should == "another name"
+ cookie.name = "another-name"
+ cookie.name.should == "another-name"
- cookie.name = "and one more"
- cookie.name.should == "and one more"
+ cookie.name = "and-one-more"
+ cookie.name.should == "and-one-more"
end
end
diff --git a/spec/ruby/library/cgi/cookie/parse_spec.rb b/spec/ruby/library/cgi/cookie/parse_spec.rb
index 90d2c3d148..d484c7bad9 100644
--- a/spec/ruby/library/cgi/cookie/parse_spec.rb
+++ b/spec/ruby/library/cgi/cookie/parse_spec.rb
@@ -6,16 +6,16 @@ describe "CGI::Cookie.parse" do
expected = { "test-cookie" => ["one", "two", "three"] }
CGI::Cookie.parse("test-cookie=one&two&three").should == expected
- expected = { "second cookie" => ["three", "four"], "first cookie" => ["one", "two"] }
- CGI::Cookie.parse("first cookie=one&two;second cookie=three&four").should == expected
+ expected = { "second-cookie" => ["three", "four"], "first-cookie" => ["one", "two"] }
+ CGI::Cookie.parse("first-cookie=one&two;second-cookie=three&four").should == expected
end
it "does not use , for cookie separators" do
expected = {
- "first cookie" => ["one", "two"],
- "second cookie" => ["three", "four,third_cookie=five", "six"]
+ "first-cookie" => ["one", "two"],
+ "second-cookie" => ["three", "four,third_cookie=five", "six"]
}
- CGI::Cookie.parse("first cookie=one&two;second cookie=three&four,third_cookie=five&six").should == expected
+ CGI::Cookie.parse("first-cookie=one&two;second-cookie=three&four,third_cookie=five&six").should == expected
end
it "unescapes the Cookie values" do
diff --git a/spec/ruby/library/cmath/math/acos_spec.rb b/spec/ruby/library/cmath/math/acos_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/acos_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/acosh_spec.rb b/spec/ruby/library/cmath/math/acosh_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/acosh_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/asin_spec.rb b/spec/ruby/library/cmath/math/asin_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/asin_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/asinh_spec.rb b/spec/ruby/library/cmath/math/asinh_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/asinh_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/atan2_spec.rb b/spec/ruby/library/cmath/math/atan2_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/atan2_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/atan_spec.rb b/spec/ruby/library/cmath/math/atan_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/atan_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/atanh_spec.rb b/spec/ruby/library/cmath/math/atanh_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/atanh_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/cos_spec.rb b/spec/ruby/library/cmath/math/cos_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/cos_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/cosh_spec.rb b/spec/ruby/library/cmath/math/cosh_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/cosh_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/exp_spec.rb b/spec/ruby/library/cmath/math/exp_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/exp_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/fixtures/classes.rb b/spec/ruby/library/cmath/math/fixtures/classes.rb
deleted file mode 100644
index 443c1a9ace..0000000000
--- a/spec/ruby/library/cmath/math/fixtures/classes.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'cmath'
-class IncludesMath
- include CMath
-end
diff --git a/spec/ruby/library/cmath/math/log10_spec.rb b/spec/ruby/library/cmath/math/log10_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/log10_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/log_spec.rb b/spec/ruby/library/cmath/math/log_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/log_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/shared/acos.rb b/spec/ruby/library/cmath/math/shared/acos.rb
deleted file mode 100644
index 65637fa838..0000000000
--- a/spec/ruby/library/cmath/math/shared/acos.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_acos, shared: true do
- it "returns the arccosine of the passed argument" do
- @object.send(:acos, 1).should be_close(0.0, TOLERANCE)
- @object.send(:acos, 0).should be_close(1.5707963267949, TOLERANCE)
- @object.send(:acos, -1).should be_close(Math::PI,TOLERANCE)
- end
-
- it "returns the arccosine for Complex numbers" do
- @object.send(:acos, Complex(3, 4)).should be_close(Complex(0.93681246115572, -2.30550903124348), TOLERANCE)
- end
-
- it "returns the arccosine for numbers greater than 1.0 as a Complex number" do
- @object.send(:acos, 1.0001).should be_close(Complex(0.0, 0.0141420177752494), TOLERANCE)
- end
-
- it "returns the arccosine for numbers less than -1.0 as a Complex number" do
- @object.send(:acos, -1.0001).should be_close(Complex(3.14159265358979, -0.0141420177752495), TOLERANCE)
- end
-end
-
-describe :complex_math_acos_bang, shared: true do
- it "returns the arccosine of the argument" do
- @object.send(:acos!, 1).should be_close(0.0, TOLERANCE)
- @object.send(:acos!, 0).should be_close(1.5707963267949, TOLERANCE)
- @object.send(:acos!, -1).should be_close(Math::PI,TOLERANCE)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:acos!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-
- it "raises an Errno::EDOM for numbers greater than 1.0" do
- -> { @object.send(:acos!, 1.0001) }.should raise_error(Errno::EDOM)
- end
-
- it "raises an Errno::EDOM for numbers less than -1.0" do
- -> { @object.send(:acos!, -1.0001) }.should raise_error(Errno::EDOM)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/acosh.rb b/spec/ruby/library/cmath/math/shared/acosh.rb
deleted file mode 100644
index 285b0b823f..0000000000
--- a/spec/ruby/library/cmath/math/shared/acosh.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_acosh, shared: true do
- it "returns the principle value of the inverse hyperbolic cosine of the argument" do
- @object.send(:acosh, 14.2).should be_close(3.345146999647, TOLERANCE)
- @object.send(:acosh, 1.0).should be_close(0.0, TOLERANCE)
- end
-
- it "returns the principle value of the inverse hyperbolic cosine for numbers less than 1.0 as a Complex number" do
- @object.send(:acosh, 1.0 - TOLERANCE).should be_close(Complex(0.0, 0.00774598605746135), TOLERANCE)
- @object.send(:acosh, 0).should be_close(Complex(0.0, 1.5707963267949), TOLERANCE)
- @object.send(:acosh, -1.0).should be_close(Complex(0.0, 3.14159265358979), TOLERANCE)
- end
-
- it "returns the principle value of the inverse hyperbolic cosine for Complex numbers" do
- @object.send(:acosh, Complex(3, 4))
- @object.send(:acosh, Complex(3, 4)).imaginary.should be_close(0.93681246115572, TOLERANCE)
- @object.send(:acosh, Complex(3, 4)).real.should be_close(2.305509031243477, TOLERANCE)
- end
-end
-
-describe :complex_math_acosh_bang, shared: true do
- it "returns the principle value of the inverse hyperbolic cosine of the argument" do
- @object.send(:acosh!, 14.2).should be_close(3.345146999647, TOLERANCE)
- @object.send(:acosh!, 1.0).should be_close(0.0, TOLERANCE)
- end
-
- it "raises Errno::EDOM for numbers less than 1.0" do
- -> { @object.send(:acosh!, 1.0 - TOLERANCE) }.should raise_error(Errno::EDOM)
- -> { @object.send(:acosh!, 0) }.should raise_error(Errno::EDOM)
- -> { @object.send(:acosh!, -1.0) }.should raise_error(Errno::EDOM)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:acosh!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/asin.rb b/spec/ruby/library/cmath/math/shared/asin.rb
deleted file mode 100644
index 91fed7aa06..0000000000
--- a/spec/ruby/library/cmath/math/shared/asin.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_asin, shared: true do
- it "returns the arcsine of the argument" do
- @object.send(:asin, 1).should be_close(Math::PI/2, TOLERANCE)
- @object.send(:asin, 0).should be_close(0.0, TOLERANCE)
- @object.send(:asin, -1).should be_close(-Math::PI/2, TOLERANCE)
- @object.send(:asin, 0.25).should be_close(0.252680255142079, TOLERANCE)
- @object.send(:asin, 0.50).should be_close(0.523598775598299, TOLERANCE)
- @object.send(:asin, 0.75).should be_close(0.8480620789814816,TOLERANCE)
- end
-
- it "returns the arcsine for Complex numbers" do
- @object.send(:asin, Complex(3, 4)).should be_close(Complex(0.633983865639174, 2.30550903124347), TOLERANCE)
- end
-
- it "returns a Complex number when the argument is greater than 1.0" do
- @object.send(:asin, 1.0001).should be_close(Complex(1.5707963267949, -0.0141420177752494), TOLERANCE)
- end
-
- it "returns a Complex number when the argument is less than -1.0" do
- @object.send(:asin, -1.0001).should be_close(Complex(-1.5707963267949, 0.0141420177752494), TOLERANCE)
- end
-end
-
-describe :complex_math_asin_bang, shared: true do
- it "returns the arcsine of the argument" do
- @object.send(:asin!, 1).should be_close(Math::PI/2, TOLERANCE)
- @object.send(:asin!, 0).should be_close(0.0, TOLERANCE)
- @object.send(:asin!, -1).should be_close(-Math::PI/2, TOLERANCE)
- @object.send(:asin!, 0.25).should be_close(0.252680255142079, TOLERANCE)
- @object.send(:asin!, 0.50).should be_close(0.523598775598299, TOLERANCE)
- @object.send(:asin!, 0.75).should be_close(0.8480620789814816,TOLERANCE)
- end
-
- it "raises an Errno::EDOM if the argument is greater than 1.0" do
- -> { @object.send(:asin!, 1.0001) }.should raise_error( Errno::EDOM)
- end
-
- it "raises an Errno::EDOM if the argument is less than -1.0" do
- -> { @object.send(:asin!, -1.0001) }.should raise_error( Errno::EDOM)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:asin!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/asinh.rb b/spec/ruby/library/cmath/math/shared/asinh.rb
deleted file mode 100644
index b4ddd3a22e..0000000000
--- a/spec/ruby/library/cmath/math/shared/asinh.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_asinh, shared: true do
- it "returns the inverse hyperbolic sin of the argument" do
- @object.send(:asinh, 1.5).should be_close(1.19476321728711, TOLERANCE)
- @object.send(:asinh, -2.97).should be_close(-1.8089166921397, TOLERANCE)
- @object.send(:asinh, 0.0).should == 0.0
- @object.send(:asinh, -0.0).should == -0.0
- @object.send(:asinh, 1.05367e-08).should be_close(1.05367e-08, TOLERANCE)
- @object.send(:asinh, -1.05367e-08).should be_close(-1.05367e-08, TOLERANCE)
- end
-
- it "returns the inverse hyperbolic sin for Complex numbers" do
- @object.send(:asinh, Complex(3, 4)).should be_close(Complex(2.29991404087927, 0.917616853351479), TOLERANCE)
- @object.send(:asinh, Complex(3.5, -4)).should be_close(Complex(2.36263337274419, -0.843166327537659), TOLERANCE)
- end
-end
-
-describe :complex_math_asinh_bang, shared: true do
- it "returns the inverse hyperbolic sin of the argument" do
- @object.send(:asinh!, 1.5).should be_close(1.19476321728711, TOLERANCE)
- @object.send(:asinh!, -2.97).should be_close(-1.8089166921397, TOLERANCE)
- @object.send(:asinh!, 0.0).should == 0.0
- @object.send(:asinh!, -0.0).should == -0.0
- @object.send(:asinh!, 1.05367e-08).should be_close(1.05367e-08, TOLERANCE)
- @object.send(:asinh!, -1.05367e-08).should be_close(-1.05367e-08, TOLERANCE)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:asinh!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/atan.rb b/spec/ruby/library/cmath/math/shared/atan.rb
deleted file mode 100644
index 63a496e841..0000000000
--- a/spec/ruby/library/cmath/math/shared/atan.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_atan, shared: true do
- it "returns the arctangent of the argument" do
- @object.send(:atan, 1).should be_close(Math::PI/4, TOLERANCE)
- @object.send(:atan, 0).should be_close(0.0, TOLERANCE)
- @object.send(:atan, -1).should be_close(-Math::PI/4, TOLERANCE)
- @object.send(:atan, 0.25).should be_close(0.244978663126864, TOLERANCE)
- @object.send(:atan, 0.50).should be_close(0.463647609000806, TOLERANCE)
- @object.send(:atan, 0.75).should be_close(0.643501108793284, TOLERANCE)
- end
-
- it "returns the arctangent for Complex numbers" do
- @object.send(:atan, Complex(3, 4)).should be_close(Complex(1.44830699523146, 0.158997191679999), TOLERANCE)
- @object.send(:atan, Complex(3.5, -4)).should be_close(Complex(1.44507428165589, -0.140323762363786), TOLERANCE)
- end
-end
-
-describe :complex_math_atan_bang, shared: true do
- it "returns the arctangent of the argument" do
- @object.send(:atan!, 1).should be_close(Math::PI/4, TOLERANCE)
- @object.send(:atan!, 0).should be_close(0.0, TOLERANCE)
- @object.send(:atan!, -1).should be_close(-Math::PI/4, TOLERANCE)
- @object.send(:atan!, 0.25).should be_close(0.244978663126864, TOLERANCE)
- @object.send(:atan!, 0.50).should be_close(0.463647609000806, TOLERANCE)
- @object.send(:atan!, 0.75).should be_close(0.643501108793284, TOLERANCE)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:atan!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/atan2.rb b/spec/ruby/library/cmath/math/shared/atan2.rb
deleted file mode 100644
index 6d89423924..0000000000
--- a/spec/ruby/library/cmath/math/shared/atan2.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_atan2, shared: true do
- it "returns the arc tangent of the passed arguments" do
- @object.send(:atan2, 4.2, 0.3).should be_close(1.49948886200961, TOLERANCE)
- @object.send(:atan2, 0.0, 1.0).should be_close(0.0, TOLERANCE)
- @object.send(:atan2, -9.1, 3.2).should be_close(-1.23265379809025, TOLERANCE)
- @object.send(:atan2, 7.22, -3.3).should be_close(1.99950888779256, TOLERANCE)
- end
-
- it "returns the arc tangent for two Complex numbers" do
- CMath.atan2(Complex(3, 4), Complex(3.5, -4)).should be_close(Complex(-0.641757436698881, 1.10829873031207), TOLERANCE)
- end
-
- it "returns the arc tangent for Complex and real numbers" do
- CMath.atan2(Complex(3, 4), -7).should be_close(Complex(2.61576754731561, -0.494290673139855), TOLERANCE)
- CMath.atan2(5, Complex(3.5, -4)).should be_close(Complex(0.739102348493673, 0.487821626522923), TOLERANCE)
- end
-end
-
-describe :complex_math_atan2_bang, shared: true do
- it "returns the arc tangent of the passed arguments" do
- @object.send(:atan2!, 4.2, 0.3).should be_close(1.49948886200961, TOLERANCE)
- @object.send(:atan2!, 0.0, 1.0).should be_close(0.0, TOLERANCE)
- @object.send(:atan2!, -9.1, 3.2).should be_close(-1.23265379809025, TOLERANCE)
- @object.send(:atan2!, 7.22, -3.3).should be_close(1.99950888779256, TOLERANCE)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:atan2!, Complex(4, 5), Complex(4, 5)) }.should raise_error(TypeError)
- -> { @object.send(:atan2!, 4, Complex(4, 5)) }.should raise_error(TypeError)
- -> { @object.send(:atan2!, Complex(4, 5), 5) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/atanh.rb b/spec/ruby/library/cmath/math/shared/atanh.rb
deleted file mode 100644
index ae80e61bec..0000000000
--- a/spec/ruby/library/cmath/math/shared/atanh.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_atanh_complex, shared: true do
- it "returns the inverse hyperbolic tangent as a Complex number for arguments greater than 1.0" do
- value = Complex(18.36840028483855, 1.5707963267948966)
- @object.send(@method, 1.0 + Float::EPSILON).should be_close(value, TOLERANCE)
-
- value = Complex(0.100335347731076, 1.5707963267949)
- @object.send(@method, 10).should be_close(value, TOLERANCE)
- end
-
- it "returns the inverse hyperbolic tangent as a Complex number for arguments greater than 1.0" do
- value = Complex(-18.36840028483855, 1.5707963267948966)
- @object.send(@method, -1.0 - Float::EPSILON).should be_close(value, TOLERANCE)
-
- value = Complex(0.100335347731076, 1.5707963267949)
- @object.send(@method, 10).should be_close(value, TOLERANCE)
- end
-
- it "returns the inverse hyperbolic tangent for Complex numbers" do
- value = Complex(0.117500907311434, 1.40992104959658)
- @object.send(@method, Complex(3, 4)).should be_close(value, TOLERANCE)
- end
-end
-
-describe :complex_math_atanh_no_complex, shared: true do
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:atanh!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/cos.rb b/spec/ruby/library/cmath/math/shared/cos.rb
deleted file mode 100644
index 31cb5ab1e5..0000000000
--- a/spec/ruby/library/cmath/math/shared/cos.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_cos, shared: true do
- it "returns the cosine of the argument expressed in radians" do
- @object.send(:cos, CMath::PI).should be_close(-1.0, TOLERANCE)
- @object.send(:cos, 0).should be_close(1.0, TOLERANCE)
- @object.send(:cos, CMath::PI/2).should be_close(0.0, TOLERANCE)
- @object.send(:cos, 3*Math::PI/2).should be_close(0.0, TOLERANCE)
- @object.send(:cos, 2*Math::PI).should be_close(1.0, TOLERANCE)
- end
-
- it "returns the cosine for Complex numbers" do
- @object.send(:cos, Complex(0, CMath::PI)).should be_close(Complex(11.5919532755215, 0.0), TOLERANCE)
- @object.send(:cos, Complex(3, 4)).should be_close(Complex(-27.0349456030742, -3.85115333481178), TOLERANCE)
- end
-end
-
-describe :complex_math_cos_bang, shared: true do
- it "returns the cosine of the argument expressed in radians" do
- @object.send(:cos!, CMath::PI).should be_close(-1.0, TOLERANCE)
- @object.send(:cos!, 0).should be_close(1.0, TOLERANCE)
- @object.send(:cos!, CMath::PI/2).should be_close(0.0, TOLERANCE)
- @object.send(:cos!, 3*Math::PI/2).should be_close(0.0, TOLERANCE)
- @object.send(:cos!, 2*Math::PI).should be_close(1.0, TOLERANCE)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:cos!, Complex(3, 4)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/cosh.rb b/spec/ruby/library/cmath/math/shared/cosh.rb
deleted file mode 100644
index 7cf561c985..0000000000
--- a/spec/ruby/library/cmath/math/shared/cosh.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_cosh, shared: true do
- it "returns the hyperbolic cosine of the passed argument" do
- @object.send(:cosh, 0.0).should == 1.0
- @object.send(:cosh, -0.0).should == 1.0
- @object.send(:cosh, 1.5).should be_close(2.35240961524325, TOLERANCE)
- @object.send(:cosh, -2.99).should be_close(9.96798496414416, TOLERANCE)
- end
-
- it "returns the hyperbolic cosine for Complex numbers" do
- @object.send(:cosh, Complex(0, CMath::PI)).should be_close(Complex(-1.0, 0.0), TOLERANCE)
- @object.send(:cosh, Complex(3, 4)).should be_close(Complex(-6.58066304055116, -7.58155274274654), TOLERANCE)
- end
-end
-
-describe :complex_math_cosh_bang, shared: true do
- it "returns the hyperbolic cosine of the passed argument" do
- @object.send(:cosh!, 0.0).should == 1.0
- @object.send(:cosh!, -0.0).should == 1.0
- @object.send(:cosh!, 1.5).should be_close(2.35240961524325, TOLERANCE)
- @object.send(:cosh!, -2.99).should be_close(9.96798496414416, TOLERANCE)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:cosh!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/exp.rb b/spec/ruby/library/cmath/math/shared/exp.rb
deleted file mode 100644
index 6715ac63d3..0000000000
--- a/spec/ruby/library/cmath/math/shared/exp.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_exp, shared: true do
- it "returns the base-e exponential of the passed argument" do
- @object.send(:exp, 0.0).should == 1.0
- @object.send(:exp, -0.0).should == 1.0
- @object.send(:exp, -1.8).should be_close(0.165298888221587, TOLERANCE)
- @object.send(:exp, 1.25).should be_close(3.49034295746184, TOLERANCE)
- end
-
- it "returns the base-e exponential for Complex numbers" do
- @object.send(:exp, Complex(0, 0)).should == Complex(1.0, 0.0)
- @object.send(:exp, Complex(1, 3)).should be_close(Complex(-2.69107861381979, 0.383603953541131), TOLERANCE)
- end
-end
-
-describe :complex_math_exp_bang, shared: true do
- it "returns the base-e exponential of the passed argument" do
- @object.send(:exp!, 0.0).should == 1.0
- @object.send(:exp!, -0.0).should == 1.0
- @object.send(:exp!, -1.8).should be_close(0.165298888221587, TOLERANCE)
- @object.send(:exp!, 1.25).should be_close(3.49034295746184, TOLERANCE)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:exp!, Complex(1, 3)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/log.rb b/spec/ruby/library/cmath/math/shared/log.rb
deleted file mode 100644
index 4b23e8c5f2..0000000000
--- a/spec/ruby/library/cmath/math/shared/log.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_log, shared: true do
- it "returns the natural logarithm of the passed argument" do
- @object.send(:log, 0.0001).should be_close(-9.21034037197618, TOLERANCE)
- @object.send(:log, 0.000000000001e-15).should be_close(-62.1697975108392, TOLERANCE)
- @object.send(:log, 1).should be_close(0.0, TOLERANCE)
- @object.send(:log, 10).should be_close( 2.30258509299405, TOLERANCE)
- @object.send(:log, 10e15).should be_close(36.8413614879047, TOLERANCE)
- end
-
- it "returns the natural logarithm for Complex numbers" do
- @object.send(:log, Complex(3, 4)).should be_close(Complex(1.6094379124341, 0.927295218001612), TOLERANCE)
- @object.send(:log, Complex(-3, 4)).should be_close(Complex(1.6094379124341, 2.21429743558818), TOLERANCE)
- end
-
- it "returns the natural logarithm for negative numbers as a Complex number" do
- @object.send(:log, -10).should be_close(Complex(2.30258509299405, 3.14159265358979), TOLERANCE)
- @object.send(:log, -20).should be_close(Complex(2.99573227355399, 3.14159265358979), TOLERANCE)
- end
-end
-
-describe :complex_math_log_bang, shared: true do
- it "returns the natural logarithm of the argument" do
- @object.send(:log!, 0.0001).should be_close(-9.21034037197618, TOLERANCE)
- @object.send(:log!, 0.000000000001e-15).should be_close(-62.1697975108392, TOLERANCE)
- @object.send(:log!, 1).should be_close(0.0, TOLERANCE)
- @object.send(:log!, 10).should be_close( 2.30258509299405, TOLERANCE)
- @object.send(:log!, 10e15).should be_close(36.8413614879047, TOLERANCE)
- end
-
- it "raises an Errno::EDOM if the argument is less than 0" do
- -> { @object.send(:log!, -10) }.should raise_error(Errno::EDOM)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:log!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/log10.rb b/spec/ruby/library/cmath/math/shared/log10.rb
deleted file mode 100644
index f49934d958..0000000000
--- a/spec/ruby/library/cmath/math/shared/log10.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_log10, shared: true do
- it "returns the base-10 logarithm of the passed argument" do
- @object.send(:log10, 0.0001).should be_close(-4.0, TOLERANCE)
- @object.send(:log10, 0.000000000001e-15).should be_close(-27.0, TOLERANCE)
- @object.send(:log10, 1).should be_close(0.0, TOLERANCE)
- @object.send(:log10, 10).should be_close(1.0, TOLERANCE)
- @object.send(:log10, 10e15).should be_close(16.0, TOLERANCE)
- end
-
- it "returns the base-10 logarithm for Complex numbers" do
- @object.send(:log10, Complex(3, 4)).should be_close(Complex(0.698970004336019, 0.402719196273373), TOLERANCE)
- @object.send(:log10, Complex(-3, 4)).should be_close(Complex(0.698970004336019, 0.961657157568468), TOLERANCE)
- end
-
- # BUG: does not work correctly, because Math#log10
- # does not check for negative values
- #it "returns the base-10 logarithm for negative numbers as a Complex number" do
- # @object.send(:log10, -10).should be_close(Complex(2.30258509299405, 3.14159265358979), TOLERANCE)
- # @object.send(:log10, -20).should be_close(Complex(2.99573227355399, 3.14159265358979), TOLERANCE)
- #end
-end
-
-describe :complex_math_log10_bang, shared: true do
- it "returns the base-10 logarithm of the argument" do
- @object.send(:log10!, 0.0001).should be_close(-4.0, TOLERANCE)
- @object.send(:log10!, 0.000000000001e-15).should be_close(-27.0, TOLERANCE)
- @object.send(:log10!, 1).should be_close(0.0, TOLERANCE)
- @object.send(:log10!, 10).should be_close(1.0, TOLERANCE)
- @object.send(:log10!, 10e15).should be_close(16.0, TOLERANCE)
- end
-
- it "raises an Errno::EDOM when the passed argument is negative" do
- -> { @object.send(:log10!, -10) }.should raise_error(Errno::EDOM)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:log10!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/sin.rb b/spec/ruby/library/cmath/math/shared/sin.rb
deleted file mode 100644
index 1cb1b29cda..0000000000
--- a/spec/ruby/library/cmath/math/shared/sin.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_sin, shared: true do
- it "returns the sine of the passed argument expressed in radians" do
- @object.send(:sin, CMath::PI).should be_close(0.0, TOLERANCE)
- @object.send(:sin, 0).should be_close(0.0, TOLERANCE)
- @object.send(:sin, CMath::PI/2).should be_close(1.0, TOLERANCE)
- @object.send(:sin, 3*Math::PI/2).should be_close(-1.0, TOLERANCE)
- @object.send(:sin, 2*Math::PI).should be_close(0.0, TOLERANCE)
- end
-
- it "returns the sine for Complex numbers" do
- @object.send(:sin, Complex(0, CMath::PI)).should be_close(Complex(0.0, 11.5487393572577), TOLERANCE)
- @object.send(:sin, Complex(3, 4)).should be_close(Complex(3.85373803791938, -27.0168132580039), TOLERANCE)
- end
-end
-
-describe :complex_math_sin_bang, shared: true do
- it "returns the sine of the passed argument expressed in radians" do
- @object.send(:sin!, CMath::PI).should be_close(0.0, TOLERANCE)
- @object.send(:sin!, 0).should be_close(0.0, TOLERANCE)
- @object.send(:sin!, CMath::PI/2).should be_close(1.0, TOLERANCE)
- @object.send(:sin!, 3*Math::PI/2).should be_close(-1.0, TOLERANCE)
- @object.send(:sin!, 2*Math::PI).should be_close(0.0, TOLERANCE)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:sin!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/sinh.rb b/spec/ruby/library/cmath/math/shared/sinh.rb
deleted file mode 100644
index de80a376da..0000000000
--- a/spec/ruby/library/cmath/math/shared/sinh.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_sinh, shared: true do
- it "returns the hyperbolic sin of the argument" do
- @object.send(:sinh, 0.0).should == 0.0
- @object.send(:sinh, -0.0).should == 0.0
- @object.send(:sinh, 1.5).should be_close(2.12927945509482, TOLERANCE)
- @object.send(:sinh, -2.8).should be_close(-8.19191835423591, TOLERANCE)
- end
-
- it "returns the hyperbolic sin for Complex numbers" do
- @object.send(:sinh, Complex(0, CMath::PI)).should be_close(Complex(-0.0, 1.22464679914735e-16), TOLERANCE)
- @object.send(:sinh, Complex(3, 4)).should be_close(Complex(-6.548120040911, -7.61923172032141), TOLERANCE)
- end
-end
-
-describe :complex_math_sinh_bang, shared: true do
- it "returns the hyperbolic sin of the argument" do
- @object.send(:sinh!, 0.0).should == 0.0
- @object.send(:sinh!, -0.0).should == 0.0
- @object.send(:sinh!, 1.5).should be_close(2.12927945509482, TOLERANCE)
- @object.send(:sinh!, -2.8).should be_close(-8.19191835423591, TOLERANCE)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:sinh!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/sqrt.rb b/spec/ruby/library/cmath/math/shared/sqrt.rb
deleted file mode 100644
index 23b1ba48ff..0000000000
--- a/spec/ruby/library/cmath/math/shared/sqrt.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_sqrt, shared: true do
- it "returns the square root for positive numbers" do
- @object.send(:sqrt, 4).should == 2
- @object.send(:sqrt, 19.36).should == 4.4
- end
-
- it "returns the square root for negative numbers" do
- @object.send(:sqrt, -4).should == Complex(0, 2.0)
- @object.send(:sqrt, -19.36).should == Complex(0, 4.4)
- end
-
- it "returns the square root for Complex numbers" do
- @object.send(:sqrt, Complex(4, 5)).should be_close(Complex(2.2806933416653, 1.09615788950152), TOLERANCE)
- @object.send(:sqrt, Complex(4, -5)).should be_close(Complex(2.2806933416653, -1.09615788950152), TOLERANCE)
- end
-end
-
-describe :complex_math_sqrt_bang, shared: true do
- it "returns the square root for positive numbers" do
- @object.send(:sqrt!, 4).should == 2
- @object.send(:sqrt!, 19.36).should == 4.4
- end
-
- it "raises Errno::EDOM when the passed argument is negative" do
- -> { @object.send(:sqrt!, -4) }.should raise_error(Errno::EDOM)
- -> { @object.send(:sqrt!, -19.36) }.should raise_error(Errno::EDOM)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:sqrt!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/tan.rb b/spec/ruby/library/cmath/math/shared/tan.rb
deleted file mode 100644
index 9022c84fc9..0000000000
--- a/spec/ruby/library/cmath/math/shared/tan.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_tan, shared: true do
- it "returns the tangent of the argument" do
- @object.send(:tan, 0.0).should == 0.0
- @object.send(:tan, -0.0).should == -0.0
- @object.send(:tan, 4.22).should be_close(1.86406937682395, TOLERANCE)
- @object.send(:tan, -9.65).should be_close(-0.229109052606441, TOLERANCE)
- end
-
- it "returns the tangent for Complex numbers" do
- @object.send(:tan, Complex(0, CMath::PI)).should be_close(Complex(0.0, 0.99627207622075), TOLERANCE)
- @object.send(:tan, Complex(3, 4)).should be_close(Complex(-0.000187346204629452, 0.999355987381473), TOLERANCE)
- end
-end
-
-describe :complex_math_tan_bang, shared: true do
- it "returns the tangent of the argument" do
- @object.send(:tan!, 0.0).should == 0.0
- @object.send(:tan!, -0.0).should == -0.0
- @object.send(:tan!, 4.22).should be_close(1.86406937682395, TOLERANCE)
- @object.send(:tan!, -9.65).should be_close(-0.229109052606441, TOLERANCE)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:tan!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/shared/tanh.rb b/spec/ruby/library/cmath/math/shared/tanh.rb
deleted file mode 100644
index f2c9a5abb1..0000000000
--- a/spec/ruby/library/cmath/math/shared/tanh.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :complex_math_tanh, shared: true do
- it "returns the hyperbolic tangent of the argument" do
- @object.send(:tanh, 0.0).should == 0.0
- @object.send(:tanh, -0.0).should == -0.0
- @object.send(:tanh, infinity_value).should == 1.0
- @object.send(:tanh, -infinity_value).should == -1.0
- @object.send(:tanh, 2.5).should be_close(0.98661429815143, TOLERANCE)
- @object.send(:tanh, -4.892).should be_close(-0.999887314427707, TOLERANCE)
- end
-
- it "returns the hyperbolic tangent for Complex numbers" do
- @object.send(:tanh, Complex(0, CMath::PI)).should be_close(Complex(0.0, -1.22464679914735e-16), TOLERANCE)
- @object.send(:tanh, Complex(3, 4)).should be_close(Complex(1.00070953606723, 0.00490825806749599), TOLERANCE)
- end
-end
-
-describe :complex_math_tanh_bang, shared: true do
- it "returns the hyperbolic tangent of the argument" do
- @object.send(:tanh!, 0.0).should == 0.0
- @object.send(:tanh!, -0.0).should == -0.0
- @object.send(:tanh!, infinity_value).should == 1.0
- @object.send(:tanh!, -infinity_value).should == -1.0
- @object.send(:tanh!, 2.5).should be_close(0.98661429815143, TOLERANCE)
- @object.send(:tanh!, -4.892).should be_close(-0.999887314427707, TOLERANCE)
- end
-
- it "raises a TypeError when passed a Complex number" do
- -> { @object.send(:tanh!, Complex(4, 5)) }.should raise_error(TypeError)
- end
-end
diff --git a/spec/ruby/library/cmath/math/sin_spec.rb b/spec/ruby/library/cmath/math/sin_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/sin_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/sinh_spec.rb b/spec/ruby/library/cmath/math/sinh_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/sinh_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/sqrt_spec.rb b/spec/ruby/library/cmath/math/sqrt_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/sqrt_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/tan_spec.rb b/spec/ruby/library/cmath/math/tan_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/tan_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/cmath/math/tanh_spec.rb b/spec/ruby/library/cmath/math/tanh_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/cmath/math/tanh_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/coverage/result_spec.rb b/spec/ruby/library/coverage/result_spec.rb
index 4cc43e8462..4bcce00cd7 100644
--- a/spec/ruby/library/coverage/result_spec.rb
+++ b/spec/ruby/library/coverage/result_spec.rb
@@ -91,15 +91,51 @@ describe 'Coverage.result' do
Coverage.result.should_not include(@config_file)
end
- it 'returns the correct results when eval is used' do
- Coverage.start
- require @eval_code_file.chomp('.rb')
- result = Coverage.result
+ ruby_version_is '3.1'...'3.2' do
+ it 'returns the correct results when eval is used' do
+ Coverage.start
+ require @eval_code_file.chomp('.rb')
+ result = Coverage.result
- result.should == {
- @eval_code_file => [
- 1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1
- ]
- }
+ result.should == {
+ @eval_code_file => [
+ 1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1
+ ]
+ }
+ end
+ end
+
+ ruby_version_is '3.2' do
+ it 'indicates support for different features' do
+ Coverage.supported?(:lines).should == true
+ end
+
+ it 'returns the correct results when eval coverage is enabled' do
+ Coverage.supported?(:eval).should == true
+
+ Coverage.start(lines: true, eval: true)
+ require @eval_code_file.chomp('.rb')
+ result = Coverage.result
+
+ result.should == {
+ @eval_code_file => {
+ lines: [1, nil, 1, nil, 1, 1, nil, nil, nil, nil, 1]
+ }
+ }
+ end
+
+ it 'returns the correct results when eval coverage is enabled' do
+ Coverage.supported?(:eval).should == true
+
+ Coverage.start(lines: true, eval: false)
+ require @eval_code_file.chomp('.rb')
+ result = Coverage.result
+
+ result.should == {
+ @eval_code_file => {
+ lines: [1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1]
+ }
+ }
+ end
end
end
diff --git a/spec/ruby/library/coverage/running_spec.rb b/spec/ruby/library/coverage/running_spec.rb
new file mode 100644
index 0000000000..745270164f
--- /dev/null
+++ b/spec/ruby/library/coverage/running_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require 'coverage'
+
+describe 'Coverage.running?' do
+ it "returns false if coverage is not started" do
+ Coverage.running?.should == false
+ end
+
+ it "returns true if coverage is started" do
+ Coverage.start
+ Coverage.running?.should == true
+ Coverage.result
+ end
+
+ it "returns false if coverage was started and stopped" do
+ Coverage.start
+ Coverage.result
+ Coverage.running?.should == false
+ end
+end \ No newline at end of file
diff --git a/spec/ruby/library/coverage/start_spec.rb b/spec/ruby/library/coverage/start_spec.rb
index ddfad8b47c..a993abbf4e 100644
--- a/spec/ruby/library/coverage/start_spec.rb
+++ b/spec/ruby/library/coverage/start_spec.rb
@@ -2,5 +2,11 @@ require_relative '../../spec_helper'
require 'coverage'
describe 'Coverage.start' do
- it 'needs to be reviewed for spec completeness'
+ ruby_version_is '3.2' do
+ it "can measure coverage within eval" do
+ Coverage.start(lines: true, eval: true)
+ eval("Object.new\n"*3, binding, "test.rb", 1)
+ Coverage.result["test.rb"].should == {lines: [1, 1, 1]}
+ end
+ end
end
diff --git a/spec/ruby/library/date/civil_spec.rb b/spec/ruby/library/date/civil_spec.rb
index f3537c2f84..1c780fce56 100644
--- a/spec/ruby/library/date/civil_spec.rb
+++ b/spec/ruby/library/date/civil_spec.rb
@@ -2,11 +2,6 @@ require_relative '../../spec_helper'
require_relative 'shared/civil'
require 'date'
-describe "Date#civil" do
- it_behaves_like :date_civil, :civil
-end
-
-
describe "Date.civil" do
- it "needs to be reviewed for spec completeness"
+ it_behaves_like :date_civil, :civil
end
diff --git a/spec/ruby/library/datetime/to_time_spec.rb b/spec/ruby/library/datetime/to_time_spec.rb
index a11b6e30e1..95eca864da 100644
--- a/spec/ruby/library/datetime/to_time_spec.rb
+++ b/spec/ruby/library/datetime/to_time_spec.rb
@@ -7,10 +7,10 @@ describe "DateTime#to_time" do
end
it "returns a Time representing the same instant" do
- datetime = DateTime.civil(3, 12, 31, 23, 58, 59)
+ datetime = DateTime.civil(2012, 12, 31, 23, 58, 59)
time = datetime.to_time.utc
- time.year.should == 3
+ time.year.should == 2012
time.month.should == 12
time.day.should == 31
time.hour.should == 23
@@ -18,6 +18,20 @@ describe "DateTime#to_time" do
time.sec.should == 59
end
+ date_version = defined?(Date::VERSION) ? Date::VERSION : '0.0.0'
+ version_is(date_version, '3.2.3') do
+ it "returns a Time representing the same instant before Gregorian" do
+ datetime = DateTime.civil(1582, 10, 4, 23, 58, 59)
+ time = datetime.to_time.utc
+ time.year.should == 1582
+ time.month.should == 10
+ time.day.should == 14
+ time.hour.should == 23
+ time.min.should == 58
+ time.sec.should == 59
+ end
+ end
+
it "preserves the same time regardless of local time or zone" do
date = DateTime.new(2012, 12, 24, 12, 23, 00, '+03:00')
diff --git a/spec/ruby/library/erb/new_spec.rb b/spec/ruby/library/erb/new_spec.rb
index f18e25939e..4d7f7bf36a 100644
--- a/spec/ruby/library/erb/new_spec.rb
+++ b/spec/ruby/library/erb/new_spec.rb
@@ -138,4 +138,20 @@ END
ERB.new(@eruby_str).result
->{ ERB.new("<%= list %>").result }.should raise_error(NameError)
end
+
+ describe "warning about arguments" do
+ ruby_version_is "3.1" do
+ it "warns when passed safe_level and later arguments" do
+ -> {
+ ERB.new(@eruby_str, nil, '%')
+ }.should complain(/warning: Passing safe_level with the 2nd argument of ERB.new is deprecated. Do not use it, and specify other arguments as keyword arguments./)
+ end
+
+ it "does not warn when passed arguments as keyword argument" do
+ -> {
+ ERB.new(@eruby_str, trim_mode: '%')
+ }.should_not complain(/warning: Passing safe_level with the 2nd argument of ERB.new is deprecated. Do not use it, and specify other arguments as keyword arguments./)
+ end
+ end
+ end
end
diff --git a/spec/ruby/library/fiddle/handle/initialize_spec.rb b/spec/ruby/library/fiddle/handle/initialize_spec.rb
new file mode 100644
index 0000000000..51c2470efd
--- /dev/null
+++ b/spec/ruby/library/fiddle/handle/initialize_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'fiddle'
+
+describe "Fiddle::Handle#initialize" do
+ it "raises Fiddle::DLError if the library cannot be found" do
+ -> {
+ Fiddle::Handle.new("doesnotexist.doesnotexist")
+ }.should raise_error(Fiddle::DLError)
+ end
+end
diff --git a/spec/ruby/library/io-wait/wait_readable_spec.rb b/spec/ruby/library/io-wait/wait_readable_spec.rb
new file mode 100644
index 0000000000..06ffbda5c8
--- /dev/null
+++ b/spec/ruby/library/io-wait/wait_readable_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+ruby_version_is ''...'3.2' do
+ require 'io/wait'
+end
+
+describe "IO#wait_readable" do
+ before :each do
+ @io = File.new(__FILE__ )
+ end
+
+ after :each do
+ @io.close
+ end
+
+ it "waits for the IO to become readable with no timeout" do
+ @io.wait_readable.should == @io
+ end
+
+ it "waits for the IO to become readable with the given timeout" do
+ @io.wait_readable(1).should == @io
+ end
+
+ it "waits for the IO to become readable with the given large timeout" do
+ @io.wait_readable(365 * 24 * 60 * 60).should == @io
+ end
+end
diff --git a/spec/ruby/library/io-wait/wait_writable_spec.rb b/spec/ruby/library/io-wait/wait_writable_spec.rb
new file mode 100644
index 0000000000..8c44780d39
--- /dev/null
+++ b/spec/ruby/library/io-wait/wait_writable_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+
+ruby_version_is ''...'3.2' do
+ require 'io/wait'
+end
+
+describe "IO#wait_writable" do
+ it "waits for the IO to become writable with no timeout" do
+ STDOUT.wait_writable.should == STDOUT
+ end
+
+ it "waits for the IO to become writable with the given timeout" do
+ STDOUT.wait_writable(1).should == STDOUT
+ end
+
+ it "waits for the IO to become writable with the given large timeout" do
+ # Represents one year and is larger than a 32-bit int
+ STDOUT.wait_writable(365 * 24 * 60 * 60).should == STDOUT
+ end
+end
diff --git a/spec/ruby/library/objectspace/fixtures/trace.rb b/spec/ruby/library/objectspace/fixtures/trace.rb
new file mode 100644
index 0000000000..fd4524b0ba
--- /dev/null
+++ b/spec/ruby/library/objectspace/fixtures/trace.rb
@@ -0,0 +1,5 @@
+require "objspace/trace"
+a = "foo"
+b = "b" + "a" + "r"
+c = 42
+p a, b, c
diff --git a/spec/ruby/library/objectspace/trace_spec.rb b/spec/ruby/library/objectspace/trace_spec.rb
new file mode 100644
index 0000000000..59952a006c
--- /dev/null
+++ b/spec/ruby/library/objectspace/trace_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.1" do
+ describe 'require "objspace/trace"' do
+ it "shows object allocation sites" do
+ file = fixture(__FILE__ , "trace.rb")
+ ruby_exe(file, args: "2>&1").lines(chomp: true).should == [
+ "objspace/trace is enabled",
+ "\"foo\" @ #{file}:2",
+ "\"bar\" @ #{file}:3",
+ "42"
+ ]
+ end
+ end
+end
diff --git a/spec/ruby/library/openssl/x509/name/verify_spec.rb b/spec/ruby/library/openssl/x509/name/verify_spec.rb
index a8bf865bda..6dcfc99466 100644
--- a/spec/ruby/library/openssl/x509/name/verify_spec.rb
+++ b/spec/ruby/library/openssl/x509/name/verify_spec.rb
@@ -12,7 +12,7 @@ describe "OpenSSL::X509::Name.verify" do
cert.public_key = key.public_key
cert.not_before = Time.now - 10
cert.not_after = cert.not_before + 365 * 24 * 60 * 60
- cert.sign key, OpenSSL::Digest.new('SHA1')
+ cert.sign key, OpenSSL::Digest.new('SHA256')
store = OpenSSL::X509::Store.new
store.add_cert(cert)
[store.verify(cert), store.error, store.error_string].should == [true, 0, "ok"]
@@ -28,7 +28,7 @@ describe "OpenSSL::X509::Name.verify" do
cert.public_key = key.public_key
cert.not_before = Time.now - 10
cert.not_after = Time.now - 5
- cert.sign key, OpenSSL::Digest.new('SHA1')
+ cert.sign key, OpenSSL::Digest.new('SHA256')
store = OpenSSL::X509::Store.new
store.add_cert(cert)
store.verify(cert).should == false
diff --git a/spec/ruby/library/rbconfig/unicode_emoji_version_spec.rb b/spec/ruby/library/rbconfig/unicode_emoji_version_spec.rb
index a4a24c535d..3dc9900127 100644
--- a/spec/ruby/library/rbconfig/unicode_emoji_version_spec.rb
+++ b/spec/ruby/library/rbconfig/unicode_emoji_version_spec.rb
@@ -16,8 +16,8 @@ describe "RbConfig::CONFIG['UNICODE_EMOJI_VERSION']" do
# Caution: ruby_version_is means is_or_later
ruby_version_is "3.2" do
- it "is 14.0" do
- RbConfig::CONFIG['UNICODE_EMOJI_VERSION'].should == "14.0"
+ it "is 15.0" do
+ RbConfig::CONFIG['UNICODE_EMOJI_VERSION'].should == "15.0"
end
end
end
diff --git a/spec/ruby/library/rbconfig/unicode_version_spec.rb b/spec/ruby/library/rbconfig/unicode_version_spec.rb
index d0ff856764..458f13bf03 100644
--- a/spec/ruby/library/rbconfig/unicode_version_spec.rb
+++ b/spec/ruby/library/rbconfig/unicode_version_spec.rb
@@ -16,8 +16,8 @@ describe "RbConfig::CONFIG['UNICODE_VERSION']" do
# Caution: ruby_version_is means is_or_later
ruby_version_is "3.2" do
- it "is 14.0.0" do
- RbConfig::CONFIG['UNICODE_VERSION'].should == "14.0.0"
+ it "is 15.0.0" do
+ RbConfig::CONFIG['UNICODE_VERSION'].should == "15.0.0"
end
end
end
diff --git a/spec/ruby/library/scanf/io/block_scanf_spec.rb b/spec/ruby/library/scanf/io/block_scanf_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/scanf/io/block_scanf_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/scanf/io/fixtures/date.txt b/spec/ruby/library/scanf/io/fixtures/date.txt
deleted file mode 100644
index a1bd635c0c..0000000000
--- a/spec/ruby/library/scanf/io/fixtures/date.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Beethoven 1770
-Bach 1685
-Handel 1685
-
diff --git a/spec/ruby/library/scanf/io/fixtures/helloworld.txt b/spec/ruby/library/scanf/io/fixtures/helloworld.txt
deleted file mode 100644
index 3b18e512db..0000000000
--- a/spec/ruby/library/scanf/io/fixtures/helloworld.txt
+++ /dev/null
@@ -1 +0,0 @@
-hello world
diff --git a/spec/ruby/library/scanf/io/scanf_spec.rb b/spec/ruby/library/scanf/io/scanf_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/scanf/io/scanf_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/scanf/io/shared/block_scanf.rb b/spec/ruby/library/scanf/io/shared/block_scanf.rb
deleted file mode 100644
index d938f43734..0000000000
--- a/spec/ruby/library/scanf/io/shared/block_scanf.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require 'scanf'
-
-describe :scanf_io_block_scanf, shared: true do
- before :each do
- @data = File.open(fixture(__FILE__, 'date.txt'), 'rb')
- end
-
- after :each do
- @data.close unless @data.closed?
- end
-
- it "passes each match to the block as an array" do
- res = @data.send(@method, "%s%d") { |name, year| "#{name} was born in #{year}." }
- res.should == ["Beethoven was born in 1770.", "Bach was born in 1685.", "Handel was born in 1685."]
- end
-
- it "keeps scanning the input and cycling back to the beginning of the input string" do
- a = []
- @data.send(@method, "%s"){|w| a << w}
- a.should == [["Beethoven"], ["1770"], ["Bach"], ["1685"], ["Handel"], ["1685"]]
- end
-
- it "returns an empty array when a wrong specifier is passed" do
- a = []
- @data.send(@method, "%z"){|w| a << w}
- a.empty?.should be_true
- end
-end
diff --git a/spec/ruby/library/scanf/string/block_scanf_spec.rb b/spec/ruby/library/scanf/string/block_scanf_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/scanf/string/block_scanf_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/scanf/string/scanf_spec.rb b/spec/ruby/library/scanf/string/scanf_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/scanf/string/scanf_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/scanf/string/shared/block_scanf.rb b/spec/ruby/library/scanf/string/shared/block_scanf.rb
deleted file mode 100644
index 25ab3f442a..0000000000
--- a/spec/ruby/library/scanf/string/shared/block_scanf.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require 'scanf'
-
-describe :scanf_string_block_scanf, shared: true do
- it "passes each match to the block as an array" do
- a = []
- "hello world".send(@method, "%s%s"){|w| a << w}
- a.should == [["hello", "world"]]
- end
-
- it "keeps scanning the input and cycling back to the beginning of the input string" do
- a = []
- "hello world".send(@method, "%s"){|w| a << w}
- a.should == [["hello"], ["world"]]
-
- string = "123 abc 456 def 789 ghi"
- s = string.send(@method, "%d%s"){|num,str| [num * 2, str.upcase]}
- s.should == [[246, "ABC"], [912, "DEF"], [1578, "GHI"]]
- end
-
- it "returns an empty array when a wrong specifier is passed" do
- a = []
- "hello world".send(@method, "%z"){|w| a << w}
- a.empty?.should be_true
- end
-end
diff --git a/spec/ruby/library/socket/addrinfo/initialize_spec.rb b/spec/ruby/library/socket/addrinfo/initialize_spec.rb
index 00250439fd..83b204b575 100644
--- a/spec/ruby/library/socket/addrinfo/initialize_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/initialize_spec.rb
@@ -91,7 +91,7 @@ describe "Addrinfo#initialize" do
@addrinfo.afamily.should == Socket::AF_INET6
end
- it "returns the 0 socket type" do
+ it "returns the specified socket type" do
@addrinfo.socktype.should == Socket::SOCK_STREAM
end
diff --git a/spec/ruby/library/socket/shared/pack_sockaddr.rb b/spec/ruby/library/socket/shared/pack_sockaddr.rb
index 2df09027c9..9f6238e7bc 100644
--- a/spec/ruby/library/socket/shared/pack_sockaddr.rb
+++ b/spec/ruby/library/socket/shared/pack_sockaddr.rb
@@ -19,6 +19,13 @@ describe :socket_pack_sockaddr_in, shared: true do
Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '127.0.0.1']
end
+ platform_is_not :solaris do
+ it 'resolves the service name to a port' do
+ sockaddr_in = Socket.public_send(@method, 'http', '127.0.0.1')
+ Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1']
+ end
+ end
+
describe 'using an IPv4 address' do
it 'returns a String of 16 bytes' do
str = Socket.public_send(@method, 80, '127.0.0.1')
diff --git a/spec/ruby/library/socket/tcpsocket/shared/new.rb b/spec/ruby/library/socket/tcpsocket/shared/new.rb
index 4189acc2f8..e7eb2f3c13 100644
--- a/spec/ruby/library/socket/tcpsocket/shared/new.rb
+++ b/spec/ruby/library/socket/tcpsocket/shared/new.rb
@@ -14,7 +14,7 @@ describe :tcpsocket_new, shared: true do
}
end
- ruby_version_is "3.0" do
+ ruby_version_is "3.0"..."3.1" do
it 'raises Errno::ETIMEDOUT with :connect_timeout when no server is listening on the given address' do
-> {
TCPSocket.send(@method, "192.0.2.1", 80, connect_timeout: 0)
@@ -22,6 +22,14 @@ describe :tcpsocket_new, shared: true do
end
end
+ ruby_version_is "3.2" do
+ it 'raises IO::TimeoutError with :connect_timeout when no server is listening on the given address' do
+ -> {
+ TCPSocket.send(@method, "192.0.2.1", 80, connect_timeout: 0)
+ }.should raise_error(IO::TimeoutError)
+ end
+ end
+
describe "with a running server" do
before :each do
@server = SocketSpecs::SpecTCPServer.new
diff --git a/spec/ruby/library/stringio/initialize_spec.rb b/spec/ruby/library/stringio/initialize_spec.rb
index 1e8096e3bb..c597e328d3 100644
--- a/spec/ruby/library/stringio/initialize_spec.rb
+++ b/spec/ruby/library/stringio/initialize_spec.rb
@@ -163,6 +163,91 @@ describe "StringIO#initialize when passed [Object]" do
end
end
+# NOTE: Synchronise with core/io/new_spec.rb (core/io/shared/new.rb)
+describe "StringIO#initialize when passed keyword arguments" do
+ it "sets the mode based on the passed :mode option" do
+ io = StringIO.new("example", "r")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_true
+ end
+
+ it "accepts a mode argument set to nil with a valid :mode option" do
+ @io = StringIO.new('', nil, mode: "w")
+ @io.write("foo").should == 3
+ end
+
+ it "accepts a mode argument with a :mode option set to nil" do
+ @io = StringIO.new('', "w", mode: nil)
+ @io.write("foo").should == 3
+ end
+
+ it "sets binmode from :binmode option" do
+ @io = StringIO.new('', 'w', binmode: true)
+ @io.external_encoding.to_s.should == "ASCII-8BIT" # #binmode? isn't implemented in StringIO
+ end
+
+ it "does not set binmode from false :binmode" do
+ @io = StringIO.new('', 'w', binmode: false)
+ @io.external_encoding.to_s.should == "UTF-8" # #binmode? isn't implemented in StringIO
+ end
+end
+
+# NOTE: Synchronise with core/io/new_spec.rb (core/io/shared/new.rb)
+describe "StringIO#initialize when passed keyword arguments and error happens" do
+ it "raises an error if passed encodings two ways" do
+ -> {
+ @io = StringIO.new('', 'w:ISO-8859-1', encoding: 'ISO-8859-1')
+ }.should raise_error(ArgumentError)
+ -> {
+ @io = StringIO.new('', 'w:ISO-8859-1', external_encoding: 'ISO-8859-1')
+ }.should raise_error(ArgumentError)
+ -> {
+ @io = StringIO.new('', 'w:ISO-8859-1:UTF-8', internal_encoding: 'ISO-8859-1')
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if passed matching binary/text mode two ways" do
+ -> {
+ @io = StringIO.new('', "wb", binmode: true)
+ }.should raise_error(ArgumentError)
+ -> {
+ @io = StringIO.new('', "wt", textmode: true)
+ }.should raise_error(ArgumentError)
+
+ -> {
+ @io = StringIO.new('', "wb", textmode: false)
+ }.should raise_error(ArgumentError)
+ -> {
+ @io = StringIO.new('', "wt", binmode: false)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if passed conflicting binary/text mode two ways" do
+ -> {
+ @io = StringIO.new('', "wb", binmode: false)
+ }.should raise_error(ArgumentError)
+ -> {
+ @io = StringIO.new('', "wt", textmode: false)
+ }.should raise_error(ArgumentError)
+
+ -> {
+ @io = StringIO.new('', "wb", textmode: true)
+ }.should raise_error(ArgumentError)
+ -> {
+ @io = StringIO.new('', "wt", binmode: true)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error when trying to set both binmode and textmode" do
+ -> {
+ @io = StringIO.new('', "w", textmode: true, binmode: true)
+ }.should raise_error(ArgumentError)
+ -> {
+ @io = StringIO.new('', File::Constants::WRONLY, textmode: true, binmode: true)
+ }.should raise_error(ArgumentError)
+ end
+end
+
describe "StringIO#initialize when passed no arguments" do
before :each do
@io = StringIO.allocate
diff --git a/spec/ruby/library/stringio/new_spec.rb b/spec/ruby/library/stringio/new_spec.rb
new file mode 100644
index 0000000000..e36d210caa
--- /dev/null
+++ b/spec/ruby/library/stringio/new_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'stringio'
+
+describe "StringIO.new" do
+ it "warns when called with a block" do
+ -> { eval("StringIO.new {}") }.should complain(/StringIO::new\(\) does not take block; use StringIO::open\(\) instead/)
+ end
+end \ No newline at end of file
diff --git a/spec/ruby/library/stringio/open_spec.rb b/spec/ruby/library/stringio/open_spec.rb
index acab6e9056..3068e19435 100644
--- a/spec/ruby/library/stringio/open_spec.rb
+++ b/spec/ruby/library/stringio/open_spec.rb
@@ -167,10 +167,14 @@ describe "StringIO.open when passed [Object]" do
io.should equal(ret)
end
- it "sets the mode to read-write" do
+ it "sets the mode to read-write (r+)" do
io = StringIO.open("example")
io.closed_read?.should be_false
io.closed_write?.should be_false
+
+ io = StringIO.new("example")
+ io.printf("%d", 123)
+ io.string.should == "123mple"
end
it "tries to convert the passed Object to a String using #to_str" do
@@ -195,10 +199,14 @@ describe "StringIO.open when passed no arguments" do
io.should equal(ret)
end
- it "sets the mode to read-write" do
+ it "sets the mode to read-write (r+)" do
io = StringIO.open
io.closed_read?.should be_false
io.closed_write?.should be_false
+
+ io = StringIO.new("example")
+ io.printf("%d", 123)
+ io.string.should == "123mple"
end
it "uses an empty String as the StringIO backend" do
diff --git a/spec/ruby/library/stringio/printf_spec.rb b/spec/ruby/library/stringio/printf_spec.rb
index 9dd1a3b410..f3f669a185 100644
--- a/spec/ruby/library/stringio/printf_spec.rb
+++ b/spec/ruby/library/stringio/printf_spec.rb
@@ -4,7 +4,7 @@ require_relative '../../core/kernel/shared/sprintf'
describe "StringIO#printf" do
before :each do
- @io = StringIO.new('example')
+ @io = StringIO.new()
end
it "returns nil" do
@@ -12,9 +12,9 @@ describe "StringIO#printf" do
end
it "pads self with \\000 when the current position is after the end" do
- @io.pos = 10
+ @io.pos = 3
@io.printf("%d", 123)
- @io.string.should == "example\000\000\000123"
+ @io.string.should == "\000\000\000123"
end
it "performs format conversion" do
@@ -39,6 +39,27 @@ describe "StringIO#printf" do
end
end
+describe "StringIO#printf when in read-write mode" do
+ before :each do
+ @io = StringIO.new("example", "r+")
+ end
+
+ it "starts from the beginning" do
+ @io.printf("%s", "abcdefghijk")
+ @io.string.should == "abcdefghijk"
+ end
+
+ it "does not truncate existing string" do
+ @io.printf("%s", "abc")
+ @io.string.should == "abcmple"
+ end
+
+ it "correctly updates self's position" do
+ @io.printf("%s", "abc")
+ @io.pos.should eql(3)
+ end
+end
+
describe "StringIO#printf when in append mode" do
before :each do
@io = StringIO.new("example", "a")
diff --git a/spec/ruby/library/stringio/putc_spec.rb b/spec/ruby/library/stringio/putc_spec.rb
index 223b3523e5..1ce53b7ef2 100644
--- a/spec/ruby/library/stringio/putc_spec.rb
+++ b/spec/ruby/library/stringio/putc_spec.rb
@@ -35,6 +35,21 @@ describe "StringIO#putc when passed [String]" do
@io.putc("t")
@io.pos.should == 3
end
+
+ it "handles concurrent writes correctly" do
+ @io = StringIO.new
+ n = 8
+ go = false
+ threads = n.times.map { |i|
+ Thread.new {
+ Thread.pass until go
+ @io.putc i.to_s
+ }
+ }
+ go = true
+ threads.each(&:join)
+ @io.string.size.should == n
+ end
end
describe "StringIO#putc when passed [Object]" do
diff --git a/spec/ruby/library/stringio/puts_spec.rb b/spec/ruby/library/stringio/puts_spec.rb
index a9f289a5a5..9c890262dd 100644
--- a/spec/ruby/library/stringio/puts_spec.rb
+++ b/spec/ruby/library/stringio/puts_spec.rb
@@ -101,6 +101,20 @@ describe "StringIO#puts when passed 1 or more objects" do
@io.puts ''
@io.string.should == "\n"
end
+
+ it "handles concurrent writes correctly" do
+ n = 8
+ go = false
+ threads = n.times.map { |i|
+ Thread.new {
+ Thread.pass until go
+ @io.puts i
+ }
+ }
+ go = true
+ threads.each(&:join)
+ @io.string.size.should == n.times.map { |i| "#{i}\n" }.join.size
+ end
end
describe "StringIO#puts when passed no arguments" do
diff --git a/spec/ruby/library/stringio/read_nonblock_spec.rb b/spec/ruby/library/stringio/read_nonblock_spec.rb
index 2a8f926bd0..d4ec56d9aa 100644
--- a/spec/ruby/library/stringio/read_nonblock_spec.rb
+++ b/spec/ruby/library/stringio/read_nonblock_spec.rb
@@ -5,10 +5,21 @@ require_relative 'shared/sysread'
describe "StringIO#read_nonblock when passed length, buffer" do
it_behaves_like :stringio_read, :read_nonblock
+
+ it "accepts :exception option" do
+ io = StringIO.new("example")
+ io.read_nonblock(3, buffer = "", exception: true)
+ buffer.should == "exa"
+ end
end
describe "StringIO#read_nonblock when passed length" do
it_behaves_like :stringio_read_length, :read_nonblock
+
+ it "accepts :exception option" do
+ io = StringIO.new("example")
+ io.read_nonblock(3, exception: true).should == "exa"
+ end
end
describe "StringIO#read_nonblock when passed nil" do
diff --git a/spec/ruby/library/stringio/shared/read.rb b/spec/ruby/library/stringio/shared/read.rb
index c60677bba7..252a85d89d 100644
--- a/spec/ruby/library/stringio/shared/read.rb
+++ b/spec/ruby/library/stringio/shared/read.rb
@@ -89,6 +89,12 @@ describe :stringio_read_no_arguments, shared: true do
@io.send(@method)
@io.pos.should eql(7)
end
+
+ it "correctly update the current position in bytes when multi-byte characters are used" do
+ @io.print("example\u03A3") # Overwrite the original string with 8 characters containing 9 bytes.
+ @io.send(@method)
+ @io.pos.should eql(9)
+ end
end
describe :stringio_read_nil, shared: true do
diff --git a/spec/ruby/library/stringio/shared/write.rb b/spec/ruby/library/stringio/shared/write.rb
index 0eb71466e3..d9c21028e0 100644
--- a/spec/ruby/library/stringio/shared/write.rb
+++ b/spec/ruby/library/stringio/shared/write.rb
@@ -45,12 +45,49 @@ describe :stringio_write_string, shared: true do
@io.pos.should eql(4)
end
+ it "handles concurrent writes correctly" do
+ @io = StringIO.new
+ n = 8
+ go = false
+ threads = n.times.map { |i|
+ Thread.new {
+ Thread.pass until go
+ @io.write i.to_s
+ }
+ }
+ go = true
+ threads.each(&:join)
+ @io.string.size.should == n.times.map(&:to_s).join.size
+ end
+
ruby_version_is ""..."3.0" do
it "does not taint self when the passed argument is tainted" do
@io.send(@method, "test".taint)
@io.tainted?.should be_false
end
end
+
+ it "handles writing non-ASCII UTF-8 after seek" do
+ @io.binmode
+ @io << "\x80"
+ @io.pos = 0
+ @io << "\x81"
+ @io.string.should == "\x812345".b
+ end
+
+ it "handles writing with position < buffer size" do
+ @io.pos = 2
+ @io.write "abc"
+ @io.string.should == "12abc"
+
+ @io.pos = 2
+ @io.write "de"
+ @io.string.should == "12dec"
+
+ @io.pos = 2
+ @io.write "fghi"
+ @io.string.should == "12fghi"
+ end
end
describe :stringio_write_not_writable, shared: true do
diff --git a/spec/ruby/library/stringio/write_nonblock_spec.rb b/spec/ruby/library/stringio/write_nonblock_spec.rb
index 4f4c5039fe..a457b97667 100644
--- a/spec/ruby/library/stringio/write_nonblock_spec.rb
+++ b/spec/ruby/library/stringio/write_nonblock_spec.rb
@@ -8,6 +8,12 @@ end
describe "StringIO#write_nonblock when passed [String]" do
it_behaves_like :stringio_write_string, :write_nonblock
+
+ it "accepts :exception option" do
+ io = StringIO.new("12345", "a")
+ io.write_nonblock("67890", exception: true)
+ io.string.should == "1234567890"
+ end
end
describe "StringIO#write_nonblock when self is not writable" do
diff --git a/spec/ruby/library/time/to_datetime_spec.rb b/spec/ruby/library/time/to_datetime_spec.rb
index 0e37a61108..6025950b59 100644
--- a/spec/ruby/library/time/to_datetime_spec.rb
+++ b/spec/ruby/library/time/to_datetime_spec.rb
@@ -3,9 +3,9 @@ require 'time'
describe "Time#to_datetime" do
it "returns a DateTime representing the same instant" do
- time = Time.utc(3, 12, 31, 23, 58, 59)
+ time = Time.utc(2012, 12, 31, 23, 58, 59)
datetime = time.to_datetime
- datetime.year.should == 3
+ datetime.year.should == 2012
datetime.month.should == 12
datetime.day.should == 31
datetime.hour.should == 23
@@ -13,6 +13,20 @@ describe "Time#to_datetime" do
datetime.sec.should == 59
end
+ date_version = defined?(Date::VERSION) ? Date::VERSION : '0.0.0'
+ version_is(date_version, '3.2.3') do
+ it "returns a DateTime representing the same instant before Gregorian" do
+ time = Time.utc(1582, 10, 14, 23, 58, 59)
+ datetime = time.to_datetime
+ datetime.year.should == 1582
+ datetime.month.should == 10
+ datetime.day.should == 4
+ datetime.hour.should == 23
+ datetime.min.should == 58
+ datetime.sec.should == 59
+ end
+ end
+
it "roundtrips" do
time = Time.utc(3, 12, 31, 23, 58, 59)
datetime = time.to_datetime
diff --git a/spec/ruby/library/zlib/deflate/deflate_spec.rb b/spec/ruby/library/zlib/deflate/deflate_spec.rb
index 828880f8d8..50a563ef6f 100644
--- a/spec/ruby/library/zlib/deflate/deflate_spec.rb
+++ b/spec/ruby/library/zlib/deflate/deflate_spec.rb
@@ -58,6 +58,11 @@ describe "Zlib::Deflate#deflate" do
Array.new(31, 0) +
[24, 128, 0, 0, 1]).pack('C*')
end
+
+ it "has a binary encoding" do
+ @deflator.deflate("").encoding.should == Encoding::BINARY
+ @deflator.finish.encoding.should == Encoding::BINARY
+ end
end
describe "Zlib::Deflate#deflate" do
diff --git a/spec/ruby/library/zlib/inflate/inflate_spec.rb b/spec/ruby/library/zlib/inflate/inflate_spec.rb
index cc33bd4c32..79b72bf91c 100644
--- a/spec/ruby/library/zlib/inflate/inflate_spec.rb
+++ b/spec/ruby/library/zlib/inflate/inflate_spec.rb
@@ -39,6 +39,13 @@ describe "Zlib::Inflate#inflate" do
@inflator.finish.should == 'uncompressed_data'
end
+ it "has a binary encoding" do
+ data = [120, 156, 99, 96, 128, 1, 0, 0, 10, 0, 1].pack('C*')
+ unzipped = @inflator.inflate data
+ @inflator.finish.encoding.should == Encoding::BINARY
+ unzipped.encoding.should == Encoding::BINARY
+ end
+
end
describe "Zlib::Inflate.inflate" do
diff --git a/spec/ruby/optional/capi/class_spec.rb b/spec/ruby/optional/capi/class_spec.rb
index 8e678c9111..66af381243 100644
--- a/spec/ruby/optional/capi/class_spec.rb
+++ b/spec/ruby/optional/capi/class_spec.rb
@@ -204,6 +204,10 @@ describe "C-API Class function" do
it "returns a string for an anonymous class" do
@s.rb_class2name(Class.new).should be_kind_of(String)
end
+
+ it "returns a string beginning with # for an anonymous class" do
+ @s.rb_class2name(Struct.new(:x, :y).new(1, 2).class).should.start_with?('#')
+ end
end
describe "rb_class_path" do
@@ -319,6 +323,15 @@ describe "C-API Class function" do
@s.rb_define_class("ClassSpecDefineClass4", nil)
}.should raise_error(ArgumentError)
end
+
+ it "allows arbitrary names, including constant names not valid in Ruby" do
+ cls = @s.rb_define_class("_INVALID_CLASS", CApiClassSpecs::Super)
+ cls.name.should == "_INVALID_CLASS"
+
+ -> {
+ Object.const_get(cls.name)
+ }.should raise_error(NameError, /wrong constant name/)
+ end
end
describe "rb_define_class_under" do
@@ -363,6 +376,15 @@ describe "C-API Class function" do
it "raises a TypeError if class is defined and its superclass mismatches the given one" do
-> { @s.rb_define_class_under(CApiClassSpecs, "Sub", Object) }.should raise_error(TypeError)
end
+
+ it "allows arbitrary names, including constant names not valid in Ruby" do
+ cls = @s.rb_define_class_under(CApiClassSpecs, "_INVALID_CLASS", CApiClassSpecs::Super)
+ cls.name.should == "CApiClassSpecs::_INVALID_CLASS"
+
+ -> {
+ CApiClassSpecs.const_get(cls.name)
+ }.should raise_error(NameError, /wrong constant name/)
+ end
end
describe "rb_define_class_id_under" do
@@ -390,6 +412,15 @@ describe "C-API Class function" do
it "raises a TypeError if class is defined and its superclass mismatches the given one" do
-> { @s.rb_define_class_id_under(CApiClassSpecs, :Sub, Object) }.should raise_error(TypeError)
end
+
+ it "allows arbitrary names, including constant names not valid in Ruby" do
+ cls = @s.rb_define_class_id_under(CApiClassSpecs, :_INVALID_CLASS2, CApiClassSpecs::Super)
+ cls.name.should == "CApiClassSpecs::_INVALID_CLASS2"
+
+ -> {
+ CApiClassSpecs.const_get(cls.name)
+ }.should raise_error(NameError, /wrong constant name/)
+ end
end
describe "rb_define_class_variable" do
diff --git a/spec/ruby/optional/capi/encoding_spec.rb b/spec/ruby/optional/capi/encoding_spec.rb
index e18108c022..aa632b963b 100644
--- a/spec/ruby/optional/capi/encoding_spec.rb
+++ b/spec/ruby/optional/capi/encoding_spec.rb
@@ -63,6 +63,48 @@ describe "C-API Encoding function" do
end
end
+ describe "rb_enc_strlen" do
+ before :each do
+ @str = 'こにちわ' # Each codepoint in this string is 3 bytes in UTF-8
+ end
+
+ it "returns the correct string length for the encoding" do
+ @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_8).should == 4
+ @s.rb_enc_strlen(@str, @str.bytesize, Encoding::BINARY).should == 12
+ end
+
+ it "returns the string length based on a fixed-width encoding's character length, even if the encoding is incompatible" do
+ @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_16BE).should == 6
+ @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_16LE).should == 6
+ @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_32BE).should == 3
+ @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_32LE).should == 3
+ end
+
+ it "does not consider strings to be NUL-terminated" do
+ s = "abc\0def"
+ @s.rb_enc_strlen(s, s.bytesize, Encoding::US_ASCII).should == 7
+ @s.rb_enc_strlen(s, s.bytesize, Encoding::UTF_8).should == 7
+ end
+
+ describe "handles broken strings" do
+ it "combines valid character and invalid character counts in UTF-8" do
+ # The result is 3 because `rb_enc_strlen` counts the first valid character and then adds
+ # the byte count for the invalid character that follows for 1 + 2.
+ @s.rb_enc_strlen(@str, 5, Encoding::UTF_8).should == 3
+ end
+
+ it "combines valid character and invalid character counts in UTF-16" do
+ @s.rb_enc_strlen(@str, 5, Encoding::UTF_16BE).should == 3
+ end
+
+ it "rounds up for fixed-width encodings" do
+ @s.rb_enc_strlen(@str, 7, Encoding::UTF_32BE).should == 2
+ @s.rb_enc_strlen(@str, 7, Encoding::UTF_32LE).should == 2
+ @s.rb_enc_strlen(@str, 5, Encoding::BINARY).should == 5
+ end
+ end
+ end
+
describe "rb_enc_find" do
it "returns the encoding of an Encoding" do
@s.rb_enc_find("UTF-8").should == "UTF-8"
@@ -128,10 +170,16 @@ describe "C-API Encoding function" do
describe "rb_enc_mbc_to_codepoint" do
it "returns the correct codepoint for the given character and size" do
- @s.rb_enc_mbc_to_codepoint("é", 2).should == 0x00E9
- @s.rb_enc_mbc_to_codepoint("éa", 2).should == 0x00E9
- @s.rb_enc_mbc_to_codepoint("éa", 1).should == 0xC3
- @s.rb_enc_mbc_to_codepoint("éa", 3).should == 0x00E9
+ @s.rb_enc_mbc_to_codepoint("é").should == 0xE9
+ end
+
+ it "returns 0 if p == e" do
+ @s.rb_enc_mbc_to_codepoint("").should == 0
+ end
+
+ it "returns the raw byte if incomplete character in UTF-8" do
+ @s.rb_enc_mbc_to_codepoint("\xC3").should == 0xC3
+ @s.rb_enc_mbc_to_codepoint("\x80").should == 0x80
end
end
@@ -630,6 +678,7 @@ describe "C-API Encoding function" do
it "returns the correct case fold for the given string" do
@s.ONIGENC_MBC_CASE_FOLD("lower").should == ["l", 1]
@s.ONIGENC_MBC_CASE_FOLD("Upper").should == ["u", 1]
+ @s.ONIGENC_MBC_CASE_FOLD("ABC"[1..-1]).should == ["b", 1]
end
it "works with other encodings" do
diff --git a/spec/ruby/optional/capi/ext/encoding_spec.c b/spec/ruby/optional/capi/ext/encoding_spec.c
index 68c4161bab..a0136530f2 100644
--- a/spec/ruby/optional/capi/ext/encoding_spec.c
+++ b/spec/ruby/optional/capi/ext/encoding_spec.c
@@ -71,11 +71,9 @@ static VALUE encoding_spec_rb_default_external_encoding(VALUE self) {
return rb_str_new2(enc->name);
}
-#ifdef RUBY_VERSION_IS_2_6
static VALUE encoding_spec_rb_enc_alias(VALUE self, VALUE alias, VALUE orig) {
return INT2NUM(rb_enc_alias(RSTRING_PTR(alias), RSTRING_PTR(orig)));
}
-#endif
static VALUE encoding_spec_rb_enc_associate(VALUE self, VALUE obj, VALUE enc) {
return rb_enc_associate(obj, NIL_P(enc) ? NULL : rb_enc_find(RSTRING_PTR(enc)));
@@ -120,10 +118,9 @@ static VALUE encoding_spec_rb_enc_from_index(VALUE self, VALUE index) {
return rb_str_new2(rb_enc_from_index(NUM2INT(index))->name);
}
-static VALUE encoding_spec_rb_enc_mbc_to_codepoint(VALUE self, VALUE str, VALUE offset) {
- int o = FIX2INT(offset);
+static VALUE encoding_spec_rb_enc_mbc_to_codepoint(VALUE self, VALUE str) {
char *p = RSTRING_PTR(str);
- char *e = p + o;
+ char *e = RSTRING_END(str);
return INT2FIX(rb_enc_mbc_to_codepoint(p, e, rb_enc_get(str)));
}
@@ -302,6 +299,14 @@ static VALUE encoding_spec_rb_enc_codelen(VALUE self, VALUE code, VALUE encoding
return INT2FIX(rb_enc_codelen(c, enc));
}
+static VALUE encoding_spec_rb_enc_strlen(VALUE self, VALUE str, VALUE length, VALUE encoding) {
+ int l = FIX2INT(length);
+ char *p = RSTRING_PTR(str);
+ char *e = p + l;
+
+ return LONG2FIX(rb_enc_strlen(p, e, rb_to_encoding(encoding)));
+}
+
void Init_encoding_spec(void) {
VALUE cls;
native_rb_encoding_pointer = (rb_encoding**) malloc(sizeof(rb_encoding*));
@@ -320,28 +325,22 @@ void Init_encoding_spec(void) {
rb_define_method(cls, "rb_locale_encindex", encoding_spec_rb_locale_encindex, 0);
rb_define_method(cls, "rb_filesystem_encoding", encoding_spec_rb_filesystem_encoding, 0);
rb_define_method(cls, "rb_filesystem_encindex", encoding_spec_rb_filesystem_encindex, 0);
- rb_define_method(cls, "rb_default_internal_encoding",
- encoding_spec_rb_default_internal_encoding, 0);
-
- rb_define_method(cls, "rb_default_external_encoding",
- encoding_spec_rb_default_external_encoding, 0);
-
-#ifdef RUBY_VERSION_IS_2_6
+ rb_define_method(cls, "rb_default_internal_encoding", encoding_spec_rb_default_internal_encoding, 0);
+ rb_define_method(cls, "rb_default_external_encoding", encoding_spec_rb_default_external_encoding, 0);
rb_define_method(cls, "rb_enc_alias", encoding_spec_rb_enc_alias, 2);
-#endif
-
rb_define_method(cls, "MBCLEN_CHARFOUND_P", encoding_spec_MBCLEN_CHARFOUND_P, 1);
rb_define_method(cls, "rb_enc_associate", encoding_spec_rb_enc_associate, 2);
rb_define_method(cls, "rb_enc_associate_index", encoding_spec_rb_enc_associate_index, 2);
rb_define_method(cls, "rb_enc_compatible", encoding_spec_rb_enc_compatible, 2);
rb_define_method(cls, "rb_enc_copy", encoding_spec_rb_enc_copy, 2);
rb_define_method(cls, "rb_enc_codelen", encoding_spec_rb_enc_codelen, 2);
+ rb_define_method(cls, "rb_enc_strlen", encoding_spec_rb_enc_strlen, 3);
rb_define_method(cls, "rb_enc_find", encoding_spec_rb_enc_find, 1);
rb_define_method(cls, "rb_enc_find_index", encoding_spec_rb_enc_find_index, 1);
rb_define_method(cls, "rb_enc_isalnum", encoding_spec_rb_enc_isalnum, 2);
rb_define_method(cls, "rb_enc_isspace", encoding_spec_rb_enc_isspace, 2);
rb_define_method(cls, "rb_enc_from_index", encoding_spec_rb_enc_from_index, 1);
- rb_define_method(cls, "rb_enc_mbc_to_codepoint", encoding_spec_rb_enc_mbc_to_codepoint, 2);
+ rb_define_method(cls, "rb_enc_mbc_to_codepoint", encoding_spec_rb_enc_mbc_to_codepoint, 1);
rb_define_method(cls, "rb_enc_mbcput", encoding_spec_rb_enc_mbcput, 2);
rb_define_method(cls, "rb_enc_from_encoding", encoding_spec_rb_enc_from_encoding, 1);
rb_define_method(cls, "rb_enc_get", encoding_spec_rb_enc_get, 1);
diff --git a/spec/ruby/optional/capi/ext/gc_spec.c b/spec/ruby/optional/capi/ext/gc_spec.c
index 7dc9c347c7..b323c2456d 100644
--- a/spec/ruby/optional/capi/ext/gc_spec.c
+++ b/spec/ruby/optional/capi/ext/gc_spec.c
@@ -7,6 +7,9 @@ extern "C" {
VALUE registered_tagged_value;
VALUE registered_reference_value;
+VALUE registered_before_rb_gc_register_address;
+VALUE registered_before_rb_global_variable;
+VALUE rb_gc_register_address_outside_init;
static VALUE registered_tagged_address(VALUE self) {
return registered_tagged_value;
@@ -16,6 +19,25 @@ static VALUE registered_reference_address(VALUE self) {
return registered_reference_value;
}
+static VALUE get_registered_before_rb_gc_register_address(VALUE self) {
+ return registered_before_rb_gc_register_address;
+}
+
+static VALUE get_registered_before_rb_global_variable(VALUE self) {
+ return registered_before_rb_global_variable;
+}
+
+static VALUE gc_spec_rb_gc_register_address(VALUE self) {
+ rb_gc_register_address(&rb_gc_register_address_outside_init);
+ rb_gc_register_address_outside_init = rb_str_new_cstr("rb_gc_register_address() outside Init_");
+ return rb_gc_register_address_outside_init;
+}
+
+static VALUE gc_spec_rb_gc_unregister_address(VALUE self) {
+ rb_gc_unregister_address(&rb_gc_register_address_outside_init);
+ return Qnil;
+}
+
static VALUE gc_spec_rb_gc_enable(VALUE self) {
return rb_gc_enable();
}
@@ -45,14 +67,23 @@ static VALUE gc_spec_rb_gc_register_mark_object(VALUE self, VALUE obj) {
void Init_gc_spec(void) {
VALUE cls = rb_define_class("CApiGCSpecs", rb_cObject);
- registered_tagged_value = INT2NUM(10);
- registered_reference_value = rb_str_new2("Globally registered data");
rb_gc_register_address(&registered_tagged_value);
rb_gc_register_address(&registered_reference_value);
+ rb_gc_register_address(&registered_before_rb_gc_register_address);
+ rb_global_variable(&registered_before_rb_global_variable);
+
+ registered_tagged_value = INT2NUM(10);
+ registered_reference_value = rb_str_new2("Globally registered data");
+ registered_before_rb_gc_register_address = rb_str_new_cstr("registered before rb_gc_register_address()");
+ registered_before_rb_global_variable = rb_str_new_cstr("registered before rb_global_variable()");
rb_define_method(cls, "registered_tagged_address", registered_tagged_address, 0);
rb_define_method(cls, "registered_reference_address", registered_reference_address, 0);
+ rb_define_method(cls, "registered_before_rb_gc_register_address", get_registered_before_rb_gc_register_address, 0);
+ rb_define_method(cls, "registered_before_rb_global_variable", get_registered_before_rb_global_variable, 0);
+ rb_define_method(cls, "rb_gc_register_address", gc_spec_rb_gc_register_address, 0);
+ rb_define_method(cls, "rb_gc_unregister_address", gc_spec_rb_gc_unregister_address, 0);
rb_define_method(cls, "rb_gc_enable", gc_spec_rb_gc_enable, 0);
rb_define_method(cls, "rb_gc_disable", gc_spec_rb_gc_disable, 0);
rb_define_method(cls, "rb_gc", gc_spec_rb_gc, 0);
diff --git a/spec/ruby/optional/capi/ext/globals_spec.c b/spec/ruby/optional/capi/ext/globals_spec.c
index 28a9633f98..20dea1a05a 100644
--- a/spec/ruby/optional/capi/ext/globals_spec.c
+++ b/spec/ruby/optional/capi/ext/globals_spec.c
@@ -20,6 +20,16 @@ static VALUE sb_define_hooked_variable(VALUE self, VALUE var_name) {
return Qnil;
}
+static VALUE sb_define_hooked_variable_default_accessors(VALUE self, VALUE var_name) {
+ rb_define_hooked_variable(StringValuePtr(var_name), &g_hooked_var, (rb_gvar_getter_t*) NULL, (rb_gvar_setter_t*) NULL);
+ return Qnil;
+}
+
+static VALUE sb_define_hooked_variable_null_var(VALUE self, VALUE var_name) {
+ rb_define_hooked_variable(StringValuePtr(var_name), NULL, (rb_gvar_getter_t*) NULL, (rb_gvar_setter_t*) NULL);
+ return Qnil;
+}
+
VALUE g_ro_var;
static VALUE sb_define_readonly_variable(VALUE self, VALUE var_name, VALUE val) {
@@ -40,6 +50,26 @@ static VALUE sb_define_variable(VALUE self, VALUE var_name, VALUE val) {
return Qnil;
}
+long virtual_var_storage;
+
+VALUE incrementing_getter(ID id, VALUE *data) {
+ return LONG2FIX(virtual_var_storage++);
+}
+
+void incrementing_setter(VALUE val, ID id, VALUE *data) {
+ virtual_var_storage = FIX2LONG(val);
+}
+
+static VALUE sb_define_virtual_variable_default_accessors(VALUE self, VALUE name) {
+ rb_define_virtual_variable(StringValuePtr(name), (rb_gvar_getter_t*) NULL, (rb_gvar_setter_t*) NULL);
+ return Qnil;
+}
+
+static VALUE sb_define_virtual_variable_incrementing_accessors(VALUE self, VALUE name) {
+ rb_define_virtual_variable(StringValuePtr(name), incrementing_getter, incrementing_setter);
+ return Qnil;
+}
+
static VALUE sb_f_global_variables(VALUE self) {
return rb_f_global_variables();
}
@@ -101,10 +131,14 @@ void Init_globals_spec(void) {
VALUE cls = rb_define_class("CApiGlobalSpecs", rb_cObject);
g_hooked_var = Qnil;
rb_define_method(cls, "rb_define_hooked_variable_2x", sb_define_hooked_variable, 1);
+ rb_define_method(cls, "rb_define_hooked_variable_default_accessors", sb_define_hooked_variable_default_accessors, 1);
+ rb_define_method(cls, "rb_define_hooked_variable_null_var", sb_define_hooked_variable_null_var, 1);
g_ro_var = Qnil;
rb_define_method(cls, "rb_define_readonly_variable", sb_define_readonly_variable, 2);
g_var = Qnil;
rb_define_method(cls, "rb_define_variable", sb_define_variable, 2);
+ rb_define_method(cls, "rb_define_virtual_variable_default_accessors", sb_define_virtual_variable_default_accessors, 1);
+ rb_define_method(cls, "rb_define_virtual_variable_incrementing_accessors", sb_define_virtual_variable_incrementing_accessors, 1);
rb_define_method(cls, "sb_get_global_value", sb_get_global_value, 0);
rb_define_method(cls, "rb_f_global_variables", sb_f_global_variables, 0);
rb_define_method(cls, "sb_gv_get", sb_gv_get, 1);
diff --git a/spec/ruby/optional/capi/ext/io_spec.c b/spec/ruby/optional/capi/ext/io_spec.c
index b4ffe9207a..f257cef554 100644
--- a/spec/ruby/optional/capi/ext/io_spec.c
+++ b/spec/ruby/optional/capi/ext/io_spec.c
@@ -182,6 +182,46 @@ VALUE io_spec_rb_thread_fd_writable(VALUE self, VALUE io) {
return Qnil;
}
+VALUE io_spec_rb_thread_fd_select_read(VALUE self, VALUE io) {
+ int fd = io_spec_get_fd(io);
+
+ rb_fdset_t fds;
+ rb_fd_init(&fds);
+ rb_fd_set(fd, &fds);
+
+ int r = rb_thread_fd_select(fd + 1, &fds, NULL, NULL, NULL);
+ rb_fd_term(&fds);
+ return INT2FIX(r);
+}
+
+VALUE io_spec_rb_thread_fd_select_write(VALUE self, VALUE io) {
+ int fd = io_spec_get_fd(io);
+
+ rb_fdset_t fds;
+ rb_fd_init(&fds);
+ rb_fd_set(fd, &fds);
+
+ int r = rb_thread_fd_select(fd + 1, NULL, &fds, NULL, NULL);
+ rb_fd_term(&fds);
+ return INT2FIX(r);
+}
+
+VALUE io_spec_rb_thread_fd_select_timeout(VALUE self, VALUE io) {
+ int fd = io_spec_get_fd(io);
+
+ struct timeval timeout;
+ timeout.tv_sec = 10;
+ timeout.tv_usec = 20;
+
+ rb_fdset_t fds;
+ rb_fd_init(&fds);
+ rb_fd_set(fd, &fds);
+
+ int r = rb_thread_fd_select(fd + 1, NULL, &fds, NULL, &timeout);
+ rb_fd_term(&fds);
+ return INT2FIX(r);
+}
+
VALUE io_spec_rb_io_binmode(VALUE self, VALUE io) {
return rb_io_binmode(io);
}
@@ -256,6 +296,9 @@ void Init_io_spec(void) {
rb_define_method(cls, "rb_io_wait_writable", io_spec_rb_io_wait_writable, 1);
rb_define_method(cls, "rb_thread_wait_fd", io_spec_rb_thread_wait_fd, 1);
rb_define_method(cls, "rb_thread_fd_writable", io_spec_rb_thread_fd_writable, 1);
+ rb_define_method(cls, "rb_thread_fd_select_read", io_spec_rb_thread_fd_select_read, 1);
+ rb_define_method(cls, "rb_thread_fd_select_write", io_spec_rb_thread_fd_select_write, 1);
+ rb_define_method(cls, "rb_thread_fd_select_timeout", io_spec_rb_thread_fd_select_timeout, 1);
rb_define_method(cls, "rb_wait_for_single_fd", io_spec_rb_wait_for_single_fd, 4);
rb_define_method(cls, "rb_io_binmode", io_spec_rb_io_binmode, 1);
rb_define_method(cls, "rb_fd_fix_cloexec", io_spec_rb_fd_fix_cloexec, 1);
diff --git a/spec/ruby/optional/capi/ext/kernel_spec.c b/spec/ruby/optional/capi/ext/kernel_spec.c
index 46af8696a5..e194ba8fde 100644
--- a/spec/ruby/optional/capi/ext/kernel_spec.c
+++ b/spec/ruby/optional/capi/ext/kernel_spec.c
@@ -112,6 +112,10 @@ VALUE kernel_spec_rb_eval_string(VALUE self, VALUE str) {
return rb_eval_string(RSTRING_PTR(str));
}
+VALUE kernel_spec_rb_eval_cmd_kw(VALUE self, VALUE cmd, VALUE args, VALUE kw_splat) {
+ return rb_eval_cmd_kw(cmd, args, NUM2INT(kw_splat));
+}
+
VALUE kernel_spec_rb_raise(VALUE self, VALUE hash) {
rb_hash_aset(hash, ID2SYM(rb_intern("stage")), ID2SYM(rb_intern("before")));
if (self != Qundef)
@@ -361,6 +365,7 @@ void Init_kernel_spec(void) {
rb_define_method(cls, "rb_frame_this_func_test_again", kernel_spec_rb_frame_this_func, 0);
rb_define_method(cls, "rb_ensure", kernel_spec_rb_ensure, 4);
rb_define_method(cls, "rb_eval_string", kernel_spec_rb_eval_string, 1);
+ rb_define_method(cls, "rb_eval_cmd_kw", kernel_spec_rb_eval_cmd_kw, 3);
rb_define_method(cls, "rb_raise", kernel_spec_rb_raise, 1);
rb_define_method(cls, "rb_throw", kernel_spec_rb_throw, 1);
rb_define_method(cls, "rb_throw_obj", kernel_spec_rb_throw_obj, 2);
diff --git a/spec/ruby/optional/capi/ext/object_spec.c b/spec/ruby/optional/capi/ext/object_spec.c
index 967b355c4a..30ac44cf1f 100644
--- a/spec/ruby/optional/capi/ext/object_spec.c
+++ b/spec/ruby/optional/capi/ext/object_spec.c
@@ -393,6 +393,18 @@ static VALUE object_spec_rb_class_inherited_p(VALUE self, VALUE mod, VALUE arg)
return rb_class_inherited_p(mod, arg);
}
+static int foreach_f(ID key, VALUE val, VALUE ary) {
+ rb_ary_push(ary, ID2SYM(key));
+ rb_ary_push(ary, val);
+ return ST_CONTINUE;
+}
+
+static VALUE object_spec_rb_ivar_foreach(VALUE self, VALUE obj) {
+ VALUE ary = rb_ary_new();
+ rb_ivar_foreach(obj, foreach_f, ary);
+ return ary;
+}
+
static VALUE speced_allocator(VALUE klass) {
VALUE flags = 0;
VALUE instance;
@@ -508,6 +520,7 @@ void Init_object_spec(void) {
rb_define_method(cls, "speced_allocator?", speced_allocator_p, 1);
rb_define_method(cls, "custom_alloc_func?", custom_alloc_func_p, 1);
rb_define_method(cls, "not_implemented_method", rb_f_notimplement, -1);
+ rb_define_method(cls, "rb_ivar_foreach", object_spec_rb_ivar_foreach, 1);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/rubyspec.h b/spec/ruby/optional/capi/ext/rubyspec.h
index 426b1ddc04..245669d200 100644
--- a/spec/ruby/optional/capi/ext/rubyspec.h
+++ b/spec/ruby/optional/capi/ext/rubyspec.h
@@ -34,34 +34,4 @@
#define RUBY_VERSION_IS_3_0
#endif
-#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 7)
-#define RUBY_VERSION_IS_2_7
-#endif
-
-#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 6)
-#define RUBY_VERSION_IS_2_6
-#endif
-
-#if defined(__cplusplus) && !defined(RUBY_VERSION_IS_2_7)
-/* Ruby < 2.7 needs this to let these function with callbacks and compile in C++ code */
-#define rb_define_method(mod, name, func, argc) rb_define_method(mod, name, RUBY_METHOD_FUNC(func), argc)
-#define rb_define_protected_method(mod, name, func, argc) rb_define_protected_method(mod, name, RUBY_METHOD_FUNC(func), argc)
-#define rb_define_private_method(mod, name, func, argc) rb_define_private_method(mod, name, RUBY_METHOD_FUNC(func), argc)
-#define rb_define_singleton_method(mod, name, func, argc) rb_define_singleton_method(mod, name, RUBY_METHOD_FUNC(func), argc)
-#define rb_define_module_function(mod, name, func, argc) rb_define_module_function(mod, name, RUBY_METHOD_FUNC(func), argc)
-#define rb_define_global_function(name, func, argc) rb_define_global_function(name, RUBY_METHOD_FUNC(func), argc)
-#define rb_hash_foreach(hash, func, farg) rb_hash_foreach(hash, (int (*)(...))func, farg)
-#define st_foreach(tab, func, arg) st_foreach(tab, (int (*)(...))func, arg)
-#define rb_block_call(object, name, args_count, args, block_call_func, data) rb_block_call(object, name, args_count, args, RUBY_METHOD_FUNC(block_call_func), data)
-#define rb_ensure(b_proc, data1, e_proc, data2) rb_ensure(RUBY_METHOD_FUNC(b_proc), data1, RUBY_METHOD_FUNC(e_proc), data2)
-#define rb_rescue(b_proc, data1, e_proc, data2) rb_rescue(RUBY_METHOD_FUNC(b_proc), data1, RUBY_METHOD_FUNC(e_proc), data2)
-#define rb_rescue2(b_proc, data1, e_proc, data2, ...) rb_rescue2(RUBY_METHOD_FUNC(b_proc), data1, RUBY_METHOD_FUNC(e_proc), data2, __VA_ARGS__)
-#define rb_catch(tag, func, data) rb_catch(tag, RUBY_METHOD_FUNC(func), data)
-#define rb_catch_obj(tag, func, data) rb_catch_obj(tag, RUBY_METHOD_FUNC(func), data)
-#define rb_proc_new(fn, arg) rb_proc_new(RUBY_METHOD_FUNC(fn), arg)
-#define rb_fiber_new(fn, arg) rb_fiber_new(RUBY_METHOD_FUNC(fn), arg)
-#define rb_thread_create(fn, arg) rb_thread_create(RUBY_METHOD_FUNC(fn), arg)
-#define rb_define_hooked_variable(name, var, getter, setter) rb_define_hooked_variable(name, var, RUBY_METHOD_FUNC(getter), (void (*)(...))setter)
-#endif
-
#endif
diff --git a/spec/ruby/optional/capi/ext/string_spec.c b/spec/ruby/optional/capi/ext/string_spec.c
index b9a4a55853..a858936243 100644
--- a/spec/ruby/optional/capi/ext/string_spec.c
+++ b/spec/ruby/optional/capi/ext/string_spec.c
@@ -437,6 +437,12 @@ VALUE string_spec_RSTRING_PTR_read(VALUE self, VALUE str, VALUE path) {
return capacities;
}
+VALUE string_spec_RSTRING_PTR_null_terminate(VALUE self, VALUE str, VALUE min_length) {
+ char* ptr = RSTRING_PTR(str);
+ char* end = ptr + RSTRING_LEN(str);
+ return rb_str_new(end, FIX2LONG(min_length));
+}
+
VALUE string_spec_StringValue(VALUE self, VALUE str) {
return StringValue(str);
}
@@ -591,6 +597,18 @@ static VALUE string_spec_rb_str_unlocktmp(VALUE self, VALUE str) {
return rb_str_unlocktmp(str);
}
+VALUE rb_str_to_interned_str(VALUE str);
+VALUE rb_enc_interned_str_cstr(const char *ptr, rb_encoding *enc);
+
+static VALUE string_spec_rb_enc_interned_str_cstr(VALUE self, VALUE str, VALUE enc) {
+ rb_encoding *e = NIL_P(enc) ? 0 : rb_to_encoding(enc);
+ return rb_enc_interned_str_cstr(RSTRING_PTR(str), e);
+}
+
+static VALUE string_spec_rb_str_to_interned_str(VALUE self, VALUE str) {
+ return rb_str_to_interned_str(str);
+}
+
void Init_string_spec(void) {
VALUE cls = rb_define_class("CApiStringSpecs", rb_cObject);
rb_define_method(cls, "rb_cstr2inum", string_spec_rb_cstr2inum, 2);
@@ -662,6 +680,7 @@ void Init_string_spec(void) {
rb_define_method(cls, "RSTRING_PTR_after_funcall", string_spec_RSTRING_PTR_after_funcall, 2);
rb_define_method(cls, "RSTRING_PTR_after_yield", string_spec_RSTRING_PTR_after_yield, 1);
rb_define_method(cls, "RSTRING_PTR_read", string_spec_RSTRING_PTR_read, 2);
+ rb_define_method(cls, "RSTRING_PTR_null_terminate", string_spec_RSTRING_PTR_null_terminate, 2);
rb_define_method(cls, "StringValue", string_spec_StringValue, 1);
rb_define_method(cls, "SafeStringValue", string_spec_SafeStringValue, 1);
rb_define_method(cls, "rb_str_hash", string_spec_rb_str_hash, 1);
@@ -691,6 +710,8 @@ void Init_string_spec(void) {
rb_define_method(cls, "rb_str_catf", string_spec_rb_str_catf, 1);
rb_define_method(cls, "rb_str_locktmp", string_spec_rb_str_locktmp, 1);
rb_define_method(cls, "rb_str_unlocktmp", string_spec_rb_str_unlocktmp, 1);
+ rb_define_method(cls, "rb_enc_interned_str_cstr", string_spec_rb_enc_interned_str_cstr, 2);
+ rb_define_method(cls, "rb_str_to_interned_str", string_spec_rb_str_to_interned_str, 1);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/util_spec.c b/spec/ruby/optional/capi/ext/util_spec.c
index a7269353c2..95ba71ea9d 100644
--- a/spec/ruby/optional/capi/ext/util_spec.c
+++ b/spec/ruby/optional/capi/ext/util_spec.c
@@ -7,15 +7,18 @@ extern "C" {
#endif
VALUE util_spec_rb_scan_args(VALUE self, VALUE argv, VALUE fmt, VALUE expected, VALUE acc) {
- int i, result, argc = (int)RARRAY_LEN(argv);
- VALUE args[6], failed, a1, a2, a3, a4, a5, a6;
-
- failed = rb_intern("failed");
- a1 = a2 = a3 = a4 = a5 = a6 = failed;
-
- for(i = 0; i < argc; i++) {
- args[i] = rb_ary_entry(argv, i);
- }
+ int result, argc;
+ VALUE a1, a2, a3, a4, a5, a6;
+
+ argc = (int) RARRAY_LEN(argv);
+ VALUE* args = RARRAY_PTR(argv);
+ /* the line above can be replaced with this for Ruby implementations which do not support RARRAY_PTR() yet
+ VALUE args[6];
+ for(int i = 0; i < argc; i++) {
+ args[i] = rb_ary_entry(argv, i);
+ } */
+
+ a1 = a2 = a3 = a4 = a5 = a6 = INT2FIX(-1);
#ifdef RB_SCAN_ARGS_KEYWORDS
if (*RSTRING_PTR(fmt) == 'k') {
diff --git a/spec/ruby/optional/capi/fixtures/object.rb b/spec/ruby/optional/capi/fixtures/object.rb
new file mode 100644
index 0000000000..a59f2309d8
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/object.rb
@@ -0,0 +1,29 @@
+class CApiObjectSpecs
+ class IVars
+ def initialize
+ @a = 3
+ @b = 7
+ @c = 4
+ end
+
+ def self.set_class_variables
+ @@foo = :a
+ @@bar = :b
+ @@baz = :c
+ end
+ end
+
+ module MVars
+ @@mvar = :foo
+ @@mvar2 = :bar
+
+ @ivar = :baz
+ end
+
+ module CVars
+ @@cvar = :foo
+ @@cvar2 = :bar
+
+ @ivar = :baz
+ end
+end
diff --git a/spec/ruby/optional/capi/gc_spec.rb b/spec/ruby/optional/capi/gc_spec.rb
index 23e2b7c9ab..d76ea7394f 100644
--- a/spec/ruby/optional/capi/gc_spec.rb
+++ b/spec/ruby/optional/capi/gc_spec.rb
@@ -7,15 +7,33 @@ describe "CApiGCSpecs" do
@f = CApiGCSpecs.new
end
- it "correctly gets the value from a registered address" do
- @f.registered_tagged_address.should == 10
- @f.registered_tagged_address.should equal(@f.registered_tagged_address)
- @f.registered_reference_address.should == "Globally registered data"
- @f.registered_reference_address.should equal(@f.registered_reference_address)
+ describe "rb_gc_register_address" do
+ it "correctly gets the value from a registered address" do
+ @f.registered_tagged_address.should == 10
+ @f.registered_tagged_address.should equal(@f.registered_tagged_address)
+ @f.registered_reference_address.should == "Globally registered data"
+ @f.registered_reference_address.should equal(@f.registered_reference_address)
+ end
+
+ it "keeps the value alive even if the value is assigned after rb_gc_register_address() is called" do
+ GC.start
+ @f.registered_before_rb_gc_register_address.should == "registered before rb_gc_register_address()"
+ end
+
+ it "can be called outside Init_" do
+ @f.rb_gc_register_address.should == "rb_gc_register_address() outside Init_"
+ @f.rb_gc_unregister_address
+ end
end
- describe "rb_gc_enable" do
+ describe "rb_global_variable" do
+ it "keeps the value alive even if the value is assigned after rb_global_variable() is called" do
+ GC.start
+ @f.registered_before_rb_global_variable.should == "registered before rb_global_variable()"
+ end
+ end
+ describe "rb_gc_enable" do
after do
GC.enable
end
diff --git a/spec/ruby/optional/capi/globals_spec.rb b/spec/ruby/optional/capi/globals_spec.rb
index cc6f6ef3a8..48677620bc 100644
--- a/spec/ruby/optional/capi/globals_spec.rb
+++ b/spec/ruby/optional/capi/globals_spec.rb
@@ -9,7 +9,7 @@ describe "CApiGlobalSpecs" do
end
it "correctly gets global values" do
- @f.sb_gv_get("$BLAH").should == nil
+ suppress_warning { @f.sb_gv_get("$BLAH") }.should == nil
@f.sb_gv_get("$\\").should == nil
@f.sb_gv_get("\\").should == nil # rb_gv_get should change \ to $\
end
@@ -21,7 +21,7 @@ describe "CApiGlobalSpecs" do
end
it "correctly sets global values" do
- @f.sb_gv_get("$BLAH").should == nil
+ suppress_warning { @f.sb_gv_get("$BLAH") }.should == nil
@f.sb_gv_set("$BLAH", 10)
begin
@f.sb_gv_get("$BLAH").should == 10
@@ -42,6 +42,10 @@ describe "CApiGlobalSpecs" do
end
it "rb_define_readonly_variable should define a new readonly global variable" do
+ # Check the gvar doesn't exist and ensure rb_gv_get doesn't implicitly declare the gvar,
+ # otherwise the rb_define_readonly_variable call will conflict.
+ suppress_warning { @f.sb_gv_get("ro_gvar") } .should == nil
+
@f.rb_define_readonly_variable("ro_gvar", 15)
$ro_gvar.should == 15
-> { $ro_gvar = 10 }.should raise_error(NameError)
@@ -53,6 +57,52 @@ describe "CApiGlobalSpecs" do
$hooked_gvar.should == 4
end
+ it "rb_define_hooked_variable should use default accessors if NULL ones are supplied" do
+ @f.rb_define_hooked_variable_default_accessors("$hooked_gvar_default_accessors")
+ $hooked_gvar_default_accessors = 10
+ $hooked_gvar_default_accessors.should == 10
+ end
+
+ it "rb_define_hooked_variable with default accessors should return nil for NULL variables" do
+ @f.rb_define_hooked_variable_null_var("$hooked_gvar_null_value")
+ $hooked_gvar_null_value.should == nil
+ end
+
+ describe "rb_define_virtual_variable" do
+ describe "with default accessors" do
+ before :all do
+ @f.rb_define_virtual_variable_default_accessors("$virtual_variable_default_accessors")
+ end
+
+ it "is read-only" do
+ -> { $virtual_variable_default_accessors = 10 }.should raise_error(NameError, /read-only/)
+ end
+
+ it "returns false with the default getter" do
+ $virtual_variable_default_accessors.should == false
+ $virtual_variable_default_accessors.should == false
+ end
+ end
+
+ describe "with supplied accessors" do
+ before :all do
+ @f.rb_define_virtual_variable_incrementing_accessors("$virtual_variable_incrementing_accessors")
+ end
+
+ it "returns a dynamically changing value" do
+ $virtual_variable_incrementing_accessors = 20
+ $virtual_variable_incrementing_accessors.should == 20
+ $virtual_variable_incrementing_accessors.should == 21
+ $virtual_variable_incrementing_accessors.should == 22
+
+ $virtual_variable_incrementing_accessors = 100
+ $virtual_variable_incrementing_accessors.should == 100
+ $virtual_variable_incrementing_accessors.should == 101
+ $virtual_variable_incrementing_accessors.should == 102
+ end
+ end
+ end
+
describe "rb_fs" do
before :each do
@field_separator = $;
diff --git a/spec/ruby/optional/capi/io_spec.rb b/spec/ruby/optional/capi/io_spec.rb
index 489a01c515..95717351a6 100644
--- a/spec/ruby/optional/capi/io_spec.rb
+++ b/spec/ruby/optional/capi/io_spec.rb
@@ -262,6 +262,21 @@ describe "C-API IO function" do
end
end
+ describe "rb_thread_fd_select" do
+ it "waits until an fd is ready for reading" do
+ @w_io.write "rb_thread_fd_select"
+ @o.rb_thread_fd_select_read(@r_io).should == 1
+ end
+
+ it "waits until an fd is ready for writing" do
+ @o.rb_thread_fd_select_write(@w_io).should == 1
+ end
+
+ it "waits until an fd is ready for writing with timeout" do
+ @o.rb_thread_fd_select_timeout(@w_io).should == 1
+ end
+ end
+
platform_is_not :windows do
describe "rb_io_wait_readable" do
it "returns false if there is no error condition" do
diff --git a/spec/ruby/optional/capi/kernel_spec.rb b/spec/ruby/optional/capi/kernel_spec.rb
index d1e3e03582..54d8d8a8d3 100644
--- a/spec/ruby/optional/capi/kernel_spec.rb
+++ b/spec/ruby/optional/capi/kernel_spec.rb
@@ -504,6 +504,25 @@ describe "C-API Kernel function" do
end
end
+ describe "rb_eval_cmd_kw" do
+ it "evaluates a string of ruby code" do
+ @s.rb_eval_cmd_kw("1+1", [], 0).should == 2
+ end
+
+ it "calls a proc with the supplied arguments" do
+ @s.rb_eval_cmd_kw(-> *x { x.map { |i| i + 1 } }, [1, 3, 7], 0).should == [2, 4, 8]
+ end
+
+ it "calls a proc with keyword arguments if kw_splat is non zero" do
+ a_proc = -> *x, **y {
+ res = x.map { |i| i + 1 }
+ y.each { |k, v| res << k; res << v }
+ res
+ }
+ @s.rb_eval_cmd_kw(a_proc, [1, 3, 7, {a: 1, b: 2, c: 3}], 1).should == [2, 4, 8, :a, 1, :b, 2, :c, 3]
+ end
+ end
+
describe "rb_block_proc" do
it "converts the implicit block into a proc" do
proc = @s.rb_block_proc { 1+1 }
diff --git a/spec/ruby/optional/capi/object_spec.rb b/spec/ruby/optional/capi/object_spec.rb
index b0f8a1f891..25a43d8908 100644
--- a/spec/ruby/optional/capi/object_spec.rb
+++ b/spec/ruby/optional/capi/object_spec.rb
@@ -1,4 +1,5 @@
require_relative 'spec_helper'
+require_relative 'fixtures/object'
load_extension("object")
@@ -983,5 +984,24 @@ describe "CApiObject" do
@o.speced_allocator?(parent).should == true
end
end
+
+ describe "rb_ivar_foreach" do
+ it "calls the callback function for each instance variable on an object" do
+ o = CApiObjectSpecs::IVars.new
+ ary = @o.rb_ivar_foreach(o)
+ ary.should == [:@a, 3, :@b, 7, :@c, 4]
+ end
+
+ it "calls the callback function for each cvar and ivar on a class" do
+ ary = @o.rb_ivar_foreach(CApiObjectSpecs::CVars)
+ ary.should == [:__classpath__, 'CApiObjectSpecs::CVars', :@@cvar, :foo, :@@cvar2, :bar, :@ivar, :baz]
+ end
+
+ it "calls the callback function for each cvar and ivar on a module" do
+ ary = @o.rb_ivar_foreach(CApiObjectSpecs::MVars)
+ ary.should == [:__classpath__, 'CApiObjectSpecs::MVars', :@@mvar, :foo, :@@mvar2, :bar, :@ivar, :baz]
+ end
+
+ end
end
end
diff --git a/spec/ruby/optional/capi/rbasic_spec.rb b/spec/ruby/optional/capi/rbasic_spec.rb
index 6300680d99..577f2060da 100644
--- a/spec/ruby/optional/capi/rbasic_spec.rb
+++ b/spec/ruby/optional/capi/rbasic_spec.rb
@@ -1,7 +1,6 @@
require_relative 'spec_helper'
require_relative 'shared/rbasic'
load_extension("rbasic")
-return if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS') # not working from the beginning
load_extension("data")
load_extension("array")
diff --git a/spec/ruby/optional/capi/shared/rbasic.rb b/spec/ruby/optional/capi/shared/rbasic.rb
index 99c2044bd7..95c3137143 100644
--- a/spec/ruby/optional/capi/shared/rbasic.rb
+++ b/spec/ruby/optional/capi/shared/rbasic.rb
@@ -10,7 +10,7 @@ describe :rbasic, shared: true do
obj, _ = @data.call
initial = @specs.get_flags(obj)
obj.freeze
- @specs.get_flags(obj).should == @freeze | initial
+ (@specs.get_flags(obj) & 0xFFFF).should == (@freeze | initial) & 0xFFFF
end
it "supports setting the FREEZE flag" do
diff --git a/spec/ruby/optional/capi/spec_helper.rb b/spec/ruby/optional/capi/spec_helper.rb
index ec6b909397..2691aa1332 100644
--- a/spec/ruby/optional/capi/spec_helper.rb
+++ b/spec/ruby/optional/capi/spec_helper.rb
@@ -34,7 +34,8 @@ def compile_extension(name)
abi_header = "#{rubyhdrdir}/ruby/internal/abi.h"
if RbConfig::CONFIG["ENABLE_SHARED"] == "yes"
- libdirname = RbConfig::CONFIG['libdirname'] # defined since 2.1
+ # below is defined since 2.1, except for mswin, and maybe other platforms
+ libdirname = RbConfig::CONFIG.fetch 'libdirname', 'libdir'
libruby = "#{RbConfig::CONFIG[libdirname]}/#{RbConfig::CONFIG['LIBRUBY']}"
end
diff --git a/spec/ruby/optional/capi/string_spec.rb b/spec/ruby/optional/capi/string_spec.rb
index 7ad4d10ee4..c0fbf3d1ba 100644
--- a/spec/ruby/optional/capi/string_spec.rb
+++ b/spec/ruby/optional/capi/string_spec.rb
@@ -97,6 +97,32 @@ describe "C-API String function" do
end
end
+ describe "rb_str_set_len on a UTF-16 String" do
+ before :each do
+ @str = "abcdefghij".force_encoding(Encoding::UTF_16BE)
+ # Make sure to unshare the string
+ @s.rb_str_modify(@str)
+ end
+
+ it "inserts two NULL bytes at the length" do
+ @s.rb_str_set_len(@str, 4).b.should == "abcd".b
+ @s.rb_str_set_len(@str, 8).b.should == "abcd\x00\x00gh".b
+ end
+ end
+
+ describe "rb_str_set_len on a UTF-32 String" do
+ before :each do
+ @str = "abcdefghijkl".force_encoding(Encoding::UTF_32BE)
+ # Make sure to unshare the string
+ @s.rb_str_modify(@str)
+ end
+
+ it "inserts four NULL bytes at the length" do
+ @s.rb_str_set_len(@str, 4).b.should == "abcd".b
+ @s.rb_str_set_len(@str, 12).b.should == "abcd\x00\x00\x00\x00ijkl".b
+ end
+ end
+
describe "rb_str_buf_new" do
it "returns the equivalent of an empty string" do
buf = @s.rb_str_buf_new(10, nil)
@@ -592,6 +618,12 @@ describe "C-API String function" do
capacities[0].should < capacities[1]
str.should == "fixture file contents to test read() with RSTRING_PTR"
end
+
+ it "terminates the string with at least (encoding min length) \\0 bytes" do
+ @s.RSTRING_PTR_null_terminate("abc", 1).should == "\x00"
+ @s.RSTRING_PTR_null_terminate("abc".encode("UTF-16BE"), 2).should == "\x00\x00"
+ @s.RSTRING_PTR_null_terminate("abc".encode("UTF-32BE"), 4).should == "\x00\x00\x00\x00"
+ end
end
describe "RSTRING_LEN" do
@@ -1008,9 +1040,11 @@ end
@s.rb_sprintf3(true.class).should == s
end
- it "formats a TrueClass VALUE as 'true' if sign specified in format" do
- s = 'Result: true.'
- @s.rb_sprintf4(true.class).should == s
+ ruby_bug "#19167", ""..."3.2" do
+ it "formats a TrueClass VALUE as 'true' if sign specified in format" do
+ s = 'Result: TrueClass.'
+ @s.rb_sprintf4(true.class).should == s
+ end
end
it "truncates a string to a supplied precision if that is shorter than the string" do
@@ -1193,4 +1227,67 @@ end
-> { @s.rb_str_unlocktmp("test") }.should raise_error(RuntimeError, 'temporal unlocking already unlocked string')
end
end
+
+ describe "rb_enc_interned_str_cstr" do
+ it "returns a frozen string" do
+ str = "hello"
+ val = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
+
+ val.should.is_a?(String)
+ val.encoding.should == Encoding::US_ASCII
+ val.should.frozen?
+ end
+
+ it "returns the same frozen string" do
+ str = "hello"
+ result1 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
+ result2 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
+ result1.should.equal?(result2)
+ end
+
+ it "returns different frozen strings for different encodings" do
+ str = "hello"
+ result1 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
+ result2 = @s.rb_enc_interned_str_cstr(str, Encoding::UTF_8)
+ result1.should_not.equal?(result2)
+ end
+
+ it "returns the same string as String#-@" do
+ @s.rb_enc_interned_str_cstr("hello", Encoding::UTF_8).should.equal?(-"hello")
+ end
+
+ ruby_bug "#20322", ""..."3.4" do
+ it "uses the default encoding if encoding is null" do
+ str = "hello"
+ val = @s.rb_enc_interned_str_cstr(str, nil)
+ val.encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+ end
+
+ describe "rb_str_to_interned_str" do
+ it "returns a frozen string" do
+ str = "hello"
+ result = @s.rb_str_to_interned_str(str)
+ result.should.is_a?(String)
+ result.should.frozen?
+ end
+
+ it "returns the same frozen string" do
+ str = "hello"
+ result1 = @s.rb_str_to_interned_str(str)
+ result2 = @s.rb_str_to_interned_str(str)
+ result1.should.equal?(result2)
+ end
+
+ it "returns different frozen strings for different encodings" do
+ result1 = @s.rb_str_to_interned_str("hello".force_encoding(Encoding::US_ASCII))
+ result2 = @s.rb_str_to_interned_str("hello".force_encoding(Encoding::UTF_8))
+ result1.should_not.equal?(result2)
+ end
+
+ it "returns the same string as String#-@" do
+ @s.rb_str_to_interned_str("hello").should.equal?(-"hello")
+ end
+ end
end
diff --git a/spec/ruby/optional/capi/util_spec.rb b/spec/ruby/optional/capi/util_spec.rb
index 64b0894087..38f6f47b1a 100644
--- a/spec/ruby/optional/capi/util_spec.rb
+++ b/spec/ruby/optional/capi/util_spec.rb
@@ -15,8 +15,9 @@ describe "C-API Util function" do
end
it "assigns the required arguments scanned" do
- @o.rb_scan_args([1, 2], "2", 2, @acc).should == 2
- ScratchPad.recorded.should == [1, 2]
+ obj = Object.new
+ @o.rb_scan_args([obj, 2], "2", 2, @acc).should == 2
+ ScratchPad.recorded.should == [obj, 2]
end
it "raises an ArgumentError if there are insufficient arguments" do
diff --git a/spec/ruby/security/cve_2019_8325_spec.rb b/spec/ruby/security/cve_2019_8325_spec.rb
index 04692e01fe..bbddb3a6ce 100644
--- a/spec/ruby/security/cve_2019_8325_spec.rb
+++ b/spec/ruby/security/cve_2019_8325_spec.rb
@@ -5,8 +5,17 @@ require 'rubygems/command_manager'
describe "CVE-2019-8325 is resisted by" do
describe "sanitising error message components" do
+ before :each do
+ @ui = Gem::SilentUI.new
+ end
+
+ after :each do
+ @ui.close
+ end
+
it "for the 'while executing' message" do
manager = Gem::CommandManager.new
+ manager.ui = @ui
def manager.process_args(args, build_args)
raise StandardError, "\e]2;nyan\a"
end
@@ -26,6 +35,7 @@ describe "CVE-2019-8325 is resisted by" do
it "for the 'loading command' message" do
manager = Gem::CommandManager.new
+ manager.ui = @ui
def manager.require(x)
raise 'foo'
end
diff --git a/spec/ruby/shared/file/executable.rb b/spec/ruby/shared/file/executable.rb
index 7b5c4c580c..baa156de98 100644
--- a/spec/ruby/shared/file/executable.rb
+++ b/spec/ruby/shared/file/executable.rb
@@ -39,6 +39,41 @@ describe :file_executable, shared: true do
-> { @object.send(@method, nil) }.should raise_error(TypeError)
-> { @object.send(@method, false) }.should raise_error(TypeError)
end
+
+ platform_is_not :windows do
+ as_superuser do
+ context "when run by a superuser" do
+ before :each do
+ @file = tmp('temp3.txt')
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns true if file owner has permission to execute" do
+ File.chmod(0766, @file)
+ @object.send(@method, @file).should == true
+ end
+
+ it "returns true if group has permission to execute" do
+ File.chmod(0676, @file)
+ @object.send(@method, @file).should == true
+ end
+
+ it "returns true if other have permission to execute" do
+ File.chmod(0667, @file)
+ @object.send(@method, @file).should == true
+ end
+
+ it "return false if nobody has permission to execute" do
+ File.chmod(0666, @file)
+ @object.send(@method, @file).should == false
+ end
+ end
+ end
+ end
end
describe :file_executable_missing, shared: true do
diff --git a/spec/ruby/shared/file/executable_real.rb b/spec/ruby/shared/file/executable_real.rb
index ce3d5ca176..bf2734ea07 100644
--- a/spec/ruby/shared/file/executable_real.rb
+++ b/spec/ruby/shared/file/executable_real.rb
@@ -37,6 +37,41 @@ describe :file_executable_real, shared: true do
-> { @object.send(@method, nil) }.should raise_error(TypeError)
-> { @object.send(@method, false) }.should raise_error(TypeError)
end
+
+ platform_is_not :windows do
+ as_real_superuser do
+ context "when run by a real superuser" do
+ before :each do
+ @file = tmp('temp3.txt')
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns true if file owner has permission to execute" do
+ File.chmod(0766, @file)
+ @object.send(@method, @file).should == true
+ end
+
+ it "returns true if group has permission to execute" do
+ File.chmod(0676, @file)
+ @object.send(@method, @file).should == true
+ end
+
+ it "returns true if other have permission to execute" do
+ File.chmod(0667, @file)
+ @object.send(@method, @file).should == true
+ end
+
+ it "return false if nobody has permission to execute" do
+ File.chmod(0666, @file)
+ @object.send(@method, @file).should == false
+ end
+ end
+ end
+ end
end
describe :file_executable_real_missing, shared: true do
diff --git a/spec/ruby/shared/file/readable.rb b/spec/ruby/shared/file/readable.rb
index eb2ca06812..7b45e23e36 100644
--- a/spec/ruby/shared/file/readable.rb
+++ b/spec/ruby/shared/file/readable.rb
@@ -24,6 +24,22 @@ describe :file_readable, shared: true do
it "accepts an object that has a #to_path method" do
@object.send(@method, mock_to_path(@file2)).should == true
end
+
+ platform_is_not :windows do
+ as_superuser do
+ context "when run by a superuser" do
+ it "returns true unconditionally" do
+ file = tmp('temp.txt')
+ touch file
+
+ File.chmod(0333, file)
+ @object.send(@method, file).should == true
+
+ rm_r file
+ end
+ end
+ end
+ end
end
describe :file_readable_missing, shared: true do
diff --git a/spec/ruby/shared/file/readable_real.rb b/spec/ruby/shared/file/readable_real.rb
index b6e53ac76d..32d38bc7a2 100644
--- a/spec/ruby/shared/file/readable_real.rb
+++ b/spec/ruby/shared/file/readable_real.rb
@@ -14,6 +14,22 @@ describe :file_readable_real, shared: true do
it "accepts an object that has a #to_path method" do
File.open(@file,'w') { @object.send(@method, mock_to_path(@file)).should == true }
end
+
+ platform_is_not :windows do
+ as_real_superuser do
+ context "when run by a real superuser" do
+ it "returns true unconditionally" do
+ file = tmp('temp.txt')
+ touch file
+
+ File.chmod(0333, file)
+ @object.send(@method, file).should == true
+
+ rm_r file
+ end
+ end
+ end
+ end
end
describe :file_readable_real_missing, shared: true do
diff --git a/spec/ruby/shared/file/writable.rb b/spec/ruby/shared/file/writable.rb
index 4bb8aedce6..65ea2c1781 100644
--- a/spec/ruby/shared/file/writable.rb
+++ b/spec/ruby/shared/file/writable.rb
@@ -19,6 +19,22 @@ describe :file_writable, shared: true do
it "accepts an object that has a #to_path method" do
File.open(@file,'w') { @object.send(@method, mock_to_path(@file)).should == true }
end
+
+ platform_is_not :windows do
+ as_superuser do
+ context "when run by a superuser" do
+ it "returns true unconditionally" do
+ file = tmp('temp.txt')
+ touch file
+
+ File.chmod(0555, file)
+ @object.send(@method, file).should == true
+
+ rm_r file
+ end
+ end
+ end
+ end
end
describe :file_writable_missing, shared: true do
diff --git a/spec/ruby/shared/file/writable_real.rb b/spec/ruby/shared/file/writable_real.rb
index e9721fd379..b4a0a58c6e 100644
--- a/spec/ruby/shared/file/writable_real.rb
+++ b/spec/ruby/shared/file/writable_real.rb
@@ -24,6 +24,22 @@ describe :file_writable_real, shared: true do
-> { @object.send(@method, nil) }.should raise_error(TypeError)
-> { @object.send(@method, false) }.should raise_error(TypeError)
end
+
+ platform_is_not :windows do
+ as_real_superuser do
+ context "when run by a real superuser" do
+ it "returns true unconditionally" do
+ file = tmp('temp.txt')
+ touch file
+
+ File.chmod(0555, file)
+ @object.send(@method, file).should == true
+
+ rm_r file
+ end
+ end
+ end
+ end
end
describe :file_writable_real_missing, shared: true do
diff --git a/spec/ruby/shared/kernel/complex.rb b/spec/ruby/shared/kernel/complex.rb
new file mode 100644
index 0000000000..98ee0b2b3f
--- /dev/null
+++ b/spec/ruby/shared/kernel/complex.rb
@@ -0,0 +1,133 @@
+# Specs shared by Kernel#Complex() and String#to_c()
+describe :kernel_complex, shared: true do
+
+ it "returns a Complex object" do
+ @object.send(@method, '9').should be_an_instance_of(Complex)
+ end
+
+ it "understands integers" do
+ @object.send(@method, '20').should == Complex(20)
+ end
+
+ it "understands negative integers" do
+ @object.send(@method, '-3').should == Complex(-3)
+ end
+
+ it "understands fractions (numerator/denominator) for the real part" do
+ @object.send(@method, '2/3').should == Complex(Rational(2, 3))
+ end
+
+ it "understands fractions (numerator/denominator) for the imaginary part" do
+ @object.send(@method, '4+2/3i').should == Complex(4, Rational(2, 3))
+ end
+
+ it "understands negative fractions (-numerator/denominator) for the real part" do
+ @object.send(@method, '-2/3').should == Complex(Rational(-2, 3))
+ end
+
+ it "understands negative fractions (-numerator/denominator) for the imaginary part" do
+ @object.send(@method, '7-2/3i').should == Complex(7, Rational(-2, 3))
+ end
+
+ it "understands floats (a.b) for the real part" do
+ @object.send(@method, '2.3').should == Complex(2.3)
+ end
+
+ it "understands floats (a.b) for the imaginary part" do
+ @object.send(@method, '4+2.3i').should == Complex(4, 2.3)
+ end
+
+ it "understands negative floats (-a.b) for the real part" do
+ @object.send(@method, '-2.33').should == Complex(-2.33)
+ end
+
+ it "understands negative floats (-a.b) for the imaginary part" do
+ @object.send(@method, '7-28.771i').should == Complex(7, -28.771)
+ end
+
+ it "understands an integer followed by 'i' to mean that integer is the imaginary part" do
+ @object.send(@method, '35i').should == Complex(0,35)
+ end
+
+ it "understands a negative integer followed by 'i' to mean that negative integer is the imaginary part" do
+ @object.send(@method, '-29i').should == Complex(0,-29)
+ end
+
+ it "understands an 'i' by itself as denoting a complex number with an imaginary part of 1" do
+ @object.send(@method, 'i').should == Complex(0,1)
+ end
+
+ it "understands a '-i' by itself as denoting a complex number with an imaginary part of -1" do
+ @object.send(@method, '-i').should == Complex(0,-1)
+ end
+
+ it "understands 'a+bi' to mean a complex number with 'a' as the real part, 'b' as the imaginary" do
+ @object.send(@method, '79+4i').should == Complex(79,4)
+ end
+
+ it "understands 'a-bi' to mean a complex number with 'a' as the real part, '-b' as the imaginary" do
+ @object.send(@method, '79-4i').should == Complex(79,-4)
+ end
+
+ it "understands 'a+i' to mean a complex number with 'a' as the real part, 1i as the imaginary" do
+ @object.send(@method, '79+i').should == Complex(79, 1)
+ end
+
+ it "understands 'a-i' to mean a complex number with 'a' as the real part, -1i as the imaginary" do
+ @object.send(@method, '79-i').should == Complex(79, -1)
+ end
+
+ it "understands i, I, j, and J imaginary units" do
+ @object.send(@method, '79+4i').should == Complex(79, 4)
+ @object.send(@method, '79+4I').should == Complex(79, 4)
+ @object.send(@method, '79+4j').should == Complex(79, 4)
+ @object.send(@method, '79+4J').should == Complex(79, 4)
+ end
+
+ it "understands scientific notation for the real part" do
+ @object.send(@method, '2e3+4i').should == Complex(2e3,4)
+ end
+
+ it "understands negative scientific notation for the real part" do
+ @object.send(@method, '-2e3+4i').should == Complex(-2e3,4)
+ end
+
+ it "understands scientific notation for the imaginary part" do
+ @object.send(@method, '4+2e3i').should == Complex(4, 2e3)
+ end
+
+ it "understands negative scientific notation for the imaginary part" do
+ @object.send(@method, '4-2e3i').should == Complex(4, -2e3)
+ end
+
+ it "understands scientific notation for the real and imaginary part in the same String" do
+ @object.send(@method, '2e3+2e4i').should == Complex(2e3,2e4)
+ end
+
+ it "understands negative scientific notation for the real and imaginary part in the same String" do
+ @object.send(@method, '-2e3-2e4i').should == Complex(-2e3,-2e4)
+ end
+
+ it "understands scientific notation with e and E" do
+ @object.send(@method, '2e3+2e4i').should == Complex(2e3, 2e4)
+ @object.send(@method, '2E3+2E4i').should == Complex(2e3, 2e4)
+ end
+
+ it "understands 'm@a' to mean a complex number in polar form with 'm' as the modulus, 'a' as the argument" do
+ @object.send(@method, '79@4').should == Complex.polar(79, 4)
+ @object.send(@method, '-79@4').should == Complex.polar(-79, 4)
+ @object.send(@method, '79@-4').should == Complex.polar(79, -4)
+ end
+
+ it "ignores leading whitespaces" do
+ @object.send(@method, ' 79+4i').should == Complex(79, 4)
+ end
+
+ it "ignores trailing whitespaces" do
+ @object.send(@method, '79+4i ').should == Complex(79, 4)
+ end
+
+ it "understands _" do
+ @object.send(@method, '7_9+4_0i').should == Complex(79, 40)
+ end
+end
diff --git a/spec/ruby/shared/queue/deque.rb b/spec/ruby/shared/queue/deque.rb
index 8b755dd9b7..616e56ec8a 100644
--- a/spec/ruby/shared/queue/deque.rb
+++ b/spec/ruby/shared/queue/deque.rb
@@ -55,6 +55,68 @@ describe :queue_deq, shared: true do
t.join
end
+ describe "with a timeout" do
+ ruby_version_is "3.2" do
+ it "returns an item if one is available in time" do
+ q = @object.call
+
+ t = Thread.new {
+ q.send(@method, timeout: 1).should == 1
+ }
+ Thread.pass until t.status == "sleep" && q.num_waiting == 1
+ q << 1
+ t.join
+ end
+
+ it "returns nil if no item is available in time" do
+ q = @object.call
+
+ t = Thread.new {
+ q.send(@method, timeout: 0.1).should == nil
+ }
+ t.join
+ end
+
+ it "does nothing if the timeout is nil" do
+ q = @object.call
+ t = Thread.new {
+ q.send(@method, timeout: nil).should == 1
+ }
+ t.join(0.2).should == nil
+ q << 1
+ t.join
+ end
+
+ it "immediately returns nil if no item is available and the timeout is 0" do
+ q = @object.call
+ q << 1
+ q.send(@method, timeout: 0).should == 1
+ q.send(@method, timeout: 0).should == nil
+ end
+
+ it "raise TypeError if timeout is not a valid numeric" do
+ q = @object.call
+ -> { q.send(@method, timeout: "1") }.should raise_error(
+ TypeError,
+ "no implicit conversion to float from string",
+ )
+
+ -> { q.send(@method, timeout: false) }.should raise_error(
+ TypeError,
+ "no implicit conversion to float from false",
+ )
+ end
+
+ it "raise ArgumentError if non_block = true is passed too" do
+ q = @object.call
+ -> { q.send(@method, true, timeout: 1) }.should raise_error(
+ ArgumentError,
+ "can't set a timeout if non_block is enabled",
+ )
+ end
+ end
+ end
+
describe "in non-blocking mode" do
it "removes an item from the queue" do
q = @object.call
diff --git a/spec/ruby/shared/rational/Rational.rb b/spec/ruby/shared/rational/Rational.rb
index 936a90c086..2dc49c869c 100644
--- a/spec/ruby/shared/rational/Rational.rb
+++ b/spec/ruby/shared/rational/Rational.rb
@@ -65,40 +65,40 @@ describe :kernel_Rational, shared: true do
r_s.should == r
r_s.should_not == f_r
end
+ end
- describe "when passed a Numeric" do
- it "calls #to_r to convert the first argument to a Rational" do
- num = RationalSpecs::SubNumeric.new(2)
+ describe "when passed a Numeric" do
+ it "calls #to_r to convert the first argument to a Rational" do
+ num = RationalSpecs::SubNumeric.new(2)
- Rational(num).should == Rational(2)
- end
+ Rational(num).should == Rational(2)
end
+ end
- describe "when passed a Complex" do
- it "returns a Rational from the real part if the imaginary part is 0" do
- Rational(Complex(1, 0)).should == Rational(1)
- end
-
- it "raises a RangeError if the imaginary part is not 0" do
- -> { Rational(Complex(1, 2)) }.should raise_error(RangeError)
- end
+ describe "when passed a Complex" do
+ it "returns a Rational from the real part if the imaginary part is 0" do
+ Rational(Complex(1, 0)).should == Rational(1)
end
- it "raises a TypeError if the first argument is nil" do
- -> { Rational(nil) }.should raise_error(TypeError)
+ it "raises a RangeError if the imaginary part is not 0" do
+ -> { Rational(Complex(1, 2)) }.should raise_error(RangeError)
end
+ end
- it "raises a TypeError if the second argument is nil" do
- -> { Rational(1, nil) }.should raise_error(TypeError)
- end
+ it "raises a TypeError if the first argument is nil" do
+ -> { Rational(nil) }.should raise_error(TypeError)
+ end
- it "raises a TypeError if the first argument is a Symbol" do
- -> { Rational(:sym) }.should raise_error(TypeError)
- end
+ it "raises a TypeError if the second argument is nil" do
+ -> { Rational(1, nil) }.should raise_error(TypeError)
+ end
- it "raises a TypeError if the second argument is a Symbol" do
- -> { Rational(1, :sym) }.should raise_error(TypeError)
- end
+ it "raises a TypeError if the first argument is a Symbol" do
+ -> { Rational(:sym) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the second argument is a Symbol" do
+ -> { Rational(1, :sym) }.should raise_error(TypeError)
end
describe "when passed exception: false" do
diff --git a/spec/ruby/shared/sizedqueue/enque.rb b/spec/ruby/shared/sizedqueue/enque.rb
index 6ef12349f8..059f1025a7 100644
--- a/spec/ruby/shared/sizedqueue/enque.rb
+++ b/spec/ruby/shared/sizedqueue/enque.rb
@@ -47,4 +47,67 @@ describe :sizedqueue_enq, shared: true do
t.join
q.pop.should == 1
end
+
+ describe "with a timeout" do
+ ruby_version_is "3.2" do
+ it "returns self if the item was pushed in time" do
+ q = @object.call(1)
+ q << 1
+
+ t = Thread.new {
+ q.send(@method, 2, timeout: 1).should == q
+ }
+ Thread.pass until t.status == "sleep" && q.num_waiting == 1
+ q.pop
+ t.join
+ end
+
+ it "does nothing if the timeout is nil" do
+ q = @object.call(1)
+ q << 1
+ t = Thread.new {
+ q.send(@method, 2, timeout: nil).should == q
+ }
+ t.join(0.2).should == nil
+ q.pop
+ t.join
+ end
+
+ it "returns nil if no space is avialable and timeout is 0" do
+ q = @object.call(1)
+ q.send(@method, 1, timeout: 0).should == q
+ q.send(@method, 2, timeout: 0).should == nil
+ end
+
+ it "returns nil if no space is available in time" do
+ q = @object.call(1)
+ q << 1
+ t = Thread.new {
+ q.send(@method, 2, timeout: 0.1).should == nil
+ }
+ t.join
+ end
+
+ it "raise TypeError if timeout is not a valid numeric" do
+ q = @object.call(1)
+ -> { q.send(@method, 2, timeout: "1") }.should raise_error(
+ TypeError,
+ "no implicit conversion to float from string",
+ )
+
+ -> { q.send(@method, 2, timeout: false) }.should raise_error(
+ TypeError,
+ "no implicit conversion to float from false",
+ )
+ end
+
+ it "raise ArgumentError if non_block = true is passed too" do
+ q = @object.call(1)
+ -> { q.send(@method, 2, true, timeout: 1) }.should raise_error(
+ ArgumentError,
+ "can't set a timeout if non_block is enabled",
+ )
+ end
+ end
+ end
end
diff --git a/spec/ruby/shared/sizedqueue/new.rb b/spec/ruby/shared/sizedqueue/new.rb
index 713785fb50..2573194efb 100644
--- a/spec/ruby/shared/sizedqueue/new.rb
+++ b/spec/ruby/shared/sizedqueue/new.rb
@@ -1,7 +1,12 @@
describe :sizedqueue_new, shared: true do
- it "raises a TypeError when the given argument is not Numeric" do
- -> { @object.call("foo") }.should raise_error(TypeError)
+ it "raises a TypeError when the given argument doesn't respond to #to_int" do
+ -> { @object.call("12") }.should raise_error(TypeError)
-> { @object.call(Object.new) }.should raise_error(TypeError)
+
+ @object.call(12.9).max.should == 12
+ object = Object.new
+ object.define_singleton_method(:to_int) { 42 }
+ @object.call(object).max.should == 42
end
it "raises an argument error when no argument is given" do
diff --git a/spec/ruby/shared/string/end_with.rb b/spec/ruby/shared/string/end_with.rb
index 5f2a011235..0e4c1386e8 100644
--- a/spec/ruby/shared/string/end_with.rb
+++ b/spec/ruby/shared/string/end_with.rb
@@ -38,7 +38,7 @@ describe :end_with, shared: true do
it "uses only the needed arguments" do
find = mock('h')
find.should_not_receive(:to_str)
- "hello".send(@method).should.end_with?("o",find)
+ "hello".send(@method).should.end_with?("o", find)
end
it "works for multibyte strings" do
@@ -51,4 +51,11 @@ describe :end_with, shared: true do
"あれ".send(@method).end_with?(pat)
end.should raise_error(Encoding::CompatibilityError)
end
+
+ it "checks that we are starting to match at the head of a character" do
+ "\xC3\xA9".send(@method).should_not.end_with?("\xA9")
+ "\xe3\x81\x82".send(@method).should_not.end_with?("\x82")
+ "ab".force_encoding("UTF-16BE").send(@method).should_not.end_with?(
+ "b".force_encoding("UTF-16BE"))
+ end
end
diff --git a/spec/ruby/shared/string/start_with.rb b/spec/ruby/shared/string/start_with.rb
index d8d6e13f6a..6932a017b6 100644
--- a/spec/ruby/shared/string/start_with.rb
+++ b/spec/ruby/shared/string/start_with.rb
@@ -69,4 +69,8 @@ describe :start_with, shared: true do
Regexp.last_match.should be_nil
$1.should be_nil
end
+
+ it "does not check that we are not matching part of a character" do
+ "\xC3\xA9".send(@method).should.start_with?("\xC3")
+ end
end
diff --git a/spec/ruby/spec_helper.rb b/spec/ruby/spec_helper.rb
index f92d316c2a..3404521c03 100644
--- a/spec/ruby/spec_helper.rb
+++ b/spec/ruby/spec_helper.rb
@@ -27,7 +27,8 @@ unless ENV['MSPEC_RUNNER'] # Running directly with ruby some_spec.rb
end
end
-ruby_version_is ""..."2.7" do
+# Compare with SpecVersion directly here so it works even with --unguarded
+if VersionGuard::FULL_RUBY_VERSION < SpecVersion.new('2.7')
abort "This version of ruby/spec requires Ruby 2.7+"
end
diff --git a/spec/syntax_suggest/fixtures/derailed_require_tree.rb.txt b/spec/syntax_suggest/fixtures/derailed_require_tree.rb.txt
new file mode 100644
index 0000000000..668ac4010b
--- /dev/null
+++ b/spec/syntax_suggest/fixtures/derailed_require_tree.rb.txt
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+# Tree structure used to store and sort require memory costs
+# RequireTree.new('get_process_mem')
+module DerailedBenchmarks
+ class RequireTree
+ REQUIRED_BY = {}
+
+ attr_reader :name
+ attr_writer :cost
+ attr_accessor :parent
+
+ def initialize(name)
+ @name = name
+ @children = {}
+ @cost = 0
+
+ def self.reset!
+ REQUIRED_BY.clear
+ if defined?(Kernel::REQUIRE_STACK)
+ Kernel::REQUIRE_STACK.clear
+
+ Kernel::REQUIRE_STACK.push(TOP_REQUIRE)
+ end
+ end
+
+ def <<(tree)
+ @children[tree.name.to_s] = tree
+ tree.parent = self
+ (REQUIRED_BY[tree.name.to_s] ||= []) << self.name
+ end
+
+ def [](name)
+ @children[name.to_s]
+ end
+
+ # Returns array of child nodes
+ def children
+ @children.values
+ end
+
+ def cost
+ @cost || 0
+ end
+
+ # Returns sorted array of child nodes from Largest to Smallest
+ def sorted_children
+ children.sort { |c1, c2| c2.cost <=> c1.cost }
+ end
+
+ def to_string
+ str = String.new("#{name}: #{cost.round(4)} MiB")
+ if parent && REQUIRED_BY[self.name.to_s]
+ names = REQUIRED_BY[self.name.to_s].uniq - [parent.name.to_s]
+ if names.any?
+ str << " (Also required by: #{ names.first(2).join(", ") }"
+ str << ", and #{names.count - 2} others" if names.count > 3
+ str << ")"
+ end
+ end
+ str
+ end
+
+ # Recursively prints all child nodes
+ def print_sorted_children(level = 0, out = STDOUT)
+ return if cost < ENV['CUT_OFF'].to_f
+ out.puts " " * level + self.to_string
+ level += 1
+ sorted_children.each do |child|
+ child.print_sorted_children(level, out)
+ end
+ end
+ end
+end
diff --git a/spec/syntax_suggest/fixtures/rexe.rb.txt b/spec/syntax_suggest/fixtures/rexe.rb.txt
new file mode 100755
index 0000000000..92e44d4d1e
--- /dev/null
+++ b/spec/syntax_suggest/fixtures/rexe.rb.txt
@@ -0,0 +1,569 @@
+#!/usr/bin/env ruby
+#
+# rexe - Ruby Command Line Executor Filter
+#
+# Inspired by https://github.com/thisredone/rb
+
+# frozen_string_literal: true
+
+
+require 'bundler'
+require 'date'
+require 'optparse'
+require 'ostruct'
+require 'shellwords'
+
+class Rexe
+
+ VERSION = '1.5.1'
+
+ PROJECT_URL = 'https://github.com/keithrbennett/rexe'
+
+
+ module Helpers
+
+ # Try executing code. If error raised, print message (but not stack trace) & exit -1.
+ def try
+ begin
+ yield
+ rescue Exception => e
+ unless e.class == SystemExit
+ $stderr.puts("rexe: #{e}")
+ $stderr.puts("Use the -h option to get help.")
+ exit(-1)
+ end
+ end
+ end
+ end
+
+
+ class Options < Struct.new(
+ :input_filespec,
+ :input_format,
+ :input_mode,
+ :loads,
+ :output_format,
+ :output_format_tty,
+ :output_format_block,
+ :requires,
+ :log_format,
+ :noop)
+
+
+ def initialize
+ super
+ clear
+ end
+
+
+ def clear
+ self.input_filespec = nil
+ self.input_format = :none
+ self.input_mode = :none
+ self.output_format = :none
+ self.output_format_tty = :none
+ self.output_format_block = :none
+ self.loads = []
+ self.requires = []
+ self.log_format = :none
+ self.noop = false
+ end
+ end
+
+
+
+
+
+ class Lookups
+ def input_modes
+ @input_modes ||= {
+ 'l' => :line,
+ 'e' => :enumerator,
+ 'b' => :one_big_string,
+ 'n' => :none
+ }
+ end
+
+
+ def input_formats
+ @input_formats ||= {
+ 'j' => :json,
+ 'm' => :marshal,
+ 'n' => :none,
+ 'y' => :yaml,
+ }
+ end
+
+
+ def input_parsers
+ @input_parsers ||= {
+ json: ->(string) { JSON.parse(string) },
+ marshal: ->(string) { Marshal.load(string) },
+ none: ->(string) { string },
+ yaml: ->(string) { YAML.load(string) },
+ }
+ end
+
+
+ def output_formats
+ @output_formats ||= {
+ 'a' => :amazing_print,
+ 'i' => :inspect,
+ 'j' => :json,
+ 'J' => :pretty_json,
+ 'm' => :marshal,
+ 'n' => :none,
+ 'p' => :puts, # default
+ 'P' => :pretty_print,
+ 's' => :to_s,
+ 'y' => :yaml,
+ }
+ end
+
+
+ def formatters
+ @formatters ||= {
+ amazing_print: ->(obj) { obj.ai + "\n" },
+ inspect: ->(obj) { obj.inspect + "\n" },
+ json: ->(obj) { obj.to_json },
+ marshal: ->(obj) { Marshal.dump(obj) },
+ none: ->(_obj) { nil },
+ pretty_json: ->(obj) { JSON.pretty_generate(obj) },
+ pretty_print: ->(obj) { obj.pretty_inspect },
+ puts: ->(obj) { require 'stringio'; sio = StringIO.new; sio.puts(obj); sio.string },
+ to_s: ->(obj) { obj.to_s + "\n" },
+ yaml: ->(obj) { obj.to_yaml },
+ }
+ end
+
+
+ def format_requires
+ @format_requires ||= {
+ json: 'json',
+ pretty_json: 'json',
+ amazing_print: 'amazing_print',
+ pretty_print: 'pp',
+ yaml: 'yaml'
+ }
+ end
+ end
+
+
+
+ class CommandLineParser
+
+ include Helpers
+
+ attr_reader :lookups, :options
+
+ def initialize
+ @lookups = Lookups.new
+ @options = Options.new
+ end
+
+
+ # Inserts contents of REXE_OPTIONS environment variable at the beginning of ARGV.
+ private def prepend_environment_options
+ env_opt_string = ENV['REXE_OPTIONS']
+ if env_opt_string
+ args_to_prepend = Shellwords.shellsplit(env_opt_string)
+ ARGV.unshift(args_to_prepend).flatten!
+ end
+ end
+
+
+ private def add_format_requires_to_requires_list
+ formats = [options.input_format, options.output_format, options.log_format]
+ requires = formats.map { |format| lookups.format_requires[format] }.uniq.compact
+ requires.each { |r| options.requires << r }
+ end
+
+
+ private def help_text
+ unless @help_text
+ @help_text ||= <<~HEREDOC
+
+ rexe -- Ruby Command Line Executor/Filter -- v#{VERSION} -- #{PROJECT_URL}
+
+ Executes Ruby code on the command line,
+ optionally automating management of standard input and standard output,
+ and optionally parsing input and formatting output with YAML, JSON, etc.
+
+ rexe [options] [Ruby source code]
+
+ Options:
+
+ -c --clear_options Clear all previous command line options specified up to now
+ -f --input_file Use this file instead of stdin for preprocessed input;
+ if filespec has a YAML and JSON file extension,
+ sets input format accordingly and sets input mode to -mb
+ -g --log_format FORMAT Log format, logs to stderr, defaults to -gn (none)
+ (see -o for format options)
+ -h, --help Print help and exit
+ -i, --input_format FORMAT Input format, defaults to -in (None)
+ -ij JSON
+ -im Marshal
+ -in None (default)
+ -iy YAML
+ -l, --load RUBY_FILE(S) Ruby file(s) to load, comma separated;
+ ! to clear all, or precede a name with '-' to remove
+ -m, --input_mode MODE Input preprocessing mode (determines what `self` will be)
+ defaults to -mn (none)
+ -ml line; each line is ingested as a separate string
+ -me enumerator (each_line on STDIN or File)
+ -mb big string; all lines combined into one string
+ -mn none (default); no input preprocessing;
+ self is an Object.new
+ -n, --[no-]noop Do not execute the code (useful with -g);
+ For true: yes, true, y, +; for false: no, false, n
+ -o, --output_format FORMAT Output format, defaults to -on (no output):
+ -oa Amazing Print
+ -oi Inspect
+ -oj JSON
+ -oJ Pretty JSON
+ -om Marshal
+ -on No Output (default)
+ -op Puts
+ -oP Pretty Print
+ -os to_s
+ -oy YAML
+ If 2 letters are provided, 1st is for tty devices, 2nd for block
+ --project-url Outputs project URL on Github, then exits
+ -r, --require REQUIRE(S) Gems and built-in libraries to require, comma separated;
+ ! to clear all, or precede a name with '-' to remove
+ -v, --version Prints version and exits
+
+ ---------------------------------------------------------------------------------------
+
+ In many cases you will need to enclose your source code in single or double quotes.
+
+ If source code is not specified, it will default to 'self',
+ which is most likely useful only in a filter mode (-ml, -me, -mb).
+
+ If there is a .rexerc file in your home directory, it will be run as Ruby code
+ before processing the input.
+
+ If there is a REXE_OPTIONS environment variable, its content will be prepended
+ to the command line so that you can specify options implicitly
+ (e.g. `export REXE_OPTIONS="-r amazing_print,yaml"`)
+
+ HEREDOC
+
+ @help_text.freeze
+ end
+
+ @help_text
+ end
+
+
+ # File file input mode; detects the input mode (JSON, YAML, or None) from the extension.
+ private def autodetect_file_format(filespec)
+ extension = File.extname(filespec).downcase
+ if extension == '.json'
+ :json
+ elsif extension == '.yml' || extension == '.yaml'
+ :yaml
+ else
+ :none
+ end
+ end
+
+
+ private def open_resource(resource_identifier)
+ command = case (`uname`.chomp)
+ when 'Darwin'
+ 'open'
+ when 'Linux'
+ 'xdg-open'
+ else
+ 'start'
+ end
+
+ `#{command} #{resource_identifier}`
+ end
+
+
+ # Using 'optparse', parses the command line.
+ # Settings go into this instance's properties (see Struct declaration).
+ def parse
+
+ prepend_environment_options
+
+ OptionParser.new do |parser|
+
+ parser.on('-c', '--clear_options', "Clear all previous command line options") do |v|
+ options.clear
+ end
+
+ parser.on('-f', '--input_file FILESPEC',
+ 'Use this file instead of stdin; autodetects YAML and JSON file extensions') do |v|
+ unless File.exist?(v)
+ raise "File #{v} does not exist."
+ end
+ options.input_filespec = v
+ options.input_format = autodetect_file_format(v)
+ if [:json, :yaml].include?(options.input_format)
+ options.input_mode = :one_big_string
+ end
+ end
+
+ parser.on('-g', '--log_format FORMAT', 'Log format, logs to stderr, defaults to none (see -o for format options)') do |v|
+ options.log_format = lookups.output_formats[v]
+ if options.log_format.nil?
+ raise("Output mode was '#{v}' but must be one of #{lookups.output_formats.keys}.")
+ end
+ end
+
+ parser.on("-h", "--help", "Show help") do |_help_requested|
+ puts help_text
+ exit
+ end
+
+ parser.on('-i', '--input_format FORMAT',
+ 'Mode with which to parse input values (n = none (default), j = JSON, m = Marshal, y = YAML') do |v|
+
+ options.input_format = lookups.input_formats[v]
+ if options.input_format.nil?
+ raise("Input mode was '#{v}' but must be one of #{lookups.input_formats.keys}.")
+ end
+ end
+
+ parser.on('-l', '--load RUBY_FILE(S)', 'Ruby file(s) to load, comma separated, or ! to clear') do |v|
+ if v == '!'
+ options.loads.clear
+ else
+ loadfiles = v.split(',').map(&:strip).map { |s| File.expand_path(s) }
+ removes, adds = loadfiles.partition { |filespec| filespec[0] == '-' }
+
+ existent, nonexistent = adds.partition { |filespec| File.exists?(filespec) }
+ if nonexistent.any?
+ raise("\nDid not find the following files to load: #{nonexistent}\n\n")
+ else
+ existent.each { |filespec| options.loads << filespec }
+ end
+
+ removes.each { |filespec| options.loads -= [filespec[1..-1]] }
+ end
+ end
+
+ parser.on('-m', '--input_mode MODE',
+ 'Mode with which to handle input (-ml, -me, -mb, -mn (default)') do |v|
+
+ options.input_mode = lookups.input_modes[v]
+ if options.input_mode.nil?
+ raise("Input mode was '#{v}' but must be one of #{lookups.input_modes.keys}.")
+ end
+ end
+
+ # See https://stackoverflow.com/questions/54576873/ruby-optionparser-short-code-for-boolean-option
+ # for an excellent explanation of this optparse incantation.
+ # According to the answer, valid options are:
+ # -n no, -n yes, -n false, -n true, -n n, -n y, -n +, but not -n -.
+ parser.on('-n', '--[no-]noop [FLAG]', TrueClass, "Do not execute the code (useful with -g)") do |v|
+ options.noop = (v.nil? ? true : v)
+ end
+
+ parser.on('-o', '--output_format FORMAT',
+ 'Mode with which to format values for output (`-o` + [aijJmnpsy])') do |v|
+ options.output_format_tty = lookups.output_formats[v[0]]
+ options.output_format_block = lookups.output_formats[v[-1]]
+ options.output_format = ($stdout.tty? ? options.output_format_tty : options.output_format_block)
+ if [options.output_format_tty, options.output_format_block].include?(nil)
+ raise("Bad output mode '#{v}'; each must be one of #{lookups.output_formats.keys}.")
+ end
+ end
+
+ parser.on('-r', '--require REQUIRE(S)',
+ 'Gems and built-in libraries (e.g. shellwords, yaml) to require, comma separated, or ! to clear') do |v|
+ if v == '!'
+ options.requires.clear
+ else
+ v.split(',').map(&:strip).each do |r|
+ if r[0] == '-'
+ options.requires -= [r[1..-1]]
+ else
+ options.requires << r
+ end
+ end
+ end
+ end
+
+ parser.on('-v', '--version', 'Print version') do
+ puts VERSION
+ exit(0)
+ end
+
+ # Undocumented feature: open Github project with default web browser on a Mac
+ parser.on('', '--open-project') do
+ open_resource(PROJECT_URL)
+ exit(0)
+ end
+
+ parser.on('', '--project-url') do
+ puts PROJECT_URL
+ exit(0)
+ end
+
+ end.parse!
+
+ # We want to do this after all options have been processed because we don't want any clearing of the
+ # options (by '-c', etc.) to result in exclusion of these needed requires.
+ add_format_requires_to_requires_list
+
+ options.requires = options.requires.sort.uniq
+ options.loads.uniq!
+
+ options
+
+ end
+ end
+
+
+ class Main
+
+ include Helpers
+
+ attr_reader :callable, :input_parser, :lookups,
+ :options, :output_formatter,
+ :log_formatter, :start_time, :user_source_code
+
+
+ def initialize
+ @lookups = Lookups.new
+ @start_time = DateTime.now
+ end
+
+
+ private def load_global_config_if_exists
+ filespec = File.join(Dir.home, '.rexerc')
+ load(filespec) if File.exists?(filespec)
+ end
+
+
+ private def init_parser_and_formatters
+ @input_parser = lookups.input_parsers[options.input_format]
+ @output_formatter = lookups.formatters[options.output_format]
+ @log_formatter = lookups.formatters[options.log_format]
+ end
+
+
+ # Executes the user specified code in the manner appropriate to the input mode.
+ # Performs any optionally specified parsing on input and formatting on output.
+ private def execute(eval_context_object, code)
+ if options.input_format != :none && options.input_mode != :none
+ eval_context_object = input_parser.(eval_context_object)
+ end
+
+ value = eval_context_object.instance_eval(&code)
+
+ unless options.output_format == :none
+ print output_formatter.(value)
+ end
+ rescue Errno::EPIPE
+ exit(-13)
+ end
+
+
+ # The global $RC (Rexe Context) OpenStruct is available in your user code.
+ # In order to make it possible to access this object in your loaded files, we are not creating
+ # it here; instead we add properties to it. This way, you can initialize an OpenStruct yourself
+ # in your loaded code and it will still work. If you do that, beware, any properties you add will be
+ # included in the log output. If the to_s of your added objects is large, that might be a pain.
+ private def init_rexe_context
+ $RC ||= OpenStruct.new
+ $RC.count = 0
+ $RC.rexe_version = VERSION
+ $RC.start_time = start_time.iso8601
+ $RC.source_code = user_source_code
+ $RC.options = options.to_h
+
+ def $RC.i; count end # `i` aliases `count` so you can more concisely get the count in your user code
+ end
+
+
+ private def create_callable
+ eval("Proc.new { #{user_source_code} }")
+ end
+
+
+ private def lookup_action(mode)
+ input = options.input_filespec ? File.open(options.input_filespec) : STDIN
+ {
+ line: -> { input.each { |l| execute(l.chomp, callable); $RC.count += 1 } },
+ enumerator: -> { execute(input.each_line, callable); $RC.count += 1 },
+ one_big_string: -> { big_string = input.read; execute(big_string, callable); $RC.count += 1 },
+ none: -> { execute(Object.new, callable) }
+ }.fetch(mode)
+ end
+
+
+ private def output_log_entry
+ if options.log_format != :none
+ $RC.duration_secs = Time.now - start_time.to_time
+ STDERR.puts(log_formatter.($RC.to_h))
+ end
+ end
+
+
+ # Bypasses Bundler's restriction on loading gems
+ # (see https://stackoverflow.com/questions/55144094/bundler-doesnt-permit-using-gems-in-project-home-directory)
+ private def require!(the_require)
+ begin
+ require the_require
+ rescue LoadError => error
+ gem_path = `gem which #{the_require}`
+ if gem_path.chomp.strip.empty?
+ raise error # re-raise the error, can't fix it
+ else
+ load_dir = File.dirname(gem_path)
+ $LOAD_PATH += load_dir
+ require the_require
+ end
+ end
+ end
+
+
+ # This class' entry point.
+ def call
+
+ try do
+
+ @options = CommandLineParser.new.parse
+
+ options.requires.each { |r| require!(r) }
+ load_global_config_if_exists
+ options.loads.each { |file| load(file) }
+
+ @user_source_code = ARGV.join(' ')
+ @user_source_code = 'self' if @user_source_code == ''
+
+ @callable = create_callable
+
+ init_rexe_context
+ init_parser_and_formatters
+
+ # This is where the user's source code will be executed; the action will in turn call `execute`.
+ lookup_action(options.input_mode).call unless options.noop
+
+ output_log_entry
+ end
+ end
+ end
+end
+
+
+def bundler_run(&block)
+ # This used to be an unconditional call to with_clean_env but that method is now deprecated:
+ # [DEPRECATED] `Bundler.with_clean_env` has been deprecated in favor of `Bundler.with_unbundled_env`.
+ # If you instead want the environment before bundler was originally loaded,
+ # use `Bundler.with_original_env`
+
+ if Bundler.respond_to?(:with_unbundled_env)
+ Bundler.with_unbundled_env { block.call }
+ else
+ Bundler.with_clean_env { block.call }
+ end
+end
+
+
+bundler_run { Rexe::Main.new.call }
diff --git a/spec/syntax_suggest/fixtures/routes.rb.txt b/spec/syntax_suggest/fixtures/routes.rb.txt
new file mode 100644
index 0000000000..86733821c0
--- /dev/null
+++ b/spec/syntax_suggest/fixtures/routes.rb.txt
@@ -0,0 +1,121 @@
+Rails.application.routes.draw do
+ constraints -> { Rails.application.config.non_production } do
+ namespace :foo do
+ resource :bar
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+ constraints -> { Rails.application.config.non_production } do
+ namespace :bar do
+ resource :baz
+ end
+ end
+
+ namespace :admin do
+ resource :session
+
+ match "/foobar(*path)", via: :all, to: redirect { |_params, req|
+ uri = URI(req.path.gsub("foobar", "foobaz"))
+ uri.query = req.query_string.presence
+ uri.to_s
+ }
+end
diff --git a/spec/syntax_suggest/fixtures/ruby_buildpack.rb.txt b/spec/syntax_suggest/fixtures/ruby_buildpack.rb.txt
new file mode 100644
index 0000000000..9acdbf3a61
--- /dev/null
+++ b/spec/syntax_suggest/fixtures/ruby_buildpack.rb.txt
@@ -0,0 +1,1344 @@
+require "tmpdir"
+require "digest/md5"
+require "benchmark"
+require "rubygems"
+require "language_pack"
+require "language_pack/base"
+require "language_pack/ruby_version"
+require "language_pack/helpers/nodebin"
+require "language_pack/helpers/node_installer"
+require "language_pack/helpers/yarn_installer"
+require "language_pack/helpers/layer"
+require "language_pack/helpers/binstub_check"
+require "language_pack/version"
+
+# base Ruby Language Pack. This is for any base ruby app.
+class LanguagePack::Ruby < LanguagePack::Base
+ NAME = "ruby"
+ LIBYAML_VERSION = "0.1.7"
+ LIBYAML_PATH = "libyaml-#{LIBYAML_VERSION}"
+ RBX_BASE_URL = "http://binaries.rubini.us/heroku"
+ NODE_BP_PATH = "vendor/node/bin"
+
+ Layer = LanguagePack::Helpers::Layer
+
+ # detects if this is a valid Ruby app
+ # @return [Boolean] true if it's a Ruby app
+ def self.use?
+ instrument "ruby.use" do
+ File.exist?("Gemfile")
+ end
+ end
+
+ def self.bundler
+ @@bundler ||= LanguagePack::Helpers::BundlerWrapper.new.install
+ end
+
+ def bundler
+ self.class.bundler
+ end
+
+ def initialize(*args)
+ super(*args)
+ @fetchers[:mri] = LanguagePack::Fetcher.new(VENDOR_URL, @stack)
+ @fetchers[:rbx] = LanguagePack::Fetcher.new(RBX_BASE_URL, @stack)
+ @node_installer = LanguagePack::Helpers::NodeInstaller.new
+ @yarn_installer = LanguagePack::Helpers::YarnInstaller.new
+ end
+
+ def name
+ "Ruby"
+ end
+
+ def default_addons
+ instrument "ruby.default_addons" do
+ add_dev_database_addon
+ end
+ end
+
+ def default_config_vars
+ instrument "ruby.default_config_vars" do
+ vars = {
+ "LANG" => env("LANG") || "en_US.UTF-8",
+ }
+
+ ruby_version.jruby? ? vars.merge({
+ "JRUBY_OPTS" => default_jruby_opts
+ }) : vars
+ end
+ end
+
+ def default_process_types
+ instrument "ruby.default_process_types" do
+ {
+ "rake" => "bundle exec rake",
+ "console" => "bundle exec irb"
+ }
+ end
+ end
+
+ def best_practice_warnings
+ if bundler.has_gem?("asset_sync")
+ warn(<<-WARNING)
+You are using the `asset_sync` gem.
+This is not recommended.
+See https://devcenter.heroku.com/articles/please-do-not-use-asset-sync for more information.
+WARNING
+ end
+ end
+
+ def compile
+ instrument 'ruby.compile' do
+ # check for new app at the beginning of the compile
+ new_app?
+ Dir.chdir(build_path)
+ remove_vendor_bundle
+ warn_bundler_upgrade
+ warn_bad_binstubs
+ install_ruby(slug_vendor_ruby, build_ruby_path)
+ setup_language_pack_environment(
+ ruby_layer_path: File.expand_path("."),
+ gem_layer_path: File.expand_path("."),
+ bundle_path: "vendor/bundle",
+ bundle_default_without: "development:test"
+ )
+ allow_git do
+ install_bundler_in_app(slug_vendor_base)
+ load_bundler_cache
+ build_bundler
+ post_bundler
+ create_database_yml
+ install_binaries
+ run_assets_precompile_rake_task
+ end
+ config_detect
+ best_practice_warnings
+ warn_outdated_ruby
+ setup_profiled(ruby_layer_path: "$HOME", gem_layer_path: "$HOME") # $HOME is set to /app at run time
+ setup_export
+ cleanup
+ super
+ end
+ rescue => e
+ warn_outdated_ruby
+ raise e
+ end
+
+
+ def build
+ new_app?
+ remove_vendor_bundle
+ warn_bad_binstubs
+ ruby_layer = Layer.new(@layer_dir, "ruby", launch: true)
+ install_ruby("#{ruby_layer.path}/#{slug_vendor_ruby}")
+ ruby_layer.metadata[:version] = ruby_version.version
+ ruby_layer.metadata[:patchlevel] = ruby_version.patchlevel if ruby_version.patchlevel
+ ruby_layer.metadata[:engine] = ruby_version.engine.to_s
+ ruby_layer.metadata[:engine_version] = ruby_version.engine_version
+ ruby_layer.write
+
+ gem_layer = Layer.new(@layer_dir, "gems", launch: true, cache: true, build: true)
+ setup_language_pack_environment(
+ ruby_layer_path: ruby_layer.path,
+ gem_layer_path: gem_layer.path,
+ bundle_path: "#{gem_layer.path}/vendor/bundle",
+ bundle_default_without: "development:test"
+ )
+ allow_git do
+ # TODO install bundler in separate layer
+ topic "Loading Bundler Cache"
+ gem_layer.validate! do |metadata|
+ valid_bundler_cache?(gem_layer.path, gem_layer.metadata)
+ end
+ install_bundler_in_app("#{gem_layer.path}/#{slug_vendor_base}")
+ build_bundler
+ # TODO post_bundler might need to be done in a new layer
+ bundler.clean
+ gem_layer.metadata[:gems] = Digest::SHA2.hexdigest(File.read("Gemfile.lock"))
+ gem_layer.metadata[:stack] = @stack
+ gem_layer.metadata[:ruby_version] = run_stdout(%q(ruby -v)).strip
+ gem_layer.metadata[:rubygems_version] = run_stdout(%q(gem -v)).strip
+ gem_layer.metadata[:buildpack_version] = BUILDPACK_VERSION
+ gem_layer.write
+
+ create_database_yml
+ # TODO replace this with multibuildpack stuff? put binaries in their own layer?
+ install_binaries
+ run_assets_precompile_rake_task
+ end
+ setup_profiled(ruby_layer_path: ruby_layer.path, gem_layer_path: gem_layer.path)
+ setup_export(gem_layer)
+ config_detect
+ best_practice_warnings
+ cleanup
+
+ super
+ end
+
+ def cleanup
+ end
+
+ def config_detect
+ end
+
+private
+
+ # A bad shebang line looks like this:
+ #
+ # ```
+ # #!/usr/bin/env ruby2.5
+ # ```
+ #
+ # Since `ruby2.5` is not a valid binary name
+ #
+ def warn_bad_binstubs
+ check = LanguagePack::Helpers::BinstubCheck.new(app_root_dir: Dir.pwd, warn_object: self)
+ check.call
+ end
+
+ def default_malloc_arena_max?
+ return true if @metadata.exists?("default_malloc_arena_max")
+ return @metadata.touch("default_malloc_arena_max") if new_app?
+
+ return false
+ end
+
+ def warn_bundler_upgrade
+ old_bundler_version = @metadata.read("bundler_version").strip if @metadata.exists?("bundler_version")
+
+ if old_bundler_version && old_bundler_version != bundler.version
+ warn(<<-WARNING, inline: true)
+Your app was upgraded to bundler #{ bundler.version }.
+Previously you had a successful deploy with bundler #{ old_bundler_version }.
+
+If you see problems related to the bundler version please refer to:
+https://devcenter.heroku.com/articles/bundler-version#known-upgrade-issues
+
+WARNING
+ end
+ end
+
+ # For example "vendor/bundle/ruby/2.6.0"
+ def self.slug_vendor_base
+ @slug_vendor_base ||= begin
+ command = %q(ruby -e "require 'rbconfig';puts \"vendor/bundle/#{RUBY_ENGINE}/#{RbConfig::CONFIG['ruby_version']}\"")
+ out = run_no_pipe(command, user_env: true).strip
+ error "Problem detecting bundler vendor directory: #{out}" unless $?.success?
+ out
+ end
+ end
+
+ # the relative path to the bundler directory of gems
+ # @return [String] resulting path
+ def slug_vendor_base
+ instrument 'ruby.slug_vendor_base' do
+ @slug_vendor_base ||= self.class.slug_vendor_base
+ end
+ end
+
+ # the relative path to the vendored ruby directory
+ # @return [String] resulting path
+ def slug_vendor_ruby
+ "vendor/#{ruby_version.version_without_patchlevel}"
+ end
+
+ # the absolute path of the build ruby to use during the buildpack
+ # @return [String] resulting path
+ def build_ruby_path
+ "/tmp/#{ruby_version.version_without_patchlevel}"
+ end
+
+ # fetch the ruby version from bundler
+ # @return [String, nil] returns the ruby version if detected or nil if none is detected
+ def ruby_version
+ instrument 'ruby.ruby_version' do
+ return @ruby_version if @ruby_version
+ new_app = !File.exist?("vendor/heroku")
+ last_version_file = "buildpack_ruby_version"
+ last_version = nil
+ last_version = @metadata.read(last_version_file).strip if @metadata.exists?(last_version_file)
+
+ @ruby_version = LanguagePack::RubyVersion.new(bundler.ruby_version,
+ is_new: new_app,
+ last_version: last_version)
+ return @ruby_version
+ end
+ end
+
+ def set_default_web_concurrency
+ <<-EOF
+case $(ulimit -u) in
+256)
+ export HEROKU_RAM_LIMIT_MB=${HEROKU_RAM_LIMIT_MB:-512}
+ export WEB_CONCURRENCY=${WEB_CONCURRENCY:-2}
+ ;;
+512)
+ export HEROKU_RAM_LIMIT_MB=${HEROKU_RAM_LIMIT_MB:-1024}
+ export WEB_CONCURRENCY=${WEB_CONCURRENCY:-4}
+ ;;
+16384)
+ export HEROKU_RAM_LIMIT_MB=${HEROKU_RAM_LIMIT_MB:-2560}
+ export WEB_CONCURRENCY=${WEB_CONCURRENCY:-8}
+ ;;
+32768)
+ export HEROKU_RAM_LIMIT_MB=${HEROKU_RAM_LIMIT_MB:-6144}
+ export WEB_CONCURRENCY=${WEB_CONCURRENCY:-16}
+ ;;
+*)
+ ;;
+esac
+EOF
+ end
+
+ # default JRUBY_OPTS
+ # return [String] string of JRUBY_OPTS
+ def default_jruby_opts
+ "-Xcompile.invokedynamic=false"
+ end
+
+ # sets up the environment variables for the build process
+ def setup_language_pack_environment(ruby_layer_path:, gem_layer_path:, bundle_path:, bundle_default_without:)
+ instrument 'ruby.setup_language_pack_environment' do
+ if ruby_version.jruby?
+ ENV["PATH"] += ":bin"
+ ENV["JRUBY_OPTS"] = env('JRUBY_BUILD_OPTS') || env('JRUBY_OPTS')
+ end
+ setup_ruby_install_env(ruby_layer_path)
+
+ # By default Node can address 1.5GB of memory, a limitation it inherits from
+ # the underlying v8 engine. This can occasionally cause issues during frontend
+ # builds where memory use can exceed this threshold.
+ #
+ # This passes an argument to all Node processes during the build, so that they
+ # can take advantage of all available memory on the build dynos.
+ ENV["NODE_OPTIONS"] ||= "--max_old_space_size=2560"
+
+ # TODO when buildpack-env-args rolls out, we can get rid of
+ # ||= and the manual setting below
+ default_config_vars.each do |key, value|
+ ENV[key] ||= value
+ end
+
+ paths = []
+ gem_path = "#{gem_layer_path}/#{slug_vendor_base}"
+ ENV["GEM_PATH"] = gem_path
+ ENV["GEM_HOME"] = gem_path
+
+ ENV["DISABLE_SPRING"] = "1"
+
+ # Rails has a binstub for yarn that doesn't work for all applications
+ # we need to ensure that yarn comes before local bin dir for that case
+ paths << yarn_preinstall_bin_path if yarn_preinstalled?
+
+ # Need to remove `./bin` folder since it links to the wrong --prefix ruby binstubs breaking require in Ruby 1.9.2 and 1.8.7.
+ # Because for 1.9.2 and 1.8.7 there is a "build" ruby and a non-"build" Ruby
+ paths << "#{File.expand_path(".")}/bin" unless ruby_version.ruby_192_or_lower?
+
+ paths << "#{gem_layer_path}/#{bundler_binstubs_path}" # Binstubs from bundler, eg. vendor/bundle/bin
+ paths << "#{gem_layer_path}/#{slug_vendor_base}/bin" # Binstubs from rubygems, eg. vendor/bundle/ruby/2.6.0/bin
+ paths << ENV["PATH"]
+
+ ENV["PATH"] = paths.join(":")
+
+ ENV["BUNDLE_WITHOUT"] = env("BUNDLE_WITHOUT") || bundle_default_without
+ if ENV["BUNDLE_WITHOUT"].include?(' ')
+ ENV["BUNDLE_WITHOUT"] = ENV["BUNDLE_WITHOUT"].tr(' ', ':')
+
+ warn("Your BUNDLE_WITHOUT contains a space, we are converting it to a colon `:` BUNDLE_WITHOUT=#{ENV["BUNDLE_WITHOUT"]}", inline: true)
+ end
+ ENV["BUNDLE_PATH"] = bundle_path
+ ENV["BUNDLE_BIN"] = bundler_binstubs_path
+ ENV["BUNDLE_DEPLOYMENT"] = "1"
+ ENV["BUNDLE_GLOBAL_PATH_APPENDS_RUBY_SCOPE"] = "1" if bundler.needs_ruby_global_append_path?
+ end
+ end
+
+ # Sets up the environment variables for subsequent processes run by
+ # muiltibuildpack. We can't use profile.d because $HOME isn't set up
+ def setup_export(layer = nil)
+ instrument 'ruby.setup_export' do
+ if layer
+ paths = ENV["PATH"]
+ else
+ paths = ENV["PATH"].split(":").map do |path|
+ /^\/.*/ !~ path ? "#{build_path}/#{path}" : path
+ end.join(":")
+ end
+
+ # TODO ensure path exported is correct
+ set_export_path "PATH", paths, layer
+
+ if layer
+ gem_path = "#{layer.path}/#{slug_vendor_base}"
+ else
+ gem_path = "#{build_path}/#{slug_vendor_base}"
+ end
+ set_export_path "GEM_PATH", gem_path, layer
+ set_export_default "LANG", "en_US.UTF-8", layer
+
+ # TODO handle jruby
+ if ruby_version.jruby?
+ set_export_default "JRUBY_OPTS", default_jruby_opts
+ end
+
+ set_export_default "BUNDLE_PATH", ENV["BUNDLE_PATH"], layer
+ set_export_default "BUNDLE_WITHOUT", ENV["BUNDLE_WITHOUT"], layer
+ set_export_default "BUNDLE_BIN", ENV["BUNDLE_BIN"], layer
+ set_export_default "BUNDLE_GLOBAL_PATH_APPENDS_RUBY_SCOPE", ENV["BUNDLE_GLOBAL_PATH_APPENDS_RUBY_SCOPE"], layer if bundler.needs_ruby_global_append_path?
+ set_export_default "BUNDLE_DEPLOYMENT", ENV["BUNDLE_DEPLOYMENT"], layer if ENV["BUNDLE_DEPLOYMENT"] # Unset on windows since we delete the Gemfile.lock
+ end
+ end
+
+ # sets up the profile.d script for this buildpack
+ def setup_profiled(ruby_layer_path: , gem_layer_path: )
+ instrument 'setup_profiled' do
+ profiled_path = []
+
+ # Rails has a binstub for yarn that doesn't work for all applications
+ # we need to ensure that yarn comes before local bin dir for that case
+ if yarn_preinstalled?
+ profiled_path << yarn_preinstall_bin_path.gsub(File.expand_path("."), "$HOME")
+ elsif has_yarn_binary?
+ profiled_path << "#{ruby_layer_path}/vendor/#{@yarn_installer.binary_path}"
+ end
+ profiled_path << "$HOME/bin" # /app in production
+ profiled_path << "#{gem_layer_path}/#{bundler_binstubs_path}" # Binstubs from bundler, eg. vendor/bundle/bin
+ profiled_path << "#{gem_layer_path}/#{slug_vendor_base}/bin" # Binstubs from rubygems, eg. vendor/bundle/ruby/2.6.0/bin
+ profiled_path << "$PATH"
+
+ set_env_default "LANG", "en_US.UTF-8"
+ set_env_override "GEM_PATH", "#{gem_layer_path}/#{slug_vendor_base}:$GEM_PATH"
+ set_env_override "PATH", profiled_path.join(":")
+ set_env_override "DISABLE_SPRING", "1"
+
+ set_env_default "MALLOC_ARENA_MAX", "2" if default_malloc_arena_max?
+
+ web_concurrency = env("SENSIBLE_DEFAULTS") ? set_default_web_concurrency : ""
+ add_to_profiled(web_concurrency, filename: "WEB_CONCURRENCY.sh", mode: "w") # always write that file, even if its empty (meaning no defaults apply), for interop with other buildpacks - and we overwrite the file rather than appending (which is the default)
+
+ # TODO handle JRUBY
+ if ruby_version.jruby?
+ set_env_default "JRUBY_OPTS", default_jruby_opts
+ end
+
+ set_env_default "BUNDLE_PATH", ENV["BUNDLE_PATH"]
+ set_env_default "BUNDLE_WITHOUT", ENV["BUNDLE_WITHOUT"]
+ set_env_default "BUNDLE_BIN", ENV["BUNDLE_BIN"]
+ set_env_default "BUNDLE_GLOBAL_PATH_APPENDS_RUBY_SCOPE", ENV["BUNDLE_GLOBAL_PATH_APPENDS_RUBY_SCOPE"] if bundler.needs_ruby_global_append_path?
+ set_env_default "BUNDLE_DEPLOYMENT", ENV["BUNDLE_DEPLOYMENT"] if ENV["BUNDLE_DEPLOYMENT"] # Unset on windows since we delete the Gemfile.lock
+ end
+ end
+
+ def warn_outdated_ruby
+ return unless defined?(@outdated_version_check)
+
+ @warn_outdated ||= begin
+ @outdated_version_check.join
+
+ warn_outdated_minor
+ warn_outdated_eol
+ warn_stack_upgrade
+ true
+ end
+ end
+
+ def warn_stack_upgrade
+ return unless defined?(@ruby_download_check)
+ return unless @ruby_download_check.next_stack(current_stack: stack)
+ return if @ruby_download_check.exists_on_next_stack?(current_stack: stack)
+
+ warn(<<~WARNING)
+ Your Ruby version is not present on the next stack
+
+ You are currently using #{ruby_version.version_for_download} on #{stack} stack.
+ This version does not exist on #{@ruby_download_check.next_stack(current_stack: stack)}. In order to upgrade your stack you will
+ need to upgrade to a supported Ruby version.
+
+ For a list of supported Ruby versions see:
+ https://devcenter.heroku.com/articles/ruby-support#supported-runtimes
+
+ For a list of the oldest Ruby versions present on a given stack see:
+ https://devcenter.heroku.com/articles/ruby-support#oldest-available-runtimes
+ WARNING
+ end
+
+ def warn_outdated_eol
+ return unless @outdated_version_check.maybe_eol?
+
+ if @outdated_version_check.eol?
+ warn(<<~WARNING)
+ EOL Ruby Version
+
+ You are using a Ruby version that has reached its End of Life (EOL)
+
+ We strongly suggest you upgrade to Ruby #{@outdated_version_check.suggest_ruby_eol_version} or later
+
+ Your current Ruby version no longer receives security updates from
+ Ruby Core and may have serious vulnerabilities. While you will continue
+ to be able to deploy on Heroku with this Ruby version you must upgrade
+ to a non-EOL version to be eligible to receive support.
+
+ Upgrade your Ruby version as soon as possible.
+
+ For a list of supported Ruby versions see:
+ https://devcenter.heroku.com/articles/ruby-support#supported-runtimes
+ WARNING
+ else
+ # Maybe EOL
+ warn(<<~WARNING)
+ Potential EOL Ruby Version
+
+ You are using a Ruby version that has either reached its End of Life (EOL)
+ or will reach its End of Life on December 25th of this year.
+
+ We suggest you upgrade to Ruby #{@outdated_version_check.suggest_ruby_eol_version} or later
+
+ Once a Ruby version becomes EOL, it will no longer receive
+ security updates from Ruby core and may have serious vulnerabilities.
+
+ Please upgrade your Ruby version.
+
+ For a list of supported Ruby versions see:
+ https://devcenter.heroku.com/articles/ruby-support#supported-runtimes
+ WARNING
+ end
+ end
+
+ def warn_outdated_minor
+ return if @outdated_version_check.latest_minor_version?
+
+ warn(<<~WARNING)
+ There is a more recent Ruby version available for you to use:
+
+ #{@outdated_version_check.suggested_ruby_minor_version}
+
+ The latest version will include security and bug fixes. We always recommend
+ running the latest version of your minor release.
+
+ Please upgrade your Ruby version.
+
+ For all available Ruby versions see:
+ https://devcenter.heroku.com/articles/ruby-support#supported-runtimes
+ WARNING
+ end
+
+ # install the vendored ruby
+ # @return [Boolean] true if it installs the vendored ruby and false otherwise
+ def install_ruby(install_path, build_ruby_path = nil)
+ instrument 'ruby.install_ruby' do
+ # Could do a compare operation to avoid re-downloading ruby
+ return false unless ruby_version
+ installer = LanguagePack::Installers::RubyInstaller.installer(ruby_version).new(@stack)
+
+ @ruby_download_check = LanguagePack::Helpers::DownloadPresence.new(ruby_version.file_name)
+ @ruby_download_check.call
+
+ if ruby_version.build?
+ installer.fetch_unpack(ruby_version, build_ruby_path, true)
+ end
+
+ installer.install(ruby_version, install_path)
+
+ @outdated_version_check = LanguagePack::Helpers::OutdatedRubyVersion.new(
+ current_ruby_version: ruby_version,
+ fetcher: installer.fetcher
+ )
+ @outdated_version_check.call
+
+ @metadata.write("buildpack_ruby_version", ruby_version.version_for_download)
+
+ topic "Using Ruby version: #{ruby_version.version_for_download}"
+ if !ruby_version.set
+ warn(<<~WARNING)
+ You have not declared a Ruby version in your Gemfile.
+
+ To declare a Ruby version add this line to your Gemfile:
+
+ ```
+ ruby "#{LanguagePack::RubyVersion::DEFAULT_VERSION_NUMBER}"
+ ```
+
+ For more information see:
+ https://devcenter.heroku.com/articles/ruby-versions
+ WARNING
+ end
+
+ if ruby_version.warn_ruby_26_bundler?
+ warn(<<~WARNING, inline: true)
+ There is a known bundler bug with your version of Ruby
+
+ Your version of Ruby contains a problem with the built-in integration of bundler. If
+ you encounter a bundler error you need to upgrade your Ruby version. We suggest you upgrade to:
+
+ #{@outdated_version_check.suggested_ruby_minor_version}
+
+ For more information see:
+ https://devcenter.heroku.com/articles/bundler-version#known-upgrade-issues
+ WARNING
+ end
+ end
+
+ true
+ rescue LanguagePack::Fetcher::FetchError
+ if @ruby_download_check.does_not_exist?
+ message = <<~ERROR
+ The Ruby version you are trying to install does not exist: #{ruby_version.version_for_download}
+ ERROR
+ else
+ message = <<~ERROR
+ The Ruby version you are trying to install does not exist on this stack.
+
+ You are trying to install #{ruby_version.version_for_download} on #{stack}.
+
+ Ruby #{ruby_version.version_for_download} is present on the following stacks:
+
+ - #{@ruby_download_check.valid_stack_list.join("\n - ")}
+ ERROR
+
+ if env("CI")
+ message << <<~ERROR
+
+ On Heroku CI you can set your stack in the `app.json`. For example:
+
+ ```
+ "stack": "heroku-20"
+ ```
+ ERROR
+ end
+ end
+
+ message << <<~ERROR
+
+ Heroku recommends you use the latest supported Ruby version listed here:
+ https://devcenter.heroku.com/articles/ruby-support#supported-runtimes
+
+ For more information on syntax for declaring a Ruby version see:
+ https://devcenter.heroku.com/articles/ruby-versions
+ ERROR
+
+ error message
+ end
+
+ # TODO make this compatible with CNB
+ def new_app?
+ @new_app ||= !File.exist?("vendor/heroku")
+ end
+
+ # find the ruby install path for its binstubs during build
+ # @return [String] resulting path or empty string if ruby is not vendored
+ def ruby_install_binstub_path(ruby_layer_path = ".")
+ @ruby_install_binstub_path ||=
+ if ruby_version.build?
+ "#{build_ruby_path}/bin"
+ elsif ruby_version
+ "#{ruby_layer_path}/#{slug_vendor_ruby}/bin"
+ else
+ ""
+ end
+ end
+
+ # setup the environment so we can use the vendored ruby
+ def setup_ruby_install_env(ruby_layer_path = ".")
+ instrument 'ruby.setup_ruby_install_env' do
+ ENV["PATH"] = "#{File.expand_path(ruby_install_binstub_path(ruby_layer_path))}:#{ENV["PATH"]}"
+ end
+ end
+
+ # installs vendored gems into the slug
+ def install_bundler_in_app(bundler_dir)
+ instrument 'ruby.install_language_pack_gems' do
+ FileUtils.mkdir_p(bundler_dir)
+ Dir.chdir(bundler_dir) do |dir|
+ `cp -R #{bundler.bundler_path}/. .`
+ end
+
+ # write bundler shim, so we can control the version bundler used
+ # Ruby 2.6.0 started vendoring bundler
+ write_bundler_shim("vendor/bundle/bin") if ruby_version.vendored_bundler?
+ end
+ end
+
+ # default set of binaries to install
+ # @return [Array] resulting list
+ def binaries
+ add_node_js_binary + add_yarn_binary
+ end
+
+ # vendors binaries into the slug
+ def install_binaries
+ instrument 'ruby.install_binaries' do
+ binaries.each {|binary| install_binary(binary) }
+ Dir["bin/*"].each {|path| run("chmod +x #{path}") }
+ end
+ end
+
+ # vendors individual binary into the slug
+ # @param [String] name of the binary package from S3.
+ # Example: https://s3.amazonaws.com/language-pack-ruby/node-0.4.7.tgz, where name is "node-0.4.7"
+ def install_binary(name)
+ topic "Installing #{name}"
+ bin_dir = "bin"
+ FileUtils.mkdir_p bin_dir
+ Dir.chdir(bin_dir) do |dir|
+ if name.match(/^node\-/)
+ @node_installer.install
+ # need to set PATH here b/c `node-gyp` can change the CWD, but still depends on executing node.
+ # the current PATH is relative, but it needs to be absolute for this.
+ # doing this here also prevents it from being exported during runtime
+ node_bin_path = File.absolute_path(".")
+ # this needs to be set after so other binaries in bin/ don't take precedence"
+ ENV["PATH"] = "#{ENV["PATH"]}:#{node_bin_path}"
+ elsif name.match(/^yarn\-/)
+ FileUtils.mkdir_p("../vendor")
+ Dir.chdir("../vendor") do |vendor_dir|
+ @yarn_installer.install
+ yarn_path = File.absolute_path("#{vendor_dir}/#{@yarn_installer.binary_path}")
+ ENV["PATH"] = "#{yarn_path}:#{ENV["PATH"]}"
+ end
+ else
+ @fetchers[:buildpack].fetch_untar("#{name}.tgz")
+ end
+ end
+ end
+
+ # removes a binary from the slug
+ # @param [String] relative path of the binary on the slug
+ def uninstall_binary(path)
+ FileUtils.rm File.join('bin', File.basename(path)), :force => true
+ end
+
+ def load_default_cache?
+ new_app? && ruby_version.default?
+ end
+
+ # loads a default bundler cache for new apps to speed up initial bundle installs
+ def load_default_cache
+ instrument "ruby.load_default_cache" do
+ if false # load_default_cache?
+ puts "New app detected loading default bundler cache"
+ patchlevel = run("ruby -e 'puts RUBY_PATCHLEVEL'").strip
+ cache_name = "#{LanguagePack::RubyVersion::DEFAULT_VERSION}-p#{patchlevel}-default-cache"
+ @fetchers[:buildpack].fetch_untar("#{cache_name}.tgz")
+ end
+ end
+ end
+
+ # remove `vendor/bundle` that comes from the git repo
+ # in case there are native ext.
+ # users should be using `bundle pack` instead.
+ # https://github.com/heroku/heroku-buildpack-ruby/issues/21
+ def remove_vendor_bundle
+ if File.exists?("vendor/bundle")
+ warn(<<-WARNING)
+Removing `vendor/bundle`.
+Checking in `vendor/bundle` is not supported. Please remove this directory
+and add it to your .gitignore. To vendor your gems with Bundler, use
+`bundle pack` instead.
+WARNING
+ FileUtils.rm_rf("vendor/bundle")
+ end
+ end
+
+ def bundler_binstubs_path
+ "vendor/bundle/bin"
+ end
+
+ def bundler_path
+ @bundler_path ||= "#{slug_vendor_base}/gems/#{bundler.dir_name}"
+ end
+
+ def write_bundler_shim(path)
+ FileUtils.mkdir_p(path)
+ shim_path = "#{path}/bundle"
+ File.open(shim_path, "w") do |file|
+ file.print <<-BUNDLE
+#!/usr/bin/env ruby
+require 'rubygems'
+
+version = "#{bundler.version}"
+
+if ARGV.first
+ str = ARGV.first
+ str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
+ if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then
+ version = $1
+ ARGV.shift
+ end
+end
+
+if Gem.respond_to?(:activate_bin_path)
+load Gem.activate_bin_path('bundler', 'bundle', version)
+else
+gem "bundler", version
+load Gem.bin_path("bundler", "bundle", version)
+end
+BUNDLE
+ end
+ FileUtils.chmod(0755, shim_path)
+ end
+
+ # runs bundler to install the dependencies
+ def build_bundler
+ instrument 'ruby.build_bundler' do
+ log("bundle") do
+ if File.exist?("#{Dir.pwd}/.bundle/config")
+ warn(<<~WARNING, inline: true)
+ You have the `.bundle/config` file checked into your repository
+ It contains local state like the location of the installed bundle
+ as well as configured git local gems, and other settings that should
+ not be shared between multiple checkouts of a single repo. Please
+ remove the `.bundle/` folder from your repo and add it to your `.gitignore` file.
+
+ https://devcenter.heroku.com/articles/bundler-configuration
+ WARNING
+ end
+
+ if bundler.windows_gemfile_lock?
+ log("bundle", "has_windows_gemfile_lock")
+
+ File.unlink("Gemfile.lock")
+ ENV.delete("BUNDLE_DEPLOYMENT")
+
+ warn(<<~WARNING, inline: true)
+ Removing `Gemfile.lock` because it was generated on Windows.
+ Bundler will do a full resolve so native gems are handled properly.
+ This may result in unexpected gem versions being used in your app.
+ In rare occasions Bundler may not be able to resolve your dependencies at all.
+
+ https://devcenter.heroku.com/articles/bundler-windows-gemfile
+ WARNING
+ end
+
+ bundle_command = String.new("")
+ bundle_command << "BUNDLE_WITHOUT='#{ENV["BUNDLE_WITHOUT"]}' "
+ bundle_command << "BUNDLE_PATH=#{ENV["BUNDLE_PATH"]} "
+ bundle_command << "BUNDLE_BIN=#{ENV["BUNDLE_BIN"]} "
+ bundle_command << "BUNDLE_DEPLOYMENT=#{ENV["BUNDLE_DEPLOYMENT"]} " if ENV["BUNDLE_DEPLOYMENT"] # Unset on windows since we delete the Gemfile.lock
+ bundle_command << "BUNDLE_GLOBAL_PATH_APPENDS_RUBY_SCOPE=#{ENV["BUNDLE_GLOBAL_PATH_APPENDS_RUBY_SCOPE"]} " if bundler.needs_ruby_global_append_path?
+ bundle_command << "bundle install -j4"
+
+ topic("Installing dependencies using bundler #{bundler.version}")
+
+ bundler_output = String.new("")
+ bundle_time = nil
+ env_vars = {}
+ Dir.mktmpdir("libyaml-") do |tmpdir|
+ libyaml_dir = "#{tmpdir}/#{LIBYAML_PATH}"
+
+ # need to setup compile environment for the psych gem
+ yaml_include = File.expand_path("#{libyaml_dir}/include").shellescape
+ yaml_lib = File.expand_path("#{libyaml_dir}/lib").shellescape
+ pwd = Dir.pwd
+ bundler_path = "#{pwd}/#{slug_vendor_base}/gems/#{bundler.dir_name}/lib"
+
+ # we need to set BUNDLE_CONFIG and BUNDLE_GEMFILE for
+ # codon since it uses bundler.
+ env_vars["BUNDLE_GEMFILE"] = "#{pwd}/Gemfile"
+ env_vars["BUNDLE_CONFIG"] = "#{pwd}/.bundle/config"
+ env_vars["CPATH"] = noshellescape("#{yaml_include}:$CPATH")
+ env_vars["CPPATH"] = noshellescape("#{yaml_include}:$CPPATH")
+ env_vars["LIBRARY_PATH"] = noshellescape("#{yaml_lib}:$LIBRARY_PATH")
+ env_vars["RUBYOPT"] = syck_hack
+ env_vars["NOKOGIRI_USE_SYSTEM_LIBRARIES"] = "true"
+ env_vars["BUNDLE_DISABLE_VERSION_CHECK"] = "true"
+ env_vars["BUNDLER_LIB_PATH"] = "#{bundler_path}" if ruby_version.ruby_version == "1.8.7"
+ env_vars["BUNDLE_DISABLE_VERSION_CHECK"] = "true"
+
+ puts "Running: #{bundle_command}"
+ instrument "ruby.bundle_install" do
+ bundle_time = Benchmark.realtime do
+ bundler_output << pipe("#{bundle_command} --no-clean", out: "2>&1", env: env_vars, user_env: true)
+ end
+ end
+ end
+
+ if $?.success?
+ puts "Bundle completed (#{"%.2f" % bundle_time}s)"
+ log "bundle", :status => "success"
+ puts "Cleaning up the bundler cache."
+ instrument "ruby.bundle_clean" do
+ # Only show bundle clean output when not using default cache
+ if load_default_cache?
+ run("bundle clean > /dev/null", user_env: true, env: env_vars)
+ else
+ pipe("bundle clean", out: "2> /dev/null", user_env: true, env: env_vars)
+ end
+ end
+ @bundler_cache.store
+
+ # Keep gem cache out of the slug
+ FileUtils.rm_rf("#{slug_vendor_base}/cache")
+ else
+ mcount "fail.bundle.install"
+ log "bundle", :status => "failure"
+ error_message = "Failed to install gems via Bundler."
+ puts "Bundler Output: #{bundler_output}"
+ if bundler_output.match(/An error occurred while installing sqlite3/)
+ mcount "fail.sqlite3"
+ error_message += <<~ERROR
+
+ Detected sqlite3 gem which is not supported on Heroku:
+ https://devcenter.heroku.com/articles/sqlite3
+ ERROR
+ end
+
+ if bundler_output.match(/but your Gemfile specified/)
+ mcount "fail.ruby_version_mismatch"
+ error_message += <<~ERROR
+
+ Detected a mismatch between your Ruby version installed and
+ Ruby version specified in Gemfile or Gemfile.lock. You can
+ correct this by running:
+
+ $ bundle update --ruby
+ $ git add Gemfile.lock
+ $ git commit -m "update ruby version"
+
+ If this does not solve the issue please see this documentation:
+
+ https://devcenter.heroku.com/articles/ruby-versions#your-ruby-version-is-x-but-your-gemfile-specified-y
+ ERROR
+ end
+
+ error error_message
+ end
+ end
+ end
+ end
+
+ def post_bundler
+ instrument "ruby.post_bundler" do
+ Dir[File.join(slug_vendor_base, "**", ".git")].each do |dir|
+ FileUtils.rm_rf(dir)
+ end
+ bundler.clean
+ end
+ end
+
+ # RUBYOPT line that requires syck_hack file
+ # @return [String] require string if needed or else an empty string
+ def syck_hack
+ instrument "ruby.syck_hack" do
+ syck_hack_file = File.expand_path(File.join(File.dirname(__FILE__), "../../vendor/syck_hack"))
+ rv = run_stdout('ruby -e "puts RUBY_VERSION"').strip
+ # < 1.9.3 includes syck, so we need to use the syck hack
+ if Gem::Version.new(rv) < Gem::Version.new("1.9.3")
+ "-r#{syck_hack_file}"
+ else
+ ""
+ end
+ end
+ end
+
+ # writes ERB based database.yml for Rails. The database.yml uses the DATABASE_URL from the environment during runtime.
+ def create_database_yml
+ instrument 'ruby.create_database_yml' do
+ return false unless File.directory?("config")
+ return false if bundler.has_gem?('activerecord') && bundler.gem_version('activerecord') >= Gem::Version.new('4.1.0.beta1')
+
+ log("create_database_yml") do
+ topic("Writing config/database.yml to read from DATABASE_URL")
+ File.open("config/database.yml", "w") do |file|
+ file.puts <<-DATABASE_YML
+<%
+
+require 'cgi'
+require 'uri'
+
+begin
+ uri = URI.parse(ENV["DATABASE_URL"])
+rescue URI::InvalidURIError
+ raise "Invalid DATABASE_URL"
+end
+
+raise "No RACK_ENV or RAILS_ENV found" unless ENV["RAILS_ENV"] || ENV["RACK_ENV"]
+
+def attribute(name, value, force_string = false)
+ if value
+ value_string =
+ if force_string
+ '"' + value + '"'
+ else
+ value
+ end
+ "\#{name}: \#{value_string}"
+ else
+ ""
+ end
+end
+
+adapter = uri.scheme
+adapter = "postgresql" if adapter == "postgres"
+
+database = (uri.path || "").split("/")[1]
+
+username = uri.user
+password = uri.password
+
+host = uri.host
+port = uri.port
+
+params = CGI.parse(uri.query || "")
+
+%>
+
+<%= ENV["RAILS_ENV"] || ENV["RACK_ENV"] %>:
+ <%= attribute "adapter", adapter %>
+ <%= attribute "database", database %>
+ <%= attribute "username", username %>
+ <%= attribute "password", password, true %>
+ <%= attribute "host", host %>
+ <%= attribute "port", port %>
+
+<% params.each do |key, value| %>
+ <%= key %>: <%= value.first %>
+<% end %>
+ DATABASE_YML
+ end
+ end
+ end
+ end
+
+ def rake
+ @rake ||= begin
+ rake_gem_available = bundler.has_gem?("rake") || ruby_version.rake_is_vendored?
+ raise_on_fail = bundler.gem_version('railties') && bundler.gem_version('railties') > Gem::Version.new('3.x')
+
+ topic "Detecting rake tasks"
+ rake = LanguagePack::Helpers::RakeRunner.new(rake_gem_available)
+ rake.load_rake_tasks!({ env: rake_env }, raise_on_fail)
+ rake
+ end
+ end
+
+ def rake_env
+ if database_url
+ { "DATABASE_URL" => database_url }
+ else
+ {}
+ end.merge(user_env_hash)
+ end
+
+ def database_url
+ env("DATABASE_URL") if env("DATABASE_URL")
+ end
+
+ # executes the block with GIT_DIR environment variable removed since it can mess with the current working directory git thinks it's in
+ # @param [block] block to be executed in the GIT_DIR free context
+ def allow_git(&blk)
+ git_dir = ENV.delete("GIT_DIR") # can mess with bundler
+ blk.call
+ ENV["GIT_DIR"] = git_dir
+ end
+
+ # decides if we need to enable the dev database addon
+ # @return [Array] the database addon if the pg gem is detected or an empty Array if it isn't.
+ def add_dev_database_addon
+ pg_adapters.any? {|a| bundler.has_gem?(a) } ? ['heroku-postgresql'] : []
+ end
+
+ def pg_adapters
+ [
+ "pg",
+ "activerecord-jdbcpostgresql-adapter",
+ "jdbc-postgres",
+ "jdbc-postgresql",
+ "jruby-pg",
+ "rjack-jdbc-postgres",
+ "tgbyte-activerecord-jdbcpostgresql-adapter"
+ ]
+ end
+
+ # decides if we need to install the node.js binary
+ # @note execjs will blow up if no JS RUNTIME is detected and is loaded.
+ # @return [Array] the node.js binary path if we need it or an empty Array
+ def add_node_js_binary
+ return [] if node_js_preinstalled?
+
+ if Pathname(build_path).join("package.json").exist? ||
+ bundler.has_gem?('execjs') ||
+ bundler.has_gem?('webpacker')
+ [@node_installer.binary_path]
+ else
+ []
+ end
+ end
+
+ def add_yarn_binary
+ return [] if yarn_preinstalled?
+|
+ if Pathname(build_path).join("yarn.lock").exist? || bundler.has_gem?('webpacker')
+ [@yarn_installer.name]
+ else
+ []
+ end
+ end
+
+ def has_yarn_binary?
+ add_yarn_binary.any?
+ end
+
+ # checks if node.js is installed via the official heroku-buildpack-nodejs using multibuildpack
+ # @return String if it's detected and false if it isn't
+ def node_preinstall_bin_path
+ return @node_preinstall_bin_path if defined?(@node_preinstall_bin_path)
+
+ legacy_path = "#{Dir.pwd}/#{NODE_BP_PATH}"
+ path = run("which node").strip
+ if path && $?.success?
+ @node_preinstall_bin_path = path
+ elsif run("#{legacy_path}/node -v") && $?.success?
+ @node_preinstall_bin_path = legacy_path
+ else
+ @node_preinstall_bin_path = false
+ end
+ end
+ alias :node_js_preinstalled? :node_preinstall_bin_path
+
+ def node_not_preinstalled?
+ !node_js_preinstalled?
+ end
+
+ # Example: tmp/build_8523f77fb96a956101d00988dfeed9d4/.heroku/yarn/bin/ (without the `yarn` at the end)
+ def yarn_preinstall_bin_path
+ (yarn_preinstall_binary_path || "").chomp("/yarn")
+ end
+
+ # Example `tmp/build_8523f77fb96a956101d00988dfeed9d4/.heroku/yarn/bin/yarn`
+ def yarn_preinstall_binary_path
+ return @yarn_preinstall_binary_path if defined?(@yarn_preinstall_binary_path)
+
+ path = run("which yarn").strip
+ if path && $?.success?
+ @yarn_preinstall_binary_path = path
+ else
+ @yarn_preinstall_binary_path = false
+ end
+ end
+
+ def yarn_preinstalled?
+ yarn_preinstall_binary_path
+ end
+
+ def yarn_not_preinstalled?
+ !yarn_preinstalled?
+ end
+
+ def run_assets_precompile_rake_task
+ instrument 'ruby.run_assets_precompile_rake_task' do
+
+ precompile = rake.task("assets:precompile")
+ return true unless precompile.is_defined?
+
+ topic "Precompiling assets"
+ precompile.invoke(env: rake_env)
+ if precompile.success?
+ puts "Asset precompilation completed (#{"%.2f" % precompile.time}s)"
+ else
+ precompile_fail(precompile.output)
+ end
+ end
+ end
+
+ def precompile_fail(output)
+ mcount "fail.assets_precompile"
+ log "assets_precompile", :status => "failure"
+ msg = "Precompiling assets failed.\n"
+ if output.match(/(127\.0\.0\.1)|(org\.postgresql\.util)/)
+ msg << "Attempted to access a nonexistent database:\n"
+ msg << "https://devcenter.heroku.com/articles/pre-provision-database\n"
+ end
+
+ sprockets_version = bundler.gem_version('sprockets')
+ if output.match(/Sprockets::FileNotFound/) && (sprockets_version < Gem::Version.new('4.0.0.beta7') && sprockets_version > Gem::Version.new('4.0.0.beta4'))
+ mcount "fail.assets_precompile.file_not_found_beta"
+ msg << "If you have this file in your project\n"
+ msg << "try upgrading to Sprockets 4.0.0.beta7 or later:\n"
+ msg << "https://github.com/rails/sprockets/pull/547\n"
+ end
+
+ error msg
+ end
+
+ def bundler_cache
+ "vendor/bundle"
+ end
+
+ def valid_bundler_cache?(path, metadata)
+ full_ruby_version = run_stdout(%q(ruby -v)).strip
+ rubygems_version = run_stdout(%q(gem -v)).strip
+ old_rubygems_version = nil
+
+ old_rubygems_version = metadata[:ruby_version]
+ old_stack = metadata[:stack]
+ old_stack ||= DEFAULT_LEGACY_STACK
+
+ stack_change = old_stack != @stack
+ if !new_app? && stack_change
+ return [false, "Purging Cache. Changing stack from #{old_stack} to #{@stack}"]
+ end
+
+ # fix bug from v37 deploy
+ if File.exists?("#{path}/vendor/ruby_version")
+ puts "Broken cache detected. Purging build cache."
+ cache.clear("vendor")
+ FileUtils.rm_rf("#{path}/vendor/ruby_version")
+ return [false, "Broken cache detected. Purging build cache."]
+ # fix bug introduced in v38
+ elsif !metadata.include?(:buildpack_version) && metadata.include?(:ruby_version)
+ puts "Broken cache detected. Purging build cache."
+ return [false, "Broken cache detected. Purging build cache."]
+ elsif (@bundler_cache.exists? || @bundler_cache.old?) && full_ruby_version != metadata[:ruby_version]
+ return [false, <<-MESSAGE]
+Ruby version change detected. Clearing bundler cache.
+Old: #{metadata[:ruby_version]}
+New: #{full_ruby_version}
+MESSAGE
+ end
+
+ # fix git gemspec bug from Bundler 1.3.0+ upgrade
+ if File.exists?(bundler_cache) && !metadata.include?(:bundler_version) && !run("find #{path}/vendor/bundle/*/*/bundler/gems/*/ -name *.gemspec").include?("No such file or directory")
+ return [false, "Old bundler cache detected. Clearing bundler cache."]
+ end
+
+ # fix for https://github.com/heroku/heroku-buildpack-ruby/issues/86
+ if (!metadata.include?(:rubygems_version) ||
+ (old_rubygems_version == "2.0.0" && old_rubygems_version != rubygems_version)) &&
+ metadata.include?(:ruby_version) && metadata[:ruby_version].strip.include?("ruby 2.0.0p0")
+ return [false, "Updating to rubygems #{rubygems_version}. Clearing bundler cache."]
+ end
+
+ # fix for https://github.com/sparklemotion/nokogiri/issues/923
+ if metadata.include?(:buildpack_version) && (bv = metadata[:buildpack_version].sub('v', '').to_i) && bv != 0 && bv <= 76
+ return [false, <<-MESSAGE]
+Fixing nokogiri install. Clearing bundler cache.
+See https://github.com/sparklemotion/nokogiri/issues/923.
+MESSAGE
+ end
+
+ # recompile nokogiri to use new libyaml
+ if metadata.include?(:buildpack_version) && (bv = metadata[:buildpack_version].sub('v', '').to_i) && bv != 0 && bv <= 99 && bundler.has_gem?("psych")
+ return [false, <<-MESSAGE]
+Need to recompile psych for CVE-2013-6393. Clearing bundler cache.
+See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=737076.
+MESSAGE
+ end
+
+ # recompile gems for libyaml 0.1.7 update
+ if metadata.include?(:buildpack_version) && (bv = metadata[:buildpack_version].sub('v', '').to_i) && bv != 0 && bv <= 147 &&
+ (metadata.include?(:ruby_version) && metadata[:ruby_version].match(/ruby 2\.1\.(9|10)/) ||
+ bundler.has_gem?("psych")
+ )
+ return [false, <<-MESSAGE]
+Need to recompile gems for CVE-2014-2014-9130. Clearing bundler cache.
+See https://devcenter.heroku.com/changelog-items/1016.
+MESSAGE
+ end
+
+ true
+ end
+
+ def load_bundler_cache
+ instrument "ruby.load_bundler_cache" do
+ cache.load "vendor"
+
+ full_ruby_version = run_stdout(%q(ruby -v)).strip
+ rubygems_version = run_stdout(%q(gem -v)).strip
+ heroku_metadata = "vendor/heroku"
+ old_rubygems_version = nil
+ ruby_version_cache = "ruby_version"
+ buildpack_version_cache = "buildpack_version"
+ bundler_version_cache = "bundler_version"
+ rubygems_version_cache = "rubygems_version"
+ stack_cache = "stack"
+
+ # bundle clean does not remove binstubs
+ FileUtils.rm_rf("vendor/bundler/bin")
+
+ old_rubygems_version = @metadata.read(ruby_version_cache).strip if @metadata.exists?(ruby_version_cache)
+ old_stack = @metadata.read(stack_cache).strip if @metadata.exists?(stack_cache)
+ old_stack ||= DEFAULT_LEGACY_STACK
+
+ stack_change = old_stack != @stack
+ convert_stack = @bundler_cache.old?
+ @bundler_cache.convert_stack(stack_change) if convert_stack
+ if !new_app? && stack_change
+ puts "Purging Cache. Changing stack from #{old_stack} to #{@stack}"
+ purge_bundler_cache(old_stack)
+ elsif !new_app? && !convert_stack
+ @bundler_cache.load
+ end
+
+ # fix bug from v37 deploy
+ if File.exists?("vendor/ruby_version")
+ puts "Broken cache detected. Purging build cache."
+ cache.clear("vendor")
+ FileUtils.rm_rf("vendor/ruby_version")
+ purge_bundler_cache
+ # fix bug introduced in v38
+ elsif !@metadata.include?(buildpack_version_cache) && @metadata.exists?(ruby_version_cache)
+ puts "Broken cache detected. Purging build cache."
+ purge_bundler_cache
+ elsif (@bundler_cache.exists? || @bundler_cache.old?) && @metadata.exists?(ruby_version_cache) && full_ruby_version != @metadata.read(ruby_version_cache).strip
+ puts "Ruby version change detected. Clearing bundler cache."
+ puts "Old: #{@metadata.read(ruby_version_cache).strip}"
+ puts "New: #{full_ruby_version}"
+ purge_bundler_cache
+ end
+
+ # fix git gemspec bug from Bundler 1.3.0+ upgrade
+ if File.exists?(bundler_cache) && !@metadata.exists?(bundler_version_cache) && !run("find vendor/bundle/*/*/bundler/gems/*/ -name *.gemspec").include?("No such file or directory")
+ puts "Old bundler cache detected. Clearing bundler cache."
+ purge_bundler_cache
+ end
+
+ # fix for https://github.com/heroku/heroku-buildpack-ruby/issues/86
+ if (!@metadata.exists?(rubygems_version_cache) ||
+ (old_rubygems_version == "2.0.0" && old_rubygems_version != rubygems_version)) &&
+ @metadata.exists?(ruby_version_cache) && @metadata.read(ruby_version_cache).strip.include?("ruby 2.0.0p0")
+ puts "Updating to rubygems #{rubygems_version}. Clearing bundler cache."
+ purge_bundler_cache
+ end
+
+ # fix for https://github.com/sparklemotion/nokogiri/issues/923
+ if @metadata.exists?(buildpack_version_cache) && (bv = @metadata.read(buildpack_version_cache).sub('v', '').to_i) && bv != 0 && bv <= 76
+ puts "Fixing nokogiri install. Clearing bundler cache."
+ puts "See https://github.com/sparklemotion/nokogiri/issues/923."
+ purge_bundler_cache
+ end
+
+ # recompile nokogiri to use new libyaml
+ if @metadata.exists?(buildpack_version_cache) && (bv = @metadata.read(buildpack_version_cache).sub('v', '').to_i) && bv != 0 && bv <= 99 && bundler.has_gem?("psych")
+ puts "Need to recompile psych for CVE-2013-6393. Clearing bundler cache."
+ puts "See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=737076."
+ purge_bundler_cache
+ end
+
+ # recompile gems for libyaml 0.1.7 update
+ if @metadata.exists?(buildpack_version_cache) && (bv = @metadata.read(buildpack_version_cache).sub('v', '').to_i) && bv != 0 && bv <= 147 &&
+ (@metadata.exists?(ruby_version_cache) && @metadata.read(ruby_version_cache).strip.match(/ruby 2\.1\.(9|10)/) ||
+ bundler.has_gem?("psych")
+ )
+ puts "Need to recompile gems for CVE-2014-2014-9130. Clearing bundler cache."
+ puts "See https://devcenter.heroku.com/changelog-items/1016."
+ purge_bundler_cache
+ end
+
+ FileUtils.mkdir_p(heroku_metadata)
+ @metadata.write(ruby_version_cache, full_ruby_version, false)
+ @metadata.write(buildpack_version_cache, BUILDPACK_VERSION, false)
+ @metadata.write(bundler_version_cache, bundler.version, false)
+ @metadata.write(rubygems_version_cache, rubygems_version, false)
+ @metadata.write(stack_cache, @stack, false)
+ @metadata.save
+ end
+ end
+
+ def purge_bundler_cache(stack = nil)
+ instrument "ruby.purge_bundler_cache" do
+ @bundler_cache.clear(stack)
+ # need to reinstall language pack gems
+ install_bundler_in_app(slug_vendor_base)
+ end
+ end
+end
diff --git a/spec/syntax_suggest/fixtures/syntax_tree.rb.txt b/spec/syntax_suggest/fixtures/syntax_tree.rb.txt
new file mode 100644
index 0000000000..1c110783f9
--- /dev/null
+++ b/spec/syntax_suggest/fixtures/syntax_tree.rb.txt
@@ -0,0 +1,9234 @@
+# frozen_string_literal: true
+
+require 'ripper'
+require_relative 'syntax_tree/version'
+
+class SyntaxTree < Ripper
+ # Represents a line in the source. If this class is being used, it means that
+ # every character in the string is 1 byte in length, so we can just return the
+ # start of the line + the index.
+ class SingleByteString
+ def initialize(start)
+ @start = start
+ end
+
+ def [](byteindex)
+ @start + byteindex
+ end
+ end
+
+ # Represents a line in the source. If this class is being used, it means that
+ # there are characters in the string that are multi-byte, so we will build up
+ # an array of indices, such that array[byteindex] will be equal to the index
+ # of the character within the string.
+ class MultiByteString
+ def initialize(start, line)
+ @indices = []
+
+ line
+ .each_char
+ .with_index(start) do |char, index|
+ char.bytesize.times { @indices << index }
+ end
+ end
+
+ def [](byteindex)
+ @indices[byteindex]
+ end
+ end
+
+ # Represents the location of a node in the tree from the source code.
+ class Location
+ attr_reader :start_line, :start_char, :end_line, :end_char
+
+ def initialize(start_line:, start_char:, end_line:, end_char:)
+ @start_line = start_line
+ @start_char = start_char
+ @end_line = end_line
+ @end_char = end_char
+ end
+
+ def ==(other)
+ other.is_a?(Location) && start_line == other.start_line &&
+ start_char == other.start_char && end_line == other.end_line &&
+ end_char == other.end_char
+ end
+
+ def to(other)
+ Location.new(
+ start_line: start_line,
+ start_char: start_char,
+ end_line: other.end_line,
+ end_char: other.end_char
+ )
+ end
+
+ def to_json(*opts)
+ [start_line, start_char, end_line, end_char].to_json(*opts)
+ end
+
+ def self.token(line:, char:, size:)
+ new(
+ start_line: line,
+ start_char: char,
+ end_line: line,
+ end_char: char + size
+ )
+ end
+
+ def self.fixed(line:, char:)
+ new(start_line: line, start_char: char, end_line: line, end_char: char)
+ end
+ end
+
+ # A special parser error so that we can get nice syntax displays on the error
+ # message when prettier prints out the results.
+ class ParseError < StandardError
+ attr_reader :lineno, :column
+
+ def initialize(error, lineno, column)
+ super(error)
+ @lineno = lineno
+ @column = column
+ end
+ end
+
+ attr_reader :source, :lines, :tokens
+
+ # This is an attr_accessor so Stmts objects can grab comments out of this
+ # array and attach them to themselves.
+ attr_accessor :comments
+
+ def initialize(source, *)
+ super
+
+ # We keep the source around so that we can refer back to it when we're
+ # generating the AST. Sometimes it's easier to just reference the source
+ # string when you want to check if it contains a certain character, for
+ # example.
+ @source = source
+
+ # Similarly, we keep the lines of the source string around to be able to
+ # check if certain lines contain certain characters. For example, we'll use
+ # this to generate the content that goes after the __END__ keyword. Or we'll
+ # use this to check if a comment has other content on its line.
+ @lines = source.split("\n")
+
+ # This is the full set of comments that have been found by the parser. It's
+ # a running list. At the end of every block of statements, they will go in
+ # and attempt to grab any comments that are on their own line and turn them
+ # into regular statements. So at the end of parsing the only comments left
+ # in here will be comments on lines that also contain code.
+ @comments = []
+
+ # This is the current embdoc (comments that start with =begin and end with
+ # =end). Since they can't be nested, there's no need for a stack here, as
+ # there can only be one active. These end up getting dumped into the
+ # comments list before getting picked up by the statements that surround
+ # them.
+ @embdoc = nil
+
+ # This is an optional node that can be present if the __END__ keyword is
+ # used in the file. In that case, this will represent the content after that
+ # keyword.
+ @__end__ = nil
+
+ # Heredocs can actually be nested together if you're using interpolation, so
+ # this is a stack of heredoc nodes that are currently being created. When we
+ # get to the token that finishes off a heredoc node, we pop the top
+ # one off. If there are others surrounding it, then the body events will now
+ # be added to the correct nodes.
+ @heredocs = []
+
+ # This is a running list of tokens that have fired. It's useful
+ # mostly for maintaining location information. For example, if you're inside
+ # the handle of a def event, then in order to determine where the AST node
+ # started, you need to look backward in the tokens to find a def
+ # keyword. Most of the time, when a parser event consumes one of these
+ # events, it will be deleted from the list. So ideally, this list stays
+ # pretty short over the course of parsing a source string.
+ @tokens = []
+
+ # Here we're going to build up a list of SingleByteString or MultiByteString
+ # objects. They're each going to represent a string in the source. They are
+ # used by the `char_pos` method to determine where we are in the source
+ # string.
+ @line_counts = []
+ last_index = 0
+
+ @source.lines.each do |line|
+ if line.size == line.bytesize
+ @line_counts << SingleByteString.new(last_index)
+ else
+ @line_counts << MultiByteString.new(last_index, line)
+ end
+
+ last_index += line.size
+ end
+ end
+
+ def self.parse(source)
+ parser = new(source)
+ response = parser.parse
+ response unless parser.error?
+ end
+
+ private
+
+ # ----------------------------------------------------------------------------
+ # :section: Helper methods
+ # The following methods are used by the ripper event handlers to either
+ # determine their bounds or query other nodes.
+ # ----------------------------------------------------------------------------
+
+ # This represents the current place in the source string that we've gotten to
+ # so far. We have a memoized line_counts object that we can use to get the
+ # number of characters that we've had to go through to get to the beginning of
+ # this line, then we add the number of columns into this line that we've gone
+ # through.
+ def char_pos
+ @line_counts[lineno - 1][column]
+ end
+
+ # As we build up a list of tokens, we'll periodically need to go backwards and
+ # find the ones that we've already hit in order to determine the location
+ # information for nodes that use them. For example, if you have a module node
+ # then you'll look backward for a kw token to determine your start location.
+ #
+ # This works with nesting since we're deleting tokens from the list once
+ # they've been used up. For example if you had nested module declarations then
+ # the innermost declaration would grab the last kw node that matches "module"
+ # (which would happen to be the innermost keyword). Then the outer one would
+ # only be able to grab the first one. In this way all of the tokens act as
+ # their own stack.
+ def find_token(type, value = :any, consume: true)
+ index =
+ tokens.rindex do |token|
+ token.is_a?(type) && (value == :any || (token.value == value))
+ end
+
+ if consume
+ # If we're expecting to be able to find a token and consume it,
+ # but can't actually find it, then we need to raise an error. This is
+ # _usually_ caused by a syntax error in the source that we're printing. It
+ # could also be caused by accidentally attempting to consume a token twice
+ # by two different parser event handlers.
+ unless index
+ message = "Cannot find expected #{value == :any ? type : value}"
+ raise ParseError.new(message, lineno, column)
+ end
+
+ tokens.delete_at(index)
+ elsif index
+ tokens[index]
+ end
+ end
+
+ # A helper function to find a :: operator. We do special handling instead of
+ # using find_token here because we don't pop off all of the ::
+ # operators so you could end up getting the wrong information if you have for
+ # instance ::X::Y::Z.
+ def find_colon2_before(const)
+ index =
+ tokens.rindex do |token|
+ token.is_a?(Op) && token.value == '::' &&
+ token.location.start_char < const.location.start_char
+ end
+
+ tokens[index]
+ end
+
+ # Finds the next position in the source string that begins a statement. This
+ # is used to bind statements lists and make sure they don't include a
+ # preceding comment. For example, we want the following comment to be attached
+ # to the class node and not the statement node:
+ #
+ # class Foo # :nodoc:
+ # ...
+ # end
+ #
+ # By finding the next non-space character, we can make sure that the bounds of
+ # the statement list are correct.
+ def find_next_statement_start(position)
+ remaining = source[position..-1]
+
+ if remaining.sub(/\A +/, '')[0] == '#'
+ return position + remaining.index("\n")
+ end
+
+ position
+ end
+
+ # ----------------------------------------------------------------------------
+ # :section: Ripper event handlers
+ # The following methods all handle a dispatched ripper event.
+ # ----------------------------------------------------------------------------
+
+ # BEGINBlock represents the use of the +BEGIN+ keyword, which hooks into the
+ # lifecycle of the interpreter. Whatever is inside the block will get executed
+ # when the program starts.
+ #
+ # BEGIN {
+ # }
+ #
+ # Interestingly, the BEGIN keyword doesn't allow the do and end keywords for
+ # the block. Only braces are permitted.
+ class BEGINBlock
+ # [LBrace] the left brace that is seen after the keyword
+ attr_reader :lbrace
+
+ # [Statements] the expressions to be executed
+ attr_reader :statements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(lbrace:, statements:, location:)
+ @lbrace = lbrace
+ @statements = statements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('BEGIN')
+ q.breakable
+ q.pp(statements)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :BEGIN,
+ lbrace: lbrace,
+ stmts: statements,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_BEGIN: (Statements statements) -> BEGINBlock
+ def on_BEGIN(statements)
+ lbrace = find_token(LBrace)
+ rbrace = find_token(RBrace)
+
+ statements.bind(
+ find_next_statement_start(lbrace.location.end_char),
+ rbrace.location.start_char
+ )
+
+ keyword = find_token(Kw, 'BEGIN')
+
+ BEGINBlock.new(
+ lbrace: lbrace,
+ statements: statements,
+ location: keyword.location.to(rbrace.location)
+ )
+ end
+
+ # CHAR irepresents a single codepoint in the script encoding.
+ #
+ # ?a
+ #
+ # In the example above, the CHAR node represents the string literal "a". You
+ # can use control characters with this as well, as in ?\C-a.
+ class CHAR
+ # [String] the value of the character literal
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('CHAR')
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :CHAR, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_CHAR: (String value) -> CHAR
+ def on_CHAR(value)
+ node =
+ CHAR.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # ENDBlock represents the use of the +END+ keyword, which hooks into the
+ # lifecycle of the interpreter. Whatever is inside the block will get executed
+ # when the program ends.
+ #
+ # END {
+ # }
+ #
+ # Interestingly, the END keyword doesn't allow the do and end keywords for the
+ # block. Only braces are permitted.
+ class ENDBlock
+ # [LBrace] the left brace that is seen after the keyword
+ attr_reader :lbrace
+
+ # [Statements] the expressions to be executed
+ attr_reader :statements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(lbrace:, statements:, location:)
+ @lbrace = lbrace
+ @statements = statements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('END')
+ q.breakable
+ q.pp(statements)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :END, lbrace: lbrace, stmts: statements, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_END: (Statements statements) -> ENDBlock
+ def on_END(statements)
+ lbrace = find_token(LBrace)
+ rbrace = find_token(RBrace)
+
+ statements.bind(
+ find_next_statement_start(lbrace.location.end_char),
+ rbrace.location.start_char
+ )
+
+ keyword = find_token(Kw, 'END')
+
+ ENDBlock.new(
+ lbrace: lbrace,
+ statements: statements,
+ location: keyword.location.to(rbrace.location)
+ )
+ end
+
+ # EndContent represents the use of __END__ syntax, which allows individual
+ # scripts to keep content after the main ruby code that can be read through
+ # the DATA constant.
+ #
+ # puts DATA.read
+ #
+ # __END__
+ # some other content that is not executed by the program
+ #
+ class EndContent
+ # [String] the content after the script
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('__end__')
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :__end__, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on___end__: (String value) -> EndContent
+ def on___end__(value)
+ @__end__ =
+ EndContent.new(
+ value: lines[lineno..-1].join("\n"),
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+ end
+
+ # Alias represents the use of the +alias+ keyword with regular arguments (not
+ # global variables). The +alias+ keyword is used to make a method respond to
+ # another name as well as the current one.
+ #
+ # alias aliased_name name
+ #
+ # For the example above, in the current context you can now call aliased_name
+ # and it will execute the name method. When you're aliasing two methods, you
+ # can either provide bare words (like the example above) or you can provide
+ # symbols (note that this includes dynamic symbols like
+ # :"left-#{middle}-right").
+ class Alias
+ # [DynaSymbol | SymbolLiteral] the new name of the method
+ attr_reader :left
+
+ # [DynaSymbol | SymbolLiteral] the old name of the method
+ attr_reader :right
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(left:, right:, location:)
+ @left = left
+ @right = right
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('alias')
+ q.breakable
+ q.pp(left)
+ q.breakable
+ q.pp(right)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :alias, left: left, right: right, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_alias: (
+ # (DynaSymbol | SymbolLiteral) left,
+ # (DynaSymbol | SymbolLiteral) right
+ # ) -> Alias
+ def on_alias(left, right)
+ keyword = find_token(Kw, 'alias')
+
+ Alias.new(
+ left: left,
+ right: right,
+ location: keyword.location.to(right.location)
+ )
+ end
+
+ # ARef represents when you're pulling a value out of a collection at a
+ # specific index. Put another way, it's any time you're calling the method
+ # #[].
+ #
+ # collection[index]
+ #
+ # The nodes usually contains two children, the collection and the index. In
+ # some cases, you don't necessarily have the second child node, because you
+ # can call procs with a pretty esoteric syntax. In the following example, you
+ # wouldn't have a second child node:
+ #
+ # collection[]
+ #
+ class ARef
+ # [untyped] the value being indexed
+ attr_reader :collection
+
+ # [nil | Args | ArgsAddBlock] the value being passed within the brackets
+ attr_reader :index
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(collection:, index:, location:)
+ @collection = collection
+ @index = index
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('aref')
+ q.breakable
+ q.pp(collection)
+ q.breakable
+ q.pp(index)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :aref,
+ collection: collection,
+ index: index,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_aref: (untyped collection, (nil | Args | ArgsAddBlock) index) -> ARef
+ def on_aref(collection, index)
+ find_token(LBracket)
+ rbracket = find_token(RBracket)
+
+ ARef.new(
+ collection: collection,
+ index: index,
+ location: collection.location.to(rbracket.location)
+ )
+ end
+
+ # ARefField represents assigning values into collections at specific indices.
+ # Put another way, it's any time you're calling the method #[]=. The
+ # ARefField node itself is just the left side of the assignment, and they're
+ # always wrapped in assign nodes.
+ #
+ # collection[index] = value
+ #
+ class ARefField
+ # [untyped] the value being indexed
+ attr_reader :collection
+
+ # [nil | ArgsAddBlock] the value being passed within the brackets
+ attr_reader :index
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(collection:, index:, location:)
+ @collection = collection
+ @index = index
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('aref_field')
+ q.breakable
+ q.pp(collection)
+ q.breakable
+ q.pp(index)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :aref_field,
+ collection: collection,
+ index: index,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_aref_field: (
+ # untyped collection,
+ # (nil | ArgsAddBlock) index
+ # ) -> ARefField
+ def on_aref_field(collection, index)
+ find_token(LBracket)
+ rbracket = find_token(RBracket)
+
+ ARefField.new(
+ collection: collection,
+ index: index,
+ location: collection.location.to(rbracket.location)
+ )
+ end
+
+ # def on_arg_ambiguous(value)
+ # value
+ # end
+
+ # ArgParen represents wrapping arguments to a method inside a set of
+ # parentheses.
+ #
+ # method(argument)
+ #
+ # In the example above, there would be an ArgParen node around the
+ # ArgsAddBlock node that represents the set of arguments being sent to the
+ # method method. The argument child node can be +nil+ if no arguments were
+ # passed, as in:
+ #
+ # method()
+ #
+ class ArgParen
+ # [nil | Args | ArgsAddBlock | ArgsForward] the arguments inside the
+ # parentheses
+ attr_reader :arguments
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(arguments:, location:)
+ @arguments = arguments
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('arg_paren')
+ q.breakable
+ q.pp(arguments)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :arg_paren, args: arguments, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_arg_paren: (
+ # (nil | Args | ArgsAddBlock | ArgsForward) arguments
+ # ) -> ArgParen
+ def on_arg_paren(arguments)
+ lparen = find_token(LParen)
+ rparen = find_token(RParen)
+
+ # If the arguments exceed the ending of the parentheses, then we know we
+ # have a heredoc in the arguments, and we need to use the bounds of the
+ # arguments to determine how large the arg_paren is.
+ ending =
+ if arguments && arguments.location.end_line > rparen.location.end_line
+ arguments
+ else
+ rparen
+ end
+
+ ArgParen.new(
+ arguments: arguments,
+ location: lparen.location.to(ending.location)
+ )
+ end
+
+ # Args represents a list of arguments being passed to a method call or array
+ # literal.
+ #
+ # method(first, second, third)
+ #
+ class Args
+ # [Array[ untyped ]] the arguments that this node wraps
+ attr_reader :parts
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parts:, location:)
+ @parts = parts
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('args')
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(parts) { |part| q.pp(part) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :args, parts: parts, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_args_add: (Args arguments, untyped argument) -> Args
+ def on_args_add(arguments, argument)
+ if arguments.parts.empty?
+ # If this is the first argument being passed into the list of arguments,
+ # then we're going to use the bounds of the argument to override the
+ # parent node's location since this will be more accurate.
+ Args.new(parts: [argument], location: argument.location)
+ else
+ # Otherwise we're going to update the existing list with the argument
+ # being added as well as the new end bounds.
+ Args.new(
+ parts: arguments.parts << argument,
+ location: arguments.location.to(argument.location)
+ )
+ end
+ end
+
+ # ArgsAddBlock represents a list of arguments and potentially a block
+ # argument. ArgsAddBlock is commonly seen being passed to any method where you
+ # use parentheses (wrapped in an ArgParen node). It’s also used to pass
+ # arguments to the various control-flow keywords like +return+.
+ #
+ # method(argument, &block)
+ #
+ class ArgsAddBlock
+ # [Args] the arguments before the optional block
+ attr_reader :arguments
+
+ # [nil | untyped] the optional block argument
+ attr_reader :block
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(arguments:, block:, location:)
+ @arguments = arguments
+ @block = block
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('args_add_block')
+ q.breakable
+ q.pp(arguments)
+ q.breakable
+ q.pp(block)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :args_add_block,
+ args: arguments,
+ block: block,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_args_add_block: (
+ # Args arguments,
+ # (false | untyped) block
+ # ) -> ArgsAddBlock
+ def on_args_add_block(arguments, block)
+ ending = block || arguments
+
+ ArgsAddBlock.new(
+ arguments: arguments,
+ block: block || nil,
+ location: arguments.location.to(ending.location)
+ )
+ end
+
+ # Star represents using a splat operator on an expression.
+ #
+ # method(*arguments)
+ #
+ class ArgStar
+ # [untyped] the expression being splatted
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('arg_star')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :arg_star, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_args_add_star: (Args arguments, untyped star) -> Args
+ def on_args_add_star(arguments, argument)
+ beginning = find_token(Op, '*')
+ ending = argument || beginning
+
+ location =
+ if arguments.parts.empty?
+ ending.location
+ else
+ arguments.location.to(ending.location)
+ end
+
+ arg_star =
+ ArgStar.new(
+ value: argument,
+ location: beginning.location.to(ending.location)
+ )
+
+ Args.new(parts: arguments.parts << arg_star, location: location)
+ end
+
+ # ArgsForward represents forwarding all kinds of arguments onto another method
+ # call.
+ #
+ # def request(method, path, **headers, &block); end
+ #
+ # def get(...)
+ # request(:GET, ...)
+ # end
+ #
+ # def post(...)
+ # request(:POST, ...)
+ # end
+ #
+ # In the example above, both the get and post methods are forwarding all of
+ # their arguments (positional, keyword, and block) on to the request method.
+ # The ArgsForward node appears in both the caller (the request method calls)
+ # and the callee (the get and post definitions).
+ class ArgsForward
+ # [String] the value of the operator
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('args_forward')
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :args_forward, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_args_forward: () -> ArgsForward
+ def on_args_forward
+ op = find_token(Op, '...')
+
+ ArgsForward.new(value: op.value, location: op.location)
+ end
+
+ # :call-seq:
+ # on_args_new: () -> Args
+ def on_args_new
+ Args.new(parts: [], location: Location.fixed(line: lineno, char: char_pos))
+ end
+
+ # ArrayLiteral represents any form of an array literal, and contains myriad
+ # child nodes because of the special array literal syntax like %w and %i.
+ #
+ # []
+ # [one, two, three]
+ # [*one_two_three]
+ # %i[one two three]
+ # %w[one two three]
+ # %I[one two three]
+ # %W[one two three]
+ #
+ # Every line in the example above produces an ArrayLiteral node. In order, the
+ # child contents node of this ArrayLiteral node would be nil, Args, QSymbols,
+ # QWords, Symbols, and Words.
+ class ArrayLiteral
+ # [nil | Args | QSymbols | QWords | Symbols | Words] the
+ # contents of the array
+ attr_reader :contents
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(contents:, location:)
+ @contents = contents
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('array')
+ q.breakable
+ q.pp(contents)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :array, cnts: contents, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_array: (
+ # (nil | Args | QSymbols | QWords | Symbols | Words) contents
+ # ) -> ArrayLiteral
+ def on_array(contents)
+ if !contents || contents.is_a?(Args)
+ lbracket = find_token(LBracket)
+ rbracket = find_token(RBracket)
+
+ ArrayLiteral.new(
+ contents: contents,
+ location: lbracket.location.to(rbracket.location)
+ )
+ else
+ tstring_end = find_token(TStringEnd)
+ contents =
+ contents.class.new(
+ elements: contents.elements,
+ location: contents.location.to(tstring_end.location)
+ )
+
+ ArrayLiteral.new(contents: contents, location: contents.location)
+ end
+ end
+
+ # AryPtn represents matching against an array pattern using the Ruby 2.7+
+ # pattern matching syntax. It’s one of the more complicated nodes, because
+ # the four parameters that it accepts can almost all be nil.
+ #
+ # case [1, 2, 3]
+ # in [Integer, Integer]
+ # "matched"
+ # in Container[Integer, Integer]
+ # "matched"
+ # in [Integer, *, Integer]
+ # "matched"
+ # end
+ #
+ # An AryPtn node is created with four parameters: an optional constant
+ # wrapper, an array of positional matches, an optional splat with identifier,
+ # and an optional array of positional matches that occur after the splat.
+ # All of the in clauses above would create an AryPtn node.
+ class AryPtn
+ # [nil | VarRef] the optional constant wrapper
+ attr_reader :constant
+
+ # [Array[ untyped ]] the regular positional arguments that this array
+ # pattern is matching against
+ attr_reader :requireds
+
+ # [nil | VarField] the optional starred identifier that grabs up a list of
+ # positional arguments
+ attr_reader :rest
+
+ # [Array[ untyped ]] the list of positional arguments occurring after the
+ # optional star if there is one
+ attr_reader :posts
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(constant:, requireds:, rest:, posts:, location:)
+ @constant = constant
+ @requireds = requireds
+ @rest = rest
+ @posts = posts
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('aryptn')
+
+ if constant
+ q.breakable
+ q.pp(constant)
+ end
+
+ if requireds.any?
+ q.breakable
+ q.group(2, '(', ')') do
+ q.seplist(requireds) { |required| q.pp(required) }
+ end
+ end
+
+ if rest
+ q.breakable
+ q.pp(rest)
+ end
+
+ if posts.any?
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(posts) { |post| q.pp(post) } }
+ end
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :aryptn,
+ constant: constant,
+ reqs: requireds,
+ rest: rest,
+ posts: posts,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_aryptn: (
+ # (nil | VarRef) constant,
+ # (nil | Array[untyped]) requireds,
+ # (nil | VarField) rest,
+ # (nil | Array[untyped]) posts
+ # ) -> AryPtn
+ def on_aryptn(constant, requireds, rest, posts)
+ parts = [constant, *requireds, rest, *posts].compact
+
+ AryPtn.new(
+ constant: constant,
+ requireds: requireds || [],
+ rest: rest,
+ posts: posts || [],
+ location: parts[0].location.to(parts[-1].location)
+ )
+ end
+
+ # Assign represents assigning something to a variable or constant. Generally,
+ # the left side of the assignment is going to be any node that ends with the
+ # name "Field".
+ #
+ # variable = value
+ #
+ class Assign
+ # [ARefField | ConstPathField | Field | TopConstField | VarField] the target
+ # to assign the result of the expression to
+ attr_reader :target
+
+ # [untyped] the expression to be assigned
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(target:, value:, location:)
+ @target = target
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('assign')
+ q.breakable
+ q.pp(target)
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :assign, target: target, value: value, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_assign: (
+ # (ARefField | ConstPathField | Field | TopConstField | VarField) target,
+ # untyped value
+ # ) -> Assign
+ def on_assign(target, value)
+ Assign.new(
+ target: target,
+ value: value,
+ location: target.location.to(value.location)
+ )
+ end
+
+ # Assoc represents a key-value pair within a hash. It is a child node of
+ # either an AssocListFromArgs or a BareAssocHash.
+ #
+ # { key1: value1, key2: value2 }
+ #
+ # In the above example, the would be two AssocNew nodes.
+ class Assoc
+ # [untyped] the key of this pair
+ attr_reader :key
+
+ # [untyped] the value of this pair
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(key:, value:, location:)
+ @key = key
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('assoc')
+ q.breakable
+ q.pp(key)
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :assoc, key: key, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_assoc_new: (untyped key, untyped value) -> Assoc
+ def on_assoc_new(key, value)
+ Assoc.new(
+ key: key,
+ value: value,
+ location: key.location.to(value.location)
+ )
+ end
+
+ # AssocSplat represents double-splatting a value into a hash (either a hash
+ # literal or a bare hash in a method call).
+ #
+ # { **pairs }
+ #
+ class AssocSplat
+ # [untyped] the expression that is being splatted
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('assoc_splat')
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :assoc_splat, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_assoc_splat: (untyped value) -> AssocSplat
+ def on_assoc_splat(value)
+ operator = find_token(Op, '**')
+
+ AssocSplat.new(value: value, location: operator.location.to(value.location))
+ end
+
+ # AssocListFromArgs represents the key-value pairs of a hash literal. Its
+ # parent node is always a hash.
+ #
+ # { key1: value1, key2: value2 }
+ #
+ class AssocListFromArgs
+ # [Array[ AssocNew | AssocSplat ]]
+ attr_reader :assocs
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(assocs:, location:)
+ @assocs = assocs
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('assoclist_from_args')
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(assocs) { |assoc| q.pp(assoc) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :assoclist_from_args, assocs: assocs, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_assoclist_from_args: (
+ # Array[AssocNew | AssocSplat] assocs
+ # ) -> AssocListFromArgs
+ def on_assoclist_from_args(assocs)
+ AssocListFromArgs.new(
+ assocs: assocs,
+ location: assocs[0].location.to(assocs[-1].location)
+ )
+ end
+
+ # Backref represents a global variable referencing a matched value. It comes
+ # in the form of a $ followed by a positive integer.
+ #
+ # $1
+ #
+ class Backref
+ # [String] the name of the global backreference variable
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('backref')
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :backref, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_backref: (String value) -> Backref
+ def on_backref(value)
+ node =
+ Backref.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # Backtick represents the use of the ` operator. It's usually found being used
+ # for an XStringLiteral, but could also be found as the name of a method being
+ # defined.
+ class Backtick
+ # [String] the backtick in the string
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('backtick')
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :backtick, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_backtick: (String value) -> Backtick
+ def on_backtick(value)
+ node =
+ Backtick.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # BareAssocHash represents a hash of contents being passed as a method
+ # argument (and therefore has omitted braces). It's very similar to an
+ # AssocListFromArgs node.
+ #
+ # method(key1: value1, key2: value2)
+ #
+ class BareAssocHash
+ # [Array[ AssocNew | AssocSplat ]]
+ attr_reader :assocs
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(assocs:, location:)
+ @assocs = assocs
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('bare_assoc_hash')
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(assocs) { |assoc| q.pp(assoc) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :bare_assoc_hash, assocs: assocs, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_bare_assoc_hash: (Array[AssocNew | AssocSplat] assocs) -> BareAssocHash
+ def on_bare_assoc_hash(assocs)
+ BareAssocHash.new(
+ assocs: assocs,
+ location: assocs[0].location.to(assocs[-1].location)
+ )
+ end
+
+ # Begin represents a begin..end chain.
+ #
+ # begin
+ # value
+ # end
+ #
+ class Begin
+ # [BodyStmt] the bodystmt that contains the contents of this begin block
+ attr_reader :bodystmt
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(bodystmt:, location:)
+ @bodystmt = bodystmt
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('begin')
+ q.breakable
+ q.pp(bodystmt)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :begin, bodystmt: bodystmt, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_begin: (BodyStmt bodystmt) -> Begin
+ def on_begin(bodystmt)
+ keyword = find_token(Kw, 'begin')
+ end_char =
+ if bodystmt.rescue_clause || bodystmt.ensure_clause ||
+ bodystmt.else_clause
+ bodystmt.location.end_char
+ else
+ find_token(Kw, 'end').location.end_char
+ end
+
+ bodystmt.bind(keyword.location.end_char, end_char)
+
+ Begin.new(
+ bodystmt: bodystmt,
+ location: keyword.location.to(bodystmt.location)
+ )
+ end
+
+ # Binary represents any expression that involves two sub-expressions with an
+ # operator in between. This can be something that looks like a mathematical
+ # operation:
+ #
+ # 1 + 1
+ #
+ # but can also be something like pushing a value onto an array:
+ #
+ # array << value
+ #
+ class Binary
+ # [untyped] the left-hand side of the expression
+ attr_reader :left
+
+ # [String] the operator used between the two expressions
+ attr_reader :operator
+
+ # [untyped] the right-hand side of the expression
+ attr_reader :right
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(left:, operator:, right:, location:)
+ @left = left
+ @operator = operator
+ @right = right
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('binary')
+ q.breakable
+ q.pp(left)
+ q.breakable
+ q.text(operator)
+ q.breakable
+ q.pp(right)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :binary,
+ left: left,
+ op: operator,
+ right: right,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_binary: (untyped left, (Op | Symbol) operator, untyped right) -> Binary
+ def on_binary(left, operator, right)
+ # On most Ruby implementations, operator is a Symbol that represents that
+ # operation being performed. For instance in the example `1 < 2`, the
+ # `operator` object would be `:<`. However, on JRuby, it's an `@op` node,
+ # so here we're going to explicitly convert it into the same normalized
+ # form.
+ operator = tokens.delete(operator).value unless operator.is_a?(Symbol)
+
+ Binary.new(
+ left: left,
+ operator: operator,
+ right: right,
+ location: left.location.to(right.location)
+ )
+ end
+
+ # BlockVar represents the parameters being declared for a block. Effectively
+ # this node is everything contained within the pipes. This includes all of the
+ # various parameter types, as well as block-local variable declarations.
+ #
+ # method do |positional, optional = value, keyword:, &block; local|
+ # end
+ #
+ class BlockVar
+ # [Params] the parameters being declared with the block
+ attr_reader :params
+
+ # [Array[ Ident ]] the list of block-local variable declarations
+ attr_reader :locals
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(params:, locals:, location:)
+ @params = params
+ @locals = locals
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('block_var')
+ q.breakable
+ q.pp(params)
+
+ if locals.any?
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(locals) { |local| q.pp(local) } }
+ end
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :block_var,
+ params: params,
+ locals: locals,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_block_var: (Params params, (nil | Array[Ident]) locals) -> BlockVar
+ def on_block_var(params, locals)
+ index =
+ tokens.rindex do |node|
+ node.is_a?(Op) && %w[| ||].include?(node.value) &&
+ node.location.start_char < params.location.start_char
+ end
+
+ beginning = tokens[index]
+ ending = tokens[-1]
+
+ BlockVar.new(
+ params: params,
+ locals: locals || [],
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # BlockArg represents declaring a block parameter on a method definition.
+ #
+ # def method(&block); end
+ #
+ class BlockArg
+ # [Ident] the name of the block argument
+ attr_reader :name
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(name:, location:)
+ @name = name
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('blockarg')
+ q.breakable
+ q.pp(name)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :blockarg, name: name, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_blockarg: (Ident name) -> BlockArg
+ def on_blockarg(name)
+ operator = find_token(Op, '&')
+
+ BlockArg.new(name: name, location: operator.location.to(name.location))
+ end
+
+ # bodystmt can't actually determine its bounds appropriately because it
+ # doesn't necessarily know where it started. So the parent node needs to
+ # report back down into this one where it goes.
+ class BodyStmt
+ # [Statements] the list of statements inside the begin clause
+ attr_reader :statements
+
+ # [nil | Rescue] the optional rescue chain attached to the begin clause
+ attr_reader :rescue_clause
+
+ # [nil | Statements] the optional set of statements inside the else clause
+ attr_reader :else_clause
+
+ # [nil | Ensure] the optional ensure clause
+ attr_reader :ensure_clause
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(
+ statements:,
+ rescue_clause:,
+ else_clause:,
+ ensure_clause:,
+ location:
+ )
+ @statements = statements
+ @rescue_clause = rescue_clause
+ @else_clause = else_clause
+ @ensure_clause = ensure_clause
+ @location = location
+ end
+
+ def bind(start_char, end_char)
+ @location =
+ Location.new(
+ start_line: location.start_line,
+ start_char: start_char,
+ end_line: location.end_line,
+ end_char: end_char
+ )
+
+ parts = [rescue_clause, else_clause, ensure_clause]
+
+ # Here we're going to determine the bounds for the statements
+ consequent = parts.compact.first
+ statements.bind(
+ start_char,
+ consequent ? consequent.location.start_char : end_char
+ )
+
+ # Next we're going to determine the rescue clause if there is one
+ if rescue_clause
+ consequent = parts.drop(1).compact.first
+ rescue_clause.bind_end(
+ consequent ? consequent.location.start_char : end_char
+ )
+ end
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('bodystmt')
+ q.breakable
+ q.pp(statements)
+
+ if rescue_clause
+ q.breakable
+ q.pp(rescue_clause)
+ end
+
+ if else_clause
+ q.breakable
+ q.pp(else_clause)
+ end
+
+ if ensure_clause
+ q.breakable
+ q.pp(ensure_clause)
+ end
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :bodystmt,
+ stmts: statements,
+ rsc: rescue_clause,
+ els: else_clause,
+ ens: ensure_clause,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_bodystmt: (
+ # Statements statements,
+ # (nil | Rescue) rescue_clause,
+ # (nil | Statements) else_clause,
+ # (nil | Ensure) ensure_clause
+ # ) -> BodyStmt
+ def on_bodystmt(statements, rescue_clause, else_clause, ensure_clause)
+ BodyStmt.new(
+ statements: statements,
+ rescue_clause: rescue_clause,
+ else_clause: else_clause,
+ ensure_clause: ensure_clause,
+ location: Location.fixed(line: lineno, char: char_pos)
+ )
+ end
+
+ # BraceBlock represents passing a block to a method call using the { }
+ # operators.
+ #
+ # method { |variable| variable + 1 }
+ #
+ class BraceBlock
+ # [LBrace] the left brace that opens this block
+ attr_reader :lbrace
+
+ # [nil | BlockVar] the optional set of parameters to the block
+ attr_reader :block_var
+
+ # [Statements] the list of expressions to evaluate within the block
+ attr_reader :statements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(lbrace:, block_var:, statements:, location:)
+ @lbrace = lbrace
+ @block_var = block_var
+ @statements = statements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('brace_block')
+
+ if block_var
+ q.breakable
+ q.pp(block_var)
+ end
+
+ q.breakable
+ q.pp(statements)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :brace_block,
+ lbrace: lbrace,
+ block_var: block_var,
+ stmts: statements,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_brace_block: (
+ # (nil | BlockVar) block_var,
+ # Statements statements
+ # ) -> BraceBlock
+ def on_brace_block(block_var, statements)
+ lbrace = find_token(LBrace)
+ rbrace = find_token(RBrace)
+
+ statements.bind(
+ find_next_statement_start((block_var || lbrace).location.end_char),
+ rbrace.location.start_char
+ )
+
+ location =
+ Location.new(
+ start_line: lbrace.location.start_line,
+ start_char: lbrace.location.start_char,
+ end_line: [rbrace.location.end_line, statements.location.end_line].max,
+ end_char: rbrace.location.end_char
+ )
+
+ BraceBlock.new(
+ lbrace: lbrace,
+ block_var: block_var,
+ statements: statements,
+ location: location
+ )
+ end
+
+ # Break represents using the +break+ keyword.
+ #
+ # break
+ #
+ # It can also optionally accept arguments, as in:
+ #
+ # break 1
+ #
+ class Break
+ # [Args | ArgsAddBlock] the arguments being sent to the keyword
+ attr_reader :arguments
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(arguments:, location:)
+ @arguments = arguments
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('break')
+ q.breakable
+ q.pp(arguments)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :break, args: arguments, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_break: ((Args | ArgsAddBlock) arguments) -> Break
+ def on_break(arguments)
+ keyword = find_token(Kw, 'break')
+
+ location = keyword.location
+ location = location.to(arguments.location) unless arguments.is_a?(Args)
+
+ Break.new(arguments: arguments, location: location)
+ end
+
+ # Call represents a method call. This node doesn't contain the arguments being
+ # passed (if arguments are passed, this node will get nested under a
+ # MethodAddArg node).
+ #
+ # receiver.message
+ #
+ class Call
+ # [untyped] the receiver of the method call
+ attr_reader :receiver
+
+ # [:"::" | Op | Period] the operator being used to send the message
+ attr_reader :operator
+
+ # [:call | Backtick | Const | Ident | Op] the message being sent
+ attr_reader :message
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(receiver:, operator:, message:, location:)
+ @receiver = receiver
+ @operator = operator
+ @message = message
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('call')
+ q.breakable
+ q.pp(receiver)
+ q.breakable
+ q.pp(operator)
+ q.breakable
+ q.pp(message)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :call,
+ receiver: receiver,
+ op: operator,
+ message: message,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_call: (
+ # untyped receiver,
+ # (:"::" | Op | Period) operator,
+ # (:call | Backtick | Const | Ident | Op) message
+ # ) -> Call
+ def on_call(receiver, operator, message)
+ ending = message
+ ending = operator if message == :call
+
+ Call.new(
+ receiver: receiver,
+ operator: operator,
+ message: message,
+ location:
+ Location.new(
+ start_line: receiver.location.start_line,
+ start_char: receiver.location.start_char,
+ end_line: [ending.location.end_line, receiver.location.end_line].max,
+ end_char: ending.location.end_char
+ )
+ )
+ end
+
+ # Case represents the beginning of a case chain.
+ #
+ # case value
+ # when 1
+ # "one"
+ # when 2
+ # "two"
+ # else
+ # "number"
+ # end
+ #
+ class Case
+ # [nil | untyped] optional value being switched on
+ attr_reader :value
+
+ # [In | When] the next clause in the chain
+ attr_reader :consequent
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, consequent:, location:)
+ @value = value
+ @consequent = consequent
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('case')
+
+ if value
+ q.breakable
+ q.pp(value)
+ end
+
+ q.breakable
+ q.pp(consequent)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :case, value: value, cons: consequent, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # RAssign represents a single-line pattern match.
+ #
+ # value in pattern
+ # value => pattern
+ #
+ class RAssign
+ # [untyped] the left-hand expression
+ attr_reader :value
+
+ # [Kw | Op] the operator being used to match against the pattern, which is
+ # either => or in
+ attr_reader :operator
+
+ # [untyped] the pattern on the right-hand side of the expression
+ attr_reader :pattern
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, operator:, pattern:, location:)
+ @value = value
+ @operator = operator
+ @pattern = pattern
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('rassign')
+
+ q.breakable
+ q.pp(value)
+
+ q.breakable
+ q.pp(operator)
+
+ q.breakable
+ q.pp(pattern)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :rassign,
+ value: value,
+ op: operator,
+ pattern: pattern,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_case: (untyped value, untyped consequent) -> Case | RAssign
+ def on_case(value, consequent)
+ if keyword = find_token(Kw, 'case', consume: false)
+ tokens.delete(keyword)
+
+ Case.new(
+ value: value,
+ consequent: consequent,
+ location: keyword.location.to(consequent.location)
+ )
+ else
+ operator = find_token(Kw, 'in', consume: false) || find_token(Op, '=>')
+
+ RAssign.new(
+ value: value,
+ operator: operator,
+ pattern: consequent,
+ location: value.location.to(consequent.location)
+ )
+ end
+ end
+
+ # Class represents defining a class using the +class+ keyword.
+ #
+ # class Container
+ # end
+ #
+ # Classes can have path names as their class name in case it's being nested
+ # under a namespace, as in:
+ #
+ # class Namespace::Container
+ # end
+ #
+ # Classes can also be defined as a top-level path, in the case that it's
+ # already in a namespace but you want to define it at the top-level instead,
+ # as in:
+ #
+ # module OtherNamespace
+ # class ::Namespace::Container
+ # end
+ # end
+ #
+ # All of these declarations can also have an optional superclass reference, as
+ # in:
+ #
+ # class Child < Parent
+ # end
+ #
+ # That superclass can actually be any Ruby expression, it doesn't necessarily
+ # need to be a constant, as in:
+ #
+ # class Child < method
+ # end
+ #
+ class ClassDeclaration
+ # [ConstPathRef | ConstRef | TopConstRef] the name of the class being
+ # defined
+ attr_reader :constant
+
+ # [nil | untyped] the optional superclass declaration
+ attr_reader :superclass
+
+ # [BodyStmt] the expressions to execute within the context of the class
+ attr_reader :bodystmt
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(constant:, superclass:, bodystmt:, location:)
+ @constant = constant
+ @superclass = superclass
+ @bodystmt = bodystmt
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('class')
+
+ q.breakable
+ q.pp(constant)
+
+ if superclass
+ q.breakable
+ q.pp(superclass)
+ end
+
+ q.breakable
+ q.pp(bodystmt)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :class,
+ constant: constant,
+ superclass: superclass,
+ bodystmt: bodystmt,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_class: (
+ # (ConstPathRef | ConstRef | TopConstRef) constant,
+ # untyped superclass,
+ # BodyStmt bodystmt
+ # ) -> ClassDeclaration
+ def on_class(constant, superclass, bodystmt)
+ beginning = find_token(Kw, 'class')
+ ending = find_token(Kw, 'end')
+
+ bodystmt.bind(
+ find_next_statement_start((superclass || constant).location.end_char),
+ ending.location.start_char
+ )
+
+ ClassDeclaration.new(
+ constant: constant,
+ superclass: superclass,
+ bodystmt: bodystmt,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # Comma represents the use of the , operator.
+ class Comma
+ # [String] the comma in the string
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_comma: (String value) -> Comma
+ def on_comma(value)
+ node =
+ Comma.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # Command represents a method call with arguments and no parentheses. Note
+ # that Command nodes only happen when there is no explicit receiver for this
+ # method.
+ #
+ # method argument
+ #
+ class Command
+ # [Const | Ident] the message being sent to the implicit receiver
+ attr_reader :message
+
+ # [Args | ArgsAddBlock] the arguments being sent with the message
+ attr_reader :arguments
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(message:, arguments:, location:)
+ @message = message
+ @arguments = arguments
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('command')
+
+ q.breakable
+ q.pp(message)
+
+ q.breakable
+ q.pp(arguments)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :command,
+ message: message,
+ args: arguments,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_command: (
+ # (Const | Ident) message,
+ # (Args | ArgsAddBlock) arguments
+ # ) -> Command
+ def on_command(message, arguments)
+ Command.new(
+ message: message,
+ arguments: arguments,
+ location: message.location.to(arguments.location)
+ )
+ end
+
+ # CommandCall represents a method call on an object with arguments and no
+ # parentheses.
+ #
+ # object.method argument
+ #
+ class CommandCall
+ # [untyped] the receiver of the message
+ attr_reader :receiver
+
+ # [:"::" | Op | Period] the operator used to send the message
+ attr_reader :operator
+
+ # [Const | Ident | Op] the message being send
+ attr_reader :message
+
+ # [Args | ArgsAddBlock] the arguments going along with the message
+ attr_reader :arguments
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(receiver:, operator:, message:, arguments:, location:)
+ @receiver = receiver
+ @operator = operator
+ @message = message
+ @arguments = arguments
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('command_call')
+
+ q.breakable
+ q.pp(receiver)
+
+ q.breakable
+ q.pp(operator)
+
+ q.breakable
+ q.pp(message)
+
+ q.breakable
+ q.pp(arguments)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :command_call,
+ receiver: receiver,
+ op: operator,
+ message: message,
+ args: arguments,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_command_call: (
+ # untyped receiver,
+ # (:"::" | Op | Period) operator,
+ # (Const | Ident | Op) message,
+ # (Args | ArgsAddBlock) arguments
+ # ) -> CommandCall
+ def on_command_call(receiver, operator, message, arguments)
+ ending = arguments || message
+
+ CommandCall.new(
+ receiver: receiver,
+ operator: operator,
+ message: message,
+ arguments: arguments,
+ location: receiver.location.to(ending.location)
+ )
+ end
+
+ # Comment represents a comment in the source.
+ #
+ # # comment
+ #
+ class Comment
+ # [String] the contents of the comment
+ attr_reader :value
+
+ # [boolean] whether or not there is code on the same line as this comment.
+ # If there is, then inline will be true.
+ attr_reader :inline
+ alias inline? inline
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, inline:, location:)
+ @value = value
+ @inline = inline
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('comment')
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :comment,
+ value: value.force_encoding('UTF-8'),
+ inline: inline,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_comment: (String value) -> Comment
+ def on_comment(value)
+ line = lineno
+ comment =
+ Comment.new(
+ value: value[1..-1].chomp,
+ inline: value.strip != lines[line - 1],
+ location:
+ Location.token(line: line, char: char_pos, size: value.size - 1)
+ )
+
+ @comments << comment
+ comment
+ end
+
+ # Const represents a literal value that _looks_ like a constant. This could
+ # actually be a reference to a constant:
+ #
+ # Constant
+ #
+ # It could also be something that looks like a constant in another context, as
+ # in a method call to a capitalized method:
+ #
+ # object.Constant
+ #
+ # or a symbol that starts with a capital letter:
+ #
+ # :Constant
+ #
+ class Const
+ # [String] the name of the constant
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('const')
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :const, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_const: (String value) -> Const
+ def on_const(value)
+ node =
+ Const.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # ConstPathField represents the child node of some kind of assignment. It
+ # represents when you're assigning to a constant that is being referenced as
+ # a child of another variable.
+ #
+ # object::Const = value
+ #
+ class ConstPathField
+ # [untyped] the source of the constant
+ attr_reader :parent
+
+ # [Const] the constant itself
+ attr_reader :constant
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parent:, constant:, location:)
+ @parent = parent
+ @constant = constant
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('const_path_field')
+
+ q.breakable
+ q.pp(parent)
+
+ q.breakable
+ q.pp(constant)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :const_path_field,
+ parent: parent,
+ constant: constant,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_const_path_field: (untyped parent, Const constant) -> ConstPathField
+ def on_const_path_field(parent, constant)
+ ConstPathField.new(
+ parent: parent,
+ constant: constant,
+ location: parent.location.to(constant.location)
+ )
+ end
+
+ # ConstPathRef represents referencing a constant by a path.
+ #
+ # object::Const
+ #
+ class ConstPathRef
+ # [untyped] the source of the constant
+ attr_reader :parent
+
+ # [Const] the constant itself
+ attr_reader :constant
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parent:, constant:, location:)
+ @parent = parent
+ @constant = constant
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('const_path_ref')
+
+ q.breakable
+ q.pp(parent)
+
+ q.breakable
+ q.pp(constant)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :const_path_ref,
+ parent: parent,
+ constant: constant,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_const_path_ref: (untyped parent, Const constant) -> ConstPathRef
+ def on_const_path_ref(parent, constant)
+ ConstPathRef.new(
+ parent: parent,
+ constant: constant,
+ location: parent.location.to(constant.location)
+ )
+ end
+
+ # ConstRef represents the name of the constant being used in a class or module
+ # declaration.
+ #
+ # class Container
+ # end
+ #
+ class ConstRef
+ # [Const] the constant itself
+ attr_reader :constant
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(constant:, location:)
+ @constant = constant
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('const_ref')
+
+ q.breakable
+ q.pp(constant)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :const_ref, constant: constant, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_const_ref: (Const constant) -> ConstRef
+ def on_const_ref(constant)
+ ConstRef.new(constant: constant, location: constant.location)
+ end
+
+ # CVar represents the use of a class variable.
+ #
+ # @@variable
+ #
+ class CVar
+ # [String] the name of the class variable
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('cvar')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :cvar, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_cvar: (String value) -> CVar
+ def on_cvar(value)
+ node =
+ CVar.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # Def represents defining a regular method on the current self object.
+ #
+ # def method(param) result end
+ #
+ class Def
+ # [Backtick | Const | Ident | Kw | Op] the name of the method
+ attr_reader :name
+
+ # [Params | Paren] the parameter declaration for the method
+ attr_reader :params
+
+ # [BodyStmt] the expressions to be executed by the method
+ attr_reader :bodystmt
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(name:, params:, bodystmt:, location:)
+ @name = name
+ @params = params
+ @bodystmt = bodystmt
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('def')
+
+ q.breakable
+ q.pp(name)
+
+ q.breakable
+ q.pp(params)
+
+ q.breakable
+ q.pp(bodystmt)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :def,
+ name: name,
+ params: params,
+ bodystmt: bodystmt,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # DefEndless represents defining a single-line method since Ruby 3.0+.
+ #
+ # def method = result
+ #
+ class DefEndless
+ # [Backtick | Const | Ident | Kw | Op] the name of the method
+ attr_reader :name
+
+ # [Paren] the parameter declaration for the method
+ attr_reader :paren
+
+ # [untyped] the expression to be executed by the method
+ attr_reader :statement
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(name:, paren:, statement:, location:)
+ @name = name
+ @paren = paren
+ @statement = statement
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('def_endless')
+
+ q.breakable
+ q.pp(name)
+
+ q.breakable
+ q.pp(paren)
+
+ q.breakable
+ q.pp(statement)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :def_endless,
+ name: name,
+ paren: paren,
+ stmt: statement,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_def: (
+ # (Backtick | Const | Ident | Kw | Op) name,
+ # (Params | Paren) params,
+ # untyped bodystmt
+ # ) -> Def | DefEndless
+ def on_def(name, params, bodystmt)
+ # Make sure to delete this token in case you're defining something like def
+ # class which would lead to this being a kw and causing all kinds of trouble
+ tokens.delete(name)
+
+ # Find the beginning of the method definition, which works for single-line
+ # and normal method definitions.
+ beginning = find_token(Kw, 'def')
+
+ # If we don't have a bodystmt node, then we have a single-line method
+ unless bodystmt.is_a?(BodyStmt)
+ node =
+ DefEndless.new(
+ name: name,
+ paren: params,
+ statement: bodystmt,
+ location: beginning.location.to(bodystmt.location)
+ )
+
+ return node
+ end
+
+ # If there aren't any params then we need to correct the params node
+ # location information
+ if params.is_a?(Params) && params.empty?
+ end_char = name.location.end_char
+ location =
+ Location.new(
+ start_line: params.location.start_line,
+ start_char: end_char,
+ end_line: params.location.end_line,
+ end_char: end_char
+ )
+
+ params = Params.new(location: location)
+ end
+
+ ending = find_token(Kw, 'end')
+ bodystmt.bind(
+ find_next_statement_start(params.location.end_char),
+ ending.location.start_char
+ )
+
+ Def.new(
+ name: name,
+ params: params,
+ bodystmt: bodystmt,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # Defined represents the use of the +defined?+ operator. It can be used with
+ # and without parentheses.
+ #
+ # defined?(variable)
+ #
+ class Defined
+ # [untyped] the value being sent to the keyword
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('defined')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :defined, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_defined: (untyped value) -> Defined
+ def on_defined(value)
+ beginning = find_token(Kw, 'defined?')
+ ending = value
+
+ range = beginning.location.end_char...value.location.start_char
+ if source[range].include?('(')
+ find_token(LParen)
+ ending = find_token(RParen)
+ end
+
+ Defined.new(value: value, location: beginning.location.to(ending.location))
+ end
+
+ # Defs represents defining a singleton method on an object.
+ #
+ # def object.method(param) result end
+ #
+ class Defs
+ # [untyped] the target where the method is being defined
+ attr_reader :target
+
+ # [Op | Period] the operator being used to declare the method
+ attr_reader :operator
+
+ # [Backtick | Const | Ident | Kw | Op] the name of the method
+ attr_reader :name
+
+ # [Params | Paren] the parameter declaration for the method
+ attr_reader :params
+
+ # [BodyStmt] the expressions to be executed by the method
+ attr_reader :bodystmt
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(target:, operator:, name:, params:, bodystmt:, location:)
+ @target = target
+ @operator = operator
+ @name = name
+ @params = params
+ @bodystmt = bodystmt
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('defs')
+
+ q.breakable
+ q.pp(target)
+
+ q.breakable
+ q.pp(operator)
+
+ q.breakable
+ q.pp(name)
+
+ q.breakable
+ q.pp(params)
+
+ q.breakable
+ q.pp(bodystmt)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :defs,
+ target: target,
+ op: operator,
+ name: name,
+ params: params,
+ bodystmt: bodystmt,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_defs: (
+ # untyped target,
+ # (Op | Period) operator,
+ # (Backtick | Const | Ident | Kw | Op) name,
+ # (Params | Paren) params,
+ # BodyStmt bodystmt
+ # ) -> Defs
+ def on_defs(target, operator, name, params, bodystmt)
+ # Make sure to delete this token in case you're defining something
+ # like def class which would lead to this being a kw and causing all kinds
+ # of trouble
+ tokens.delete(name)
+
+ # If there aren't any params then we need to correct the params node
+ # location information
+ if params.is_a?(Params) && params.empty?
+ end_char = name.location.end_char
+ location =
+ Location.new(
+ start_line: params.location.start_line,
+ start_char: end_char,
+ end_line: params.location.end_line,
+ end_char: end_char
+ )
+
+ params = Params.new(location: location)
+ end
+
+ beginning = find_token(Kw, 'def')
+ ending = find_token(Kw, 'end')
+
+ bodystmt.bind(
+ find_next_statement_start(params.location.end_char),
+ ending.location.start_char
+ )
+
+ Defs.new(
+ target: target,
+ operator: operator,
+ name: name,
+ params: params,
+ bodystmt: bodystmt,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # DoBlock represents passing a block to a method call using the +do+ and +end+
+ # keywords.
+ #
+ # method do |value|
+ # end
+ #
+ class DoBlock
+ # [Kw] the do keyword that opens this block
+ attr_reader :keyword
+
+ # [nil | BlockVar] the optional variable declaration within this block
+ attr_reader :block_var
+
+ # [BodyStmt] the expressions to be executed within this block
+ attr_reader :bodystmt
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(keyword:, block_var:, bodystmt:, location:)
+ @keyword = keyword
+ @block_var = block_var
+ @bodystmt = bodystmt
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('do_block')
+
+ if block_var
+ q.breakable
+ q.pp(block_var)
+ end
+
+ q.breakable
+ q.pp(bodystmt)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :do_block,
+ keyword: keyword,
+ block_var: block_var,
+ bodystmt: bodystmt,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_do_block: (BlockVar block_var, BodyStmt bodystmt) -> DoBlock
+ def on_do_block(block_var, bodystmt)
+ beginning = find_token(Kw, 'do')
+ ending = find_token(Kw, 'end')
+
+ bodystmt.bind(
+ find_next_statement_start((block_var || beginning).location.end_char),
+ ending.location.start_char
+ )
+
+ DoBlock.new(
+ keyword: beginning,
+ block_var: block_var,
+ bodystmt: bodystmt,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # Dot2 represents using the .. operator between two expressions. Usually this
+ # is to create a range object.
+ #
+ # 1..2
+ #
+ # Sometimes this operator is used to create a flip-flop.
+ #
+ # if value == 5 .. value == 10
+ # end
+ #
+ # One of the sides of the expression may be nil, but not both.
+ class Dot2
+ # [nil | untyped] the left side of the expression
+ attr_reader :left
+
+ # [nil | untyped] the right side of the expression
+ attr_reader :right
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(left:, right:, location:)
+ @left = left
+ @right = right
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('dot2')
+
+ if left
+ q.breakable
+ q.pp(left)
+ end
+
+ if right
+ q.breakable
+ q.pp(right)
+ end
+ end
+ end
+
+ def to_json(*opts)
+ { type: :dot2, left: left, right: right, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_dot2: ((nil | untyped) left, (nil | untyped) right) -> Dot2
+ def on_dot2(left, right)
+ operator = find_token(Op, '..')
+
+ beginning = left || operator
+ ending = right || operator
+
+ Dot2.new(
+ left: left,
+ right: right,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # Dot3 represents using the ... operator between two expressions. Usually this
+ # is to create a range object. It's effectively the same event as the Dot2
+ # node but with this operator you're asking Ruby to omit the final value.
+ #
+ # 1...2
+ #
+ # Like Dot2 it can also be used to create a flip-flop.
+ #
+ # if value == 5 ... value == 10
+ # end
+ #
+ # One of the sides of the expression may be nil, but not both.
+ class Dot3
+ # [nil | untyped] the left side of the expression
+ attr_reader :left
+
+ # [nil | untyped] the right side of the expression
+ attr_reader :right
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(left:, right:, location:)
+ @left = left
+ @right = right
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('dot3')
+
+ if left
+ q.breakable
+ q.pp(left)
+ end
+
+ if right
+ q.breakable
+ q.pp(right)
+ end
+ end
+ end
+
+ def to_json(*opts)
+ { type: :dot3, left: left, right: right, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_dot3: ((nil | untyped) left, (nil | untyped) right) -> Dot3
+ def on_dot3(left, right)
+ operator = find_token(Op, '...')
+
+ beginning = left || operator
+ ending = right || operator
+
+ Dot3.new(
+ left: left,
+ right: right,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # DynaSymbol represents a symbol literal that uses quotes to dynamically
+ # define its value.
+ #
+ # :"#{variable}"
+ #
+ # They can also be used as a special kind of dynamic hash key, as in:
+ #
+ # { "#{key}": value }
+ #
+ class DynaSymbol
+ # [Array[ StringDVar | StringEmbExpr | TStringContent ]] the parts of the
+ # dynamic symbol
+ attr_reader :parts
+
+ # [String] the quote used to delimit the dynamic symbol
+ attr_reader :quote
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parts:, quote:, location:)
+ @parts = parts
+ @quote = quote
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('dyna_symbol')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(parts) { |part| q.pp(part) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :dyna_symbol, parts: parts, quote: quote, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_dyna_symbol: (StringContent string_content) -> DynaSymbol
+ def on_dyna_symbol(string_content)
+ if find_token(SymBeg, consume: false)
+ # A normal dynamic symbol
+ symbeg = find_token(SymBeg)
+ tstring_end = find_token(TStringEnd)
+
+ DynaSymbol.new(
+ quote: symbeg.value,
+ parts: string_content.parts,
+ location: symbeg.location.to(tstring_end.location)
+ )
+ else
+ # A dynamic symbol as a hash key
+ tstring_beg = find_token(TStringBeg)
+ label_end = find_token(LabelEnd)
+
+ DynaSymbol.new(
+ parts: string_content.parts,
+ quote: label_end.value[0],
+ location: tstring_beg.location.to(label_end.location)
+ )
+ end
+ end
+
+ # Else represents the end of an +if+, +unless+, or +case+ chain.
+ #
+ # if variable
+ # else
+ # end
+ #
+ class Else
+ # [Statements] the expressions to be executed
+ attr_reader :statements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(statements:, location:)
+ @statements = statements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('else')
+
+ q.breakable
+ q.pp(statements)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :else, stmts: statements, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_else: (Statements statements) -> Else
+ def on_else(statements)
+ beginning = find_token(Kw, 'else')
+
+ # else can either end with an end keyword (in which case we'll want to
+ # consume that event) or it can end with an ensure keyword (in which case
+ # we'll leave that to the ensure to handle).
+ index =
+ tokens.rindex do |token|
+ token.is_a?(Kw) && %w[end ensure].include?(token.value)
+ end
+
+ node = tokens[index]
+ ending = node.value == 'end' ? tokens.delete_at(index) : node
+
+ statements.bind(beginning.location.end_char, ending.location.start_char)
+
+ Else.new(
+ statements: statements,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # Elsif represents another clause in an +if+ or +unless+ chain.
+ #
+ # if variable
+ # elsif other_variable
+ # end
+ #
+ class Elsif
+ # [untyped] the expression to be checked
+ attr_reader :predicate
+
+ # [Statements] the expressions to be executed
+ attr_reader :statements
+
+ # [nil | Elsif | Else] the next clause in the chain
+ attr_reader :consequent
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(predicate:, statements:, consequent:, location:)
+ @predicate = predicate
+ @statements = statements
+ @consequent = consequent
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('elsif')
+
+ q.breakable
+ q.pp(predicate)
+
+ q.breakable
+ q.pp(statements)
+
+ if consequent
+ q.breakable
+ q.pp(consequent)
+ end
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :elsif,
+ pred: predicate,
+ stmts: statements,
+ cons: consequent,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_elsif: (
+ # untyped predicate,
+ # Statements statements,
+ # (nil | Elsif | Else) consequent
+ # ) -> Elsif
+ def on_elsif(predicate, statements, consequent)
+ beginning = find_token(Kw, 'elsif')
+ ending = consequent || find_token(Kw, 'end')
+
+ statements.bind(predicate.location.end_char, ending.location.start_char)
+
+ Elsif.new(
+ predicate: predicate,
+ statements: statements,
+ consequent: consequent,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # EmbDoc represents a multi-line comment.
+ #
+ # =begin
+ # first line
+ # second line
+ # =end
+ #
+ class EmbDoc
+ # [String] the contents of the comment
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def inline?
+ false
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('embdoc')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :embdoc, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_embdoc: (String value) -> EmbDoc
+ def on_embdoc(value)
+ @embdoc.value << value
+ @embdoc
+ end
+
+ # :call-seq:
+ # on_embdoc_beg: (String value) -> EmbDoc
+ def on_embdoc_beg(value)
+ @embdoc =
+ EmbDoc.new(
+ value: value,
+ location: Location.fixed(line: lineno, char: char_pos)
+ )
+ end
+
+ # :call-seq:
+ # on_embdoc_end: (String value) -> EmbDoc
+ def on_embdoc_end(value)
+ location = @embdoc.location
+ embdoc =
+ EmbDoc.new(
+ value: @embdoc.value << value.chomp,
+ location:
+ Location.new(
+ start_line: location.start_line,
+ start_char: location.start_char,
+ end_line: lineno,
+ end_char: char_pos + value.length - 1
+ )
+ )
+
+ @comments << embdoc
+ @embdoc = nil
+
+ embdoc
+ end
+
+ # EmbExprBeg represents the beginning token for using interpolation inside of
+ # a parent node that accepts string content (like a string or regular
+ # expression).
+ #
+ # "Hello, #{person}!"
+ #
+ class EmbExprBeg
+ # [String] the #{ used in the string
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_embexpr_beg: (String value) -> EmbExprBeg
+ def on_embexpr_beg(value)
+ node =
+ EmbExprBeg.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # EmbExprEnd represents the ending token for using interpolation inside of a
+ # parent node that accepts string content (like a string or regular
+ # expression).
+ #
+ # "Hello, #{person}!"
+ #
+ class EmbExprEnd
+ # [String] the } used in the string
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_embexpr_end: (String value) -> EmbExprEnd
+ def on_embexpr_end(value)
+ node =
+ EmbExprEnd.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # EmbVar represents the use of shorthand interpolation for an instance, class,
+ # or global variable into a parent node that accepts string content (like a
+ # string or regular expression).
+ #
+ # "#@variable"
+ #
+ # In the example above, an EmbVar node represents the # because it forces
+ # @variable to be interpolated.
+ class EmbVar
+ # [String] the # used in the string
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_embvar: (String value) -> EmbVar
+ def on_embvar(value)
+ node =
+ EmbVar.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # Ensure represents the use of the +ensure+ keyword and its subsequent
+ # statements.
+ #
+ # begin
+ # ensure
+ # end
+ #
+ class Ensure
+ # [Kw] the ensure keyword that began this node
+ attr_reader :keyword
+
+ # [Statements] the expressions to be executed
+ attr_reader :statements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(keyword:, statements:, location:)
+ @keyword = keyword
+ @statements = statements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('ensure')
+
+ q.breakable
+ q.pp(statements)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :ensure,
+ keyword: keyword,
+ stmts: statements,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_ensure: (Statements statements) -> Ensure
+ def on_ensure(statements)
+ keyword = find_token(Kw, 'ensure')
+
+ # We don't want to consume the :@kw event, because that would break
+ # def..ensure..end chains.
+ ending = find_token(Kw, 'end', consume: false)
+ statements.bind(
+ find_next_statement_start(keyword.location.end_char),
+ ending.location.start_char
+ )
+
+ Ensure.new(
+ keyword: keyword,
+ statements: statements,
+ location: keyword.location.to(ending.location)
+ )
+ end
+
+ # ExcessedComma represents a trailing comma in a list of block parameters. It
+ # changes the block parameters such that they will destructure.
+ #
+ # [[1, 2, 3], [2, 3, 4]].each do |first, second,|
+ # end
+ #
+ # In the above example, an ExcessedComma node would appear in the third
+ # position of the Params node that is used to declare that block. The third
+ # position typically represents a rest-type parameter, but in this case is
+ # used to indicate that a trailing comma was used.
+ class ExcessedComma
+ # [String] the comma
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('excessed_comma')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :excessed_comma, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # The handler for this event accepts no parameters (though in previous
+ # versions of Ruby it accepted a string literal with a value of ",").
+ #
+ # :call-seq:
+ # on_excessed_comma: () -> ExcessedComma
+ def on_excessed_comma(*)
+ comma = find_token(Comma)
+
+ ExcessedComma.new(value: comma.value, location: comma.location)
+ end
+
+ # FCall represents the piece of a method call that comes before any arguments
+ # (i.e., just the name of the method). It is used in places where the parser
+ # is sure that it is a method call and not potentially a local variable.
+ #
+ # method(argument)
+ #
+ # In the above example, it's referring to the +method+ segment.
+ class FCall
+ # [Const | Ident] the name of the method
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('fcall')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :fcall, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_fcall: ((Const | Ident) value) -> FCall
+ def on_fcall(value)
+ FCall.new(value: value, location: value.location)
+ end
+
+ # Field is always the child of an assignment. It represents assigning to a
+ # “field” on an object.
+ #
+ # object.variable = value
+ #
+ class Field
+ # [untyped] the parent object that owns the field being assigned
+ attr_reader :parent
+
+ # [:"::" | Op | Period] the operator being used for the assignment
+ attr_reader :operator
+
+ # [Const | Ident] the name of the field being assigned
+ attr_reader :name
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parent:, operator:, name:, location:)
+ @parent = parent
+ @operator = operator
+ @name = name
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('field')
+
+ q.breakable
+ q.pp(parent)
+
+ q.breakable
+ q.pp(operator)
+
+ q.breakable
+ q.pp(name)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :field,
+ parent: parent,
+ op: operator,
+ name: name,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_field: (
+ # untyped parent,
+ # (:"::" | Op | Period) operator
+ # (Const | Ident) name
+ # ) -> Field
+ def on_field(parent, operator, name)
+ Field.new(
+ parent: parent,
+ operator: operator,
+ name: name,
+ location: parent.location.to(name.location)
+ )
+ end
+
+ # FloatLiteral represents a floating point number literal.
+ #
+ # 1.0
+ #
+ class FloatLiteral
+ # [String] the value of the floating point number literal
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('float')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :float, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_float: (String value) -> FloatLiteral
+ def on_float(value)
+ node =
+ FloatLiteral.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # FndPtn represents matching against a pattern where you find a pattern in an
+ # array using the Ruby 3.0+ pattern matching syntax.
+ #
+ # case value
+ # in [*, 7, *]
+ # end
+ #
+ class FndPtn
+ # [nil | untyped] the optional constant wrapper
+ attr_reader :constant
+
+ # [VarField] the splat on the left-hand side
+ attr_reader :left
+
+ # [Array[ untyped ]] the list of positional expressions in the pattern that
+ # are being matched
+ attr_reader :values
+
+ # [VarField] the splat on the right-hand side
+ attr_reader :right
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(constant:, left:, values:, right:, location:)
+ @constant = constant
+ @left = left
+ @values = values
+ @right = right
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('fndptn')
+
+ if constant
+ q.breakable
+ q.pp(constant)
+ end
+
+ q.breakable
+ q.pp(left)
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(values) { |value| q.pp(value) } }
+
+ q.breakable
+ q.pp(right)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :fndptn,
+ constant: constant,
+ left: left,
+ values: values,
+ right: right,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_fndptn: (
+ # (nil | untyped) constant,
+ # VarField left,
+ # Array[untyped] values,
+ # VarField right
+ # ) -> FndPtn
+ def on_fndptn(constant, left, values, right)
+ beginning = constant || find_token(LBracket)
+ ending = find_token(RBracket)
+
+ FndPtn.new(
+ constant: constant,
+ left: left,
+ values: values,
+ right: right,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # For represents using a +for+ loop.
+ #
+ # for value in list do
+ # end
+ #
+ class For
+ # [MLHS | MLHSAddStar | VarField] the variable declaration being used to
+ # pull values out of the object being enumerated
+ attr_reader :index
+
+ # [untyped] the object being enumerated in the loop
+ attr_reader :collection
+
+ # [Statements] the statements to be executed
+ attr_reader :statements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(index:, collection:, statements:, location:)
+ @index = index
+ @collection = collection
+ @statements = statements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('for')
+
+ q.breakable
+ q.pp(index)
+
+ q.breakable
+ q.pp(collection)
+
+ q.breakable
+ q.pp(statements)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :for,
+ index: index,
+ collection: collection,
+ stmts: statements,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_for: (
+ # (MLHS | MLHSAddStar | VarField) value,
+ # untyped collection,
+ # Statements statements
+ # ) -> For
+ def on_for(index, collection, statements)
+ beginning = find_token(Kw, 'for')
+ ending = find_token(Kw, 'end')
+
+ # Consume the do keyword if it exists so that it doesn't get confused for
+ # some other block
+ keyword = find_token(Kw, 'do', consume: false)
+ if keyword && keyword.location.start_char > collection.location.end_char &&
+ keyword.location.end_char < ending.location.start_char
+ tokens.delete(keyword)
+ end
+
+ statements.bind(
+ (keyword || collection).location.end_char,
+ ending.location.start_char
+ )
+
+ For.new(
+ index: index,
+ collection: collection,
+ statements: statements,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # GVar represents a global variable literal.
+ #
+ # $variable
+ #
+ class GVar
+ # [String] the name of the global variable
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('gvar')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :gvar, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_gvar: (String value) -> GVar
+ def on_gvar(value)
+ node =
+ GVar.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # HashLiteral represents a hash literal.
+ #
+ # { key => value }
+ #
+ class HashLiteral
+ # [nil | AssocListFromArgs] the contents of the hash
+ attr_reader :contents
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(contents:, location:)
+ @contents = contents
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('hash')
+
+ q.breakable
+ q.pp(contents)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :hash, cnts: contents, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_hash: ((nil | AssocListFromArgs) contents) -> HashLiteral
+ def on_hash(contents)
+ lbrace = find_token(LBrace)
+ rbrace = find_token(RBrace)
+
+ if contents
+ # Here we're going to expand out the location information for the contents
+ # node so that it can grab up any remaining comments inside the hash.
+ location =
+ Location.new(
+ start_line: contents.location.start_line,
+ start_char: lbrace.location.end_char,
+ end_line: contents.location.end_line,
+ end_char: rbrace.location.start_char
+ )
+
+ contents = contents.class.new(assocs: contents.assocs, location: location)
+ end
+
+ HashLiteral.new(
+ contents: contents,
+ location: lbrace.location.to(rbrace.location)
+ )
+ end
+
+ # Heredoc represents a heredoc string literal.
+ #
+ # <<~DOC
+ # contents
+ # DOC
+ #
+ class Heredoc
+ # [HeredocBeg] the opening of the heredoc
+ attr_reader :beginning
+
+ # [String] the ending of the heredoc
+ attr_reader :ending
+
+ # [Array[ StringEmbExpr | StringDVar | TStringContent ]] the parts of the
+ # heredoc string literal
+ attr_reader :parts
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(beginning:, ending: nil, parts: [], location:)
+ @beginning = beginning
+ @ending = ending
+ @parts = parts
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('heredoc')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(parts) { |part| q.pp(part) } }
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :heredoc,
+ beging: beginning,
+ ending: ending,
+ parts: parts,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # HeredocBeg represents the beginning declaration of a heredoc.
+ #
+ # <<~DOC
+ # contents
+ # DOC
+ #
+ # In the example above the HeredocBeg node represents <<~DOC.
+ class HeredocBeg
+ # [String] the opening declaration of the heredoc
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('heredoc_beg')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :heredoc_beg, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_heredoc_beg: (String value) -> HeredocBeg
+ def on_heredoc_beg(value)
+ location =
+ Location.token(line: lineno, char: char_pos, size: value.size + 1)
+
+ # Here we're going to artificially create an extra node type so that if
+ # there are comments after the declaration of a heredoc, they get printed.
+ beginning = HeredocBeg.new(value: value, location: location)
+ @heredocs << Heredoc.new(beginning: beginning, location: location)
+
+ beginning
+ end
+
+ # :call-seq:
+ # on_heredoc_dedent: (StringContent string, Integer width) -> Heredoc
+ def on_heredoc_dedent(string, width)
+ heredoc = @heredocs[-1]
+
+ @heredocs[-1] =
+ Heredoc.new(
+ beginning: heredoc.beginning,
+ ending: heredoc.ending,
+ parts: string.parts,
+ location: heredoc.location
+ )
+ end
+
+ # :call-seq:
+ # on_heredoc_end: (String value) -> Heredoc
+ def on_heredoc_end(value)
+ heredoc = @heredocs[-1]
+
+ @heredocs[-1] =
+ Heredoc.new(
+ beginning: heredoc.beginning,
+ ending: value.chomp,
+ parts: heredoc.parts,
+ location:
+ Location.new(
+ start_line: heredoc.location.start_line,
+ start_char: heredoc.location.start_char,
+ end_line: lineno,
+ end_char: char_pos
+ )
+ )
+ end
+
+ # HshPtn represents matching against a hash pattern using the Ruby 2.7+
+ # pattern matching syntax.
+ #
+ # case value
+ # in { key: }
+ # end
+ #
+ class HshPtn
+ # [nil | untyped] the optional constant wrapper
+ attr_reader :constant
+
+ # [Array[ [Label, untyped] ]] the set of tuples representing the keywords
+ # that should be matched against in the pattern
+ attr_reader :keywords
+
+ # [nil | VarField] an optional parameter to gather up all remaining keywords
+ attr_reader :keyword_rest
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(constant:, keywords:, keyword_rest:, location:)
+ @constant = constant
+ @keywords = keywords
+ @keyword_rest = keyword_rest
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('hshptn')
+
+ if constant
+ q.breakable
+ q.pp(constant)
+ end
+
+ if keywords.any?
+ q.breakable
+ q.group(2, '(', ')') do
+ q.seplist(keywords) { |keyword| q.pp(keyword) }
+ end
+ end
+
+ if keyword_rest
+ q.breakable
+ q.pp(keyword_rest)
+ end
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :hshptn,
+ constant: constant,
+ keywords: keywords,
+ kwrest: keyword_rest,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_hshptn: (
+ # (nil | untyped) constant,
+ # Array[[Label, untyped]] keywords,
+ # (nil | VarField) keyword_rest
+ # ) -> HshPtn
+ def on_hshptn(constant, keywords, keyword_rest)
+ parts = [constant, keywords, keyword_rest].flatten(2).compact
+
+ HshPtn.new(
+ constant: constant,
+ keywords: keywords,
+ keyword_rest: keyword_rest,
+ location: parts[0].location.to(parts[-1].location)
+ )
+ end
+
+ # Ident represents an identifier anywhere in code. It can represent a very
+ # large number of things, depending on where it is in the syntax tree.
+ #
+ # value
+ #
+ class Ident
+ # [String] the value of the identifier
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('ident')
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :ident,
+ value: value.force_encoding('UTF-8'),
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_ident: (String value) -> Ident
+ def on_ident(value)
+ node =
+ Ident.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # If represents the first clause in an +if+ chain.
+ #
+ # if predicate
+ # end
+ #
+ class If
+ # [untyped] the expression to be checked
+ attr_reader :predicate
+
+ # [Statements] the expressions to be executed
+ attr_reader :statements
+
+ # [nil, Elsif, Else] the next clause in the chain
+ attr_reader :consequent
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(predicate:, statements:, consequent:, location:)
+ @predicate = predicate
+ @statements = statements
+ @consequent = consequent
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('if')
+
+ q.breakable
+ q.pp(predicate)
+
+ q.breakable
+ q.pp(statements)
+
+ if consequent
+ q.breakable
+ q.pp(consequent)
+ end
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :if,
+ pred: predicate,
+ stmts: statements,
+ cons: consequent,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_if: (
+ # untyped predicate,
+ # Statements statements,
+ # (nil | Elsif | Else) consequent
+ # ) -> If
+ def on_if(predicate, statements, consequent)
+ beginning = find_token(Kw, 'if')
+ ending = consequent || find_token(Kw, 'end')
+
+ statements.bind(predicate.location.end_char, ending.location.start_char)
+
+ If.new(
+ predicate: predicate,
+ statements: statements,
+ consequent: consequent,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # IfOp represents a ternary clause.
+ #
+ # predicate ? truthy : falsy
+ #
+ class IfOp
+ # [untyped] the expression to be checked
+ attr_reader :predicate
+
+ # [untyped] the expression to be executed if the predicate is truthy
+ attr_reader :truthy
+
+ # [untyped] the expression to be executed if the predicate is falsy
+ attr_reader :falsy
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(predicate:, truthy:, falsy:, location:)
+ @predicate = predicate
+ @truthy = truthy
+ @falsy = falsy
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('ifop')
+
+ q.breakable
+ q.pp(predicate)
+
+ q.breakable
+ q.pp(truthy)
+
+ q.breakable
+ q.pp(falsy)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :ifop,
+ pred: predicate,
+ tthy: truthy,
+ flsy: falsy,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_ifop: (untyped predicate, untyped truthy, untyped falsy) -> IfOp
+ def on_ifop(predicate, truthy, falsy)
+ IfOp.new(
+ predicate: predicate,
+ truthy: truthy,
+ falsy: falsy,
+ location: predicate.location.to(falsy.location)
+ )
+ end
+
+ # IfMod represents the modifier form of an +if+ statement.
+ #
+ # expression if predicate
+ #
+ class IfMod
+ # [untyped] the expression to be executed
+ attr_reader :statement
+
+ # [untyped] the expression to be checked
+ attr_reader :predicate
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(statement:, predicate:, location:)
+ @statement = statement
+ @predicate = predicate
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('if_mod')
+
+ q.breakable
+ q.pp(statement)
+
+ q.breakable
+ q.pp(predicate)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :if_mod,
+ stmt: statement,
+ pred: predicate,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_if_mod: (untyped predicate, untyped statement) -> IfMod
+ def on_if_mod(predicate, statement)
+ find_token(Kw, 'if')
+
+ IfMod.new(
+ statement: statement,
+ predicate: predicate,
+ location: statement.location.to(predicate.location)
+ )
+ end
+
+ # def on_ignored_nl(value)
+ # value
+ # end
+
+ # def on_ignored_sp(value)
+ # value
+ # end
+
+ # Imaginary represents an imaginary number literal.
+ #
+ # 1i
+ #
+ class Imaginary
+ # [String] the value of the imaginary number literal
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('imaginary')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :imaginary, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_imaginary: (String value) -> Imaginary
+ def on_imaginary(value)
+ node =
+ Imaginary.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # In represents using the +in+ keyword within the Ruby 2.7+ pattern matching
+ # syntax.
+ #
+ # case value
+ # in pattern
+ # end
+ #
+ class In
+ # [untyped] the pattern to check against
+ attr_reader :pattern
+
+ # [Statements] the expressions to execute if the pattern matched
+ attr_reader :statements
+
+ # [nil | In | Else] the next clause in the chain
+ attr_reader :consequent
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(pattern:, statements:, consequent:, location:)
+ @pattern = pattern
+ @statements = statements
+ @consequent = consequent
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('in')
+
+ q.breakable
+ q.pp(pattern)
+
+ q.breakable
+ q.pp(statements)
+
+ if consequent
+ q.breakable
+ q.pp(consequent)
+ end
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :in,
+ pattern: pattern,
+ stmts: statements,
+ cons: consequent,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_in: (RAssign pattern, nil statements, nil consequent) -> RAssign
+ # | (
+ # untyped pattern,
+ # Statements statements,
+ # (nil | In | Else) consequent
+ # ) -> In
+ def on_in(pattern, statements, consequent)
+ # Here we have a rightward assignment
+ return pattern unless statements
+
+ beginning = find_token(Kw, 'in')
+ ending = consequent || find_token(Kw, 'end')
+
+ statements.bind(beginning.location.end_char, ending.location.start_char)
+
+ In.new(
+ pattern: pattern,
+ statements: statements,
+ consequent: consequent,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # Int represents an integer number literal.
+ #
+ # 1
+ #
+ class Int
+ # [String] the value of the integer
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('int')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :int, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_int: (String value) -> Int
+ def on_int(value)
+ node =
+ Int.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # IVar represents an instance variable literal.
+ #
+ # @variable
+ #
+ class IVar
+ # [String] the name of the instance variable
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('ivar')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :ivar, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_ivar: (String value) -> IVar
+ def on_ivar(value)
+ node =
+ IVar.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # Kw represents the use of a keyword. It can be almost anywhere in the syntax
+ # tree, so you end up seeing it quite a lot.
+ #
+ # if value
+ # end
+ #
+ # In the above example, there would be two Kw nodes: one for the if and one
+ # for the end. Note that anything that matches the list of keywords in Ruby
+ # will use a Kw, so if you use a keyword in a symbol literal for instance:
+ #
+ # :if
+ #
+ # then the contents of the symbol node will contain a Kw node.
+ class Kw
+ # [String] the value of the keyword
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('kw')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :kw, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_kw: (String value) -> Kw
+ def on_kw(value)
+ node =
+ Kw.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # KwRestParam represents defining a parameter in a method definition that
+ # accepts all remaining keyword parameters.
+ #
+ # def method(**kwargs) end
+ #
+ class KwRestParam
+ # [nil | Ident] the name of the parameter
+ attr_reader :name
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(name:, location:)
+ @name = name
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('kwrest_param')
+
+ q.breakable
+ q.pp(name)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :kwrest_param, name: name, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_kwrest_param: ((nil | Ident) name) -> KwRestParam
+ def on_kwrest_param(name)
+ location = find_token(Op, '**').location
+ location = location.to(name.location) if name
+
+ KwRestParam.new(name: name, location: location)
+ end
+
+ # Label represents the use of an identifier to associate with an object. You
+ # can find it in a hash key, as in:
+ #
+ # { key: value }
+ #
+ # In this case "key:" would be the body of the label. You can also find it in
+ # pattern matching, as in:
+ #
+ # case value
+ # in key:
+ # end
+ #
+ # In this case "key:" would be the body of the label.
+ class Label
+ # [String] the value of the label
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('label')
+
+ q.breakable
+ q.text(':')
+ q.text(value[0...-1])
+ end
+ end
+
+ def to_json(*opts)
+ { type: :label, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_label: (String value) -> Label
+ def on_label(value)
+ node =
+ Label.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # LabelEnd represents the end of a dynamic symbol.
+ #
+ # { "key": value }
+ #
+ # In the example above, LabelEnd represents the "\":" token at the end of the
+ # hash key. This node is important for determining the type of quote being
+ # used by the label.
+ class LabelEnd
+ # [String] the end of the label
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_label_end: (String value) -> LabelEnd
+ def on_label_end(value)
+ node =
+ LabelEnd.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # Lambda represents using a lambda literal (not the lambda method call).
+ #
+ # ->(value) { value * 2 }
+ #
+ class Lambda
+ # [Params | Paren] the parameter declaration for this lambda
+ attr_reader :params
+
+ # [BodyStmt | Statements] the expressions to be executed in this lambda
+ attr_reader :statements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(params:, statements:, location:)
+ @params = params
+ @statements = statements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('lambda')
+
+ q.breakable
+ q.pp(params)
+
+ q.breakable
+ q.pp(statements)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :lambda,
+ params: params,
+ stmts: statements,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_lambda: (
+ # (Params | Paren) params,
+ # (BodyStmt | Statements) statements
+ # ) -> Lambda
+ def on_lambda(params, statements)
+ beginning = find_token(TLambda)
+
+ if token = find_token(TLamBeg, consume: false)
+ opening = tokens.delete(token)
+ closing = find_token(RBrace)
+ else
+ opening = find_token(Kw, 'do')
+ closing = find_token(Kw, 'end')
+ end
+
+ statements.bind(opening.location.end_char, closing.location.start_char)
+
+ Lambda.new(
+ params: params,
+ statements: statements,
+ location: beginning.location.to(closing.location)
+ )
+ end
+
+ # LBrace represents the use of a left brace, i.e., {.
+ class LBrace
+ # [String] the left brace
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('lbrace')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :lbrace, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_lbrace: (String value) -> LBrace
+ def on_lbrace(value)
+ node =
+ LBrace.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # LBracket represents the use of a left bracket, i.e., [.
+ class LBracket
+ # [String] the left bracket
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_lbracket: (String value) -> LBracket
+ def on_lbracket(value)
+ node =
+ LBracket.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # LParen represents the use of a left parenthesis, i.e., (.
+ class LParen
+ # [String] the left parenthesis
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('lparen')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :lparen, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_lparen: (String value) -> LParen
+ def on_lparen(value)
+ node =
+ LParen.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # def on_magic_comment(key, value)
+ # [key, value]
+ # end
+
+ # MAssign is a parent node of any kind of multiple assignment. This includes
+ # splitting out variables on the left like:
+ #
+ # first, second, third = value
+ #
+ # as well as splitting out variables on the right, as in:
+ #
+ # value = first, second, third
+ #
+ # Both sides support splats, as well as variables following them. There's also
+ # destructuring behavior that you can achieve with the following:
+ #
+ # first, = value
+ #
+ class MAssign
+ # [Mlhs | MlhsAddPost | MlhsAddStar | MlhsParen] the target of the multiple
+ # assignment
+ attr_reader :target
+
+ # [untyped] the value being assigned
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(target:, value:, location:)
+ @target = target
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('massign')
+
+ q.breakable
+ q.pp(target)
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :massign, target: target, value: value, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_massign: (
+ # (Mlhs | MlhsAddPost | MlhsAddStar | MlhsParen) target,
+ # untyped value
+ # ) -> MAssign
+ def on_massign(target, value)
+ comma_range = target.location.end_char...value.location.start_char
+ target.comma = true if source[comma_range].strip.start_with?(',')
+
+ MAssign.new(
+ target: target,
+ value: value,
+ location: target.location.to(value.location)
+ )
+ end
+
+ # MethodAddArg represents a method call with arguments and parentheses.
+ #
+ # method(argument)
+ #
+ # MethodAddArg can also represent with a method on an object, as in:
+ #
+ # object.method(argument)
+ #
+ # Finally, MethodAddArg can represent calling a method with no receiver that
+ # ends in a ?. In this case, the parser knows it's a method call and not a
+ # local variable, so it uses a MethodAddArg node as opposed to a VCall node,
+ # as in:
+ #
+ # method?
+ #
+ class MethodAddArg
+ # [Call | FCall] the method call
+ attr_reader :call
+
+ # [ArgParen | Args | ArgsAddBlock] the arguments to the method call
+ attr_reader :arguments
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(call:, arguments:, location:)
+ @call = call
+ @arguments = arguments
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('method_add_arg')
+
+ q.breakable
+ q.pp(call)
+
+ q.breakable
+ q.pp(arguments)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :method_add_arg,
+ call: call,
+ args: arguments,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_method_add_arg: (
+ # (Call | FCall) call,
+ # (ArgParen | Args | ArgsAddBlock) arguments
+ # ) -> MethodAddArg
+ def on_method_add_arg(call, arguments)
+ location = call.location
+
+ location = location.to(arguments.location) unless arguments.is_a?(Args)
+
+ MethodAddArg.new(call: call, arguments: arguments, location: location)
+ end
+
+ # MethodAddBlock represents a method call with a block argument.
+ #
+ # method {}
+ #
+ class MethodAddBlock
+ # [Call | Command | CommandCall | FCall | MethodAddArg] the method call
+ attr_reader :call
+
+ # [BraceBlock | DoBlock] the block being sent with the method call
+ attr_reader :block
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(call:, block:, location:)
+ @call = call
+ @block = block
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('method_add_block')
+
+ q.breakable
+ q.pp(call)
+
+ q.breakable
+ q.pp(block)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :method_add_block,
+ call: call,
+ block: block,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_method_add_block: (
+ # (Call | Command | CommandCall | FCall | MethodAddArg) call,
+ # (BraceBlock | DoBlock) block
+ # ) -> MethodAddBlock
+ def on_method_add_block(call, block)
+ MethodAddBlock.new(
+ call: call,
+ block: block,
+ location: call.location.to(block.location)
+ )
+ end
+
+ # MLHS represents a list of values being destructured on the left-hand side
+ # of a multiple assignment.
+ #
+ # first, second, third = value
+ #
+ class MLHS
+ # Array[ARefField | Field | Ident | MlhsParen | VarField] the parts of
+ # the left-hand side of a multiple assignment
+ attr_reader :parts
+
+ # [boolean] whether or not there is a trailing comma at the end of this
+ # list, which impacts destructuring. It's an attr_accessor so that while
+ # the syntax tree is being built it can be set by its parent node
+ attr_accessor :comma
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parts:, comma: false, location:)
+ @parts = parts
+ @comma = comma
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('mlhs')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(parts) { |part| q.pp(part) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :mlhs, parts: parts, comma: comma, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_mlhs_add: (
+ # MLHS mlhs,
+ # (ARefField | Field | Ident | MlhsParen | VarField) part
+ # ) -> MLHS
+ def on_mlhs_add(mlhs, part)
+ if mlhs.parts.empty?
+ MLHS.new(parts: [part], location: part.location)
+ else
+ MLHS.new(
+ parts: mlhs.parts << part,
+ location: mlhs.location.to(part.location)
+ )
+ end
+ end
+
+ # MLHSAddPost represents adding another set of variables onto a list of
+ # assignments after a splat variable within a multiple assignment.
+ #
+ # left, *middle, right = values
+ #
+ class MLHSAddPost
+ # [MlhsAddStar] the value being starred
+ attr_reader :star
+
+ # [Mlhs] the values after the star
+ attr_reader :mlhs
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(star:, mlhs:, location:)
+ @star = star
+ @mlhs = mlhs
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('mlhs_add_post')
+
+ q.breakable
+ q.pp(star)
+
+ q.breakable
+ q.pp(mlhs)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :mlhs_add_post, star: star, mlhs: mlhs, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_mlhs_add_post: (MLHSAddStar star, MLHS mlhs) -> MLHSAddPost
+ def on_mlhs_add_post(star, mlhs)
+ MLHSAddPost.new(
+ star: star,
+ mlhs: mlhs,
+ location: star.location.to(mlhs.location)
+ )
+ end
+
+ # MLHSAddStar represents a splatted variable inside of a multiple assignment
+ # on the left hand side.
+ #
+ # first, *rest = values
+ #
+ class MLHSAddStar
+ # [MLHS] the values before the starred expression
+ attr_reader :mlhs
+
+ # [nil | ARefField | Field | Ident | VarField] the expression being
+ # splatted
+ attr_reader :star
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(mlhs:, star:, location:)
+ @mlhs = mlhs
+ @star = star
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('mlhs_add_star')
+
+ q.breakable
+ q.pp(mlhs)
+
+ q.breakable
+ q.pp(star)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :mlhs_add_star, mlhs: mlhs, star: star, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_mlhs_add_star: (
+ # MLHS mlhs,
+ # (nil | ARefField | Field | Ident | VarField) part
+ # ) -> MLHSAddStar
+ def on_mlhs_add_star(mlhs, part)
+ beginning = find_token(Op, '*')
+ ending = part || beginning
+
+ MLHSAddStar.new(
+ mlhs: mlhs,
+ star: part,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # :call-seq:
+ # on_mlhs_new: () -> MLHS
+ def on_mlhs_new
+ MLHS.new(parts: [], location: Location.fixed(line: lineno, char: char_pos))
+ end
+
+ # MLHSParen represents parentheses being used to destruct values in a multiple
+ # assignment on the left hand side.
+ #
+ # (left, right) = value
+ #
+ class MLHSParen
+ # [Mlhs | MlhsAddPost | MlhsAddStar | MlhsParen] the contents inside of the
+ # parentheses
+ attr_reader :contents
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(contents:, location:)
+ @contents = contents
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('mlhs_paren')
+
+ q.breakable
+ q.pp(contents)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :mlhs_paren, cnts: contents, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_mlhs_paren: (
+ # (Mlhs | MlhsAddPost | MlhsAddStar | MlhsParen) contents
+ # ) -> MLHSParen
+ def on_mlhs_paren(contents)
+ lparen = find_token(LParen)
+ rparen = find_token(RParen)
+
+ comma_range = lparen.location.end_char...rparen.location.start_char
+ contents.comma = true if source[comma_range].strip.end_with?(',')
+
+ MLHSParen.new(
+ contents: contents,
+ location: lparen.location.to(rparen.location)
+ )
+ end
+
+ # ModuleDeclaration represents defining a module using the +module+ keyword.
+ #
+ # module Namespace
+ # end
+ #
+ class ModuleDeclaration
+ # [ConstPathRef | ConstRef | TopConstRef] the name of the module
+ attr_reader :constant
+
+ # [BodyStmt] the expressions to be executed in the context of the module
+ attr_reader :bodystmt
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(constant:, bodystmt:, location:)
+ @constant = constant
+ @bodystmt = bodystmt
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('module')
+
+ q.breakable
+ q.pp(constant)
+
+ q.breakable
+ q.pp(bodystmt)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :module,
+ constant: constant,
+ bodystmt: bodystmt,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_module: (
+ # (ConstPathRef | ConstRef | TopConstRef) constant,
+ # BodyStmt bodystmt
+ # ) -> ModuleDeclaration
+ def on_module(constant, bodystmt)
+ beginning = find_token(Kw, 'module')
+ ending = find_token(Kw, 'end')
+
+ bodystmt.bind(
+ find_next_statement_start(constant.location.end_char),
+ ending.location.start_char
+ )
+
+ ModuleDeclaration.new(
+ constant: constant,
+ bodystmt: bodystmt,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # MRHS represents the values that are being assigned on the right-hand side of
+ # a multiple assignment.
+ #
+ # values = first, second, third
+ #
+ class MRHS
+ # Array[untyped] the parts that are being assigned
+ attr_reader :parts
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parts:, location:)
+ @parts = parts
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('mrhs')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(parts) { |part| q.pp(part) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :mrhs, parts: parts, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_mrhs_new: () -> MRHS
+ def on_mrhs_new
+ MRHS.new(parts: [], location: Location.fixed(line: lineno, char: char_pos))
+ end
+
+ # :call-seq:
+ # on_mrhs_add: (MRHS mrhs, untyped part) -> MRHS
+ def on_mrhs_add(mrhs, part)
+ if mrhs.is_a?(MRHSNewFromArgs)
+ MRHS.new(
+ parts: [*mrhs.arguments.parts, part],
+ location: mrhs.location.to(part.location)
+ )
+ elsif mrhs.parts.empty?
+ MRHS.new(parts: [part], location: mrhs.location)
+ else
+ MRHS.new(parts: mrhs.parts << part, loc: mrhs.location.to(part.location))
+ end
+ end
+
+ # MRHSAddStar represents using the splat operator to expand out a value on the
+ # right hand side of a multiple assignment.
+ #
+ # values = first, *rest
+ #
+ class MRHSAddStar
+ # [MRHS | MRHSNewFromArgs] the values before the splatted expression
+ attr_reader :mrhs
+
+ # [untyped] the splatted expression
+ attr_reader :star
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(mrhs:, star:, location:)
+ @mrhs = mrhs
+ @star = star
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('mrhs_add_star')
+
+ q.breakable
+ q.pp(mrhs)
+
+ q.breakable
+ q.pp(star)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :mrhs_add_star, mrhs: mrhs, star: star, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_mrhs_add_star: (
+ # (MRHS | MRHSNewFromArgs) mrhs,
+ # untyped star
+ # ) -> MRHSAddStar
+ def on_mrhs_add_star(mrhs, star)
+ beginning = find_token(Op, '*')
+ ending = star || beginning
+
+ MRHSAddStar.new(
+ mrhs: mrhs,
+ star: star,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # MRHSNewFromArgs represents the shorthand of a multiple assignment that
+ # allows you to assign values using just commas as opposed to assigning from
+ # an array.
+ #
+ # values = first, second, third
+ #
+ class MRHSNewFromArgs
+ # [Args] the arguments being used in the assignment
+ attr_reader :arguments
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(arguments:, location:)
+ @arguments = arguments
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('mrhs_new_from_args')
+
+ q.breakable
+ q.pp(arguments)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :mrhs_new_from_args, args: arguments, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_mrhs_new_from_args: (Args arguments) -> MRHSNewFromArgs
+ def on_mrhs_new_from_args(arguments)
+ MRHSNewFromArgs.new(arguments: arguments, location: arguments.location)
+ end
+
+ # Next represents using the +next+ keyword.
+ #
+ # next
+ #
+ # The +next+ keyword can also optionally be called with an argument:
+ #
+ # next value
+ #
+ # +next+ can even be called with multiple arguments, but only if parentheses
+ # are omitted, as in:
+ #
+ # next first, second, third
+ #
+ # If a single value is being given, parentheses can be used, as in:
+ #
+ # next(value)
+ #
+ class Next
+ # [Args | ArgsAddBlock] the arguments passed to the next keyword
+ attr_reader :arguments
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(arguments:, location:)
+ @arguments = arguments
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('next')
+
+ q.breakable
+ q.pp(arguments)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :next, args: arguments, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_next: ((Args | ArgsAddBlock) arguments) -> Next
+ def on_next(arguments)
+ keyword = find_token(Kw, 'next')
+
+ location = keyword.location
+ location = location.to(arguments.location) unless arguments.is_a?(Args)
+
+ Next.new(arguments: arguments, location: location)
+ end
+
+ # def on_nl(value)
+ # value
+ # end
+
+ # def on_nokw_param(value)
+ # value
+ # end
+
+ # Op represents an operator literal in the source.
+ #
+ # 1 + 2
+ #
+ # In the example above, the Op node represents the + operator.
+ class Op
+ # [String] the operator
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('op')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :op, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_op: (String value) -> Op
+ def on_op(value)
+ node =
+ Op.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # OpAssign represents assigning a value to a variable or constant using an
+ # operator like += or ||=.
+ #
+ # variable += value
+ #
+ class OpAssign
+ # [ARefField | ConstPathField | Field | TopConstField | VarField] the target
+ # to assign the result of the expression to
+ attr_reader :target
+
+ # [Op] the operator being used for the assignment
+ attr_reader :operator
+
+ # [untyped] the expression to be assigned
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(target:, operator:, value:, location:)
+ @target = target
+ @operator = operator
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('opassign')
+
+ q.breakable
+ q.pp(target)
+
+ q.breakable
+ q.pp(operator)
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :opassign,
+ target: target,
+ op: operator,
+ value: value,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_opassign: (
+ # (ARefField | ConstPathField | Field | TopConstField | VarField) target,
+ # Op operator,
+ # untyped value
+ # ) -> OpAssign
+ def on_opassign(target, operator, value)
+ OpAssign.new(
+ target: target,
+ operator: operator,
+ value: value,
+ location: target.location.to(value.location)
+ )
+ end
+
+ # def on_operator_ambiguous(value)
+ # value
+ # end
+
+ # Params represents defining parameters on a method or lambda.
+ #
+ # def method(param) end
+ #
+ class Params
+ # [Array[ Ident ]] any required parameters
+ attr_reader :requireds
+
+ # [Array[ [ Ident, untyped ] ]] any optional parameters and their default
+ # values
+ attr_reader :optionals
+
+ # [nil | ArgsForward | ExcessedComma | RestParam] the optional rest
+ # parameter
+ attr_reader :rest
+
+ # [Array[ Ident ]] any positional parameters that exist after a rest
+ # parameter
+ attr_reader :posts
+
+ # [Array[ [ Ident, nil | untyped ] ]] any keyword parameters and their
+ # optional default values
+ attr_reader :keywords
+
+ # [nil | :nil | KwRestParam] the optional keyword rest parameter
+ attr_reader :keyword_rest
+
+ # [nil | BlockArg] the optional block parameter
+ attr_reader :block
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(
+ requireds: [],
+ optionals: [],
+ rest: nil,
+ posts: [],
+ keywords: [],
+ keyword_rest: nil,
+ block: nil,
+ location:
+ )
+ @requireds = requireds
+ @optionals = optionals
+ @rest = rest
+ @posts = posts
+ @keywords = keywords
+ @keyword_rest = keyword_rest
+ @block = block
+ @location = location
+ end
+
+ # Params nodes are the most complicated in the tree. Occasionally you want
+ # to know if they are "empty", which means not having any parameters
+ # declared. This logic accesses every kind of parameter and determines if
+ # it's missing.
+ def empty?
+ requireds.empty? && optionals.empty? && !rest && posts.empty? &&
+ keywords.empty? && !keyword_rest && !block
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('params')
+
+ if requireds.any?
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(requireds) { |name| q.pp(name) } }
+ end
+
+ if optionals.any?
+ q.breakable
+ q.group(2, '(', ')') do
+ q.seplist(optionals) do |(name, default)|
+ q.pp(name)
+ q.text('=')
+ q.group(2) do
+ q.breakable('')
+ q.pp(default)
+ end
+ end
+ end
+ end
+
+ if rest
+ q.breakable
+ q.pp(rest)
+ end
+
+ if posts.any?
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(posts) { |value| q.pp(value) } }
+ end
+
+ if keywords.any?
+ q.breakable
+ q.group(2, '(', ')') do
+ q.seplist(keywords) do |(name, default)|
+ q.pp(name)
+
+ if default
+ q.text('=')
+ q.group(2) do
+ q.breakable('')
+ q.pp(default)
+ end
+ end
+ end
+ end
+ end
+
+ if keyword_rest
+ q.breakable
+ q.pp(keyword_rest)
+ end
+
+ if block
+ q.breakable
+ q.pp(block)
+ end
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :params,
+ reqs: requireds,
+ opts: optionals,
+ rest: rest,
+ posts: posts,
+ keywords: keywords,
+ kwrest: keyword_rest,
+ block: block,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_params: (
+ # (nil | Array[Ident]) requireds,
+ # (nil | Array[[Ident, untyped]]) optionals,
+ # (nil | ArgsForward | ExcessedComma | RestParam) rest,
+ # (nil | Array[Ident]) posts,
+ # (nil | Array[[Ident, nil | untyped]]) keywords,
+ # (nil | :nil | KwRestParam) keyword_rest,
+ # (nil | BlockArg) block
+ # ) -> Params
+ def on_params(
+ requireds,
+ optionals,
+ rest,
+ posts,
+ keywords,
+ keyword_rest,
+ block
+ )
+ parts = [
+ *requireds,
+ *optionals&.flatten(1),
+ rest,
+ *posts,
+ *keywords&.flat_map { |(key, value)| [key, value || nil] },
+ (keyword_rest if keyword_rest != :nil),
+ block
+ ].compact
+
+ location =
+ if parts.any?
+ parts[0].location.to(parts[-1].location)
+ else
+ Location.fixed(line: lineno, char: char_pos)
+ end
+
+ Params.new(
+ requireds: requireds || [],
+ optionals: optionals || [],
+ rest: rest,
+ posts: posts || [],
+ keywords: keywords || [],
+ keyword_rest: keyword_rest,
+ block: block,
+ location: location
+ )
+ end
+
+ # Paren represents using balanced parentheses in a couple places in a Ruby
+ # program. In general parentheses can be used anywhere a Ruby expression can
+ # be used.
+ #
+ # (1 + 2)
+ #
+ class Paren
+ # [LParen] the left parenthesis that opened this statement
+ attr_reader :lparen
+
+ # [untyped] the expression inside the parentheses
+ attr_reader :contents
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(lparen:, contents:, location:)
+ @lparen = lparen
+ @contents = contents
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('paren')
+
+ q.breakable
+ q.pp(contents)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :paren, lparen: lparen, cnts: contents, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_paren: (untyped contents) -> Paren
+ def on_paren(contents)
+ lparen = find_token(LParen)
+ rparen = find_token(RParen)
+
+ if contents && contents.is_a?(Params)
+ location = contents.location
+ location =
+ Location.new(
+ start_line: location.start_line,
+ start_char: find_next_statement_start(lparen.location.end_char),
+ end_line: location.end_line,
+ end_char: rparen.location.start_char
+ )
+
+ contents =
+ Params.new(
+ requireds: contents.requireds,
+ optionals: contents.optionals,
+ rest: contents.rest,
+ posts: contents.posts,
+ keywords: contents.keywords,
+ keyword_rest: contents.keyword_rest,
+ block: contents.block,
+ location: location
+ )
+ end
+
+ Paren.new(
+ lparen: lparen,
+ contents: contents,
+ location: lparen.location.to(rparen.location)
+ )
+ end
+
+ # If we encounter a parse error, just immediately bail out so that our runner
+ # can catch it.
+ def on_parse_error(error, *)
+ raise ParseError.new(error, lineno, column)
+ end
+ alias on_alias_error on_parse_error
+ alias on_assign_error on_parse_error
+ alias on_class_name_error on_parse_error
+ alias on_param_error on_parse_error
+
+ # Period represents the use of the +.+ operator. It is usually found in method
+ # calls.
+ class Period
+ # [String] the period
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('period')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :period, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_period: (String value) -> Period
+ def on_period(value)
+ Period.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+ end
+
+ # Program represents the overall syntax tree.
+ class Program
+ # [Statements] the top-level expressions of the program
+ attr_reader :statements
+
+ # [Array[ Comment | EmbDoc ]] the comments inside the program
+ attr_reader :comments
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(statements:, comments:, location:)
+ @statements = statements
+ @comments = comments
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('program')
+
+ q.breakable
+ q.pp(statements)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :program,
+ stmts: statements,
+ comments: comments,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_program: (Statements statements) -> Program
+ def on_program(statements)
+ location =
+ Location.new(
+ start_line: 1,
+ start_char: 0,
+ end_line: lines.length,
+ end_char: source.length
+ )
+
+ statements.body << @__end__ if @__end__
+ statements.bind(0, source.length)
+
+ Program.new(statements: statements, comments: @comments, location: location)
+ end
+
+ # QSymbols represents a symbol literal array without interpolation.
+ #
+ # %i[one two three]
+ #
+ class QSymbols
+ # [Array[ TStringContent ]] the elements of the array
+ attr_reader :elements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(elements:, location:)
+ @elements = elements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('qsymbols')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(elements) { |element| q.pp(element) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :qsymbols, elems: elements, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_qsymbols_add: (QSymbols qsymbols, TStringContent element) -> QSymbols
+ def on_qsymbols_add(qsymbols, element)
+ QSymbols.new(
+ elements: qsymbols.elements << element,
+ location: qsymbols.location.to(element.location)
+ )
+ end
+
+ # QSymbolsBeg represents the beginning of a symbol literal array.
+ #
+ # %i[one two three]
+ #
+ # In the snippet above, QSymbolsBeg represents the "%i[" token. Note that
+ # these kinds of arrays can start with a lot of different delimiter types
+ # (e.g., %i| or %i<).
+ class QSymbolsBeg
+ # [String] the beginning of the array literal
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_qsymbols_beg: (String value) -> QSymbolsBeg
+ def on_qsymbols_beg(value)
+ node =
+ QSymbolsBeg.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # :call-seq:
+ # on_qsymbols_new: () -> QSymbols
+ def on_qsymbols_new
+ qsymbols_beg = find_token(QSymbolsBeg)
+
+ QSymbols.new(elements: [], location: qsymbols_beg.location)
+ end
+
+ # QWords represents a string literal array without interpolation.
+ #
+ # %w[one two three]
+ #
+ class QWords
+ # [Array[ TStringContent ]] the elements of the array
+ attr_reader :elements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(elements:, location:)
+ @elements = elements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('qwords')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(elements) { |element| q.pp(element) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :qwords, elems: elements, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_qwords_add: (QWords qwords, TStringContent element) -> QWords
+ def on_qwords_add(qwords, element)
+ QWords.new(
+ elements: qwords.elements << element,
+ location: qwords.location.to(element.location)
+ )
+ end
+
+ # QWordsBeg represents the beginning of a string literal array.
+ #
+ # %w[one two three]
+ #
+ # In the snippet above, QWordsBeg represents the "%w[" token. Note that these
+ # kinds of arrays can start with a lot of different delimiter types (e.g.,
+ # %w| or %w<).
+ class QWordsBeg
+ # [String] the beginning of the array literal
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_qwords_beg: (String value) -> QWordsBeg
+ def on_qwords_beg(value)
+ node =
+ QWordsBeg.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # :call-seq:
+ # on_qwords_new: () -> QWords
+ def on_qwords_new
+ qwords_beg = find_token(QWordsBeg)
+
+ QWords.new(elements: [], location: qwords_beg.location)
+ end
+
+ # RationalLiteral represents the use of a rational number literal.
+ #
+ # 1r
+ #
+ class RationalLiteral
+ # [String] the rational number literal
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('rational')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :rational, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_rational: (String value) -> RationalLiteral
+ def on_rational(value)
+ node =
+ RationalLiteral.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # RBrace represents the use of a right brace, i.e., +++.
+ class RBrace
+ # [String] the right brace
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_rbrace: (String value) -> RBrace
+ def on_rbrace(value)
+ node =
+ RBrace.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # RBracket represents the use of a right bracket, i.e., +]+.
+ class RBracket
+ # [String] the right bracket
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_rbracket: (String value) -> RBracket
+ def on_rbracket(value)
+ node =
+ RBracket.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # Redo represents the use of the +redo+ keyword.
+ #
+ # redo
+ #
+ class Redo
+ # [String] the value of the keyword
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('redo')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :redo, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_redo: () -> Redo
+ def on_redo
+ keyword = find_token(Kw, 'redo')
+
+ Redo.new(value: keyword.value, location: keyword.location)
+ end
+
+ # RegexpContent represents the body of a regular expression.
+ #
+ # /.+ #{pattern} .+/
+ #
+ # In the example above, a RegexpContent node represents everything contained
+ # within the forward slashes.
+ class RegexpContent
+ # [String] the opening of the regular expression
+ attr_reader :beginning
+
+ # [Array[ StringDVar | StringEmbExpr | TStringContent ]] the parts of the
+ # regular expression
+ attr_reader :parts
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(beginning:, parts:, location:)
+ @beginning = beginning
+ @parts = parts
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_regexp_add: (
+ # RegexpContent regexp_content,
+ # (StringDVar | StringEmbExpr | TStringContent) part
+ # ) -> RegexpContent
+ def on_regexp_add(regexp_content, part)
+ RegexpContent.new(
+ beginning: regexp_content.beginning,
+ parts: regexp_content.parts << part,
+ location: regexp_content.location.to(part.location)
+ )
+ end
+
+ # RegexpBeg represents the start of a regular expression literal.
+ #
+ # /.+/
+ #
+ # In the example above, RegexpBeg represents the first / token. Regular
+ # expression literals can also be declared using the %r syntax, as in:
+ #
+ # %r{.+}
+ #
+ class RegexpBeg
+ # [String] the beginning of the regular expression
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_regexp_beg: (String value) -> RegexpBeg
+ def on_regexp_beg(value)
+ node =
+ RegexpBeg.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # RegexpEnd represents the end of a regular expression literal.
+ #
+ # /.+/m
+ #
+ # In the example above, the RegexpEnd event represents the /m at the end of
+ # the regular expression literal. You can also declare regular expression
+ # literals using %r, as in:
+ #
+ # %r{.+}m
+ #
+ class RegexpEnd
+ # [String] the end of the regular expression
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_regexp_end: (String value) -> RegexpEnd
+ def on_regexp_end(value)
+ RegexpEnd.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+ end
+
+ # RegexpLiteral represents a regular expression literal.
+ #
+ # /.+/
+ #
+ class RegexpLiteral
+ # [String] the beginning of the regular expression literal
+ attr_reader :beginning
+
+ # [String] the ending of the regular expression literal
+ attr_reader :ending
+
+ # [Array[ StringEmbExpr | StringDVar | TStringContent ]] the parts of the
+ # regular expression literal
+ attr_reader :parts
+
+ # [Locatione] the location of this node
+ attr_reader :location
+
+ def initialize(beginning:, ending:, parts:, location:)
+ @beginning = beginning
+ @ending = ending
+ @parts = parts
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('regexp_literal')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(parts) { |part| q.pp(part) } }
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :regexp_literal,
+ beging: beginning,
+ ending: ending,
+ parts: parts,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_regexp_literal: (
+ # RegexpContent regexp_content,
+ # RegexpEnd ending
+ # ) -> RegexpLiteral
+ def on_regexp_literal(regexp_content, ending)
+ RegexpLiteral.new(
+ beginning: regexp_content.beginning,
+ ending: ending.value,
+ parts: regexp_content.parts,
+ location: regexp_content.location.to(ending.location)
+ )
+ end
+
+ # :call-seq:
+ # on_regexp_new: () -> RegexpContent
+ def on_regexp_new
+ regexp_beg = find_token(RegexpBeg)
+
+ RegexpContent.new(
+ beginning: regexp_beg.value,
+ parts: [],
+ location: regexp_beg.location
+ )
+ end
+
+ # RescueEx represents the list of exceptions being rescued in a rescue clause.
+ #
+ # begin
+ # rescue Exception => exception
+ # end
+ #
+ class RescueEx
+ # [untyped] the list of exceptions being rescued
+ attr_reader :exceptions
+
+ # [nil | Field | VarField] the expression being used to capture the raised
+ # exception
+ attr_reader :variable
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(exceptions:, variable:, location:)
+ @exceptions = exceptions
+ @variable = variable
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('rescue_ex')
+
+ q.breakable
+ q.pp(exceptions)
+
+ q.breakable
+ q.pp(variable)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :rescue_ex,
+ extns: exceptions,
+ var: variable,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # Rescue represents the use of the rescue keyword inside of a BodyStmt node.
+ #
+ # begin
+ # rescue
+ # end
+ #
+ class Rescue
+ # [RescueEx] the exceptions being rescued
+ attr_reader :exception
+
+ # [Statements] the expressions to evaluate when an error is rescued
+ attr_reader :statements
+
+ # [nil | Rescue] the optional next clause in the chain
+ attr_reader :consequent
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(exception:, statements:, consequent:, location:)
+ @exception = exception
+ @statements = statements
+ @consequent = consequent
+ @location = location
+ end
+
+ def bind_end(end_char)
+ @location =
+ Location.new(
+ start_line: location.start_line,
+ start_char: location.start_char,
+ end_line: location.end_line,
+ end_char: end_char
+ )
+
+ if consequent
+ consequent.bind_end(end_char)
+ statements.bind_end(consequent.location.start_char)
+ else
+ statements.bind_end(end_char)
+ end
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('rescue')
+
+ if exception
+ q.breakable
+ q.pp(exception)
+ end
+
+ q.breakable
+ q.pp(statements)
+
+ if consequent
+ q.breakable
+ q.pp(consequent)
+ end
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :rescue,
+ extn: exception,
+ stmts: statements,
+ cons: consequent,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_rescue: (
+ # (nil | [untyped] | MRHS | MRHSAddStar) exceptions,
+ # (nil | Field | VarField) variable,
+ # Statements statements,
+ # (nil | Rescue) consequent
+ # ) -> Rescue
+ def on_rescue(exceptions, variable, statements, consequent)
+ keyword = find_token(Kw, 'rescue')
+ exceptions = exceptions[0] if exceptions.is_a?(Array)
+
+ last_node = variable || exceptions || keyword
+ statements.bind(
+ find_next_statement_start(last_node.location.end_char),
+ char_pos
+ )
+
+ # We add an additional inner node here that ripper doesn't provide so that
+ # we have a nice place to attach inline comments. But we only need it if we
+ # have an exception or a variable that we're rescuing.
+ rescue_ex =
+ if exceptions || variable
+ RescueEx.new(
+ exceptions: exceptions,
+ variable: variable,
+ location:
+ Location.new(
+ start_line: keyword.location.start_line,
+ start_char: keyword.location.end_char + 1,
+ end_line: last_node.location.end_line,
+ end_char: last_node.location.end_char
+ )
+ )
+ end
+
+ Rescue.new(
+ exception: rescue_ex,
+ statements: statements,
+ consequent: consequent,
+ location:
+ Location.new(
+ start_line: keyword.location.start_line,
+ start_char: keyword.location.start_char,
+ end_line: lineno,
+ end_char: char_pos
+ )
+ )
+ end
+
+ # RescueMod represents the use of the modifier form of a +rescue+ clause.
+ #
+ # expression rescue value
+ #
+ class RescueMod
+ # [untyped] the expression to execute
+ attr_reader :statement
+
+ # [untyped] the value to use if the executed expression raises an error
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(statement:, value:, location:)
+ @statement = statement
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('rescue_mod')
+
+ q.breakable
+ q.pp(statement)
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :rescue_mod,
+ stmt: statement,
+ value: value,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_rescue_mod: (untyped statement, untyped value) -> RescueMod
+ def on_rescue_mod(statement, value)
+ find_token(Kw, 'rescue')
+
+ RescueMod.new(
+ statement: statement,
+ value: value,
+ location: statement.location.to(value.location)
+ )
+ end
+
+ # RestParam represents defining a parameter in a method definition that
+ # accepts all remaining positional parameters.
+ #
+ # def method(*rest) end
+ #
+ class RestParam
+ # [nil | Ident] the name of the parameter
+ attr_reader :name
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(name:, location:)
+ @name = name
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('rest_param')
+
+ q.breakable
+ q.pp(name)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :rest_param, name: name, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_rest_param: ((nil | Ident) name) -> RestParam
+ def on_rest_param(name)
+ location = find_token(Op, '*').location
+ location = location.to(name.location) if name
+
+ RestParam.new(name: name, location: location)
+ end
+
+ # Retry represents the use of the +retry+ keyword.
+ #
+ # retry
+ #
+ class Retry
+ # [String] the value of the keyword
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('retry')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :retry, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_retry: () -> Retry
+ def on_retry
+ keyword = find_token(Kw, 'retry')
+
+ Retry.new(value: keyword.value, location: keyword.location)
+ end
+
+ # Return represents using the +return+ keyword with arguments.
+ #
+ # return value
+ #
+ class Return
+ # [Args | ArgsAddBlock] the arguments being passed to the keyword
+ attr_reader :arguments
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(arguments:, location:)
+ @arguments = arguments
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('return')
+
+ q.breakable
+ q.pp(arguments)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :return, args: arguments, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_return: ((Args | ArgsAddBlock) arguments) -> Return
+ def on_return(arguments)
+ keyword = find_token(Kw, 'return')
+
+ Return.new(
+ arguments: arguments,
+ location: keyword.location.to(arguments.location)
+ )
+ end
+
+ # Return0 represents the bare +return+ keyword with no arguments.
+ #
+ # return
+ #
+ class Return0
+ # [String] the value of the keyword
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('return0')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :return0, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_return0: () -> Return0
+ def on_return0
+ keyword = find_token(Kw, 'return')
+
+ Return0.new(value: keyword.value, location: keyword.location)
+ end
+
+ # RParen represents the use of a right parenthesis, i.e., +)+.
+ class RParen
+ # [String] the parenthesis
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_rparen: (String value) -> RParen
+ def on_rparen(value)
+ node =
+ RParen.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # SClass represents a block of statements that should be evaluated within the
+ # context of the singleton class of an object. It's frequently used to define
+ # singleton methods.
+ #
+ # class << self
+ # end
+ #
+ class SClass
+ # [untyped] the target of the singleton class to enter
+ attr_reader :target
+
+ # [BodyStmt] the expressions to be executed
+ attr_reader :bodystmt
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(target:, bodystmt:, location:)
+ @target = target
+ @bodystmt = bodystmt
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('sclass')
+
+ q.breakable
+ q.pp(target)
+
+ q.breakable
+ q.pp(bodystmt)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :sclass,
+ target: target,
+ bodystmt: bodystmt,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_sclass: (untyped target, BodyStmt bodystmt) -> SClass
+ def on_sclass(target, bodystmt)
+ beginning = find_token(Kw, 'class')
+ ending = find_token(Kw, 'end')
+
+ bodystmt.bind(
+ find_next_statement_start(target.location.end_char),
+ ending.location.start_char
+ )
+
+ SClass.new(
+ target: target,
+ bodystmt: bodystmt,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # def on_semicolon(value)
+ # value
+ # end
+
+ # def on_sp(value)
+ # value
+ # end
+
+ # stmts_add is a parser event that represents a single statement inside a
+ # list of statements within any lexical block. It accepts as arguments the
+ # parent stmts node as well as an stmt which can be any expression in
+ # Ruby.
+ def on_stmts_add(statements, statement)
+ statements << statement
+ end
+
+ # Everything that has a block of code inside of it has a list of statements.
+ # Normally we would just track those as a node that has an array body, but we
+ # have some special handling in order to handle empty statement lists. They
+ # need to have the right location information, so all of the parent node of
+ # stmts nodes will report back down the location information. We then
+ # propagate that onto void_stmt nodes inside the stmts in order to make sure
+ # all comments get printed appropriately.
+ class Statements
+ # [SyntaxTree] the parser that created this node
+ attr_reader :parser
+
+ # [Array[ untyped ]] the list of expressions contained within this node
+ attr_reader :body
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parser:, body:, location:)
+ @parser = parser
+ @body = body
+ @location = location
+ end
+
+ def bind(start_char, end_char)
+ @location =
+ Location.new(
+ start_line: location.start_line,
+ start_char: start_char,
+ end_line: location.end_line,
+ end_char: end_char
+ )
+
+ if body[0].is_a?(VoidStmt)
+ location = body[0].location
+ location =
+ Location.new(
+ start_line: location.start_line,
+ start_char: start_char,
+ end_line: location.end_line,
+ end_char: start_char
+ )
+
+ body[0] = VoidStmt.new(location: location)
+ end
+
+ attach_comments(start_char, end_char)
+ end
+
+ def bind_end(end_char)
+ @location =
+ Location.new(
+ start_line: location.start_line,
+ start_char: location.start_char,
+ end_line: location.end_line,
+ end_char: end_char
+ )
+ end
+
+ def <<(statement)
+ @location =
+ body.any? ? location.to(statement.location) : statement.location
+
+ body << statement
+ self
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('statements')
+
+ q.breakable
+ q.seplist(body) { |statement| q.pp(statement) }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :statements, body: body, loc: location }.to_json(*opts)
+ end
+
+ private
+
+ def attach_comments(start_char, end_char)
+ attachable =
+ parser.comments.select do |comment|
+ !comment.inline? && start_char <= comment.location.start_char &&
+ end_char >= comment.location.end_char &&
+ !comment.value.include?('prettier-ignore')
+ end
+
+ return if attachable.empty?
+
+ parser.comments -= attachable
+ @body = (body + attachable).sort_by! { |node| node.location.start_char }
+ end
+ end
+
+ # :call-seq:
+ # on_stmts_new: () -> Statements
+ def on_stmts_new
+ Statements.new(
+ parser: self,
+ body: [],
+ location: Location.fixed(line: lineno, char: char_pos)
+ )
+ end
+
+ # StringContent represents the contents of a string-like value.
+ #
+ # "string"
+ #
+ class StringContent
+ # [Array[ StringEmbExpr | StringDVar | TStringContent ]] the parts of the
+ # string
+ attr_reader :parts
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parts:, location:)
+ @parts = parts
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_string_add: (
+ # String string,
+ # (StringEmbExpr | StringDVar | TStringContent) part
+ # ) -> StringContent
+ def on_string_add(string, part)
+ location =
+ string.parts.any? ? string.location.to(part.location) : part.location
+
+ StringContent.new(parts: string.parts << part, location: location)
+ end
+
+ # StringConcat represents concatenating two strings together using a backward
+ # slash.
+ #
+ # "first" \
+ # "second"
+ #
+ class StringConcat
+ # [StringConcat | StringLiteral] the left side of the concatenation
+ attr_reader :left
+
+ # [StringLiteral] the right side of the concatenation
+ attr_reader :right
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(left:, right:, location:)
+ @left = left
+ @right = right
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('string_concat')
+
+ q.breakable
+ q.pp(left)
+
+ q.breakable
+ q.pp(right)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :string_concat, left: left, right: right, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_string_concat: (
+ # (StringConcat | StringLiteral) left,
+ # StringLiteral right
+ # ) -> StringConcat
+ def on_string_concat(left, right)
+ StringConcat.new(
+ left: left,
+ right: right,
+ location: left.location.to(right.location)
+ )
+ end
+
+ # :call-seq:
+ # on_string_content: () -> StringContent
+ def on_string_content
+ StringContent.new(
+ parts: [],
+ location: Location.fixed(line: lineno, char: char_pos)
+ )
+ end
+
+ # StringDVar represents shorthand interpolation of a variable into a string.
+ # It allows you to take an instance variable, class variable, or global
+ # variable and omit the braces when interpolating.
+ #
+ # "#@variable"
+ #
+ class StringDVar
+ # [Backref | VarRef] the variable being interpolated
+ attr_reader :variable
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(variable:, location:)
+ @variable = variable
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('string_dvar')
+
+ q.breakable
+ q.pp(variable)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :string_dvar, var: variable, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_string_dvar: ((Backref | VarRef) variable) -> StringDVar
+ def on_string_dvar(variable)
+ embvar = find_token(EmbVar)
+
+ StringDVar.new(
+ variable: variable,
+ location: embvar.location.to(variable.location)
+ )
+ end
+
+ # StringEmbExpr represents interpolated content. It can be contained within a
+ # couple of different parent nodes, including regular expressions, strings,
+ # and dynamic symbols.
+ #
+ # "string #{expression}"
+ #
+ class StringEmbExpr
+ # [Statements] the expressions to be interpolated
+ attr_reader :statements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(statements:, location:)
+ @statements = statements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('string_embexpr')
+
+ q.breakable
+ q.pp(statements)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :string_embexpr, stmts: statements, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_string_embexpr: (Statements statements) -> StringEmbExpr
+ def on_string_embexpr(statements)
+ embexpr_beg = find_token(EmbExprBeg)
+ embexpr_end = find_token(EmbExprEnd)
+
+ statements.bind(
+ embexpr_beg.location.end_char,
+ embexpr_end.location.start_char
+ )
+
+ StringEmbExpr.new(
+ statements: statements,
+ location: embexpr_beg.location.to(embexpr_end.location)
+ )
+ end
+
+ # StringLiteral represents a string literal.
+ #
+ # "string"
+ #
+ class StringLiteral
+ # [Array[ StringEmbExpr | StringDVar | TStringContent ]] the parts of the
+ # string literal
+ attr_reader :parts
+
+ # [String] which quote was used by the string literal
+ attr_reader :quote
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parts:, quote:, location:)
+ @parts = parts
+ @quote = quote
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('string_literal')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(parts) { |part| q.pp(part) } }
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :string_literal,
+ parts: parts,
+ quote: quote,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_string_literal: (String string) -> Heredoc | StringLiteral
+ def on_string_literal(string)
+ heredoc = @heredocs[-1]
+
+ if heredoc && heredoc.ending
+ heredoc = @heredocs.pop
+
+ Heredoc.new(
+ beginning: heredoc.beginning,
+ ending: heredoc.ending,
+ parts: string.parts,
+ location: heredoc.location
+ )
+ else
+ tstring_beg = find_token(TStringBeg)
+ tstring_end = find_token(TStringEnd)
+
+ StringLiteral.new(
+ parts: string.parts,
+ quote: tstring_beg.value,
+ location: tstring_beg.location.to(tstring_end.location)
+ )
+ end
+ end
+
+ # Super represents using the +super+ keyword with arguments. It can optionally
+ # use parentheses.
+ #
+ # super(value)
+ #
+ class Super
+ # [ArgParen | Args | ArgsAddBlock] the arguments to the keyword
+ attr_reader :arguments
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(arguments:, location:)
+ @arguments = arguments
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('super')
+
+ q.breakable
+ q.pp(arguments)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :super, args: arguments, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_super: ((ArgParen | Args | ArgsAddBlock) arguments) -> Super
+ def on_super(arguments)
+ keyword = find_token(Kw, 'super')
+
+ Super.new(
+ arguments: arguments,
+ location: keyword.location.to(arguments.location)
+ )
+ end
+
+ # SymBeg represents the beginning of a symbol literal.
+ #
+ # :symbol
+ #
+ # SymBeg is also used for dynamic symbols, as in:
+ #
+ # :"symbol"
+ #
+ # Finally, SymBeg is also used for symbols using the %s syntax, as in:
+ #
+ # %s[symbol]
+ #
+ # The value of this node is a string. In most cases (as in the first example
+ # above) it will contain just ":". In the case of dynamic symbols it will
+ # contain ":'" or ":\"". In the case of %s symbols, it will contain the start
+ # of the symbol including the %s and the delimiter.
+ class SymBeg
+ # [String] the beginning of the symbol
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # symbeg is a token that represents the beginning of a symbol literal.
+ # In most cases it will contain just ":" as in the value, but if its a dynamic
+ # symbol being defined it will contain ":'" or ":\"".
+ def on_symbeg(value)
+ node =
+ SymBeg.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # SymbolContent represents symbol contents and is always the child of a
+ # SymbolLiteral node.
+ #
+ # :symbol
+ #
+ class SymbolContent
+ # [Backtick | Const | CVar | GVar | Ident | IVar | Kw | Op] the value of the
+ # symbol
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_symbol: (
+ # (Backtick | Const | CVar | GVar | Ident | IVar | Kw | Op) value
+ # ) -> SymbolContent
+ def on_symbol(value)
+ tokens.pop
+
+ SymbolContent.new(value: value, location: value.location)
+ end
+
+ # SymbolLiteral represents a symbol in the system with no interpolation
+ # (as opposed to a DynaSymbol which has interpolation).
+ #
+ # :symbol
+ #
+ class SymbolLiteral
+ # [Backtick | Const | CVar | GVar | Ident | IVar | Kw | Op] the value of the
+ # symbol
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('symbol_literal')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :symbol_literal, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_symbol_literal: (
+ # (
+ # Backtick | Const | CVar | GVar | Ident |
+ # IVar | Kw | Op | SymbolContent
+ # ) value
+ # ) -> SymbolLiteral
+ def on_symbol_literal(value)
+ if tokens[-1] == value
+ SymbolLiteral.new(value: tokens.pop, location: value.location)
+ else
+ symbeg = find_token(SymBeg)
+
+ SymbolLiteral.new(
+ value: value.value,
+ location: symbeg.location.to(value.location)
+ )
+ end
+ end
+
+ # Symbols represents a symbol array literal with interpolation.
+ #
+ # %I[one two three]
+ #
+ class Symbols
+ # [Array[ Word ]] the words in the symbol array literal
+ attr_reader :elements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(elements:, location:)
+ @elements = elements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('symbols')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(elements) { |element| q.pp(element) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :symbols, elems: elements, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_symbols_add: (Symbols symbols, Word word) -> Symbols
+ def on_symbols_add(symbols, word)
+ Symbols.new(
+ elements: symbols.elements << word,
+ location: symbols.location.to(word.location)
+ )
+ end
+
+ # SymbolsBeg represents the start of a symbol array literal with
+ # interpolation.
+ #
+ # %I[one two three]
+ #
+ # In the snippet above, SymbolsBeg represents the "%I[" token. Note that these
+ # kinds of arrays can start with a lot of different delimiter types
+ # (e.g., %I| or %I<).
+ class SymbolsBeg
+ # [String] the beginning of the symbol literal array
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_symbols_beg: (String value) -> SymbolsBeg
+ def on_symbols_beg(value)
+ node =
+ SymbolsBeg.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # :call-seq:
+ # on_symbols_new: () -> Symbols
+ def on_symbols_new
+ symbols_beg = find_token(SymbolsBeg)
+
+ Symbols.new(elements: [], location: symbols_beg.location)
+ end
+
+ # TLambda represents the beginning of a lambda literal.
+ #
+ # -> { value }
+ #
+ # In the example above the TLambda represents the +->+ operator.
+ class TLambda
+ # [String] the beginning of the lambda literal
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_tlambda: (String value) -> TLambda
+ def on_tlambda(value)
+ node =
+ TLambda.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # TLamBeg represents the beginning of the body of a lambda literal using
+ # braces.
+ #
+ # -> { value }
+ #
+ # In the example above the TLamBeg represents the +{+ operator.
+ class TLamBeg
+ # [String] the beginning of the body of the lambda literal
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_tlambeg: (String value) -> TLamBeg
+ def on_tlambeg(value)
+ node =
+ TLamBeg.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # TopConstField is always the child node of some kind of assignment. It
+ # represents when you're assigning to a constant that is being referenced at
+ # the top level.
+ #
+ # ::Constant = value
+ #
+ class TopConstField
+ # [Const] the constant being assigned
+ attr_reader :constant
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(constant:, location:)
+ @constant = constant
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('top_const_field')
+
+ q.breakable
+ q.pp(constant)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :top_const_field, constant: constant, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_top_const_field: (Const constant) -> TopConstRef
+ def on_top_const_field(constant)
+ operator = find_colon2_before(constant)
+
+ TopConstField.new(
+ constant: constant,
+ location: operator.location.to(constant.location)
+ )
+ end
+
+ # TopConstRef is very similar to TopConstField except that it is not involved
+ # in an assignment.
+ #
+ # ::Constant
+ #
+ class TopConstRef
+ # [Const] the constant being referenced
+ attr_reader :constant
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(constant:, location:)
+ @constant = constant
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('top_const_ref')
+
+ q.breakable
+ q.pp(constant)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :top_const_ref, constant: constant, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_top_const_ref: (Const constant) -> TopConstRef
+ def on_top_const_ref(constant)
+ operator = find_colon2_before(constant)
+
+ TopConstRef.new(
+ constant: constant,
+ location: operator.location.to(constant.location)
+ )
+ end
+
+ # TStringBeg represents the beginning of a string literal.
+ #
+ # "string"
+ #
+ # In the example above, TStringBeg represents the first set of quotes. Strings
+ # can also use single quotes. They can also be declared using the +%q+ and
+ # +%Q+ syntax, as in:
+ #
+ # %q{string}
+ #
+ class TStringBeg
+ # [String] the beginning of the string
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_tstring_beg: (String value) -> TStringBeg
+ def on_tstring_beg(value)
+ node =
+ TStringBeg.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # TStringContent represents plain characters inside of an entity that accepts
+ # string content like a string, heredoc, command string, or regular
+ # expression.
+ #
+ # "string"
+ #
+ # In the example above, TStringContent represents the +string+ token contained
+ # within the string.
+ class TStringContent
+ # [String] the content of the string
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('tstring_content')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :tstring_content,
+ value: value.force_encoding('UTF-8'),
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_tstring_content: (String value) -> TStringContent
+ def on_tstring_content(value)
+ TStringContent.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+ end
+
+ # TStringEnd represents the end of a string literal.
+ #
+ # "string"
+ #
+ # In the example above, TStringEnd represents the second set of quotes.
+ # Strings can also use single quotes. They can also be declared using the +%q+
+ # and +%Q+ syntax, as in:
+ #
+ # %q{string}
+ #
+ class TStringEnd
+ # [String] the end of the string
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_tstring_end: (String value) -> TStringEnd
+ def on_tstring_end(value)
+ node =
+ TStringEnd.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # Not represents the unary +not+ method being called on an expression.
+ #
+ # not value
+ #
+ class Not
+ # [untyped] the statement on which to operate
+ attr_reader :statement
+
+ # [boolean] whether or not parentheses were used
+ attr_reader :parentheses
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(statement:, parentheses:, location:)
+ @statement = statement
+ @parentheses = parentheses
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('not')
+
+ q.breakable
+ q.pp(statement)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :not,
+ value: statement,
+ paren: parentheses,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # Unary represents a unary method being called on an expression, as in +!+ or
+ # +~+.
+ #
+ # !value
+ #
+ class Unary
+ # [String] the operator being used
+ attr_reader :operator
+
+ # [untyped] the statement on which to operate
+ attr_reader :statement
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(operator:, statement:, location:)
+ @operator = operator
+ @statement = statement
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('unary')
+
+ q.breakable
+ q.pp(operator)
+
+ q.breakable
+ q.pp(statement)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :unary, op: operator, value: statement, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_unary: (:not operator, untyped statement) -> Not
+ # | (Symbol operator, untyped statement) -> Unary
+ def on_unary(operator, statement)
+ if operator == :not
+ # We have somewhat special handling of the not operator since if it has
+ # parentheses they don't get reported as a paren node for some reason.
+
+ beginning = find_token(Kw, 'not')
+ ending = statement
+
+ range = beginning.location.end_char...statement.location.start_char
+ paren = source[range].include?('(')
+
+ if paren
+ find_token(LParen)
+ ending = find_token(RParen)
+ end
+
+ Not.new(
+ statement: statement,
+ parentheses: paren,
+ location: beginning.location.to(ending.location)
+ )
+ else
+ # Special case instead of using find_token here. It turns out that
+ # if you have a range that goes from a negative number to a negative
+ # number then you can end up with a .. or a ... that's higher in the
+ # stack. So we need to explicitly disallow those operators.
+ index =
+ tokens.rindex do |token|
+ token.is_a?(Op) &&
+ token.location.start_char < statement.location.start_char &&
+ !%w[.. ...].include?(token.value)
+ end
+
+ beginning = tokens.delete_at(index)
+
+ Unary.new(
+ operator: operator[0], # :+@ -> "+"
+ statement: statement,
+ location: beginning.location.to(statement.location)
+ )
+ end
+ end
+
+ # Undef represents the use of the +undef+ keyword.
+ #
+ # undef method
+ #
+ class Undef
+ # [Array[ DynaSymbol | SymbolLiteral ]] the symbols to undefine
+ attr_reader :symbols
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(symbols:, location:)
+ @symbols = symbols
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('undef')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(symbols) { |symbol| q.pp(symbol) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :undef, syms: symbols, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_undef: (Array[DynaSymbol | SymbolLiteral] symbols) -> Undef
+ def on_undef(symbols)
+ keyword = find_token(Kw, 'undef')
+
+ Undef.new(
+ symbols: symbols,
+ location: keyword.location.to(symbols.last.location)
+ )
+ end
+
+ # Unless represents the first clause in an +unless+ chain.
+ #
+ # unless predicate
+ # end
+ #
+ class Unless
+ # [untyped] the expression to be checked
+ attr_reader :predicate
+
+ # [Statements] the expressions to be executed
+ attr_reader :statements
+
+ # [nil, Elsif, Else] the next clause in the chain
+ attr_reader :consequent
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(predicate:, statements:, consequent:, location:)
+ @predicate = predicate
+ @statements = statements
+ @consequent = consequent
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('unless')
+
+ q.breakable
+ q.pp(predicate)
+
+ q.breakable
+ q.pp(statements)
+
+ if consequent
+ q.breakable
+ q.pp(consequent)
+ end
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :unless,
+ pred: predicate,
+ stmts: statements,
+ cons: consequent,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_unless: (
+ # untyped predicate,
+ # Statements statements,
+ # ((nil | Elsif | Else) consequent)
+ # ) -> Unless
+ def on_unless(predicate, statements, consequent)
+ beginning = find_token(Kw, 'unless')
+ ending = consequent || find_token(Kw, 'end')
+
+ statements.bind(predicate.location.end_char, ending.location.start_char)
+
+ Unless.new(
+ predicate: predicate,
+ statements: statements,
+ consequent: consequent,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # UnlessMod represents the modifier form of an +unless+ statement.
+ #
+ # expression unless predicate
+ #
+ class UnlessMod
+ # [untyped] the expression to be executed
+ attr_reader :statement
+
+ # [untyped] the expression to be checked
+ attr_reader :predicate
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(statement:, predicate:, location:)
+ @statement = statement
+ @predicate = predicate
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('unless_mod')
+
+ q.breakable
+ q.pp(statement)
+
+ q.breakable
+ q.pp(predicate)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :unless_mod,
+ stmt: statement,
+ pred: predicate,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_unless_mod: (untyped predicate, untyped statement) -> UnlessMod
+ def on_unless_mod(predicate, statement)
+ find_token(Kw, 'unless')
+
+ UnlessMod.new(
+ statement: statement,
+ predicate: predicate,
+ location: statement.location.to(predicate.location)
+ )
+ end
+
+ # Until represents an +until+ loop.
+ #
+ # until predicate
+ # end
+ #
+ class Until
+ # [untyped] the expression to be checked
+ attr_reader :predicate
+
+ # [Statements] the expressions to be executed
+ attr_reader :statements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(predicate:, statements:, location:)
+ @predicate = predicate
+ @statements = statements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('until')
+
+ q.breakable
+ q.pp(predicate)
+
+ q.breakable
+ q.pp(statements)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :until,
+ pred: predicate,
+ stmts: statements,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_until: (untyped predicate, Statements statements) -> Until
+ def on_until(predicate, statements)
+ beginning = find_token(Kw, 'until')
+ ending = find_token(Kw, 'end')
+
+ # Consume the do keyword if it exists so that it doesn't get confused for
+ # some other block
+ keyword = find_token(Kw, 'do', consume: false)
+ if keyword && keyword.location.start_char > predicate.location.end_char &&
+ keyword.location.end_char < ending.location.start_char
+ tokens.delete(keyword)
+ end
+
+ # Update the Statements location information
+ statements.bind(predicate.location.end_char, ending.location.start_char)
+
+ Until.new(
+ predicate: predicate,
+ statements: statements,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # UntilMod represents the modifier form of a +until+ loop.
+ #
+ # expression until predicate
+ #
+ class UntilMod
+ # [untyped] the expression to be executed
+ attr_reader :statement
+
+ # [untyped] the expression to be checked
+ attr_reader :predicate
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(statement:, predicate:, location:)
+ @statement = statement
+ @predicate = predicate
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('until_mod')
+
+ q.breakable
+ q.pp(statement)
+
+ q.breakable
+ q.pp(predicate)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :until_mod,
+ stmt: statement,
+ pred: predicate,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_until_mod: (untyped predicate, untyped statement) -> UntilMod
+ def on_until_mod(predicate, statement)
+ find_token(Kw, 'until')
+
+ UntilMod.new(
+ statement: statement,
+ predicate: predicate,
+ location: statement.location.to(predicate.location)
+ )
+ end
+
+ # VarAlias represents when you're using the +alias+ keyword with global
+ # variable arguments.
+ #
+ # alias $new $old
+ #
+ class VarAlias
+ # [GVar] the new alias of the variable
+ attr_reader :left
+
+ # [Backref | GVar] the current name of the variable to be aliased
+ attr_reader :right
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(left:, right:, location:)
+ @left = left
+ @right = right
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('var_alias')
+
+ q.breakable
+ q.pp(left)
+
+ q.breakable
+ q.pp(right)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :var_alias, left: left, right: right, loc: location }.to_json(
+ *opts
+ )
+ end
+ end
+
+ # :call-seq:
+ # on_var_alias: (GVar left, (Backref | GVar) right) -> VarAlias
+ def on_var_alias(left, right)
+ keyword = find_token(Kw, 'alias')
+
+ VarAlias.new(
+ left: left,
+ right: right,
+ location: keyword.location.to(right.location)
+ )
+ end
+
+ # VarField represents a variable that is being assigned a value. As such, it
+ # is always a child of an assignment type node.
+ #
+ # variable = value
+ #
+ # In the example above, the VarField node represents the +variable+ token.
+ class VarField
+ # [nil | Const | CVar | GVar | Ident | IVar] the target of this node
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('var_field')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :var_field, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_var_field: (
+ # (nil | Const | CVar | GVar | Ident | IVar) value
+ # ) -> VarField
+ def on_var_field(value)
+ location =
+ if value
+ value.location
+ else
+ # You can hit this pattern if you're assigning to a splat using pattern
+ # matching syntax in Ruby 2.7+
+ Location.fixed(line: lineno, char: char_pos)
+ end
+
+ VarField.new(value: value, location: location)
+ end
+
+ # VarRef represents a variable reference.
+ #
+ # true
+ #
+ # This can be a plain local variable like the example above. It can also be a
+ # constant, a class variable, a global variable, an instance variable, a
+ # keyword (like +self+, +nil+, +true+, or +false+), or a numbered block
+ # variable.
+ class VarRef
+ # [Const | CVar | GVar | Ident | IVar | Kw] the value of this node
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('var_ref')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :var_ref, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_var_ref: ((Const | CVar | GVar | Ident | IVar | Kw) value) -> VarRef
+ def on_var_ref(value)
+ VarRef.new(value: value, location: value.location)
+ end
+
+ # AccessCtrl represents a call to a method visibility control, i.e., +public+,
+ # +protected+, or +private+.
+ #
+ # private
+ #
+ class AccessCtrl
+ # [Ident] the value of this expression
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('access_ctrl')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :access_ctrl, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # VCall represent any plain named object with Ruby that could be either a
+ # local variable or a method call.
+ #
+ # variable
+ #
+ class VCall
+ # [Ident] the value of this expression
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('vcall')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :vcall, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_vcall: (Ident ident) -> AccessCtrl | VCall
+ def on_vcall(ident)
+ @controls ||= %w[private protected public].freeze
+
+ if @controls.include?(ident.value) && ident.value == lines[lineno - 1].strip
+ # Access controls like private, protected, and public are reported as
+ # vcall nodes since they're technically method calls. We want to be able
+ # add new lines around them as necessary, so here we're going to
+ # explicitly track those as a different node type.
+ AccessCtrl.new(value: ident, location: ident.location)
+ else
+ VCall.new(value: ident, location: ident.location)
+ end
+ end
+
+ # VoidStmt represents an empty lexical block of code.
+ #
+ # ;;
+ #
+ class VoidStmt
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(location:)
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') { q.text('void_stmt') }
+ end
+
+ def to_json(*opts)
+ { type: :void_stmt, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_void_stmt: () -> VoidStmt
+ def on_void_stmt
+ VoidStmt.new(location: Location.fixed(line: lineno, char: char_pos))
+ end
+
+ # When represents a +when+ clause in a +case+ chain.
+ #
+ # case value
+ # when predicate
+ # end
+ #
+ class When
+ # [untyped] the arguments to the when clause
+ attr_reader :arguments
+
+ # [Statements] the expressions to be executed
+ attr_reader :statements
+
+ # [nil | Else | When] the next clause in the chain
+ attr_reader :consequent
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(arguments:, statements:, consequent:, location:)
+ @arguments = arguments
+ @statements = statements
+ @consequent = consequent
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('when')
+
+ q.breakable
+ q.pp(arguments)
+
+ q.breakable
+ q.pp(statements)
+
+ if consequent
+ q.breakable
+ q.pp(consequent)
+ end
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :when,
+ args: arguments,
+ stmts: statements,
+ cons: consequent,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_when: (
+ # untyped arguments,
+ # Statements statements,
+ # (nil | Else | When) consequent
+ # ) -> When
+ def on_when(arguments, statements, consequent)
+ beginning = find_token(Kw, 'when')
+ ending = consequent || find_token(Kw, 'end')
+
+ statements.bind(arguments.location.end_char, ending.location.start_char)
+
+ When.new(
+ arguments: arguments,
+ statements: statements,
+ consequent: consequent,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # While represents a +while+ loop.
+ #
+ # while predicate
+ # end
+ #
+ class While
+ # [untyped] the expression to be checked
+ attr_reader :predicate
+
+ # [Statements] the expressions to be executed
+ attr_reader :statements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(predicate:, statements:, location:)
+ @predicate = predicate
+ @statements = statements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('while')
+
+ q.breakable
+ q.pp(predicate)
+
+ q.breakable
+ q.pp(statements)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :while,
+ pred: predicate,
+ stmts: statements,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_while: (untyped predicate, Statements statements) -> While
+ def on_while(predicate, statements)
+ beginning = find_token(Kw, 'while')
+ ending = find_token(Kw, 'end')
+
+ # Consume the do keyword if it exists so that it doesn't get confused for
+ # some other block
+ keyword = find_token(Kw, 'do', consume: false)
+ if keyword && keyword.location.start_char > predicate.location.end_char &&
+ keyword.location.end_char < ending.location.start_char
+ tokens.delete(keyword)
+ end
+
+ # Update the Statements location information
+ statements.bind(predicate.location.end_char, ending.location.start_char)
+
+ While.new(
+ predicate: predicate,
+ statements: statements,
+ location: beginning.location.to(ending.location)
+ )
+ end
+
+ # WhileMod represents the modifier form of a +while+ loop.
+ #
+ # expression while predicate
+ #
+ class WhileMod
+ # [untyped] the expression to be executed
+ attr_reader :statement
+
+ # [untyped] the expression to be checked
+ attr_reader :predicate
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(statement:, predicate:, location:)
+ @statement = statement
+ @predicate = predicate
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('while_mod')
+
+ q.breakable
+ q.pp(statement)
+
+ q.breakable
+ q.pp(predicate)
+ end
+ end
+
+ def to_json(*opts)
+ {
+ type: :while_mod,
+ stmt: statement,
+ pred: predicate,
+ loc: location
+ }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_while_mod: (untyped predicate, untyped statement) -> WhileMod
+ def on_while_mod(predicate, statement)
+ find_token(Kw, 'while')
+
+ WhileMod.new(
+ statement: statement,
+ predicate: predicate,
+ location: statement.location.to(predicate.location)
+ )
+ end
+
+ # Word represents an element within a special array literal that accepts
+ # interpolation.
+ #
+ # %W[a#{b}c xyz]
+ #
+ # In the example above, there would be two Word nodes within a parent Words
+ # node.
+ class Word
+ # [Array[ StringEmbExpr | StringDVar | TStringContent ]] the parts of the
+ # word
+ attr_reader :parts
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parts:, location:)
+ @parts = parts
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('word')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(parts) { |part| q.pp(part) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :word, parts: parts, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_word_add: (
+ # Word word,
+ # (StringEmbExpr | StringDVar | TStringContent) part
+ # ) -> Word
+ def on_word_add(word, part)
+ location =
+ word.parts.empty? ? part.location : word.location.to(part.location)
+
+ Word.new(parts: word.parts << part, location: location)
+ end
+
+ # :call-seq:
+ # on_word_new: () -> Word
+ def on_word_new
+ Word.new(parts: [], location: Location.fixed(line: lineno, char: char_pos))
+ end
+
+ # Words represents a string literal array with interpolation.
+ #
+ # %W[one two three]
+ #
+ class Words
+ # [Array[ Word ]] the elements of this array
+ attr_reader :elements
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(elements:, location:)
+ @elements = elements
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('words')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(elements) { |element| q.pp(element) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :words, elems: elements, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_words_add: (Words words, Word word) -> Words
+ def on_words_add(words, word)
+ Words.new(
+ elements: words.elements << word,
+ location: words.location.to(word.location)
+ )
+ end
+
+ # WordsBeg represents the beginning of a string literal array with
+ # interpolation.
+ #
+ # %W[one two three]
+ #
+ # In the snippet above, a WordsBeg would be created with the value of "%W[".
+ # Note that these kinds of arrays can start with a lot of different delimiter
+ # types (e.g., %W| or %W<).
+ class WordsBeg
+ # [String] the start of the word literal array
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_words_beg: (String value) -> WordsBeg
+ def on_words_beg(value)
+ node =
+ WordsBeg.new(
+ value: value,
+ location: Location.token(line: lineno, char: char_pos, size: value.size)
+ )
+
+ tokens << node
+ node
+ end
+
+ # :call-seq:
+ # on_words_new: () -> Words
+ def on_words_new
+ words_beg = find_token(WordsBeg)
+
+ Words.new(elements: [], location: words_beg.location)
+ end
+
+ # def on_words_sep(value)
+ # value
+ # end
+
+ # XString represents the contents of an XStringLiteral.
+ #
+ # `ls`
+ #
+ class XString
+ # [Array[ StringEmbExpr | StringDVar | TStringContent ]] the parts of the
+ # xstring
+ attr_reader :parts
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parts:, location:)
+ @parts = parts
+ @location = location
+ end
+ end
+
+ # :call-seq:
+ # on_xstring_add: (
+ # XString xstring,
+ # (StringEmbExpr | StringDVar | TStringContent) part
+ # ) -> XString
+ def on_xstring_add(xstring, part)
+ XString.new(
+ parts: xstring.parts << part,
+ location: xstring.location.to(part.location)
+ )
+ end
+
+ # :call-seq:
+ # on_xstring_new: () -> XString
+ def on_xstring_new
+ heredoc = @heredocs[-1]
+
+ location =
+ if heredoc && heredoc.beginning.value.include?('`')
+ heredoc.location
+ else
+ find_token(Backtick).location
+ end
+
+ XString.new(parts: [], location: location)
+ end
+
+ # XStringLiteral represents a string that gets executed.
+ #
+ # `ls`
+ #
+ class XStringLiteral
+ # [Array[ StringEmbExpr | StringDVar | TStringContent ]] the parts of the
+ # xstring
+ attr_reader :parts
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(parts:, location:)
+ @parts = parts
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('xstring_literal')
+
+ q.breakable
+ q.group(2, '(', ')') { q.seplist(parts) { |part| q.pp(part) } }
+ end
+ end
+
+ def to_json(*opts)
+ { type: :xstring_literal, parts: parts, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_xstring_literal: (XString xstring) -> Heredoc | XStringLiteral
+ def on_xstring_literal(xstring)
+ heredoc = @heredocs[-1]
+
+ if heredoc && heredoc.beginning.value.include?('`')
+ Heredoc.new(
+ beginning: heredoc.beginning,
+ ending: heredoc.ending,
+ parts: xstring.parts,
+ location: heredoc.location
+ )
+ else
+ ending = find_token(TStringEnd)
+
+ XStringLiteral.new(
+ parts: xstring.parts,
+ location: xstring.location.to(ending.location)
+ )
+ end
+ end
+
+ # Yield represents using the +yield+ keyword with arguments.
+ #
+ # yield value
+ #
+ class Yield
+ # [ArgsAddBlock | Paren] the arguments passed to the yield
+ attr_reader :arguments
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(arguments:, location:)
+ @arguments = arguments
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('yield')
+
+ q.breakable
+ q.pp(arguments)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :yield, args: arguments, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_yield: ((ArgsAddBlock | Paren) arguments) -> Yield
+ def on_yield(arguments)
+ keyword = find_token(Kw, 'yield')
+
+ Yield.new(
+ arguments: arguments,
+ location: keyword.location.to(arguments.location)
+ )
+ end
+
+ # Yield0 represents the bare +yield+ keyword with no arguments.
+ #
+ # yield
+ #
+ class Yield0
+ # [String] the value of the keyword
+ attr_reader :value
+
+ # [Location] the location of this node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('yield0')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :yield0, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_yield0: () -> Yield0
+ def on_yield0
+ keyword = find_token(Kw, 'yield')
+
+ Yield0.new(value: keyword.value, location: keyword.location)
+ end
+
+ # ZSuper represents the bare +super+ keyword with no arguments.
+ #
+ # super
+ #
+ class ZSuper
+ # [String] the value of the keyword
+ attr_reader :value
+
+ # [Location] the location of the node
+ attr_reader :location
+
+ def initialize(value:, location:)
+ @value = value
+ @location = location
+ end
+
+ def pretty_print(q)
+ q.group(2, '(', ')') do
+ q.text('zsuper')
+
+ q.breakable
+ q.pp(value)
+ end
+ end
+
+ def to_json(*opts)
+ { type: :zsuper, value: value, loc: location }.to_json(*opts)
+ end
+ end
+
+ # :call-seq:
+ # on_zsuper: () -> ZSuper
+ def on_zsuper
+ keyword = find_token(Kw, 'super')
+
+ ZSuper.new(value: keyword.value, location: keyword.location)
+ end
+end
diff --git a/spec/syntax_suggest/fixtures/this_project_extra_def.rb.txt b/spec/syntax_suggest/fixtures/this_project_extra_def.rb.txt
new file mode 100644
index 0000000000..e62fd3fa66
--- /dev/null
+++ b/spec/syntax_suggest/fixtures/this_project_extra_def.rb.txt
@@ -0,0 +1,64 @@
+module SyntaxErrorSearch
+ # Used for formatting invalid blocks
+ class DisplayInvalidBlocks
+ attr_reader :filename
+
+ def initialize(block_array, io: $stderr, filename: nil)
+ @filename = filename
+ @io = io
+ @blocks = block_array
+ @lines = @blocks.map(&:lines).flatten
+ @digit_count = @lines.last.line_number.to_s.length
+ @code_lines = @blocks.first.code_lines
+
+ @invalid_line_hash = @lines.each_with_object({}) {|line, h| h[line] = true}
+ end
+
+ def call
+ @io.puts <<~EOM
+
+ SyntaxSuggest: A syntax error was detected
+
+ This code has an unmatched `end` this is caused by either
+ missing a syntax keyword (`def`, `do`, etc.) or inclusion
+ of an extra `end` line:
+ EOM
+
+ @io.puts(<<~EOM) if filename
+ file: #{filename}
+ EOM
+
+ @io.puts <<~EOM
+ #{code_with_filename}
+ EOM
+ end
+
+ def filename
+
+ def code_with_filename
+ string = String.new("")
+ string << "```\n"
+ string << "#".rjust(@digit_count) + " filename: #{filename}\n\n" if filename
+ string << code_with_lines
+ string << "```\n"
+ string
+ end
+
+ def code_with_lines
+ @code_lines.map do |line|
+ next if line.hidden?
+ number = line.line_number.to_s.rjust(@digit_count)
+ if line.empty?
+ "#{number.to_s}#{line}"
+ else
+ string = String.new
+ string << "\e[1;3m" if @invalid_line_hash[line] # Bold, italics
+ string << "#{number.to_s} "
+ string << line.to_s
+ string << "\e[0m"
+ string
+ end
+ end.join
+ end
+ end
+end
diff --git a/spec/syntax_suggest/fixtures/webmock.rb.txt b/spec/syntax_suggest/fixtures/webmock.rb.txt
new file mode 100644
index 0000000000..16da0d2ac0
--- /dev/null
+++ b/spec/syntax_suggest/fixtures/webmock.rb.txt
@@ -0,0 +1,35 @@
+describe "webmock tests" do
+ before(:each) do
+ WebMock.enable!
+ end
+
+ after(:each) do
+ WebMock.disable!
+ end
+
+ it "port" do
+ port = rand(1000...9999)
+ stub_request(:any, "localhost:#{port}")
+
+ query = Cutlass::FunctionQuery.new(
+ port: port
+ ).call
+
+ expect(WebMock).to have_requested(:post, "localhost:#{port}").
+ with(body: "{}")
+ end
+
+ it "body" do
+ body = { lol: "hi" }
+ port = 8080
+ stub_request(:any, "localhost:#{port}")
+
+ query = Cutlass::FunctionQuery.new(
+ port: port
+ body: body
+ ).call
+
+ expect(WebMock).to have_requested(:post, "localhost:#{port}").
+ with(body: body.to_json)
+ end
+end
diff --git a/spec/syntax_suggest/integration/exe_cli_spec.rb b/spec/syntax_suggest/integration/exe_cli_spec.rb
new file mode 100644
index 0000000000..b9a3173715
--- /dev/null
+++ b/spec/syntax_suggest/integration/exe_cli_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require_relative "../spec_helper"
+
+module SyntaxSuggest
+ RSpec.describe "exe" do
+ def exe_path
+ if ruby_core?
+ root_dir.join("../libexec").join("syntax_suggest")
+ else
+ root_dir.join("exe").join("syntax_suggest")
+ end
+ end
+
+ def exe(cmd)
+ ruby = ENV.fetch("RUBY", "ruby")
+ out = run!("#{ruby} #{exe_path} #{cmd}", raise_on_nonzero_exit: false)
+ puts out if ENV["SYNTAX_SUGGEST_DEBUG"]
+ out
+ end
+
+ it "prints the version" do
+ out = exe("-v")
+ expect(out.strip).to include(SyntaxSuggest::VERSION)
+ end
+ end
+end
diff --git a/spec/syntax_suggest/integration/ruby_command_line_spec.rb b/spec/syntax_suggest/integration/ruby_command_line_spec.rb
new file mode 100644
index 0000000000..b41a4c86e3
--- /dev/null
+++ b/spec/syntax_suggest/integration/ruby_command_line_spec.rb
@@ -0,0 +1,193 @@
+# frozen_string_literal: true
+
+require_relative "../spec_helper"
+
+module SyntaxSuggest
+ ruby = ENV.fetch("RUBY", "ruby")
+ RSpec.describe "Requires with ruby cli" do
+ it "namespaces all monkeypatched methods" do
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ script.write <<~'EOM'
+ puts Kernel.private_methods
+ EOM
+
+ syntax_suggest_methods_file = tmpdir.join("syntax_suggest_methods.txt")
+ api_only_methods_file = tmpdir.join("api_only_methods.txt")
+ kernel_methods_file = tmpdir.join("kernel_methods.txt")
+
+ d_pid = Process.spawn("#{ruby} -I#{lib_dir} -rsyntax_suggest #{script} 2>&1 > #{syntax_suggest_methods_file}")
+ k_pid = Process.spawn("#{ruby} #{script} 2>&1 >> #{kernel_methods_file}")
+ r_pid = Process.spawn("#{ruby} -I#{lib_dir} -rsyntax_suggest/api #{script} 2>&1 > #{api_only_methods_file}")
+
+ Process.wait(k_pid)
+ Process.wait(d_pid)
+ Process.wait(r_pid)
+
+ kernel_methods_array = kernel_methods_file.read.strip.lines.map(&:strip)
+ syntax_suggest_methods_array = syntax_suggest_methods_file.read.strip.lines.map(&:strip)
+ api_only_methods_array = api_only_methods_file.read.strip.lines.map(&:strip)
+
+ # In ruby 3.1.0-preview1 the `timeout` file is already required
+ # we can remove it if it exists to normalize the output for
+ # all ruby versions
+ [syntax_suggest_methods_array, kernel_methods_array, api_only_methods_array].each do |array|
+ array.delete("timeout")
+ end
+
+ methods = (syntax_suggest_methods_array - kernel_methods_array).sort
+ if methods.any?
+ expect(methods).to eq(["syntax_suggest_original_load", "syntax_suggest_original_require", "syntax_suggest_original_require_relative"])
+ end
+
+ methods = (api_only_methods_array - kernel_methods_array).sort
+ expect(methods).to eq([])
+ end
+ end
+
+ # Since Ruby 3.2 includes syntax_suggest as a default gem, we might accidentally
+ # be requiring the default gem instead of this library under test. Assert that's
+ # not the case
+ it "tests current version of syntax_suggest" do
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ contents = <<~'EOM'
+ puts "suggest_version is #{SyntaxSuggest::VERSION}"
+ EOM
+ script.write(contents)
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest/version #{script} 2>&1`
+
+ expect(out).to include("suggest_version is #{SyntaxSuggest::VERSION}").once
+ end
+ end
+
+ it "detects require error and adds a message with auto mode" do
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ script.write <<~EOM
+ describe "things" do
+ it "blerg" do
+ end
+
+ it "flerg"
+ end
+
+ it "zlerg" do
+ end
+ end
+ EOM
+
+ require_rb = tmpdir.join("require.rb")
+ require_rb.write <<~EOM
+ load "#{script.expand_path}"
+ EOM
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest #{require_rb} 2>&1`
+
+ expect($?.success?).to be_falsey
+ expect(out).to include('> 5 it "flerg"').once
+ end
+ end
+
+ it "gem can be tested when executing on Ruby with default gem included" do
+ skip if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2")
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest -e "puts SyntaxError.instance_method(:detailed_message).source_location" 2>&1`
+
+ expect($?.success?).to be_truthy
+ expect(out).to include(lib_dir.join("syntax_suggest").join("core_ext.rb").to_s).once
+ end
+
+ it "annotates a syntax error in Ruby 3.2+ when require is not used" do
+ skip if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2")
+
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ script.write <<~EOM
+ describe "things" do
+ it "blerg" do
+ end
+
+ it "flerg"
+ end
+
+ it "zlerg" do
+ end
+ end
+ EOM
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest #{script} 2>&1`
+
+ expect($?.success?).to be_falsey
+ expect(out).to include('> 5 it "flerg"').once
+ end
+ end
+
+ it "does not load internals into memory if no syntax error" do
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ script.write <<~EOM
+ class Dog
+ end
+
+ if defined?(SyntaxSuggest::DEFAULT_VALUE)
+ puts "SyntaxSuggest is loaded"
+ else
+ puts "SyntaxSuggest is NOT loaded"
+ end
+ EOM
+
+ require_rb = tmpdir.join("require.rb")
+ require_rb.write <<~EOM
+ load "#{script.expand_path}"
+ EOM
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest #{require_rb} 2>&1`
+
+ expect($?.success?).to be_truthy
+ expect(out).to include("SyntaxSuggest is NOT loaded").once
+ end
+ end
+
+ it "ignores eval" do
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ script.write <<~'EOM'
+ $stderr = STDOUT
+ eval("def lol")
+ EOM
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest #{script} 2>&1`
+
+ expect($?.success?).to be_falsey
+ expect(out).to include("(eval):1")
+
+ expect(out).to_not include("SyntaxSuggest")
+ expect(out).to_not include("Could not find filename")
+ end
+ end
+
+ it "does not say 'syntax ok' when a syntax error fires" do
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ script.write <<~'EOM'
+ break
+ EOM
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest -e "require_relative '#{script}'" 2>&1`
+
+ expect($?.success?).to be_falsey
+ expect(out.downcase).to_not include("syntax ok")
+ expect(out).to include("Invalid break")
+ end
+ end
+ end
+end
diff --git a/spec/syntax_suggest/integration/syntax_suggest_spec.rb b/spec/syntax_suggest/integration/syntax_suggest_spec.rb
new file mode 100644
index 0000000000..64dafabcdd
--- /dev/null
+++ b/spec/syntax_suggest/integration/syntax_suggest_spec.rb
@@ -0,0 +1,239 @@
+# frozen_string_literal: true
+
+require_relative "../spec_helper"
+
+module SyntaxSuggest
+ RSpec.describe "Integration tests that don't spawn a process (like using the cli)" do
+ it "does not timeout on massive files" do
+ next unless ENV["SYNTAX_SUGGEST_TIMEOUT"]
+
+ file = fixtures_dir.join("syntax_tree.rb.txt")
+ lines = file.read.lines
+ lines.delete_at(768 - 1)
+
+ io = StringIO.new
+
+ benchmark = Benchmark.measure do
+ debug_perf do
+ SyntaxSuggest.call(
+ io: io,
+ source: lines.join,
+ filename: file
+ )
+ end
+ end
+
+ debug_display(io.string)
+ debug_display(benchmark)
+
+ expect(io.string).to include(<<~'EOM')
+ 6 class SyntaxTree < Ripper
+ 170 def self.parse(source)
+ 174 end
+ > 754 def on_args_add(arguments, argument)
+ > 776 class ArgsAddBlock
+ > 810 end
+ 9233 end
+ EOM
+ end
+
+ it "re-checks all block code, not just what's visible issues/95" do
+ file = fixtures_dir.join("ruby_buildpack.rb.txt")
+ io = StringIO.new
+
+ debug_perf do
+ benchmark = Benchmark.measure do
+ SyntaxSuggest.call(
+ io: io,
+ source: file.read,
+ filename: file
+ )
+ end
+ debug_display(io.string)
+ debug_display(benchmark)
+ end
+
+ expect(io.string).to_not include("def ruby_install_binstub_path")
+ expect(io.string).to include(<<~'EOM')
+ > 1067 def add_yarn_binary
+ > 1068 return [] if yarn_preinstalled?
+ > 1069 |
+ > 1075 end
+ EOM
+ end
+
+ it "returns good results on routes.rb" do
+ source = fixtures_dir.join("routes.rb.txt").read
+
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ debug_display(io.string)
+
+ expect(io.string).to include(<<~'EOM')
+ 1 Rails.application.routes.draw do
+ > 113 namespace :admin do
+ > 116 match "/foobar(*path)", via: :all, to: redirect { |_params, req|
+ > 120 }
+ 121 end
+ EOM
+ end
+
+ it "handles multi-line-methods issues/64" do
+ source = fixtures_dir.join("webmock.rb.txt").read
+
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ debug_display(io.string)
+
+ expect(io.string).to include(<<~'EOM')
+ 1 describe "webmock tests" do
+ 22 it "body" do
+ 27 query = Cutlass::FunctionQuery.new(
+ > 28 port: port
+ > 29 body: body
+ 30 ).call
+ 34 end
+ 35 end
+ EOM
+ end
+
+ it "handles derailed output issues/50" do
+ source = fixtures_dir.join("derailed_require_tree.rb.txt").read
+
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ debug_display(io.string)
+
+ expect(io.string).to include(<<~'EOM')
+ 5 module DerailedBenchmarks
+ 6 class RequireTree
+ > 13 def initialize(name)
+ > 18 def self.reset!
+ > 25 end
+ 73 end
+ 74 end
+ EOM
+ end
+
+ it "handles heredocs" do
+ lines = fixtures_dir.join("rexe.rb.txt").read.lines
+ lines.delete_at(85 - 1)
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: lines.join
+ )
+
+ out = io.string
+ debug_display(out)
+
+ expect(out).to include(<<~EOM)
+ 16 class Rexe
+ > 77 class Lookups
+ > 78 def input_modes
+ > 148 end
+ 551 end
+ EOM
+ end
+
+ it "rexe" do
+ lines = fixtures_dir.join("rexe.rb.txt").read.lines
+ lines.delete_at(148 - 1)
+ source = lines.join
+
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ out = io.string
+ expect(out).to include(<<~EOM)
+ 16 class Rexe
+ > 77 class Lookups
+ > 140 def format_requires
+ > 148 end
+ 551 end
+ EOM
+ end
+
+ it "ambiguous end" do
+ source = <<~'EOM'
+ def call # 0
+ print "lol" # 1
+ end # one # 2
+ end # two # 3
+ EOM
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ out = io.string
+ expect(out).to include(<<~EOM)
+ > 1 def call # 0
+ > 3 end # one # 2
+ > 4 end # two # 3
+ EOM
+ end
+
+ it "simple regression" do
+ source = <<~'EOM'
+ class Dog
+ def bark
+ puts "woof"
+ end
+ EOM
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ out = io.string
+ expect(out).to include(<<~EOM)
+ > 1 class Dog
+ > 2 def bark
+ > 4 end
+ EOM
+ end
+
+ it "empty else" do
+ source = <<~'EOM'
+ class Foo
+ def foo
+ if cond?
+ foo
+ else
+
+ end
+ end
+
+ # ...
+
+ def bar
+ if @recv
+ end_is_missing_here
+ end
+ end
+ EOM
+
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ out = io.string
+ expect(out).to include(<<~EOM)
+ end_is_missing_here
+ EOM
+ end
+ end
+end
diff --git a/spec/syntax_suggest/spec_helper.rb b/spec/syntax_suggest/spec_helper.rb
new file mode 100644
index 0000000000..89bc9f4ab1
--- /dev/null
+++ b/spec/syntax_suggest/spec_helper.rb